245 lines
7.3 KiB
C
245 lines
7.3 KiB
C
/*
|
|
* 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
|