xenocara/driver/xf86-video-mga/src/mga_bios.c
2008-07-29 20:02:36 +00:00

472 lines
12 KiB
C

/*
* (C) Copyright IBM Corporation 2005
* All Rights Reserved.
*
* 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
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
* IBM AND/OR THEIR SUPPLIERS 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.
*/
/**
* \file mga_bios.c
* Routines for processing the PInS data stored in the MGA BIOS.
*
* The structure of this code was inspired by similar routines in the Linux
* kernel matroxfb code. Specifically, the routines in matroxfb_misc.c. In
* addition, that code was used in place of documentation about the structure
* of the PInS data for non-G450 cards.
*
* \author Ian Romanick <idr@us.ibm.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* All drivers should typically include these */
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Resources.h"
/* All drivers need this */
#include "compiler.h"
/* Drivers for PCI hardware need this */
#include "xf86PciInfo.h"
/* Drivers that need to access the PCI config space directly need this */
#include "xf86Pci.h"
#include <X11/Xmd.h>
#include "mga.h"
#if defined(DEBUG)
#define BIOS_DEBUG
#endif
/**
* Read a little-endian, unaligned data value and return as 16-bit.
*/
static __inline__ CARD16 get_u16( const CARD8 * data )
{
CARD16 temp;
temp = data[1];
temp <<= 8;
temp += data[0];
return temp;
}
/**
* Read a little-endian, unaligned data value and return as 32-bit.
*/
static __inline__ CARD32 get_u32( const CARD8 * data )
{
CARD32 temp;
temp = data[3];
temp <<= 8;
temp += data[2];
temp <<= 8;
temp += data[1];
temp <<= 8;
temp += data[0];
return temp;
}
/**
* Parse version 0x01XX of the BIOS PInS structure.
*
* Version 0x01XX of the BIOS PInS structure is only found in Millenium cards.
*
* \todo
* There used to be an "OverclockMem" option that would scale the memory clock
* by 12 instead 10. Add support for this back in.
*/
static void mga_parse_bios_ver_1( struct mga_bios_values * bios,
const CARD8 * bios_data )
{
unsigned maxdac;
if ( get_u16( & bios_data[24] ) ) {
maxdac = get_u16( & bios_data[24] ) * 10;
}
else {
/* There is some disagreement here between the Linux kernel matroxfb
* driver and the old X.org mga driver. matroxfb has case-statements
* for 0 and 1 (with the values below), and the mga driver has
* case-statements for 1 and 2 (with the values below). The default
* value for the mga driver is 17500, but the default value for the
* matroxfb driver is 240000.
*/
switch( bios_data[22] ) {
case 0: maxdac = 175000; break;
case 1: maxdac = 220000; break;
case 2: maxdac = 250000; break;
default: maxdac = 240000; break;
}
}
if ( get_u16( & bios_data[28] ) ) {
bios->mem_clock = get_u16( & bios_data[28] ) * 10;
}
if ( (bios_data[48] & 0x01) == 0 ) {
bios->fast_bitblt = TRUE;
}
bios->pixel.max_freq = maxdac;
}
/**
* Parse version 0x02XX of the BIOS PInS structure.
*
* Version 0x02XX of the BIOS PInS structure is only found in Millenium II
* and Mystique cards.
*/
static void mga_parse_bios_ver_2( struct mga_bios_values * bios,
const CARD8 * bios_data )
{
if ( bios_data[41] != 0xff ) {
const unsigned maxdac = (bios_data[41] + 100) * 1000;
bios->pixel.max_freq = maxdac;
bios->system.max_freq = maxdac;
}
if ( bios_data[43] != 0xff ) {
const unsigned system_pll = (bios_data[43] + 100) * 1000;
bios->mem_clock = system_pll;
}
}
/**
* Parse version 0x03XX of the BIOS PInS structure.
*
* Version 0x03XX of the BIOS PInS structure is only found in G100 and G200
* cards.
*/
static void mga_parse_bios_ver_3( struct mga_bios_values * bios,
const CARD8 * bios_data )
{
if ( bios_data[36] != 0xff ) {
const unsigned maxdac = (bios_data[36] + 100) * 1000;
bios->pixel.max_freq = maxdac;
bios->system.max_freq = maxdac;
}
if ( (bios_data[52] & 0x20) != 0 ) {
bios->pll_ref_freq = 14318;
}
}
/**
* Parse version 0x04XX of the BIOS PInS structure.
*
* Version 0x04XX of the BIOS PInS structure is only found in G400 cards.
*/
static void mga_parse_bios_ver_4( struct mga_bios_values * bios,
const CARD8 * bios_data )
{
if ( bios_data[39] != 0xff ) {
const unsigned maxdac = bios_data[39] * 4 * 1000;
bios->pixel.max_freq = maxdac;
bios->system.max_freq = maxdac;
}
if ( bios_data[38] != 0xff ) {
const unsigned maxdac = bios_data[38] * 4 * 1000;
bios->system.max_freq = maxdac;
}
if ( (bios_data[92] & 0x01) != 0 ) {
bios->pll_ref_freq = 14318;
}
bios->host_interface = (bios_data[95] >> 3) & 0x07;
if ( bios_data[65] != 0xff ) {
const unsigned system_pll = bios_data[65] * 4 * 1000;
bios->mem_clock = system_pll;
}
}
/**
* Parse version 0x05XX of the BIOS PInS structure.
*
* Version 0x05XX of the BIOS PInS structure is only found in G450 and G550
* cards.
*/
static void mga_parse_bios_ver_5( struct mga_bios_values * bios,
const CARD8 * bios_data )
{
const unsigned scale = (bios_data[4] != 0) ? 8000 : 6000;
if ( bios_data[38] != 0xff ) {
const unsigned maxdac = bios_data[38] * scale;
bios->pixel.max_freq = maxdac;
bios->system.max_freq = maxdac;
bios->video.max_freq = maxdac;
}
if ( bios_data[36] != 0xff ) {
const unsigned maxdac = bios_data[36] * scale;
bios->system.max_freq = maxdac;
bios->video.max_freq = maxdac;
}
if ( bios_data[37] != 0xff ) {
const unsigned maxdac = bios_data[37] * scale;
bios->video.max_freq = maxdac;
}
if ( bios_data[123] != 0xff ) {
const unsigned mindac = bios_data[123] * scale;
bios->pixel.min_freq = mindac;
bios->system.min_freq = mindac;
bios->video.min_freq = mindac;
}
if ( bios_data[121] != 0xff ) {
const unsigned mindac = bios_data[121] * scale;
bios->system.min_freq = mindac;
bios->video.min_freq = mindac;
}
if ( bios_data[122] != 0xff ) {
const unsigned mindac = bios_data[122] * scale;
bios->video.min_freq = mindac;
}
if ( bios_data[92] != 0xff ) {
const unsigned system_pll = bios_data[92] * 4 * 1000;
bios->mem_clock = system_pll;
}
if ( (bios_data[110] & 0x01) != 0 ) {
bios->pll_ref_freq = 14318;
}
bios->host_interface = (bios_data[113] >> 3) & 0x07;
}
/**
* Read the BIOS data from the card and initialize internal values.
*/
Bool mga_read_and_process_bios( ScrnInfoPtr pScrn )
{
CARD8 bios_data[0x20000];
unsigned offset;
MGAPtr pMga = MGAPTR(pScrn);
#ifndef XSERVER_LIBPCIACCESS
Bool pciBIOS = TRUE;
#endif
int rlen;
static const unsigned expected_length[] = { 0, 64, 64, 64, 128, 128 };
unsigned version;
unsigned pins_len;
const CARD8 * pins_data;
int err;
#ifdef BIOS_DEBUG
static const char * const host_interface_strings[8] = {
"Reserved",
"Reserved",
"Reserved",
"Hybrid (AGP 4x on data transfers only)",
"PCI",
"AGP 1x",
"AGP 2x",
"AGP 4x"
};
#endif
/* Initialize the stored BIOS data to some reasonable values for the
* card at hand. This is done now so that even if the PInS data block
* isn't found or can't be read we'll still have some reasonable values
* to use.
*/
(void) memcpy(& pMga->bios, & pMga->chip_attribs->default_bios_values,
sizeof(struct mga_bios_values));
/* If the BIOS address was probed, it was found from the PCI config space
* If it was given in the config file, try to guess when it looks like it
* might be controlled by the PCI config space.
*/
#ifdef XSERVER_LIBPCIACCESS
err = pci_device_read_rom(pMga->PciInfo, bios_data);
#else
if (pMga->BiosFrom == X_DEFAULT) {
pciBIOS = FALSE;
}
else if (pMga->BiosFrom == X_CONFIG && pMga->BiosAddress < 0x100000) {
pciBIOS = TRUE;
}
if (pciBIOS) {
rlen = xf86ReadPciBIOS(0, pMga->PciTag, pMga->framebuffer_bar,
bios_data, sizeof(bios_data));
}
else {
rlen = xf86ReadDomainMemory(pMga->PciTag, pMga->BiosAddress,
sizeof(bios_data), bios_data);
}
err = rlen < (bios_data[2] << 9);
#endif
if (err) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"Could not retrieve video BIOS!\n");
return FALSE;
}
/* Get the output mode set by the BIOS */
pMga->BiosOutputMode = bios_data[0x7ff1];
/* Get the video BIOS info block */
if (strncmp((char *)(&bios_data[45]), "MATROX", 6)) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"Video BIOS info block not detected!\n");
return FALSE;
}
#if X_BYTE_ORDER == X_BIG_ENDIAN
/* The offset information that is included in the BIOS on PC Matrox cards
* is not there on PowerPC cards. Instead, we have to search the BIOS
* image for the magic signature. This is the 16-bit value 0x412d. This
* value is followed by the length of the PInS block. We know that this
* must (currently) be either 0x80 or 0x40.
*
* Happy hunting.
*/
for (offset = 0 ; offset < 0x7ffc ; offset++) {
if ((bios_data[offset] == 0x2e) && (bios_data[offset+1] == 0x41)
&& ((bios_data[offset+2] == 0x80) || (bios_data[offset+2] == 0x40))) {
break;
}
}
if (offset == 0x7ffc) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"Video BIOS PInS data not found!\n");
return FALSE;
}
#else
/* Get the info block offset */
offset = (unsigned)((bios_data[0x7ffd] << 8) | bios_data[0x7ffc]);
#endif
/* Let the world know what we are up to */
xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
"Video BIOS info block at offset 0x%05lX\n",
(long)(offset));
/* Determine the version of the PInS block. This will determine how the
* data is processed. Only the first version of the PInS data structure
* did *NOT* have the initial 0x412e (in little-endian order!) signature.
*/
pins_data = & bios_data[ offset ];
if ( (pins_data[0] == 0x2e) && (pins_data[1] == 0x41) ) {
version = pins_data[5];
pins_len = pins_data[2];
}
else {
version = 1;
pins_len = get_u16( pins_data );
}
if ( (version < 1) || (version > 5) ) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"PInS data version (%u) not supported.\n", version);
return FALSE;
}
if ( pins_len != expected_length[ version ] ) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"PInS data length (%u) does not match expected length (%u)"
" for version %u.X.\n",
pins_len, expected_length[ version ], version);
return FALSE;
}
switch( version ) {
case 1: mga_parse_bios_ver_1( & pMga->bios, pins_data ); break;
case 2: mga_parse_bios_ver_2( & pMga->bios, pins_data ); break;
case 3: mga_parse_bios_ver_3( & pMga->bios, pins_data ); break;
case 4: mga_parse_bios_ver_4( & pMga->bios, pins_data ); break;
case 5: mga_parse_bios_ver_5( & pMga->bios, pins_data ); break;
}
#ifdef BIOS_DEBUG
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"system VCO = [%u, %u]\n",
pMga->bios.system.min_freq, pMga->bios.system.max_freq);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"pixel VCO = [%u, %u]\n",
pMga->bios.pixel.min_freq, pMga->bios.pixel.max_freq);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"video VCO = [%u, %u]\n",
pMga->bios.video.min_freq, pMga->bios.video.max_freq);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"memory clock = %ukHz\n", pMga->bios.mem_clock);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"PLL reference frequency = %ukHz\n",
pMga->bios.pll_ref_freq);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"%s fast bitblt\n",
(pMga->bios.fast_bitblt) ? "Has" : "Does not have");
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Host interface: %s (%u)\n",
host_interface_strings[ pMga->bios.host_interface ],
pMga->bios.host_interface);
#endif
return TRUE;
}