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 displaymode = &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 dblclksscanf(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 calculationssnprintf(mode->name, sizeof(mode->name),"%dx%d%c",mode->hdisplay, mode->vdisplay,(mode->flags & DRM_MODE_FLAG_INTERLACE ? 'i':0));
Comments