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:
parent
339d1a26ad
commit
abec7ab276
2 changed files with 231 additions and 0 deletions
175
src/lib/edid.c
175
src/lib/edid.c
|
|
@ -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
56
src/lib/edid_displayid.h
Normal 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__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue