From d9238bca6a921fc83573f6cc698b7c1f8ed4a825 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 14 Apr 2026 15:55:28 -0500 Subject: [PATCH] drm: Look for YUV420 capabilities in the mode list For now, we tag modes with their YUV capabilities manually here, and print that. Signed-off-by: Derek Foreman --- libweston/backend-drm/drm-internal.h | 6 ++ libweston/backend-drm/modes.c | 140 ++++++++++++++++++++++----- 2 files changed, 123 insertions(+), 23 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index e040c05cc..99c25e612 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -294,6 +294,8 @@ struct drm_mode { drmModeModeInfo mode_info; uint32_t blob_id; uint8_t vic; + bool can_yuv420; + bool must_yuv420; }; enum drm_fb_type { @@ -577,6 +579,10 @@ struct drm_head { void *display_data; /**< EDID or DisplayID blob */ size_t display_data_len; /**< bytes */ + + bool yuv420_vics; + bool can_yuv420_vic_map[256]; + bool must_yuv420_vic_map[256]; }; struct drm_crtc { diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index 472b2469d..96b8ffb0c 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -57,6 +57,10 @@ struct drm_head_info { /* The monitor supported color foramts, combination of * enum_weston_color_format bits. */ uint32_t color_format_mask; + + bool yuv420_vics; + bool must_yuv420_vic_map[256]; + bool can_yuv420_vic_map[256]; }; static void @@ -309,25 +313,91 @@ get_colorimetry_mask(const struct di_info *info) return mask; } -static bool -has_yuv420_cap_map(const struct di_edid_cta *cta) +/* Much of the yuv420 capability check code is duplicated or barely modified + * from: + * libdisplay-info (https://gitlab.freedesktop.org/emersion/libdisplay-info) + * + * However, bugs should be considered an original work of the Weston project. + */ +static void +add_yuv420_only_block(struct drm_head_info *dhi, + const struct di_edid_cta *cta, + const struct di_cta_data_block *yuv420_data_block) { - const struct di_cta_data_block *const *data_blocks; - enum di_cta_data_block_tag db_tag; - int i; + const struct di_cta_svd *const *svds; + int i; - data_blocks = di_edid_cta_get_data_blocks(cta); - for (i = 0; data_blocks[i] != NULL; i++) { - db_tag = di_cta_data_block_get_tag(data_blocks[i]); - if (db_tag == DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP) - return true; + svds = di_cta_data_block_get_ycbcr420_svds(yuv420_data_block); + if (!svds) + return; + + for (i = 0; svds[i] != NULL; i++) { + dhi->yuv420_vics = true; + dhi->must_yuv420_vic_map[svds[i]->vic] = true; + } +} + +static void +add_map_to_yuv420_caps(struct drm_head_info *dhi, + const struct di_edid_cta *cta, + const struct di_cta_data_block *yuv420_cap_map_block) +{ + const struct di_cta_ycbcr420_cap_map *ycbcr420_cap_map; + const struct di_cta_data_block *const *data_blocks; + const struct di_cta_data_block *data_block; + const struct di_cta_svd *const *svds = NULL; + enum di_cta_data_block_tag db_tag; + int i, j, svd_index = 0; + + ycbcr420_cap_map = di_cta_data_block_get_ycbcr420_cap_map(yuv420_cap_map_block); + + data_blocks = di_edid_cta_get_data_blocks(cta); + for (i = 0; data_blocks[i] != NULL; i++) { + data_block = data_blocks[i]; + db_tag = di_cta_data_block_get_tag(data_block); + if (db_tag != DI_CTA_DATA_BLOCK_VIDEO) + continue; + svds = di_cta_data_block_get_svds(data_block); + + for (j = 0; svds[j] != NULL; j++) { + if (di_cta_ycbcr420_cap_map_supported(ycbcr420_cap_map, svd_index)) { + dhi->yuv420_vics = true; + dhi->can_yuv420_vic_map[svds[j]->vic] = true; + } + svd_index++; + } + } +} + +static void +add_cta_to_yuv420_caps(struct drm_head_info *dhi, + const struct di_edid_cta *cta) +{ + const struct di_cta_data_block *const *data_blocks; + const struct di_cta_data_block *data_block; + enum di_cta_data_block_tag db_tag; + int i; + + data_blocks = di_edid_cta_get_data_blocks(cta); + for (i = 0; data_blocks[i] != NULL; i++) { + data_block = data_blocks[i]; + db_tag = di_cta_data_block_get_tag(data_block); + switch (db_tag) { + case DI_CTA_DATA_BLOCK_YCBCR420: + add_yuv420_only_block(dhi, cta, data_block); + break; + case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP: + add_map_to_yuv420_caps(dhi, cta, data_block); + break; + default: + break; + } } - - return false; } static uint32_t -get_color_format_mask(const struct di_info *info) +get_color_format_mask(struct drm_head_info *dhi, + const struct di_info *info) { const struct di_edid *edid = di_info_get_edid(info); const struct di_edid_color_encoding_formats *fmts = @@ -364,18 +434,16 @@ get_color_format_mask(const struct di_info *info) if (cta_flags->ycc422) mask |= WESTON_COLOR_FORMAT_YUV422; - /* FIXME: YUV420 detection is really complicated, - * do it properly at some point... - */ - if (has_yuv420_cap_map(cta)) - mask |= WESTON_COLOR_FORMAT_YUV420; - - break; + add_cta_to_yuv420_caps(dhi, cta); + break; default: break; } } + if (dhi->yuv420_vics) + mask |= WESTON_COLOR_FORMAT_YUV420; + return mask; } @@ -395,6 +463,10 @@ drm_head_info_from_edid(struct drm_head_info *dhi, return NULL; } + dhi->yuv420_vics = 0; + memset(dhi->can_yuv420_vic_map, 0, sizeof(dhi->can_yuv420_vic_map)); + memset(dhi->must_yuv420_vic_map, 0, sizeof(dhi->must_yuv420_vic_map)); + msg = di_info_get_failure_msg(di_ctx); if (msg) weston_log("DRM: EDID for the following head fails conformity:\n%s\n", msg); @@ -404,7 +476,7 @@ drm_head_info_from_edid(struct drm_head_info *dhi, dhi->serial_number = di_info_get_serial(di_ctx); dhi->eotf_mask = get_eotf_mask(di_ctx); dhi->colorimetry_mask = get_colorimetry_mask(di_ctx); - dhi->color_format_mask = get_color_format_mask(di_ctx); + dhi->color_format_mask = get_color_format_mask(dhi, di_ctx); return di_ctx; } @@ -622,6 +694,7 @@ match_cta_mode(const drmModeModeInfo *info) static struct drm_mode * drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) { + struct drm_head *head = to_drm_head(weston_output_get_first_head(&output->base)); struct drm_mode *mode; mode = malloc(sizeof *mode); @@ -638,6 +711,14 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) mode->vic = match_cta_mode(info); + mode->can_yuv420 = false; + mode->must_yuv420 = false; + if (mode->vic && head->can_yuv420_vic_map[mode->vic]) + mode->can_yuv420 = true; + + if (mode->vic && head->must_yuv420_vic_map[mode->vic]) + mode->must_yuv420 = false; + if (info->type & DRM_MODE_TYPE_PREFERRED) mode->base.flags |= WL_OUTPUT_MODE_PREFERRED; @@ -682,6 +763,8 @@ drm_output_print_modes(struct drm_output *output) const char *aspect_ratio; wl_list_for_each(m, &output->base.mode_list, link) { + const char *yuv_cap_str; + dm = to_drm_mode(m); aspect_ratio = aspect_ratio_to_string(m->aspect_ratio); @@ -690,14 +773,21 @@ drm_output_print_modes(struct drm_output *output) else weston_log_continue(STAMP_SPACE " "); - weston_log_continue("%s@%.1f%s%s%s, %.1f MHz\n", + yuv_cap_str = NULL; + if (dm->must_yuv420) + yuv_cap_str = ", YUV420 only"; + else if (dm->can_yuv420) + yuv_cap_str = ", YUV420 optional"; + + weston_log_continue("%s@%.1f%s%s%s, %.1f MHz%s\n", dm->mode_info.name, m->refresh / 1000.0, aspect_ratio, m->flags & WL_OUTPUT_MODE_PREFERRED ? ", preferred" : "", m->flags & WL_OUTPUT_MODE_CURRENT ? ", current" : "", - dm->mode_info.clock / 1000.0); + dm->mode_info.clock / 1000.0, + yuv_cap_str ?: ""); } } @@ -828,6 +918,10 @@ update_head_from_connector(struct drm_head *head) vrr_mode_mask = WESTON_VRR_MODE_GAME; weston_head_set_supported_vrr_modes_mask(&head->base, vrr_mode_mask); + head->yuv420_vics = dhi.yuv420_vics; + memcpy(&head->can_yuv420_vic_map, &dhi.can_yuv420_vic_map, sizeof(head->can_yuv420_vic_map)); + memcpy(&head->must_yuv420_vic_map, &dhi.must_yuv420_vic_map, sizeof(head->must_yuv420_vic_map)); + drm_head_info_fini(&dhi); }