3

Frame packing is a method of sending a left and a right view (for stereoscopic 3D) vertically stacked within one frame via HDMI. If each view has size width x height the resulting "packed" frame has size width x 49/24 height, i.e., both views with some blank lines in between.

For 1080p this results in frames of size 1920x2205. For most FullHD (but not UHD or similar) devices frame packing is the only method of sending a FullHD view for each eye.

The following method works for setting the size:

cvt cvt 1920 2205 24 #generate Modeline
xrandr --newmode "1920x2205_24.00"  135.75  1920 2024 2224 2528  2205 2208 2218 2238 -hsync +vsync #Modeline 
xrandr --addmode HDMI1 "1920x2205_24.00"
xrandr --output  HDMI1 --mode 1920x2205_24.00

However, stereoscopic modes need to be announced to the TV/projector with so called info frames that specify the 3D Mode. While the kernel has support for sending these info frames, xrandr etc. do not allow to pass the relevant information to drm.

How can I make the kernel send the required info frames?

PS: I cannot add a tag "stereo3d" due to missing reputation. The tag "3d" does not fit.

frafl
  • 204
  • 1
  • 11

2 Answers2

3

The following solution comes without any warranty. Be aware that sending HDMI signals that violate the standard may be bad for your TV/projector and that the following tiny hack breaks abstraction layers, which were introduced for a reason. Please do not ask for a kernel patch (i.e. file a bug there).

One method is a tiny modification of the drm kernel module. Please read How (recipe) to build only one kernel module? first.

The file you have to modify is: drivers/gpu/drm/drm_edid.c. We change this line to:

    s3d_flags = (mode->flags & DRM_MODE_FLAG_3D_MASK) ||
            (mode->vdisplay == 2205);

and insert here the following lines:

    if(mode->vdisplay == 2205)
            return HDMI_3D_STRUCTURE_FRAME_PACKING;

You should remove most (if not all) graphic card specific modules from drivers/gpu/drm/Makefile before compiling the drm module according to the question mentioned in the beginning. Be aware that you have to update initramfs, since the drm module is loaded at boot time.

This solution was tested with an Epson projector and intel graphics card (uses i915). Before modifying the drm module you should check that your graphic card kernel module actually uses the relevant functions of the drm module. Otherwise this hack is pointless.

frafl
  • 204
  • 1
  • 11
0

I think I've got another solution to this but I haven't had the opportunity to test it on my projector yet. But based on the kernel code I think it should work. Basically the kernel and x11 appear to have all the required code to work properly right now. It is just xrandr which lacks support for the required drm mode flags (it doesn't support beyond "-CSync").

https://gitlab.freedesktop.org/xorg/app/xrandr/-/blob/master/xrandr.c#L90

You can see in the kernel all the flags are there though. https://github.com/torvalds/linux/blob/5437f30d3458ad36e83ab96088d490ebfee844d8/include/drm/drm_modes.h#L303 https://github.com/torvalds/linux/blob/5437f30d3458ad36e83ab96088d490ebfee844d8/include/uapi/drm/drm_mode.h#L89

Also the kernel side(?) xlib xrandr library appears to just accept a 32bit mode flag and doesn't restrict it like the xrandr client side application does.

As such if you write some code to interface directly with the xlib xrandr library and send the proper 32 bit mode flag when you create your mode, you can create a monitor mode with the required "DRM_MODE_FLAG_3D_XXXXX"

Below is a simple python script showing how to create such a monitor mode. I haven't tested it yet, but based on my understanding when you switch to that mode it should send the proper hdmi mode flag.

from __future__ import print_function
from Xlib import X, display
from Xlib.ext import randr

mode_flags = { 'DRM_MODE_FLAG_PHSYNC': (1<<0), 'DRM_MODE_FLAG_NHSYNC': (1<<1), 'DRM_MODE_FLAG_PVSYNC': (1<<2), 'DRM_MODE_FLAG_NVSYNC': (1<<3), 'DRM_MODE_FLAG_INTERLACE': (1<<4), 'DRM_MODE_FLAG_DBLSCAN': (1<<5), 'DRM_MODE_FLAG_CSYNC': (1<<6), 'DRM_MODE_FLAG_PCSYNC': (1<<7), 'DRM_MODE_FLAG_NCSYNC': (1<<8), 'DRM_MODE_FLAG_HSKEW': (1<<9), # /* hskew provided / 'DRM_MODE_FLAG_BCAST': (1<<10), # / deprecated / 'DRM_MODE_FLAG_PIXMUX': (1<<11), # / deprecated */ 'DRM_MODE_FLAG_DBLCLK': (1<<12), 'DRM_MODE_FLAG_CLKDIV2': (1<<13), #'DRM_MODE_FLAG_3D_NONE': (0<<14) 'DRM_MODE_FLAG_3D_FRAME_PACKING': (1<<14), 'DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE': (2<<14), 'DRM_MODE_FLAG_3D_LINE_ALTERNATIVE': (3<<14), 'DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL': (4<<14), 'DRM_MODE_FLAG_3D_L_DEPTH': (5<<14), 'DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH': (6<<14), 'DRM_MODE_FLAG_3D_TOP_AND_BOTTOM': (7<<14), 'DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF': (8<<14), #'DRM_MODE_FLAG_3D_MASK': (0x1f<<14), #'DRM_MODE_FLAG_PIC_AR_MASK': (0x0F<<19), }

d = display.Display() s = d.screen() window = s.root.create_window(0, 0, 1, 1, 1, s.root_depth)

def get_modes(): res = randr.get_screen_resources(window) for mode in res.modes: mode_string = '' for n, i in mode_flags.items(): if mode.flags & i == i: mode_string += f' +{n}' print(f'{mode.id} {mode.name if hasattr(mode, "name") else "noname"} {mode.name_length} {mode.width}, height: {mode.height}, mode_flags: {mode.flags}, mode_string: {mode_string}')

def create_mode():

based on existing timings "1920x2205_24.00" 148.5 1920 2366 2454 2750 2205 2209 2214 2250 +hsync +vsync

based on hdmi 3d spec "1920x2205_24.00" 148.5 1920 2558 2602 2750 2205 2209 2214 2250 +hsync +vsync https://archive.org/details/HDMI-1.4a-3D-Extract/page/n9/mode/1up

name = '1920x2205_24.00' flags = mode_flags['DRM_MODE_FLAG_PHSYNC'] | mode_flags['DRM_MODE_FLAG_PVSYNC'] | mode_flags['DRM_MODE_FLAG_3D_FRAME_PACKING'] randr.create_mode( window, mode={ 'id': 900, 'width': 1920, 'height': 2205, 'dot_clock': 148500000, 'h_sync_start': 2366, 'h_sync_end': 2454, 'h_total': 2750, 'h_skew': 0, 'v_sync_start': 2209, 'v_sync_end': 2214, 'v_total': 2250, 'name_length': len(name), 'flags': flags }, name='1920x2205_24.00' )

xrandr --addmode HDMI-1-1 "1920x2205_24.00"

xrandr --delmode HDMI-1-1 "1920x2205_24.00"

xrandr --rmmode "1920x2205_24.00"

create_mode() get_modes()

Peter
  • 161