mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2026-05-07 06:08:05 +02:00
Merge branch 'YUV420' into 'main'
Draft: Check drm modes for YUV420 capabilities See merge request wayland/weston!2044
This commit is contained in:
commit
b52234349e
3 changed files with 236 additions and 29 deletions
|
|
@ -2270,16 +2270,15 @@ wet_output_set_color_format(struct weston_output *output,
|
|||
|
||||
entry = weston_enum_map_find_name(color_formats, str);
|
||||
if (!entry) {
|
||||
char *mask_to_str = NULL;
|
||||
unsigned int i;
|
||||
|
||||
weston_log("Error in config for output '%s': '%s' is not a "
|
||||
"valid color format. Try one of: ",
|
||||
output->name, str);
|
||||
|
||||
mask_to_str = bits_to_str(WESTON_COLOR_FORMAT_ALL_MASK,
|
||||
weston_color_format_to_str);
|
||||
weston_log_continue("%s\n", mask_to_str);
|
||||
|
||||
free(mask_to_str);
|
||||
for (i = 0; i < ARRAY_LENGTH(color_formats); i++)
|
||||
weston_log_continue(" %s", color_formats[i].name);
|
||||
weston_log_continue("\n");
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,6 +293,9 @@ struct drm_mode {
|
|||
struct weston_mode base;
|
||||
drmModeModeInfo mode_info;
|
||||
uint32_t blob_id;
|
||||
uint8_t vic;
|
||||
bool can_yuv420;
|
||||
bool must_yuv420;
|
||||
};
|
||||
|
||||
enum drm_fb_type {
|
||||
|
|
@ -576,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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -100,6 +104,27 @@ drm_to_weston_mode_aspect_ratio(uint32_t drm_mode_flags)
|
|||
}
|
||||
}
|
||||
|
||||
static enum di_cta_video_format_picture_aspect_ratio
|
||||
drm_to_cta_aspect_ratio(uint32_t drm_mode_flags, bool *valid)
|
||||
{
|
||||
*valid = true;
|
||||
|
||||
switch (drm_mode_flags & DRM_MODE_FLAG_PIC_AR_MASK) {
|
||||
case DRM_MODE_FLAG_PIC_AR_4_3:
|
||||
return DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3;
|
||||
case DRM_MODE_FLAG_PIC_AR_16_9:
|
||||
return DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9;
|
||||
case DRM_MODE_FLAG_PIC_AR_64_27:
|
||||
return DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27;
|
||||
case DRM_MODE_FLAG_PIC_AR_256_135:
|
||||
return DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135;
|
||||
case DRM_MODE_FLAG_PIC_AR_NONE:
|
||||
default:
|
||||
*valid = false;
|
||||
return DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
aspect_ratio_to_string(enum weston_mode_aspect_ratio ratio)
|
||||
{
|
||||
|
|
@ -288,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 =
|
||||
|
|
@ -343,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;
|
||||
}
|
||||
|
||||
|
|
@ -374,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);
|
||||
|
|
@ -383,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;
|
||||
}
|
||||
|
|
@ -509,6 +602,85 @@ drm_refresh_rate_mHz(const drmModeModeInfo *info)
|
|||
return refresh;
|
||||
}
|
||||
|
||||
static bool
|
||||
drmmodeinfo_is_fmt(const drmModeModeInfo *info,
|
||||
const struct di_cta_video_format *fmt)
|
||||
{
|
||||
int vfront = info->vsync_start - info->vdisplay;
|
||||
int vsync = info->vsync_end - info->vsync_start;
|
||||
int vback = info->vtotal - info->vsync_end;
|
||||
int hfront = info->hsync_start - info->hdisplay;
|
||||
int hsync = info->hsync_end - info->hsync_start;
|
||||
int hback = info->htotal - info->hsync_end;
|
||||
uint32_t fmt_clock_khz = fmt->pixel_clock_hz / 1000;
|
||||
enum di_cta_video_format_sync_polarity hpol, vpol;
|
||||
bool interlaced = !!(info->flags & DRM_MODE_FLAG_INTERLACE);
|
||||
enum di_cta_video_format_picture_aspect_ratio info_ar;
|
||||
bool valid_ar;
|
||||
|
||||
if (info->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
hpol = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE;
|
||||
else if (info->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
hpol = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (info->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
vpol = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE;
|
||||
else if (info->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
vpol = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE;
|
||||
else
|
||||
return false;
|
||||
|
||||
info_ar = drm_to_cta_aspect_ratio(info->flags, &valid_ar);
|
||||
if (!valid_ar)
|
||||
return false;
|
||||
|
||||
/* The clock match is imprecise because it's already rounded off
|
||||
* in drmModeModeInfo.
|
||||
*/
|
||||
if (info->hdisplay != fmt->h_active ||
|
||||
info->vdisplay != fmt->v_active ||
|
||||
hfront != fmt->h_front ||
|
||||
vfront != fmt->v_front ||
|
||||
hsync != fmt->h_sync ||
|
||||
vsync != fmt->v_sync ||
|
||||
hback != fmt->h_back ||
|
||||
vback != fmt->v_back ||
|
||||
info->clock != fmt_clock_khz ||
|
||||
hpol != fmt->h_sync_polarity ||
|
||||
vpol != fmt->v_sync_polarity ||
|
||||
interlaced != fmt->interlaced ||
|
||||
info_ar != fmt->picture_aspect_ratio)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
match_cta_mode(const drmModeModeInfo *info)
|
||||
{
|
||||
const struct di_cta_video_format *fmt;
|
||||
uint8_t vic;
|
||||
|
||||
/* FIXME: Some day we'll probably get this from libdi high-
|
||||
* level api, but for now we kludge it here.
|
||||
*
|
||||
* The stop at 219 works around an off by one bug in some
|
||||
* releases of libdi, and needs to be 255 when we can
|
||||
* bump version to ensure the fix.
|
||||
*/
|
||||
for (vic = 0; vic < 220; vic++) {
|
||||
fmt = di_cta_video_format_from_vic(vic);
|
||||
if (!fmt)
|
||||
continue;
|
||||
assert(fmt->vic == vic);
|
||||
if (drmmodeinfo_is_fmt(info, fmt))
|
||||
return vic;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mode to output's mode list
|
||||
*
|
||||
|
|
@ -522,6 +694,7 @@ drm_refresh_rate_mHz(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);
|
||||
|
|
@ -536,6 +709,16 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
|||
mode->mode_info = *info;
|
||||
mode->blob_id = 0;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -580,17 +763,31 @@ 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);
|
||||
weston_log_continue(STAMP_SPACE "%s@%.1f%s%s%s, %.1f MHz\n",
|
||||
if (dm->vic)
|
||||
weston_log_continue(STAMP_SPACE "VIC %3d, ", dm->vic);
|
||||
else
|
||||
weston_log_continue(STAMP_SPACE " ");
|
||||
|
||||
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 ?: "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue