summaryrefslogtreecommitdiff
path: root/src/i830_bios.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/i830_bios.c')
-rw-r--r--src/i830_bios.c401
1 files changed, 120 insertions, 281 deletions
diff --git a/src/i830_bios.c b/src/i830_bios.c
index a8193fc5..fe55d239 100644
--- a/src/i830_bios.c
+++ b/src/i830_bios.c
@@ -40,84 +40,152 @@
#include "edid.h"
#define INTEL_BIOS_8(_addr) (bios[_addr])
-#define INTEL_BIOS_16(_addr) (bios[_addr] | \
+#define INTEL_BIOS_16(_addr) (bios[_addr] | \
(bios[_addr + 1] << 8))
-#define INTEL_BIOS_32(_addr) (bios[_addr] | \
- (bios[_addr + 1] << 8) \
- (bios[_addr + 2] << 16) \
+#define INTEL_BIOS_32(_addr) (bios[_addr] | \
+ (bios[_addr + 1] << 8) \
+ (bios[_addr + 2] << 16) \
(bios[_addr + 3] << 24))
/* XXX */
#define INTEL_VBIOS_SIZE (64 * 1024)
+static void *
+find_section(struct bdb_header *bdb, int section_id)
+{
+ unsigned char *base = (unsigned char *)bdb;
+ int index = 0;
+ uint16_t total, current_size;
+ unsigned char current_id;
+
+ /* skip to first section */
+ index += bdb->header_size;
+ total = bdb->bdb_size;
+
+ /* walk the sections looking for section_id */
+ while (index < total) {
+ current_id = *(base + index);
+ index++;
+ current_size = *((uint16_t *)(base + index));
+ index += 2;
+ if (current_id == section_id)
+ return base + index;
+ index += current_size;
+ }
+
+ return NULL;
+}
+
+/**
+ * Returns the BIOS's fixed panel mode.
+ *
+ * Note that many BIOSes will have the appropriate tables for a panel even when
+ * a panel is not attached. Additionally, many BIOSes adjust table sizes or
+ * offsets, such that this parsing fails. Thus, almost any other method for
+ * detecting the panel mode is preferable.
+ */
static void
-i830DumpBIOSToFile(ScrnInfoPtr pScrn, unsigned char *bios)
+parse_panel_data(I830Ptr pI830, struct bdb_header *bdb)
{
- const char *filename = "/tmp/xf86-video-intel-VBIOS";
- FILE *f;
+ struct bdb_lvds_options *lvds_options;
+ struct bdb_lvds_lfp_data *lvds_lfp_data;
+ struct bdb_lvds_lfp_data_entry *entry;
+ DisplayModePtr fixed_mode;
+ unsigned char *timing_ptr;
- f = fopen(filename, "w");
- if (f == NULL) {
- xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Couldn't open %s\n", filename);
+ /* Defaults if we can't find VBT info */
+ pI830->lvds_dither = 0;
+
+ lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+ if (!lvds_options)
+ return;
+
+ pI830->lvds_dither = lvds_options->pixel_dither;
+ if (lvds_options->panel_type == 0xff)
+ return;
+
+ lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
+ if (!lvds_lfp_data)
return;
- }
- if (fwrite(bios, INTEL_VBIOS_SIZE, 1, f) != 1) {
- xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Couldn't write BIOS data\n");
- }
- xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Wrote BIOS contents to %s\n",
- filename);
- fclose(f);
+ entry = &lvds_lfp_data->data[lvds_options->panel_type];
+ timing_ptr = (unsigned char *)&entry->dvo_timing;
+
+ fixed_mode = xnfalloc(sizeof(DisplayModeRec));
+ memset(fixed_mode, 0, sizeof(*fixed_mode));
+
+ /* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing
+ * block, pull the contents out using EDID macros.
+ */
+ fixed_mode->HDisplay = _H_ACTIVE(timing_ptr);
+ fixed_mode->VDisplay = _V_ACTIVE(timing_ptr);
+ fixed_mode->HSyncStart = fixed_mode->HDisplay +
+ _H_SYNC_OFF(timing_ptr);
+ fixed_mode->HSyncEnd = fixed_mode->HSyncStart +
+ _H_SYNC_WIDTH(timing_ptr);
+ fixed_mode->HTotal = fixed_mode->HDisplay +
+ _H_BLANK(timing_ptr);
+ fixed_mode->VSyncStart = fixed_mode->VDisplay +
+ _V_SYNC_OFF(timing_ptr);
+ fixed_mode->VSyncEnd = fixed_mode->VSyncStart +
+ _V_SYNC_WIDTH(timing_ptr);
+ fixed_mode->VTotal = fixed_mode->VDisplay +
+ _V_BLANK(timing_ptr);
+ fixed_mode->Clock = _PIXEL_CLOCK(timing_ptr) / 1000;
+ fixed_mode->type = M_T_PREFERRED;
+
+ xf86SetModeDefaultName(fixed_mode);
+
+ pI830->lvds_fixed_mode = fixed_mode;
}
-static void *
-find_section(struct bdb_header *bdb, int section_id)
+static void
+parse_general_features(I830Ptr pI830, struct bdb_header *bdb)
{
- unsigned char *base = (unsigned char *)bdb;
- int index = 0;
- uint16_t total, current_size;
- unsigned char current_id;
-
- /* skip to first section */
- index += bdb->header_size;
- total = bdb->bdb_size;
-
- /* walk the sections looking for section_id */
- while (index < total) {
- current_id = *(base + index);
- index++;
- current_size = *((uint16_t *)(base + index));
- index += 2;
- if (current_id == section_id)
- return base + index;
- index += current_size;
- }
-
- return NULL;
+ struct bdb_general_features *general;
+
+ /* Set sensible defaults in case we can't find the general block */
+ pI830->tv_present = 1;
+
+ general = find_section(bdb, BDB_GENERAL_FEATURES);
+ if (!general)
+ return;
+
+ pI830->tv_present = general->int_tv_support;
+ pI830->lvds_use_ssc = general->enable_ssc;
+ if (pI830->lvds_use_ssc) {
+ if (IS_I855(pI830))
+ pI830->lvds_ssc_freq = general->ssc_freq ? 66 : 48;
+ else
+ pI830->lvds_ssc_freq = general->ssc_freq ? 100 : 96;
+ }
}
/**
- * Loads the Video BIOS and checks that the VBT exists.
+ * i830_bios_init - map VBIOS, find VBT
*
* VBT existence is a sanity check that is relied on by other i830_bios.c code.
* Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
* feed an updated VBT back through that, compared to what we'll fetch using
* this method of groping around in the BIOS data.
+ *
+ * Returns 0 on success, nonzero on failure.
*/
-unsigned char *
-i830_bios_get (ScrnInfoPtr pScrn)
+int
+i830_bios_init(ScrnInfoPtr pScrn)
{
I830Ptr pI830 = I830PTR(pScrn);
struct vbt_header *vbt;
- int vbt_off;
+ struct bdb_header *bdb;
+ int vbt_off, bdb_off;
unsigned char *bios;
vbeInfoPtr pVbe;
bios = xalloc(INTEL_VBIOS_SIZE);
if (bios == NULL)
- return NULL;
+ return -1;
- pVbe = VBEInit (NULL, pI830->pEnt->index);
+ pVbe = VBEInit(NULL, pI830->pEnt->index);
if (pVbe != NULL) {
memcpy(bios, xf86int10Addr(pVbe->pInt10,
pVbe->pInt10->BIOSseg << 4),
@@ -131,15 +199,12 @@ i830_bios_get (ScrnInfoPtr pScrn)
#endif
}
- if (0)
- i830DumpBIOSToFile(pScrn, bios);
-
vbt_off = INTEL_BIOS_16(0x1a);
if (vbt_off >= INTEL_VBIOS_SIZE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT offset: 0x%x\n",
vbt_off);
xfree(bios);
- return NULL;
+ return -1;
}
vbt = (struct vbt_header *)(bios + vbt_off);
@@ -147,243 +212,17 @@ i830_bios_get (ScrnInfoPtr pScrn)
if (memcmp(vbt->signature, "$VBT", 4) != 0) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT signature\n");
xfree(bios);
- return NULL;
+ return -1;
}
- return bios;
-}
-
-void
-i830_bios_get_ssc(ScrnInfoPtr pScrn)
-{
- I830Ptr pI830 = I830PTR(pScrn);
- struct vbt_header *vbt;
- struct bdb_header *bdb;
- struct bdb_general_features *bdb_features;
- int vbt_off, bdb_off;
- unsigned char *bios;
-
- bios = i830_bios_get(pScrn);
-
- if (bios == NULL)
- return;
-
- vbt_off = INTEL_BIOS_16(0x1a);
- vbt = (struct vbt_header *)(bios + vbt_off);
+ /* Now that we've found the VBIOS, go scour the VBTs */
bdb_off = vbt_off + vbt->bdb_offset;
bdb = (struct bdb_header *)(bios + bdb_off);
- bdb_features = find_section(bdb, BDB_GENERAL_FEATURES);
- if (!bdb_features)
- return;
-
- pI830->lvds_use_ssc = bdb_features->enable_ssc;
- if (pI830->lvds_use_ssc) {
- if (IS_I855(pI830))
- pI830->lvds_ssc_freq = bdb_features->ssc_freq ? 66 : 48;
- else
- pI830->lvds_ssc_freq = bdb_features->ssc_freq ? 100 : 96;
- }
+ parse_general_features(pI830, bdb);
+ parse_panel_data(pI830, bdb);
xfree(bios);
-}
-void
-i830_bios_get_tv(ScrnInfoPtr pScrn)
-{
- I830Ptr pI830 = I830PTR(pScrn);
- struct vbt_header *vbt;
- struct bdb_header *bdb;
- struct bdb_general_features *bdb_features;
- int vbt_off, bdb_off;
- unsigned char *bios;
-
- bios = i830_bios_get(pScrn);
-
- if (bios == NULL)
- return;
-
- vbt_off = INTEL_BIOS_16(0x1a);
- vbt = (struct vbt_header *)(bios + vbt_off);
- bdb_off = vbt_off + vbt->bdb_offset;
- bdb = (struct bdb_header *)(bios + bdb_off);
-
- bdb_features = find_section(bdb, BDB_GENERAL_FEATURES);
- if (!bdb_features)
- return;
-
- pI830->tv_present = bdb_features->int_tv_support;
-
- xfree(bios);
-}
-
-/**
- * Returns the BIOS's fixed panel mode.
- *
- * Note that many BIOSes will have the appropriate tables for a panel even when
- * a panel is not attached. Additionally, many BIOSes adjust table sizes or
- * offsets, such that this parsing fails. Thus, almost any other method for
- * detecting the panel mode is preferable.
- */
-DisplayModePtr
-i830_bios_get_panel_mode(ScrnInfoPtr pScrn, Bool *wants_dither)
-{
- I830Ptr pI830 = I830PTR(pScrn);
- struct vbt_header *vbt;
- struct bdb_header *bdb;
- int vbt_off, bdb_off, bdb_block_off, block_size;
- int panel_type = -1;
- unsigned char *bios;
-
- bios = i830_bios_get (pScrn);
-
- if (bios == NULL)
- return NULL;
-
- vbt_off = INTEL_BIOS_16(0x1a);
- vbt = (struct vbt_header *)(bios + vbt_off);
- bdb_off = vbt_off + vbt->bdb_offset;
- bdb = (struct bdb_header *)(bios + bdb_off);
-
- if (memcmp(bdb->signature, "BIOS_DATA_BLOCK ", 16) != 0) {
- xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad BDB signature\n");
- xfree(bios);
- return NULL;
- }
-
- *wants_dither = FALSE;
- for (bdb_block_off = bdb->header_size; bdb_block_off < bdb->bdb_size;
- bdb_block_off += block_size)
- {
- int start = bdb_off + bdb_block_off;
- int id;
- struct lvds_bdb_1 *lvds1;
- struct lvds_bdb_2 *lvds2;
- struct lvds_bdb_2_fp_params *fpparam;
- struct lvds_bdb_2_fp_edid_dtd *fptiming;
- DisplayModePtr fixed_mode;
- uint8_t *timing_ptr;
-
- id = INTEL_BIOS_8(start);
- block_size = INTEL_BIOS_16(start + 1) + 3;
- switch (id) {
- case 40:
- lvds1 = (struct lvds_bdb_1 *)(bios + start);
- panel_type = lvds1->panel_type;
- if (lvds1->caps & LVDS_CAP_DITHER)
- *wants_dither = TRUE;
- break;
- case 41:
- if (panel_type == -1)
- break;
-
- lvds2 = (struct lvds_bdb_2 *)(bios + start);
- fpparam = (struct lvds_bdb_2_fp_params *)(bios +
- bdb_off + lvds2->panels[panel_type].fp_params_offset);
- fptiming = (struct lvds_bdb_2_fp_edid_dtd *)(bios +
- bdb_off + lvds2->panels[panel_type].fp_edid_dtd_offset);
- timing_ptr = bios + bdb_off +
- lvds2->panels[panel_type].fp_edid_dtd_offset;
-
- if (fpparam->terminator != 0xffff) {
- /* Apparently the offsets are wrong for some BIOSes, so we
- * try the other offsets if we find a bad terminator.
- */
- fpparam = (struct lvds_bdb_2_fp_params *)(bios +
- bdb_off + lvds2->panels[panel_type].fp_params_offset + 8);
- fptiming = (struct lvds_bdb_2_fp_edid_dtd *)(bios +
- bdb_off + lvds2->panels[panel_type].fp_edid_dtd_offset + 8);
- timing_ptr = bios + bdb_off +
- lvds2->panels[panel_type].fp_edid_dtd_offset + 8;
-
- if (fpparam->terminator != 0xffff)
- continue;
- }
-
- fixed_mode = xnfalloc(sizeof(DisplayModeRec));
- memset(fixed_mode, 0, sizeof(*fixed_mode));
-
- /* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing
- * block, pull the contents out using EDID macros.
- */
- fixed_mode->HDisplay = _H_ACTIVE(timing_ptr);
- fixed_mode->VDisplay = _V_ACTIVE(timing_ptr);
- fixed_mode->HSyncStart = fixed_mode->HDisplay +
- _H_SYNC_OFF(timing_ptr);
- fixed_mode->HSyncEnd = fixed_mode->HSyncStart +
- _H_SYNC_WIDTH(timing_ptr);
- fixed_mode->HTotal = fixed_mode->HDisplay +
- _H_BLANK(timing_ptr);
- fixed_mode->VSyncStart = fixed_mode->VDisplay +
- _V_SYNC_OFF(timing_ptr);
- fixed_mode->VSyncEnd = fixed_mode->VSyncStart +
- _V_SYNC_WIDTH(timing_ptr);
- fixed_mode->VTotal = fixed_mode->VDisplay +
- _V_BLANK(timing_ptr);
- fixed_mode->Clock = _PIXEL_CLOCK(timing_ptr) / 1000;
- fixed_mode->type = M_T_PREFERRED;
-
- xf86SetModeDefaultName(fixed_mode);
-
- if (pI830->debug_modes) {
- xf86DrvMsg(pScrn->scrnIndex, X_INFO,
- "Found panel mode in BIOS VBT tables:\n");
- xf86PrintModeline(pScrn->scrnIndex, fixed_mode);
- }
-
- xfree(bios);
- return fixed_mode;
- }
- }
-
- xfree(bios);
- return NULL;
-}
-
-unsigned char *
-i830_bios_get_aim_data_block (ScrnInfoPtr pScrn, int aim, int data_block)
-{
- unsigned char *bios;
- int bdb_off;
- int vbt_off;
- int aim_off;
- struct vbt_header *vbt;
- struct aimdb_header *aimdb;
- struct aimdb_block *aimdb_block;
-
- bios = i830_bios_get (pScrn);
- if (!bios)
- return NULL;
-
- vbt_off = INTEL_BIOS_16(0x1a);
- vbt = (struct vbt_header *)(bios + vbt_off);
-
- aim_off = vbt->aim_offset[aim];
- if (!aim_off)
- {
- free (bios);
- return NULL;
- }
- xf86DrvMsg(pScrn->scrnIndex, X_INFO, "aim_off %d\n", aim_off);
- aimdb = (struct aimdb_header *) (bios + vbt_off + aim_off);
- bdb_off = aimdb->aimdb_header_size;
- while (bdb_off < aimdb->aimdb_size)
- {
- aimdb_block = (struct aimdb_block *) (bios + vbt_off + aim_off + bdb_off);
- if (aimdb_block->aimdb_id == data_block)
- {
- unsigned char *aim = malloc (aimdb_block->aimdb_size + sizeof (struct aimdb_block));
- if (!aim)
- {
- free (bios);
- return NULL;
- }
- memcpy (aim, aimdb_block, aimdb_block->aimdb_size + sizeof (struct aimdb_block));
- free (bios);
- return aim;
- }
- bdb_off += aimdb_block->aimdb_size + sizeof (struct aimdb_block);
- }
- free (bios);
- return NULL;
+ return 0;
}