| @@ -1,281 +1,281 @@ | | | @@ -1,281 +1,281 @@ |
1 | /* | | 1 | /* |
2 | * Copyright © 2006 Intel Corporation | | 2 | * Copyright © 2006 Intel Corporation |
3 | * | | 3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | | 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), | | 5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation | | 6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | | 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the | | 8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: | | 9 | * Software is furnished to do so, subject to the following conditions: |
10 | * | | 10 | * |
11 | * The above copyright notice and this permission notice (including the next | | 11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the | | 12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. | | 13 | * Software. |
14 | * | | 14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | | 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | | 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | | 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | | 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | | 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | * SOFTWARE. | | 21 | * SOFTWARE. |
22 | * | | 22 | * |
23 | * Authors: | | 23 | * Authors: |
24 | * Eric Anholt <eric@anholt.net> | | 24 | * Eric Anholt <eric@anholt.net> |
25 | * | | 25 | * |
26 | */ | | 26 | */ |
27 | #ifdef HAVE_CONFIG_H | | 27 | #ifdef HAVE_CONFIG_H |
28 | #include "config.h" | | 28 | #include "config.h" |
29 | #undef VERSION /* XXX edid.h has a VERSION too */ | | 29 | #undef VERSION /* XXX edid.h has a VERSION too */ |
30 | #endif | | 30 | #endif |
31 | | | 31 | |
32 | #include <stdio.h> | | 32 | #include <stdio.h> |
33 | #include <string.h> | | 33 | #include <string.h> |
34 | #include <stdlib.h> | | 34 | #include <stdlib.h> |
35 | | | 35 | |
36 | #define _PARSE_EDID_ | | 36 | #define _PARSE_EDID_ |
37 | #include "xf86.h" | | 37 | #include "xf86.h" |
38 | #include "i830.h" | | 38 | #include "i830.h" |
39 | #include "i830_bios.h" | | 39 | #include "i830_bios.h" |
40 | #include "edid.h" | | 40 | #include "edid.h" |
41 | | | 41 | |
42 | #define INTEL_BIOS_8(_addr) (bios[_addr]) | | 42 | #define INTEL_BIOS_8(_addr) (bios[_addr]) |
43 | #define INTEL_BIOS_16(_addr) (bios[_addr] | \ | | 43 | #define INTEL_BIOS_16(_addr) (bios[_addr] | \ |
44 | (bios[_addr + 1] << 8)) | | 44 | (bios[_addr + 1] << 8)) |
45 | #define INTEL_BIOS_32(_addr) (bios[_addr] | \ | | 45 | #define INTEL_BIOS_32(_addr) (bios[_addr] | \ |
46 | (bios[_addr + 1] << 8) \ | | 46 | (bios[_addr + 1] << 8) \ |
47 | (bios[_addr + 2] << 16) \ | | 47 | (bios[_addr + 2] << 16) \ |
48 | (bios[_addr + 3] << 24)) | | 48 | (bios[_addr + 3] << 24)) |
49 | | | 49 | |
50 | static void * | | 50 | static void * |
51 | find_section(struct bdb_header *bdb, int section_id) | | 51 | find_section(struct bdb_header *bdb, int section_id) |
52 | { | | 52 | { |
53 | unsigned char *base = (unsigned char *)bdb; | | 53 | unsigned char *base = (unsigned char *)bdb; |
54 | int index = 0; | | 54 | int index = 0; |
55 | uint16_t total, current_size; | | 55 | uint16_t total, current_size; |
56 | unsigned char current_id; | | 56 | unsigned char current_id; |
57 | | | 57 | |
58 | /* skip to first section */ | | 58 | /* skip to first section */ |
59 | index += bdb->header_size; | | 59 | index += bdb->header_size; |
60 | total = bdb->bdb_size; | | 60 | total = bdb->bdb_size; |
61 | | | 61 | |
62 | /* walk the sections looking for section_id */ | | 62 | /* walk the sections looking for section_id */ |
63 | while (index < total) { | | 63 | while (index < total) { |
64 | current_id = *(base + index); | | 64 | current_id = *(base + index); |
65 | index++; | | 65 | index++; |
66 | current_size = *((uint16_t *)(base + index)); | | 66 | current_size = *((uint16_t *)(base + index)); |
67 | index += 2; | | 67 | index += 2; |
68 | if (current_id == section_id) | | 68 | if (current_id == section_id) |
69 | return base + index; | | 69 | return base + index; |
70 | index += current_size; | | 70 | index += current_size; |
71 | } | | 71 | } |
72 | | | 72 | |
73 | return NULL; | | 73 | return NULL; |
74 | } | | 74 | } |
75 | | | 75 | |
76 | /** | | 76 | /** |
77 | * Returns the BIOS's fixed panel mode. | | 77 | * Returns the BIOS's fixed panel mode. |
78 | * | | 78 | * |
79 | * Note that many BIOSes will have the appropriate tables for a panel even when | | 79 | * Note that many BIOSes will have the appropriate tables for a panel even when |
80 | * a panel is not attached. Additionally, many BIOSes adjust table sizes or | | 80 | * a panel is not attached. Additionally, many BIOSes adjust table sizes or |
81 | * offsets, such that this parsing fails. Thus, almost any other method for | | 81 | * offsets, such that this parsing fails. Thus, almost any other method for |
82 | * detecting the panel mode is preferable. | | 82 | * detecting the panel mode is preferable. |
83 | */ | | 83 | */ |
84 | static void | | 84 | static void |
85 | parse_panel_data(I830Ptr pI830, struct bdb_header *bdb) | | 85 | parse_panel_data(I830Ptr pI830, struct bdb_header *bdb) |
86 | { | | 86 | { |
87 | struct bdb_lvds_options *lvds_options; | | 87 | struct bdb_lvds_options *lvds_options; |
88 | struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; | | 88 | struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; |
89 | int timing_offset; | | 89 | int timing_offset; |
90 | DisplayModePtr fixed_mode; | | 90 | DisplayModePtr fixed_mode; |
91 | unsigned char *timing_ptr; | | 91 | unsigned char *timing_ptr; |
92 | | | 92 | |
93 | /* Defaults if we can't find VBT info */ | | 93 | /* Defaults if we can't find VBT info */ |
94 | pI830->lvds_dither = 0; | | 94 | pI830->lvds_dither = 0; |
95 | | | 95 | |
96 | lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); | | 96 | lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); |
97 | if (!lvds_options) | | 97 | if (!lvds_options) |
98 | return; | | 98 | return; |
99 | | | 99 | |
100 | pI830->lvds_dither = lvds_options->pixel_dither; | | 100 | pI830->lvds_dither = lvds_options->pixel_dither; |
101 | if (lvds_options->panel_type == 0xff) | | 101 | if (lvds_options->panel_type == 0xff) |
102 | return; | | 102 | return; |
103 | | | 103 | |
104 | lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); | | 104 | lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); |
105 | if (!lvds_lfp_data_ptrs) | | 105 | if (!lvds_lfp_data_ptrs) |
106 | return; | | 106 | return; |
107 | | | 107 | |
108 | timing_offset = | | 108 | timing_offset = |
109 | lvds_lfp_data_ptrs->ptr[lvds_options->panel_type].dvo_timing_offset; | | 109 | lvds_lfp_data_ptrs->ptr[lvds_options->panel_type].dvo_timing_offset; |
110 | timing_ptr = (unsigned char *)bdb + timing_offset; | | 110 | timing_ptr = (unsigned char *)bdb + timing_offset; |
111 | | | 111 | |
112 | if (pI830->skip_panel_detect) | | 112 | if (pI830->skip_panel_detect) |
113 | return; | | 113 | return; |
114 | | | 114 | |
115 | fixed_mode = xnfalloc(sizeof(DisplayModeRec)); | | 115 | fixed_mode = xnfalloc(sizeof(DisplayModeRec)); |
116 | memset(fixed_mode, 0, sizeof(*fixed_mode)); | | 116 | memset(fixed_mode, 0, sizeof(*fixed_mode)); |
117 | | | 117 | |
118 | /* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing | | 118 | /* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing |
119 | * block, pull the contents out using EDID macros. | | 119 | * block, pull the contents out using EDID macros. |
120 | */ | | 120 | */ |
121 | fixed_mode->HDisplay = _H_ACTIVE(timing_ptr); | | 121 | fixed_mode->HDisplay = _H_ACTIVE(timing_ptr); |
122 | fixed_mode->VDisplay = _V_ACTIVE(timing_ptr); | | 122 | fixed_mode->VDisplay = _V_ACTIVE(timing_ptr); |
123 | fixed_mode->HSyncStart = fixed_mode->HDisplay + | | 123 | fixed_mode->HSyncStart = fixed_mode->HDisplay + |
124 | _H_SYNC_OFF(timing_ptr); | | 124 | _H_SYNC_OFF(timing_ptr); |
125 | fixed_mode->HSyncEnd = fixed_mode->HSyncStart + | | 125 | fixed_mode->HSyncEnd = fixed_mode->HSyncStart + |
126 | _H_SYNC_WIDTH(timing_ptr); | | 126 | _H_SYNC_WIDTH(timing_ptr); |
127 | fixed_mode->HTotal = fixed_mode->HDisplay + | | 127 | fixed_mode->HTotal = fixed_mode->HDisplay + |
128 | _H_BLANK(timing_ptr); | | 128 | _H_BLANK(timing_ptr); |
129 | fixed_mode->VSyncStart = fixed_mode->VDisplay + | | 129 | fixed_mode->VSyncStart = fixed_mode->VDisplay + |
130 | _V_SYNC_OFF(timing_ptr); | | 130 | _V_SYNC_OFF(timing_ptr); |
131 | fixed_mode->VSyncEnd = fixed_mode->VSyncStart + | | 131 | fixed_mode->VSyncEnd = fixed_mode->VSyncStart + |
132 | _V_SYNC_WIDTH(timing_ptr); | | 132 | _V_SYNC_WIDTH(timing_ptr); |
133 | fixed_mode->VTotal = fixed_mode->VDisplay + | | 133 | fixed_mode->VTotal = fixed_mode->VDisplay + |
134 | _V_BLANK(timing_ptr); | | 134 | _V_BLANK(timing_ptr); |
135 | fixed_mode->Clock = _PIXEL_CLOCK(timing_ptr) / 1000; | | 135 | fixed_mode->Clock = _PIXEL_CLOCK(timing_ptr) / 1000; |
136 | fixed_mode->type = M_T_PREFERRED; | | 136 | fixed_mode->type = M_T_PREFERRED; |
137 | | | 137 | |
138 | /* Some VBTs have bogus h/vtotal values */ | | 138 | /* Some VBTs have bogus h/vtotal values */ |
139 | if (fixed_mode->HSyncEnd > fixed_mode->HTotal) | | 139 | if (fixed_mode->HSyncEnd > fixed_mode->HTotal) |
140 | fixed_mode->HTotal = fixed_mode->HSyncEnd + 1; | | 140 | fixed_mode->HTotal = fixed_mode->HSyncEnd + 1; |
141 | if (fixed_mode->VSyncEnd > fixed_mode->VTotal) | | 141 | if (fixed_mode->VSyncEnd > fixed_mode->VTotal) |
142 | fixed_mode->VTotal = fixed_mode->VSyncEnd + 1; | | 142 | fixed_mode->VTotal = fixed_mode->VSyncEnd + 1; |
143 | | | 143 | |
144 | xf86SetModeDefaultName(fixed_mode); | | 144 | xf86SetModeDefaultName(fixed_mode); |
145 | | | 145 | |
146 | pI830->lvds_fixed_mode = fixed_mode; | | 146 | pI830->lvds_fixed_mode = fixed_mode; |
147 | } | | 147 | } |
148 | | | 148 | |
149 | static void | | 149 | static void |
150 | parse_general_features(I830Ptr pI830, struct bdb_header *bdb) | | 150 | parse_general_features(I830Ptr pI830, struct bdb_header *bdb) |
151 | { | | 151 | { |
152 | struct bdb_general_features *general; | | 152 | struct bdb_general_features *general; |
153 | | | 153 | |
154 | /* Set sensible defaults in case we can't find the general block */ | | 154 | /* Set sensible defaults in case we can't find the general block */ |
155 | pI830->tv_present = 1; | | 155 | pI830->tv_present = 1; |
156 | | | 156 | |
157 | general = find_section(bdb, BDB_GENERAL_FEATURES); | | 157 | general = find_section(bdb, BDB_GENERAL_FEATURES); |
158 | if (!general) | | 158 | if (!general) |
159 | return; | | 159 | return; |
160 | | | 160 | |
161 | pI830->tv_present = general->int_tv_support; | | 161 | pI830->tv_present = general->int_tv_support; |
162 | pI830->lvds_use_ssc = general->enable_ssc; | | 162 | pI830->lvds_use_ssc = general->enable_ssc; |
163 | if (pI830->lvds_use_ssc) { | | 163 | if (pI830->lvds_use_ssc) { |
164 | if (IS_I85X(pI830)) | | 164 | if (IS_I85X(pI830)) |
165 | pI830->lvds_ssc_freq = general->ssc_freq ? 66 : 48; | | 165 | pI830->lvds_ssc_freq = general->ssc_freq ? 66 : 48; |
166 | else | | 166 | else |
167 | pI830->lvds_ssc_freq = general->ssc_freq ? 100 : 96; | | 167 | pI830->lvds_ssc_freq = general->ssc_freq ? 100 : 96; |
168 | } | | 168 | } |
169 | } | | 169 | } |
170 | | | 170 | |
171 | static void | | 171 | static void |
172 | parse_driver_feature(I830Ptr pI830, struct bdb_header *bdb) | | 172 | parse_driver_feature(I830Ptr pI830, struct bdb_header *bdb) |
173 | { | | 173 | { |
174 | struct bdb_driver_feature *feature; | | 174 | struct bdb_driver_feature *feature; |
175 | | | 175 | |
176 | /* For mobile chip, set default as true */ | | 176 | /* For mobile chip, set default as true */ |
177 | if (IS_MOBILE(pI830) && !IS_I830(pI830)) | | 177 | if (IS_MOBILE(pI830) && !IS_I830(pI830)) |
178 | pI830->integrated_lvds = TRUE; | | 178 | pI830->integrated_lvds = TRUE; |
179 | | | 179 | |
180 | /* skip pre-9xx chips which is broken to parse this block. */ | | 180 | /* skip pre-9xx chips which is broken to parse this block. */ |
181 | if (!IS_I9XX(pI830)) | | 181 | if (!IS_I9XX(pI830)) |
182 | return; | | 182 | return; |
183 | | | 183 | |
184 | /* XXX Disable this parsing, as it looks doesn't work for all | | 184 | /* XXX Disable this parsing, as it looks doesn't work for all |
185 | VBIOS. Reenable it if we could find out the reliable VBT parsing | | 185 | VBIOS. Reenable it if we could find out the reliable VBT parsing |
186 | for LVDS config later. */ | | 186 | for LVDS config later. */ |
187 | if (1) | | 187 | if (1) |
188 | return; | | 188 | return; |
189 | | | 189 | |
190 | feature = find_section(bdb, BDB_DRIVER_FEATURES); | | 190 | feature = find_section(bdb, BDB_DRIVER_FEATURES); |
191 | if (!feature) | | 191 | if (!feature) |
192 | return; | | 192 | return; |
193 | | | 193 | |
194 | if (feature->lvds_config != BDB_DRIVER_INT_LVDS) | | 194 | if (feature->lvds_config != BDB_DRIVER_INT_LVDS) |
195 | pI830->integrated_lvds = FALSE; | | 195 | pI830->integrated_lvds = FALSE; |
196 | } | | 196 | } |
197 | | | 197 | |
198 | #define INTEL_VBIOS_SIZE (64 * 1024) /* XXX */ | | 198 | #define INTEL_VBIOS_SIZE (64 * 1024) /* XXX */ |
199 | | | 199 | |
200 | /** | | 200 | /** |
201 | * i830_bios_init - map VBIOS, find VBT | | 201 | * i830_bios_init - map VBIOS, find VBT |
202 | * | | 202 | * |
203 | * VBT existence is a sanity check that is relied on by other i830_bios.c code. | | 203 | * VBT existence is a sanity check that is relied on by other i830_bios.c code. |
204 | * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may | | 204 | * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may |
205 | * feed an updated VBT back through that, compared to what we'll fetch using | | 205 | * feed an updated VBT back through that, compared to what we'll fetch using |
206 | * this method of groping around in the BIOS data. | | 206 | * this method of groping around in the BIOS data. |
207 | * | | 207 | * |
208 | * Returns 0 on success, nonzero on failure. | | 208 | * Returns 0 on success, nonzero on failure. |
209 | */ | | 209 | */ |
210 | int | | 210 | int |
211 | i830_bios_init(ScrnInfoPtr pScrn) | | 211 | i830_bios_init(ScrnInfoPtr pScrn) |
212 | { | | 212 | { |
213 | I830Ptr pI830 = I830PTR(pScrn); | | 213 | I830Ptr pI830 = I830PTR(pScrn); |
214 | struct vbt_header *vbt; | | 214 | struct vbt_header *vbt; |
215 | struct bdb_header *bdb; | | 215 | struct bdb_header *bdb; |
216 | int vbt_off, bdb_off; | | 216 | int vbt_off, bdb_off; |
217 | unsigned char *bios; | | 217 | unsigned char *bios; |
218 | int ret; | | 218 | int ret; |
219 | int size; | | 219 | int size; |
220 | | | 220 | |
221 | #if XSERVER_LIBPCIACCESS | | 221 | #if XSERVER_LIBPCIACCESS |
222 | size = pI830->PciInfo->rom_size; | | 222 | size = pI830->PciInfo->rom_size; |
223 | if (size == 0) { | | 223 | if (size == 0) { |
224 | size = INTEL_VBIOS_SIZE; | | 224 | size = INTEL_VBIOS_SIZE; |
225 | xf86DrvMsg(pScrn->scrnIndex, X_WARNING, | | 225 | xf86DrvMsg(pScrn->scrnIndex, X_WARNING, |
226 | "libpciaccess reported 0 rom size, guessing %dkB\n", | | 226 | "libpciaccess reported 0 rom size, guessing %dkB\n", |
227 | size / 1024); | | 227 | size / 1024); |
228 | } | | 228 | } |
229 | #else | | 229 | #else |
230 | size = INTEL_VBIOS_SIZE; | | 230 | size = INTEL_VBIOS_SIZE; |
231 | #endif | | 231 | #endif |
232 | bios = xalloc(size); | | 232 | bios = xalloc(size); |
233 | if (bios == NULL) | | 233 | if (bios == NULL) |
234 | return -1; | | 234 | return -1; |
235 | | | 235 | |
236 | #if XSERVER_LIBPCIACCESS | | 236 | #if XSERVER_LIBPCIACCESS |
237 | ret = pci_device_read_rom (pI830->PciInfo, bios); | | 237 | ret = pci_device_read_rom (pI830->PciInfo, bios); |
238 | if (ret != 0) { | | 238 | if (ret != 0) { |
239 | xf86DrvMsg(pScrn->scrnIndex, X_WARNING, | | 239 | xf86DrvMsg(pScrn->scrnIndex, X_WARNING, |
240 | "libpciaccess failed to read %dkB video BIOS: %s\n", | | 240 | "libpciaccess failed to read %dkB video BIOS: %s\n", |
241 | size / 1024, strerror(-ret)); | | 241 | size / 1024, strerror(ret)); |
242 | xfree (bios); | | 242 | xfree (bios); |
243 | return -1; | | 243 | return -1; |
244 | } | | 244 | } |
245 | #else | | 245 | #else |
246 | /* xf86ReadPciBIOS returns the length read */ | | 246 | /* xf86ReadPciBIOS returns the length read */ |
247 | ret = xf86ReadPciBIOS(0, pI830->PciTag, 0, bios, size); | | 247 | ret = xf86ReadPciBIOS(0, pI830->PciTag, 0, bios, size); |
248 | if (ret <= 0) { | | 248 | if (ret <= 0) { |
249 | xfree (bios); | | 249 | xfree (bios); |
250 | return -1; | | 250 | return -1; |
251 | } | | 251 | } |
252 | #endif | | 252 | #endif |
253 | | | 253 | |
254 | vbt_off = INTEL_BIOS_16(0x1a); | | 254 | vbt_off = INTEL_BIOS_16(0x1a); |
255 | if (vbt_off >= size) { | | 255 | if (vbt_off >= size) { |
256 | xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT offset: 0x%x\n", | | 256 | xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT offset: 0x%x\n", |
257 | vbt_off); | | 257 | vbt_off); |
258 | xfree(bios); | | 258 | xfree(bios); |
259 | return -1; | | 259 | return -1; |
260 | } | | 260 | } |
261 | | | 261 | |
262 | vbt = (struct vbt_header *)(bios + vbt_off); | | 262 | vbt = (struct vbt_header *)(bios + vbt_off); |
263 | | | 263 | |
264 | if (memcmp(vbt->signature, "$VBT", 4) != 0) { | | 264 | if (memcmp(vbt->signature, "$VBT", 4) != 0) { |
265 | xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT signature\n"); | | 265 | xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad VBT signature\n"); |
266 | xfree(bios); | | 266 | xfree(bios); |
267 | return -1; | | 267 | return -1; |
268 | } | | 268 | } |
269 | | | 269 | |
270 | /* Now that we've found the VBIOS, go scour the VBTs */ | | 270 | /* Now that we've found the VBIOS, go scour the VBTs */ |
271 | bdb_off = vbt_off + vbt->bdb_offset; | | 271 | bdb_off = vbt_off + vbt->bdb_offset; |
272 | bdb = (struct bdb_header *)(bios + bdb_off); | | 272 | bdb = (struct bdb_header *)(bios + bdb_off); |
273 | | | 273 | |
274 | parse_general_features(pI830, bdb); | | 274 | parse_general_features(pI830, bdb); |
275 | parse_panel_data(pI830, bdb); | | 275 | parse_panel_data(pI830, bdb); |
276 | parse_driver_feature(pI830, bdb); | | 276 | parse_driver_feature(pI830, bdb); |
277 | | | 277 | |
278 | xfree(bios); | | 278 | xfree(bios); |
279 | | | 279 | |
280 | return 0; | | 280 | return 0; |
281 | } | | 281 | } |