diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_bios.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_bios.c | 3409 |
1 files changed, 2244 insertions, 1165 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 839124647202..4b41068e9e35 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -25,13 +25,23 @@ * */ -#include <drm/drm_dp_helper.h> +#include <linux/debugfs.h> +#include <linux/firmware.h> -#include "display/intel_display.h" -#include "display/intel_display_types.h" -#include "display/intel_gmbus.h" +#include <drm/display/drm_dp_helper.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_fixed.h> +#include <drm/drm_print.h> -#include "i915_drv.h" +#include "soc/intel_rom.h" + +#include "intel_display.h" +#include "intel_display_core.h" +#include "intel_display_rpm.h" +#include "intel_display_types.h" +#include "intel_display_utils.h" +#include "intel_gmbus.h" #define _INTEL_BIOS_PRIVATE #include "intel_vbt_defs.h" @@ -59,14 +69,16 @@ */ /* Wrapper for VBT child device config */ -struct display_device_data { +struct intel_bios_encoder_data { + struct intel_display *display; + struct child_device_config child; struct dsc_compression_parameters_entry *dsc; struct list_head node; }; -#define SLAVE_ADDR1 0x70 -#define SLAVE_ADDR2 0x72 +#define TARGET_ADDR1 0x70 +#define TARGET_ADDR2 0x72 /* Get BDB block size given a pointer to Block ID. */ static u32 _get_blocksize(const u8 *block_base) @@ -85,7 +97,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; @@ -115,9 +127,413 @@ 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 raw_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; +} + +struct bdb_block_entry { + struct list_head node; + enum bdb_block_id section_id; + u8 data[]; +}; + +static const void * +bdb_find_section(struct intel_display *display, + enum bdb_block_id section_id) +{ + struct bdb_block_entry *entry; + + list_for_each_entry(entry, &display->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_LVDS_DTD, + .min_size = sizeof(struct bdb_sdvo_lvds_dtd), }, + { .section_id = BDB_EDP, + .min_size = sizeof(struct bdb_edp), }, + { .section_id = BDB_LFP_OPTIONS, + .min_size = sizeof(struct bdb_lfp_options), }, + /* + * BDB_LFP_DATA depends on BDB_LFP_DATA_PTRS, + * so keep the two ordered. + */ + { .section_id = BDB_LFP_DATA_PTRS, + .min_size = sizeof(struct bdb_lfp_data_ptrs), }, + { .section_id = BDB_LFP_DATA, + .min_size = 0, /* special case */ }, + { .section_id = BDB_LFP_BACKLIGHT, + .min_size = sizeof(struct bdb_lfp_backlight), }, + { .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 size_t lfp_data_min_size(struct intel_display *display) +{ + const struct bdb_lfp_data_ptrs *ptrs; + size_t size; + + ptrs = bdb_find_section(display, BDB_LFP_DATA_PTRS); + if (!ptrs) + return 0; + + size = sizeof(struct bdb_lfp_data); + if (ptrs->panel_name.table_size) + size = max(size, ptrs->panel_name.offset + + sizeof(struct bdb_lfp_data_tail)); + + return size; +} + +static bool validate_lfp_data_ptrs(const void *bdb, + const struct bdb_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; + const void *data_block; + int i; + + data_block = find_raw_section(bdb, BDB_LFP_DATA); + if (!data_block) + return false; + + data_block_size = get_blocksize(data_block); + if (data_block_size == 0) + return false; + + /* always 3 indicating the presence of fp_timing+dvo_timing+panel_pnp_id */ + if (ptrs->num_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 bdb_edid_dtd) || + panel_pnp_id_size != sizeof(struct bdb_edid_pnp_id)) + return false; + + /* panel_name is not present in old VBTs */ + if (panel_name_size != 0 && + panel_name_size != sizeof(struct bdb_edid_product_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; + + /* 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; + } + + /* + * 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 + 6 + dvo_timing_size + panel_pnp_id_size == lfp_data_size) + fp_timing_size += 6; + + if (fp_timing_size + 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 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; + + /* make sure fp_timing terminators are present at expected locations */ + for (i = 0; i < 16; i++) { + const u16 *t = data_block + ptrs->ptr[i].fp_timing.offset + + fp_timing_size - 2; + + if (*t != 0xffff) + 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_lfp_data_ptrs *ptrs = ptrs_block; + u32 offset; + int i; + + offset = raw_block_offset(bdb, BDB_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 int make_lfp_data_ptr(struct 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_lfp_data_ptr(struct lfp_data_ptr_table *next, + const struct lfp_data_ptr_table *prev, + int size) +{ + next->table_size = prev->table_size; + next->offset = prev->offset + size; +} + +static void *generate_lfp_data_ptrs(struct intel_display *display, + const void *bdb) +{ + int i, size, table_size, block_size, offset, fp_timing_size; + struct bdb_lfp_data_ptrs *ptrs; + const void *block; + void *ptrs_block; + + /* + * The hardcoded fp_timing_size is only valid for + * modernish VBTs. All older VBTs definitely should + * include block 41 and thus we don't need to + * generate one. + */ + if (display->vbt.version < 155) + return NULL; + + fp_timing_size = 38; + + block = find_raw_section(bdb, BDB_LFP_DATA); + if (!block) + return NULL; + + drm_dbg_kms(display->drm, "Generating LFP data table pointers\n"); + + block_size = get_blocksize(block); + + size = fp_timing_size + sizeof(struct bdb_edid_dtd) + + sizeof(struct bdb_edid_pnp_id); + if (size * 16 > block_size) + return NULL; + + ptrs_block = kzalloc(sizeof(*ptrs) + 3, GFP_KERNEL); + if (!ptrs_block) + return NULL; + + *(u8 *)(ptrs_block + 0) = BDB_LFP_DATA_PTRS; + *(u16 *)(ptrs_block + 1) = sizeof(*ptrs); + ptrs = ptrs_block + 3; + + table_size = sizeof(struct bdb_edid_pnp_id); + size = make_lfp_data_ptr(&ptrs->ptr[0].panel_pnp_id, table_size, size); + + table_size = sizeof(struct bdb_edid_dtd); + size = make_lfp_data_ptr(&ptrs->ptr[0].dvo_timing, table_size, size); + + table_size = fp_timing_size; + size = make_lfp_data_ptr(&ptrs->ptr[0].fp_timing, table_size, size); + + if (ptrs->ptr[0].fp_timing.table_size) + ptrs->num_entries++; + if (ptrs->ptr[0].dvo_timing.table_size) + ptrs->num_entries++; + if (ptrs->ptr[0].panel_pnp_id.table_size) + ptrs->num_entries++; + + if (size != 0 || ptrs->num_entries != 3) { + kfree(ptrs_block); + return NULL; + } + + size = fp_timing_size + sizeof(struct bdb_edid_dtd) + + sizeof(struct bdb_edid_pnp_id); + for (i = 1; i < 16; i++) { + next_lfp_data_ptr(&ptrs->ptr[i].fp_timing, &ptrs->ptr[i-1].fp_timing, size); + next_lfp_data_ptr(&ptrs->ptr[i].dvo_timing, &ptrs->ptr[i-1].dvo_timing, size); + next_lfp_data_ptr(&ptrs->ptr[i].panel_pnp_id, &ptrs->ptr[i-1].panel_pnp_id, size); + } + + table_size = sizeof(struct bdb_edid_product_name); + + if (16 * (size + table_size) <= block_size) { + ptrs->panel_name.table_size = table_size; + ptrs->panel_name.offset = size * 16; + } + + offset = block - bdb; + + for (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.table_size) + ptrs->panel_name.offset += offset; + + return ptrs_block; +} + static void -fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, - const struct lvds_dvo_timing *dvo_timing) +init_bdb_block(struct intel_display *display, + const void *bdb, enum bdb_block_id section_id, + size_t min_size) +{ + struct bdb_block_entry *entry; + void *temp_block = NULL; + const void *block; + size_t block_size; + + block = find_raw_section(bdb, section_id); + + /* Modern VBTs lack the LFP data table pointers block, make one up */ + if (!block && section_id == BDB_LFP_DATA_PTRS) { + temp_block = generate_lfp_data_ptrs(display, bdb); + if (temp_block) + block = temp_block + 3; + } + if (!block) + return; + + drm_WARN(display->drm, min_size == 0, + "Block %d min_size is zero\n", section_id); + + block_size = get_blocksize(block); + + /* + * Version number and new block size are considered + * part of the header for MIPI sequenece block v3+. + */ + if (section_id == BDB_MIPI_SEQUENCE && *(const u8 *)block >= 3) + block_size += 5; + + entry = kzalloc(struct_size(entry, data, max(min_size, block_size) + 3), + GFP_KERNEL); + if (!entry) { + kfree(temp_block); + return; + } + + entry->section_id = section_id; + memcpy(entry->data, block - 3, block_size + 3); + + kfree(temp_block); + + drm_dbg_kms(display->drm, + "Found BDB block %d (size %zu, min size %zu)\n", + section_id, block_size, min_size); + + if (section_id == BDB_LFP_DATA_PTRS && + !fixup_lfp_data_ptrs(bdb, entry->data + 3)) { + drm_err(display->drm, + "VBT has malformed LFP data table pointers\n"); + kfree(entry); + return; + } + + list_add_tail(&entry->node, &display->vbt.bdb_blocks); +} + +static void init_bdb_blocks(struct intel_display *display, + 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; + + if (section_id == BDB_LFP_DATA) + min_size = lfp_data_min_size(display); + + init_bdb_block(display, bdb, section_id, min_size); + } +} + +static void +fill_detail_timing_data(struct intel_display *display, + struct drm_display_mode *panel_fixed_mode, + const struct bdb_edid_dtd *dvo_timing) { panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | dvo_timing->hactive_lo; @@ -156,97 +572,249 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, panel_fixed_mode->height_mm = (dvo_timing->vimage_hi << 8) | dvo_timing->vimage_lo; - /* Some VBTs have bogus h/vtotal values */ - if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) - panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; - if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) - panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; + /* Some VBTs have bogus h/vsync_end values */ + if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) { + drm_dbg_kms(display->drm, "reducing hsync_end %d->%d\n", + panel_fixed_mode->hsync_end, panel_fixed_mode->htotal); + panel_fixed_mode->hsync_end = panel_fixed_mode->htotal; + } + if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) { + drm_dbg_kms(display->drm, "reducing vsync_end %d->%d\n", + panel_fixed_mode->vsync_end, panel_fixed_mode->vtotal); + panel_fixed_mode->vsync_end = panel_fixed_mode->vtotal; + } drm_mode_set_name(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, - int index) +static const struct bdb_edid_dtd * +get_lfp_dvo_timing(const struct bdb_lfp_data *data, + const struct bdb_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 (const void *)data + ptrs->ptr[index].dvo_timing.offset; +} - return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); +static const struct fp_timing * +get_lfp_fp_timing(const struct bdb_lfp_data *data, + const struct bdb_lfp_data_ptrs *ptrs, + int index) +{ + return (const void *)data + ptrs->ptr[index].fp_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, - const struct bdb_lvds_lfp_data_ptrs *ptrs, - int index) +static const struct drm_edid_product_id * +get_lfp_pnp_id(const struct bdb_lfp_data *data, + const struct bdb_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; + /* These two are supposed to have the same layout in memory. */ + BUILD_BUG_ON(sizeof(struct bdb_edid_pnp_id) != sizeof(struct drm_edid_product_id)); - 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 (const void *)data + ptrs->ptr[index].panel_pnp_id.offset; +} + +static const struct bdb_lfp_data_tail * +get_lfp_data_tail(const struct bdb_lfp_data *data, + const struct bdb_lfp_data_ptrs *ptrs) +{ + if (ptrs->panel_name.table_size) + return (const void *)data + ptrs->panel_name.offset; + else return NULL; - return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs); +} + +static int opregion_get_panel_type(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback) +{ + return intel_opregion_get_panel_type(display); +} + +static int vbt_get_panel_type(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback) +{ + const struct bdb_lfp_options *lfp_options; + + lfp_options = bdb_find_section(display, BDB_LFP_OPTIONS); + if (!lfp_options) + return -1; + + if (lfp_options->panel_type > 0xf && + lfp_options->panel_type != 0xff) { + drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n", + lfp_options->panel_type); + return -1; + } + + if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) + return lfp_options->panel_type2; + + drm_WARN_ON(display->drm, + devdata && devdata->child.handle != DEVICE_HANDLE_LFP1); + + return lfp_options->panel_type; +} + +static int pnpid_get_panel_type(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback) +{ + const struct bdb_lfp_data *data; + const struct bdb_lfp_data_ptrs *ptrs; + struct drm_edid_product_id product_id, product_id_nodate; + struct drm_printer p; + int i, best = -1; + + if (!drm_edid) + return -1; + + drm_edid_get_product_id(drm_edid, &product_id); + + product_id_nodate = product_id; + product_id_nodate.week_of_manufacture = 0; + product_id_nodate.year_of_manufacture = 0; + + p = drm_dbg_printer(display->drm, DRM_UT_KMS, "EDID"); + drm_edid_print_product_id(&p, &product_id, true); + + ptrs = bdb_find_section(display, BDB_LFP_DATA_PTRS); + if (!ptrs) + return -1; + + data = bdb_find_section(display, BDB_LFP_DATA); + if (!data) + return -1; + + for (i = 0; i < 16; i++) { + const struct drm_edid_product_id *vbt_id = + get_lfp_pnp_id(data, ptrs, i); + + /* full match? */ + if (!memcmp(vbt_id, &product_id, sizeof(*vbt_id))) + return i; + + /* + * Accept a match w/o date if no full match is found, + * and the VBT entry does not specify a date. + */ + if (best < 0 && + !memcmp(vbt_id, &product_id_nodate, sizeof(*vbt_id))) + best = i; + } + + return best; +} + +static int fallback_get_panel_type(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback) +{ + return use_fallback ? 0 : -1; +} + +enum panel_type { + PANEL_TYPE_OPREGION, + PANEL_TYPE_VBT, + PANEL_TYPE_PNPID, + PANEL_TYPE_FALLBACK, +}; + +static int get_panel_type(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback) +{ + struct { + const char *name; + int (*get_panel_type)(struct intel_display *display, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, bool use_fallback); + int panel_type; + } panel_types[] = { + [PANEL_TYPE_OPREGION] = { + .name = "OpRegion", + .get_panel_type = opregion_get_panel_type, + }, + [PANEL_TYPE_VBT] = { + .name = "VBT", + .get_panel_type = vbt_get_panel_type, + }, + [PANEL_TYPE_PNPID] = { + .name = "PNPID", + .get_panel_type = pnpid_get_panel_type, + }, + [PANEL_TYPE_FALLBACK] = { + .name = "fallback", + .get_panel_type = fallback_get_panel_type, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(panel_types); i++) { + panel_types[i].panel_type = panel_types[i].get_panel_type(display, devdata, + drm_edid, use_fallback); + + drm_WARN_ON(display->drm, panel_types[i].panel_type > 0xf && + panel_types[i].panel_type != 0xff); + + if (panel_types[i].panel_type >= 0) + drm_dbg_kms(display->drm, "Panel type (%s): %d\n", + panel_types[i].name, panel_types[i].panel_type); + } + + if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + i = PANEL_TYPE_OPREGION; + else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff && + panel_types[PANEL_TYPE_PNPID].panel_type >= 0) + i = PANEL_TYPE_PNPID; + else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff && + panel_types[PANEL_TYPE_VBT].panel_type >= 0) + i = PANEL_TYPE_VBT; + else + i = PANEL_TYPE_FALLBACK; + + drm_dbg_kms(display->drm, "Selected panel type (%s): %d\n", + panel_types[i].name, panel_types[i].panel_type); + + return panel_types[i].panel_type; +} + +static unsigned int panel_bits(unsigned int value, int panel_type, int num_bits) +{ + return (value >> (panel_type * num_bits)) & (BIT(num_bits) - 1); +} + +static bool panel_bool(unsigned int value, int panel_type) +{ + return panel_bits(value, panel_type, 1); } /* Parse general panel options */ static void -parse_panel_options(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_panel_options(struct intel_display *display, + struct intel_panel *panel) { - const struct bdb_lvds_options *lvds_options; - int panel_type; + const struct bdb_lfp_options *lfp_options; + int panel_type = panel->vbt.panel_type; int drrs_mode; - int ret; - lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); - if (!lvds_options) + lfp_options = bdb_find_section(display, BDB_LFP_OPTIONS); + if (!lfp_options) return; - dev_priv->vbt.lvds_dither = lvds_options->pixel_dither; - - ret = intel_opregion_get_panel_type(dev_priv); - if (ret >= 0) { - drm_WARN_ON(&dev_priv->drm, ret > 0xf); - panel_type = ret; - drm_dbg_kms(&dev_priv->drm, "Panel type: %d (OpRegion)\n", - panel_type); - } else { - if (lvds_options->panel_type > 0xf) { - drm_dbg_kms(&dev_priv->drm, - "Invalid VBT panel type 0x%x\n", - lvds_options->panel_type); - return; - } - panel_type = lvds_options->panel_type; - drm_dbg_kms(&dev_priv->drm, "Panel type: %d (VBT)\n", - panel_type); - } + panel->vbt.lvds_dither = lfp_options->pixel_dither; - dev_priv->vbt.panel_type = panel_type; + /* + * Empirical evidence indicates the block size can be + * either 4,14,16,24+ bytes. For older VBTs no clear + * relationship between the block size vs. BDB version. + */ + if (get_blocksize(lfp_options) < 16) + return; - drrs_mode = (lvds_options->dps_panel_type_bits - >> (panel_type * 2)) & MODE_MASK; + drrs_mode = panel_bits(lfp_options->dps_panel_type_bits, + panel_type, 2); /* * VBT has static DRRS = 0 and seamless DRRS = 2. * The below piece of code is required to adjust vbt.drrs_type @@ -254,107 +822,152 @@ parse_panel_options(struct drm_i915_private *dev_priv, */ switch (drrs_mode) { case 0: - dev_priv->vbt.drrs_type = STATIC_DRRS_SUPPORT; - drm_dbg_kms(&dev_priv->drm, "DRRS supported mode is static\n"); + panel->vbt.drrs_type = DRRS_TYPE_STATIC; + drm_dbg_kms(display->drm, "DRRS supported mode is static\n"); break; case 2: - dev_priv->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT; - drm_dbg_kms(&dev_priv->drm, + panel->vbt.drrs_type = DRRS_TYPE_SEAMLESS; + drm_dbg_kms(display->drm, "DRRS supported mode is seamless\n"); break; default: - dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED; - drm_dbg_kms(&dev_priv->drm, + panel->vbt.drrs_type = DRRS_TYPE_NONE; + drm_dbg_kms(display->drm, "DRRS not supported (VBT input)\n"); break; } } -/* Try to find integrated panel timing data */ static void -parse_lfp_panel_dtd(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_lfp_panel_dtd(struct intel_display *display, + struct intel_panel *panel, + const struct bdb_lfp_data *lfp_data, + const struct bdb_lfp_data_ptrs *lfp_data_ptrs) { - const struct bdb_lvds_lfp_data *lvds_lfp_data; - const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; - const struct lvds_dvo_timing *panel_dvo_timing; - const struct lvds_fp_timing *fp_timing; + const struct bdb_edid_dtd *panel_dvo_timing; + const struct fp_timing *fp_timing; struct drm_display_mode *panel_fixed_mode; - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; - lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); - if (!lvds_lfp_data) + panel_dvo_timing = get_lfp_dvo_timing(lfp_data, + lfp_data_ptrs, + panel_type); + + panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); + if (!panel_fixed_mode) return; - lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); - if (!lvds_lfp_data_ptrs) + fill_detail_timing_data(display, panel_fixed_mode, panel_dvo_timing); + + panel->vbt.lfp_vbt_mode = panel_fixed_mode; + + drm_dbg_kms(display->drm, + "Found panel mode in BIOS VBT legacy lfp table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); + + fp_timing = get_lfp_fp_timing(lfp_data, + lfp_data_ptrs, + panel_type); + + /* check the resolution, just to be sure */ + if (fp_timing->x_res == panel_fixed_mode->hdisplay && + fp_timing->y_res == panel_fixed_mode->vdisplay) { + panel->vbt.bios_lvds_val = fp_timing->lvds_reg_val; + drm_dbg_kms(display->drm, + "VBT initial LVDS value %x\n", + panel->vbt.bios_lvds_val); + } +} + +static void +parse_lfp_data(struct intel_display *display, + struct intel_panel *panel) +{ + const struct bdb_lfp_data *data; + const struct bdb_lfp_data_tail *tail; + const struct bdb_lfp_data_ptrs *ptrs; + const struct drm_edid_product_id *pnp_id; + struct drm_printer p; + int panel_type = panel->vbt.panel_type; + + ptrs = bdb_find_section(display, BDB_LFP_DATA_PTRS); + if (!ptrs) return; - panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, - lvds_lfp_data_ptrs, - panel_type); + data = bdb_find_section(display, BDB_LFP_DATA); + if (!data) + return; - panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); - if (!panel_fixed_mode) + if (!panel->vbt.lfp_vbt_mode) + parse_lfp_panel_dtd(display, panel, data, ptrs); + + pnp_id = get_lfp_pnp_id(data, ptrs, panel_type); + + p = drm_dbg_printer(display->drm, DRM_UT_KMS, "Panel"); + drm_edid_print_product_id(&p, pnp_id, false); + + tail = get_lfp_data_tail(data, ptrs); + if (!tail) return; - fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing); - - dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; - - drm_dbg_kms(&dev_priv->drm, - "Found panel mode in BIOS VBT legacy lfp table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); - - fp_timing = get_lvds_fp_timing(bdb, 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) { - dev_priv->vbt.bios_lvds_val = fp_timing->lvds_reg_val; - drm_dbg_kms(&dev_priv->drm, - "VBT initial LVDS value %x\n", - dev_priv->vbt.bios_lvds_val); - } + drm_dbg_kms(display->drm, "Panel name: %.*s\n", + (int)sizeof(tail->panel_name[0].name), + tail->panel_name[panel_type].name); + + if (display->vbt.version >= 188) { + panel->vbt.seamless_drrs_min_refresh_rate = + tail->seamless_drrs_min_refresh_rate[panel_type]; + drm_dbg_kms(display->drm, + "Seamless DRRS min refresh rate: %d Hz\n", + panel->vbt.seamless_drrs_min_refresh_rate); } } static void -parse_generic_dtd(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_generic_dtd(struct intel_display *display, + struct intel_panel *panel) { 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); + /* + * Older VBTs provided DTD information for internal displays through + * the "LFP panel tables" block (42). As of VBT revision 229 the + * DTD information should be provided via a newer "generic DTD" + * block (58). Just to be safe, we'll try the new generic DTD block + * first on VBT >= 229, but still fall back to trying the old LFP + * block if that fails. + */ + if (display->vbt.version < 229) + return; + + generic_dtd = bdb_find_section(display, BDB_GENERIC_DTD); if (!generic_dtd) return; if (generic_dtd->gdtd_size < sizeof(struct generic_dtd_entry)) { - drm_err(&dev_priv->drm, "GDTD size %u is too small.\n", + drm_err(display->drm, "GDTD size %u is too small.\n", generic_dtd->gdtd_size); return; } else if (generic_dtd->gdtd_size != sizeof(struct generic_dtd_entry)) { - drm_err(&dev_priv->drm, "Unexpected GDTD size %u\n", + drm_err(display->drm, "Unexpected GDTD size %u\n", generic_dtd->gdtd_size); /* DTD has unknown fields, but keep going */ } num_dtd = (get_blocksize(generic_dtd) - sizeof(struct bdb_generic_dtd)) / generic_dtd->gdtd_size; - if (dev_priv->vbt.panel_type >= num_dtd) { - drm_err(&dev_priv->drm, + if (panel->vbt.panel_type >= num_dtd) { + drm_err(display->drm, "Panel type %d not found in table of %d DTD's\n", - dev_priv->vbt.panel_type, num_dtd); + panel->vbt.panel_type, num_dtd); return; } - dtd = &generic_dtd->dtd[dev_priv->vbt.panel_type]; + dtd = &generic_dtd->dtd[panel->vbt.panel_type]; panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); if (!panel_fixed_mode) @@ -393,45 +1006,28 @@ parse_generic_dtd(struct drm_i915_private *dev_priv, else panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; - drm_dbg_kms(&dev_priv->drm, - "Found panel mode in BIOS VBT generic dtd table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + drm_dbg_kms(display->drm, + "Found panel mode in BIOS VBT generic dtd table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); - dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; -} - -static void -parse_panel_dtd(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) -{ - /* - * Older VBTs provided provided DTD information for internal displays - * through the "LFP panel DTD" block (42). As of VBT revision 229, - * that block is now deprecated and DTD information should be provided - * via a newer "generic DTD" block (58). Just to be safe, we'll - * 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(dev_priv, bdb); - if (!dev_priv->vbt.lfp_lvds_vbt_mode) - parse_lfp_panel_dtd(dev_priv, bdb); + panel->vbt.lfp_vbt_mode = panel_fixed_mode; } static void -parse_lfp_backlight(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_lfp_backlight(struct intel_display *display, + struct intel_panel *panel) { - const struct bdb_lfp_backlight_data *backlight_data; + const struct bdb_lfp_backlight *backlight_data; const struct lfp_backlight_data_entry *entry; - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; + u16 level; - backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT); + backlight_data = bdb_find_section(display, BDB_LFP_BACKLIGHT); if (!backlight_data) return; if (backlight_data->entry_size != sizeof(backlight_data->data[0])) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Unsupported backlight data entry size %u\n", backlight_data->entry_size); return; @@ -439,84 +1035,130 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, entry = &backlight_data->data[panel_type]; - dev_priv->vbt.backlight.present = entry->type == BDB_BACKLIGHT_TYPE_PWM; - if (!dev_priv->vbt.backlight.present) { - drm_dbg_kms(&dev_priv->drm, + panel->vbt.backlight.present = entry->type == BDB_BACKLIGHT_TYPE_PWM; + if (!panel->vbt.backlight.present) { + drm_dbg_kms(display->drm, "PWM backlight not present in VBT (type %u)\n", entry->type); return; } - dev_priv->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI; - if (bdb->version >= 191 && - get_blocksize(backlight_data) >= sizeof(*backlight_data)) { + panel->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI; + panel->vbt.backlight.controller = 0; + if (display->vbt.version >= 191) { const struct lfp_backlight_control_method *method; method = &backlight_data->backlight_control[panel_type]; - dev_priv->vbt.backlight.type = method->type; - dev_priv->vbt.backlight.controller = method->controller; + panel->vbt.backlight.type = method->type; + panel->vbt.backlight.controller = method->controller; + } + + panel->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; + panel->vbt.backlight.active_low_pwm = entry->active_low_pwm; + + if (display->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 (display->vbt.version >= 236) + scale = backlight_data->brightness_precision_bits[panel_type] == 16; + else + scale = level > 255; + + if (scale) + min_level = min_level / 255; + + if (min_level > 255) { + drm_warn(display->drm, "Brightness min level > 255\n"); + level = 255; + } + panel->vbt.backlight.min_brightness = min_level; + + panel->vbt.backlight.brightness_precision_bits = + backlight_data->brightness_precision_bits[panel_type]; + } else { + level = backlight_data->level[panel_type]; + panel->vbt.backlight.min_brightness = entry->min_brightness; } - dev_priv->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; - dev_priv->vbt.backlight.active_low_pwm = entry->active_low_pwm; - dev_priv->vbt.backlight.min_brightness = entry->min_brightness; - drm_dbg_kms(&dev_priv->drm, + if (display->vbt.version >= 239) + panel->vbt.backlight.hdr_dpcd_refresh_timeout = + DIV_ROUND_UP(backlight_data->hdr_dpcd_refresh_timeout[panel_type], 100); + else + panel->vbt.backlight.hdr_dpcd_refresh_timeout = 30; + + drm_dbg_kms(display->drm, "VBT backlight PWM modulation frequency %u Hz, " "active %s, min brightness %u, level %u, controller %u\n", - dev_priv->vbt.backlight.pwm_freq_hz, - dev_priv->vbt.backlight.active_low_pwm ? "low" : "high", - dev_priv->vbt.backlight.min_brightness, - backlight_data->level[panel_type], - dev_priv->vbt.backlight.controller); + panel->vbt.backlight.pwm_freq_hz, + panel->vbt.backlight.active_low_pwm ? "low" : "high", + panel->vbt.backlight.min_brightness, + level, + panel->vbt.backlight.controller); } -/* Try to find sdvo panel data */ static void -parse_sdvo_panel_data(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_sdvo_lvds_data(struct intel_display *display, + struct intel_panel *panel) { - const struct bdb_sdvo_panel_dtds *dtds; + const struct bdb_sdvo_lvds_dtd *dtd; struct drm_display_mode *panel_fixed_mode; int index; - index = i915_modparams.vbt_sdvo_panel_type; + index = display->params.vbt_sdvo_panel_type; if (index == -2) { - drm_dbg_kms(&dev_priv->drm, - "Ignore SDVO panel mode from BIOS VBT tables.\n"); + drm_dbg_kms(display->drm, + "Ignore SDVO LVDS mode from BIOS VBT tables.\n"); return; } 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 = bdb_find_section(display, BDB_SDVO_LVDS_OPTIONS); if (!sdvo_lvds_options) return; index = sdvo_lvds_options->panel_type; } - dtds = find_section(bdb, BDB_SDVO_PANEL_DTDS); - if (!dtds) + dtd = bdb_find_section(display, BDB_SDVO_LVDS_DTD); + if (!dtd) + return; + + /* + * This should not happen, as long as the panel_type + * enumeration doesn't grow over 4 items. But if it does, it + * could lead to hard-to-detect bugs, so better double-check + * it here to be sure. + */ + if (index >= ARRAY_SIZE(dtd->dtd)) { + drm_err(display->drm, + "index %d is larger than dtd->dtd[4] array\n", + index); return; + } panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); if (!panel_fixed_mode) return; - fill_detail_timing_data(panel_fixed_mode, &dtds->dtds[index]); + fill_detail_timing_data(display, panel_fixed_mode, &dtd->dtd[index]); - dev_priv->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; + panel->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; - drm_dbg_kms(&dev_priv->drm, - "Found SDVO panel mode in BIOS VBT tables:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + drm_dbg_kms(display->drm, + "Found SDVO LVDS 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 *dev_priv, +static int intel_bios_ssc_frequency(struct intel_display *display, bool alternate) { - switch (INTEL_GEN(dev_priv)) { + switch (DISPLAY_VER(display)) { case 2: return alternate ? 66667 : 48000; case 3: @@ -528,40 +1170,45 @@ static int intel_bios_ssc_frequency(struct drm_i915_private *dev_priv, } static void -parse_general_features(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_general_features(struct intel_display *display) { const struct bdb_general_features *general; - general = find_section(bdb, BDB_GENERAL_FEATURES); + general = bdb_find_section(display, BDB_GENERAL_FEATURES); if (!general) return; - dev_priv->vbt.int_tv_support = general->int_tv_support; + display->vbt.int_tv_support = general->int_tv_support; /* int_crt_support can't be trusted on earlier platforms */ - if (bdb->version >= 155 && - (HAS_DDI(dev_priv) || IS_VALLEYVIEW(dev_priv))) - dev_priv->vbt.int_crt_support = general->int_crt_support; - dev_priv->vbt.lvds_use_ssc = general->enable_ssc; - dev_priv->vbt.lvds_ssc_freq = - intel_bios_ssc_frequency(dev_priv, general->ssc_freq); - dev_priv->vbt.display_clock_mode = general->display_clock_mode; - dev_priv->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; - if (bdb->version >= 181) { - dev_priv->vbt.orientation = general->rotate_180 ? + if (display->vbt.version >= 155 && + (HAS_DDI(display) || display->platform.valleyview)) + display->vbt.int_crt_support = general->int_crt_support; + display->vbt.lvds_use_ssc = general->enable_ssc; + display->vbt.lvds_ssc_freq = + intel_bios_ssc_frequency(display, general->ssc_freq); + display->vbt.display_clock_mode = general->display_clock_mode; + display->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; + if (display->vbt.version >= 181) { + display->vbt.orientation = general->rotate_180 ? DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP : DRM_MODE_PANEL_ORIENTATION_NORMAL; } else { - dev_priv->vbt.orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + display->vbt.orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; } - drm_dbg_kms(&dev_priv->drm, + + if (display->vbt.version >= 249 && general->afc_startup_config) { + display->vbt.override_afc_startup = true; + display->vbt.override_afc_startup_val = general->afc_startup_config == 1 ? 0 : 7; + } + + drm_dbg_kms(display->drm, "BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d fdi_rx_polarity_inverted %d\n", - dev_priv->vbt.int_tv_support, - dev_priv->vbt.int_crt_support, - dev_priv->vbt.lvds_use_ssc, - dev_priv->vbt.lvds_ssc_freq, - dev_priv->vbt.display_clock_mode, - dev_priv->vbt.fdi_rx_polarity_inverted); + display->vbt.int_tv_support, + display->vbt.int_crt_support, + display->vbt.lvds_use_ssc, + display->vbt.lvds_ssc_freq, + display->vbt.display_clock_mode, + display->vbt.fdi_rx_polarity_inverted); } static const struct child_device_config * @@ -571,29 +1218,28 @@ child_device_ptr(const struct bdb_general_definitions *defs, int i) } static void -parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, u8 bdb_version) +parse_sdvo_device_mapping(struct intel_display *display) { - struct sdvo_device_mapping *mapping; - const struct display_device_data *devdata; - const struct child_device_config *child; + const struct intel_bios_encoder_data *devdata; int count = 0; /* * Only parse SDVO mappings on gens that could have SDVO. This isn't * accurate and doesn't have to be, as long as it's not too strict. */ - if (!IS_GEN_RANGE(dev_priv, 3, 7)) { - drm_dbg_kms(&dev_priv->drm, "Skipping SDVO device mapping\n"); + if (!IS_DISPLAY_VER(display, 3, 7)) { + drm_dbg_kms(display->drm, "Skipping SDVO device mapping\n"); return; } - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; + struct sdvo_device_mapping *mapping; - if (child->slave_addr != SLAVE_ADDR1 && - child->slave_addr != SLAVE_ADDR2) { + if (child->target_addr != TARGET_ADDR1 && + child->target_addr != TARGET_ADDR2) { /* - * If the slave address is neither 0x70 nor 0x72, + * If the target address is neither 0x70 nor 0x72, * it is not a SDVO device. Skip it. */ continue; @@ -601,39 +1247,39 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, u8 bdb_version) if (child->dvo_port != DEVICE_PORT_DVOB && child->dvo_port != DEVICE_PORT_DVOC) { /* skip the incorrect SDVO port */ - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Incorrect SDVO port. Skip it\n"); continue; } - drm_dbg_kms(&dev_priv->drm, - "the SDVO device with slave addr %2x is found on" + drm_dbg_kms(display->drm, + "the SDVO device with target addr %2x is found on" " %s port\n", - child->slave_addr, + child->target_addr, (child->dvo_port == DEVICE_PORT_DVOB) ? "SDVOB" : "SDVOC"); - mapping = &dev_priv->vbt.sdvo_mappings[child->dvo_port - 1]; + mapping = &display->vbt.sdvo_mappings[child->dvo_port - 1]; if (!mapping->initialized) { mapping->dvo_port = child->dvo_port; - mapping->slave_addr = child->slave_addr; + mapping->target_addr = child->target_addr; mapping->dvo_wiring = child->dvo_wiring; mapping->ddc_pin = child->ddc_pin; mapping->i2c_pin = child->i2c_pin; mapping->initialized = 1; - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n", - mapping->dvo_port, mapping->slave_addr, + mapping->dvo_port, mapping->target_addr, mapping->dvo_wiring, mapping->ddc_pin, mapping->i2c_pin); } else { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Maybe one SDVO port is shared by " "two SDVO device.\n"); } - if (child->slave2_addr) { + if (child->target2_addr) { /* Maybe this is a SDVO device with multiple inputs */ /* And the mapping info is not added */ - drm_dbg_kms(&dev_priv->drm, - "there exists the slave2_addr. Maybe this" + drm_dbg_kms(display->drm, + "there exists the target2_addr. Maybe this" " is a SDVO device with multiple inputs.\n"); } count++; @@ -641,29 +1287,28 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, u8 bdb_version) if (!count) { /* No SDVO device info is found */ - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "No SDVO device info is found in VBT\n"); } } static void -parse_driver_features(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_driver_features(struct intel_display *display) { const struct bdb_driver_features *driver; - driver = find_section(bdb, BDB_DRIVER_FEATURES); + driver = bdb_find_section(display, BDB_DRIVER_FEATURES); if (!driver) return; - if (INTEL_GEN(dev_priv) >= 5) { + if (DISPLAY_VER(display) >= 5) { /* * Note that we consider BDB_DRIVER_FEATURE_INT_SDVO_LVDS * to mean "eDP". The VBT spec doesn't agree with that * interpretation, but real world VBTs seem to. */ if (driver->lvds_config != BDB_DRIVER_FEATURE_INT_LVDS) - dev_priv->vbt.int_lvds_support = 0; + display->vbt.int_lvds_support = 0; } else { /* * FIXME it's not clear which BDB version has the LVDS config @@ -676,14 +1321,25 @@ parse_driver_features(struct drm_i915_private *dev_priv, * in the wild with the bits correctly populated. Version * 108 (on i85x) does not have the bits correctly populated. */ - if (bdb->version >= 134 && + if (display->vbt.version >= 134 && driver->lvds_config != BDB_DRIVER_FEATURE_INT_LVDS && driver->lvds_config != BDB_DRIVER_FEATURE_INT_SDVO_LVDS) - dev_priv->vbt.int_lvds_support = 0; + display->vbt.int_lvds_support = 0; } +} + +static void +parse_panel_driver_features(struct intel_display *display, + struct intel_panel *panel) +{ + const struct bdb_driver_features *driver; + + driver = bdb_find_section(display, BDB_DRIVER_FEATURES); + if (!driver) + return; - if (bdb->version < 228) { - drm_dbg_kms(&dev_priv->drm, "DRRS State Enabled:%d\n", + if (display->vbt.version < 228) { + drm_dbg_kms(display->drm, "DRRS State Enabled:%d\n", driver->drrs_enabled); /* * If DRRS is not supported, drrs_type has to be set to 0. @@ -691,28 +1347,38 @@ parse_driver_features(struct drm_i915_private *dev_priv, * static DRRS is 0 and DRRS not supported is represented by * driver->drrs_enabled=false */ - if (!driver->drrs_enabled) - dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED; + if (!driver->drrs_enabled && panel->vbt.drrs_type != DRRS_TYPE_NONE) { + /* + * FIXME Should DMRRS perhaps be treated as seamless + * but without the automatic downclocking? + */ + if (driver->dmrrs_enabled) + panel->vbt.drrs_type = DRRS_TYPE_STATIC; + else + panel->vbt.drrs_type = DRRS_TYPE_NONE; + } - dev_priv->vbt.psr.enable = driver->psr_enabled; + panel->vbt.psr.enable = driver->psr_enabled; } } static void -parse_power_conservation_features(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_power_conservation_features(struct intel_display *display, + struct intel_panel *panel) { const struct bdb_lfp_power *power; - u8 panel_type = dev_priv->vbt.panel_type; + u8 panel_type = panel->vbt.panel_type; + + panel->vbt.vrr = true; /* matches Windows behaviour */ - if (bdb->version < 228) + if (display->vbt.version < 228) return; - power = find_section(bdb, BDB_LFP_POWER); + power = bdb_find_section(display, BDB_LFP_POWER); if (!power) return; - dev_priv->vbt.psr.enable = power->psr & BIT(panel_type); + panel->vbt.psr.enable = panel_bool(power->psr, panel_type); /* * If DRRS is not supported, drrs_type has to be set to 0. @@ -720,66 +1386,99 @@ parse_power_conservation_features(struct drm_i915_private *dev_priv, * static DRRS is 0 and DRRS not supported is represented by * power->drrs & BIT(panel_type)=false */ - if (!(power->drrs & BIT(panel_type))) - dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED; + if (!panel_bool(power->drrs, panel_type) && panel->vbt.drrs_type != DRRS_TYPE_NONE) { + /* + * FIXME Should DMRRS perhaps be treated as seamless + * but without the automatic downclocking? + */ + if (panel_bool(power->dmrrs, panel_type)) + panel->vbt.drrs_type = DRRS_TYPE_STATIC; + else + panel->vbt.drrs_type = DRRS_TYPE_NONE; + } + + if (display->vbt.version >= 232) + panel->vbt.edp.hobl = panel_bool(power->hobl, panel_type); + + if (display->vbt.version >= 233) + panel->vbt.vrr = panel_bool(power->vrr_feature_enabled, + panel_type); +} + +static void vbt_edp_to_pps_delays(struct intel_pps_delays *pps, + const struct edp_power_seq *edp_pps) +{ + pps->power_up = edp_pps->t1_t3; + pps->backlight_on = edp_pps->t8; + pps->backlight_off = edp_pps->t9; + pps->power_down = edp_pps->t10; + pps->power_cycle = edp_pps->t11_t12; } static void -parse_edp(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) +parse_edp(struct intel_display *display, + struct intel_panel *panel) { const struct bdb_edp *edp; - const struct edp_power_seq *edp_pps; const struct edp_fast_link_params *edp_link_params; - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; - edp = find_section(bdb, BDB_EDP); + edp = bdb_find_section(display, BDB_EDP); if (!edp) return; - switch ((edp->color_depth >> (panel_type * 2)) & 3) { + switch (panel_bits(edp->color_depth, panel_type, 2)) { case EDP_18BPP: - dev_priv->vbt.edp.bpp = 18; + panel->vbt.edp.bpp = 18; break; case EDP_24BPP: - dev_priv->vbt.edp.bpp = 24; + panel->vbt.edp.bpp = 24; break; case EDP_30BPP: - dev_priv->vbt.edp.bpp = 30; + panel->vbt.edp.bpp = 30; break; } /* Get the eDP sequencing and link info */ - edp_pps = &edp->power_seqs[panel_type]; edp_link_params = &edp->fast_link_params[panel_type]; - dev_priv->vbt.edp.pps = *edp_pps; + vbt_edp_to_pps_delays(&panel->vbt.edp.pps, + &edp->power_seqs[panel_type]); - switch (edp_link_params->rate) { - case EDP_RATE_1_62: - dev_priv->vbt.edp.rate = DP_LINK_BW_1_62; - break; - case EDP_RATE_2_7: - dev_priv->vbt.edp.rate = DP_LINK_BW_2_7; - break; - default: - drm_dbg_kms(&dev_priv->drm, - "VBT has unknown eDP link rate value %u\n", - edp_link_params->rate); - break; + if (display->vbt.version >= 224) { + panel->vbt.edp.rate = + edp->edp_fast_link_training_rate[panel_type] * 20; + } else { + switch (edp_link_params->rate) { + case EDP_RATE_1_62: + panel->vbt.edp.rate = 162000; + break; + case EDP_RATE_2_7: + panel->vbt.edp.rate = 270000; + break; + case EDP_RATE_5_4: + panel->vbt.edp.rate = 540000; + break; + default: + drm_dbg_kms(display->drm, + "VBT has unknown eDP link rate value %u\n", + edp_link_params->rate); + break; + } } switch (edp_link_params->lanes) { case EDP_LANE_1: - dev_priv->vbt.edp.lanes = 1; + panel->vbt.edp.lanes = 1; break; case EDP_LANE_2: - dev_priv->vbt.edp.lanes = 2; + panel->vbt.edp.lanes = 2; break; case EDP_LANE_4: - dev_priv->vbt.edp.lanes = 4; + panel->vbt.edp.lanes = 4; break; default: - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT has unknown eDP lane count value %u\n", edp_link_params->lanes); break; @@ -787,19 +1486,19 @@ parse_edp(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) switch (edp_link_params->preemphasis) { case EDP_PREEMPHASIS_NONE: - dev_priv->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; + panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; break; case EDP_PREEMPHASIS_3_5dB: - dev_priv->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; + panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; break; case EDP_PREEMPHASIS_6dB: - dev_priv->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; + panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; break; case EDP_PREEMPHASIS_9_5dB: - dev_priv->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; + panel->vbt.edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; break; default: - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT has unknown eDP pre-emphasis value %u\n", edp_link_params->preemphasis); break; @@ -807,135 +1506,123 @@ parse_edp(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) switch (edp_link_params->vswing) { case EDP_VSWING_0_4V: - dev_priv->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; + panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; break; case EDP_VSWING_0_6V: - dev_priv->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; + panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; break; case EDP_VSWING_0_8V: - dev_priv->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; + panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; break; case EDP_VSWING_1_2V: - dev_priv->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; + panel->vbt.edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; break; default: - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT has unknown eDP voltage swing value %u\n", edp_link_params->vswing); break; } - if (bdb->version >= 173) { + if (display->vbt.version >= 173) { u8 vswing; /* Don't read from VBT if module parameter has valid value*/ - if (i915_modparams.edp_vswing) { - dev_priv->vbt.edp.low_vswing = - i915_modparams.edp_vswing == 1; + if (display->params.edp_vswing) { + panel->vbt.edp.low_vswing = + display->params.edp_vswing == 1; } else { vswing = (edp->edp_vswing_preemph >> (panel_type * 4)) & 0xF; - dev_priv->vbt.edp.low_vswing = vswing == 0; + panel->vbt.edp.low_vswing = vswing == 0; } } + + panel->vbt.edp.drrs_msa_timing_delay = + panel_bits(edp->sdrrs_msa_timing_delay, panel_type, 2); + + if (display->vbt.version >= 244) + panel->vbt.edp.max_link_rate = + edp->edp_max_port_link_rate[panel_type] * 20; + + if (display->vbt.version >= 251) + panel->vbt.edp.dsc_disable = + panel_bool(edp->edp_dsc_disable, panel_type); } static void -parse_psr(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) +parse_psr(struct intel_display *display, + struct intel_panel *panel) { const struct bdb_psr *psr; const struct psr_table *psr_table; - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; - psr = find_section(bdb, BDB_PSR); + psr = bdb_find_section(display, BDB_PSR); if (!psr) { - drm_dbg_kms(&dev_priv->drm, "No PSR BDB found.\n"); + drm_dbg_kms(display->drm, "No PSR BDB found.\n"); return; } psr_table = &psr->psr_table[panel_type]; - dev_priv->vbt.psr.full_link = psr_table->full_link; - dev_priv->vbt.psr.require_aux_wakeup = psr_table->require_aux_to_wakeup; - - /* Allowed VBT values goes from 0 to 15 */ - dev_priv->vbt.psr.idle_frames = psr_table->idle_frames < 0 ? 0 : - psr_table->idle_frames > 15 ? 15 : psr_table->idle_frames; - - switch (psr_table->lines_to_wait) { - case 0: - dev_priv->vbt.psr.lines_to_wait = PSR_0_LINES_TO_WAIT; - break; - case 1: - dev_priv->vbt.psr.lines_to_wait = PSR_1_LINE_TO_WAIT; - break; - case 2: - dev_priv->vbt.psr.lines_to_wait = PSR_4_LINES_TO_WAIT; - break; - case 3: - dev_priv->vbt.psr.lines_to_wait = PSR_8_LINES_TO_WAIT; - break; - default: - drm_dbg_kms(&dev_priv->drm, - "VBT has unknown PSR lines to wait %u\n", - psr_table->lines_to_wait); - break; - } + panel->vbt.psr.full_link = psr_table->full_link; + panel->vbt.psr.require_aux_wakeup = psr_table->require_aux_to_wakeup; + panel->vbt.psr.idle_frames = psr_table->idle_frames; /* * 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 && - (IS_GEN9_BC(dev_priv) || IS_GEMINILAKE(dev_priv) || - INTEL_GEN(dev_priv) >= 10)) { + if (display->vbt.version >= 205 && + (DISPLAY_VER(display) >= 9 && !display->platform.broxton)) { switch (psr_table->tp1_wakeup_time) { case 0: - dev_priv->vbt.psr.tp1_wakeup_time_us = 500; + panel->vbt.psr.tp1_wakeup_time_us = 500; break; case 1: - dev_priv->vbt.psr.tp1_wakeup_time_us = 100; + panel->vbt.psr.tp1_wakeup_time_us = 100; break; case 3: - dev_priv->vbt.psr.tp1_wakeup_time_us = 0; + panel->vbt.psr.tp1_wakeup_time_us = 0; break; default: - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT tp1 wakeup time value %d is outside range[0-3], defaulting to max value 2500us\n", psr_table->tp1_wakeup_time); - /* fallthrough */ + fallthrough; case 2: - dev_priv->vbt.psr.tp1_wakeup_time_us = 2500; + panel->vbt.psr.tp1_wakeup_time_us = 2500; break; } switch (psr_table->tp2_tp3_wakeup_time) { case 0: - dev_priv->vbt.psr.tp2_tp3_wakeup_time_us = 500; + panel->vbt.psr.tp2_tp3_wakeup_time_us = 500; break; case 1: - dev_priv->vbt.psr.tp2_tp3_wakeup_time_us = 100; + panel->vbt.psr.tp2_tp3_wakeup_time_us = 100; break; case 3: - dev_priv->vbt.psr.tp2_tp3_wakeup_time_us = 0; + panel->vbt.psr.tp2_tp3_wakeup_time_us = 0; break; default: - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT tp2_tp3 wakeup time value %d is outside range[0-3], defaulting to max value 2500us\n", psr_table->tp2_tp3_wakeup_time); - /* fallthrough */ + fallthrough; case 2: - dev_priv->vbt.psr.tp2_tp3_wakeup_time_us = 2500; + panel->vbt.psr.tp2_tp3_wakeup_time_us = 2500; break; } } else { - dev_priv->vbt.psr.tp1_wakeup_time_us = psr_table->tp1_wakeup_time * 100; - dev_priv->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100; + panel->vbt.psr.tp1_wakeup_time_us = psr_table->tp1_wakeup_time * 100; + panel->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100; } - if (bdb->version >= 226) { + if (display->vbt.version >= 226) { u32 wakeup_time = psr->psr2_tp2_tp3_wakeup_time; - wakeup_time = (wakeup_time >> (2 * panel_type)) & 0x3; + wakeup_time = panel_bits(wakeup_time, panel_type, 2); switch (wakeup_time) { case 0: wakeup_time = 500; @@ -951,88 +1638,83 @@ parse_psr(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) wakeup_time = 2500; break; } - dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us = wakeup_time; + panel->vbt.psr.psr2_tp2_tp3_wakeup_time_us = wakeup_time; } else { /* Reusing PSR1 wakeup time for PSR2 in older VBTs */ - dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us = dev_priv->vbt.psr.tp2_tp3_wakeup_time_us; + panel->vbt.psr.psr2_tp2_tp3_wakeup_time_us = panel->vbt.psr.tp2_tp3_wakeup_time_us; } } -static void parse_dsi_backlight_ports(struct drm_i915_private *dev_priv, - u16 version, enum port port) +static void parse_dsi_backlight_ports(struct intel_display *display, + struct intel_panel *panel, + enum port port) { - if (!dev_priv->vbt.dsi.config->dual_link || version < 197) { - dev_priv->vbt.dsi.bl_ports = BIT(port); - if (dev_priv->vbt.dsi.config->cabc_supported) - dev_priv->vbt.dsi.cabc_ports = BIT(port); + enum port port_bc = DISPLAY_VER(display) >= 11 ? PORT_B : PORT_C; + + if (!panel->vbt.dsi.config->dual_link || display->vbt.version < 197) { + panel->vbt.dsi.bl_ports = BIT(port); + if (panel->vbt.dsi.config->cabc_supported) + panel->vbt.dsi.cabc_ports = BIT(port); return; } - switch (dev_priv->vbt.dsi.config->dl_dcs_backlight_ports) { + switch (panel->vbt.dsi.config->dl_dcs_backlight_ports) { case DL_DCS_PORT_A: - dev_priv->vbt.dsi.bl_ports = BIT(PORT_A); + panel->vbt.dsi.bl_ports = BIT(PORT_A); break; case DL_DCS_PORT_C: - dev_priv->vbt.dsi.bl_ports = BIT(PORT_C); + panel->vbt.dsi.bl_ports = BIT(port_bc); break; default: case DL_DCS_PORT_A_AND_C: - dev_priv->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(PORT_C); + panel->vbt.dsi.bl_ports = BIT(PORT_A) | BIT(port_bc); break; } - if (!dev_priv->vbt.dsi.config->cabc_supported) + if (!panel->vbt.dsi.config->cabc_supported) return; - switch (dev_priv->vbt.dsi.config->dl_dcs_cabc_ports) { + switch (panel->vbt.dsi.config->dl_dcs_cabc_ports) { case DL_DCS_PORT_A: - dev_priv->vbt.dsi.cabc_ports = BIT(PORT_A); + panel->vbt.dsi.cabc_ports = BIT(PORT_A); break; case DL_DCS_PORT_C: - dev_priv->vbt.dsi.cabc_ports = BIT(PORT_C); + panel->vbt.dsi.cabc_ports = BIT(port_bc); break; default: case DL_DCS_PORT_A_AND_C: - dev_priv->vbt.dsi.cabc_ports = - BIT(PORT_A) | BIT(PORT_C); + panel->vbt.dsi.cabc_ports = + BIT(PORT_A) | BIT(port_bc); break; } } static void -parse_mipi_config(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_mipi_config(struct intel_display *display, + struct intel_panel *panel) { const struct bdb_mipi_config *start; const struct mipi_config *config; const struct mipi_pps_data *pps; - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; enum port port; /* parse MIPI blocks only if LFP type is MIPI */ - if (!intel_bios_is_dsi_present(dev_priv, &port)) + if (!intel_bios_is_dsi_present(display, &port)) return; /* Initialize this to undefined indicating no generic MIPI support */ - dev_priv->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID; + panel->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID; - /* Block #40 is already parsed and panel_fixed_mode is - * stored in dev_priv->lfp_lvds_vbt_mode - * resuse this when needed - */ - - /* Parse #52 for panel index used from panel_type already - * parsed - */ - start = find_section(bdb, BDB_MIPI_CONFIG); + start = bdb_find_section(display, BDB_MIPI_CONFIG); if (!start) { - drm_dbg_kms(&dev_priv->drm, "No MIPI config BDB found"); + drm_dbg_kms(display->drm, "No MIPI config BDB found"); return; } - drm_dbg(&dev_priv->drm, "Found MIPI Config block, panel index = %d\n", - panel_type); + drm_dbg_kms(display->drm, "Found MIPI Config block, panel index = %d\n", + panel_type); /* * get hold of the correct configuration block and pps data as per @@ -1042,17 +1724,17 @@ parse_mipi_config(struct drm_i915_private *dev_priv, pps = &start->pps[panel_type]; /* store as of now full data. Trim when we realise all is not needed */ - dev_priv->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL); - if (!dev_priv->vbt.dsi.config) + panel->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL); + if (!panel->vbt.dsi.config) return; - dev_priv->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL); - if (!dev_priv->vbt.dsi.pps) { - kfree(dev_priv->vbt.dsi.config); + panel->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL); + if (!panel->vbt.dsi.pps) { + kfree(panel->vbt.dsi.config); return; } - parse_dsi_backlight_ports(dev_priv, bdb->version, port); + parse_dsi_backlight_ports(display, panel, port); /* FIXME is the 90 vs. 270 correct? */ switch (config->rotation) { @@ -1061,30 +1743,31 @@ parse_mipi_config(struct drm_i915_private *dev_priv, * Most (all?) VBTs claim 0 degrees despite having * an upside down panel, thus we do not trust this. */ - dev_priv->vbt.dsi.orientation = + panel->vbt.dsi.orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; break; case ENABLE_ROTATION_90: - dev_priv->vbt.dsi.orientation = + panel->vbt.dsi.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; break; case ENABLE_ROTATION_180: - dev_priv->vbt.dsi.orientation = + panel->vbt.dsi.orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; break; case ENABLE_ROTATION_270: - dev_priv->vbt.dsi.orientation = + panel->vbt.dsi.orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; break; } /* We have mandatory mipi config blocks. Initialize as generic panel */ - dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID; + panel->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID; } /* Find the sequence block and size for the given panel. */ static const u8 * -find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, +find_panel_sequence_block(struct intel_display *display, + const struct bdb_mipi_sequence *sequence, u16 panel_id, u32 *seq_size) { u32 total = get_blocksize(sequence); @@ -1101,7 +1784,8 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, for (i = 0; i < MAX_MIPI_CONFIGURATIONS && index < total; i++) { if (index + header_size > total) { - DRM_ERROR("Invalid sequence block (header)\n"); + drm_err(display->drm, + "Invalid sequence block (header)\n"); return NULL; } @@ -1114,7 +1798,7 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, index += header_size; if (index + current_size > total) { - DRM_ERROR("Invalid sequence block\n"); + drm_err(display->drm, "Invalid sequence block\n"); return NULL; } @@ -1126,12 +1810,14 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, index += current_size; } - DRM_ERROR("Sequence block detected but no valid configuration\n"); + drm_err(display->drm, + "Sequence block detected but no valid configuration\n"); return NULL; } -static int goto_next_sequence(const u8 *data, int index, int total) +static int goto_next_sequence(struct intel_display *display, + const u8 *data, int index, int total) { u16 len; @@ -1161,7 +1847,7 @@ static int goto_next_sequence(const u8 *data, int index, int total) len = *(data + index + 6) + 7; break; default: - DRM_ERROR("Unknown operation byte\n"); + drm_err(display->drm, "Unknown operation byte\n"); return 0; } } @@ -1169,7 +1855,8 @@ static int goto_next_sequence(const u8 *data, int index, int total) return 0; } -static int goto_next_sequence_v3(const u8 *data, int index, int total) +static int goto_next_sequence_v3(struct intel_display *display, + const u8 *data, int index, int total) { int seq_end; u16 len; @@ -1180,7 +1867,7 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) * checking on the structure. */ if (total < 5) { - DRM_ERROR("Too small sequence size\n"); + drm_err(display->drm, "Too small sequence size\n"); return 0; } @@ -1197,7 +1884,7 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) seq_end = index + size_of_sequence; if (seq_end > total) { - DRM_ERROR("Invalid sequence size\n"); + drm_err(display->drm, "Invalid sequence size\n"); return 0; } @@ -1207,7 +1894,8 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) if (operation_byte == MIPI_SEQ_ELEM_END) { if (index != seq_end) { - DRM_ERROR("Invalid element structure\n"); + drm_err(display->drm, + "Invalid element structure\n"); return 0; } return index; @@ -1229,8 +1917,8 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) case MIPI_SEQ_ELEM_PMIC: break; default: - DRM_ERROR("Unknown operation byte %u\n", - operation_byte); + drm_err(display->drm, "Unknown operation byte %u\n", + operation_byte); break; } } @@ -1242,13 +1930,14 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) * Get len of pre-fixed deassert fragment from a v1 init OTP sequence, * skip all delay + gpio operands and stop at the first DSI packet op. */ -static int get_init_otp_deassert_fragment_len(struct drm_i915_private *dev_priv) +static int get_init_otp_deassert_fragment_len(struct intel_display *display, + struct intel_panel *panel) { - const u8 *data = dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP]; + const u8 *data = panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP]; int index, len; - if (drm_WARN_ON(&dev_priv->drm, - !data || dev_priv->vbt.dsi.seq_version != 1)) + if (drm_WARN_ON(display->drm, + !data || panel->vbt.dsi.seq_version >= 3)) return 0; /* index = 1 to skip sequence byte */ @@ -1271,60 +1960,93 @@ static int get_init_otp_deassert_fragment_len(struct drm_i915_private *dev_priv) } /* - * Some v1 VBT MIPI sequences do the deassert in the init OTP sequence. + * Some v1/v2 VBT MIPI sequences do the deassert in the init OTP sequence. * The deassert must be done before calling intel_dsi_device_ready, so for * these devices we split the init OTP sequence into a deassert sequence and * the actual init OTP part. */ -static void fixup_mipi_sequences(struct drm_i915_private *dev_priv) +static void vlv_fixup_mipi_sequences(struct intel_display *display, + struct intel_panel *panel) { u8 *init_otp; int len; - /* Limit this to VLV for now. */ - if (!IS_VALLEYVIEW(dev_priv)) - return; - - /* Limit this to v1 vid-mode sequences */ - if (dev_priv->vbt.dsi.config->is_cmd_mode || - dev_priv->vbt.dsi.seq_version != 1) + /* Limit this to v1/v2 vid-mode sequences */ + if (panel->vbt.dsi.config->is_cmd_mode || + panel->vbt.dsi.seq_version >= 3) return; /* Only do this if there are otp and assert seqs and no deassert seq */ - if (!dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] || - !dev_priv->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET] || - dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) + if (!panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] || + !panel->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET] || + panel->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) return; /* The deassert-sequence ends at the first DSI packet */ - len = get_init_otp_deassert_fragment_len(dev_priv); + len = get_init_otp_deassert_fragment_len(display, panel); if (!len) return; - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Using init OTP fragment to deassert reset\n"); /* Copy the fragment, update seq byte and terminate it */ - init_otp = (u8 *)dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP]; - dev_priv->vbt.dsi.deassert_seq = kmemdup(init_otp, len + 1, GFP_KERNEL); - if (!dev_priv->vbt.dsi.deassert_seq) + init_otp = (u8 *)panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP]; + panel->vbt.dsi.deassert_seq = kmemdup(init_otp, len + 1, GFP_KERNEL); + if (!panel->vbt.dsi.deassert_seq) return; - dev_priv->vbt.dsi.deassert_seq[0] = MIPI_SEQ_DEASSERT_RESET; - dev_priv->vbt.dsi.deassert_seq[len] = MIPI_SEQ_ELEM_END; + panel->vbt.dsi.deassert_seq[0] = MIPI_SEQ_DEASSERT_RESET; + panel->vbt.dsi.deassert_seq[len] = MIPI_SEQ_ELEM_END; /* Use the copy for deassert */ - dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET] = - dev_priv->vbt.dsi.deassert_seq; + panel->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET] = + panel->vbt.dsi.deassert_seq; /* Replace the last byte of the fragment with init OTP seq byte */ init_otp[len - 1] = MIPI_SEQ_INIT_OTP; /* And make MIPI_MIPI_SEQ_INIT_OTP point to it */ - dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] = init_otp + len - 1; + panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] = init_otp + len - 1; +} + +/* + * Some machines (eg. Lenovo 82TQ) appear to have broken + * VBT sequences: + * - INIT_OTP is not present at all + * - what should be in INIT_OTP is in DISPLAY_ON + * - what should be in DISPLAY_ON is in BACKLIGHT_ON + * (along with the actual backlight stuff) + * + * To make those work we simply swap DISPLAY_ON and INIT_OTP. + * + * TODO: Do we need to limit this to specific machines, + * or examine the contents of the sequences to + * avoid false positives? + */ +static void icl_fixup_mipi_sequences(struct intel_display *display, + struct intel_panel *panel) +{ + if (!panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP] && + panel->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_ON]) { + drm_dbg_kms(display->drm, + "Broken VBT: Swapping INIT_OTP and DISPLAY_ON sequences\n"); + + swap(panel->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP], + panel->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_ON]); + } +} + +static void fixup_mipi_sequences(struct intel_display *display, + struct intel_panel *panel) +{ + if (DISPLAY_VER(display) >= 11) + icl_fixup_mipi_sequences(display, panel); + else if (display->platform.valleyview) + vlv_fixup_mipi_sequences(display, panel); } static void -parse_mipi_sequence(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_mipi_sequence(struct intel_display *display, + struct intel_panel *panel) { - int panel_type = dev_priv->vbt.panel_type; + int panel_type = panel->vbt.panel_type; const struct bdb_mipi_sequence *sequence; const u8 *seq_data; u32 seq_size; @@ -1332,28 +2054,28 @@ parse_mipi_sequence(struct drm_i915_private *dev_priv, int index = 0; /* Only our generic panel driver uses the sequence block. */ - if (dev_priv->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID) + if (panel->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID) return; - sequence = find_section(bdb, BDB_MIPI_SEQUENCE); + sequence = bdb_find_section(display, BDB_MIPI_SEQUENCE); if (!sequence) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "No MIPI Sequence found, parsing complete\n"); return; } /* Fail gracefully for forward incompatible sequence block. */ if (sequence->version >= 4) { - drm_err(&dev_priv->drm, + drm_err(display->drm, "Unable to parse MIPI Sequence Block v%u\n", sequence->version); return; } - drm_dbg(&dev_priv->drm, "Found MIPI sequence block v%u\n", - sequence->version); + drm_dbg_kms(display->drm, "Found MIPI sequence block v%u\n", + sequence->version); - seq_data = find_panel_sequence_block(sequence, panel_type, &seq_size); + seq_data = find_panel_sequence_block(display, sequence, panel_type, &seq_size); if (!seq_data) return; @@ -1368,87 +2090,85 @@ parse_mipi_sequence(struct drm_i915_private *dev_priv, break; if (seq_id >= MIPI_SEQ_MAX) { - drm_err(&dev_priv->drm, "Unknown sequence %u\n", + drm_err(display->drm, "Unknown sequence %u\n", seq_id); goto err; } /* Log about presence of sequences we won't run. */ if (seq_id == MIPI_SEQ_TEAR_ON || seq_id == MIPI_SEQ_TEAR_OFF) - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Unsupported sequence %u\n", seq_id); - dev_priv->vbt.dsi.sequence[seq_id] = data + index; + panel->vbt.dsi.sequence[seq_id] = data + index; if (sequence->version >= 3) - index = goto_next_sequence_v3(data, index, seq_size); + index = goto_next_sequence_v3(display, data, index, seq_size); else - index = goto_next_sequence(data, index, seq_size); + index = goto_next_sequence(display, data, index, seq_size); if (!index) { - drm_err(&dev_priv->drm, "Invalid sequence %u\n", + drm_err(display->drm, "Invalid sequence %u\n", seq_id); goto err; } } - dev_priv->vbt.dsi.data = data; - dev_priv->vbt.dsi.size = seq_size; - dev_priv->vbt.dsi.seq_version = sequence->version; + panel->vbt.dsi.data = data; + panel->vbt.dsi.size = seq_size; + panel->vbt.dsi.seq_version = sequence->version; - fixup_mipi_sequences(dev_priv); + fixup_mipi_sequences(display, panel); - drm_dbg(&dev_priv->drm, "MIPI related VBT parsing complete\n"); + drm_dbg_kms(display->drm, "MIPI related VBT parsing complete\n"); return; err: kfree(data); - memset(dev_priv->vbt.dsi.sequence, 0, sizeof(dev_priv->vbt.dsi.sequence)); + memset(panel->vbt.dsi.sequence, 0, sizeof(panel->vbt.dsi.sequence)); } static void -parse_compression_parameters(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_compression_parameters(struct intel_display *display) { const struct bdb_compression_parameters *params; - struct display_device_data *devdata; - const struct child_device_config *child; + struct intel_bios_encoder_data *devdata; u16 block_size; int index; - if (bdb->version < 198) + if (display->vbt.version < 198) return; - params = find_section(bdb, BDB_COMPRESSION_PARAMETERS); + params = bdb_find_section(display, BDB_COMPRESSION_PARAMETERS); if (params) { /* Sanity checks */ if (params->entry_size != sizeof(params->data[0])) { - drm_dbg_kms(&i915->drm, + drm_dbg_kms(display->drm, "VBT: unsupported compression param entry size\n"); return; } block_size = get_blocksize(params); if (block_size < sizeof(*params)) { - drm_dbg_kms(&i915->drm, + drm_dbg_kms(display->drm, "VBT: expected 16 compression param entries\n"); return; } } - list_for_each_entry(devdata, &i915->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; if (!child->compression_enable) continue; if (!params) { - drm_dbg_kms(&i915->drm, + drm_dbg_kms(display->drm, "VBT: compression params not available\n"); continue; } if (child->compression_method_cps) { - drm_dbg_kms(&i915->drm, + drm_dbg_kms(display->drm, "VBT: CPS compression not supported\n"); continue; } @@ -1460,423 +2180,684 @@ parse_compression_parameters(struct drm_i915_private *i915, } } -static u8 translate_iboost(u8 val) +static u8 translate_iboost(struct intel_display *display, u8 val) { static const u8 mapping[] = { 1, 3, 7 }; /* See VBT spec */ if (val >= ARRAY_SIZE(mapping)) { - DRM_DEBUG_KMS("Unsupported I_boost value found in VBT (%d), display may not work properly\n", val); + drm_dbg_kms(display->drm, + "Unsupported I_boost value found in VBT (%d), display may not work properly\n", val); return 0; } return mapping[val]; } -static enum port get_port_by_ddc_pin(struct drm_i915_private *i915, u8 ddc_pin) -{ - const struct ddi_vbt_port_info *info; - enum port port; +static const u8 cnp_ddc_pin_map[] = { + [0] = 0, /* N/A */ + [GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B, + [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C, + [GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */ + [GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */ +}; - for_each_port(port) { - info = &i915->vbt.ddi_port_info[port]; +static const u8 icp_ddc_pin_map[] = { + [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C, + [GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1, + [GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2, + [GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3, + [GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4, + [GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5, + [GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6, +}; - if (info->child && ddc_pin == info->alternate_ddc_pin) - return port; - } +static const u8 rkl_pch_tgp_ddc_pin_map[] = { + [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D, + [GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E, +}; - return PORT_NONE; -} +static const u8 adls_ddc_pin_map[] = { + [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4, +}; -static void sanitize_ddc_pin(struct drm_i915_private *dev_priv, - enum port port) -{ - struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port]; - enum port p; +static const u8 gen9bc_tgp_ddc_pin_map[] = { + [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C, + [GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D, +}; - if (!info->alternate_ddc_pin) - return; +static const u8 adlp_ddc_pin_map[] = { + [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4, +}; + +static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin) +{ + const u8 *ddc_pin_map; + int i, n_entries; + + if (INTEL_PCH_TYPE(display) >= PCH_MTL || display->platform.alderlake_p) { + ddc_pin_map = adlp_ddc_pin_map; + n_entries = ARRAY_SIZE(adlp_ddc_pin_map); + } else if (display->platform.alderlake_s) { + ddc_pin_map = adls_ddc_pin_map; + n_entries = ARRAY_SIZE(adls_ddc_pin_map); + } else if (INTEL_PCH_TYPE(display) >= PCH_DG1) { + return vbt_pin; + } else if (display->platform.rocketlake && INTEL_PCH_TYPE(display) == PCH_TGP) { + ddc_pin_map = rkl_pch_tgp_ddc_pin_map; + n_entries = ARRAY_SIZE(rkl_pch_tgp_ddc_pin_map); + } else if (HAS_PCH_TGP(display) && DISPLAY_VER(display) == 9) { + ddc_pin_map = gen9bc_tgp_ddc_pin_map; + n_entries = ARRAY_SIZE(gen9bc_tgp_ddc_pin_map); + } else if (INTEL_PCH_TYPE(display) >= PCH_ICP) { + ddc_pin_map = icp_ddc_pin_map; + n_entries = ARRAY_SIZE(icp_ddc_pin_map); + } else if (HAS_PCH_CNP(display)) { + ddc_pin_map = cnp_ddc_pin_map; + n_entries = ARRAY_SIZE(cnp_ddc_pin_map); + } else { + /* Assuming direct map */ + return vbt_pin; + } - p = get_port_by_ddc_pin(dev_priv, info->alternate_ddc_pin); - if (p != PORT_NONE) { - drm_dbg_kms(&dev_priv->drm, - "port %c trying to use the same DDC pin (0x%x) as port %c, " - "disabling port %c DVI/HDMI support\n", - port_name(port), info->alternate_ddc_pin, - port_name(p), port_name(p)); + for (i = 0; i < n_entries; i++) { + if (ddc_pin_map[i] == vbt_pin) + return i; + } - /* - * If we have multiple ports supposedly sharing the - * pin, then dvi/hdmi couldn't exist on the shared - * port. Otherwise they share the same ddc bin and - * system couldn't communicate with them separately. - * - * Give inverse child device order the priority, - * last one wins. Yes, there are real machines - * (eg. Asrock B250M-HDV) where VBT has both - * port A and port E with the same AUX ch and - * we must pick port E :( - */ - info = &dev_priv->vbt.ddi_port_info[p]; + drm_dbg_kms(display->drm, + "Ignoring alternate pin: VBT claims DDC pin %d, which is not valid for this platform\n", + vbt_pin); + return 0; +} - info->supports_dvi = false; - info->supports_hdmi = false; - info->alternate_ddc_pin = 0; +static u8 dvo_port_type(u8 dvo_port) +{ + switch (dvo_port) { + case DVO_PORT_HDMIA: + case DVO_PORT_HDMIB: + case DVO_PORT_HDMIC: + case DVO_PORT_HDMID: + case DVO_PORT_HDMIE: + case DVO_PORT_HDMIF: + case DVO_PORT_HDMIG: + case DVO_PORT_HDMIH: + case DVO_PORT_HDMII: + return DVO_PORT_HDMIA; + case DVO_PORT_DPA: + case DVO_PORT_DPB: + case DVO_PORT_DPC: + case DVO_PORT_DPD: + case DVO_PORT_DPE: + case DVO_PORT_DPF: + case DVO_PORT_DPG: + case DVO_PORT_DPH: + case DVO_PORT_DPI: + return DVO_PORT_DPA; + case DVO_PORT_MIPIA: + case DVO_PORT_MIPIB: + case DVO_PORT_MIPIC: + case DVO_PORT_MIPID: + return DVO_PORT_MIPIA; + default: + return dvo_port; } } -static enum port get_port_by_aux_ch(struct drm_i915_private *i915, u8 aux_ch) +static enum port __dvo_port_to_port(int n_ports, int n_dvo, + const int port_mapping[][3], u8 dvo_port) { - const struct ddi_vbt_port_info *info; enum port port; + int i; - for_each_port(port) { - info = &i915->vbt.ddi_port_info[port]; + for (port = PORT_A; port < n_ports; port++) { + for (i = 0; i < n_dvo; i++) { + if (port_mapping[port][i] == -1) + break; - if (info->child && aux_ch == info->alternate_aux_channel) - return port; + if (dvo_port == port_mapping[port][i]) + return port; + } } return PORT_NONE; } -static void sanitize_aux_ch(struct drm_i915_private *dev_priv, - enum port port) +static enum port dvo_port_to_port(struct intel_display *display, + u8 dvo_port) { - struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port]; - enum port p; - - if (!info->alternate_aux_channel) - return; - - p = get_port_by_aux_ch(dev_priv, info->alternate_aux_channel); - if (p != PORT_NONE) { - drm_dbg_kms(&dev_priv->drm, - "port %c trying to use the same AUX CH (0x%x) as port %c, " - "disabling port %c DP support\n", - port_name(port), info->alternate_aux_channel, - port_name(p), port_name(p)); + /* + * Each DDI port can have more than one value on the "DVO Port" field, + * so look for all the possible values for each port. + */ + static const int port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_C] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_D] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + [PORT_E] = { DVO_PORT_HDMIE, DVO_PORT_DPE, DVO_PORT_CRT }, + [PORT_F] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1 }, + [PORT_G] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1 }, + [PORT_H] = { DVO_PORT_HDMIH, DVO_PORT_DPH, -1 }, + [PORT_I] = { DVO_PORT_HDMII, DVO_PORT_DPI, -1 }, + }; + /* + * RKL VBT uses PHY based mapping. Combo PHYs A,B,C,D + * map to DDI A,B,TC1,TC2 respectively. + */ + static const int rkl_port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_C] = { -1 }, + [PORT_TC1] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_TC2] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + }; + /* + * Alderlake S ports used in the driver are PORT_A, PORT_D, PORT_E, + * PORT_F and PORT_G, we need to map that to correct VBT sections. + */ + static const int adls_port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { -1 }, + [PORT_C] = { -1 }, + [PORT_TC1] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_TC2] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_TC3] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + [PORT_TC4] = { DVO_PORT_HDMIE, DVO_PORT_DPE, -1 }, + }; + static const int xelpd_port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_C] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_D_XELPD] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + [PORT_E_XELPD] = { DVO_PORT_HDMIE, DVO_PORT_DPE, -1 }, + [PORT_TC1] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1 }, + [PORT_TC2] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1 }, + [PORT_TC3] = { DVO_PORT_HDMIH, DVO_PORT_DPH, -1 }, + [PORT_TC4] = { DVO_PORT_HDMII, DVO_PORT_DPI, -1 }, + }; - /* - * If we have multiple ports supposedlt sharing the - * aux channel, then DP couldn't exist on the shared - * port. Otherwise they share the same aux channel - * and system couldn't communicate with them separately. - * - * Give inverse child device order the priority, - * last one wins. Yes, there are real machines - * (eg. Asrock B250M-HDV) where VBT has both - * port A and port E with the same AUX ch and - * we must pick port E :( - */ - info = &dev_priv->vbt.ddi_port_info[p]; + if (DISPLAY_VER(display) >= 13) + return __dvo_port_to_port(ARRAY_SIZE(xelpd_port_mapping), + ARRAY_SIZE(xelpd_port_mapping[0]), + xelpd_port_mapping, + dvo_port); + else if (display->platform.alderlake_s) + return __dvo_port_to_port(ARRAY_SIZE(adls_port_mapping), + ARRAY_SIZE(adls_port_mapping[0]), + adls_port_mapping, + dvo_port); + else if (display->platform.dg1 || display->platform.rocketlake) + return __dvo_port_to_port(ARRAY_SIZE(rkl_port_mapping), + ARRAY_SIZE(rkl_port_mapping[0]), + rkl_port_mapping, + dvo_port); + else + return __dvo_port_to_port(ARRAY_SIZE(port_mapping), + ARRAY_SIZE(port_mapping[0]), + port_mapping, + dvo_port); +} - info->supports_dp = false; - info->alternate_aux_channel = 0; +static enum port +dsi_dvo_port_to_port(struct intel_display *display, u8 dvo_port) +{ + switch (dvo_port) { + case DVO_PORT_MIPIA: + return PORT_A; + case DVO_PORT_MIPIC: + if (DISPLAY_VER(display) >= 11) + return PORT_B; + else + return PORT_C; + default: + return PORT_NONE; } } -static const u8 cnp_ddc_pin_map[] = { - [0] = 0, /* N/A */ - [DDC_BUS_DDI_B] = GMBUS_PIN_1_BXT, - [DDC_BUS_DDI_C] = GMBUS_PIN_2_BXT, - [DDC_BUS_DDI_D] = GMBUS_PIN_4_CNP, /* sic */ - [DDC_BUS_DDI_F] = GMBUS_PIN_3_BXT, /* sic */ -}; +enum port intel_bios_encoder_port(const struct intel_bios_encoder_data *devdata) +{ + struct intel_display *display = devdata->display; + const struct child_device_config *child = &devdata->child; + enum port port; -static const u8 icp_ddc_pin_map[] = { - [ICL_DDC_BUS_DDI_A] = GMBUS_PIN_1_BXT, - [ICL_DDC_BUS_DDI_B] = GMBUS_PIN_2_BXT, - [TGL_DDC_BUS_DDI_C] = GMBUS_PIN_3_BXT, - [ICL_DDC_BUS_PORT_1] = GMBUS_PIN_9_TC1_ICP, - [ICL_DDC_BUS_PORT_2] = GMBUS_PIN_10_TC2_ICP, - [ICL_DDC_BUS_PORT_3] = GMBUS_PIN_11_TC3_ICP, - [ICL_DDC_BUS_PORT_4] = GMBUS_PIN_12_TC4_ICP, - [TGL_DDC_BUS_PORT_5] = GMBUS_PIN_13_TC5_TGP, - [TGL_DDC_BUS_PORT_6] = GMBUS_PIN_14_TC6_TGP, -}; + port = dvo_port_to_port(display, child->dvo_port); + if (port == PORT_NONE && DISPLAY_VER(display) >= 11) + port = dsi_dvo_port_to_port(display, child->dvo_port); -static u8 map_ddc_pin(struct drm_i915_private *dev_priv, u8 vbt_pin) + return port; +} + +static int parse_bdb_230_dp_max_link_rate(const int vbt_max_link_rate) { - const u8 *ddc_pin_map; - int n_entries; + switch (vbt_max_link_rate) { + default: + case BDB_230_VBT_DP_MAX_LINK_RATE_DEF: + return 0; + case BDB_230_VBT_DP_MAX_LINK_RATE_UHBR20: + return 2000000; + case BDB_230_VBT_DP_MAX_LINK_RATE_UHBR13P5: + return 1350000; + case BDB_230_VBT_DP_MAX_LINK_RATE_UHBR10: + return 1000000; + case BDB_230_VBT_DP_MAX_LINK_RATE_HBR3: + return 810000; + case BDB_230_VBT_DP_MAX_LINK_RATE_HBR2: + return 540000; + case BDB_230_VBT_DP_MAX_LINK_RATE_HBR: + return 270000; + case BDB_230_VBT_DP_MAX_LINK_RATE_LBR: + return 162000; + } +} + +static int parse_bdb_216_dp_max_link_rate(const int vbt_max_link_rate) +{ + switch (vbt_max_link_rate) { + default: + case BDB_216_VBT_DP_MAX_LINK_RATE_HBR3: + return 810000; + case BDB_216_VBT_DP_MAX_LINK_RATE_HBR2: + return 540000; + case BDB_216_VBT_DP_MAX_LINK_RATE_HBR: + return 270000; + case BDB_216_VBT_DP_MAX_LINK_RATE_LBR: + return 162000; + } +} - if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) { - ddc_pin_map = icp_ddc_pin_map; - n_entries = ARRAY_SIZE(icp_ddc_pin_map); - } else if (HAS_PCH_CNP(dev_priv)) { - ddc_pin_map = cnp_ddc_pin_map; - n_entries = ARRAY_SIZE(cnp_ddc_pin_map); - } else { - /* Assuming direct map */ - return vbt_pin; +static u32 edp_rate_override_mask(int rate) +{ + switch (rate) { + case 2000000: return BDB_263_VBT_EDP_LINK_RATE_20; + case 1350000: return BDB_263_VBT_EDP_LINK_RATE_13_5; + case 1000000: return BDB_263_VBT_EDP_LINK_RATE_10; + case 810000: return BDB_263_VBT_EDP_LINK_RATE_8_1; + case 675000: return BDB_263_VBT_EDP_LINK_RATE_6_75; + case 540000: return BDB_263_VBT_EDP_LINK_RATE_5_4; + case 432000: return BDB_263_VBT_EDP_LINK_RATE_4_32; + case 324000: return BDB_263_VBT_EDP_LINK_RATE_3_24; + case 270000: return BDB_263_VBT_EDP_LINK_RATE_2_7; + case 243000: return BDB_263_VBT_EDP_LINK_RATE_2_43; + case 216000: return BDB_263_VBT_EDP_LINK_RATE_2_16; + case 162000: return BDB_263_VBT_EDP_LINK_RATE_1_62; + default: return 0; } +} - if (vbt_pin < n_entries && ddc_pin_map[vbt_pin] != 0) - return ddc_pin_map[vbt_pin]; +int intel_bios_dp_max_link_rate(const struct intel_bios_encoder_data *devdata) +{ + if (!devdata || devdata->display->vbt.version < 216) + return 0; - drm_dbg_kms(&dev_priv->drm, - "Ignoring alternate pin: VBT claims DDC pin %d, which is not valid for this platform\n", - vbt_pin); - return 0; + if (devdata->display->vbt.version >= 230) + return parse_bdb_230_dp_max_link_rate(devdata->child.dp_max_link_rate); + else + return parse_bdb_216_dp_max_link_rate(devdata->child.dp_max_link_rate); } -static enum port dvo_port_to_port(u8 dvo_port) +int intel_bios_dp_max_lane_count(const struct intel_bios_encoder_data *devdata) { - /* - * Each DDI port can have more than one value on the "DVO Port" field, - * so look for all the possible values for each port. - */ - static const int dvo_ports[][3] = { - [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1}, - [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1}, - [PORT_C] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1}, - [PORT_D] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1}, - [PORT_E] = { DVO_PORT_CRT, DVO_PORT_HDMIE, DVO_PORT_DPE}, - [PORT_F] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1}, - [PORT_G] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1}, - }; - enum port port; - int i; + if (!devdata || devdata->display->vbt.version < 244) + return 0; - for (port = PORT_A; port < ARRAY_SIZE(dvo_ports); port++) { - for (i = 0; i < ARRAY_SIZE(dvo_ports[port]); i++) { - if (dvo_ports[port][i] == -1) - break; + return devdata->child.dp_max_lane_count + 1; +} - if (dvo_port == dvo_ports[port][i]) - return port; - } - } +bool +intel_bios_encoder_reject_edp_rate(const struct intel_bios_encoder_data *devdata, + int rate) +{ + if (!devdata || devdata->display->vbt.version < 263) + return false; - return PORT_NONE; + if (devdata->child.edp_data_rate_override == BDB_263_VBT_EDP_RATES_MASK) + return false; + + return devdata->child.edp_data_rate_override & edp_rate_override_mask(rate); } -static void parse_ddi_port(struct drm_i915_private *dev_priv, - struct display_device_data *devdata, - u8 bdb_version) +static void sanitize_device_type(struct intel_bios_encoder_data *devdata, + enum port port) { - const struct child_device_config *child = &devdata->child; - struct ddi_vbt_port_info *info; - bool is_dvi, is_hdmi, is_dp, is_edp, is_crt; - enum port port; + struct intel_display *display = devdata->display; + bool is_hdmi; - port = dvo_port_to_port(child->dvo_port); - if (port == PORT_NONE) + if (port != PORT_A || DISPLAY_VER(display) >= 12) return; - info = &dev_priv->vbt.ddi_port_info[port]; + if (!intel_bios_encoder_supports_dvi(devdata)) + return; - if (info->child) { - drm_dbg_kms(&dev_priv->drm, - "More than one child device for port %c in VBT, using the first.\n", - port_name(port)); + is_hdmi = intel_bios_encoder_supports_hdmi(devdata); + + drm_dbg_kms(display->drm, "VBT claims port A supports DVI%s, ignoring\n", + is_hdmi ? "/HDMI" : ""); + + devdata->child.device_type &= ~DEVICE_TYPE_TMDS_DVI_SIGNALING; + devdata->child.device_type |= DEVICE_TYPE_NOT_HDMI_OUTPUT; +} + +static void sanitize_hdmi_level_shift(struct intel_bios_encoder_data *devdata, + enum port port) +{ + struct intel_display *display = devdata->display; + + if (!intel_bios_encoder_supports_dvi(devdata)) return; - } - is_dvi = child->device_type & DEVICE_TYPE_TMDS_DVI_SIGNALING; - is_dp = child->device_type & DEVICE_TYPE_DISPLAYPORT_OUTPUT; - is_crt = child->device_type & DEVICE_TYPE_ANALOG_OUTPUT; - is_hdmi = is_dvi && (child->device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0; - is_edp = is_dp && (child->device_type & DEVICE_TYPE_INTERNAL_CONNECTOR); + /* + * Some BDW machines (eg. HP Pavilion 15-ab) shipped + * with a HSW VBT where the level shifter value goes + * up to 11, whereas the BDW max is 9. + */ + if (display->platform.broadwell && devdata->child.hdmi_level_shifter_value > 9) { + drm_dbg_kms(display->drm, + "Bogus port %c VBT HDMI level shift %d, adjusting to %d\n", + port_name(port), devdata->child.hdmi_level_shifter_value, 9); - if (port == PORT_A && is_dvi && INTEL_GEN(dev_priv) < 12) { - drm_dbg_kms(&dev_priv->drm, - "VBT claims port A supports DVI%s, ignoring\n", - is_hdmi ? "/HDMI" : ""); - is_dvi = false; - is_hdmi = false; + devdata->child.hdmi_level_shifter_value = 9; } +} - info->supports_dvi = is_dvi; - info->supports_hdmi = is_hdmi; - info->supports_dp = is_dp; - info->supports_edp = is_edp; +static bool +intel_bios_encoder_supports_crt(const struct intel_bios_encoder_data *devdata) +{ + return devdata->child.device_type & DEVICE_TYPE_ANALOG_OUTPUT; +} - if (bdb_version >= 195) - info->supports_typec_usb = child->dp_usb_type_c; +bool +intel_bios_encoder_supports_dvi(const struct intel_bios_encoder_data *devdata) +{ + return devdata->child.device_type & DEVICE_TYPE_TMDS_DVI_SIGNALING; +} - if (bdb_version >= 209) - info->supports_tbt = child->tbt; +bool +intel_bios_encoder_supports_hdmi(const struct intel_bios_encoder_data *devdata) +{ + return intel_bios_encoder_supports_dvi(devdata) && + (devdata->child.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0; +} - drm_dbg_kms(&dev_priv->drm, - "Port %c VBT info: CRT:%d DVI:%d HDMI:%d DP:%d eDP:%d LSPCON:%d USB-Type-C:%d TBT:%d DSC:%d\n", - port_name(port), is_crt, is_dvi, is_hdmi, is_dp, is_edp, - HAS_LSPCON(dev_priv) && child->lspcon, - info->supports_typec_usb, info->supports_tbt, - devdata->dsc != NULL); +bool +intel_bios_encoder_supports_dp(const struct intel_bios_encoder_data *devdata) +{ + return devdata->child.device_type & DEVICE_TYPE_DISPLAYPORT_OUTPUT; +} - if (is_dvi) { - u8 ddc_pin; +bool +intel_bios_encoder_supports_edp(const struct intel_bios_encoder_data *devdata) +{ + return intel_bios_encoder_supports_dp(devdata) && + devdata->child.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR; +} - ddc_pin = map_ddc_pin(dev_priv, child->ddc_pin); - if (intel_gmbus_is_valid_pin(dev_priv, ddc_pin)) { - info->alternate_ddc_pin = ddc_pin; - sanitize_ddc_pin(dev_priv, port); - } else { - drm_dbg_kms(&dev_priv->drm, - "Port %c has invalid DDC pin %d, " - "sticking to defaults\n", - port_name(port), ddc_pin); - } - } +bool +intel_bios_encoder_supports_dsi(const struct intel_bios_encoder_data *devdata) +{ + return devdata->child.device_type & DEVICE_TYPE_MIPI_OUTPUT; +} + +bool +intel_bios_encoder_is_lspcon(const struct intel_bios_encoder_data *devdata) +{ + return devdata && HAS_LSPCON(devdata->display) && devdata->child.lspcon; +} + +/* This is an index in the HDMI/DVI DDI buffer translation table, or -1 */ +int intel_bios_hdmi_level_shift(const struct intel_bios_encoder_data *devdata) +{ + if (!devdata || devdata->display->vbt.version < 158 || + DISPLAY_VER(devdata->display) >= 14) + return -1; - if (is_dp) { - info->alternate_aux_channel = child->aux_channel; + return devdata->child.hdmi_level_shifter_value; +} - sanitize_aux_ch(dev_priv, port); - } +int intel_bios_hdmi_max_tmds_clock(const struct intel_bios_encoder_data *devdata) +{ + if (!devdata || devdata->display->vbt.version < 204) + return 0; - if (bdb_version >= 158) { - /* The VBT HDMI level shift values match the table we have. */ - u8 hdmi_level_shift = child->hdmi_level_shifter_value; - drm_dbg_kms(&dev_priv->drm, - "VBT HDMI level shift for port %c: %d\n", - port_name(port), - hdmi_level_shift); - info->hdmi_level_shift = hdmi_level_shift; - info->hdmi_level_shift_set = true; + switch (devdata->child.hdmi_max_data_rate) { + default: + MISSING_CASE(devdata->child.hdmi_max_data_rate); + 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: + return 165000; } +} - if (bdb_version >= 204) { - int max_tmds_clock; +static bool is_port_valid(struct intel_display *display, enum port port) +{ + /* + * On some ICL SKUs port F is not present, but broken VBTs mark + * the port as present. Only try to initialize port F for the + * SKUs that may actually have it. + */ + if (port == PORT_F && display->platform.icelake) + return display->platform.icelake_port_f; - switch (child->hdmi_max_data_rate) { - default: - MISSING_CASE(child->hdmi_max_data_rate); - /* fall through */ - case HDMI_MAX_DATA_RATE_PLATFORM: - max_tmds_clock = 0; - break; - case HDMI_MAX_DATA_RATE_297: - max_tmds_clock = 297000; - break; - case HDMI_MAX_DATA_RATE_165: - max_tmds_clock = 165000; - break; - } + return true; +} - if (max_tmds_clock) - drm_dbg_kms(&dev_priv->drm, - "VBT HDMI max TMDS clock for port %c: %d kHz\n", - port_name(port), max_tmds_clock); - info->max_tmds_clock = max_tmds_clock; - } +static void print_ddi_port(const struct intel_bios_encoder_data *devdata) +{ + struct intel_display *display = devdata->display; + const struct child_device_config *child = &devdata->child; + bool is_dvi, is_hdmi, is_dp, is_edp, is_dsi, is_crt, supports_typec_usb, supports_tbt; + int dp_boost_level, dp_max_link_rate, hdmi_boost_level, hdmi_level_shift, max_tmds_clock; + enum port port; - /* Parse the I_boost config for SKL and above */ - if (bdb_version >= 196 && child->iboost) { - info->dp_boost_level = translate_iboost(child->dp_iboost_level); - drm_dbg_kms(&dev_priv->drm, - "VBT (e)DP boost level for port %c: %d\n", - port_name(port), info->dp_boost_level); - info->hdmi_boost_level = translate_iboost(child->hdmi_iboost_level); - drm_dbg_kms(&dev_priv->drm, - "VBT HDMI boost level for port %c: %d\n", - port_name(port), info->hdmi_boost_level); - } + port = intel_bios_encoder_port(devdata); + if (port == PORT_NONE) + return; - /* DP max link rate for CNL+ */ - if (bdb_version >= 216) { - switch (child->dp_max_link_rate) { - default: - case VBT_DP_MAX_LINK_RATE_HBR3: - info->dp_max_link_rate = 810000; - break; - case VBT_DP_MAX_LINK_RATE_HBR2: - info->dp_max_link_rate = 540000; - break; - case VBT_DP_MAX_LINK_RATE_HBR: - info->dp_max_link_rate = 270000; - break; - case VBT_DP_MAX_LINK_RATE_LBR: - info->dp_max_link_rate = 162000; - break; - } - drm_dbg_kms(&dev_priv->drm, - "VBT DP max link rate for port %c: %d\n", - port_name(port), info->dp_max_link_rate); - } + is_dvi = intel_bios_encoder_supports_dvi(devdata); + is_dp = intel_bios_encoder_supports_dp(devdata); + is_crt = intel_bios_encoder_supports_crt(devdata); + is_hdmi = intel_bios_encoder_supports_hdmi(devdata); + is_edp = intel_bios_encoder_supports_edp(devdata); + is_dsi = intel_bios_encoder_supports_dsi(devdata); + + supports_typec_usb = intel_bios_encoder_supports_typec_usb(devdata); + supports_tbt = intel_bios_encoder_supports_tbt(devdata); + + drm_dbg_kms(display->drm, + "Port %c VBT info: CRT:%d DVI:%d HDMI:%d DP:%d eDP:%d DSI:%d DP++:%d LSPCON:%d USB-Type-C:%d TBT:%d DSC:%d\n", + port_name(port), is_crt, is_dvi, is_hdmi, is_dp, is_edp, is_dsi, + intel_bios_encoder_supports_dp_dual_mode(devdata), + intel_bios_encoder_is_lspcon(devdata), + supports_typec_usb, supports_tbt, + devdata->dsc != NULL); + + hdmi_level_shift = intel_bios_hdmi_level_shift(devdata); + if (hdmi_level_shift >= 0) { + drm_dbg_kms(display->drm, + "Port %c VBT HDMI level shift: %d\n", + port_name(port), hdmi_level_shift); + } + + max_tmds_clock = intel_bios_hdmi_max_tmds_clock(devdata); + if (max_tmds_clock) + drm_dbg_kms(display->drm, + "Port %c VBT HDMI max TMDS clock: %d kHz\n", + port_name(port), max_tmds_clock); + + /* I_boost config for SKL and above */ + dp_boost_level = intel_bios_dp_boost_level(devdata); + if (dp_boost_level) + drm_dbg_kms(display->drm, + "Port %c VBT (e)DP boost level: %d\n", + port_name(port), dp_boost_level); + + hdmi_boost_level = intel_bios_hdmi_boost_level(devdata); + if (hdmi_boost_level) + drm_dbg_kms(display->drm, + "Port %c VBT HDMI boost level: %d\n", + port_name(port), hdmi_boost_level); + + dp_max_link_rate = intel_bios_dp_max_link_rate(devdata); + if (dp_max_link_rate) + drm_dbg_kms(display->drm, + "Port %c VBT DP max link rate: %d\n", + port_name(port), dp_max_link_rate); - info->child = child; + /* + * FIXME need to implement support for VBT + * vswing/preemph tables should this ever trigger. + */ + drm_WARN(display->drm, child->use_vbt_vswing, + "Port %c asks to use VBT vswing/preemph tables\n", + port_name(port)); } -static void parse_ddi_ports(struct drm_i915_private *dev_priv, u8 bdb_version) +static void parse_ddi_port(struct intel_bios_encoder_data *devdata) { - struct display_device_data *devdata; + struct intel_display *display = devdata->display; + enum port port; + + port = intel_bios_encoder_port(devdata); + if (port == PORT_NONE) + return; - if (!HAS_DDI(dev_priv) && !IS_CHERRYVIEW(dev_priv)) + if (!is_port_valid(display, port)) { + drm_dbg_kms(display->drm, + "VBT reports port %c as supported, but that can't be true: skipping\n", + port_name(port)); return; + } + + sanitize_device_type(devdata, port); + sanitize_hdmi_level_shift(devdata, port); +} + +static bool has_ddi_port_info(struct intel_display *display) +{ + return DISPLAY_VER(display) >= 5 || display->platform.g4x; +} + +static void parse_ddi_ports(struct intel_display *display) +{ + struct intel_bios_encoder_data *devdata; - if (bdb_version < 155) + if (!has_ddi_port_info(display)) return; - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) - parse_ddi_port(dev_priv, devdata, bdb_version); + list_for_each_entry(devdata, &display->vbt.display_devices, node) + parse_ddi_port(devdata); + + list_for_each_entry(devdata, &display->vbt.display_devices, node) + print_ddi_port(devdata); +} + +static int child_device_expected_size(u16 version) +{ + BUILD_BUG_ON(sizeof(struct child_device_config) < 40); + + if (version > 263) + return -ENOENT; + else if (version >= 263) + return 44; + else if (version >= 256) + return 40; + else if (version >= 216) + return 39; + else if (version >= 196) + return 38; + else if (version >= 195) + return 37; + else if (version >= 111) + return LEGACY_CHILD_DEVICE_CONFIG_SIZE; + else if (version >= 106) + return 27; + else + return 22; +} + +static bool child_device_size_valid(struct intel_display *display, int size) +{ + int expected_size; + + expected_size = child_device_expected_size(display->vbt.version); + if (expected_size < 0) { + expected_size = sizeof(struct child_device_config); + drm_dbg_kms(display->drm, + "Expected child device config size for VBT version %u not known; assuming %d\n", + display->vbt.version, expected_size); + } + + /* Flag an error for unexpected size, but continue anyway. */ + if (size != expected_size) + drm_err(display->drm, + "Unexpected child device config size %d (expected %d for VBT version %u)\n", + size, expected_size, display->vbt.version); + + /* The legacy sized child device config is the minimum we need. */ + if (size < LEGACY_CHILD_DEVICE_CONFIG_SIZE) { + drm_dbg_kms(display->drm, + "Child device config size %d is too small.\n", + size); + return false; + } + + return true; } static void -parse_general_definitions(struct drm_i915_private *dev_priv, - const struct bdb_header *bdb) +parse_general_definitions(struct intel_display *display) { const struct bdb_general_definitions *defs; - struct display_device_data *devdata; + struct intel_bios_encoder_data *devdata; const struct child_device_config *child; int i, child_device_num; - u8 expected_size; u16 block_size; int bus_pin; - defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); + defs = bdb_find_section(display, BDB_GENERAL_DEFINITIONS); if (!defs) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "No general definition block is found, no devices defined.\n"); return; } block_size = get_blocksize(defs); if (block_size < sizeof(*defs)) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "General definitions block too small (%u)\n", block_size); return; } bus_pin = defs->crt_ddc_gmbus_pin; - drm_dbg_kms(&dev_priv->drm, "crt_ddc_bus_pin: %d\n", bus_pin); - if (intel_gmbus_is_valid_pin(dev_priv, bus_pin)) - dev_priv->vbt.crt_ddc_pin = bus_pin; - - if (bdb->version < 106) { - expected_size = 22; - } else if (bdb->version < 111) { - expected_size = 27; - } else if (bdb->version < 195) { - expected_size = LEGACY_CHILD_DEVICE_CONFIG_SIZE; - } else if (bdb->version == 195) { - expected_size = 37; - } else if (bdb->version <= 215) { - expected_size = 38; - } else if (bdb->version <= 229) { - expected_size = 39; - } else { - expected_size = sizeof(*child); - BUILD_BUG_ON(sizeof(*child) < 39); - drm_dbg(&dev_priv->drm, - "Expected child device config size for VBT version %u not known; assuming %u\n", - bdb->version, expected_size); - } + drm_dbg_kms(display->drm, "crt_ddc_bus_pin: %d\n", bus_pin); + if (intel_gmbus_is_valid_pin(display, bus_pin)) + display->vbt.crt_ddc_pin = bus_pin; - /* Flag an error for unexpected size, but continue anyway. */ - if (defs->child_dev_size != expected_size) - drm_err(&dev_priv->drm, - "Unexpected child device config size %u (expected %u for VBT version %u)\n", - defs->child_dev_size, expected_size, bdb->version); - - /* The legacy sized child device config is the minimum we need. */ - if (defs->child_dev_size < LEGACY_CHILD_DEVICE_CONFIG_SIZE) { - drm_dbg_kms(&dev_priv->drm, - "Child device config size %u is too small.\n", - defs->child_dev_size); + if (!child_device_size_valid(display, defs->child_dev_size)) return; - } /* get the number of child device */ child_device_num = (block_size - sizeof(*defs)) / defs->child_dev_size; @@ -1886,7 +2867,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv, if (!child->device_type) continue; - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "Found VBT child device with type 0x%x\n", child->device_type); @@ -1894,6 +2875,8 @@ parse_general_definitions(struct drm_i915_private *dev_priv, if (!devdata) break; + devdata->display = display; + /* * Copy as much as we know (sizeof) and is available * (child_dev_size) of the child device config. Accessing the @@ -1902,71 +2885,105 @@ parse_general_definitions(struct drm_i915_private *dev_priv, memcpy(&devdata->child, child, min_t(size_t, defs->child_dev_size, sizeof(*child))); - list_add_tail(&devdata->node, &dev_priv->vbt.display_devices); + list_add_tail(&devdata->node, &display->vbt.display_devices); } - if (list_empty(&dev_priv->vbt.display_devices)) - drm_dbg_kms(&dev_priv->drm, + if (list_empty(&display->vbt.display_devices)) + drm_dbg_kms(display->drm, "no child dev is parsed from VBT\n"); } /* Common defaults which may be overridden by VBT. */ static void -init_vbt_defaults(struct drm_i915_private *dev_priv) +init_vbt_defaults(struct intel_display *display) { - dev_priv->vbt.crt_ddc_pin = GMBUS_PIN_VGADDC; - - /* Default to having backlight */ - dev_priv->vbt.backlight.present = true; - - /* LFP panel data */ - dev_priv->vbt.lvds_dither = 1; - - /* SDVO panel data */ - dev_priv->vbt.sdvo_lvds_vbt_mode = NULL; + display->vbt.crt_ddc_pin = GMBUS_PIN_VGADDC; /* general features */ - dev_priv->vbt.int_tv_support = 1; - dev_priv->vbt.int_crt_support = 1; + display->vbt.int_tv_support = 1; + display->vbt.int_crt_support = 1; /* driver features */ - dev_priv->vbt.int_lvds_support = 1; + display->vbt.int_lvds_support = 1; /* Default to using SSC */ - dev_priv->vbt.lvds_use_ssc = 1; + display->vbt.lvds_use_ssc = 1; /* * Core/SandyBridge/IvyBridge use alternative (120MHz) reference * clock for LVDS. */ - dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev_priv, - !HAS_PCH_SPLIT(dev_priv)); - drm_dbg_kms(&dev_priv->drm, "Set default to SSC at %d kHz\n", - dev_priv->vbt.lvds_ssc_freq); + display->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(display, + !HAS_PCH_SPLIT(display)); + drm_dbg_kms(display->drm, "Set default to SSC at %d kHz\n", + display->vbt.lvds_ssc_freq); +} + +/* Common defaults which may be overridden by VBT. */ +static void +init_vbt_panel_defaults(struct intel_panel *panel) +{ + /* Default to having backlight */ + panel->vbt.backlight.present = true; + + /* LFP panel data */ + panel->vbt.lvds_dither = true; } /* Defaults to initialize only if there is no VBT. */ static void -init_vbt_missing_defaults(struct drm_i915_private *dev_priv) +init_vbt_missing_defaults(struct intel_display *display) { + unsigned int ports = DISPLAY_RUNTIME_INFO(display)->port_mask; enum port port; - for_each_port(port) { - struct ddi_vbt_port_info *info = - &dev_priv->vbt.ddi_port_info[port]; - enum phy phy = intel_port_to_phy(dev_priv, port); + if (!HAS_DDI(display) && !display->platform.cherryview) + return; + + for_each_port_masked(port, ports) { + struct intel_bios_encoder_data *devdata; + struct child_device_config *child; + enum phy phy = intel_port_to_phy(display, port); /* * VBT has the TypeC mode (native,TBT/USB) and we don't want * to detect it. */ - if (intel_phy_is_tc(dev_priv, phy)) + if (intel_phy_is_tc(display, phy)) continue; - info->supports_dvi = (port != PORT_A && port != PORT_E); - info->supports_hdmi = info->supports_dvi; - info->supports_dp = (port != PORT_E); - info->supports_edp = (port == PORT_A); + /* Create fake child device config */ + devdata = kzalloc(sizeof(*devdata), GFP_KERNEL); + if (!devdata) + break; + + devdata->display = display; + child = &devdata->child; + + if (port == PORT_F) + child->dvo_port = DVO_PORT_HDMIF; + else if (port == PORT_E) + child->dvo_port = DVO_PORT_HDMIE; + else + child->dvo_port = DVO_PORT_HDMIA + port; + + if (port != PORT_A && port != PORT_E) + child->device_type |= DEVICE_TYPE_TMDS_DVI_SIGNALING; + + if (port != PORT_E) + child->device_type |= DEVICE_TYPE_DISPLAYPORT_OUTPUT; + + if (port == PORT_A) + child->device_type |= DEVICE_TYPE_INTERNAL_CONNECTOR; + + list_add_tail(&devdata->node, &display->vbt.display_devices); + + drm_dbg_kms(display->drm, + "Generating default VBT child device with type 0x%04x on port %c\n", + child->device_type, port_name(port)); } + + /* Bypass some minimum baseline VBT version checks */ + display->vbt.version = 155; } static const struct bdb_header *get_bdb_header(const struct vbt_header *vbt) @@ -1976,14 +2993,19 @@ static const struct bdb_header *get_bdb_header(const struct vbt_header *vbt) return _vbt + vbt->bdb_offset; } +static const char vbt_signature[] = "$VBT"; +static const int vbt_signature_len = 4; + /** * intel_bios_is_valid_vbt - does the given buffer contain a valid VBT + * @display: display device * @buf: pointer to a buffer to validate * @size: size of the buffer * * Returns true on valid VBT. */ -bool intel_bios_is_valid_vbt(const void *buf, size_t size) +bool intel_bios_is_valid_vbt(struct intel_display *display, + const void *buf, size_t size) { const struct vbt_header *vbt = buf; const struct bdb_header *bdb; @@ -1992,17 +3014,18 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) return false; if (sizeof(struct vbt_header) > size) { - DRM_DEBUG_DRIVER("VBT header incomplete\n"); + drm_dbg_kms(display->drm, "VBT header incomplete\n"); return false; } - if (memcmp(vbt->signature, "$VBT", 4)) { - DRM_DEBUG_DRIVER("VBT invalid signature\n"); + if (memcmp(vbt->signature, vbt_signature, vbt_signature_len)) { + drm_dbg_kms(display->drm, "VBT invalid signature\n"); return false; } if (vbt->vbt_size > size) { - DRM_DEBUG_DRIVER("VBT incomplete (vbt_size overflows)\n"); + drm_dbg_kms(display->drm, + "VBT incomplete (vbt_size overflows)\n"); return false; } @@ -2012,198 +3035,304 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) vbt->bdb_offset, sizeof(struct bdb_header), size)) { - DRM_DEBUG_DRIVER("BDB header incomplete\n"); + drm_dbg_kms(display->drm, "BDB header incomplete\n"); return false; } bdb = get_bdb_header(vbt); if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) { - DRM_DEBUG_DRIVER("BDB incomplete\n"); + drm_dbg_kms(display->drm, "BDB incomplete\n"); return false; } return vbt; } -static struct vbt_header *oprom_get_vbt(struct drm_i915_private *dev_priv) +static struct vbt_header *firmware_get_vbt(struct intel_display *display, + size_t *size) { - struct pci_dev *pdev = dev_priv->drm.pdev; - void __iomem *p = NULL, *oprom; - struct vbt_header *vbt; - u16 vbt_size; - size_t i, size; + struct vbt_header *vbt = NULL; + const struct firmware *fw = NULL; + const char *name = display->params.vbt_firmware; + int ret; - oprom = pci_map_rom(pdev, &size); - if (!oprom) + if (!name || !*name) return NULL; - /* Scour memory looking for the VBT signature. */ - for (i = 0; i + 4 < size; i += 4) { - if (ioread32(oprom + i) != *((const u32 *)"$VBT")) - continue; + ret = request_firmware(&fw, name, display->drm->dev); + if (ret) { + drm_err(display->drm, + "Requesting VBT firmware \"%s\" failed (%d)\n", + name, ret); + return NULL; + } - p = oprom + i; - size -= i; - break; + if (intel_bios_is_valid_vbt(display, fw->data, fw->size)) { + vbt = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (vbt) { + drm_dbg_kms(display->drm, + "Found valid VBT firmware \"%s\"\n", name); + if (size) + *size = fw->size; + } + } else { + drm_dbg_kms(display->drm, "Invalid VBT firmware \"%s\"\n", + name); } - if (!p) - goto err_unmap_oprom; + release_firmware(fw); - if (sizeof(struct vbt_header) > size) { - drm_dbg(&dev_priv->drm, "VBT header incomplete\n"); - goto err_unmap_oprom; + return vbt; +} + +static struct vbt_header *oprom_get_vbt(struct intel_display *display, + struct intel_rom *rom, + size_t *size, const char *type) +{ + struct vbt_header *vbt; + size_t vbt_size; + loff_t offset; + + if (!rom) + return NULL; + + BUILD_BUG_ON(vbt_signature_len != sizeof(vbt_signature) - 1); + BUILD_BUG_ON(vbt_signature_len != sizeof(u32)); + + offset = intel_rom_find(rom, *(const u32 *)vbt_signature); + if (offset < 0) + goto err_free_rom; + + if (sizeof(struct vbt_header) > intel_rom_size(rom) - offset) { + drm_dbg_kms(display->drm, "VBT header incomplete\n"); + goto err_free_rom; } - vbt_size = ioread16(p + offsetof(struct vbt_header, vbt_size)); - if (vbt_size > size) { - drm_dbg(&dev_priv->drm, - "VBT incomplete (vbt_size overflows)\n"); - goto err_unmap_oprom; + BUILD_BUG_ON(sizeof(vbt->vbt_size) != sizeof(u16)); + + vbt_size = intel_rom_read16(rom, offset + offsetof(struct vbt_header, vbt_size)); + if (vbt_size > intel_rom_size(rom) - offset) { + drm_dbg_kms(display->drm, "VBT incomplete (vbt_size overflows)\n"); + goto err_free_rom; } - /* The rest will be validated by intel_bios_is_valid_vbt() */ - vbt = kmalloc(vbt_size, GFP_KERNEL); + vbt = kzalloc(round_up(vbt_size, 4), GFP_KERNEL); if (!vbt) - goto err_unmap_oprom; + goto err_free_rom; - memcpy_fromio(vbt, p, vbt_size); + intel_rom_read_block(rom, vbt, offset, vbt_size); - if (!intel_bios_is_valid_vbt(vbt, vbt_size)) + if (!intel_bios_is_valid_vbt(display, vbt, vbt_size)) goto err_free_vbt; - pci_unmap_rom(pdev, oprom); + drm_dbg_kms(display->drm, "Found valid VBT in %s\n", type); + + if (size) + *size = vbt_size; + + intel_rom_free(rom); return vbt; err_free_vbt: kfree(vbt); -err_unmap_oprom: - pci_unmap_rom(pdev, oprom); - +err_free_rom: + intel_rom_free(rom); return NULL; } +static const struct vbt_header *intel_bios_get_vbt(struct intel_display *display, + size_t *sizep) +{ + const struct vbt_header *vbt = NULL; + + vbt = firmware_get_vbt(display, sizep); + + if (!vbt) + vbt = intel_opregion_get_vbt(display, sizep); + + /* + * If the OpRegion does not have VBT, look in SPI flash + * through MMIO or PCI mapping + */ + if (!vbt && display->platform.dgfx) + with_intel_display_rpm(display) + vbt = oprom_get_vbt(display, intel_rom_spi(display->drm), sizep, "SPI flash"); + + if (!vbt) + with_intel_display_rpm(display) + vbt = oprom_get_vbt(display, intel_rom_pci(display->drm), sizep, "PCI ROM"); + + return vbt; +} + /** * intel_bios_init - find VBT and initialize settings from the BIOS - * @dev_priv: i915 device instance + * @display: display device instance * * Parse and initialize settings from the Video BIOS Tables (VBT). If the VBT * was not found in ACPI OpRegion, try to find it in PCI ROM first. Also * initialize some defaults if the VBT is not present at all. */ -void intel_bios_init(struct drm_i915_private *dev_priv) +void intel_bios_init(struct intel_display *display) { - const struct vbt_header *vbt = dev_priv->opregion.vbt; - struct vbt_header *oprom_vbt = NULL; + const struct vbt_header *vbt; const struct bdb_header *bdb; - INIT_LIST_HEAD(&dev_priv->vbt.display_devices); + INIT_LIST_HEAD(&display->vbt.display_devices); + INIT_LIST_HEAD(&display->vbt.bdb_blocks); - if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv)) { - drm_dbg_kms(&dev_priv->drm, + if (!HAS_DISPLAY(display)) { + drm_dbg_kms(display->drm, "Skipping VBT init due to disabled display.\n"); return; } - init_vbt_defaults(dev_priv); + init_vbt_defaults(display); - /* If the OpRegion does not have VBT, look in PCI ROM. */ - if (!vbt) { - oprom_vbt = oprom_get_vbt(dev_priv); - if (!oprom_vbt) - goto out; - - vbt = oprom_vbt; + vbt = intel_bios_get_vbt(display, NULL); - drm_dbg_kms(&dev_priv->drm, "Found valid VBT in PCI ROM\n"); - } + if (!vbt) + goto out; bdb = get_bdb_header(vbt); + display->vbt.version = bdb->version; - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(display->drm, "VBT signature \"%.*s\", BDB version %d\n", - (int)sizeof(vbt->signature), vbt->signature, bdb->version); + (int)sizeof(vbt->signature), vbt->signature, + display->vbt.version); + + init_bdb_blocks(display, bdb); /* Grab useful general definitions */ - parse_general_features(dev_priv, bdb); - parse_general_definitions(dev_priv, bdb); - parse_panel_options(dev_priv, bdb); - parse_panel_dtd(dev_priv, bdb); - parse_lfp_backlight(dev_priv, bdb); - parse_sdvo_panel_data(dev_priv, bdb); - parse_driver_features(dev_priv, bdb); - parse_power_conservation_features(dev_priv, bdb); - parse_edp(dev_priv, bdb); - parse_psr(dev_priv, bdb); - parse_mipi_config(dev_priv, bdb); - parse_mipi_sequence(dev_priv, bdb); + parse_general_features(display); + parse_general_definitions(display); + parse_driver_features(display); /* Depends on child device list */ - parse_compression_parameters(dev_priv, bdb); - - /* Further processing on pre-parsed data */ - parse_sdvo_device_mapping(dev_priv, bdb->version); - parse_ddi_ports(dev_priv, bdb->version); + parse_compression_parameters(display); out: if (!vbt) { - drm_info(&dev_priv->drm, + drm_info(display->drm, "Failed to find VBIOS tables (VBT)\n"); - init_vbt_missing_defaults(dev_priv); + init_vbt_missing_defaults(display); + } + + /* Further processing on pre-parsed or generated child device data */ + parse_sdvo_device_mapping(display); + parse_ddi_ports(display); + + kfree(vbt); +} + +static void intel_bios_init_panel(struct intel_display *display, + struct intel_panel *panel, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid, + bool use_fallback) +{ + /* already have it? */ + if (panel->vbt.panel_type >= 0) { + drm_WARN_ON(display->drm, !use_fallback); + return; + } + + panel->vbt.panel_type = get_panel_type(display, devdata, + drm_edid, use_fallback); + if (panel->vbt.panel_type < 0) { + drm_WARN_ON(display->drm, use_fallback); + return; } - kfree(oprom_vbt); + init_vbt_panel_defaults(panel); + + parse_panel_options(display, panel); + parse_generic_dtd(display, panel); + parse_lfp_data(display, panel); + parse_lfp_backlight(display, panel); + parse_sdvo_lvds_data(display, panel); + parse_panel_driver_features(display, panel); + parse_power_conservation_features(display, panel); + parse_edp(display, panel); + parse_psr(display, panel); + parse_mipi_config(display, panel); + parse_mipi_sequence(display, panel); +} + +void intel_bios_init_panel_early(struct intel_display *display, + struct intel_panel *panel, + const struct intel_bios_encoder_data *devdata) +{ + intel_bios_init_panel(display, panel, devdata, NULL, false); +} + +void intel_bios_init_panel_late(struct intel_display *display, + struct intel_panel *panel, + const struct intel_bios_encoder_data *devdata, + const struct drm_edid *drm_edid) +{ + intel_bios_init_panel(display, panel, devdata, drm_edid, true); } /** * intel_bios_driver_remove - Free any resources allocated by intel_bios_init() - * @dev_priv: i915 device instance + * @display: display device instance */ -void intel_bios_driver_remove(struct drm_i915_private *dev_priv) +void intel_bios_driver_remove(struct intel_display *display) { - struct display_device_data *devdata, *n; + struct intel_bios_encoder_data *devdata, *nd; + struct bdb_block_entry *entry, *ne; - list_for_each_entry_safe(devdata, n, &dev_priv->vbt.display_devices, node) { + list_for_each_entry_safe(devdata, nd, &display->vbt.display_devices, + node) { list_del(&devdata->node); kfree(devdata->dsc); kfree(devdata); } - kfree(dev_priv->vbt.sdvo_lvds_vbt_mode); - dev_priv->vbt.sdvo_lvds_vbt_mode = NULL; - kfree(dev_priv->vbt.lfp_lvds_vbt_mode); - dev_priv->vbt.lfp_lvds_vbt_mode = NULL; - kfree(dev_priv->vbt.dsi.data); - dev_priv->vbt.dsi.data = NULL; - kfree(dev_priv->vbt.dsi.pps); - dev_priv->vbt.dsi.pps = NULL; - kfree(dev_priv->vbt.dsi.config); - dev_priv->vbt.dsi.config = NULL; - kfree(dev_priv->vbt.dsi.deassert_seq); - dev_priv->vbt.dsi.deassert_seq = NULL; + list_for_each_entry_safe(entry, ne, &display->vbt.bdb_blocks, node) { + list_del(&entry->node); + kfree(entry); + } +} + +void intel_bios_fini_panel(struct intel_panel *panel) +{ + kfree(panel->vbt.sdvo_lvds_vbt_mode); + panel->vbt.sdvo_lvds_vbt_mode = NULL; + kfree(panel->vbt.lfp_vbt_mode); + panel->vbt.lfp_vbt_mode = NULL; + kfree(panel->vbt.dsi.data); + panel->vbt.dsi.data = NULL; + kfree(panel->vbt.dsi.pps); + panel->vbt.dsi.pps = NULL; + kfree(panel->vbt.dsi.config); + panel->vbt.dsi.config = NULL; + kfree(panel->vbt.dsi.deassert_seq); + panel->vbt.dsi.deassert_seq = NULL; } /** * intel_bios_is_tv_present - is integrated TV present in VBT - * @dev_priv: i915 device instance + * @display: display device instance * * Return true if TV is present. If no child devices were parsed from VBT, * assume TV is present. */ -bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv) +bool intel_bios_is_tv_present(struct intel_display *display) { - const struct display_device_data *devdata; - const struct child_device_config *child; + const struct intel_bios_encoder_data *devdata; - if (!dev_priv->vbt.int_tv_support) + if (!display->vbt.int_tv_support) return false; - if (list_empty(&dev_priv->vbt.display_devices)) + if (list_empty(&display->vbt.display_devices)) return true; - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; /* * If the device type is not TV, continue. @@ -2228,22 +3357,21 @@ bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv) /** * intel_bios_is_lvds_present - is LVDS present in VBT - * @dev_priv: i915 device instance + * @display: display device instance * @i2c_pin: i2c pin for LVDS if present * * Return true if LVDS is present. If no child devices were parsed from VBT, * assume LVDS is present. */ -bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin) +bool intel_bios_is_lvds_present(struct intel_display *display, u8 *i2c_pin) { - const struct display_device_data *devdata; - const struct child_device_config *child; + const struct intel_bios_encoder_data *devdata; - if (list_empty(&dev_priv->vbt.display_devices)) + if (list_empty(&display->vbt.display_devices)) return true; - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; /* If the device type is not LFP, continue. * We have to check both the new identifiers as well as the @@ -2253,7 +3381,7 @@ bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin) child->device_type != DEVICE_TYPE_LFP) continue; - if (intel_gmbus_is_valid_pin(dev_priv, child->i2c_pin)) + if (intel_gmbus_is_valid_pin(display, child->i2c_pin)) *i2c_pin = child->i2c_pin; /* However, we cannot trust the BIOS writers to populate @@ -2269,8 +3397,7 @@ bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin) * additional data. Trust that if the VBT was written into * the OpRegion then they have validated the LVDS's existence. */ - if (dev_priv->opregion.vbt) - return true; + return intel_opregion_vbt_present(display); } return false; @@ -2278,167 +3405,82 @@ bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin) /** * intel_bios_is_port_present - is the specified digital port present - * @dev_priv: i915 device instance + * @display: display device instance * @port: port to check * * Return true if the device in %port is present. */ -bool intel_bios_is_port_present(struct drm_i915_private *dev_priv, enum port port) +bool intel_bios_is_port_present(struct intel_display *display, enum port port) { - const struct display_device_data *devdata; - const struct child_device_config *child; - static const struct { - u16 dp, hdmi; - } port_mapping[] = { - [PORT_B] = { DVO_PORT_DPB, DVO_PORT_HDMIB, }, - [PORT_C] = { DVO_PORT_DPC, DVO_PORT_HDMIC, }, - [PORT_D] = { DVO_PORT_DPD, DVO_PORT_HDMID, }, - [PORT_E] = { DVO_PORT_DPE, DVO_PORT_HDMIE, }, - [PORT_F] = { DVO_PORT_DPF, DVO_PORT_HDMIF, }, - }; - - if (HAS_DDI(dev_priv)) { - const struct ddi_vbt_port_info *port_info = - &dev_priv->vbt.ddi_port_info[port]; + const struct intel_bios_encoder_data *devdata; - return port_info->child; - } + if (WARN_ON(!has_ddi_port_info(display))) + return true; - /* FIXME maybe deal with port A as well? */ - if (drm_WARN_ON(&dev_priv->drm, - port == PORT_A) || port >= ARRAY_SIZE(port_mapping)) + if (!is_port_valid(display, port)) return false; - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; - if ((child->dvo_port == port_mapping[port].dp || - child->dvo_port == port_mapping[port].hdmi) && - (child->device_type & (DEVICE_TYPE_TMDS_DVI_SIGNALING | - DEVICE_TYPE_DISPLAYPORT_OUTPUT))) + if (dvo_port_to_port(display, child->dvo_port) == port) return true; } return false; } -/** - * intel_bios_is_port_edp - is the device in given port eDP - * @dev_priv: i915 device instance - * @port: port to check - * - * Return true if the device in %port is eDP. - */ -bool intel_bios_is_port_edp(struct drm_i915_private *dev_priv, enum port port) -{ - const struct display_device_data *devdata; - const struct child_device_config *child; - static const short port_mapping[] = { - [PORT_B] = DVO_PORT_DPB, - [PORT_C] = DVO_PORT_DPC, - [PORT_D] = DVO_PORT_DPD, - [PORT_E] = DVO_PORT_DPE, - [PORT_F] = DVO_PORT_DPF, - }; - - if (HAS_DDI(dev_priv)) - return dev_priv->vbt.ddi_port_info[port].supports_edp; - - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; - - if (child->dvo_port == port_mapping[port] && - (child->device_type & DEVICE_TYPE_eDP_BITS) == - (DEVICE_TYPE_eDP & DEVICE_TYPE_eDP_BITS)) - return true; - } - - return false; -} - -static bool child_dev_is_dp_dual_mode(const struct child_device_config *child, - enum port port) +bool intel_bios_encoder_supports_dp_dual_mode(const struct intel_bios_encoder_data *devdata) { - static const struct { - u16 dp, hdmi; - } port_mapping[] = { - /* - * Buggy VBTs may declare DP ports as having - * HDMI type dvo_port :( So let's check both. - */ - [PORT_B] = { DVO_PORT_DPB, DVO_PORT_HDMIB, }, - [PORT_C] = { DVO_PORT_DPC, DVO_PORT_HDMIC, }, - [PORT_D] = { DVO_PORT_DPD, DVO_PORT_HDMID, }, - [PORT_E] = { DVO_PORT_DPE, DVO_PORT_HDMIE, }, - [PORT_F] = { DVO_PORT_DPF, DVO_PORT_HDMIF, }, - }; + const struct child_device_config *child = &devdata->child; - if (port == PORT_A || port >= ARRAY_SIZE(port_mapping)) + if (!devdata) return false; - if ((child->device_type & DEVICE_TYPE_DP_DUAL_MODE_BITS) != - (DEVICE_TYPE_DP_DUAL_MODE & DEVICE_TYPE_DP_DUAL_MODE_BITS)) + if (!intel_bios_encoder_supports_dp(devdata) || + !intel_bios_encoder_supports_hdmi(devdata)) return false; - if (child->dvo_port == port_mapping[port].dp) + if (dvo_port_type(child->dvo_port) == DVO_PORT_DPA) return true; /* Only accept a HDMI dvo_port as DP++ if it has an AUX channel */ - if (child->dvo_port == port_mapping[port].hdmi && + if (dvo_port_type(child->dvo_port) == DVO_PORT_HDMIA && child->aux_channel != 0) return true; return false; } -bool intel_bios_is_port_dp_dual_mode(struct drm_i915_private *dev_priv, - enum port port) -{ - const struct display_device_data *devdata; - - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - if (child_dev_is_dp_dual_mode(&devdata->child, port)) - return true; - } - - return false; -} - /** * intel_bios_is_dsi_present - is DSI present in VBT - * @dev_priv: i915 device instance + * @display: display device instance * @port: port for DSI if present * * Return true if DSI is present, and return the port in %port. */ -bool intel_bios_is_dsi_present(struct drm_i915_private *dev_priv, +bool intel_bios_is_dsi_present(struct intel_display *display, enum port *port) { - const struct display_device_data *devdata; - const struct child_device_config *child; - u8 dvo_port; + const struct intel_bios_encoder_data *devdata; - list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; + u8 dvo_port = child->dvo_port; if (!(child->device_type & DEVICE_TYPE_MIPI_OUTPUT)) continue; - dvo_port = child->dvo_port; - - if (dvo_port == DVO_PORT_MIPIA || - (dvo_port == DVO_PORT_MIPIB && INTEL_GEN(dev_priv) >= 11) || - (dvo_port == DVO_PORT_MIPIC && INTEL_GEN(dev_priv) < 11)) { - if (port) - *port = dvo_port - DVO_PORT_MIPIA; - return true; - } else if (dvo_port == DVO_PORT_MIPIB || - dvo_port == DVO_PORT_MIPIC || - dvo_port == DVO_PORT_MIPID) { - drm_dbg_kms(&dev_priv->drm, + if (dsi_dvo_port_to_port(display, dvo_port) == PORT_NONE) { + drm_dbg_kms(display->drm, "VBT has unsupported DSI port %c\n", port_name(dvo_port - DVO_PORT_MIPIA)); + continue; } + + if (port) + *port = dsi_dvo_port_to_port(display, dvo_port); + return true; } return false; @@ -2448,6 +3490,7 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, struct dsc_compression_parameters_entry *dsc, int dsc_max_bpc) { + struct intel_display *display = to_intel_display(crtc_state); struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; int bpc = 8; @@ -2461,13 +3504,13 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, else if (dsc->support_8bpc && dsc_max_bpc >= 8) bpc = 8; else - DRM_DEBUG_KMS("VBT: Unsupported BPC %d for DCS\n", - dsc_max_bpc); + drm_dbg_kms(display->drm, "VBT: Unsupported BPC %d for DCS\n", + dsc_max_bpc); crtc_state->pipe_bpp = bpc * 3; - crtc_state->dsc.compressed_bpp = min(crtc_state->pipe_bpp, - VBT_DSC_MAX_BPP(dsc->max_bpp)); + crtc_state->dsc.compressed_bpp_x16 = fxp_q4_from_int(min(crtc_state->pipe_bpp, + VBT_DSC_MAX_BPP(dsc->max_bpp))); /* * FIXME: This is ugly, and slice count should take DSC engine @@ -2482,28 +3525,25 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, } else { /* FIXME */ if (!(dsc->slices_per_line & BIT(0))) - DRM_DEBUG_KMS("VBT: Unsupported DSC slice count for DSI\n"); + drm_dbg_kms(display->drm, + "VBT: Unsupported DSC slice count for DSI\n"); crtc_state->dsc.slice_count = 1; } if (crtc_state->hw.adjusted_mode.crtc_hdisplay % crtc_state->dsc.slice_count != 0) - DRM_DEBUG_KMS("VBT: DSC hdisplay %d not divisible by slice count %d\n", - crtc_state->hw.adjusted_mode.crtc_hdisplay, - crtc_state->dsc.slice_count); + drm_dbg_kms(display->drm, + "VBT: DSC hdisplay %d not divisible by slice count %d\n", + crtc_state->hw.adjusted_mode.crtc_hdisplay, + crtc_state->dsc.slice_count); /* - * FIXME: Use VBT rc_buffer_block_size and rc_buffer_size for the - * implementation specific physical rate buffer size. Currently we use - * the required rate buffer model size calculated in - * drm_dsc_compute_rc_parameters() according to VESA DSC Annex E. - * * The VBT rc_buffer_block_size and rc_buffer_size definitions - * correspond to DP 1.4 DPCD offsets 0x62 and 0x63. The DP DSC - * implementation should also use the DPCD (or perhaps VBT for eDP) - * provided value for the buffer size. + * correspond to DP 1.4 DPCD offsets 0x62 and 0x63. */ + vdsc_cfg->rc_model_size = drm_dsc_dp_rc_buffer_size(dsc->rc_buffer_block_size, + dsc->rc_buffer_size); /* FIXME: DSI spec says bpc + 1 for this one */ vdsc_cfg->line_buf_depth = VBT_DSC_LINE_BUFFER_DEPTH(dsc->line_buffer_depth); @@ -2518,22 +3558,20 @@ bool intel_bios_get_dsc_params(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, int dsc_max_bpc) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - const struct display_device_data *devdata; - const struct child_device_config *child; + struct intel_display *display = to_intel_display(encoder); + const struct intel_bios_encoder_data *devdata; - list_for_each_entry(devdata, &i915->vbt.display_devices, node) { - child = &devdata->child; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + const struct child_device_config *child = &devdata->child; if (!(child->device_type & DEVICE_TYPE_MIPI_OUTPUT)) continue; - if (child->dvo_port - DVO_PORT_MIPIA == encoder->port) { + if (dsi_dvo_port_to_port(display, child->dvo_port) == encoder->port) { if (!devdata->dsc) return false; - if (crtc_state) - fill_dsc(crtc_state, devdata->dsc, dsc_max_bpc); + fill_dsc(crtc_state, devdata->dsc, dsc_max_bpc); return true; } @@ -2542,159 +3580,200 @@ bool intel_bios_get_dsc_params(struct intel_encoder *encoder, return false; } -/** - * intel_bios_is_port_hpd_inverted - is HPD inverted for %port - * @i915: i915 device instance - * @port: port to check - * - * Return true if HPD should be inverted for %port. +static const u8 adlp_aux_ch_map[] = { + [AUX_CH_A] = DP_AUX_A, + [AUX_CH_B] = DP_AUX_B, + [AUX_CH_C] = DP_AUX_C, + [AUX_CH_D_XELPD] = DP_AUX_D, + [AUX_CH_E_XELPD] = DP_AUX_E, + [AUX_CH_USBC1] = DP_AUX_F, + [AUX_CH_USBC2] = DP_AUX_G, + [AUX_CH_USBC3] = DP_AUX_H, + [AUX_CH_USBC4] = DP_AUX_I, +}; + +/* + * ADL-S VBT uses PHY based mapping. Combo PHYs A,B,C,D,E + * map to DDI A,TC1,TC2,TC3,TC4 respectively. */ -bool -intel_bios_is_port_hpd_inverted(const struct drm_i915_private *i915, - enum port port) +static const u8 adls_aux_ch_map[] = { + [AUX_CH_A] = DP_AUX_A, + [AUX_CH_USBC1] = DP_AUX_B, + [AUX_CH_USBC2] = DP_AUX_C, + [AUX_CH_USBC3] = DP_AUX_D, + [AUX_CH_USBC4] = DP_AUX_E, +}; + +/* + * RKL/DG1 VBT uses PHY based mapping. Combo PHYs A,B,C,D + * map to DDI A,B,TC1,TC2 respectively. + */ +static const u8 rkl_aux_ch_map[] = { + [AUX_CH_A] = DP_AUX_A, + [AUX_CH_B] = DP_AUX_B, + [AUX_CH_USBC1] = DP_AUX_C, + [AUX_CH_USBC2] = DP_AUX_D, +}; + +static const u8 direct_aux_ch_map[] = { + [AUX_CH_A] = DP_AUX_A, + [AUX_CH_B] = DP_AUX_B, + [AUX_CH_C] = DP_AUX_C, + [AUX_CH_D] = DP_AUX_D, /* aka AUX_CH_USBC1 */ + [AUX_CH_E] = DP_AUX_E, /* aka AUX_CH_USBC2 */ + [AUX_CH_F] = DP_AUX_F, /* aka AUX_CH_USBC3 */ + [AUX_CH_G] = DP_AUX_G, /* aka AUX_CH_USBC4 */ + [AUX_CH_H] = DP_AUX_H, /* aka AUX_CH_USBC5 */ + [AUX_CH_I] = DP_AUX_I, /* aka AUX_CH_USBC6 */ +}; + +static enum aux_ch map_aux_ch(struct intel_display *display, u8 aux_channel) { - const struct child_device_config *child = - i915->vbt.ddi_port_info[port].child; + const u8 *aux_ch_map; + int i, n_entries; - if (drm_WARN_ON_ONCE(&i915->drm, !IS_GEN9_LP(i915))) - return false; + if (DISPLAY_VER(display) >= 13) { + aux_ch_map = adlp_aux_ch_map; + n_entries = ARRAY_SIZE(adlp_aux_ch_map); + } else if (display->platform.alderlake_s) { + aux_ch_map = adls_aux_ch_map; + n_entries = ARRAY_SIZE(adls_aux_ch_map); + } else if (display->platform.dg1 || display->platform.rocketlake) { + aux_ch_map = rkl_aux_ch_map; + n_entries = ARRAY_SIZE(rkl_aux_ch_map); + } else { + aux_ch_map = direct_aux_ch_map; + n_entries = ARRAY_SIZE(direct_aux_ch_map); + } + + for (i = 0; i < n_entries; i++) { + if (aux_ch_map[i] == aux_channel) + return i; + } - return child && child->hpd_invert; + drm_dbg_kms(display->drm, + "Ignoring alternate AUX CH: VBT claims AUX 0x%x, which is not valid for this platform\n", + aux_channel); + + return AUX_CH_NONE; } -/** - * intel_bios_is_lspcon_present - if LSPCON is attached on %port - * @i915: i915 device instance - * @port: port to check - * - * Return true if LSPCON is present on this port - */ -bool -intel_bios_is_lspcon_present(const struct drm_i915_private *i915, - enum port port) +enum aux_ch intel_bios_dp_aux_ch(const struct intel_bios_encoder_data *devdata) { - const struct child_device_config *child = - i915->vbt.ddi_port_info[port].child; + if (!devdata || !devdata->child.aux_channel) + return AUX_CH_NONE; - return HAS_LSPCON(i915) && child && child->lspcon; + return map_aux_ch(devdata->display, devdata->child.aux_channel); } -enum aux_ch intel_bios_port_aux_ch(struct drm_i915_private *dev_priv, - enum port port) +bool intel_bios_dp_has_shared_aux_ch(const struct intel_bios_encoder_data *devdata) { - const struct ddi_vbt_port_info *info = - &dev_priv->vbt.ddi_port_info[port]; - enum aux_ch aux_ch; + struct intel_display *display; + u8 aux_channel; + int count = 0; - if (!info->alternate_aux_channel) { - aux_ch = (enum aux_ch)port; + if (!devdata || !devdata->child.aux_channel) + return false; - drm_dbg_kms(&dev_priv->drm, - "using AUX %c for port %c (platform default)\n", - aux_ch_name(aux_ch), port_name(port)); - return aux_ch; - } + display = devdata->display; + aux_channel = devdata->child.aux_channel; - switch (info->alternate_aux_channel) { - case DP_AUX_A: - aux_ch = AUX_CH_A; - break; - case DP_AUX_B: - aux_ch = AUX_CH_B; - break; - case DP_AUX_C: - aux_ch = AUX_CH_C; - break; - case DP_AUX_D: - aux_ch = AUX_CH_D; - break; - case DP_AUX_E: - aux_ch = AUX_CH_E; - break; - case DP_AUX_F: - aux_ch = AUX_CH_F; - break; - case DP_AUX_G: - aux_ch = AUX_CH_G; - break; - default: - MISSING_CASE(info->alternate_aux_channel); - aux_ch = AUX_CH_A; - break; + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + if (intel_bios_encoder_supports_dp(devdata) && + aux_channel == devdata->child.aux_channel) + count++; } - drm_dbg_kms(&dev_priv->drm, "using AUX %c for port %c (VBT)\n", - aux_ch_name(aux_ch), port_name(port)); - - return aux_ch; + return count > 1; } -int intel_bios_max_tmds_clock(struct intel_encoder *encoder) +int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); + if (!devdata || devdata->display->vbt.version < 196 || !devdata->child.iboost) + return 0; - return i915->vbt.ddi_port_info[encoder->port].max_tmds_clock; + return translate_iboost(devdata->display, devdata->child.dp_iboost_level); } -int intel_bios_hdmi_level_shift(struct intel_encoder *encoder) +int intel_bios_hdmi_boost_level(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - const struct ddi_vbt_port_info *info = - &i915->vbt.ddi_port_info[encoder->port]; + if (!devdata || devdata->display->vbt.version < 196 || !devdata->child.iboost) + return 0; - return info->hdmi_level_shift_set ? info->hdmi_level_shift : -1; + return translate_iboost(devdata->display, devdata->child.hdmi_iboost_level); } -int intel_bios_dp_boost_level(struct intel_encoder *encoder) +int intel_bios_hdmi_ddc_pin(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); + if (!devdata || !devdata->child.ddc_pin) + return 0; - return i915->vbt.ddi_port_info[encoder->port].dp_boost_level; + return map_ddc_pin(devdata->display, devdata->child.ddc_pin); } -int intel_bios_hdmi_boost_level(struct intel_encoder *encoder) +bool intel_bios_encoder_supports_typec_usb(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - - return i915->vbt.ddi_port_info[encoder->port].hdmi_boost_level; + return devdata->display->vbt.version >= 195 && devdata->child.dp_usb_type_c; } -int intel_bios_dp_max_link_rate(struct intel_encoder *encoder) +bool intel_bios_encoder_supports_tbt(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - - return i915->vbt.ddi_port_info[encoder->port].dp_max_link_rate; + return devdata->display->vbt.version >= 209 && devdata->child.tbt; } -int intel_bios_alternate_ddc_pin(struct intel_encoder *encoder) +bool intel_bios_encoder_lane_reversal(const struct intel_bios_encoder_data *devdata) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - - return i915->vbt.ddi_port_info[encoder->port].alternate_ddc_pin; + return devdata && devdata->child.lane_reversal; } -bool intel_bios_port_supports_dvi(struct drm_i915_private *i915, enum port port) +bool intel_bios_encoder_hpd_invert(const struct intel_bios_encoder_data *devdata) { - return i915->vbt.ddi_port_info[port].supports_dvi; + return devdata && devdata->child.hpd_invert; } -bool intel_bios_port_supports_hdmi(struct drm_i915_private *i915, enum port port) +const struct intel_bios_encoder_data * +intel_bios_encoder_data_lookup(struct intel_display *display, enum port port) { - return i915->vbt.ddi_port_info[port].supports_hdmi; + struct intel_bios_encoder_data *devdata; + + list_for_each_entry(devdata, &display->vbt.display_devices, node) { + if (intel_bios_encoder_port(devdata) == port) + return devdata; + } + + return NULL; } -bool intel_bios_port_supports_dp(struct drm_i915_private *i915, enum port port) +void intel_bios_for_each_encoder(struct intel_display *display, + void (*func)(struct intel_display *display, + const struct intel_bios_encoder_data *devdata)) { - return i915->vbt.ddi_port_info[port].supports_dp; + struct intel_bios_encoder_data *devdata; + + list_for_each_entry(devdata, &display->vbt.display_devices, node) + func(display, devdata); } -bool intel_bios_port_supports_typec_usb(struct drm_i915_private *i915, - enum port port) +static int intel_bios_vbt_show(struct seq_file *m, void *unused) { - return i915->vbt.ddi_port_info[port].supports_typec_usb; + struct intel_display *display = m->private; + const void *vbt; + size_t vbt_size; + + vbt = intel_bios_get_vbt(display, &vbt_size); + + if (vbt) { + seq_write(m, vbt, vbt_size); + kfree(vbt); + } + + return 0; } -bool intel_bios_port_supports_tbt(struct drm_i915_private *i915, enum port port) +DEFINE_SHOW_ATTRIBUTE(intel_bios_vbt); + +void intel_bios_debugfs_register(struct intel_display *display) { - return i915->vbt.ddi_port_info[port].supports_tbt; + debugfs_create_file("i915_vbt", 0444, display->drm->debugfs_root, + display, &intel_bios_vbt_fops); } |
