lib/edid: Support DisplayID 2.0 extension

Add support for DisplayID 2.0 extension. Right now, the implementation
only supports 'Type VII – Detailed timing' data block decoding.

Reference: 'DisplayID v2.1a.pdf'

BUG=b:392040003
BRANCH=rauru
TEST=Check FW screen on a panel than supports Display ID 2.0

Change-Id: I1b8a5ab3ada5c8eacc7b6dde3d33ec72b3790960
Signed-off-by: Yidi Lin <yidilin@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/86724
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
This commit is contained in:
Yidi Lin 2025-02-19 18:27:26 +08:00 committed by Matt DeVillier
commit abec7ab276
2 changed files with 231 additions and 0 deletions

View file

@ -10,11 +10,14 @@
#include <commonlib/helpers.h>
#include <console/console.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <edid.h>
#include <vbe.h>
#include "edid_displayid.h"
struct edid_context {
int claims_one_point_oh;
int claims_one_point_two;
@ -941,6 +944,173 @@ parse_cea(struct edid *out, unsigned char *x, struct edid_context *c)
return ret;
}
/* DisplayID extension code */
static const char * const displayid_aspect[] = {
"1:1",
"5:4",
"4:3",
"15:9",
"16:9",
"16:10",
"64:27",
"256:135",
};
static void displayid_detailed_timing(
struct displayid_type_7_detailed_timing_desc *desc,
struct edid *out)
{
unsigned int pixel_clock = (desc->pixel_clock[0] |
(desc->pixel_clock[1] << 8) |
(desc->pixel_clock[2] << 16)) + 1;
unsigned int hactive = (desc->hactive[0] | desc->hactive[1] << 8) + 1;
unsigned int hblank = (desc->hblank[0] | desc->hblank[1] << 8) + 1;
unsigned int hsync = (desc->hfront_porch[0] | (desc->hfront_porch[1] & 0x7f) << 8) + 1;
unsigned int hsync_width = (desc->hsync_width[0] | desc->hsync_width[1] << 8) + 1;
unsigned int vactive = (desc->vactive[0] | desc->vactive[1] << 8) + 1;
unsigned int vblank = (desc->vblank[0] | desc->vblank[1] << 8) + 1;
unsigned int vsync = (desc->vfront_porch[0] | (desc->vfront_porch[1] & 0x7f) << 8) + 1;
unsigned int vsync_width = (desc->vsync_width[0] | desc->vsync_width[1] << 8) + 1;
bool hsync_positive = (desc->hfront_porch[1] >> 7) & 0x1;
bool vsync_positive = (desc->vfront_porch[1] >> 7) & 0x1;
unsigned int aspect = desc->flags & 0xF;
bool is_preferred = desc->flags & 0x80;
out->mode.pixel_clock = pixel_clock;
out->mode.lvds_dual_channel = (out->mode.pixel_clock >= 95000);
out->mode.ha = hactive;
out->mode.hbl = hblank;
out->mode.hso = hsync;
out->mode.hspw = hsync_width;
out->mode.va = vactive;
out->mode.vbl = vblank;
out->mode.vso = vsync;
out->mode.vspw = vsync_width;
out->mode.phsync = hsync_positive ? '+' : '-';
out->mode.pvsync = vsync_positive ? '+' : '-';
printk(BIOS_SPEW,
"Detailed mode: clock %d KHz,\n"
" ha: %u hso: %u hspw: %u hbl: %u\n"
" va: %u vso: %u vspw: %u vbl: %u\n"
" %chsync %cvsync (aspect %s%s)\n",
out->mode.pixel_clock,
out->mode.ha, out->mode.hso, out->mode.hspw, out->mode.hbl,
out->mode.va, out->mode.vso, out->mode.vspw, out->mode.vbl,
out->mode.phsync, out->mode.pvsync,
(aspect >= ARRAY_SIZE(displayid_aspect)) ? "undefined" : displayid_aspect[aspect],
is_preferred ? ", preferred" : "");
}
static int displayid_type_7_timing_block(
struct edid *result_edid,
unsigned char *x,
struct edid_context *c,
int index)
{
struct edid tmp;
struct displayid_type_7_detailed_timing_block *block;
struct displayid_type_7_detailed_timing_desc *desc;
int desc_count;
block = (struct displayid_type_7_detailed_timing_block *)&x[index];
desc_count = block->header.length / sizeof(*desc);
if (block->header.length % sizeof(*desc)) {
printk(BIOS_ERR, "%s: the length is not a multiple of %zu\n",
__func__, sizeof(*desc));
return 1;
}
tmp = *result_edid;
for (int i = 0; i < desc_count; i++) {
desc = &block->descs[i];
displayid_detailed_timing(desc, &tmp);
c->did_detailed_timing = 1;
if ((desc->flags & 0x80) && !c->has_preferred_timing) {
c->has_preferred_timing = 1;
*result_edid = tmp;
}
}
return 0;
}
static int validate_section_header(unsigned char *x, int index)
{
int i;
size_t section_length;
struct displayid_section_header *sh = (struct displayid_section_header *)&x[index];
unsigned char sum = 0;
/* +1 for the Display ID section checksum */
section_length = sizeof(*sh) + sh->length + 1;
/* Reserve 1 byte for extension block checksum */
if (section_length > 128 - 1 - index)
return 1;
for (i = 0; i < section_length; i++)
sum += x[i + index];
if (sum) {
printk(BIOS_ERR, "%s: checksum invalid\n", __func__);
return 1;
}
return 0;
}
static int parse_displayid(
struct edid *result_edid,
unsigned char *x,
struct edid_context *c)
{
int ret = 0;
int index = 1;
struct displayid_section_header *sh = (struct displayid_section_header *)&x[index];
struct displayid_block_header *bh;
if (validate_section_header(x, index))
return 1;
/* Support Display ID structure 2.0 only */
if (sh->revision != DISPLAY_ID_STRUCTURE_VERSION_20) {
printk(BIOS_SPEW, "Unsupported Display ID revison: %x\n", sh->revision);
goto validate_checksum;
}
index += sizeof(*sh);
while (index < 1 + sh->length + sizeof(*sh)) {
bh = (struct displayid_block_header *)&x[index];
if (index + sizeof(*bh) > sh->length ||
index + sizeof(*bh) + bh->length > sh->length)
break;
if (bh->tag == DATA_BLOCK_V2_TYPE_7_DETAILED_TIMING)
displayid_type_7_timing_block(result_edid, x, c, index);
else
printk(BIOS_SPEW, "%s: Unsupported data block tag %x\n",
__func__, bh->tag);
index += sizeof(*bh) + bh->length;
}
validate_checksum:
c->has_valid_checksum &= do_checksum(x);
return ret;
}
/* generic extension code */
static void
@ -973,6 +1143,11 @@ parse_extension(struct edid *out, unsigned char *x, struct edid_context *c)
case 0x60:
printk(BIOS_SPEW, "DPVL extension block\n");
break;
case 0x70:
printk(BIOS_SPEW, "DisplayID extension block\n");
extension_version(out, x);
conformant_extension = parse_displayid(out, x, c);
break;
case 0xF0:
printk(BIOS_SPEW, "Block map\n");
break;

56
src/lib/edid_displayid.h Normal file
View file

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __EDID_DISPLAYID_H__
#define __EDID_DISPLAYID_H__
/* DisplayID section structure, version 2.0 */
#define DISPLAY_ID_STRUCTURE_VERSION_20 0x20
/* Data block types */
#define DATA_BLOCK_V2_PRODUCT_ID 0x20
#define DATA_BLOCK_V2_DISPLAY_PARAMETERS 0x21
#define DATA_BLOCK_V2_TYPE_7_DETAILED_TIMING 0x22
#define DATA_BLOCK_V2_TYPE_8_ENUMERATED_TIMING 0x23
#define DATA_BLOCK_V2_TYPE_9_FORMULA_TIMING 0x24
#define DATA_BLOCK_V2_DYNAMIC_VIDEO_TIMING 0x25
#define DATA_BLOCK_V2_DISPLAY_INTERFACE_FEATURES 0x26
#define DATA_BLOCK_V2_STEREO_DISPLAY_INTERFACE 0x27
#define DATA_BLOCK_V2_TILED_DISPLAY_TOPOLOGY 0x28
#define DATA_BLOCK_V2_CONTAINER_ID 0x29
#define DATA_BLOCK_V2_VENDOR_SPECIFIC 0x7E
#define DATA_BLOCK_V2_CTA_DISPLAY_ID 0x81
struct displayid_section_header {
u8 revision;
u8 length;
u8 product_type;
u8 extension_count;
} __packed;
struct displayid_block_header {
u8 tag;
u8 revison;
u8 length;
} ___packed;
struct displayid_type_7_detailed_timing_desc {
u8 pixel_clock[3];
u8 flags;
u8 hactive[2];
u8 hblank[2];
u8 hfront_porch[2];
u8 hsync_width[2];
u8 vactive[2];
u8 vblank[2];
u8 vfront_porch[2];
u8 vsync_width[2];
} __packed;
_Static_assert(sizeof(struct displayid_type_7_detailed_timing_desc) == 20,
"The size does not meet spec");
struct displayid_type_7_detailed_timing_block {
struct displayid_block_header header;
struct displayid_type_7_detailed_timing_desc descs[];
};
#endif /* __EDID_DISPLAYID_H__ */