From 96bef0517ec5318c6c253f27fc2f4aeb289bad9b Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Wed, 9 Sep 2020 15:23:49 -0300 Subject: [PATCH] drm-backend: add support for writeback connectors Recognize writeback connectors and add 'struct drm_writeback' objects in order to store them. These objects are created and stored in a list by the time that DRM-backend is initialized. This list is updated if a writeback connector dynamically appears or is disconnected. Signed-off-by: Leandro Ribeiro --- libweston/backend-drm/drm-internal.h | 11 ++ libweston/backend-drm/drm.c | 147 +++++++++++++++++++++++++-- libweston/backend-drm/kms.c | 2 + 3 files changed, 149 insertions(+), 11 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index dd216368..6d491576 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -278,6 +278,9 @@ struct drm_backend { /* drm_crtc::link */ struct wl_list crtc_list; + /* drm_writeback::link */ + struct wl_list writeback_connector_list; + bool sprites_are_broken; bool cursors_are_broken; @@ -478,6 +481,14 @@ struct drm_connector { struct drm_property_info props[WDRM_CONNECTOR__COUNT]; }; +struct drm_writeback { + /* drm_backend::writeback_connector_list */ + struct wl_list link; + + struct drm_backend *backend; + struct drm_connector connector; +}; + struct drm_head { struct weston_head base; struct drm_backend *backend; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 1d0ca10b..e67ae25f 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -220,6 +220,19 @@ drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id) return NULL; } +static struct drm_writeback * +drm_writeback_find_by_connector(struct drm_backend *backend, uint32_t connector_id) +{ + struct drm_writeback *writeback; + + wl_list_for_each(writeback, &backend->writeback_connector_list, link) { + if (writeback->connector.connector_id == connector_id) + return writeback; + } + + return NULL; +} + /** * Get output state to disable output * @@ -2101,6 +2114,24 @@ drm_head_update_info(struct drm_head *head, drmModeConnector *conn) return ret; } +/** Update writeback connector + * + * @param writeback The writeback to update. + * @param conn DRM connector object. + * @returns 0 on success, -1 on failure. + * + * Takes ownership of @c connector on success, not on failure. + */ +static int +drm_writeback_update_info(struct drm_writeback *writeback, drmModeConnector *conn) +{ + int ret; + + ret = drm_connector_assign_connector_info(&writeback->connector, conn); + + return ret; +} + /** * Create a Weston head for a connector * @@ -2227,9 +2258,57 @@ drm_output_create(struct weston_compositor *compositor, const char *name) return &output->base; } -/** Given the DRM connector object of a connector, create drm_head for it. +/** + * Create a Weston writeback for a writeback connector + * + * Given a DRM connector of type writeback, create a matching drm_writeback + * structure and add it to Weston's writeback list. + * + * @param b Weston backend structure + * @param conn DRM connector object of type writeback + * @returns 0 on success, -1 on failure + * + * Takes ownership of @c connector on success, not on failure. + */ +static int +drm_writeback_create(struct drm_backend *b, drmModeConnector *conn) +{ + struct drm_writeback *writeback; + int ret; + + writeback = zalloc(sizeof *writeback); + assert(writeback); + + writeback->backend = b; + + drm_connector_init(b, &writeback->connector, conn->connector_id); + + ret = drm_writeback_update_info(writeback, conn); + if (ret < 0) + goto err; + + wl_list_insert(&b->writeback_connector_list, &writeback->link); + return 0; + +err: + drm_connector_fini(&writeback->connector); + free(writeback); + return -1; +} + +static void +drm_writeback_destroy(struct drm_writeback *writeback) +{ + drm_connector_fini(&writeback->connector); + wl_list_remove(&writeback->link); + + free(writeback); +} + +/** Given the DRM connector object of a connector, create drm_head or + * drm_writeback object (depending on the type of connector) for it. * - * The object is then added to the DRM-backend list of heads. + * The object is then added to the DRM-backend list of heads or writebacks. * * @param b The DRM-backend structure * @param conn The DRM connector object @@ -2242,16 +2321,32 @@ drm_backend_add_connector(struct drm_backend *b, drmModeConnector *conn, { int ret; - ret = drm_head_create(b, conn, drm_device); - if (ret < 0) - weston_log("DRM: failed to create head for connector %d.\n", - conn->connector_id); + if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { + ret = drm_writeback_create(b, conn); + if (ret < 0) + weston_log("DRM: failed to create writeback for connector %d.\n", + conn->connector_id); + } else { + ret = drm_head_create(b, conn, drm_device); + if (ret < 0) + weston_log("DRM: failed to create head for connector %d.\n", + conn->connector_id); + } return ret; } +/** Find all connectors of the fd and create drm_head or drm_writeback objects + * (depending on the type of connector they are) for each of them + * + * These objects are added to the DRM-backend lists of heads and writebacks. + * + * @param b The DRM-backend structure + * @param drm_device udev device pointer + * @return 0 on success, -1 on failure + */ static int -drm_backend_create_heads(struct drm_backend *b, struct udev_device *drm_device) +drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_device) { drmModeRes *resources; drmModeConnector *conn; @@ -2301,8 +2396,9 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev { drmModeRes *resources; drmModeConnector *conn; - struct weston_head *base, *next; + struct weston_head *base, *base_next; struct drm_head *head; + struct drm_writeback *writeback, *writeback_next; uint32_t connector_id; int i, ret; @@ -2321,8 +2417,16 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev continue; head = drm_head_find_by_connector(b, connector_id); + writeback = drm_writeback_find_by_connector(b, connector_id); + + /* Connector can't be owned by both a head and a writeback, so + * one of the searches must fail. */ + assert(head == NULL || writeback == NULL); + if (head) ret = drm_head_update_info(head, conn); + else if (writeback) + ret = drm_writeback_update_info(writeback, conn); else ret = drm_backend_add_connector(b, conn, drm_device); @@ -2330,8 +2434,9 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev drmModeFreeConnector(conn); } - /* Remove connectors that have disappeared. */ - wl_list_for_each_safe(base, next, + /* Destroy head objects of connectors (except writeback connectors) that + * have disappeared. */ + wl_list_for_each_safe(base, base_next, &b->compositor->head_list, compositor_link) { head = to_drm_head(base); connector_id = head->connector.connector_id; @@ -2344,6 +2449,20 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev drm_head_destroy(head); } + /* Destroy writeback objects of writeback connectors that have + * disappeared. */ + wl_list_for_each_safe(writeback, writeback_next, + &b->writeback_connector_list, link) { + connector_id = writeback->connector.connector_id; + + if (resources_has_connector(resources, connector_id)) + continue; + + weston_log("DRM: writeback connector (connector %d) disappeared.\n", + connector_id); + drm_writeback_destroy(writeback); + } + drmModeFreeResources(resources); } @@ -2462,6 +2581,7 @@ drm_destroy(struct weston_compositor *ec) struct drm_backend *b = to_drm_backend(ec); struct weston_head *base, *next; struct drm_crtc *crtc, *crtc_tmp; + struct drm_writeback *writeback, *writeback_tmp; udev_input_destroy(&b->input); @@ -2482,6 +2602,10 @@ drm_destroy(struct weston_compositor *ec) wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) drm_head_destroy(to_drm_head(base)); + wl_list_for_each_safe(writeback, writeback_tmp, + &b->writeback_connector_list, link) + drm_writeback_destroy(writeback); + #ifdef BUILD_DRM_GBM if (b->gbm) gbm_device_destroy(b->gbm); @@ -2991,7 +3115,8 @@ drm_backend_create(struct weston_compositor *compositor, goto err_sprite; } - if (drm_backend_create_heads(b, drm_device) < 0) { + wl_list_init(&b->writeback_connector_list); + if (drm_backend_discover_connectors(b, drm_device) < 0) { weston_log("Failed to create heads for %s\n", b->drm.filename); goto err_udev_input; } diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index d508ab5c..f2895069 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1487,6 +1487,8 @@ init_kms_caps(struct drm_backend *b) weston_log("DRM: %s GBM modifiers\n", b->fb_modifiers ? "supports" : "does not support"); + drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); + /* * KMS support for hardware planes cannot properly synchronize * without nuclear page flip. Without nuclear/atomic, hw plane