State of affairs:
enable_tvout (Pi 4B only) On the Raspberry Pi 4, composite output is disabled by default, due to the way the internal clocks are interrelated and allocated. Because composite video requires a very specific clock, setting that clock to the required speed on the Pi 4 means that other clocks connected to it are detrimentally affected, which slightly slows down the entire system. Since composite video is a less commonly used function, we decided to disable it by default to prevent this system slowdown.
To enable composite output, use the enable_tvout=1 option. As described above, this will detrimentally affect performance to a small degree.
On older Pi models, the composite behaviour remains the same.
Source: https://www.raspberrypi.org/documentation/configuration/config-txt/video.md
Here are the settings I use on my 20" Trinitron.
framebuffer_width=580
framebuffer_height=360
enable_tvout=1
sdtv_mode=0
sdtv_aspect=1
disable_overscan=1
audio_pwm_mode=2
There are many similar cables out there bundled with various A/V equipment, however most of them have ground on PIN1 and won't work, but the cable we need must have composite video on PIN1. The wiring of the other pins doesn't matter. So for example if PIN1 on the 3.5mm jack is wired to the white or red RCA connector it will still work as video, you just plug that into the TV Composite port instead of the yellow connector. This is the cable I used and can verify that it works and has the correct wiring: https://www.adafruit.com/product/2881
I believe the following cable is also wired correctly: https://www.amazon.com/Gam3Gear-Composite-Cable-Microsoft-XBox-360/dp/B011KLLMR2
If you don't see the rainbow screen on your display, try power cycling.
I recommend you keep it that way as a boot default and Emulationstation. Try playing your favorite ROMs first, and you will probably notice that the interlace shake is rather annoying, and there may be other issues. In my experience Sakitoshi's tvout_smart
and tvout_sharp
shaders (https://github.com/Sakitoshi/retropie-crt-tvout/tree/master/to_configs/all/retroarch/shaders) do a great job of improving things quite a bit, but just make sure you Shader #X Filter to Linear. Try one of them as the only shader pass first. You may want to check out the configs in that repo as some platforms need additional tuning.
You can boot directly into 240p by setting sdtv_mode=16/18
for NTSC/PAL.
Mode switching between 480i and 240p via DRM/KMS is currently not possible.
Installing X11 and adding ModeLines using xrandr
also won't work. You will essentially resize the framebuffer, but the output will remain the same. The TV output is controlled by the VEC DAC (encoder generating the analog PAL or NTSC composite signal), but the VEC is not accessible with the fake/firmware vc4-fkms-v3d driver
and my understanding is the vc4-kms-v3d-pi4
which would allow direct hardware access is still experimental. The following gives a good description of the different driver models:
FKMS (fake) uses the dispmanx and mailbox API's to talk to the firmware for things like the composition and video output stages.
KMS does all that by accessing the HW registers directly from ARM space, bypassing the firmware completely.
It's down to what actually drives the video scaler (HVS), pixel valves, and output display blocks (HDMI/VEC/DSI/DPI). With vc4-fkms-v3d this remains with the firmware, and the firmware still allows DispmanX or MMAL to add extra layers. With vc4-kms-v3d, the Linux kernel is driving all that lot, and DRM prohibits multiple clients adding layers at the same time.
Source: https://www.raspberrypi.org/forums/viewtopic.php?p=1507622#p1507247
TV mode selection is done by an atomic property on the encoder, because a drm_mode_modeinfo is insufficient to distinguish between PAL and PAL-M or NTSC and NTSC-J.
Source: https://dri.freedesktop.org/docs/drm/gpu/vc4.html
For example setting the default NTSC mode requires updating CONFIG0 and CONFIG1 registers, apart from the DRM modeline: https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L244 https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L256 https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L286
(example would apply in full KMS mode only)
In order to achieve 240p the following changes to the output signal are needed:
- Integer number of lines (either 262 or 263) instead of 262.5 (x2 = 525) used for interlacing
- This will cause the VSync pulse to be sent at the end of a scanline and not in the middle of it, thus the scanlines will be retraced instead of being shifted down due to the ramp restart of the electron beam sawtooth wave.
- Change to the VSync and equalization pulses within the CSync signal sent during the blanking period
Source: https://www.hdretrovision.com/blog/2018/10/22/engineering-csync-part-1-setting-the-stage
When we set a progressive mode in config.txt
(sdtv_mode=16/18
) the firmware does something similar to what the vc4_vec driver would do in full KMS mode:
A. Sets the progressive scan bit in the VEC_CONFIG2
register of the VEC register set:
https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L104
B. Removes the interlace flag for the mode: https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L260
There are probably other steps needed as well.
Source: raspberrypi/firmware#683 (comment)
tvservice
essentially sends a VCHI message requesting the corresponding sdtv_mode from the firmware:
https://github.com/raspberrypi/userland/blob/2448644657e5fbfd82299416d218396ee1115ece/interface/vmcs_host/vc_sdtv.h#L60 https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/tvservice/tvservice.c#L703 https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_tvservice.c#L1213 https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_tvservice.c#L698
The VEC in the new BCM2711 SoC is the same as in the older BCM2835 therefore the register regions remain valid:
$ dtc -I dtb -O dts -o ~/devicetree.dts /boot/bcm2711-rpi-4-b.dtb
$ grep "vec" ~/devicetree.dts
vec@7e806000 {
compatible = "brcm,bcm2835-vec";
vec = "/soc/vec@7e806000";
The good news is 240p per ROM/Platform is still possible! Now granted it's not EXACTLY 240p since DRM/KMS doesn't recognize the changes we make with tvservice
. The framebuffer still has a 480 line resolution, but the interlacing flicker is gone and the TV is outputting 240p on screen. On my 20" Sony Trinitron, NES games look perfect with only the tvout_smart
or tvout_sharp
shader applied, horizontal and vertical scrolling seems fine. 240p test suite includes a horizontal scroll test for various platforms: http://junkerhq.net/xrgb/index.php?title=240p_test_suite
If you aren't happy with the outcome, read this post as it has a lot of information on tweaking the output in 240p: https://retropie.org.uk/forum/topic/11628/240p-and-mame-scaling/12
Now the last problem to resolve is enforcing the output. Out of the box RetroPie comes with a KMS DRM modesetting mechanism that works like this:
- You set the desired mode in videomodes.cfg
- runcommand.sh injects the modeset environment variables for SDL2 modesetting
- retroarch starts and loads SDL2 which sees the environment variables and sets the mode
Unfortunately this doesnt work on RPi4, and thows us back into 480i, as it is the only mode defined. Attempting to set progressive scan with tvservice
prior to retroarch launching will yield the same outcome. Our only solution is to wait for retroarch/SDL2 to finish loading and then switch back to progressive scan.
I put together a simple script vmodes_watcher.py
that runs in the background and monitors the value of a desired_mode file. If the file is modified, it waits for retroarch
to start and then changes the screen to the desired mode with tvservice
.
to install it do the following
$ git clone https://github.com/b0xspread/rpi4-crt.git
$ cd rpi4-crt
$ cp runcommand-onend.sh runcommand-onstart.sh vmodes_watcher.py /opt/retropie/configs/all
$ mkdir /opt/retropie/configs/all/desired_mode
$ echo 'NTSC 4:3 P' > /opt/retropie/configs/all/desired_mode/value
$ sudo bash
# apt install python3-pip
# pip3 install watchdog psutil
# chmod a+rwx -R /opt/retropie/configs/all/*.sh && sudo chmod a+rwx -R /opt/retropie/configs/all/*.py
# sed "s/exit 0/su pi -c 'python3 -u \/opt\/retropie\/configs\/all\/vmodes_watcher.py &> \/var\/log\/vmodes_watcher.log' &\nexit 0/" /etc/rc.local
# echo > /var/log/vmodes_watcher.log
# chmod 666 /var/log/vmodes_watcher.log
# reboot
To verify the watcher is running:
$ ps ax | grep vmodes_watcher.py$
595 ? Sl 0:00 python3 -u /opt/retropie/configs/all/vmodes_watcher.py
To monitor activity:
$ watch cat /var/log/vmodes_watcher.log
Now try playing a ROM from a platform with 240p support (ex. NES). If everything installed correctly you should no longer see the flicker. Apply those tvout shaders previously mentioned and see which one you like best.
If you were monitoring the log file you should see something like this:
Every 2.0s: cat /var/log/vmodes_watcher.log retropie: Wed May 13 19:29:54 2020
Starting watcher...
Waiting for retroarch to start...
Waiting for retroarch to start...
Waiting for retroarch to start...
Waiting for retroarch to start...
Setting desired display mode: 'NTSC 4:3 P' ...
Powering on SDTV with explicit settings (mode:16 aspect:1)
When you exit, Emulationstation should be back to 480i (this can be changed in runcommand-onend.sh
)
240p is the default mode for runcommand-onstart.sh
. In order to force 480i for a particular game or platform you need to add the rom name or all
in the corresponding 480i.txt
file.
For example if I want all MAME ROMs to run in 480i I would do the following:
cat /opt/retropie/configs/mame-libretro/480i.txt
all
Alternatively if I just wanted a particular ROM to run in 480i I would do this instead:
cat /opt/retropie/configs/mame-libretro/480i.txt
umk3.zip
Check out the following post for instructions on tweaking the image for 224/240p and the pi_iq_horz_nearest_vert shader:
https://retropie.org.uk/forum/topic/11628/240p-and-mame-scaling/12
Sakitoshi has a great repo containing shaders, configurations and palletes for various platforms: