diff options
author | Ville Syrjälä <ville.syrjala@linux.intel.com> | 2022-03-26 22:09:42 +0200 |
---|---|---|
committer | Ville Syrjälä <ville.syrjala@linux.intel.com> | 2022-06-22 15:39:00 +0300 |
commit | ce4d30196d3cfcd695130be31cf91f4dd6aef2ec (patch) | |
tree | 0235d9136f3f4c940d82ff0899d2dda0c193367b /tools/intel_vbt_decode.c | |
parent | fc3090399bb954b7b2a78711ab209e10df9588a6 (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>
Diffstat (limited to 'tools/intel_vbt_decode.c')
-rw-r--r-- | tools/intel_vbt_decode.c | 128 |
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"); |