31,11 → 31,49 |
#include "i915_drv.h" |
#include "intel_bios.h" |
|
/** |
* DOC: Video BIOS Table (VBT) |
* |
* The Video BIOS Table, or VBT, provides platform and board specific |
* configuration information to the driver that is not discoverable or available |
* through other means. The configuration is mostly related to display |
* hardware. The VBT is available via the ACPI OpRegion or, on older systems, in |
* the PCI ROM. |
* |
* The VBT consists of a VBT Header (defined as &struct vbt_header), a BDB |
* Header (&struct bdb_header), and a number of BIOS Data Blocks (BDB) that |
* contain the actual configuration information. The VBT Header, and thus the |
* VBT, begins with "$VBT" signature. The VBT Header contains the offset of the |
* BDB Header. The data blocks are concatenated after the BDB Header. The data |
* blocks have a 1-byte Block ID, 2-byte Block Size, and Block Size bytes of |
* data. (Block 53, the MIPI Sequence Block is an exception.) |
* |
* The driver parses the VBT during load. The relevant information is stored in |
* driver private data for ease of use, and the actual VBT is not read after |
* that. |
*/ |
|
#define SLAVE_ADDR1 0x70 |
#define SLAVE_ADDR2 0x72 |
|
static int panel_type; |
|
/* Get BDB block size given a pointer to Block ID. */ |
static u32 _get_blocksize(const u8 *block_base) |
{ |
/* The MIPI Sequence Block v3+ has a separate size field. */ |
if (*block_base == BDB_MIPI_SEQUENCE && *(block_base + 3) >= 3) |
return *((const u32 *)(block_base + 4)); |
else |
return *((const u16 *)(block_base + 1)); |
} |
|
/* Get BDB block size give a pointer to data after Block ID and Block Size. */ |
static u32 get_blocksize(const void *block_data) |
{ |
return _get_blocksize(block_data - 3); |
} |
|
static const void * |
find_section(const void *_bdb, int section_id) |
{ |
52,15 → 90,9 |
/* walk the sections looking for section_id */ |
while (index + 3 < total) { |
current_id = *(base + index); |
index++; |
current_size = _get_blocksize(base + index); |
index += 3; |
|
current_size = *((const u16 *)(base + index)); |
index += 2; |
|
/* The MIPI Sequence Block v3+ has a separate size field. */ |
if (current_id == BDB_MIPI_SEQUENCE && *(base + index) >= 3) |
current_size = *((const u32 *)(base + index + 1)); |
|
if (index + current_size > total) |
return NULL; |
|
73,16 → 105,6 |
return NULL; |
} |
|
static u16 |
get_blocksize(const void *p) |
{ |
u16 *block_ptr, block_size; |
|
block_ptr = (u16 *)((char *)p - 2); |
block_size = *block_ptr; |
return block_size; |
} |
|
static void |
fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, |
const struct lvds_dvo_timing *dvo_timing) |
675,84 → 697,13 |
dev_priv->vbt.psr.tp2_tp3_wakeup_time = psr_table->tp2_tp3_wakeup_time; |
} |
|
static u8 *goto_next_sequence(u8 *data, int *size) |
{ |
u16 len; |
int tmp = *size; |
|
if (--tmp < 0) |
return NULL; |
|
/* goto first element */ |
data++; |
while (1) { |
switch (*data) { |
case MIPI_SEQ_ELEM_SEND_PKT: |
/* |
* skip by this element payload size |
* skip elem id, command flag and data type |
*/ |
tmp -= 5; |
if (tmp < 0) |
return NULL; |
|
data += 3; |
len = *((u16 *)data); |
|
tmp -= len; |
if (tmp < 0) |
return NULL; |
|
/* skip by len */ |
data = data + 2 + len; |
break; |
case MIPI_SEQ_ELEM_DELAY: |
/* skip by elem id, and delay is 4 bytes */ |
tmp -= 5; |
if (tmp < 0) |
return NULL; |
|
data += 5; |
break; |
case MIPI_SEQ_ELEM_GPIO: |
tmp -= 3; |
if (tmp < 0) |
return NULL; |
|
data += 3; |
break; |
default: |
DRM_ERROR("Unknown element\n"); |
return NULL; |
} |
|
/* end of sequence ? */ |
if (*data == 0) |
break; |
} |
|
/* goto next sequence or end of block byte */ |
if (--tmp < 0) |
return NULL; |
|
data++; |
|
/* update amount of data left for the sequence block to be parsed */ |
*size = tmp; |
return data; |
} |
|
static void |
parse_mipi(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) |
parse_mipi_config(struct drm_i915_private *dev_priv, |
const struct bdb_header *bdb) |
{ |
const struct bdb_mipi_config *start; |
const struct bdb_mipi_sequence *sequence; |
const struct mipi_config *config; |
const struct mipi_pps_data *pps; |
u8 *data; |
const u8 *seq_data; |
int i, panel_id, seq_size; |
u16 block_size; |
|
/* parse MIPI blocks only if LFP type is MIPI */ |
if (!dev_priv->vbt.has_mipi) |
798,104 → 749,233 |
|
/* We have mandatory mipi config blocks. Initialize as generic panel */ |
dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID; |
} |
|
/* Check if we have sequence block as well */ |
sequence = find_section(bdb, BDB_MIPI_SEQUENCE); |
if (!sequence) { |
DRM_DEBUG_KMS("No MIPI Sequence found, parsing complete\n"); |
return; |
/* Find the sequence block and size for the given panel. */ |
static const u8 * |
find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, |
u16 panel_id, u32 *seq_size) |
{ |
u32 total = get_blocksize(sequence); |
const u8 *data = &sequence->data[0]; |
u8 current_id; |
u32 current_size; |
int header_size = sequence->version >= 3 ? 5 : 3; |
int index = 0; |
int i; |
|
/* skip new block size */ |
if (sequence->version >= 3) |
data += 4; |
|
for (i = 0; i < MAX_MIPI_CONFIGURATIONS && index < total; i++) { |
if (index + header_size > total) { |
DRM_ERROR("Invalid sequence block (header)\n"); |
return NULL; |
} |
|
/* Fail gracefully for forward incompatible sequence block. */ |
if (sequence->version >= 3) { |
DRM_ERROR("Unable to parse MIPI Sequence Block v3+\n"); |
return; |
current_id = *(data + index); |
if (sequence->version >= 3) |
current_size = *((const u32 *)(data + index + 1)); |
else |
current_size = *((const u16 *)(data + index + 1)); |
|
index += header_size; |
|
if (index + current_size > total) { |
DRM_ERROR("Invalid sequence block\n"); |
return NULL; |
} |
|
DRM_DEBUG_DRIVER("Found MIPI sequence block\n"); |
if (current_id == panel_id) { |
*seq_size = current_size; |
return data + index; |
} |
|
block_size = get_blocksize(sequence); |
index += current_size; |
} |
|
DRM_ERROR("Sequence block detected but no valid configuration\n"); |
|
return NULL; |
} |
|
static int goto_next_sequence(const u8 *data, int index, int total) |
{ |
u16 len; |
|
/* Skip Sequence Byte. */ |
for (index = index + 1; index < total; index += len) { |
u8 operation_byte = *(data + index); |
index++; |
|
switch (operation_byte) { |
case MIPI_SEQ_ELEM_END: |
return index; |
case MIPI_SEQ_ELEM_SEND_PKT: |
if (index + 4 > total) |
return 0; |
|
len = *((const u16 *)(data + index + 2)) + 4; |
break; |
case MIPI_SEQ_ELEM_DELAY: |
len = 4; |
break; |
case MIPI_SEQ_ELEM_GPIO: |
len = 2; |
break; |
case MIPI_SEQ_ELEM_I2C: |
if (index + 7 > total) |
return 0; |
len = *(data + index + 6) + 7; |
break; |
default: |
DRM_ERROR("Unknown operation byte\n"); |
return 0; |
} |
} |
|
return 0; |
} |
|
static int goto_next_sequence_v3(const u8 *data, int index, int total) |
{ |
int seq_end; |
u16 len; |
u32 size_of_sequence; |
|
/* |
* parse the sequence block for individual sequences |
* Could skip sequence based on Size of Sequence alone, but also do some |
* checking on the structure. |
*/ |
dev_priv->vbt.dsi.seq_version = sequence->version; |
if (total < 5) { |
DRM_ERROR("Too small sequence size\n"); |
return 0; |
} |
|
seq_data = &sequence->data[0]; |
/* Skip Sequence Byte. */ |
index++; |
|
/* |
* sequence block is variable length and hence we need to parse and |
* get the sequence data for specific panel id |
* Size of Sequence. Excludes the Sequence Byte and the size itself, |
* includes MIPI_SEQ_ELEM_END byte, excludes the final MIPI_SEQ_END |
* byte. |
*/ |
for (i = 0; i < MAX_MIPI_CONFIGURATIONS; i++) { |
panel_id = *seq_data; |
seq_size = *((u16 *) (seq_data + 1)); |
if (panel_id == panel_type) |
break; |
size_of_sequence = *((const uint32_t *)(data + index)); |
index += 4; |
|
/* skip the sequence including seq header of 3 bytes */ |
seq_data = seq_data + 3 + seq_size; |
if ((seq_data - &sequence->data[0]) > block_size) { |
DRM_ERROR("Sequence start is beyond sequence block size, corrupted sequence block\n"); |
return; |
seq_end = index + size_of_sequence; |
if (seq_end > total) { |
DRM_ERROR("Invalid sequence size\n"); |
return 0; |
} |
|
for (; index < total; index += len) { |
u8 operation_byte = *(data + index); |
index++; |
|
if (operation_byte == MIPI_SEQ_ELEM_END) { |
if (index != seq_end) { |
DRM_ERROR("Invalid element structure\n"); |
return 0; |
} |
return index; |
} |
|
if (i == MAX_MIPI_CONFIGURATIONS) { |
DRM_ERROR("Sequence block detected but no valid configuration\n"); |
len = *(data + index); |
index++; |
|
/* |
* FIXME: Would be nice to check elements like for v1/v2 in |
* goto_next_sequence() above. |
*/ |
switch (operation_byte) { |
case MIPI_SEQ_ELEM_SEND_PKT: |
case MIPI_SEQ_ELEM_DELAY: |
case MIPI_SEQ_ELEM_GPIO: |
case MIPI_SEQ_ELEM_I2C: |
case MIPI_SEQ_ELEM_SPI: |
case MIPI_SEQ_ELEM_PMIC: |
break; |
default: |
DRM_ERROR("Unknown operation byte %u\n", |
operation_byte); |
break; |
} |
} |
|
return 0; |
} |
|
static void |
parse_mipi_sequence(struct drm_i915_private *dev_priv, |
const struct bdb_header *bdb) |
{ |
const struct bdb_mipi_sequence *sequence; |
const u8 *seq_data; |
u32 seq_size; |
u8 *data; |
int index = 0; |
|
/* Only our generic panel driver uses the sequence block. */ |
if (dev_priv->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID) |
return; |
|
sequence = find_section(bdb, BDB_MIPI_SEQUENCE); |
if (!sequence) { |
DRM_DEBUG_KMS("No MIPI Sequence found, parsing complete\n"); |
return; |
} |
|
/* check if found sequence is completely within the sequence block |
* just being paranoid */ |
if (seq_size > block_size) { |
DRM_ERROR("Corrupted sequence/size, bailing out\n"); |
/* Fail gracefully for forward incompatible sequence block. */ |
if (sequence->version >= 4) { |
DRM_ERROR("Unable to parse MIPI Sequence Block v%u\n", |
sequence->version); |
return; |
} |
|
/* skip the panel id(1 byte) and seq size(2 bytes) */ |
dev_priv->vbt.dsi.data = kmemdup(seq_data + 3, seq_size, GFP_KERNEL); |
if (!dev_priv->vbt.dsi.data) |
DRM_DEBUG_DRIVER("Found MIPI sequence block v%u\n", sequence->version); |
|
seq_data = find_panel_sequence_block(sequence, panel_type, &seq_size); |
if (!seq_data) |
return; |
|
/* |
* loop into the sequence data and split into multiple sequneces |
* There are only 5 types of sequences as of now |
*/ |
data = dev_priv->vbt.dsi.data; |
dev_priv->vbt.dsi.size = seq_size; |
data = kmemdup(seq_data, seq_size, GFP_KERNEL); |
if (!data) |
return; |
|
/* two consecutive 0x00 indicate end of all sequences */ |
while (1) { |
int seq_id = *data; |
if (MIPI_SEQ_MAX > seq_id && seq_id > MIPI_SEQ_UNDEFINED) { |
dev_priv->vbt.dsi.sequence[seq_id] = data; |
DRM_DEBUG_DRIVER("Found mipi sequence - %d\n", seq_id); |
} else { |
DRM_ERROR("undefined sequence\n"); |
/* Parse the sequences, store pointers to each sequence. */ |
for (;;) { |
u8 seq_id = *(data + index); |
if (seq_id == MIPI_SEQ_END) |
break; |
|
if (seq_id >= MIPI_SEQ_MAX) { |
DRM_ERROR("Unknown sequence %u\n", seq_id); |
goto err; |
} |
|
/* partial parsing to skip elements */ |
data = goto_next_sequence(data, &seq_size); |
dev_priv->vbt.dsi.sequence[seq_id] = data + index; |
|
if (data == NULL) { |
DRM_ERROR("Sequence elements going beyond block itself. Sequence block parsing failed\n"); |
if (sequence->version >= 3) |
index = goto_next_sequence_v3(data, index, seq_size); |
else |
index = goto_next_sequence(data, index, seq_size); |
if (!index) { |
DRM_ERROR("Invalid sequence %u\n", seq_id); |
goto err; |
} |
|
if (*data == 0) |
break; /* end of sequence reached */ |
} |
|
DRM_DEBUG_DRIVER("MIPI related vbt parsing complete\n"); |
dev_priv->vbt.dsi.data = data; |
dev_priv->vbt.dsi.size = seq_size; |
dev_priv->vbt.dsi.seq_version = sequence->version; |
|
DRM_DEBUG_DRIVER("MIPI related VBT parsing complete\n"); |
return; |
|
err: |
kfree(dev_priv->vbt.dsi.data); |
dev_priv->vbt.dsi.data = NULL; |
|
/* error during parsing so set all pointers to null |
* because of partial parsing */ |
kfree(data); |
memset(dev_priv->vbt.dsi.sequence, 0, sizeof(dev_priv->vbt.dsi.sequence)); |
} |
|
1088,7 → 1168,12 |
DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n"); |
return; |
} |
if (bdb->version < 195) { |
if (bdb->version < 106) { |
expected_size = 22; |
} else if (bdb->version < 109) { |
expected_size = 27; |
} else if (bdb->version < 195) { |
BUILD_BUG_ON(sizeof(struct old_child_dev_config) != 33); |
expected_size = sizeof(struct old_child_dev_config); |
} else if (bdb->version == 195) { |
expected_size = 37; |
1101,18 → 1186,18 |
bdb->version, expected_size); |
} |
|
/* Flag an error for unexpected size, but continue anyway. */ |
if (p_defs->child_dev_size != expected_size) |
DRM_ERROR("Unexpected child device config size %u (expected %u for VBT version %u)\n", |
p_defs->child_dev_size, expected_size, bdb->version); |
|
/* The legacy sized child device config is the minimum we need. */ |
if (p_defs->child_dev_size < sizeof(struct old_child_dev_config)) { |
DRM_ERROR("Child device config size %u is too small.\n", |
DRM_DEBUG_KMS("Child device config size %u is too small.\n", |
p_defs->child_dev_size); |
return; |
} |
|
/* Flag an error for unexpected size, but continue anyway. */ |
if (p_defs->child_dev_size != expected_size) |
DRM_ERROR("Unexpected child device config size %u (expected %u for VBT version %u)\n", |
p_defs->child_dev_size, expected_size, bdb->version); |
|
/* get the block size of general definitions */ |
block_size = get_blocksize(p_defs); |
/* get the number of child device */ |
1285,7 → 1370,7 |
|
/** |
* intel_bios_init - find VBT and initialize settings from the BIOS |
* @dev: DRM device |
* @dev_priv: i915 device instance |
* |
* Loads the Video BIOS and checks that the VBT exists. Sets scratch registers |
* to appropriate values. |
1337,7 → 1422,8 |
parse_driver_features(dev_priv, bdb); |
parse_edp(dev_priv, bdb); |
parse_psr(dev_priv, bdb); |
parse_mipi(dev_priv, bdb); |
parse_mipi_config(dev_priv, bdb); |
parse_mipi_sequence(dev_priv, bdb); |
parse_ddi_ports(dev_priv, bdb); |
|
if (bios) |