1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2006 Intel Corporation |
4 | * |
5 | * Authors: |
6 | * Eric Anholt <eric@anholt.net> |
7 | */ |
8 | |
9 | #include <drm/display/drm_dp_helper.h> |
10 | #include <drm/drm.h> |
11 | |
12 | #include "intel_bios.h" |
13 | #include "psb_drv.h" |
14 | #include "psb_intel_drv.h" |
15 | #include "psb_intel_reg.h" |
16 | |
17 | #define SLAVE_ADDR1 0x70 |
18 | #define SLAVE_ADDR2 0x72 |
19 | |
20 | static void *find_section(struct bdb_header *bdb, int section_id) |
21 | { |
22 | u8 *base = (u8 *)bdb; |
23 | int index = 0; |
24 | u16 total, current_size; |
25 | u8 current_id; |
26 | |
27 | /* skip to first section */ |
28 | index += bdb->header_size; |
29 | total = bdb->bdb_size; |
30 | |
31 | /* walk the sections looking for section_id */ |
32 | while (index < total) { |
33 | current_id = *(base + index); |
34 | index++; |
35 | current_size = *((u16 *)(base + index)); |
36 | index += 2; |
37 | if (current_id == section_id) |
38 | return base + index; |
39 | index += current_size; |
40 | } |
41 | |
42 | return NULL; |
43 | } |
44 | |
45 | static void |
46 | parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb) |
47 | { |
48 | struct bdb_edp *edp; |
49 | struct edp_power_seq *edp_pps; |
50 | struct edp_link_params *edp_link_params; |
51 | uint8_t panel_type; |
52 | |
53 | edp = find_section(bdb, BDB_EDP); |
54 | |
55 | dev_priv->edp.bpp = 18; |
56 | if (!edp) { |
57 | if (dev_priv->edp.support) { |
58 | DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n" , |
59 | dev_priv->edp.bpp); |
60 | } |
61 | return; |
62 | } |
63 | |
64 | panel_type = dev_priv->panel_type; |
65 | switch ((edp->color_depth >> (panel_type * 2)) & 3) { |
66 | case EDP_18BPP: |
67 | dev_priv->edp.bpp = 18; |
68 | break; |
69 | case EDP_24BPP: |
70 | dev_priv->edp.bpp = 24; |
71 | break; |
72 | case EDP_30BPP: |
73 | dev_priv->edp.bpp = 30; |
74 | break; |
75 | } |
76 | |
77 | /* Get the eDP sequencing and link info */ |
78 | edp_pps = &edp->power_seqs[panel_type]; |
79 | edp_link_params = &edp->link_params[panel_type]; |
80 | |
81 | dev_priv->edp.pps = *edp_pps; |
82 | |
83 | DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n" , |
84 | dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8, |
85 | dev_priv->edp.pps.t9, dev_priv->edp.pps.t10, |
86 | dev_priv->edp.pps.t11_t12); |
87 | |
88 | dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : |
89 | DP_LINK_BW_1_62; |
90 | switch (edp_link_params->lanes) { |
91 | case 0: |
92 | dev_priv->edp.lanes = 1; |
93 | break; |
94 | case 1: |
95 | dev_priv->edp.lanes = 2; |
96 | break; |
97 | case 3: |
98 | default: |
99 | dev_priv->edp.lanes = 4; |
100 | break; |
101 | } |
102 | DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n" , |
103 | dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp); |
104 | |
105 | switch (edp_link_params->preemphasis) { |
106 | case 0: |
107 | dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; |
108 | break; |
109 | case 1: |
110 | dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; |
111 | break; |
112 | case 2: |
113 | dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; |
114 | break; |
115 | case 3: |
116 | dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; |
117 | break; |
118 | } |
119 | switch (edp_link_params->vswing) { |
120 | case 0: |
121 | dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; |
122 | break; |
123 | case 1: |
124 | dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; |
125 | break; |
126 | case 2: |
127 | dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; |
128 | break; |
129 | case 3: |
130 | dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; |
131 | break; |
132 | } |
133 | DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n" , |
134 | dev_priv->edp.vswing, dev_priv->edp.preemphasis); |
135 | } |
136 | |
137 | static u16 |
138 | get_blocksize(void *p) |
139 | { |
140 | u16 *block_ptr, block_size; |
141 | |
142 | block_ptr = (u16 *)((char *)p - 2); |
143 | block_size = *block_ptr; |
144 | return block_size; |
145 | } |
146 | |
147 | static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, |
148 | struct lvds_dvo_timing *dvo_timing) |
149 | { |
150 | panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | |
151 | dvo_timing->hactive_lo; |
152 | panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + |
153 | ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); |
154 | panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + |
155 | dvo_timing->hsync_pulse_width; |
156 | panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + |
157 | ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); |
158 | |
159 | panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | |
160 | dvo_timing->vactive_lo; |
161 | panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + |
162 | dvo_timing->vsync_off; |
163 | panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + |
164 | dvo_timing->vsync_pulse_width; |
165 | panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + |
166 | ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); |
167 | panel_fixed_mode->clock = dvo_timing->clock * 10; |
168 | panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; |
169 | |
170 | if (dvo_timing->hsync_positive) |
171 | panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC; |
172 | else |
173 | panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC; |
174 | |
175 | if (dvo_timing->vsync_positive) |
176 | panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC; |
177 | else |
178 | panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; |
179 | |
180 | /* Some VBTs have bogus h/vtotal values */ |
181 | if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) |
182 | panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; |
183 | if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) |
184 | panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; |
185 | |
186 | drm_mode_set_name(mode: panel_fixed_mode); |
187 | } |
188 | |
189 | static void parse_backlight_data(struct drm_psb_private *dev_priv, |
190 | struct bdb_header *bdb) |
191 | { |
192 | struct bdb_lvds_backlight *vbt_lvds_bl = NULL; |
193 | struct bdb_lvds_backlight *lvds_bl; |
194 | u8 p_type = 0; |
195 | void *bl_start = NULL; |
196 | struct bdb_lvds_options *lvds_opts |
197 | = find_section(bdb, BDB_LVDS_OPTIONS); |
198 | |
199 | dev_priv->lvds_bl = NULL; |
200 | |
201 | if (lvds_opts) |
202 | p_type = lvds_opts->panel_type; |
203 | else |
204 | return; |
205 | |
206 | bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT); |
207 | vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type; |
208 | |
209 | lvds_bl = kmemdup(p: vbt_lvds_bl, size: sizeof(*vbt_lvds_bl), GFP_KERNEL); |
210 | if (!lvds_bl) { |
211 | dev_err(dev_priv->dev.dev, "out of memory for backlight data\n" ); |
212 | return; |
213 | } |
214 | dev_priv->lvds_bl = lvds_bl; |
215 | } |
216 | |
217 | /* Try to find integrated panel data */ |
218 | static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, |
219 | struct bdb_header *bdb) |
220 | { |
221 | struct bdb_lvds_options *lvds_options; |
222 | struct bdb_lvds_lfp_data *lvds_lfp_data; |
223 | struct bdb_lvds_lfp_data_entry *entry; |
224 | struct lvds_dvo_timing *dvo_timing; |
225 | struct drm_display_mode *panel_fixed_mode; |
226 | |
227 | /* Defaults if we can't find VBT info */ |
228 | dev_priv->lvds_dither = 0; |
229 | dev_priv->lvds_vbt = 0; |
230 | |
231 | lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); |
232 | if (!lvds_options) |
233 | return; |
234 | |
235 | dev_priv->lvds_dither = lvds_options->pixel_dither; |
236 | dev_priv->panel_type = lvds_options->panel_type; |
237 | |
238 | if (lvds_options->panel_type == 0xff) |
239 | return; |
240 | |
241 | lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); |
242 | if (!lvds_lfp_data) |
243 | return; |
244 | |
245 | |
246 | entry = &lvds_lfp_data->data[lvds_options->panel_type]; |
247 | dvo_timing = &entry->dvo_timing; |
248 | |
249 | panel_fixed_mode = kzalloc(size: sizeof(*panel_fixed_mode), |
250 | GFP_KERNEL); |
251 | if (panel_fixed_mode == NULL) { |
252 | dev_err(dev_priv->dev.dev, "out of memory for fixed panel mode\n" ); |
253 | return; |
254 | } |
255 | |
256 | dev_priv->lvds_vbt = 1; |
257 | fill_detail_timing_data(panel_fixed_mode, dvo_timing); |
258 | |
259 | if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) { |
260 | dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; |
261 | drm_mode_debug_printmodeline(mode: panel_fixed_mode); |
262 | } else { |
263 | dev_dbg(dev_priv->dev.dev, "ignoring invalid LVDS VBT\n" ); |
264 | dev_priv->lvds_vbt = 0; |
265 | kfree(objp: panel_fixed_mode); |
266 | } |
267 | return; |
268 | } |
269 | |
270 | /* Try to find sdvo panel data */ |
271 | static void parse_sdvo_panel_data(struct drm_psb_private *dev_priv, |
272 | struct bdb_header *bdb) |
273 | { |
274 | struct bdb_sdvo_lvds_options *sdvo_lvds_options; |
275 | struct lvds_dvo_timing *dvo_timing; |
276 | struct drm_display_mode *panel_fixed_mode; |
277 | |
278 | dev_priv->sdvo_lvds_vbt_mode = NULL; |
279 | |
280 | sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); |
281 | if (!sdvo_lvds_options) |
282 | return; |
283 | |
284 | dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); |
285 | if (!dvo_timing) |
286 | return; |
287 | |
288 | panel_fixed_mode = kzalloc(size: sizeof(*panel_fixed_mode), GFP_KERNEL); |
289 | |
290 | if (!panel_fixed_mode) |
291 | return; |
292 | |
293 | fill_detail_timing_data(panel_fixed_mode, |
294 | dvo_timing: dvo_timing + sdvo_lvds_options->panel_type); |
295 | |
296 | dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; |
297 | |
298 | return; |
299 | } |
300 | |
301 | static void parse_general_features(struct drm_psb_private *dev_priv, |
302 | struct bdb_header *bdb) |
303 | { |
304 | struct bdb_general_features *general; |
305 | |
306 | /* Set sensible defaults in case we can't find the general block */ |
307 | dev_priv->int_tv_support = 1; |
308 | dev_priv->int_crt_support = 1; |
309 | |
310 | general = find_section(bdb, BDB_GENERAL_FEATURES); |
311 | if (general) { |
312 | dev_priv->int_tv_support = general->int_tv_support; |
313 | dev_priv->int_crt_support = general->int_crt_support; |
314 | dev_priv->lvds_use_ssc = general->enable_ssc; |
315 | |
316 | if (dev_priv->lvds_use_ssc) { |
317 | dev_priv->lvds_ssc_freq |
318 | = general->ssc_freq ? 100 : 96; |
319 | } |
320 | } |
321 | } |
322 | |
323 | static void |
324 | parse_sdvo_device_mapping(struct drm_psb_private *dev_priv, |
325 | struct bdb_header *bdb) |
326 | { |
327 | struct sdvo_device_mapping *p_mapping; |
328 | struct bdb_general_definitions *p_defs; |
329 | struct child_device_config *p_child; |
330 | int i, child_device_num, count; |
331 | u16 block_size; |
332 | |
333 | p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); |
334 | if (!p_defs) { |
335 | DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n" ); |
336 | return; |
337 | } |
338 | /* judge whether the size of child device meets the requirements. |
339 | * If the child device size obtained from general definition block |
340 | * is different with sizeof(struct child_device_config), skip the |
341 | * parsing of sdvo device info |
342 | */ |
343 | if (p_defs->child_dev_size != sizeof(*p_child)) { |
344 | /* different child dev size . Ignore it */ |
345 | DRM_DEBUG_KMS("different child size is found. Invalid.\n" ); |
346 | return; |
347 | } |
348 | /* get the block size of general definitions */ |
349 | block_size = get_blocksize(p: p_defs); |
350 | /* get the number of child device */ |
351 | child_device_num = (block_size - sizeof(*p_defs)) / |
352 | sizeof(*p_child); |
353 | count = 0; |
354 | for (i = 0; i < child_device_num; i++) { |
355 | p_child = &(p_defs->devices[i]); |
356 | if (!p_child->device_type) { |
357 | /* skip the device block if device type is invalid */ |
358 | continue; |
359 | } |
360 | if (p_child->slave_addr != SLAVE_ADDR1 && |
361 | p_child->slave_addr != SLAVE_ADDR2) { |
362 | /* |
363 | * If the slave address is neither 0x70 nor 0x72, |
364 | * it is not a SDVO device. Skip it. |
365 | */ |
366 | continue; |
367 | } |
368 | if (p_child->dvo_port != DEVICE_PORT_DVOB && |
369 | p_child->dvo_port != DEVICE_PORT_DVOC) { |
370 | /* skip the incorrect SDVO port */ |
371 | DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n" ); |
372 | continue; |
373 | } |
374 | DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on" |
375 | " %s port\n" , |
376 | p_child->slave_addr, |
377 | (p_child->dvo_port == DEVICE_PORT_DVOB) ? |
378 | "SDVOB" : "SDVOC" ); |
379 | p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]); |
380 | if (!p_mapping->initialized) { |
381 | p_mapping->dvo_port = p_child->dvo_port; |
382 | p_mapping->slave_addr = p_child->slave_addr; |
383 | p_mapping->dvo_wiring = p_child->dvo_wiring; |
384 | p_mapping->ddc_pin = p_child->ddc_pin; |
385 | p_mapping->i2c_pin = p_child->i2c_pin; |
386 | p_mapping->initialized = 1; |
387 | DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n" , |
388 | p_mapping->dvo_port, |
389 | p_mapping->slave_addr, |
390 | p_mapping->dvo_wiring, |
391 | p_mapping->ddc_pin, |
392 | p_mapping->i2c_pin); |
393 | } else { |
394 | DRM_DEBUG_KMS("Maybe one SDVO port is shared by " |
395 | "two SDVO device.\n" ); |
396 | } |
397 | if (p_child->slave2_addr) { |
398 | /* Maybe this is a SDVO device with multiple inputs */ |
399 | /* And the mapping info is not added */ |
400 | DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this" |
401 | " is a SDVO device with multiple inputs.\n" ); |
402 | } |
403 | count++; |
404 | } |
405 | |
406 | if (!count) { |
407 | /* No SDVO device info is found */ |
408 | DRM_DEBUG_KMS("No SDVO device info is found in VBT\n" ); |
409 | } |
410 | return; |
411 | } |
412 | |
413 | |
414 | static void |
415 | parse_driver_features(struct drm_psb_private *dev_priv, |
416 | struct bdb_header *bdb) |
417 | { |
418 | struct bdb_driver_features *driver; |
419 | |
420 | driver = find_section(bdb, BDB_DRIVER_FEATURES); |
421 | if (!driver) |
422 | return; |
423 | |
424 | if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) |
425 | dev_priv->edp.support = 1; |
426 | |
427 | dev_priv->lvds_enabled_in_vbt = driver->lvds_config != 0; |
428 | DRM_DEBUG_KMS("LVDS VBT config bits: 0x%x\n" , driver->lvds_config); |
429 | |
430 | /* This bit means to use 96Mhz for DPLL_A or not */ |
431 | if (driver->primary_lfp_id) |
432 | dev_priv->dplla_96mhz = true; |
433 | else |
434 | dev_priv->dplla_96mhz = false; |
435 | } |
436 | |
437 | static void |
438 | parse_device_mapping(struct drm_psb_private *dev_priv, |
439 | struct bdb_header *bdb) |
440 | { |
441 | struct bdb_general_definitions *p_defs; |
442 | struct child_device_config *p_child, *child_dev_ptr; |
443 | int i, child_device_num, count; |
444 | u16 block_size; |
445 | |
446 | p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); |
447 | if (!p_defs) { |
448 | DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n" ); |
449 | return; |
450 | } |
451 | /* judge whether the size of child device meets the requirements. |
452 | * If the child device size obtained from general definition block |
453 | * is different with sizeof(struct child_device_config), skip the |
454 | * parsing of sdvo device info |
455 | */ |
456 | if (p_defs->child_dev_size != sizeof(*p_child)) { |
457 | /* different child dev size . Ignore it */ |
458 | DRM_DEBUG_KMS("different child size is found. Invalid.\n" ); |
459 | return; |
460 | } |
461 | /* get the block size of general definitions */ |
462 | block_size = get_blocksize(p: p_defs); |
463 | /* get the number of child device */ |
464 | child_device_num = (block_size - sizeof(*p_defs)) / |
465 | sizeof(*p_child); |
466 | count = 0; |
467 | /* get the number of child devices that are present */ |
468 | for (i = 0; i < child_device_num; i++) { |
469 | p_child = &(p_defs->devices[i]); |
470 | if (!p_child->device_type) { |
471 | /* skip the device block if device type is invalid */ |
472 | continue; |
473 | } |
474 | count++; |
475 | } |
476 | if (!count) { |
477 | DRM_DEBUG_KMS("no child dev is parsed from VBT\n" ); |
478 | return; |
479 | } |
480 | dev_priv->child_dev = kcalloc(n: count, size: sizeof(*p_child), GFP_KERNEL); |
481 | if (!dev_priv->child_dev) { |
482 | DRM_DEBUG_KMS("No memory space for child devices\n" ); |
483 | return; |
484 | } |
485 | |
486 | dev_priv->child_dev_num = count; |
487 | count = 0; |
488 | for (i = 0; i < child_device_num; i++) { |
489 | p_child = &(p_defs->devices[i]); |
490 | if (!p_child->device_type) { |
491 | /* skip the device block if device type is invalid */ |
492 | continue; |
493 | } |
494 | child_dev_ptr = dev_priv->child_dev + count; |
495 | count++; |
496 | memcpy((void *)child_dev_ptr, (void *)p_child, |
497 | sizeof(*p_child)); |
498 | } |
499 | return; |
500 | } |
501 | |
502 | |
503 | /** |
504 | * psb_intel_init_bios - initialize VBIOS settings & find VBT |
505 | * @dev: DRM device |
506 | * |
507 | * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers |
508 | * to appropriate values. |
509 | * |
510 | * VBT existence is a sanity check that is relied on by other i830_bios.c code. |
511 | * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may |
512 | * feed an updated VBT back through that, compared to what we'll fetch using |
513 | * this method of groping around in the BIOS data. |
514 | * |
515 | * Returns 0 on success, nonzero on failure. |
516 | */ |
517 | int psb_intel_init_bios(struct drm_device *dev) |
518 | { |
519 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
520 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
521 | struct vbt_header *vbt = NULL; |
522 | struct bdb_header *bdb = NULL; |
523 | u8 __iomem *bios = NULL; |
524 | size_t size; |
525 | int i; |
526 | |
527 | |
528 | dev_priv->panel_type = 0xff; |
529 | |
530 | /* XXX Should this validation be moved to intel_opregion.c? */ |
531 | if (dev_priv->opregion.vbt) { |
532 | struct vbt_header *vbt = dev_priv->opregion.vbt; |
533 | if (memcmp(p: vbt->signature, q: "$VBT" , size: 4) == 0) { |
534 | DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n" , |
535 | vbt->signature); |
536 | bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); |
537 | } else |
538 | dev_priv->opregion.vbt = NULL; |
539 | } |
540 | |
541 | if (bdb == NULL) { |
542 | bios = pci_map_rom(pdev, size: &size); |
543 | if (!bios) |
544 | return -1; |
545 | |
546 | /* Scour memory looking for the VBT signature */ |
547 | for (i = 0; i + 4 < size; i++) { |
548 | if (!memcmp(p: bios + i, q: "$VBT" , size: 4)) { |
549 | vbt = (struct vbt_header *)(bios + i); |
550 | break; |
551 | } |
552 | } |
553 | |
554 | if (!vbt) { |
555 | dev_err(dev->dev, "VBT signature missing\n" ); |
556 | pci_unmap_rom(pdev, rom: bios); |
557 | return -1; |
558 | } |
559 | bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); |
560 | } |
561 | |
562 | /* Grab useful general dxefinitions */ |
563 | parse_general_features(dev_priv, bdb); |
564 | parse_driver_features(dev_priv, bdb); |
565 | parse_lfp_panel_data(dev_priv, bdb); |
566 | parse_sdvo_panel_data(dev_priv, bdb); |
567 | parse_sdvo_device_mapping(dev_priv, bdb); |
568 | parse_device_mapping(dev_priv, bdb); |
569 | parse_backlight_data(dev_priv, bdb); |
570 | parse_edp(dev_priv, bdb); |
571 | |
572 | if (bios) |
573 | pci_unmap_rom(pdev, rom: bios); |
574 | |
575 | return 0; |
576 | } |
577 | |
578 | /* |
579 | * Destroy and free VBT data |
580 | */ |
581 | void psb_intel_destroy_bios(struct drm_device *dev) |
582 | { |
583 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); |
584 | |
585 | kfree(objp: dev_priv->sdvo_lvds_vbt_mode); |
586 | kfree(objp: dev_priv->lfp_lvds_vbt_mode); |
587 | kfree(objp: dev_priv->lvds_bl); |
588 | } |
589 | |