drm backend: implement DRM lease issuance

This commit is contained in:
Drew DeVault 2019-06-27 15:23:50 -04:00
parent 05ed202183
commit 1b96ba8232
2 changed files with 168 additions and 10 deletions

View File

@ -269,6 +269,7 @@ bool init_drm_resources(struct wlr_drm_backend *drm) {
struct wlr_drm_crtc *crtc = &drm->crtcs[i];
crtc->id = res->crtcs[i];
crtc->legacy_crtc = drmModeGetCrtc(drm->fd, crtc->id);
crtc->lessee_id = 0;
get_drm_crtc_props(drm->fd, crtc->id, &crtc->props);
}
@ -578,6 +579,8 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn);
bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert(conn->state != WLR_DRM_CONN_LEASED
&& "Use of leased connector is a programming error");
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (conn->crtc == NULL) {
// Maybe we can steal a CRTC from a disabled output
@ -618,6 +621,8 @@ bool drm_connector_set_mode(struct wlr_output *output,
bool wlr_drm_connector_add_mode(struct wlr_output *output,
const drmModeModeInfo *modeinfo) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert(conn->state != WLR_DRM_CONN_LEASED
&& "Use of leased connector is a programming error");
if (modeinfo->type != DRM_MODE_TYPE_USERDEF) {
return false;
@ -654,6 +659,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert((conn->state != WLR_DRM_CONN_LEASED || texture == NULL)
&& "Use of leased connector is a programming error");
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
struct wlr_drm_crtc *crtc = conn->crtc;
@ -791,6 +798,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
static bool drm_connector_move_cursor(struct wlr_output *output,
int x, int y) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert(conn->state != WLR_DRM_CONN_LEASED
&& "Use of leased connector is a programming error");
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (!conn->crtc) {
return false;
@ -827,6 +836,8 @@ static bool drm_connector_move_cursor(struct wlr_output *output,
static bool drm_connector_schedule_frame(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert(conn->state != WLR_DRM_CONN_LEASED
&& "Use of leased connector is a programming error");
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (!drm->session->active) {
return false;
@ -878,6 +889,8 @@ static uint32_t strip_alpha_channel(uint32_t format) {
static bool drm_connector_attach_buffer(struct wlr_output *output,
struct wlr_buffer *buffer) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
assert(conn->state != WLR_DRM_CONN_LEASED
&& "Use of leased connector is a programming error");
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (!drm->session->active) {
return false;
@ -919,6 +932,9 @@ static bool drm_connector_attach_buffer(struct wlr_output *output,
static void drm_connector_destroy(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
if (conn->state == WLR_DRM_CONN_LEASED) {
return;
}
drm_connector_cleanup(conn);
drmModeFreeCrtc(conn->old_crtc);
wl_event_source_remove(conn->retry_pageflip);
@ -996,10 +1012,17 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
uint32_t new_match[drm->num_crtcs];
for (size_t i = 0; i < drm->num_crtcs; ++i) {
previous_match[i] = UNMATCHED;
if (drm->crtcs[i].lessee_id != 0) {
/* Do not consider leased CRTCs */
previous_match[i] = SKIP;
} else {
previous_match[i] = UNMATCHED;
}
}
wlr_log(WLR_DEBUG, "State before reallocation:");
wlr_log(WLR_DEBUG, "noutputs: %zd; ncrtc: %zd",
num_outputs, drm->num_crtcs);
size_t i = 0;
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->outputs, link) {
@ -1037,7 +1060,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
connector_match[i] = -1;
}
for (size_t i = 0; i < drm->num_crtcs; ++i) {
if (new_match[i] != UNMATCHED) {
if (new_match[i] != UNMATCHED && new_match[i] != SKIP) {
connector_match[new_match[i]] = i;
}
}
@ -1149,6 +1172,15 @@ static uint32_t get_possible_crtcs(int fd, drmModeRes *res,
return ret;
}
static void drm_conn_init_wlr_output(struct wlr_drm_backend *drm,
struct wlr_drm_connector *wlr_conn, drmModeConnector *drm_conn) {
wlr_output_init(&wlr_conn->output,
&drm->backend, &output_impl, drm->display);
snprintf(wlr_conn->output.name, sizeof(wlr_conn->output.name),
"%s-%"PRIu32, conn_get_name(drm_conn->connector_type),
drm_conn->connector_type_id);
}
void scan_drm_connectors(struct wlr_drm_backend *drm) {
/*
* This GPU is not really a modesetting device.
@ -1174,6 +1206,14 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
size_t new_outputs_len = 0;
struct wlr_drm_connector *new_outputs[res->count_connectors + 1];
drmModeLesseeListPtr lessees = drmModeListLessees(drm->fd);
if (lessees->count > 0) {
wlr_log(WLR_INFO, "With active leases:");
for (uint32_t i = 0; i < lessees->count; ++i) {
wlr_log(WLR_INFO, "%d", lessees->lessees[i]);
}
}
for (int i = 0; i < res->count_connectors; ++i) {
drmModeConnector *drm_conn = drmModeGetConnector(drm->fd,
res->connectors[i]);
@ -1202,8 +1242,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
drmModeFreeConnector(drm_conn);
continue;
}
wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl,
drm->display);
drm_conn_init_wlr_output(drm, wlr_conn, drm_conn);
struct wl_event_loop *ev = wl_display_get_event_loop(drm->display);
wlr_conn->retry_pageflip = wl_event_loop_add_timer(
@ -1212,16 +1252,13 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
wlr_conn->state = WLR_DRM_CONN_DISCONNECTED;
wlr_conn->id = drm_conn->connector_id;
snprintf(wlr_conn->output.name, sizeof(wlr_conn->output.name),
"%s-%"PRIu32, conn_get_name(drm_conn->connector_type),
drm_conn->connector_type_id);
if (curr_enc) {
wlr_conn->old_crtc = drmModeGetCrtc(drm->fd, curr_enc->crtc_id);
}
wl_list_insert(drm->outputs.prev, &wlr_conn->link);
wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->output.name);
wlr_log(WLR_INFO, "Found connector '%s' (%d)",
wlr_conn->output.name, drm_conn->connector_id);
} else {
seen[index] = true;
}
@ -1255,6 +1292,33 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
}
}
if (wlr_conn->state == WLR_DRM_CONN_LEASED) {
/* Was the lease terminated? */
bool found = false;
for (uint32_t n = 0; n < lessees->count; ++n) {
if (lessees->lessees[n] == wlr_conn->lessee_id) {
found = true;
}
}
if (!found) {
/* Yes, it was */
wlr_log(WLR_DEBUG, "DRM lease %d terminated by kernel",
wlr_conn->lessee_id);
wlr_conn->state = WLR_DRM_CONN_DISCONNECTED;
wlr_conn->lessee_id = 0;
for (size_t i = 0; i < drm->num_crtcs; ++i) {
if (drm->crtcs[i].lessee_id == wlr_conn->lessee_id) {
drm->crtcs[i].lessee_id = 0;
}
}
if (wlr_conn->lease_terminated_cb != NULL) {
wlr_conn->lease_terminated_cb(
wlr_conn, wlr_conn->lease_terminated_data);
}
drm_conn_init_wlr_output(drm, wlr_conn, drm_conn);
}
}
if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED &&
drm_conn->connection == DRM_MODE_CONNECTED) {
wlr_log(WLR_INFO, "'%s' connected", wlr_conn->output.name);
@ -1476,7 +1540,7 @@ void restore_drm_outputs(struct wlr_drm_backend *drm) {
wl_list_for_each(conn, &drm->outputs, link) {
drmModeCrtc *crtc = conn->old_crtc;
if (!crtc) {
if (!crtc || conn->state == WLR_DRM_CONN_LEASED) {
continue;
}
@ -1531,7 +1595,87 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
break;
case WLR_DRM_CONN_DISCONNECTED:
break;
case WLR_DRM_CONN_LEASED:
return; // don't change state
}
conn->state = WLR_DRM_CONN_DISCONNECTED;
}
int drm_create_lease(struct wlr_drm_backend *backend,
struct wlr_drm_connector **conns, int nconns, uint32_t *lessee_id,
void (*lease_terminated_cb)(struct wlr_drm_connector *, void *),
void *lease_terminated_data) {
int nobjects = 0;
for (int i = 0; i < nconns; ++i) {
struct wlr_drm_connector *conn = conns[i];
assert(conn->state != WLR_DRM_CONN_LEASED);
nobjects += 0
+ 1 /* connector */
+ 1 /* crtc */
+ 1 /* primary plane */
+ (conn->crtc->cursor != NULL ? 1 : 0) /* cursor plane */
+ conn->crtc->num_overlays; /* overlay planes */
}
if (nobjects <= 0) {
wlr_log(WLR_ERROR, "Attempted DRM lease with <= 0 objects");
return -1;
}
wlr_log(WLR_DEBUG, "Issuing DRM lease with the %d objects:", nobjects);
uint32_t objects[nobjects + 1];
for (int i = 0, j = 0; i < nconns; ++i) {
struct wlr_drm_connector *conn = conns[i];
objects[j++] = conn->id;
objects[j++] = conn->crtc->id;
objects[j++] = conn->crtc->primary->id;
wlr_log(WLR_DEBUG, "connector: %d crtc: %d primary plane: %d",
conn->id, conn->crtc->id, conn->crtc->primary->id);
if (conn->crtc->cursor) {
wlr_log(WLR_DEBUG, "cursor plane: %d", conn->crtc->cursor->id);
objects[j++] = conn->crtc->cursor->id;
}
if (conn->crtc->num_overlays > 0) {
wlr_log(WLR_DEBUG, "+%zd overlay planes:", conn->crtc->num_overlays);
}
for (size_t k = 0; k < conn->crtc->num_overlays; ++k) {
objects[j++] = conn->crtc->overlays[k];
wlr_log(WLR_DEBUG, "\toverlay plane: %d", conn->crtc->overlays[k]);
}
}
int lease_fd = drmModeCreateLease(backend->fd,
objects, nobjects, 0, lessee_id);
if (lease_fd < 0) {
return lease_fd;
}
wlr_log(WLR_DEBUG, "Issued DRM lease %d", *lessee_id);
for (int i = 0; i < nconns; ++i) {
struct wlr_drm_connector *conn = conns[i];
conn->lessee_id = *lessee_id;
conn->crtc->lessee_id = *lessee_id;
conn->state = WLR_DRM_CONN_LEASED;
conn->lease_terminated_cb = lease_terminated_cb;
conn->lease_terminated_data = lease_terminated_data;
wlr_output_destroy(&conn->output);
}
return lease_fd;
}
int drm_terminate_lease(struct wlr_drm_backend *backend, uint32_t lessee_id) {
wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lessee_id);
int r = drmModeRevokeLease(backend->fd, lessee_id);
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &backend->outputs, link) {
if (conn->state == WLR_DRM_CONN_LEASED
&& conn->lessee_id == lessee_id) {
conn->state = WLR_DRM_CONN_DISCONNECTED;
conn->lessee_id = 0;
}
}
for (size_t i = 0; i < backend->num_crtcs; ++i) {
if (backend->crtcs[i].lessee_id == lessee_id) {
backend->crtcs[i].lessee_id = 0;
}
}
scan_drm_connectors(backend);
return r;
}

View File

@ -47,6 +47,8 @@ struct wlr_drm_crtc {
// Legacy only
drmModeCrtc *legacy_crtc;
uint32_t lessee_id;
struct wlr_drm_plane *primary;
struct wlr_drm_plane *cursor;
@ -98,6 +100,8 @@ enum wlr_drm_connector_state {
WLR_DRM_CONN_NEEDS_MODESET,
WLR_DRM_CONN_CLEANUP,
WLR_DRM_CONN_CONNECTED,
// Connector has been leased to another DRM master
WLR_DRM_CONN_LEASED,
};
struct wlr_drm_mode {
@ -112,6 +116,9 @@ struct wlr_drm_connector {
struct wlr_output_mode *desired_mode;
bool desired_enabled;
uint32_t id;
uint32_t lessee_id;
void (*lease_terminated_cb)(struct wlr_drm_connector *, void *);
void *lease_terminated_data;
struct wlr_drm_crtc *crtc;
uint32_t possible_crtc;
@ -149,4 +156,11 @@ bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode);
/** Returns the leased file descriptor */
int drm_create_lease(struct wlr_drm_backend *backend,
struct wlr_drm_connector **conns, int nconns, uint32_t *lessee_id,
void (*lease_terminated_cb)(struct wlr_drm_connector *, void *),
void *lease_terminated_data);
int drm_terminate_lease(struct wlr_drm_backend *backend, uint32_t lessee_id);
#endif