i915: add basic VBT support
authorJesse Barnes <jbarnes@jbarnes-t61.(none)>
Fri, 9 May 2008 21:19:00 +0000 (14:19 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Fri, 9 May 2008 21:19:00 +0000 (14:19 -0700)
Map the VBIOS (and therefore VBT) at init time for use by various output
initialization routines.

linux-core/Makefile.kernel
linux-core/intel_bios.c [new file with mode: 0644]
linux-core/intel_bios.h [new file with mode: 0644]
shared-core/i915_drv.h
shared-core/i915_init.c

index 6480d51..f77f864 100644 (file)
@@ -22,7 +22,7 @@ mga-objs    := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
 i810-objs   := i810_drv.o i810_dma.o
 i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \
                i915_buffer.o i915_execbuf.o \
-               intel_display.o intel_crt.o intel_lvds.o \
+               intel_display.o intel_crt.o intel_lvds.o intel_bios.o \
                intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.o \
                intel_tv.o i915_compat.o intel_dvo.o dvo_ch7xxx.o \
                dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o
diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c
new file mode 100644 (file)
index 0000000..7f8e851
--- /dev/null
@@ -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
diff --git a/linux-core/intel_bios.h b/linux-core/intel_bios.h
new file mode 100644 (file)
index 0000000..b17856d
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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>
+ *
+ */
+
+#ifndef _I830_BIOS_H_
+#define _I830_BIOS_H_
+
+#include "drmP.h"
+
+struct vbt_header {
+       u8 signature[20];               /**< Always starts with 'VBT$' */
+       u16 version;                    /**< decimal */
+       u16 header_size;                /**< in bytes */
+       u16 vbt_size;                   /**< in bytes */
+       u8 vbt_checksum;
+       u8 reserved0;
+       u32 bdb_offset;                 /**< from beginning of VBT */
+       u32 aim_offset[4];              /**< from beginning of VBT */
+} __attribute__((packed));
+
+struct bdb_header {
+       u8 signature[16];               /**< Always 'BIOS_DATA_BLOCK' */
+       u16 version;                    /**< decimal */
+       u16 header_size;                /**< in bytes */
+       u16 bdb_size;                   /**< in bytes */
+       u8 type; /* 0 == desktop, 1 == mobile */
+       u8 relstage;
+       u8 chipset;
+       u8 lvds_present:1;
+       u8 tv_present:1;
+       u8 rsvd2:6; /* finish byte */
+       u8 rsvd3[4];
+       u8 signon[155];
+       u8 copyright[61];
+       u16 code_segment;
+       u8 dos_boot_mode;
+       u8 bandwidth_percent;
+       u8 rsvd4;
+       u8 resize_pci_bios;
+       u8 rsvd5;
+       u8 rsvd6[3];
+       u8 panel_fitting:2;
+       u8 flexaim:1;
+       u8 msg_enable:1;
+       u8 clear_screen:1;
+       u8 color_flip:1;
+       u8 rsvd7:2; /* finish byte */
+       u8 download_ext_vbt:1;
+       u8 enable_ssc:1;
+       u8 ssc_freq:1;
+       u8 enable_lfp_on_override:1;
+       u8 disable_ssc_ddt:1;
+       u8 rsvd8:3; /* finish byte */
+       u8 disable_smooth_vision:1;
+       u8 single_dvi:1;
+       u8 rsvd9:6; /* finish byte */
+       u8 legacy_monitor_detect:1;
+       u8 rsvd10:7; /* finish byte */
+       u8 int_crt_support:1;
+       u8 int_tv_support:1;
+       u8 rsvd11:6; /* finish byte */
+} __attribute__((packed));
+
+#define LVDS_CAP_EDID                  (1 << 6)
+#define LVDS_CAP_DITHER                        (1 << 5)
+#define LVDS_CAP_PFIT_AUTO_RATIO       (1 << 4)
+#define LVDS_CAP_PFIT_GRAPHICS_MODE    (1 << 3)
+#define LVDS_CAP_PFIT_TEXT_MODE                (1 << 2)
+#define LVDS_CAP_PFIT_GRAPHICS         (1 << 1)
+#define LVDS_CAP_PFIT_TEXT             (1 << 0)
+struct lvds_bdb_1 {
+       u8 id;                          /**< 40 */
+       u16 size;
+       u8 panel_type;
+       u8 reserved0;
+       u16 caps;
+} __attribute__((packed));
+
+struct lvds_bdb_2_fp_params {
+       u16 x_res;
+       u16 y_res;
+       u32 lvds_reg;
+       u32 lvds_reg_val;
+       u32 pp_on_reg;
+       u32 pp_on_reg_val;
+       u32 pp_off_reg;
+       u32 pp_off_reg_val;
+       u32 pp_cycle_reg;
+       u32 pp_cycle_reg_val;
+       u32 pfit_reg;
+       u32 pfit_reg_val;
+       u16 terminator;
+} __attribute__((packed));
+
+struct lvds_bdb_2_fp_edid_dtd {
+       u16 dclk;               /**< In 10khz */
+       u8 hactive;
+       u8 hblank;
+       u8 high_h;              /**< 7:4 = hactive 11:8, 3:0 = hblank 11:8 */
+       u8 vactive;
+       u8 vblank;
+       u8 high_v;              /**< 7:4 = vactive 11:8, 3:0 = vblank 11:8 */
+       u8 hsync_off;
+       u8 hsync_pulse_width;
+       u8 vsync_off;
+       u8 high_hsync_off;      /**< 7:6 = hsync off 9:8 */
+       u8 h_image;
+       u8 v_image;
+       u8 max_hv;
+       u8 h_border;
+       u8 v_border;
+       u8 flags;
+#define FP_EDID_FLAG_VSYNC_POSITIVE    (1 << 2)
+#define FP_EDID_FLAG_HSYNC_POSITIVE    (1 << 1)
+} __attribute__((packed));
+
+struct lvds_bdb_2_entry {
+       u16 fp_params_offset;           /**< From beginning of BDB */
+       u8 fp_params_size;
+       u16 fp_edid_dtd_offset;
+       u8 fp_edid_dtd_size;
+       u16 fp_edid_pid_offset;
+       u8 fp_edid_pid_size;
+} __attribute__((packed));
+
+struct lvds_bdb_2 {
+       u8 id;                  /**< 41 */
+       u16 size;
+       u8 table_size;  /* not sure on this one */
+       struct lvds_bdb_2_entry panels[16];
+} __attribute__((packed));
+
+struct aimdb_header {
+       char signature[16];
+       char oem_device[20];
+       u16 aimdb_version;
+       u16 aimdb_header_size;
+       u16 aimdb_size;
+} __attribute__((packed));
+
+struct aimdb_block {
+       u8 aimdb_id;
+       u16 aimdb_size;
+} __attribute__((packed));
+
+struct vch_panel_data {
+       u16 fp_timing_offset;
+       u8 fp_timing_size;
+       u16 dvo_timing_offset;
+       u8 dvo_timing_size;
+       u16 text_fitting_offset;
+       u8 text_fitting_size;
+       u16 graphics_fitting_offset;
+       u8 graphics_fitting_size;
+} __attribute__((packed));
+
+struct vch_bdb_22 {
+       struct aimdb_block aimdb_block;
+       struct vch_panel_data panels[16];
+} __attribute__((packed));
+
+bool intel_find_bios(struct drm_device *dev);
+
+#endif /* _I830_BIOS_H_ */
index a7040f5..2e7b6bd 100644 (file)
@@ -33,6 +33,8 @@
 /* General customization:
  */
 
+#include "intel_bios.h"
+
 #define DRIVER_AUTHOR          "Tungsten Graphics, Inc."
 
 #define DRIVER_NAME            "i915"
@@ -171,6 +173,10 @@ struct drm_i915_private {
        struct drm_buffer_object *sarea_bo;
        struct drm_bo_kmap_obj sarea_kmap;
 
+       /* BIOS data */
+       struct vbt_header *vbt;
+       struct bdb_header *bdb;
+
        /* Register state */
        u8 saveLBB;
        u32 saveDSPACNTR;
index 53574eb..8824b95 100644 (file)
@@ -12,6 +12,7 @@
 #include "drm_sarea.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
+#include "intel_bios.h"
 
 /**
  * i915_probe_agp - get AGP bootup configuration
@@ -259,6 +260,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                  DRM_DEBUG("Error\n");
                }
 
+               ret = intel_find_bios(dev);
+               if (ret) {
+                       DRM_ERROR("failed to find VBT\n");
+                       return -ENODEV;
+               }
+
                intel_modeset_init(dev);
                drm_initial_config(dev, false);