From d24adbbe258a53d91ed3ec369e850c8c732b8f07 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 21 Jul 2021 17:45:14 +0300 Subject: [PATCH] backend-drm: set connector max bpc "max bpc" property is meant for working around faulty sink hardware. Normally it should be set to the maximum possible value so that the kernel driver has full freedom to choose the link bpc without being artificially forced to lower color precision. The default value is 16 because that is a nice round number and more than any link technology I've heard is using today which would be 12. Also offer an API set the value, so that weston.ini could be used in the next patch for sink workaround purposes. Closes: https://gitlab.freedesktop.org/wayland/weston/-/issues/612 Signed-off-by: Pekka Paalanen --- include/libweston/backend-drm.h | 14 ++++++++++++++ libweston/backend-drm/drm-internal.h | 3 +++ libweston/backend-drm/drm.c | 13 +++++++++++++ libweston/backend-drm/kms.c | 27 +++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/include/libweston/backend-drm.h b/include/libweston/backend-drm.h index 02038e6f..548940ab 100644 --- a/include/libweston/backend-drm.h +++ b/include/libweston/backend-drm.h @@ -78,6 +78,20 @@ struct weston_drm_output_api { */ void (*set_seat)(struct weston_output *output, const char *seat); + + /** Set the "max bpc" KMS connector property + * + * The property is used for working around faulty sink hardware like + * monitors or media converters that mishandle the kernel driver + * chosen bits-per-channel on the physical link. When having trouble, + * try a lower value like 8. + * + * The value actually used in KMS is silently clamped to the range the + * KMS driver claims to support. The default value is 16. + * + * This can be set only while the output is disabled. + */ + void (*set_max_bpc)(struct weston_output *output, unsigned max_bpc); }; static inline const struct weston_drm_output_api * diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 60ab2352..3fc5f9b9 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -187,6 +187,7 @@ enum wdrm_connector_property { WDRM_CONNECTOR_HDCP_CONTENT_TYPE, WDRM_CONNECTOR_PANEL_ORIENTATION, WDRM_CONNECTOR_HDR_OUTPUT_METADATA, + WDRM_CONNECTOR_MAX_BPC, WDRM_CONNECTOR__COUNT }; @@ -559,6 +560,8 @@ struct drm_output { uint32_t hdr_output_metadata_blob_id; uint64_t ackd_color_outcome_serial; + unsigned max_bpc; + /* Plane being displayed directly on the CRTC */ struct drm_plane *scanout_plane; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index f06d8c72..ffb584ad 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1389,6 +1389,17 @@ drm_output_set_seat(struct weston_output *base, seat ? seat : ""); } +static void +drm_output_set_max_bpc(struct weston_output *base, unsigned max_bpc) +{ + struct drm_output *output = to_drm_output(base); + + assert(output); + assert(!output->base.enabled); + + output->max_bpc = max_bpc; +} + static int drm_output_init_gamma_size(struct drm_output *output) { @@ -2278,6 +2289,7 @@ drm_output_create(struct weston_compositor *compositor, const char *name) output->device = device; output->crtc = NULL; + output->max_bpc = 16; output->gbm_format = DRM_FORMAT_INVALID; #ifdef BUILD_DRM_GBM output->gbm_bo_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; @@ -3055,6 +3067,7 @@ static const struct weston_drm_output_api api = { drm_output_set_mode, drm_output_set_gbm_format, drm_output_set_seat, + drm_output_set_max_bpc, }; static struct drm_backend * diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index d1be5dbe..f6629a5d 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -146,6 +146,7 @@ const struct drm_property_info connector_props[] = { [WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = { .name = "HDR_OUTPUT_METADATA", }, + [WDRM_CONNECTOR_MAX_BPC] = { .name = "max bpc", }, }; const struct drm_property_info crtc_props[] = { @@ -905,6 +906,30 @@ drm_connector_set_hdcp_property(struct drm_connector *connector, assert(ret == 0); } +static int +drm_connector_set_max_bpc(struct drm_connector *connector, + struct drm_output *output, + drmModeAtomicReq *req) +{ + const struct drm_property_info *info; + uint64_t max_bpc; + uint64_t a, b; + + if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC)) + return 0; + + info = &connector->props[WDRM_CONNECTOR_MAX_BPC]; + assert(info->flags & DRM_MODE_PROP_RANGE); + assert(info->num_range_values == 2); + a = info->range_values[0]; + b = info->range_values[1]; + assert(a <= b); + + max_bpc = MAX(a, MIN(output->max_bpc, b)); + return connector_add_prop(req, connector, + WDRM_CONNECTOR_MAX_BPC, max_bpc); +} + static int drm_output_apply_state_atomic(struct drm_output_state *state, drmModeAtomicReq *req, @@ -965,6 +990,8 @@ drm_output_apply_state_atomic(struct drm_output_state *state, WDRM_CONNECTOR_HDR_OUTPUT_METADATA, output->hdr_output_metadata_blob_id); } + + ret |= drm_connector_set_max_bpc(&head->connector, output, req); } if (ret != 0) {