summaryrefslogtreecommitdiff
path: root/linux-core/intel_bios.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@jbarnes-t61.(none)>2008-05-09 14:19:00 -0700
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-05-09 14:19:00 -0700
commit12725a37af691345e74fe22d53300abec2581852 (patch)
tree78287fef05d529d0a3056050bd15b38ded351048 /linux-core/intel_bios.c
parenteeff906aa0f64da12a0154c66d99e8492dd95107 (diff)
i915: add basic VBT support
Map the VBIOS (and therefore VBT) at init time for use by various output initialization routines.
Diffstat (limited to 'linux-core/intel_bios.c')
-rw-r--r--linux-core/intel_bios.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c
new file mode 100644
index 00000000..7f8e8513
--- /dev/null
+++ b/linux-core/intel_bios.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#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)
+{
+ 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;
+}
+#endif