summaryrefslogtreecommitdiff
path: root/linux-core/intel_bios.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core/intel_bios.c')
-rw-r--r--linux-core/intel_bios.c322
1 files changed, 159 insertions, 163 deletions
diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c
index 7f8e8513..f124fa1a 100644
--- a/linux-core/intel_bios.c
+++ b/linux-core/intel_bios.c
@@ -30,170 +30,7 @@
#include "i915_drv.h"
#include "intel_bios.h"
-#define VBT_OFFSET 0x1a
-
-/**
- * intel_find_vbt - find the VBT
- * @dev: DRM device
- *
- * Loads the Video BIOS and checks that the VBT exists.
- *
- * 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.
- */
-bool
-intel_find_bios(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct pci_dev *pdev = dev->pdev;
- void __iomem *bios;
- size_t size;
-
- bios = pci_map_rom(pdev, &size);
- if (!bios)
- return NULL;
-
- dev_priv->vbt = (struct vbt_header *)((u8 *)bios + VBT_OFFSET);
- dev_priv->bdb = (struct bdb_header *)((u8 *)bios + VBT_OFFSET +
- dev_priv->vbt->bdb_offset);
-
- if (memcmp(dev_priv->vbt->signature, "$VBT", 4) != 0) {
- DRM_ERROR("Bad VBT signature\n");
- pci_unmap_rom(pdev, bios);
- return NULL;
- }
-
- return bios;
-}
-
#if 0
-/**
- * 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.
- */
-struct drm_display_mode *
-i830_bios_get_panel_mode(struct drm_device *dev, 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;
- CARD8 *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)
{
@@ -242,3 +79,162 @@ i830_bios_get_aim_data_block (ScrnInfoPtr pScrn, int aim, int data_block)
return NULL;
}
#endif
+
+static void *
+find_section(struct bdb_header *bdb, int section_id)
+{
+ u8 *base = (u8 *)bdb;
+ int index = 0;
+ u16 total, current_size;
+ u8 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 = *((u16 *)(base + index));
+ index += 2;
+ if (current_id == section_id)
+ return base + index;
+ index += current_size;
+ }
+
+ return NULL;
+}
+
+/* Try to find panel data */
+static void
+parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+ struct bdb_lvds_options *lvds_options;
+ struct bdb_lvds_lfp_data *lvds_lfp_data;
+ struct bdb_lvds_lfp_data_entry *entry;
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
+
+ /* Defaults if we can't find VBT info */
+ dev_priv->lvds_dither = 0;
+ dev_priv->lvds_vbt = 0;
+
+ lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+ if (!lvds_options)
+ return;
+
+ dev_priv->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;
+
+ dev_priv->lvds_vbt = 1;
+
+ entry = &lvds_lfp_data->data[lvds_options->panel_type];
+ dvo_timing = &entry->dvo_timing;
+
+ panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
+ DRM_MEM_DRIVER);
+
+ panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
+ dvo_timing->hactive_lo;
+ panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
+ panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
+ dvo_timing->hsync_pulse_width;
+ panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+
+ panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
+ dvo_timing->vactive_lo;
+ panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
+ dvo_timing->vsync_off;
+ panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
+ dvo_timing->vsync_pulse_width;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
+ ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
+ panel_fixed_mode->clock = dvo_timing->clock * 10;
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(panel_fixed_mode);
+
+ dev_priv->vbt_mode = panel_fixed_mode;
+
+ DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
+ return;
+}
+
+static void
+parse_general_features(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_general_features *general;
+
+ /* Set sensible defaults in case we can't find the general block */
+ dev_priv->int_tv_support = 1;
+ dev_priv->int_crt_support = 1;
+
+ general = find_section(bdb, BDB_GENERAL_FEATURES);
+ if (general) {
+ dev_priv->int_tv_support = general->int_tv_support;
+ dev_priv->int_crt_support = general->int_crt_support;
+ }
+}
+
+/**
+ * intel_init_bios - initialize VBIOS settings & find VBT
+ * @dev: DRM device
+ *
+ * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
+ * to appropriate values.
+ *
+ * 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.
+ */
+bool
+intel_init_bios(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct pci_dev *pdev = dev->pdev;
+ struct vbt_header *vbt = NULL;
+ struct bdb_header *bdb;
+ u8 __iomem *bios;
+ size_t size;
+ int i;
+
+ bios = pci_map_rom(pdev, &size);
+ if (!bios)
+ return -1;
+
+ /* Scour memory looking for the VBT signature */
+ for (i = 0; i + 4 < size; i++) {
+ if (!memcmp(bios + i, "$VBT", 4)) {
+ vbt = (struct vbt_header *)(bios + i);
+ break;
+ }
+ }
+
+ if (!vbt) {
+ DRM_ERROR("VBT signature missing\n");
+ pci_unmap_rom(pdev, bios);
+ return -1;
+ }
+
+ bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+
+ /* Grab useful general definitions */
+ parse_general_features(dev_priv, bdb);
+ parse_panel_data(dev_priv, bdb);
+
+ return 0;
+}