From 12725a37af691345e74fe22d53300abec2581852 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 9 May 2008 14:19:00 -0700 Subject: i915: add basic VBT support Map the VBIOS (and therefore VBT) at init time for use by various output initialization routines. --- linux-core/Makefile.kernel | 2 +- linux-core/intel_bios.c | 244 +++++++++++++++++++++++++++++++++++++++++++++ linux-core/intel_bios.h | 187 ++++++++++++++++++++++++++++++++++ shared-core/i915_drv.h | 6 ++ shared-core/i915_init.c | 7 ++ 5 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 linux-core/intel_bios.c create mode 100644 linux-core/intel_bios.h diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 6480d51a..f77f8642 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -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 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 + * + */ +#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 index 00000000..b17856de --- /dev/null +++ b/linux-core/intel_bios.h @@ -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 + * + */ + +#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_ */ diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index a7040f5b..2e7b6bd2 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -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; diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c index 53574eb7..8824b959 100644 --- a/shared-core/i915_init.c +++ b/shared-core/i915_init.c @@ -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); -- cgit v1.2.3