edid: add support for CVT timing code descriptors

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
This commit is contained in:
Sebastian Wick 2022-09-28 02:46:42 +02:00 committed by Sebastian Wick
parent f7160730cc
commit 4a8684d1b0
7 changed files with 300 additions and 0 deletions

View file

@ -7,6 +7,7 @@
#include <libdisplay-info/dmt.h>
#include <libdisplay-info/edid.h>
#include <libdisplay-info/gtf.h>
#include <libdisplay-info/cvt.h>
#include "di-edid-decode.h"
@ -441,6 +442,111 @@ print_dmt_timing(const struct di_dmt_timing *t)
printf("\n");
}
static void
print_cvt_timing(struct di_cvt_timing *t, struct di_cvt_options *options,
int hratio, int vratio, bool preferred, bool rb)
{
double hbl, htotal;
hbl = t->h_front_porch + t->h_sync + t->h_back_porch;
htotal = t->total_active_pixels + hbl;
printf(" CVT: %5dx%-5d", (int)options->h_pixels, (int)options->v_lines);
printf(" %10.6f Hz", t->act_frame_rate);
printf(" %3u:%-3u", hratio, vratio);
printf(" %8.3f kHz %13.6f MHz", t->act_pixel_freq * 1000 / htotal,
(double) t->act_pixel_freq);
if (preferred || rb) {
printf(" (%s%s%s)", rb ? "RB" : "",
(preferred && rb) ? ", " : "",
preferred ? "preferred vertical rate" : "");
}
printf("\n");
}
static void
print_cvt_timing_code(const struct di_edid_cvt_timing_code *t)
{
struct di_cvt_timing timing;
struct di_cvt_options options;
enum di_edid_cvt_timing_code_preferred_vrate pref = t->preferred_vertical_rate;
int hratio, vratio;
options.int_rqd = false;
options.margins_rqd = false;
options.v_lines = t->addressable_lines_per_field;
switch (t->aspect_ratio) {
case DI_EDID_CVT_TIMING_CODE_4_3:
hratio = 4;
vratio = 3;
break;
case DI_EDID_CVT_TIMING_CODE_16_9:
hratio = 16;
vratio = 9;
break;
case DI_EDID_CVT_TIMING_CODE_16_10:
hratio = 16;
vratio = 10;
break;
case DI_EDID_CVT_TIMING_CODE_15_9:
hratio = 15;
vratio = 9;
break;
}
options.h_pixels = 8 * (((options.v_lines * hratio) / vratio) / 8);
if (t->supports_50hz_sb) {
options.ip_freq_rqd = 50;
options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE;
di_cvt_compute(&timing, &options);
print_cvt_timing(&timing, &options, hratio, vratio,
pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ,
false);
}
if (t->supports_60hz_sb) {
options.ip_freq_rqd = 60;
options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE;
di_cvt_compute(&timing, &options);
print_cvt_timing(&timing, &options, hratio, vratio,
pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ &&
!t->supports_60hz_rb,
false);
}
if (t->supports_75hz_sb) {
options.ip_freq_rqd = 75;
options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE;
di_cvt_compute(&timing, &options);
print_cvt_timing(&timing, &options, hratio, vratio,
pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ,
false);
}
if (t->supports_85hz_sb) {
options.ip_freq_rqd = 85;
options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE;
di_cvt_compute(&timing, &options);
print_cvt_timing(&timing, &options, hratio, vratio,
pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ,
false);
}
if (t->supports_60hz_rb) {
options.ip_freq_rqd = 60;
options.red_blank_ver = DI_CVT_REDUCED_BLANKING_V1;
di_cvt_compute(&timing, &options);
print_cvt_timing(&timing, &options, hratio, vratio,
pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ,
true);
}
}
static void
print_display_desc(const struct di_edid *edid,
const struct di_edid_display_descriptor *desc)
@ -453,6 +559,7 @@ print_display_desc(const struct di_edid *edid,
const struct di_edid_color_point *const *color_points;
const struct di_dmt_timing *const *established_timings_iii;
const struct di_edid_color_management_data *color_management_data;
const struct di_edid_cvt_timing_code *const *cvt_timings;
size_t i;
tag = di_edid_display_descriptor_get_tag(desc);
@ -589,6 +696,13 @@ print_display_desc(const struct di_edid *edid,
uncommon_features.color_management_data = true;
break;
case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES:
cvt_timings = di_edid_display_descriptor_get_cvt_timing_codes(desc);
printf("\n");
for (i = 0; cvt_timings[i] != NULL; i++)
print_cvt_timing_code(cvt_timings[i]);
break;
default:
printf("\n");
break; /* TODO: print other tags */

126
edid.c
View file

@ -17,6 +17,10 @@
* The size of an EDID standard timing, defined in section 3.9.
*/
#define EDID_STANDARD_TIMING_SIZE 2
/**
* The size of an EDID CVT timing code, defined in section 3.10.3.8.
*/
#define EDID_CVT_TIMING_CODE_SIZE 3
/**
* Fixed EDID header, defined in section 3.1.
@ -915,6 +919,108 @@ parse_color_management_data_descriptor(struct di_edid *edid,
}
}
static bool
is_cvt_timing_code_preferred_vrate_supported(const struct di_edid_cvt_timing_code *t)
{
switch (t->preferred_vertical_rate) {
case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ:
return t->supports_50hz_sb;
case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ:
return t->supports_60hz_sb || t->supports_60hz_rb;
case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ:
return t->supports_75hz_sb;
case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ:
return t->supports_85hz_sb;
}
abort(); /* unreachable */
}
static bool
parse_cvt_timing_code(struct di_edid *edid,
const uint8_t data[static EDID_CVT_TIMING_CODE_SIZE],
struct di_edid_cvt_timing_code **out,
bool first)
{
struct di_edid_cvt_timing_code *t;
int32_t raw;
*out = NULL;
if (!first && data[0] == 0 && data[1] == 0 && data[2] == 0) {
/* Unused */
return true;
}
if (data[0] == 0) {
add_failure(edid,
"CVT byte 0 is 0, which is a reserved value.");
}
t = calloc(1, sizeof(*t));
if (!t) {
return false;
}
raw = (int32_t)(data[0] | (get_bit_range(data[1], 7, 4) << 8));
t->addressable_lines_per_field = (raw + 1) * 2;
t->aspect_ratio = get_bit_range(data[1], 3, 2);
if (get_bit_range(data[1], 1, 0) != 0) {
add_failure(edid,
"Reserved bits of CVT byte 1 are non-zero.");
}
t->supports_50hz_sb = has_bit(data[2], 4);
t->supports_60hz_sb = has_bit(data[2], 3);
t->supports_75hz_sb = has_bit(data[2], 2);
t->supports_85hz_sb = has_bit(data[2], 1);
t->supports_60hz_rb = has_bit(data[2], 0);
if (get_bit_range(data[2], 4, 0) == 0) {
add_failure(edid,
"CVT byte 2 does not support any vertical rates.");
}
t->preferred_vertical_rate = get_bit_range(data[2], 6, 5);
if (has_bit(data[2], 7) != 0) {
add_failure(edid,
"Reserved bit of CVT byte 2 is non-zero.");
}
if (!is_cvt_timing_code_preferred_vrate_supported(t))
add_failure(edid, "The preferred CVT Vertical Rate is not supported.");
*out = t;
return true;
}
static bool
parse_cvt_timing_codes_descriptor(struct di_edid *edid,
const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE],
struct di_edid_display_descriptor *desc)
{
struct di_edid_cvt_timing_code *t;
size_t i;
const uint8_t *timing_data;
if (data[5] != 1) {
add_failure_until(edid, 4, "Invalid version number %u.",
data[5]);
}
for (i = 0; i < EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT; i++) {
timing_data = &data[6 + i * EDID_CVT_TIMING_CODE_SIZE];
if (!parse_cvt_timing_code(edid, timing_data, &t, !i))
return false;
if (t) {
assert(desc->cvt_timing_codes_len < EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT);
desc->cvt_timing_codes[desc->cvt_timing_codes_len++] = t;
}
}
return true;
}
static bool
parse_byte_descriptor(struct di_edid *edid,
const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE])
@ -991,6 +1097,11 @@ parse_byte_descriptor(struct di_edid *edid,
parse_color_management_data_descriptor(edid, data, desc);
break;
case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES:
if (!parse_cvt_timing_codes_descriptor(edid, data, desc)) {
free(desc);
return false;
}
break;
case DI_EDID_DISPLAY_DESCRIPTOR_DUMMY:
break; /* Ignore */
default:
@ -1186,6 +1297,12 @@ destroy_display_descriptor(struct di_edid_display_descriptor *desc)
free(desc->standard_timings[i]);
}
break;
case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES:
for (i = 0; i < desc->cvt_timing_codes_len; i++) {
free(desc->cvt_timing_codes[i]);
}
break;
default:
break; /* Nothing to do */
}
@ -1429,6 +1546,15 @@ di_edid_display_descriptor_get_color_management_data(const struct di_edid_displa
return &desc->dcm_data;
}
const struct di_edid_cvt_timing_code *const *
di_edid_display_descriptor_get_cvt_timing_codes(const struct di_edid_display_descriptor *desc)
{
if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES) {
return NULL;
}
return (const struct di_edid_cvt_timing_code *const *) desc->cvt_timing_codes;
}
const struct di_edid_ext *const *
di_edid_get_extensions(const struct di_edid *edid)
{

View file

@ -46,6 +46,11 @@
* defined in section 3.10.3.9.
*/
#define EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT 44
/**
* The maximum number of CVT timing codes in an EDID display descriptor,
* defined in section 3.10.3.8.
*/
#define EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT 4
struct di_edid_detailed_timing_def_priv {
struct di_edid_detailed_timing_def base;
@ -115,6 +120,9 @@ struct di_edid_display_descriptor {
size_t established_timings_iii_len;
/* Used for DCM_DATA */
struct di_edid_color_management_data dcm_data;
/* Used for DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES, NULL-terminated */
struct di_edid_cvt_timing_code *cvt_timing_codes[EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT + 1];
size_t cvt_timing_codes_len;
};
struct di_edid_ext {

View file

@ -710,6 +710,54 @@ struct di_edid_color_management_data {
const struct di_edid_color_management_data *
di_edid_display_descriptor_get_color_management_data(const struct di_edid_display_descriptor *desc);
/**
* Aspect ratio for an EDID CVT Timing Code.
*/
enum di_edid_cvt_timing_code_aspect_ratio {
DI_EDID_CVT_TIMING_CODE_4_3 = 0, /* 4:3 */
DI_EDID_CVT_TIMING_CODE_16_9 = 1, /* 16:9 */
DI_EDID_CVT_TIMING_CODE_16_10 = 2, /* 16:10 */
DI_EDID_CVT_TIMING_CODE_15_9 = 3, /* 15:9 */
};
/**
* Preferred Vertical Rate for an EDID CVT Timing Code.
*/
enum di_edid_cvt_timing_code_preferred_vrate {
DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ = 0, /* 50 Hz */
DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ = 1, /* 60 Hz */
DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ = 2, /* 75 Hz */
DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ = 3, /* 85 Hz */
};
/**
* EDID CVT Timing Code, defined in section 3.10.3.8
*
* For more information see VESA Coordinated Video Timings (CVT) Standard.
*/
struct di_edid_cvt_timing_code {
int32_t addressable_lines_per_field;
enum di_edid_cvt_timing_code_aspect_ratio aspect_ratio;
bool supports_50hz_sb;
bool supports_60hz_sb;
bool supports_75hz_sb;
bool supports_85hz_sb;
bool supports_60hz_rb;
enum di_edid_cvt_timing_code_preferred_vrate preferred_vertical_rate;
};
/**
* Get a list of CVT timing codes from an EDID display descriptor.
* The highest priority code comes first, the lowest priority code last.
*
* The returned array is NULL-terminated.
*
* Returns NULL if the display descriptor tag isn't
* DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES.
*/
const struct di_edid_cvt_timing_code *const *
di_edid_display_descriptor_get_cvt_timing_codes(const struct di_edid_display_descriptor *desc);
/**
* Get a list of EDID extensions.
*

BIN
test/data/cvt.edid Normal file

Binary file not shown.

3
test/data/cvt.print Normal file
View file

@ -0,0 +1,3 @@
make: The Linux Foundation
model: hdmi-1080p
serial: {null}

View file

@ -27,6 +27,7 @@ test_cases = [
'samsung-s27a950d-dp',
'sun-gh19ps-dvi',
'viewsonic-vp2768-dp',
'cvt',
]
test_env = [