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