diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_bios.c')
-rw-r--r-- | drivers/gpu/drm/i915/display/intel_bios.c | 506 |
1 files changed, 363 insertions, 143 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 40b5e7ed12c2..bc195769dd07 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -88,7 +88,7 @@ static u32 get_blocksize(const void *block_data) } static const void * -find_section(const void *_bdb, enum bdb_block_id section_id) +find_raw_section(const void *_bdb, enum bdb_block_id section_id) { const struct bdb_header *bdb = _bdb; const u8 *base = _bdb; @@ -118,6 +118,250 @@ find_section(const void *_bdb, enum bdb_block_id section_id) return NULL; } +/* + * Offset from the start of BDB to the start of the + * block data (just past the block header). + */ +static u32 block_offset(const void *bdb, enum bdb_block_id section_id) +{ + const void *block; + + block = find_raw_section(bdb, section_id); + if (!block) + return 0; + + return block - bdb; +} + +/* size of the block excluding the header */ +static u32 block_size(const void *bdb, enum bdb_block_id section_id) +{ + const void *block; + + block = find_raw_section(bdb, section_id); + if (!block) + return 0; + + return get_blocksize(block); +} + +struct bdb_block_entry { + struct list_head node; + enum bdb_block_id section_id; + u8 data[]; +}; + +static const void * +find_section(struct drm_i915_private *i915, + enum bdb_block_id section_id) +{ + struct bdb_block_entry *entry; + + list_for_each_entry(entry, &i915->vbt.bdb_blocks, node) { + if (entry->section_id == section_id) + return entry->data + 3; + } + + return NULL; +} + +static const struct { + enum bdb_block_id section_id; + size_t min_size; +} bdb_blocks[] = { + { .section_id = BDB_GENERAL_FEATURES, + .min_size = sizeof(struct bdb_general_features), }, + { .section_id = BDB_GENERAL_DEFINITIONS, + .min_size = sizeof(struct bdb_general_definitions), }, + { .section_id = BDB_PSR, + .min_size = sizeof(struct bdb_psr), }, + { .section_id = BDB_DRIVER_FEATURES, + .min_size = sizeof(struct bdb_driver_features), }, + { .section_id = BDB_SDVO_LVDS_OPTIONS, + .min_size = sizeof(struct bdb_sdvo_lvds_options), }, + { .section_id = BDB_SDVO_PANEL_DTDS, + .min_size = sizeof(struct bdb_sdvo_panel_dtds), }, + { .section_id = BDB_EDP, + .min_size = sizeof(struct bdb_edp), }, + { .section_id = BDB_LVDS_OPTIONS, + .min_size = sizeof(struct bdb_lvds_options), }, + { .section_id = BDB_LVDS_LFP_DATA_PTRS, + .min_size = sizeof(struct bdb_lvds_lfp_data_ptrs), }, + { .section_id = BDB_LVDS_LFP_DATA, + .min_size = sizeof(struct bdb_lvds_lfp_data), }, + { .section_id = BDB_LVDS_BACKLIGHT, + .min_size = sizeof(struct bdb_lfp_backlight_data), }, + { .section_id = BDB_LFP_POWER, + .min_size = sizeof(struct bdb_lfp_power), }, + { .section_id = BDB_MIPI_CONFIG, + .min_size = sizeof(struct bdb_mipi_config), }, + { .section_id = BDB_MIPI_SEQUENCE, + .min_size = sizeof(struct bdb_mipi_sequence) }, + { .section_id = BDB_COMPRESSION_PARAMETERS, + .min_size = sizeof(struct bdb_compression_parameters), }, + { .section_id = BDB_GENERIC_DTD, + .min_size = sizeof(struct bdb_generic_dtd), }, +}; + +static bool validate_lfp_data_ptrs(const void *bdb, + const struct bdb_lvds_lfp_data_ptrs *ptrs) +{ + int fp_timing_size, dvo_timing_size, panel_pnp_id_size, panel_name_size; + int data_block_size, lfp_data_size; + int i; + + data_block_size = block_size(bdb, BDB_LVDS_LFP_DATA); + if (data_block_size == 0) + return false; + + /* always 3 indicating the presence of fp_timing+dvo_timing+panel_pnp_id */ + if (ptrs->lvds_entries != 3) + return false; + + fp_timing_size = ptrs->ptr[0].fp_timing.table_size; + dvo_timing_size = ptrs->ptr[0].dvo_timing.table_size; + panel_pnp_id_size = ptrs->ptr[0].panel_pnp_id.table_size; + panel_name_size = ptrs->panel_name.table_size; + + /* fp_timing has variable size */ + if (fp_timing_size < 32 || + dvo_timing_size != sizeof(struct lvds_dvo_timing) || + panel_pnp_id_size != sizeof(struct lvds_pnp_id)) + return false; + + /* panel_name is not present in old VBTs */ + if (panel_name_size != 0 && + panel_name_size != sizeof(struct lvds_lfp_panel_name)) + return false; + + lfp_data_size = ptrs->ptr[1].fp_timing.offset - ptrs->ptr[0].fp_timing.offset; + if (16 * lfp_data_size > data_block_size) + return false; + + /* + * Except for vlv/chv machines all real VBTs seem to have 6 + * unaccounted bytes in the fp_timing table. And it doesn't + * appear to be a really intentional hole as the fp_timing + * 0xffff terminator is always within those 6 missing bytes. + */ + if (fp_timing_size + dvo_timing_size + panel_pnp_id_size != lfp_data_size && + fp_timing_size + 6 + dvo_timing_size + panel_pnp_id_size != lfp_data_size) + return false; + + if (ptrs->ptr[0].fp_timing.offset + fp_timing_size > ptrs->ptr[0].dvo_timing.offset || + ptrs->ptr[0].dvo_timing.offset + dvo_timing_size != ptrs->ptr[0].panel_pnp_id.offset || + ptrs->ptr[0].panel_pnp_id.offset + panel_pnp_id_size != lfp_data_size) + return false; + + /* make sure the table entries have uniform size */ + for (i = 1; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.table_size != fp_timing_size || + ptrs->ptr[i].dvo_timing.table_size != dvo_timing_size || + ptrs->ptr[i].panel_pnp_id.table_size != panel_pnp_id_size) + return false; + + if (ptrs->ptr[i].fp_timing.offset - ptrs->ptr[i-1].fp_timing.offset != lfp_data_size || + ptrs->ptr[i].dvo_timing.offset - ptrs->ptr[i-1].dvo_timing.offset != lfp_data_size || + ptrs->ptr[i].panel_pnp_id.offset - ptrs->ptr[i-1].panel_pnp_id.offset != lfp_data_size) + return false; + } + + /* make sure the tables fit inside the data block */ + for (i = 0; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.offset + fp_timing_size > data_block_size || + ptrs->ptr[i].dvo_timing.offset + dvo_timing_size > data_block_size || + ptrs->ptr[i].panel_pnp_id.offset + panel_pnp_id_size > data_block_size) + return false; + } + + if (ptrs->panel_name.offset + 16 * panel_name_size > data_block_size) + return false; + + return true; +} + +/* make the data table offsets relative to the data block */ +static bool fixup_lfp_data_ptrs(const void *bdb, void *ptrs_block) +{ + struct bdb_lvds_lfp_data_ptrs *ptrs = ptrs_block; + u32 offset; + int i; + + offset = block_offset(bdb, BDB_LVDS_LFP_DATA); + + for (i = 0; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.offset < offset || + ptrs->ptr[i].dvo_timing.offset < offset || + ptrs->ptr[i].panel_pnp_id.offset < offset) + return false; + + 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.table_size) { + if (ptrs->panel_name.offset < offset) + return false; + + ptrs->panel_name.offset -= offset; + } + + return validate_lfp_data_ptrs(bdb, ptrs); +} + +static void +init_bdb_block(struct drm_i915_private *i915, + const void *bdb, enum bdb_block_id section_id, + size_t min_size) +{ + struct bdb_block_entry *entry; + const void *block; + size_t block_size; + + block = find_raw_section(bdb, section_id); + if (!block) + return; + + drm_WARN(&i915->drm, min_size == 0, + "Block %d min_size is zero\n", section_id); + + block_size = get_blocksize(block); + + entry = kzalloc(struct_size(entry, data, max(min_size, block_size) + 3), + GFP_KERNEL); + if (!entry) + return; + + entry->section_id = section_id; + memcpy(entry->data, block - 3, block_size + 3); + + drm_dbg_kms(&i915->drm, "Found BDB block %d (size %zu, min size %zu)\n", + section_id, block_size, min_size); + + if (section_id == BDB_LVDS_LFP_DATA_PTRS && + !fixup_lfp_data_ptrs(bdb, entry->data + 3)) { + drm_err(&i915->drm, "VBT has malformed LFP data table pointers\n"); + kfree(entry); + return; + } + + list_add_tail(&entry->node, &i915->vbt.bdb_blocks); +} + +static void init_bdb_blocks(struct drm_i915_private *i915, + const void *bdb) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bdb_blocks); i++) { + enum bdb_block_id section_id = bdb_blocks[i].section_id; + size_t min_size = bdb_blocks[i].min_size; + + init_bdb_block(i915, bdb, section_id, min_size); + } +} + static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, const struct lvds_dvo_timing *dvo_timing) @@ -169,60 +413,31 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, } static const struct lvds_dvo_timing * -get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data, - const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs, +get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *data, + const struct bdb_lvds_lfp_data_ptrs *ptrs, int index) { - /* - * the size of fp_timing varies on the different platform. - * So calculate the DVO timing relative offset in LVDS data - * entry to get the DVO timing entry - */ - - int lfp_data_size = - lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; - int dvo_timing_offset = - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; - char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index; - - return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); + return (const void *)data + ptrs->ptr[index].dvo_timing.offset; } -/* get lvds_fp_timing entry - * this function may return NULL if the corresponding entry is invalid - */ static const struct lvds_fp_timing * -get_lvds_fp_timing(const struct bdb_header *bdb, - const struct bdb_lvds_lfp_data *data, +get_lvds_fp_timing(const struct bdb_lvds_lfp_data *data, const struct bdb_lvds_lfp_data_ptrs *ptrs, int index) { - size_t data_ofs = (const u8 *)data - (const u8 *)bdb; - u16 data_size = ((const u16 *)data)[-1]; /* stored in header */ - size_t ofs; - - if (index >= ARRAY_SIZE(ptrs->ptr)) - return NULL; - ofs = ptrs->ptr[index].fp_timing_offset; - if (ofs < data_ofs || - ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size) - return NULL; - return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs); + return (const void *)data + ptrs->ptr[index].fp_timing.offset; } /* Parse general panel options */ static void -parse_panel_options(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_panel_options(struct drm_i915_private *i915) { const struct bdb_lvds_options *lvds_options; int panel_type; int drrs_mode; int ret; - lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); + lvds_options = find_section(i915, BDB_LVDS_OPTIONS); if (!lvds_options) return; @@ -257,16 +472,16 @@ parse_panel_options(struct drm_i915_private *i915, */ switch (drrs_mode) { case 0: - i915->vbt.drrs_type = STATIC_DRRS_SUPPORT; + i915->vbt.drrs_type = DRRS_TYPE_STATIC; drm_dbg_kms(&i915->drm, "DRRS supported mode is static\n"); break; case 2: - i915->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT; + i915->vbt.drrs_type = DRRS_TYPE_SEAMLESS; drm_dbg_kms(&i915->drm, "DRRS supported mode is seamless\n"); break; default: - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; drm_dbg_kms(&i915->drm, "DRRS not supported (VBT input)\n"); break; @@ -275,8 +490,7 @@ parse_panel_options(struct drm_i915_private *i915, /* Try to find integrated panel timing data */ static void -parse_lfp_panel_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_lfp_panel_dtd(struct drm_i915_private *i915) { const struct bdb_lvds_lfp_data *lvds_lfp_data; const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; @@ -285,11 +499,11 @@ parse_lfp_panel_dtd(struct drm_i915_private *i915, struct drm_display_mode *panel_fixed_mode; int panel_type = i915->vbt.panel_type; - lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); + lvds_lfp_data = find_section(i915, BDB_LVDS_LFP_DATA); if (!lvds_lfp_data) return; - lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); + lvds_lfp_data_ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS); if (!lvds_lfp_data_ptrs) return; @@ -306,34 +520,32 @@ parse_lfp_panel_dtd(struct drm_i915_private *i915, i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; drm_dbg_kms(&i915->drm, - "Found panel mode in BIOS VBT legacy lfp table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found panel mode in BIOS VBT legacy lfp table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); - fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data, + fp_timing = get_lvds_fp_timing(lvds_lfp_data, lvds_lfp_data_ptrs, panel_type); - if (fp_timing) { - /* check the resolution, just to be sure */ - if (fp_timing->x_res == panel_fixed_mode->hdisplay && - fp_timing->y_res == panel_fixed_mode->vdisplay) { - i915->vbt.bios_lvds_val = fp_timing->lvds_reg_val; - drm_dbg_kms(&i915->drm, - "VBT initial LVDS value %x\n", - i915->vbt.bios_lvds_val); - } + + /* check the resolution, just to be sure */ + if (fp_timing->x_res == panel_fixed_mode->hdisplay && + fp_timing->y_res == panel_fixed_mode->vdisplay) { + i915->vbt.bios_lvds_val = fp_timing->lvds_reg_val; + drm_dbg_kms(&i915->drm, + "VBT initial LVDS value %x\n", + i915->vbt.bios_lvds_val); } } static void -parse_generic_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_generic_dtd(struct drm_i915_private *i915) { const struct bdb_generic_dtd *generic_dtd; const struct generic_dtd_entry *dtd; struct drm_display_mode *panel_fixed_mode; int num_dtd; - generic_dtd = find_section(bdb, BDB_GENERIC_DTD); + generic_dtd = find_section(i915, BDB_GENERIC_DTD); if (!generic_dtd) return; @@ -397,15 +609,14 @@ parse_generic_dtd(struct drm_i915_private *i915, panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; drm_dbg_kms(&i915->drm, - "Found panel mode in BIOS VBT generic dtd table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found panel mode in BIOS VBT generic dtd table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; } static void -parse_panel_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_panel_dtd(struct drm_i915_private *i915) { /* * Older VBTs provided provided DTD information for internal displays @@ -415,22 +626,21 @@ parse_panel_dtd(struct drm_i915_private *i915, * try the new generic DTD block first on VBT >= 229, but still fall * back to trying the old LFP block if that fails. */ - if (bdb->version >= 229) - parse_generic_dtd(i915, bdb); + if (i915->vbt.version >= 229) + parse_generic_dtd(i915); if (!i915->vbt.lfp_lvds_vbt_mode) - parse_lfp_panel_dtd(i915, bdb); + parse_lfp_panel_dtd(i915); } static void -parse_lfp_backlight(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_lfp_backlight(struct drm_i915_private *i915) { const struct bdb_lfp_backlight_data *backlight_data; const struct lfp_backlight_data_entry *entry; int panel_type = i915->vbt.panel_type; u16 level; - backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT); + backlight_data = find_section(i915, BDB_LVDS_BACKLIGHT); if (!backlight_data) return; @@ -452,12 +662,12 @@ parse_lfp_backlight(struct drm_i915_private *i915, } i915->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI; - if (bdb->version >= 191) { + if (i915->vbt.version >= 191) { size_t exp_size; - if (bdb->version >= 236) + if (i915->vbt.version >= 236) exp_size = sizeof(struct bdb_lfp_backlight_data); - else if (bdb->version >= 234) + else if (i915->vbt.version >= 234) exp_size = EXP_BDB_LFP_BL_DATA_SIZE_REV_234; else exp_size = EXP_BDB_LFP_BL_DATA_SIZE_REV_191; @@ -474,14 +684,14 @@ parse_lfp_backlight(struct drm_i915_private *i915, i915->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; i915->vbt.backlight.active_low_pwm = entry->active_low_pwm; - if (bdb->version >= 234) { + if (i915->vbt.version >= 234) { u16 min_level; bool scale; level = backlight_data->brightness_level[panel_type].level; min_level = backlight_data->brightness_min_level[panel_type].level; - if (bdb->version >= 236) + if (i915->vbt.version >= 236) scale = backlight_data->brightness_precision_bits[panel_type] == 16; else scale = level > 255; @@ -514,8 +724,7 @@ parse_lfp_backlight(struct drm_i915_private *i915, /* Try to find sdvo panel data */ static void -parse_sdvo_panel_data(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_sdvo_panel_data(struct drm_i915_private *i915) { const struct bdb_sdvo_panel_dtds *dtds; struct drm_display_mode *panel_fixed_mode; @@ -531,14 +740,14 @@ parse_sdvo_panel_data(struct drm_i915_private *i915, if (index == -1) { const struct bdb_sdvo_lvds_options *sdvo_lvds_options; - sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); + sdvo_lvds_options = find_section(i915, BDB_SDVO_LVDS_OPTIONS); if (!sdvo_lvds_options) return; index = sdvo_lvds_options->panel_type; } - dtds = find_section(bdb, BDB_SDVO_PANEL_DTDS); + dtds = find_section(i915, BDB_SDVO_PANEL_DTDS); if (!dtds) return; @@ -551,8 +760,8 @@ parse_sdvo_panel_data(struct drm_i915_private *i915, i915->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; drm_dbg_kms(&i915->drm, - "Found SDVO panel mode in BIOS VBT tables:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found SDVO panel mode in BIOS VBT tables: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); } static int intel_bios_ssc_frequency(struct drm_i915_private *i915, @@ -570,18 +779,17 @@ static int intel_bios_ssc_frequency(struct drm_i915_private *i915, } static void -parse_general_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_general_features(struct drm_i915_private *i915) { const struct bdb_general_features *general; - general = find_section(bdb, BDB_GENERAL_FEATURES); + general = find_section(i915, BDB_GENERAL_FEATURES); if (!general) return; i915->vbt.int_tv_support = general->int_tv_support; /* int_crt_support can't be trusted on earlier platforms */ - if (bdb->version >= 155 && + if (i915->vbt.version >= 155 && (HAS_DDI(i915) || IS_VALLEYVIEW(i915))) i915->vbt.int_crt_support = general->int_crt_support; i915->vbt.lvds_use_ssc = general->enable_ssc; @@ -589,7 +797,7 @@ parse_general_features(struct drm_i915_private *i915, intel_bios_ssc_frequency(i915, general->ssc_freq); i915->vbt.display_clock_mode = general->display_clock_mode; i915->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; - if (bdb->version >= 181) { + if (i915->vbt.version >= 181) { i915->vbt.orientation = general->rotate_180 ? DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP : DRM_MODE_PANEL_ORIENTATION_NORMAL; @@ -597,7 +805,7 @@ parse_general_features(struct drm_i915_private *i915, i915->vbt.orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; } - if (bdb->version >= 249 && general->afc_startup_config) { + if (i915->vbt.version >= 249 && general->afc_startup_config) { i915->vbt.override_afc_startup = true; i915->vbt.override_afc_startup_val = general->afc_startup_config == 0x1 ? 0x0 : 0x7; } @@ -695,12 +903,11 @@ parse_sdvo_device_mapping(struct drm_i915_private *i915) } static void -parse_driver_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_driver_features(struct drm_i915_private *i915) { const struct bdb_driver_features *driver; - driver = find_section(bdb, BDB_DRIVER_FEATURES); + driver = find_section(i915, BDB_DRIVER_FEATURES); if (!driver) return; @@ -724,13 +931,13 @@ parse_driver_features(struct drm_i915_private *i915, * in the wild with the bits correctly populated. Version * 108 (on i85x) does not have the bits correctly populated. */ - if (bdb->version >= 134 && + if (i915->vbt.version >= 134 && driver->lvds_config != BDB_DRIVER_FEATURE_INT_LVDS && driver->lvds_config != BDB_DRIVER_FEATURE_INT_SDVO_LVDS) i915->vbt.int_lvds_support = 0; } - if (bdb->version < 228) { + if (i915->vbt.version < 228) { drm_dbg_kms(&i915->drm, "DRRS State Enabled:%d\n", driver->drrs_enabled); /* @@ -740,23 +947,22 @@ parse_driver_features(struct drm_i915_private *i915, * driver->drrs_enabled=false */ if (!driver->drrs_enabled) - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; i915->vbt.psr.enable = driver->psr_enabled; } } static void -parse_power_conservation_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_power_conservation_features(struct drm_i915_private *i915) { const struct bdb_lfp_power *power; u8 panel_type = i915->vbt.panel_type; - if (bdb->version < 228) + if (i915->vbt.version < 228) return; - power = find_section(bdb, BDB_LFP_POWER); + power = find_section(i915, BDB_LFP_POWER); if (!power) return; @@ -769,21 +975,21 @@ parse_power_conservation_features(struct drm_i915_private *i915, * power->drrs & BIT(panel_type)=false */ if (!(power->drrs & BIT(panel_type))) - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; - if (bdb->version >= 232) + if (i915->vbt.version >= 232) i915->vbt.edp.hobl = power->hobl & BIT(panel_type); } static void -parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) +parse_edp(struct drm_i915_private *i915) { const struct bdb_edp *edp; const struct edp_power_seq *edp_pps; const struct edp_fast_link_params *edp_link_params; int panel_type = i915->vbt.panel_type; - edp = find_section(bdb, BDB_EDP); + edp = find_section(i915, BDB_EDP); if (!edp) return; @@ -876,7 +1082,7 @@ parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) break; } - if (bdb->version >= 173) { + if (i915->vbt.version >= 173) { u8 vswing; /* Don't read from VBT if module parameter has valid value*/ @@ -888,16 +1094,19 @@ parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) i915->vbt.edp.low_vswing = vswing == 0; } } + + i915->vbt.edp.drrs_msa_timing_delay = + (edp->sdrrs_msa_timing_delay >> (panel_type * 2)) & 3; } static void -parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) +parse_psr(struct drm_i915_private *i915) { const struct bdb_psr *psr; const struct psr_table *psr_table; int panel_type = i915->vbt.panel_type; - psr = find_section(bdb, BDB_PSR); + psr = find_section(i915, BDB_PSR); if (!psr) { drm_dbg_kms(&i915->drm, "No PSR BDB found.\n"); return; @@ -916,7 +1125,7 @@ parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) * New psr options 0=500us, 1=100us, 2=2500us, 3=0us * Old decimal value is wake up time in multiples of 100 us. */ - if (bdb->version >= 205 && + if (i915->vbt.version >= 205 && (DISPLAY_VER(i915) >= 9 && !IS_BROXTON(i915))) { switch (psr_table->tp1_wakeup_time) { case 0: @@ -962,7 +1171,7 @@ parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) i915->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100; } - if (bdb->version >= 226) { + if (i915->vbt.version >= 226) { u32 wakeup_time = psr->psr2_tp2_tp3_wakeup_time; wakeup_time = (wakeup_time >> (2 * panel_type)) & 0x3; @@ -1031,8 +1240,7 @@ static void parse_dsi_backlight_ports(struct drm_i915_private *i915, } static void -parse_mipi_config(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_mipi_config(struct drm_i915_private *i915) { const struct bdb_mipi_config *start; const struct mipi_config *config; @@ -1055,7 +1263,7 @@ parse_mipi_config(struct drm_i915_private *i915, /* Parse #52 for panel index used from panel_type already * parsed */ - start = find_section(bdb, BDB_MIPI_CONFIG); + start = find_section(i915, BDB_MIPI_CONFIG); if (!start) { drm_dbg_kms(&i915->drm, "No MIPI config BDB found"); return; @@ -1082,7 +1290,7 @@ parse_mipi_config(struct drm_i915_private *i915, return; } - parse_dsi_backlight_ports(i915, bdb->version, port); + parse_dsi_backlight_ports(i915, i915->vbt.version, port); /* FIXME is the 90 vs. 270 correct? */ switch (config->rotation) { @@ -1351,8 +1559,7 @@ static void fixup_mipi_sequences(struct drm_i915_private *i915) } static void -parse_mipi_sequence(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_mipi_sequence(struct drm_i915_private *i915) { int panel_type = i915->vbt.panel_type; const struct bdb_mipi_sequence *sequence; @@ -1365,7 +1572,7 @@ parse_mipi_sequence(struct drm_i915_private *i915, if (i915->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID) return; - sequence = find_section(bdb, BDB_MIPI_SEQUENCE); + sequence = find_section(i915, BDB_MIPI_SEQUENCE); if (!sequence) { drm_dbg_kms(&i915->drm, "No MIPI Sequence found, parsing complete\n"); @@ -1436,8 +1643,7 @@ err: } static void -parse_compression_parameters(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_compression_parameters(struct drm_i915_private *i915) { const struct bdb_compression_parameters *params; struct intel_bios_encoder_data *devdata; @@ -1445,10 +1651,10 @@ parse_compression_parameters(struct drm_i915_private *i915, u16 block_size; int index; - if (bdb->version < 198) + if (i915->vbt.version < 198) return; - params = find_section(bdb, BDB_COMPRESSION_PARAMETERS); + params = find_section(i915, BDB_COMPRESSION_PARAMETERS); if (params) { /* Sanity checks */ if (params->entry_size != sizeof(params->data[0])) { @@ -1955,6 +2161,12 @@ static int _intel_bios_max_tmds_clock(const struct intel_bios_encoder_data *devd fallthrough; case HDMI_MAX_DATA_RATE_PLATFORM: return 0; + case HDMI_MAX_DATA_RATE_594: + return 594000; + case HDMI_MAX_DATA_RATE_340: + return 340000; + case HDMI_MAX_DATA_RATE_300: + return 300000; case HDMI_MAX_DATA_RATE_297: return 297000; case HDMI_MAX_DATA_RATE_165: @@ -2077,8 +2289,7 @@ static void parse_ddi_ports(struct drm_i915_private *i915) } static void -parse_general_definitions(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_general_definitions(struct drm_i915_private *i915) { const struct bdb_general_definitions *defs; struct intel_bios_encoder_data *devdata; @@ -2088,7 +2299,7 @@ parse_general_definitions(struct drm_i915_private *i915, u16 block_size; int bus_pin; - defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); + defs = find_section(i915, BDB_GENERAL_DEFINITIONS); if (!defs) { drm_dbg_kms(&i915->drm, "No general definition block is found, no devices defined.\n"); @@ -2108,31 +2319,31 @@ parse_general_definitions(struct drm_i915_private *i915, if (intel_gmbus_is_valid_pin(i915, bus_pin)) i915->vbt.crt_ddc_pin = bus_pin; - if (bdb->version < 106) { + if (i915->vbt.version < 106) { expected_size = 22; - } else if (bdb->version < 111) { + } else if (i915->vbt.version < 111) { expected_size = 27; - } else if (bdb->version < 195) { + } else if (i915->vbt.version < 195) { expected_size = LEGACY_CHILD_DEVICE_CONFIG_SIZE; - } else if (bdb->version == 195) { + } else if (i915->vbt.version == 195) { expected_size = 37; - } else if (bdb->version <= 215) { + } else if (i915->vbt.version <= 215) { expected_size = 38; - } else if (bdb->version <= 237) { + } else if (i915->vbt.version <= 237) { expected_size = 39; } else { expected_size = sizeof(*child); BUILD_BUG_ON(sizeof(*child) < 39); drm_dbg(&i915->drm, "Expected child device config size for VBT version %u not known; assuming %u\n", - bdb->version, expected_size); + i915->vbt.version, expected_size); } /* Flag an error for unexpected size, but continue anyway. */ if (defs->child_dev_size != expected_size) drm_err(&i915->drm, "Unexpected child device config size %u (expected %u for VBT version %u)\n", - defs->child_dev_size, expected_size, bdb->version); + defs->child_dev_size, expected_size, i915->vbt.version); /* The legacy sized child device config is the minimum we need. */ if (defs->child_dev_size < LEGACY_CHILD_DEVICE_CONFIG_SIZE) { @@ -2457,6 +2668,7 @@ void intel_bios_init(struct drm_i915_private *i915) const struct bdb_header *bdb; INIT_LIST_HEAD(&i915->vbt.display_devices); + INIT_LIST_HEAD(&i915->vbt.bdb_blocks); if (!HAS_DISPLAY(i915)) { drm_dbg_kms(&i915->drm, @@ -2488,24 +2700,26 @@ void intel_bios_init(struct drm_i915_private *i915) drm_dbg_kms(&i915->drm, "VBT signature \"%.*s\", BDB version %d\n", - (int)sizeof(vbt->signature), vbt->signature, bdb->version); + (int)sizeof(vbt->signature), vbt->signature, i915->vbt.version); + + init_bdb_blocks(i915, bdb); /* Grab useful general definitions */ - parse_general_features(i915, bdb); - parse_general_definitions(i915, bdb); - parse_panel_options(i915, bdb); - parse_panel_dtd(i915, bdb); - parse_lfp_backlight(i915, bdb); - parse_sdvo_panel_data(i915, bdb); - parse_driver_features(i915, bdb); - parse_power_conservation_features(i915, bdb); - parse_edp(i915, bdb); - parse_psr(i915, bdb); - parse_mipi_config(i915, bdb); - parse_mipi_sequence(i915, bdb); + parse_general_features(i915); + parse_general_definitions(i915); + parse_panel_options(i915); + parse_panel_dtd(i915); + parse_lfp_backlight(i915); + parse_sdvo_panel_data(i915); + parse_driver_features(i915); + parse_power_conservation_features(i915); + parse_edp(i915); + parse_psr(i915); + parse_mipi_config(i915); + parse_mipi_sequence(i915); /* Depends on child device list */ - parse_compression_parameters(i915, bdb); + parse_compression_parameters(i915); out: if (!vbt) { @@ -2527,14 +2741,20 @@ out: */ void intel_bios_driver_remove(struct drm_i915_private *i915) { - struct intel_bios_encoder_data *devdata, *n; + struct intel_bios_encoder_data *devdata, *nd; + struct bdb_block_entry *entry, *ne; - list_for_each_entry_safe(devdata, n, &i915->vbt.display_devices, node) { + list_for_each_entry_safe(devdata, nd, &i915->vbt.display_devices, node) { list_del(&devdata->node); kfree(devdata->dsc); kfree(devdata); } + list_for_each_entry_safe(entry, ne, &i915->vbt.bdb_blocks, node) { + list_del(&entry->node); + kfree(entry); + } + kfree(i915->vbt.sdvo_lvds_vbt_mode); i915->vbt.sdvo_lvds_vbt_mode = NULL; kfree(i915->vbt.lfp_lvds_vbt_mode); |