setting a custom video mode using DRM/KMS in Linux (tested on raspberry pi 4)

Whether by design or by oversight, it turns out the kms/drm driver architecture used in linux to set the current video mode allows easy selection of custom modes as well. You just stuff the values you want into the mode structure returned by getConnector, and then tell drm to use that mode. Because this is close to the metal you can't just say height, width, and refresh, but need to specify the full timing info like you would using xorg's modelines. In fact, here's some code that takes a xorg compliant modeline and stuffs it into the proper fields of the mode structure. 


drmModeConnector *connector = getConnector(resources);
   connectorId = connector->connector_id;

    // choose a mode (resolution + refresh rate)

    mode_info = connector->modes[modenum]; // array of resolutions and refresh rates supported by this display
    mode = &mode_info;
    dump_mode(mode);

  if (argc == 3)
    {
    mode->type = DRM_MODE_TYPE_USERDEF;

     char flags[5][16] = {0,0,0,0,0};;
   float fclock;
// example: 13.514 720 739 801 858 480 488 494 525 -hsync -vsync interlace dblclk
    sscanf(argv[1], "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s %15s %15s",
   &fclock,
   &mode->hdisplay,
   &mode->hsync_start,
   &mode->hsync_end,
   &mode->htotal,
   &mode->vdisplay,
   &mode->vsync_start,
   &mode->vsync_end,
   &mode->vtotal, flags[0], flags[1], flags[2], flags[3], flags[4]);
   
    mode->clock = fclock * 1000;

         mode->flags = 0;

for (int f = 0; f < 4; f++)  // this could use the array that's used to dump the flags
{
if (strcasecmp(flags[f], "+hsync") == 0)
                mode->flags |= DRM_MODE_FLAG_PHSYNC;
        if (strcasecmp(flags[f], "-hsync") == 0)
                mode->flags |= DRM_MODE_FLAG_NHSYNC;

        if (strcasecmp(flags[f], "+vsync") == 0)
                mode->flags |= DRM_MODE_FLAG_PVSYNC;
        if (strcasecmp(flags[f], "-vsync") == 0)
                mode->flags |= DRM_MODE_FLAG_NVSYNC;

        if (strcasecmp(flags[f], "interlace") == 0)
                mode->flags |= DRM_MODE_FLAG_INTERLACE;

if (strcasecmp(flags[f], "dblclk") == 0)
                mode->flags |= DRM_MODE_FLAG_DBLCLK;
}

  mode->vrefresh = .49 + drm_mode_vrefresh(mode); // looks like this is just for display, since it's an integer it's notused for any real display calculations
snprintf(mode->name, sizeof(mode->name),"%dx%d%c", 
mode->hdisplay, mode->vdisplay,
    (mode->flags & DRM_MODE_FLAG_INTERLACE ? 'i':0)); 

Comments

Email me

Name

Email *

Message *

Popular posts from this blog

Panasonic TH-42PX75U Plasma TV review: input lag and upscaling tested using the piLagTesterPRO

piLagTester PRO order page

How to pause animation in PowerPoint