1fa225cbcSrjs/* 2fa225cbcSrjs * Copyright � 2006 Intel Corporation 3fa225cbcSrjs * 4fa225cbcSrjs * Permission is hereby granted, free of charge, to any person obtaining a 5fa225cbcSrjs * copy of this software and associated documentation files (the "Software"), 6fa225cbcSrjs * to deal in the Software without restriction, including without limitation 7fa225cbcSrjs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8fa225cbcSrjs * and/or sell copies of the Software, and to permit persons to whom the 9fa225cbcSrjs * Software is furnished to do so, subject to the following conditions: 10fa225cbcSrjs * 11fa225cbcSrjs * The above copyright notice and this permission notice (including the next 12fa225cbcSrjs * paragraph) shall be included in all copies or substantial portions of the 13fa225cbcSrjs * Software. 14fa225cbcSrjs * 15fa225cbcSrjs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16fa225cbcSrjs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17fa225cbcSrjs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18fa225cbcSrjs * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19fa225cbcSrjs * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20fa225cbcSrjs * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21fa225cbcSrjs * SOFTWARE. 22fa225cbcSrjs * 23fa225cbcSrjs * Authors: 24fa225cbcSrjs * Eric Anholt <eric@anholt.net> 25fa225cbcSrjs * 26fa225cbcSrjs */ 27fa225cbcSrjs#ifdef HAVE_CONFIG_H 28fa225cbcSrjs#include "config.h" 29fa225cbcSrjs#undef VERSION /* XXX edid.h has a VERSION too */ 30fa225cbcSrjs#endif 31fa225cbcSrjs 32fa225cbcSrjs#include <stdio.h> 33fa225cbcSrjs#include <string.h> 34fa225cbcSrjs#include <stdlib.h> 35fa225cbcSrjs 36fa225cbcSrjs#define _PARSE_EDID_ 37fa225cbcSrjs#include "xf86.h" 38fa225cbcSrjs#include "i830.h" 39fa225cbcSrjs#include "i830_bios.h" 40fa225cbcSrjs#include "edid.h" 41fa225cbcSrjs 42fa225cbcSrjs#define INTEL_BIOS_8(_addr) (bios[_addr]) 43fa225cbcSrjs#define INTEL_BIOS_16(_addr) (bios[_addr] | \ 44fa225cbcSrjs (bios[_addr + 1] << 8)) 45fa225cbcSrjs#define INTEL_BIOS_32(_addr) (bios[_addr] | \ 46fa225cbcSrjs (bios[_addr + 1] << 8) | \ 47fa225cbcSrjs (bios[_addr + 2] << 16) | \ 48fa225cbcSrjs (bios[_addr + 3] << 24)) 49fa225cbcSrjs 50fa225cbcSrjs#define SLAVE_ADDR1 0x70 51fa225cbcSrjs#define SLAVE_ADDR2 0x72 52fa225cbcSrjsstatic void * 53fa225cbcSrjsfind_section(struct bdb_header *bdb, int section_id) 54fa225cbcSrjs{ 55fa225cbcSrjs unsigned char *base = (unsigned char *)bdb; 56fa225cbcSrjs int index = 0; 57fa225cbcSrjs uint16_t total, current_size; 58fa225cbcSrjs unsigned char current_id; 59fa225cbcSrjs 60fa225cbcSrjs /* skip to first section */ 61fa225cbcSrjs index += bdb->header_size; 62fa225cbcSrjs total = bdb->bdb_size; 63fa225cbcSrjs 64fa225cbcSrjs /* walk the sections looking for section_id */ 65fa225cbcSrjs while (index < total) { 66fa225cbcSrjs current_id = *(base + index); 67fa225cbcSrjs index++; 68fa225cbcSrjs current_size = *((uint16_t *)(base + index)); 69fa225cbcSrjs index += 2; 70fa225cbcSrjs if (current_id == section_id) 71fa225cbcSrjs return base + index; 72fa225cbcSrjs index += current_size; 73fa225cbcSrjs } 74fa225cbcSrjs 75fa225cbcSrjs return NULL; 76fa225cbcSrjs} 77fa225cbcSrjs 78fa225cbcSrjsstatic void 79fa225cbcSrjsfill_detail_timing_data(DisplayModePtr fixed_mode, unsigned char *timing_ptr) 80fa225cbcSrjs{ 81fa225cbcSrjs fixed_mode->HDisplay = _H_ACTIVE(timing_ptr); 82fa225cbcSrjs fixed_mode->VDisplay = _V_ACTIVE(timing_ptr); 83fa225cbcSrjs fixed_mode->HSyncStart = fixed_mode->HDisplay + 84fa225cbcSrjs _H_SYNC_OFF(timing_ptr); 85fa225cbcSrjs fixed_mode->HSyncEnd = fixed_mode->HSyncStart + 86fa225cbcSrjs _H_SYNC_WIDTH(timing_ptr); 87fa225cbcSrjs fixed_mode->HTotal = fixed_mode->HDisplay + 88fa225cbcSrjs _H_BLANK(timing_ptr); 89fa225cbcSrjs fixed_mode->VSyncStart = fixed_mode->VDisplay + 90fa225cbcSrjs _V_SYNC_OFF(timing_ptr); 91fa225cbcSrjs fixed_mode->VSyncEnd = fixed_mode->VSyncStart + 92fa225cbcSrjs _V_SYNC_WIDTH(timing_ptr); 93fa225cbcSrjs fixed_mode->VTotal = fixed_mode->VDisplay + 94fa225cbcSrjs _V_BLANK(timing_ptr); 95fa225cbcSrjs fixed_mode->Clock = _PIXEL_CLOCK(timing_ptr) / 1000; 96fa225cbcSrjs fixed_mode->type = M_T_PREFERRED; 97fa225cbcSrjs 98fa225cbcSrjs /* Some VBTs have bogus h/vtotal values */ 99fa225cbcSrjs if (fixed_mode->HSyncEnd > fixed_mode->HTotal) 100fa225cbcSrjs fixed_mode->HTotal = fixed_mode->HSyncEnd + 1; 101fa225cbcSrjs if (fixed_mode->VSyncEnd > fixed_mode->VTotal) 102fa225cbcSrjs fixed_mode->VTotal = fixed_mode->VSyncEnd + 1; 103fa225cbcSrjs 104fa225cbcSrjs xf86SetModeDefaultName(fixed_mode); 105fa225cbcSrjs 106fa225cbcSrjs} 107fa225cbcSrjs 108fa225cbcSrjs/** 109fa225cbcSrjs * Returns the BIOS's fixed panel mode. 110fa225cbcSrjs * 111fa225cbcSrjs * Note that many BIOSes will have the appropriate tables for a panel even when 112fa225cbcSrjs * a panel is not attached. Additionally, many BIOSes adjust table sizes or 113fa225cbcSrjs * offsets, such that this parsing fails. Thus, almost any other method for 114fa225cbcSrjs * detecting the panel mode is preferable. 115fa225cbcSrjs */ 116fa225cbcSrjsstatic void 117fa225cbcSrjsparse_integrated_panel_data(I830Ptr pI830, struct bdb_header *bdb) 118fa225cbcSrjs{ 119fa225cbcSrjs struct bdb_lvds_options *lvds_options; 120fa225cbcSrjs struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; 121fa225cbcSrjs struct bdb_lvds_lfp_data *lvds_data; 122fa225cbcSrjs struct bdb_lvds_lfp_data_entry *entry; 123fa225cbcSrjs DisplayModePtr fixed_mode; 124fa225cbcSrjs unsigned char *timing_ptr; 125fa225cbcSrjs int lfp_data_size; 126fa225cbcSrjs int dvo_offset; 127fa225cbcSrjs 128fa225cbcSrjs /* Defaults if we can't find VBT info */ 129fa225cbcSrjs pI830->lvds_dither = 0; 130fa225cbcSrjs 131fa225cbcSrjs lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); 132fa225cbcSrjs if (!lvds_options) 133fa225cbcSrjs return; 134fa225cbcSrjs 135fa225cbcSrjs pI830->lvds_dither = lvds_options->pixel_dither; 136fa225cbcSrjs if (lvds_options->panel_type == 0xff) 137fa225cbcSrjs return; 138fa225cbcSrjs 139fa225cbcSrjs lvds_data = find_section(bdb, BDB_LVDS_LFP_DATA); 140fa225cbcSrjs if (!lvds_data) { 141fa225cbcSrjs return; 142fa225cbcSrjs } 143fa225cbcSrjs 144fa225cbcSrjs lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); 145fa225cbcSrjs if (!lvds_lfp_data_ptrs) 146fa225cbcSrjs return; 147fa225cbcSrjs 148fa225cbcSrjs lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - 149fa225cbcSrjs lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; 150fa225cbcSrjs dvo_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - 151fa225cbcSrjs lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; 152fa225cbcSrjs entry = (struct bdb_lvds_lfp_data_entry *)((uint8_t *)lvds_data->data + 153fa225cbcSrjs (lfp_data_size * lvds_options->panel_type)); 154fa225cbcSrjs timing_ptr = (unsigned char *)entry + dvo_offset; 155fa225cbcSrjs if (pI830->skip_panel_detect) 156fa225cbcSrjs return; 157fa225cbcSrjs 158fa225cbcSrjs fixed_mode = xnfalloc(sizeof(DisplayModeRec)); 159fa225cbcSrjs memset(fixed_mode, 0, sizeof(*fixed_mode)); 160fa225cbcSrjs 161fa225cbcSrjs /* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing 162fa225cbcSrjs * block, pull the contents out using EDID macros. 163fa225cbcSrjs */ 164fa225cbcSrjs fill_detail_timing_data(fixed_mode, timing_ptr); 165fa225cbcSrjs pI830->lvds_fixed_mode = fixed_mode; 166fa225cbcSrjs} 167fa225cbcSrjs 168fa225cbcSrjsstatic void 169fa225cbcSrjsparse_sdvo_panel_data(I830Ptr pI830, struct bdb_header *bdb) 170fa225cbcSrjs{ 171fa225cbcSrjs DisplayModePtr fixed_mode; 172fa225cbcSrjs struct bdb_sdvo_lvds_options *sdvo_lvds_options; 173fa225cbcSrjs unsigned char *timing_ptr; 174fa225cbcSrjs 175fa225cbcSrjs pI830->sdvo_lvds_fixed_mode = NULL; 176fa225cbcSrjs 177fa225cbcSrjs sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); 178fa225cbcSrjs if (sdvo_lvds_options == NULL) 179fa225cbcSrjs return; 180fa225cbcSrjs 181fa225cbcSrjs timing_ptr = find_section(bdb, BDB_SDVO_PANEL_DTDS); 182fa225cbcSrjs if (timing_ptr == NULL) 183fa225cbcSrjs return; 184fa225cbcSrjs 185fa225cbcSrjs fixed_mode = xnfalloc(sizeof(DisplayModeRec)); 186fa225cbcSrjs if (fixed_mode == NULL) 187fa225cbcSrjs return; 188fa225cbcSrjs 189fa225cbcSrjs memset(fixed_mode, 0, sizeof(*fixed_mode)); 190fa225cbcSrjs fill_detail_timing_data(fixed_mode, timing_ptr + 191fa225cbcSrjs (sdvo_lvds_options->panel_type * DET_TIMING_INFO_LEN)); 192fa225cbcSrjs pI830->sdvo_lvds_fixed_mode = fixed_mode; 193fa225cbcSrjs 194fa225cbcSrjs} 195fa225cbcSrjs 196fa225cbcSrjsstatic void 197fa225cbcSrjsparse_panel_data(I830Ptr pI830, struct bdb_header *bdb) 198fa225cbcSrjs{ 199fa225cbcSrjs parse_integrated_panel_data(pI830, bdb); 200fa225cbcSrjs parse_sdvo_panel_data(pI830, bdb); 201fa225cbcSrjs} 202fa225cbcSrjs 203fa225cbcSrjsstatic void 204fa225cbcSrjsparse_general_features(I830Ptr pI830, struct bdb_header *bdb) 205fa225cbcSrjs{ 206fa225cbcSrjs struct bdb_general_features *general; 207fa225cbcSrjs 208fa225cbcSrjs /* Set sensible defaults in case we can't find the general block */ 209fa225cbcSrjs pI830->tv_present = 1; 210fa225cbcSrjs 211fa225cbcSrjs general = find_section(bdb, BDB_GENERAL_FEATURES); 212fa225cbcSrjs if (!general) 213fa225cbcSrjs return; 214fa225cbcSrjs 215fa225cbcSrjs pI830->tv_present = general->int_tv_support; 216fa225cbcSrjs pI830->lvds_use_ssc = general->enable_ssc; 217fa225cbcSrjs if (pI830->lvds_use_ssc) { 218fa225cbcSrjs if (IS_I85X(pI830)) 219fa225cbcSrjs pI830->lvds_ssc_freq = general->ssc_freq ? 66 : 48; 220fa225cbcSrjs else 221fa225cbcSrjs pI830->lvds_ssc_freq = general->ssc_freq ? 100 : 96; 222fa225cbcSrjs } 223fa225cbcSrjs} 224fa225cbcSrjs 225fa225cbcSrjsstatic void 226fa225cbcSrjsparse_driver_feature(I830Ptr pI830, struct bdb_header *bdb) 227fa225cbcSrjs{ 228fa225cbcSrjs struct bdb_driver_feature *feature; 229fa225cbcSrjs 230fa225cbcSrjs /* For mobile chip, set default as true */ 231fa225cbcSrjs if (IS_MOBILE(pI830) && !IS_I830(pI830)) 232fa225cbcSrjs pI830->integrated_lvds = TRUE; 233fa225cbcSrjs 234fa225cbcSrjs /* skip pre-9xx chips which is broken to parse this block. */ 235fa225cbcSrjs if (!IS_I9XX(pI830)) 236fa225cbcSrjs return; 237fa225cbcSrjs 238fa225cbcSrjs /* XXX Disable this parsing, as it looks doesn't work for all 239fa225cbcSrjs VBIOS. Reenable it if we could find out the reliable VBT parsing 240fa225cbcSrjs for LVDS config later. */ 241fa225cbcSrjs if (1) 242fa225cbcSrjs return; 243fa225cbcSrjs 244fa225cbcSrjs feature = find_section(bdb, BDB_DRIVER_FEATURES); 245fa225cbcSrjs if (!feature) 246fa225cbcSrjs return; 247fa225cbcSrjs 248fa225cbcSrjs if (feature->lvds_config != BDB_DRIVER_INT_LVDS) 249fa225cbcSrjs pI830->integrated_lvds = FALSE; 250fa225cbcSrjs} 251fa225cbcSrjs 252fa225cbcSrjsstatic 253fa225cbcSrjsvoid parse_sdvo_mapping(ScrnInfoPtr pScrn, struct bdb_header *bdb) 254fa225cbcSrjs{ 255fa225cbcSrjs unsigned int block_size; 256fa225cbcSrjs uint16_t *block_ptr; 257fa225cbcSrjs struct bdb_general_definitions *defs; 258fa225cbcSrjs struct child_device_config *child; 259fa225cbcSrjs int i, child_device_num, count; 260fa225cbcSrjs struct sdvo_device_mapping *p_mapping; 261fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 262fa225cbcSrjs 263fa225cbcSrjs defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); 264fa225cbcSrjs if (!defs) { 265fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 266fa225cbcSrjs "can't find the general definition blocks\n"); 267fa225cbcSrjs return; 268fa225cbcSrjs } 269fa225cbcSrjs /* Get the block size of general defintion block */ 270fa225cbcSrjs block_ptr = (uint16_t *)((char *)defs - 2); 271fa225cbcSrjs block_size = *block_ptr; 272fa225cbcSrjs child_device_num = (block_size - sizeof(*defs)) / sizeof(*child); 273fa225cbcSrjs count = 0; 274fa225cbcSrjs 275fa225cbcSrjs for (i = 0; i < child_device_num; i++) { 276fa225cbcSrjs child = &defs->devices[i]; 277fa225cbcSrjs if (!child->device_type) { 278fa225cbcSrjs /* skip invalid child device type*/ 279fa225cbcSrjs continue; 280fa225cbcSrjs } 281fa225cbcSrjs if (child->slave_addr == SLAVE_ADDR1 || 282fa225cbcSrjs child->slave_addr == SLAVE_ADDR2) { 283fa225cbcSrjs if (child->dvo_port != DEVICE_PORT_DVOB && 284fa225cbcSrjs child->dvo_port != DEVICE_PORT_DVOC) { 285fa225cbcSrjs /* skip the incorrect sdvo port */ 286fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 287fa225cbcSrjs "Incorrect SDVO port\n"); 288fa225cbcSrjs continue; 289fa225cbcSrjs } 290fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_INFO, 291fa225cbcSrjs "the SDVO device with slave addr %x " 292fa225cbcSrjs "is found on DVO %x port\n", 293fa225cbcSrjs child->slave_addr, child->dvo_port); 294fa225cbcSrjs /* fill the primary dvo port */ 295fa225cbcSrjs p_mapping = &(pI830->sdvo_mappings[child->dvo_port - 1]); 296fa225cbcSrjs if (!p_mapping->initialized) { 297fa225cbcSrjs p_mapping->dvo_port = child->dvo_port; 298fa225cbcSrjs p_mapping->dvo_wiring = child->dvo_wiring; 299fa225cbcSrjs p_mapping->initialized = 1; 300fa225cbcSrjs p_mapping->slave_addr = child->slave_addr; 301fa225cbcSrjs } else { 302fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 303fa225cbcSrjs "One DVO port is shared by two slave " 304fa225cbcSrjs "address. Maybe it can't be handled\n"); 305fa225cbcSrjs } 306fa225cbcSrjs /* If there exists the slave2_addr, maybe it is a sdvo 307fa225cbcSrjs * device that contain multiple inputs. And it can't 308fa225cbcSrjs * handled by SDVO driver. 309fa225cbcSrjs * Ignore the dvo mapping of slave2_addr 310fa225cbcSrjs * of course its mapping info won't be added. 311fa225cbcSrjs */ 312fa225cbcSrjs if (child->slave2_addr) { 313fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 314fa225cbcSrjs "Two DVO ports uses the same slave address." 315fa225cbcSrjs "Maybe it can't be handled by SDVO driver\n"); 316fa225cbcSrjs } 317fa225cbcSrjs count++; 318fa225cbcSrjs } else { 319fa225cbcSrjs /* if the slave address is neither 0x70 nor 0x72, skip it. */ 320fa225cbcSrjs continue; 321fa225cbcSrjs } 322fa225cbcSrjs } 323fa225cbcSrjs /* If the count is zero, it indicates that no sdvo device is found */ 324fa225cbcSrjs if (!count) 325fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_INFO, 326fa225cbcSrjs "No SDVO device is found in VBT\n"); 327fa225cbcSrjs 328fa225cbcSrjs return; 329fa225cbcSrjs} 330fa225cbcSrjs#define INTEL_VBIOS_SIZE (64 * 1024) /* XXX */ 331fa225cbcSrjs 332fa225cbcSrjs/** 333fa225cbcSrjs * i830_bios_init - map VBIOS, find VBT 334fa225cbcSrjs * 335fa225cbcSrjs * VBT existence is a sanity check that is relied on by other i830_bios.c code. 336fa225cbcSrjs * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may 337fa225cbcSrjs * feed an updated VBT back through that, compared to what we'll fetch using 338fa225cbcSrjs * this method of groping around in the BIOS data. 339fa225cbcSrjs * 340fa225cbcSrjs * Returns 0 on success, nonzero on failure. 341fa225cbcSrjs */ 342fa225cbcSrjsint 343fa225cbcSrjsi830_bios_init(ScrnInfoPtr pScrn) 344fa225cbcSrjs{ 345fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 346fa225cbcSrjs struct vbt_header *vbt; 347fa225cbcSrjs struct bdb_header *bdb; 348fa225cbcSrjs int vbt_off, bdb_off; 349fa225cbcSrjs unsigned char *bios; 350fa225cbcSrjs int ret; 351fa225cbcSrjs int size; 352fa225cbcSrjs 353fa225cbcSrjs size = pI830->PciInfo->rom_size; 354fa225cbcSrjs if (size == 0) { 355fa225cbcSrjs size = INTEL_VBIOS_SIZE; 356fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 357fa225cbcSrjs "libpciaccess reported 0 rom size, guessing %dkB\n", 358fa225cbcSrjs size / 1024); 359fa225cbcSrjs } 360fa225cbcSrjs bios = xalloc(size); 361fa225cbcSrjs if (bios == NULL) 362fa225cbcSrjs return -1; 363fa225cbcSrjs 364fa225cbcSrjs ret = pci_device_read_rom (pI830->PciInfo, bios); 365fa225cbcSrjs if (ret != 0) { 366fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 367fa225cbcSrjs "libpciaccess failed to read %dkB video BIOS: %s\n", 368fa225cbcSrjs size / 1024, strerror(-ret)); 369fa225cbcSrjs xfree (bios); 370fa225cbcSrjs return -1; 371fa225cbcSrjs } 372fa225cbcSrjs 373fa225cbcSrjs vbt_off = INTEL_BIOS_16(0x1a); 374fa225cbcSrjs if (vbt_off >= size) { 375fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT offset: 0x%x\n", 376fa225cbcSrjs vbt_off); 377fa225cbcSrjs xfree(bios); 378fa225cbcSrjs return -1; 379fa225cbcSrjs } 380fa225cbcSrjs 381fa225cbcSrjs vbt = (struct vbt_header *)(bios + vbt_off); 382fa225cbcSrjs 383fa225cbcSrjs if (memcmp(vbt->signature, "$VBT", 4) != 0) { 384fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT signature\n"); 385fa225cbcSrjs xfree(bios); 386fa225cbcSrjs return -1; 387fa225cbcSrjs } 388fa225cbcSrjs 389fa225cbcSrjs /* Now that we've found the VBIOS, go scour the VBTs */ 390fa225cbcSrjs bdb_off = vbt_off + vbt->bdb_offset; 391fa225cbcSrjs bdb = (struct bdb_header *)(bios + bdb_off); 392fa225cbcSrjs 393fa225cbcSrjs parse_general_features(pI830, bdb); 394fa225cbcSrjs parse_panel_data(pI830, bdb); 395fa225cbcSrjs parse_driver_feature(pI830, bdb); 396fa225cbcSrjs parse_sdvo_mapping(pScrn, bdb); 397fa225cbcSrjs 398fa225cbcSrjs xfree(bios); 399fa225cbcSrjs 400fa225cbcSrjs return 0; 401fa225cbcSrjs} 402