mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-02-05 04:06:11 -05:00
backend/drm: support hotplug_mode_update connector prop
For connectors that set the hotplug_mode_update prop (usually connectors corresponding to virtualised outputs), ignore modes other than the preferred mode, treat the preferred mode as a custom mode, and do not add it to the output->modes list. When the preferred mode changes, send a request_state event on the output to request for the compositor to change to the new mode.
This commit is contained in:
parent
4abd26ac55
commit
e0b4379ce5
4 changed files with 151 additions and 35 deletions
|
|
@ -468,6 +468,9 @@ static struct wlr_output_mode *drm_connector_get_pending_mode(
|
|||
output->pending.custom_mode.height,
|
||||
(float)output->pending.custom_mode.refresh / 1000, false, false);
|
||||
mode.type = DRM_MODE_TYPE_USERDEF;
|
||||
if (conn->hot_plug_mode) {
|
||||
return &wlr_drm_connector_create_mode(output, &mode)->wlr_mode;
|
||||
}
|
||||
return wlr_drm_connector_add_mode(output, &mode);
|
||||
}
|
||||
abort();
|
||||
|
|
@ -536,6 +539,26 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool drm_connector_has_hotplug_mode(struct wlr_drm_connector *conn) {
|
||||
uint64_t hotplug_mode_update = 0;
|
||||
struct wlr_drm_backend *drm = conn->backend;
|
||||
get_drm_prop(drm->fd, conn->id, conn->props.hotplug_mode_update,
|
||||
&hotplug_mode_update);
|
||||
return hotplug_mode_update == 1;
|
||||
}
|
||||
|
||||
bool drm_connector_get_preferred_mode(drmModeConnector *drm_conn,
|
||||
drmModeModeInfo *drm_mode) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < drm_conn->count_modes; ++i) {
|
||||
if (drm_conn->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
|
||||
found = true;
|
||||
*drm_mode = drm_conn->modes[i];
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool drm_connector_commit(struct wlr_output *output) {
|
||||
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
|
||||
struct wlr_drm_backend *drm = conn->backend;
|
||||
|
|
@ -567,6 +590,10 @@ static bool drm_connector_commit(struct wlr_output *output) {
|
|||
}
|
||||
|
||||
if (!drm_connector_set_mode(conn, wlr_mode)) {
|
||||
if (conn->hot_plug_mode) {
|
||||
struct wlr_drm_mode *drm_mode = (struct wlr_drm_mode *)wlr_mode;
|
||||
free(drm_mode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||
|
|
@ -763,7 +790,9 @@ static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) {
|
|||
"Output has a desired mode and a CRTC, requesting a modeset");
|
||||
struct wlr_output_state state = {
|
||||
.committed = WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED,
|
||||
.mode_type = WLR_OUTPUT_STATE_MODE_FIXED,
|
||||
.mode_type = conn->hot_plug_mode
|
||||
? WLR_OUTPUT_STATE_MODE_CUSTOM
|
||||
: WLR_OUTPUT_STATE_MODE_FIXED,
|
||||
.mode = conn->desired_mode,
|
||||
.enabled = true,
|
||||
};
|
||||
|
|
@ -823,7 +852,12 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn,
|
|||
|
||||
conn->state = WLR_DRM_CONN_CONNECTED;
|
||||
conn->desired_mode = NULL;
|
||||
struct wlr_output_mode *previous_mode = conn->output.current_mode;
|
||||
wlr_output_update_mode(&conn->output, wlr_mode);
|
||||
if (previous_mode != NULL && conn->hot_plug_mode) {
|
||||
struct wlr_drm_mode *drm_mode = (struct wlr_drm_mode *)previous_mode;
|
||||
free(drm_mode);
|
||||
}
|
||||
wlr_output_update_enabled(&conn->output, true);
|
||||
conn->desired_enabled = true;
|
||||
|
||||
|
|
@ -834,6 +868,20 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn,
|
|||
return true;
|
||||
}
|
||||
|
||||
struct wlr_drm_mode *wlr_drm_connector_create_mode(struct wlr_output *output,
|
||||
const drmModeModeInfo *modeinfo) {
|
||||
struct wlr_drm_mode *mode = calloc(1, sizeof(*mode));
|
||||
if (!mode) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&mode->drm_mode, modeinfo, sizeof(*modeinfo));
|
||||
|
||||
mode->wlr_mode.width = mode->drm_mode.hdisplay;
|
||||
mode->wlr_mode.height = mode->drm_mode.vdisplay;
|
||||
mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo);
|
||||
return mode;
|
||||
}
|
||||
|
||||
struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output,
|
||||
const drmModeModeInfo *modeinfo) {
|
||||
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
|
||||
|
|
@ -850,15 +898,7 @@ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output,
|
|||
}
|
||||
}
|
||||
|
||||
struct wlr_drm_mode *mode = calloc(1, sizeof(*mode));
|
||||
if (!mode) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&mode->drm_mode, modeinfo, sizeof(*modeinfo));
|
||||
|
||||
mode->wlr_mode.width = mode->drm_mode.hdisplay;
|
||||
mode->wlr_mode.height = mode->drm_mode.vdisplay;
|
||||
mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo);
|
||||
struct wlr_drm_mode *mode = wlr_drm_connector_create_mode(output, modeinfo);
|
||||
|
||||
wlr_drm_conn_log(conn, WLR_INFO, "Registered custom mode "
|
||||
"%"PRId32"x%"PRId32"@%"PRId32,
|
||||
|
|
@ -1020,7 +1060,15 @@ static void drm_connector_destroy_output(struct wlr_output *output) {
|
|||
|
||||
conn->state = WLR_DRM_CONN_DISCONNECTED;
|
||||
conn->desired_enabled = false;
|
||||
if (conn->desired_mode != NULL && conn->hot_plug_mode) {
|
||||
struct wlr_drm_mode *drm_mode = (struct wlr_drm_mode *)conn->desired_mode;
|
||||
free(drm_mode);
|
||||
}
|
||||
conn->desired_mode = NULL;
|
||||
if (output->current_mode != NULL && conn->hot_plug_mode) {
|
||||
struct wlr_drm_mode *drm_mode = (struct wlr_drm_mode *)output->current_mode;
|
||||
free(drm_mode);
|
||||
}
|
||||
conn->possible_crtcs = 0;
|
||||
conn->pending_page_flip_crtc = 0;
|
||||
|
||||
|
|
@ -1186,7 +1234,12 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
|
|||
conn->state = WLR_DRM_CONN_NEEDS_MODESET;
|
||||
wlr_output_update_enabled(&conn->output, false);
|
||||
conn->desired_mode = conn->output.current_mode;
|
||||
struct wlr_output_mode *previous_mode = conn->output.current_mode;
|
||||
wlr_output_update_mode(&conn->output, NULL);
|
||||
if (previous_mode != NULL && conn->hot_plug_mode) {
|
||||
struct wlr_drm_mode *drm_mode = (struct wlr_drm_mode *)previous_mode;
|
||||
free(drm_mode);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1331,6 +1384,44 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
|
|||
}
|
||||
}
|
||||
|
||||
if ((wlr_conn->state == WLR_DRM_CONN_CONNECTED
|
||||
|| wlr_conn->state == WLR_DRM_CONN_NEEDS_MODESET) &&
|
||||
drm_conn->connection == DRM_MODE_CONNECTED &&
|
||||
wlr_conn->hot_plug_mode) {
|
||||
wlr_log(WLR_INFO, "Scanning '%s' for hot plugged mode", wlr_conn->name);
|
||||
drmModeModeInfo drm_mode;
|
||||
if (!drm_connector_get_preferred_mode(drm_conn, &drm_mode)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"%s supports hotplug_mode_update but has no preferred mode",
|
||||
wlr_conn->name);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t width = drm_mode.hdisplay;
|
||||
int32_t height = drm_mode.vdisplay;
|
||||
int32_t refresh = calculate_refresh_rate(&drm_mode);
|
||||
if (width == wlr_conn->output.width &&
|
||||
height == wlr_conn->output.height &&
|
||||
refresh == wlr_conn->output.refresh) {
|
||||
wlr_log(WLR_INFO, "No changes");
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO,
|
||||
"Preferred mode changed to: %"PRId32"x%"PRId32"@%"PRId32"",
|
||||
width, height, refresh);
|
||||
struct wlr_output_state state = {
|
||||
.committed = WLR_OUTPUT_STATE_MODE,
|
||||
.mode_type = WLR_OUTPUT_STATE_MODE_CUSTOM,
|
||||
.custom_mode = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.refresh = refresh,
|
||||
},
|
||||
};
|
||||
wlr_output_send_request_state(&wlr_conn->output, &state);
|
||||
}
|
||||
|
||||
if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED &&
|
||||
drm_conn->connection == DRM_MODE_CONNECTED) {
|
||||
wlr_log(WLR_INFO, "'%s' connected", wlr_conn->name);
|
||||
|
|
@ -1351,6 +1442,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
|
|||
|
||||
get_drm_connector_props(drm->fd, wlr_conn->id, &wlr_conn->props);
|
||||
|
||||
wlr_conn->hot_plug_mode = drm_connector_has_hotplug_mode(wlr_conn);
|
||||
|
||||
size_t edid_len = 0;
|
||||
uint8_t *edid = get_drm_prop_blob(drm->fd,
|
||||
wlr_conn->id, wlr_conn->props.edid, &edid_len);
|
||||
|
|
@ -1376,34 +1469,49 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
|
|||
|
||||
free(subconnector);
|
||||
|
||||
wlr_log(WLR_INFO, "Detected modes:");
|
||||
|
||||
for (int i = 0; i < drm_conn->count_modes; ++i) {
|
||||
struct wlr_drm_mode *mode = calloc(1, sizeof(*mode));
|
||||
if (!mode) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
continue;
|
||||
if (wlr_conn->hot_plug_mode) {
|
||||
drmModeModeInfo drm_mode;
|
||||
if (!drm_connector_get_preferred_mode(drm_conn, &drm_mode)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"%s supports hotplug_mode_update but has no preferred mode",
|
||||
wlr_conn->name);
|
||||
abort();
|
||||
}
|
||||
wlr_log(WLR_INFO, "Detected hot pluggable mode");
|
||||
int32_t width = drm_mode.hdisplay;
|
||||
int32_t height = drm_mode.vdisplay;
|
||||
int32_t refresh = calculate_refresh_rate(&drm_mode);
|
||||
wlr_output_set_custom_mode(output, width, height, refresh);
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "Detected modes:");
|
||||
|
||||
if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
free(mode);
|
||||
continue;
|
||||
for (int i = 0; i < drm_conn->count_modes; ++i) {
|
||||
struct wlr_drm_mode *mode = calloc(1, sizeof(*mode));
|
||||
if (!mode) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
free(mode);
|
||||
continue;
|
||||
}
|
||||
|
||||
mode->drm_mode = drm_conn->modes[i];
|
||||
mode->wlr_mode.width = mode->drm_mode.hdisplay;
|
||||
mode->wlr_mode.height = mode->drm_mode.vdisplay;
|
||||
mode->wlr_mode.refresh = calculate_refresh_rate(&mode->drm_mode);
|
||||
if (mode->drm_mode.type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode->wlr_mode.preferred = true;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, " %"PRId32"x%"PRId32"@%"PRId32" %s",
|
||||
mode->wlr_mode.width, mode->wlr_mode.height,
|
||||
mode->wlr_mode.refresh,
|
||||
mode->wlr_mode.preferred ? "(preferred)" : "");
|
||||
|
||||
wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link);
|
||||
}
|
||||
|
||||
mode->drm_mode = drm_conn->modes[i];
|
||||
mode->wlr_mode.width = mode->drm_mode.hdisplay;
|
||||
mode->wlr_mode.height = mode->drm_mode.vdisplay;
|
||||
mode->wlr_mode.refresh = calculate_refresh_rate(&mode->drm_mode);
|
||||
if (mode->drm_mode.type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode->wlr_mode.preferred = true;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, " %"PRId32"x%"PRId32"@%"PRId32" %s",
|
||||
mode->wlr_mode.width, mode->wlr_mode.height,
|
||||
mode->wlr_mode.refresh,
|
||||
mode->wlr_mode.preferred ? "(preferred)" : "");
|
||||
|
||||
wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link);
|
||||
}
|
||||
|
||||
wlr_conn->possible_crtcs = get_possible_crtcs(drm->fd, res, drm_conn);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ static const struct prop_info connector_info[] = {
|
|||
{ "DPMS", INDEX(dpms) },
|
||||
{ "EDID", INDEX(edid) },
|
||||
{ "PATH", INDEX(path) },
|
||||
{ "hotplug_mode_update", INDEX(hotplug_mode_update) },
|
||||
{ "link-status", INDEX(link_status) },
|
||||
{ "subconnector", INDEX(subconnector) },
|
||||
{ "vrr_capable", INDEX(vrr_capable) },
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ struct wlr_drm_connector {
|
|||
char name[24];
|
||||
enum wlr_drm_connector_state state;
|
||||
struct wlr_output_mode *desired_mode;
|
||||
bool hot_plug_mode;
|
||||
bool desired_enabled;
|
||||
uint32_t id;
|
||||
|
||||
|
|
@ -153,6 +154,11 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn,
|
|||
struct wlr_output_mode *mode);
|
||||
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
|
||||
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn);
|
||||
bool drm_connector_has_hotplug_mode(struct wlr_drm_connector *conn);
|
||||
bool drm_connector_get_preferred_mode(drmModeConnector *drm_conn,
|
||||
drmModeModeInfo *drm_mode);
|
||||
struct wlr_drm_mode *wlr_drm_connector_create_mode(struct wlr_output *output,
|
||||
const drmModeModeInfo *modeinfo);
|
||||
size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_crtc *crtc);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ union wlr_drm_connector_props {
|
|||
uint32_t path;
|
||||
uint32_t vrr_capable; // not guaranteed to exist
|
||||
uint32_t subconnector; // not guaranteed to exist
|
||||
uint32_t hotplug_mode_update; // not guaranteed to exist
|
||||
|
||||
// atomic-modesetting only
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue