summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVille Syrjälä <ville.syrjala@linux.intel.com>2022-03-26 22:09:42 +0200
committerVille Syrjälä <ville.syrjala@linux.intel.com>2022-06-22 15:39:00 +0300
commitce4d30196d3cfcd695130be31cf91f4dd6aef2ec (patch)
tree0235d9136f3f4c940d82ff0899d2dda0c193367b
parentfc3090399bb954b7b2a78711ab209e10df9588a6 (diff)
tools/intel_vbt_decode: Generate LVDS data table pointes if not provided
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") Reviewed-by: Jani Nikula <jani.nikula@intel.com> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
-rw-r--r--tools/intel_vbt_decode.c128
1 files changed, 127 insertions, 1 deletions
diff --git a/tools/intel_vbt_decode.c b/tools/intel_vbt_decode.c
index 2ec99902..e45bab4a 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");