[igt-dev] [PATCH i-g-t 22/23] tools/intel_vbt_decode: Generate LVDS data table pointes if not provided
Ville Syrjala
ville.syrjala at linux.intel.com
Tue Jun 14 23:30:59 UTC 2022
From: Ville Syrjälä <ville.syrjala at linux.intel.com>
Modern VBTs (at least observed on TGL machines) no longer provide the
LVDS data table pointers block. Thus we can't currently decode the
contents of the LVDS data table block.
I see two options how to handle this:
1) Just hardocode the offsets/sizes (+ some checks to make sure the
hardcoded values makes sense)
2) Deduce the offsets/sizes from the actual LVDS data table block
contents
I've chosen option 2 here. The fp_timing table 0xffff terminator
is what allows us to do this. We just look up the first two
of those from the LVDS data block and calculate the offsets/sizes
from there. Only the fp_timing entries should have a variable size,
and the dvo_timings and panel_pnp_id have fixed size (in fact IIRC
they are 1:1 match for the equivalent EDID stuff).
This is the same thing we do in the kernel parser as well since
commit a87d0a847607 ("drm/i915/bios: Generate LFP data table
pointers if the VBT lacks them")
Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
tools/intel_vbt_decode.c | 128 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 127 insertions(+), 1 deletion(-)
diff --git a/tools/intel_vbt_decode.c b/tools/intel_vbt_decode.c
index 2ec9990279d9..e45bab4a3e01 100644
--- a/tools/intel_vbt_decode.c
+++ b/tools/intel_vbt_decode.c
@@ -189,6 +189,121 @@ static size_t lfp_data_min_size(const struct context *context)
return size;
}
+static const uint8_t *find_fp_timing_terminator(const uint8_t *data, int size)
+{
+ if (!data)
+ return NULL;
+
+ for (int i = 0; i < size - 1; i++) {
+ if (data[i] == 0xff && data[i+1] == 0xff)
+ return &data[i];
+ }
+
+ return NULL;
+}
+
+static int make_lvds_data_ptr(struct lvds_lfp_data_ptr_table *table,
+ int table_size, int total_size)
+{
+ if (total_size < table_size)
+ return total_size;
+
+ table->table_size = table_size;
+ table->offset = total_size - table_size;
+
+ return total_size - table_size;
+}
+
+static void next_lvds_data_ptr(struct lvds_lfp_data_ptr_table *next,
+ const struct lvds_lfp_data_ptr_table *prev,
+ int size)
+{
+ next->table_size = prev->table_size;
+ next->offset = prev->offset + size;
+}
+
+static void *generate_lvds_data_ptrs(const struct context *context)
+{
+ int size, table_size, block_size, offset;
+ const void *t0, *t1, *block;
+ struct bdb_lvds_lfp_data_ptrs *ptrs;
+ void *ptrs_block;
+
+ block = find_raw_section(context, BDB_LVDS_LFP_DATA);
+ if (!block)
+ return NULL;
+
+ block_size = get_blocksize(block);
+
+ size = block_size;
+ t0 = find_fp_timing_terminator(block, size);
+ if (!t0)
+ return NULL;
+
+ size -= t0 - block - 2;
+ t1 = find_fp_timing_terminator(t0 + 2, size);
+ if (!t1)
+ return NULL;
+
+ size = t1 - t0;
+ if (size * 16 > block_size)
+ return NULL;
+
+ ptrs_block = calloc(1, sizeof(*ptrs) + 3);
+ if (!ptrs_block)
+ return NULL;
+
+ *(uint8_t *)(ptrs_block + 0) = BDB_LVDS_LFP_DATA_PTRS;
+ *(uint16_t *)(ptrs_block + 1) = sizeof(*ptrs);
+ ptrs = ptrs_block + 3;
+
+ table_size = sizeof(struct lvds_pnp_id);
+ size = make_lvds_data_ptr(&ptrs->ptr[0].panel_pnp_id, table_size, size);
+
+ table_size = sizeof(struct lvds_dvo_timing);
+ size = make_lvds_data_ptr(&ptrs->ptr[0].dvo_timing, table_size, size);
+
+ table_size = t0 - block + 2;
+ size = make_lvds_data_ptr(&ptrs->ptr[0].fp_timing, table_size, size);
+
+ if (ptrs->ptr[0].fp_timing.table_size)
+ ptrs->lvds_entries++;
+ if (ptrs->ptr[0].dvo_timing.table_size)
+ ptrs->lvds_entries++;
+ if (ptrs->ptr[0].panel_pnp_id.table_size)
+ ptrs->lvds_entries++;
+
+ if (size != 0 || ptrs->lvds_entries != 3)
+ return NULL;
+
+ size = t1 - t0;
+ for (int i = 1; i < 16; i++) {
+ next_lvds_data_ptr(&ptrs->ptr[i].fp_timing, &ptrs->ptr[i-1].fp_timing, size);
+ next_lvds_data_ptr(&ptrs->ptr[i].dvo_timing, &ptrs->ptr[i-1].dvo_timing, size);
+ next_lvds_data_ptr(&ptrs->ptr[i].panel_pnp_id, &ptrs->ptr[i-1].panel_pnp_id, size);
+ }
+
+ size = t1 - t0;
+ table_size = sizeof(struct lvds_lfp_panel_name);
+
+ if (16 * (size + table_size) <= block_size) {
+ ptrs->panel_name.table_size = table_size;
+ ptrs->panel_name.offset = size * 16;
+ }
+
+ offset = block - (const void *)context->bdb;
+ for (int i = 0; i < 16; i++) {
+ ptrs->ptr[i].fp_timing.offset += offset;
+ ptrs->ptr[i].dvo_timing.offset += offset;
+ ptrs->ptr[i].panel_pnp_id.offset += offset;
+ }
+
+ if (ptrs->panel_name.offset)
+ ptrs->panel_name.offset += offset;
+
+ return ptrs_block;
+}
+
static size_t block_min_size(const struct context *context, int section_id)
{
switch (section_id) {
@@ -345,23 +460,34 @@ static struct bdb_block *find_section(const struct context *context, int section
{
size_t min_size = block_min_size(context, section_id);
struct bdb_block *block;
+ void *temp_block = NULL;
const void *data;
size_t size;
data = find_raw_section(context, section_id);
+ if (!data && section_id == BDB_LVDS_LFP_DATA_PTRS) {
+ printf("Generating LVDS data table pointers\n");
+ temp_block = generate_lvds_data_ptrs(context);
+ if (temp_block)
+ data = temp_block + 3;
+ }
if (!data)
return NULL;
size = get_blocksize(data);
block = calloc(1, sizeof(*block) + 3 + max(size, min_size));
- if (!block)
+ if (!block) {
+ free(temp_block);
return NULL;
+ }
block->id = section_id;
block->size = size;
memcpy(block->data, data - 3, 3 + size);
+ free(temp_block);
+
if (section_id == BDB_LVDS_LFP_DATA_PTRS &&
!fixup_lfp_data_ptrs(context, 3 + block->data)) {
fprintf(stderr, "VBT has malformed LFP data table pointers\n");
--
2.35.1
More information about the igt-dev
mailing list