mirror of
https://gitlab.freedesktop.org/emersion/libdisplay-info.git
synced 2024-11-16 19:48:30 +01:00
edid: add support for CVT timing code descriptors
Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
This commit is contained in:
parent
f7160730cc
commit
4a8684d1b0
7 changed files with 300 additions and 0 deletions
|
@ -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
126
edid.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
BIN
test/data/cvt.edid
Normal file
Binary file not shown.
3
test/data/cvt.print
Normal file
3
test/data/cvt.print
Normal file
|
@ -0,0 +1,3 @@
|
|||
make: The Linux Foundation
|
||||
model: hdmi-1080p
|
||||
serial: {null}
|
|
@ -27,6 +27,7 @@ test_cases = [
|
|||
'samsung-s27a950d-dp',
|
||||
'sun-gh19ps-dvi',
|
||||
'viewsonic-vp2768-dp',
|
||||
'cvt',
|
||||
]
|
||||
|
||||
test_env = [
|
||||
|
|
Loading…
Reference in a new issue