raspberrypi / firmware

This repository contains pre-compiled binaries of the current Raspberry Pi kernel and modules, userspace libraries, and bootloader/GPU firmware.
5.18k stars 1.68k forks source link

modify HDMI timings on the fly without rebooting revisit #1743

Open bit-bash opened 2 years ago

bit-bash commented 2 years ago

For reference: #637 Please can the ability to modify HDMI timings on the fly (i.e. without having to carry out a reboot) be added to the firmware drivers

I have been trying this with limited success without an fbset workaround. I have tested this on the 2022-04-04 LITE releases of 32-bit buster and 64-bit bullseye on both RPi 3B+ and RPi 4 (no desktop). I want to access /dev/fb0 at different display resolutions at 32 bits/pixel. I have added fbcon=map:2 to /boot/cmdline.txt so the kernel doesn't touch or display on /dev/fb0.

The basic premise is I can boot the RPi and it generally adapts to my HDMI display's preferred settings. Then, if I want to change the resolution to say 800x600, I can do this:

vcgencmd hdmi_timings 800 1 40 128 88 600 1 1 4 23 0 0 0 60 0 40000000 1
tvservice -e "DMT 87"
fbset -depth 16 && fbset -depth 32

The issue I'm having is the fbset line isn't correctly setting the timings on the HDMI port. My 800x600 image loaded to /dev/fb0 only displays in the top corner of my native display (was 1920x1080).

To make it work the way I want, I have to do this: fbset 800x600-60 -depth 32 Then I get the correct display timings for 800x600 @ 60Hz.

The new issue this creates is that I now need to add any missing mode settings to /etc/fb.modes if they are not present. For example, if I want to change to 1280x720 (CEA mode 4), I can do this:

vcgencmd hdmi_timings 1280 1 110 40 220 720 1 5 5 20 0 0 0 60 0 74250000 3
tvservice -e "DMT 87"

But then I have to add these lines into /etc/fb.modes:

# CEA mode 4
mode "1280x720-60"
    geometry 1280 720 1280 720 32
    timings 13468 220 110 20 5 40 5
    hsync high
    vsync high

Before I can do this: fbset 1280x720-60 -depth 32

What I'm trying to figure out is how did this work before with only setting the -depth for fbset? Of course I can add resolutions to /etc/fb.modes that don't exist in my huge list of hdmi_timings. Am I missing something from the original issue or specifically a framebuffer issue that would have worked before? Thanks!

popcornmix commented 2 years ago

Note on bullseye the default is the kms driver and the firmware doesn't control HDMI (the kernel does). The firmware drivers are deprecated and unlikely to get new features.

You should look at doing this using drm/kms. That means no vcgencmd or tvservice.

bit-bash commented 2 years ago

I have disabled (commented out) the line dtoverlay=vc4-kms-v3d in /boot/config.txt. tvservice doesn't like the kms driver. And for the lite versions of the Raspbian OS, I don't need any fancy acceleration - I'm just displaying images on the framebuffer directly from Python.

You should look at doing this using drm/kms.

Can you give me a shove in the right direction? Not sure how to do HDMI/framebuffer timing/res changes on-the-fly by the drm/kms method.

popcornmix commented 2 years ago

I did ask @6by9 about this and:

I've looked through the code. DRM_IOCTL_MODE_SETCRTC takes a struct drm_mode_crtc - https://elixir.free-electrons.com/linux/latest/source/include/uapi/drm/drm_mode.h#L277 That includes a struct drm_mode_modeinfo with the full timings - https://elixir.free-electrons.com/linux/latest/source/include/uapi/drm/drm_mode.h#L242 modetest and xrandr are looking through the list of probed modes and copying the chosen one into the struct drm_mode_crtc. https://gitlab.freedesktop.org/mesa/drm/-/blob/main/tests/modetest/modetest.c#L836 is finding the specified mode in the list, called from https://gitlab.freedesktop.org/mesa/drm/-/blob/main/tests/modetest/modetest.c#L922 which assigns to pipe->mode. https://gitlab.freedesktop.org/mesa/drm/-/blob/main/tests/modetest/modetest.c#L1617 is then calling drmModeSetCrtc to set the mode. So it is largely going to be down to whether restoring the framebuffer resets that mode.

I'm not sure if there is example code that does this. X does allow custom modes to be specified in Xorg.conf (see modeline, and if you were to dig into that code, you can probably find how it does it (and you could add it to your own application which would run outside of X). It probably is doing this through drmModeSetCrtc.

bit-bash commented 2 years ago

Thanks for the great info! I will start looking through that stuff soon. One other thing I noticed, my current method using vcgencmd, tvservice, fbset works ok on the RPi 4 with a single HDMI display. If there are two HDMI displays things go strange and after trying to set the different fb options, sometimes tvservice will hang when asking it to display current settings. I'm also not sure if vcgencmd hdmi_timings tries to set both HDMI ports as I don't think vcgencmd has a option to specify witch HDMI port to set the timings for.