displayid: add support for type III timings

Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Simon Ser 2022-12-18 19:40:55 +01:00
parent 245207e5ef
commit 7067ab6ad1
4 changed files with 200 additions and 1 deletions

View file

@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <libdisplay-info/cvt.h>
#include <libdisplay-info/displayid.h>
#include "di-edid-decode.h"
@ -253,6 +254,59 @@ print_displayid_tiled_topo(const struct di_displayid_tiled_topo *tiled_topo)
tiled_topo->serial_number);
}
static void
print_displayid_type_iii_timing(const struct di_displayid_type_iii_timing *t)
{
struct di_cvt_options cvt_options = {0};
struct di_cvt_timing cvt_timing = {0};
int hratio, vratio;
double hbl, htotal;
switch (t->algo) {
case DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING:
cvt_options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE;
break;
case DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING:
cvt_options.red_blank_ver = DI_CVT_REDUCED_BLANKING_V1;
break;
}
cvt_options.h_pixels = t->horiz_active;
get_displayid_timing_aspect_ratio(t->aspect_ratio, &hratio, &vratio);
if (t->aspect_ratio == DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED)
return;
cvt_options.v_lines = (cvt_options.h_pixels * vratio) / hratio;
cvt_options.ip_freq_rqd = t->refresh_rate_hz;
cvt_options.int_rqd = t->interlaced;
di_cvt_compute(&cvt_timing, &cvt_options);
hbl = cvt_timing.h_front_porch + cvt_timing.h_sync + cvt_timing.h_back_porch;
htotal = cvt_timing.total_active_pixels + hbl;
printf(" CVT: %5dx%-5d", (int)cvt_options.h_pixels, (int)cvt_options.v_lines);
printf(" %10.6f Hz", cvt_timing.act_frame_rate);
printf(" %3u:%-3u", hratio, vratio);
printf(" %8.3f kHz %13.6f MHz", cvt_timing.act_pixel_freq * 1000 / htotal,
(double) cvt_timing.act_pixel_freq);
printf(" (aspect %d:%d%s)", hratio, vratio,
t->preferred ? ", preferred" : "");
printf("\n");
}
static void
print_displayid_type_iii_timing_block(const struct di_displayid_data_block *data_block)
{
size_t i;
const struct di_displayid_type_iii_timing *const *timings;
timings = di_displayid_data_block_get_type_iii_timings(data_block);
for (i = 0; timings[i] != NULL; i++)
print_displayid_type_iii_timing(timings[i]);
}
static const char *
displayid_product_type_name(enum di_displayid_product_type type)
{
@ -361,6 +415,9 @@ print_displayid(const struct di_displayid *displayid)
case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
print_displayid_type_ii_timing_block(data_block);
break;
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
print_displayid_type_iii_timing_block(data_block);
break;
default:
break; /* Ignore */
}

View file

@ -28,6 +28,10 @@
* The size of a DisplayID type II timing.
*/
#define DISPLAYID_TYPE_II_TIMING_SIZE 11
/**
* The size of a DisplayID type III timing.
*/
#define DISPLAYID_TYPE_III_TIMING_SIZE 3
static bool
is_all_zeroes(const uint8_t *data, size_t size)
@ -436,6 +440,82 @@ parse_tiled_topo_block(struct di_displayid *displayid,
return true;
}
static bool
parse_type_iii_timing(struct di_displayid *displayid,
struct di_displayid_data_block *data_block,
const uint8_t data[static DISPLAYID_TYPE_III_TIMING_SIZE])
{
struct di_displayid_type_iii_timing *t;
uint8_t algo, aspect_ratio;
t = calloc(1, sizeof(*t));
if (t == NULL)
return false;
t->preferred = has_bit(data[0], 7);
algo = get_bit_range(data[0], 6, 4);
switch (algo) {
case DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING:
case DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING:
t->algo = algo;
break;
default:
add_failure(displayid,
"Video Timing Modes Type 3 - Short Timings Data Block: Reserved algorithm 0x%02x.",
algo);
goto error_reserved;
}
aspect_ratio = get_bit_range(data[0], 3, 0);
if (timing_aspect_ratio_is_valid(aspect_ratio)) {
t->aspect_ratio = aspect_ratio;
} else {
add_failure(displayid,
"Video Timing Modes Type 3 - Short Timings Data Block: Reserved aspect ratio 0x%02x.",
aspect_ratio);
goto error_reserved;
}
t->horiz_active = ((int32_t)data[1] + 1) * 8;
t->interlaced = has_bit(data[2], 7);
t->refresh_rate_hz = (int32_t)get_bit_range(data[2], 6, 0) + 1;
assert(data_block->type_iii_timings_len < DISPLAYID_MAX_TYPE_III_TIMINGS);
data_block->type_iii_timings[data_block->type_iii_timings_len++] = t;
return true;
error_reserved:
free(t);
return true;
}
static bool
parse_type_iii_timing_block(struct di_displayid *displayid,
struct di_displayid_data_block *data_block,
const uint8_t *data, size_t size)
{
size_t i;
check_data_block_revision(displayid, data,
"Video Timing Modes Type 3 - Short Timings Data Block",
1);
if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_III_TIMING_SIZE != 0)
add_failure(displayid,
"Video Timing Modes Type 3 - Short Timings Data Block: payload size not divisible by element size.");
for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
i + DISPLAYID_TYPE_III_TIMING_SIZE <= size;
i += DISPLAYID_TYPE_III_TIMING_SIZE) {
if (!parse_type_iii_timing(displayid, data_block, &data[i]))
return false;
}
return true;
}
static ssize_t
parse_data_block(struct di_displayid *displayid, const uint8_t *data,
size_t size)
@ -479,9 +559,12 @@ parse_data_block(struct di_displayid *displayid, const uint8_t *data,
if (!parse_type_ii_timing_block(displayid, data_block, data, data_block_size))
goto error;
break;
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
if (!parse_type_iii_timing_block(displayid, data_block, data, data_block_size))
goto error;
break;
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID:
case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT:
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING:
case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING:
@ -630,6 +713,10 @@ destroy_data_block(struct di_displayid_data_block *data_block)
for (i = 0; i < data_block->type_ii_timings_len; i++)
free(data_block->type_ii_timings[i]);
break;
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
for (i = 0; i < data_block->type_iii_timings_len; i++)
free(data_block->type_iii_timings[i]);
break;
default:
break; /* Nothing to do */
}
@ -706,6 +793,15 @@ di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block *dat
return &data_block->tiled_topo.base;
}
const struct di_displayid_type_iii_timing *const *
di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block *data_block)
{
if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING) {
return NULL;
}
return (const struct di_displayid_type_iii_timing *const *) data_block->type_iii_timings;
}
const struct di_displayid_data_block *const *
di_displayid_get_data_blocks(const struct di_displayid *displayid)
{

View file

@ -34,6 +34,13 @@
* I timing takes up 11 bytes.
*/
#define DISPLAYID_MAX_TYPE_II_TIMINGS 22
/**
* The maxiumum number of type III timings in a data block.
*
* A DisplayID data block has a maximum payload size of 248 bytes, and each type
* III timing takes up 3 bytes.
*/
#define DISPLAYID_MAX_TYPE_III_TIMINGS 82
struct di_displayid {
int version, revision;
@ -67,6 +74,10 @@ struct di_displayid_data_block {
struct di_displayid_type_i_ii_vii_timing *type_ii_timings[DISPLAYID_MAX_TYPE_II_TIMINGS + 1];
size_t type_ii_timings_len;
/* Used for TYPE_III_TIMING, NULL-terminated */
struct di_displayid_type_iii_timing *type_iii_timings[DISPLAYID_MAX_TYPE_III_TIMINGS + 1];
size_t type_iii_timings_len;
/* Used for DISPLAY_PARAMS */
struct di_displayid_display_params_priv display_params;

View file

@ -310,6 +310,41 @@ struct di_displayid_tiled_topo {
const struct di_displayid_tiled_topo *
di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block *data_block);
/**
* Formula/algorithm for type III timings.
*/
enum di_displayid_type_iii_timing_algo {
/* VESA CVT, standard blanking */
DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING = 0,
/* VESA CVT, reduced blanking */
DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING = 1,
};
/**
* Type III timing, defined in section 4.4.3.
*/
struct di_displayid_type_iii_timing {
bool preferred;
enum di_displayid_type_iii_timing_algo algo;
enum di_displayid_timing_aspect_ratio aspect_ratio;
/* Horizontal Active Image (in pixels) */
int32_t horiz_active;
bool interlaced;
/* Frame/Field Refresh Rate (in Hz) */
int32_t refresh_rate_hz;
};
/**
* Get type III timings from a DisplayID data block.
*
* The returned array is NULL-terminated.
*
* Returns NULL if the data block tag isn't
* DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING.
*/
const struct di_displayid_type_iii_timing *const *
di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block *data_block);
/**
* Get DisplayID data blocks.
*