mirror of
https://github.com/mpv-player/mpv
synced 2024-12-27 17:42:17 +00:00
drm_common: Support --drm-mode=<preferred|highest|N|WxH[@R]>
This allows to select the drm mode using a string specification. You can either select the the preferred mode, the mode with the highest resolution, by specifying WxH[@R] or by its index in the list of modes as before.
This commit is contained in:
parent
d155b7541f
commit
dcb7838bb7
@ -484,10 +484,21 @@ Available video output drivers are:
|
||||
argument to disambiguate.
|
||||
(default: empty)
|
||||
|
||||
``--drm-mode=<number>``
|
||||
Mode ID to use (resolution and frame rate). Use ``--drm-mode=help`` to
|
||||
get a list of available modes for all active connectors.
|
||||
(default: 0)
|
||||
``--drm-mode=<preferred|highest|N|WxH[@R]>``
|
||||
Mode to use (resolution and frame rate).
|
||||
Possible values:
|
||||
|
||||
:preferred: Use the preferred mode for the screen on the selected
|
||||
connector. (default)
|
||||
:highest: Use the mode with the highest resolution available on the
|
||||
selected connector.
|
||||
:N: Select mode by index.
|
||||
:WxH[@R]: Specify mode by width, height, and optionally refresh rate.
|
||||
In case several modes match, selects the mode that comes
|
||||
first in the EDID list of modes.
|
||||
|
||||
Use ``--drm-mode=help`` to get a list of available modes for all active
|
||||
connectors.
|
||||
|
||||
``--drm-atomic=<no|auto>``
|
||||
Toggle use of atomic modesetting. Mostly useful for debugging.
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <sys/vt.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "drm_common.h"
|
||||
|
||||
@ -55,6 +56,7 @@ static void kms_show_available_modes(
|
||||
struct mp_log *log, const drmModeConnector *connector);
|
||||
|
||||
static void kms_show_available_connectors(struct mp_log *log, int card_no);
|
||||
static double mode_get_Hz(const drmModeModeInfo *mode);
|
||||
|
||||
#define OPT_BASE_STRUCT struct drm_opts
|
||||
const struct m_sub_options drm_conf = {
|
||||
@ -83,6 +85,7 @@ const struct m_sub_options drm_conf = {
|
||||
{0},
|
||||
},
|
||||
.defaults = &(const struct drm_opts) {
|
||||
.drm_mode_spec = "preferred",
|
||||
.drm_atomic = 1,
|
||||
.drm_draw_plane = DRM_OPTS_PRIMARY_PLANE,
|
||||
.drm_drmprime_video_plane = DRM_OPTS_OVERLAY_PLANE,
|
||||
@ -111,6 +114,19 @@ static const char *connector_names[] = {
|
||||
"DPI", // DRM_MODE_CONNECTOR_DPI
|
||||
};
|
||||
|
||||
struct drm_mode_spec {
|
||||
enum {
|
||||
DRM_MODE_SPEC_BY_IDX, // Specified by idx
|
||||
DRM_MODE_SPEC_BY_NUMBERS, // Specified by width, height and opt. refresh
|
||||
DRM_MODE_SPEC_PREFERRED, // Select the preferred mode of the display
|
||||
DRM_MODE_SPEC_HIGHEST, // Select the mode with the highest resolution
|
||||
} type;
|
||||
unsigned int idx;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
double refresh;
|
||||
};
|
||||
|
||||
// KMS ------------------------------------------------------------------------
|
||||
|
||||
static void get_connector_name(const drmModeConnector *connector,
|
||||
@ -255,23 +271,218 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_mode(struct kms *kms, const char *mode_spec)
|
||||
static bool all_digits(const char *str)
|
||||
{
|
||||
const int mode_id = (mode_spec != NULL) ? atoi(mode_spec) : 0;
|
||||
|
||||
if (mode_id < 0 || mode_id >= kms->connector->count_modes) {
|
||||
MP_ERR(kms, "Bad mode ID (max = %d).\n",
|
||||
kms->connector->count_modes - 1);
|
||||
|
||||
MP_INFO(kms, "Available modes:\n");
|
||||
kms_show_available_modes(kms->log, kms->connector);
|
||||
if (str == NULL || str[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
kms->mode.mode = kms->connector->modes[mode_id];
|
||||
for (const char *c = str; *c != '\0'; ++c) {
|
||||
if (!mp_isdigit(*c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_mode_spec(const char *spec, struct drm_mode_spec *parse_result)
|
||||
{
|
||||
if (spec == NULL || spec[0] == '\0' || strcmp(spec, "preferred") == 0) {
|
||||
if (parse_result) {
|
||||
*parse_result =
|
||||
(struct drm_mode_spec) { .type = DRM_MODE_SPEC_PREFERRED };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(spec, "highest") == 0) {
|
||||
if (parse_result) {
|
||||
*parse_result =
|
||||
(struct drm_mode_spec) { .type = DRM_MODE_SPEC_HIGHEST };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the string is made up of only digits, it means that it is an index number
|
||||
if (all_digits(spec)) {
|
||||
if (parse_result) {
|
||||
*parse_result = (struct drm_mode_spec) {
|
||||
.type = DRM_MODE_SPEC_BY_IDX,
|
||||
.idx = strtoul(spec, NULL, 10),
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mp_isdigit(spec[0]))
|
||||
return false;
|
||||
char *height_part, *refresh_part;
|
||||
const unsigned int width = strtoul(spec, &height_part, 10);
|
||||
if (spec == height_part || height_part[0] == '\0' || height_part[0] != 'x')
|
||||
return false;
|
||||
|
||||
height_part += 1;
|
||||
if (!mp_isdigit(height_part[0]))
|
||||
return false;
|
||||
const unsigned int height = strtoul(height_part, &refresh_part, 10);
|
||||
if (height_part == refresh_part)
|
||||
return false;
|
||||
|
||||
char *rest = NULL;
|
||||
double refresh;
|
||||
switch (refresh_part[0]) {
|
||||
case '\0':
|
||||
refresh = nan("");
|
||||
break;
|
||||
case '@':
|
||||
refresh_part += 1;
|
||||
if (!(mp_isdigit(refresh_part[0]) || refresh_part[0] == '.'))
|
||||
return false;
|
||||
refresh = strtod(refresh_part, &rest);
|
||||
if (refresh_part == rest || rest[0] != '\0' || refresh < 0.0)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parse_result) {
|
||||
*parse_result = (struct drm_mode_spec) {
|
||||
.type = DRM_MODE_SPEC_BY_NUMBERS,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.refresh = refresh,
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_mode_by_idx(struct kms *kms, unsigned int mode_idx)
|
||||
{
|
||||
if (mode_idx >= kms->connector->count_modes) {
|
||||
MP_ERR(kms, "Bad mode index (max = %d).\n",
|
||||
kms->connector->count_modes - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
kms->mode.mode = kms->connector->modes[mode_idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mode_match(const drmModeModeInfo *mode,
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double refresh)
|
||||
{
|
||||
if (isnan(refresh)) {
|
||||
return
|
||||
(mode->hdisplay == width) &&
|
||||
(mode->vdisplay == height);
|
||||
} else {
|
||||
const double mode_refresh = mode_get_Hz(mode);
|
||||
return
|
||||
(mode->hdisplay == width) &&
|
||||
(mode->vdisplay == height) &&
|
||||
((int)round(refresh*100) == (int)round(mode_refresh*100));
|
||||
}
|
||||
}
|
||||
|
||||
static bool setup_mode_by_numbers(struct kms *kms,
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double refresh,
|
||||
const char *mode_spec)
|
||||
{
|
||||
for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
|
||||
drmModeModeInfo *current_mode = &kms->connector->modes[i];
|
||||
if (mode_match(current_mode, width, height, refresh)) {
|
||||
kms->mode.mode = *current_mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MP_ERR(kms, "Could not find mode matching %s\n", mode_spec);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool setup_mode_preferred(struct kms *kms)
|
||||
{
|
||||
for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
|
||||
drmModeModeInfo *current_mode = &kms->connector->modes[i];
|
||||
if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
kms->mode.mode = *current_mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to first mode
|
||||
MP_WARN(kms, "Could not find any preferred mode. Picking the first mode.\n");
|
||||
kms->mode.mode = kms->connector->modes[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_mode_highest(struct kms *kms)
|
||||
{
|
||||
unsigned int area = 0;
|
||||
drmModeModeInfo *highest_resolution_mode = &kms->connector->modes[0];
|
||||
for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
|
||||
drmModeModeInfo *current_mode = &kms->connector->modes[i];
|
||||
|
||||
const unsigned int current_area =
|
||||
current_mode->hdisplay * current_mode->vdisplay;
|
||||
if (current_area > area) {
|
||||
highest_resolution_mode = current_mode;
|
||||
area = current_area;
|
||||
}
|
||||
}
|
||||
|
||||
kms->mode.mode = *highest_resolution_mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_mode(struct kms *kms, const char *mode_spec)
|
||||
{
|
||||
if (kms->connector->count_modes <= 0) {
|
||||
MP_ERR(kms, "No available modes\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct drm_mode_spec parsed;
|
||||
if (!parse_mode_spec(mode_spec, &parsed)) {
|
||||
MP_ERR(kms, "Parse error\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (parsed.type) {
|
||||
case DRM_MODE_SPEC_BY_IDX:
|
||||
if (!setup_mode_by_idx(kms, parsed.idx))
|
||||
goto err;
|
||||
break;
|
||||
case DRM_MODE_SPEC_BY_NUMBERS:
|
||||
if (!setup_mode_by_numbers(kms, parsed.width, parsed.height, parsed.refresh,
|
||||
mode_spec))
|
||||
goto err;
|
||||
break;
|
||||
case DRM_MODE_SPEC_PREFERRED:
|
||||
if (!setup_mode_preferred(kms))
|
||||
goto err;
|
||||
break;
|
||||
case DRM_MODE_SPEC_HIGHEST:
|
||||
if (!setup_mode_highest(kms))
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
MP_ERR(kms, "setup_mode: Internal error\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
MP_INFO(kms, "Available modes:\n");
|
||||
kms_show_available_modes(kms->log, kms->connector);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int open_card(int card_no)
|
||||
{
|
||||
char card_path[128];
|
||||
@ -522,12 +733,15 @@ static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
|
||||
kms_show_available_cards_connectors_and_modes(log);
|
||||
return M_OPT_EXIT;
|
||||
}
|
||||
for (unsigned i = 0; i < param.len; ++i) {
|
||||
if (!mp_isdigit(param.start[i])) {
|
||||
mp_fatal(log, "Invalid value for option drm-mode. Must be a positive number or 'help'\n");
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
|
||||
char *spec = bstrto0(NULL, param);
|
||||
if (!parse_mode_spec(spec, NULL)) {
|
||||
mp_fatal(log, "Invalid value for option drm-mode. Must be a positive number, a string of the format WxH[@R] or 'help'\n");
|
||||
talloc_free(spec);
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
talloc_free(spec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user