1/* 2 * Copyright � 2006 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 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 9 * Software is furnished to do so, subject to the following conditions: 10 * 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 13 * Software. 14 * 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, 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 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 21 * SOFTWARE. 22 * 23 * Authors: 24 * Eric Anholt <eric@anholt.net> 25 * 26 */ 27 28#include <errno.h> 29#include <fcntl.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34#include <sys/mman.h> 35#include <sys/stat.h> 36#include <sys/types.h> 37 38 39#include "../i830_bios.h" 40 41#include <X11/Xfuncproto.h> 42#include <X11/Xmd.h> 43#define _PARSE_EDID_ 44#include "edid.h" 45 46 47/* Make a fake pI830 so we can easily pull i830_bios.c code in here. */ 48struct _fake_i830 { 49 uint8_t *VBIOS; 50}; 51struct _fake_i830 I830; 52struct _fake_i830 *pI830 = &I830; 53 54#define INTEL_BIOS_8(_addr) (pI830->VBIOS[_addr]) 55#define INTEL_BIOS_16(_addr) (pI830->VBIOS[_addr] | \ 56 (pI830->VBIOS[_addr + 1] << 8)) 57#define INTEL_BIOS_32(_addr) (pI830->VBIOS[_addr] | \ 58 (pI830->VBIOS[_addr + 1] << 8) | \ 59 (pI830->VBIOS[_addr + 2] << 16) | \ 60 (pI830->VBIOS[_addr + 3] << 24)) 61 62#define YESNO(val) ((val) ? "yes" : "no") 63 64struct bdb_block { 65 uint8_t id; 66 uint16_t size; 67 void *data; 68}; 69 70struct bdb_header *bdb; 71static int tv_present; 72static int lvds_present; 73static int panel_type; 74 75static struct bdb_block *find_section(int section_id) 76{ 77 struct bdb_block *block; 78 unsigned char *base = (unsigned char *)bdb; 79 int index = 0; 80 uint16_t total, current_size; 81 unsigned char current_id; 82 83 /* skip to first section */ 84 index += bdb->header_size; 85 total = bdb->bdb_size; 86 87 block = malloc(sizeof(*block)); 88 if (!block) { 89 fprintf(stderr, "out of memory\n"); 90 exit(-1); 91 } 92 93 /* walk the sections looking for section_id */ 94 while (index < total) { 95 current_id = *(base + index); 96 index++; 97 current_size = *((uint16_t *)(base + index)); 98 index += 2; 99 if (current_id == section_id) { 100 block->id = current_id; 101 block->size = current_size; 102 block->data = base + index; 103 return block; 104 } 105 index += current_size; 106 } 107 108 free(block); 109 return NULL; 110} 111 112static void dump_general_features(void) 113{ 114 struct bdb_general_features *features; 115 struct bdb_block *block; 116 117 block = find_section(BDB_GENERAL_FEATURES); 118 119 if (!block) 120 return; 121 122 features = block->data; 123 124 printf("General features block:\n"); 125 126 printf("\tPanel fitting: "); 127 switch (features->panel_fitting) { 128 case 0: 129 printf("disabled\n"); 130 break; 131 case 1: 132 printf("text only\n"); 133 break; 134 case 2: 135 printf("graphics only\n"); 136 break; 137 case 3: 138 printf("text & graphics\n"); 139 break; 140 } 141 printf("\tFlexaim: %s\n", YESNO(features->flexaim)); 142 printf("\tMessage: %s\n", YESNO(features->msg_enable)); 143 printf("\tClear screen: %d\n", features->clear_screen); 144 printf("\tDVO color flip required: %s\n", YESNO(features->color_flip)); 145 printf("\tExternal VBT: %s\n", YESNO(features->download_ext_vbt)); 146 printf("\tEnable SSC: %s\n", YESNO(features->enable_ssc)); 147 if (features->enable_ssc) 148 printf("\tSSC frequency: %s\n", features->ssc_freq ? 149 "100 MHz (66 MHz on 855)" : "96 MHz (48 MHz on 855)"); 150 printf("\tLFP on override: %s\n", YESNO(features->enable_lfp_on_override)); 151 printf("\tDisable SSC on clone: %s\n", YESNO(features->disable_ssc_ddt)); 152 printf("\tDisable smooth vision: %s\n", 153 YESNO(features->disable_smooth_vision)); 154 printf("\tSingle DVI for CRT/DVI: %s\n", YESNO(features->single_dvi)); 155 printf("\tLegacy monitor detect: %s\n", 156 YESNO(features->legacy_monitor_detect)); 157 printf("\tIntegrated CRT: %s\n", YESNO(features->int_crt_support)); 158 printf("\tIntegrated TV: %s\n", YESNO(features->int_tv_support)); 159 160 tv_present = 1; /* should be based on whether TV DAC exists */ 161 lvds_present = 1; /* should be based on IS_MOBILE() */ 162 163 free(block); 164} 165 166static void dump_backlight_info(void) 167{ 168 struct bdb_block *block; 169 struct bdb_lvds_backlight *backlight; 170 struct blc_struct *blc; 171 172 block = find_section(BDB_LVDS_BACKLIGHT); 173 174 if (!block) 175 return; 176 177 backlight = block->data; 178 179 printf("Backlight info block (len %d):\n", block->size); 180 181 if (sizeof(struct blc_struct) != backlight->blcstruct_size) { 182 printf("\tBacklight struct sizes don't match (expected %d, got %d), skipping\n", 183 sizeof(struct blc_struct), backlight->blcstruct_size); 184 return; 185 } 186 187 blc = &backlight->panels[panel_type]; 188 189 printf("\tInverter type: %d\n", blc->inverter_type); 190 printf("\t polarity: %d\n", blc->inverter_polarity); 191 printf("\t GPIO pins: %d\n", blc->gpio_pins); 192 printf("\t GMBUS speed: %d\n", blc->gmbus_speed); 193 printf("\t PWM freq: %d\n", blc->pwm_freq); 194 printf("\tMinimum brightness: %d\n", blc->min_brightness); 195 printf("\tI2C slave addr: 0x%02x\n", blc->i2c_slave_addr); 196 printf("\tI2C command: 0x%02x\n", blc->i2c_cmd); 197} 198 199static void dump_general_definitions(void) 200{ 201 struct bdb_block *block; 202 struct bdb_general_definitions *defs; 203 struct child_device_config *child; 204 int i; 205 char child_id[11]; 206 int child_device_num; 207 208 block = find_section(BDB_GENERAL_DEFINITIONS); 209 210 if (!block) 211 return; 212 213 defs = block->data; 214 215 printf("General definitions block:\n"); 216 217 printf("\tCRT DDC GMBUS addr: 0x%02x\n", defs->crt_ddc_gmbus_pin); 218 printf("\tUse ACPI DPMS CRT power states: %s\n", YESNO(defs->dpms_acpi)); 219 printf("\tSkip CRT detect at boot: %s\n", 220 YESNO(defs->skip_boot_crt_detect)); 221 printf("\tUse DPMS on AIM devices: %s\n", YESNO(defs->dpms_aim)); 222 printf("\tBoot display type: 0x%02x%02x\n", defs->boot_display[1], 223 defs->boot_display[0]); 224 printf("\tTV data block present: %s\n", YESNO(tv_present)); 225 child_device_num = (block->size - sizeof(*defs)) / sizeof(*child); 226 for (i = 0; i < child_device_num; i++) { 227 child = &defs->devices[i]; 228 if (!child->device_type) { 229 printf("\tChild device %d not present\n", i); 230 continue; 231 } 232 strncpy(child_id, (char *)child->device_id, 10); 233 child_id[10] = 0; 234 printf("\tChild %d device info:\n", i); 235 printf("\t\tSignature: %s\n", child_id); 236 printf("\t\tAIM offset: %d\n", child->addin_offset); 237 printf("\t\tDVO port: 0x%02x\n", child->dvo_port); 238 } 239 240 free(block); 241} 242 243#if 0 244static void dump_child_devices(void) 245{ 246 struct bdb_block *block; 247 struct bdb_child_devices *child_devs; 248 struct child_device_config *child; 249 int i; 250 251 block = find_section(BDB_CHILD_DEVICE_TABLE); 252 if (!block) { 253 printf("No child device table found\n"); 254 return; 255 } 256 257 child_devs = block->data; 258 259 printf("Child devices block:\n"); 260 for (i = 0; i < DEVICE_CHILD_SIZE; i++) { 261 child = &child_devs->children[i]; 262 /* Skip nonexistent children */ 263 if (!child->device_type) 264 continue; 265 printf("\tChild device %d\n", i); 266 printf("\t\tType: 0x%04x\n", child->device_type); 267 printf("\t\tDVO port: 0x%02x\n", child->dvo_port); 268 printf("\t\tI2C pin: 0x%02x\n", child->i2c_pin); 269 printf("\t\tSlave addr: 0x%02x\n", child->slave_addr); 270 printf("\t\tDDC pin: 0x%02x\n", child->ddc_pin); 271 printf("\t\tDVO config: 0x%02x\n", child->dvo_cfg); 272 printf("\t\tDVO wiring: 0x%02x\n", child->dvo_wiring); 273 } 274 275 free(block); 276} 277#endif 278 279static void dump_lvds_options(void) 280{ 281 struct bdb_block *block; 282 struct bdb_lvds_options *options; 283 284 block = find_section(BDB_LVDS_OPTIONS); 285 if (!block) { 286 printf("No LVDS options block\n"); 287 return; 288 } 289 290 options = block->data; 291 292 printf("LVDS options block:\n"); 293 294 panel_type = options->panel_type; 295 printf("\tPanel type: %d\n", panel_type); 296 printf("\tLVDS EDID available: %s\n", YESNO(options->lvds_edid)); 297 printf("\tPixel dither: %s\n", YESNO(options->pixel_dither)); 298 printf("\tPFIT auto ratio: %s\n", YESNO(options->pfit_ratio_auto)); 299 printf("\tPFIT enhanced graphics mode: %s\n", 300 YESNO(options->pfit_gfx_mode_enhanced)); 301 printf("\tPFIT enhanced text mode: %s\n", 302 YESNO(options->pfit_text_mode_enhanced)); 303 printf("\tPFIT mode: %d\n", options->pfit_mode); 304 305 free(block); 306} 307 308static void dump_lvds_ptr_data(void) 309{ 310 struct bdb_block *block; 311 struct bdb_lvds_lfp_data *lvds_data; 312 struct bdb_lvds_lfp_data_ptrs *ptrs; 313 struct lvds_fp_timing *fp_timing; 314 struct bdb_lvds_lfp_data_entry *entry; 315 int lfp_data_size; 316 317 block = find_section(BDB_LVDS_LFP_DATA_PTRS); 318 if (!block) { 319 printf("No LFP data pointers block\n"); 320 return; 321 } 322 ptrs = block->data; 323 324 block = find_section(BDB_LVDS_LFP_DATA); 325 if (!block) { 326 printf("No LVDS data block\n"); 327 return; 328 } 329 lvds_data = block->data; 330 331 lfp_data_size = ptrs->ptr[1].fp_timing_offset - ptrs->ptr[0].fp_timing_offset; 332 entry = (struct bdb_lvds_lfp_data_entry *)((uint8_t *)lvds_data->data + 333 (lfp_data_size * panel_type)); 334 fp_timing = &entry->fp_timing; 335 336 printf("LVDS timing pointer data:\n"); 337 printf(" Number of entries: %d\n", ptrs->lvds_entries); 338 339 printf("\tpanel type %02i: %dx%d\n", panel_type, fp_timing->x_res, 340 fp_timing->y_res); 341 342 free(block); 343} 344 345static void dump_lvds_data(void) 346{ 347 struct bdb_block *block; 348 struct bdb_lvds_lfp_data *lvds_data; 349 struct bdb_lvds_lfp_data_ptrs *ptrs; 350 int num_entries; 351 int i; 352 int hdisplay, hsyncstart, hsyncend, htotal; 353 int vdisplay, vsyncstart, vsyncend, vtotal; 354 float clock; 355 int lfp_data_size, dvo_offset; 356 357 block = find_section(BDB_LVDS_LFP_DATA_PTRS); 358 if (!block) { 359 printf("No LVDS ptr block\n"); 360 return; 361 } 362 ptrs = block->data; 363 lfp_data_size = ptrs->ptr[1].fp_timing_offset - ptrs->ptr[0].fp_timing_offset; 364 dvo_offset = ptrs->ptr[0].dvo_timing_offset - ptrs->ptr[0].fp_timing_offset; 365 free(block); 366 367 block = find_section(BDB_LVDS_LFP_DATA); 368 if (!block) { 369 printf("No LVDS data block\n"); 370 return; 371 } 372 373 lvds_data = block->data; 374 num_entries = block->size / lfp_data_size; 375 376 printf("LVDS panel data block (preferred block marked with '*'):\n"); 377 printf(" Number of entries: %d\n", num_entries); 378 379 for (i = 0; i < num_entries; i++) { 380 uint8_t *lfp_data_ptr = (uint8_t *)lvds_data->data + lfp_data_size * i; 381 uint8_t *timing_data = lfp_data_ptr + dvo_offset; 382 struct bdb_lvds_lfp_data_entry *lfp_data = 383 (struct bdb_lvds_lfp_data_entry *)lfp_data_ptr; 384 char marker; 385 386 if (i == panel_type) 387 marker = '*'; 388 else 389 marker = ' '; 390 391 hdisplay = _H_ACTIVE(timing_data); 392 hsyncstart = hdisplay + _H_SYNC_OFF(timing_data); 393 hsyncend = hsyncstart + _H_SYNC_WIDTH(timing_data); 394 htotal = hdisplay + _H_BLANK(timing_data); 395 396 vdisplay = _V_ACTIVE(timing_data); 397 vsyncstart = vdisplay + _V_SYNC_OFF(timing_data); 398 vsyncend = vsyncstart + _V_SYNC_WIDTH(timing_data); 399 vtotal = vdisplay + _V_BLANK(timing_data); 400 clock = _PIXEL_CLOCK(timing_data) / 1000; 401 402 printf("%c\tpanel type %02i: %dx%d clock %d\n", marker, 403 i, lfp_data->fp_timing.x_res, lfp_data->fp_timing.y_res, 404 _PIXEL_CLOCK(timing_data)); 405 printf("\t\tinfo:\n"); 406 printf("\t\t LVDS: 0x%08lx\n", 407 (unsigned long)lfp_data->fp_timing.lvds_reg_val); 408 printf("\t\t PP_ON_DELAYS: 0x%08lx\n", 409 (unsigned long)lfp_data->fp_timing.pp_on_reg_val); 410 printf("\t\t PP_OFF_DELAYS: 0x%08lx\n", 411 (unsigned long)lfp_data->fp_timing.pp_off_reg_val); 412 printf("\t\t PP_DIVISOR: 0x%08lx\n", 413 (unsigned long)lfp_data->fp_timing.pp_cycle_reg_val); 414 printf("\t\t PFIT: 0x%08lx\n", 415 (unsigned long)lfp_data->fp_timing.pfit_reg_val); 416 printf("\t\ttimings: %d %d %d %d %d %d %d %d %.2f (%s)\n", 417 hdisplay, hsyncstart, hsyncend, htotal, 418 vdisplay, vsyncstart, vsyncend, vtotal, clock, 419 (hsyncend > htotal || vsyncend > vtotal) ? 420 "BAD!" : "good"); 421 } 422 free(block); 423} 424 425static void dump_driver_feature(void) 426{ 427 struct bdb_block *block; 428 struct bdb_driver_feature *feature; 429 430 block = find_section(BDB_DRIVER_FEATURES); 431 if (!block) { 432 printf("No Driver feature data block\n"); 433 return; 434 } 435 feature = block->data; 436 437 printf("Driver feature Data Block:\n"); 438 printf("\tBoot Device Algorithm: %s\n", feature->boot_dev_algorithm ? 439 "driver default": "os default"); 440 printf("\tBlock display switching when DVD active: %s\n", 441 YESNO(feature->block_display_switch)); 442 printf("\tAllow display switching when in Full Screen DOS: %s\n", 443 YESNO(feature->allow_display_switch)); 444 printf("\tHot Plug DVO: %s\n", YESNO(feature->hotplug_dvo)); 445 printf("\tDual View Zoom: %s\n", YESNO(feature->dual_view_zoom)); 446 printf("\tDriver INT 15h hook: %s\n", YESNO(feature->int15h_hook)); 447 printf("\tEnable Sprite in Clone Mode: %s\n", YESNO(feature->sprite_in_clone)); 448 printf("\tUse 00000110h ID for Primary LFP: %s\n", YESNO(feature->primary_lfp_id)); 449 printf("\tBoot Mode X: %u\n", feature->boot_mode_x); 450 printf("\tBoot Mode Y: %u\n", feature->boot_mode_y); 451 printf("\tBoot Mode Bpp: %u\n", feature->boot_mode_bpp); 452 printf("\tBoot Mode Refresh: %u\n", feature->boot_mode_refresh); 453 printf("\tEnable LFP as primary: %s\n", YESNO(feature->enable_lfp_primary)); 454 printf("\tSelective Mode Pruning: %s\n", YESNO(feature->selective_mode_pruning)); 455 printf("\tDual-Frequency Graphics Technology: %s\n", YESNO(feature->dual_frequency)); 456 printf("\tDefault Render Clock Frequency: %s\n", feature->render_clock_freq ? "low" : "high"); 457 printf("\tNT 4.0 Dual Display Clone Support: %s\n", YESNO(feature->nt_clone_support)); 458 printf("\tDefault Power Scheme user interface: %s\n", feature->power_scheme_ui ? "3rd party":"CUI"); 459 printf("\tSprite Display Assignment when Overlay is Active in Clone Mode: %s\n", 460 feature->sprite_display_assign ? "primary" : "secondary"); 461 printf("\tDisplay Maintain Aspect Scaling via CUI: %s\n", YESNO(feature->cui_aspect_scaling)); 462 printf("\tPreserve Aspect Ratio: %s\n", YESNO(feature->preserve_aspect_ratio)); 463 printf("\tEnable SDVO device power down: %s\n", YESNO(feature->sdvo_device_power_down)); 464 printf("\tCRT hotplug: %s\n", YESNO(feature->crt_hotplug)); 465 printf("\tLVDS config: "); 466 switch (feature->lvds_config) { 467 case BDB_DRIVER_NO_LVDS: 468 printf("No LVDS\n"); 469 break; 470 case BDB_DRIVER_INT_LVDS: 471 printf("Integrated LVDS\n"); 472 break; 473 case BDB_DRIVER_SDVO_LVDS: 474 printf("SDVO LVDS\n"); 475 break; 476 case BDB_DRIVER_EDP: 477 printf("Embedded DisplayPort\n"); 478 break; 479 } 480 printf("\tDefine Display statically: %s\n", YESNO(feature->static_display)); 481 printf("\tLegacy CRT max X: %d\n", feature->legacy_crt_max_x); 482 printf("\tLegacy CRT max Y: %d\n", feature->legacy_crt_max_y); 483 printf("\tLegacy CRT max refresh: %d\n", feature->legacy_crt_max_refresh); 484 free(block); 485} 486 487int main(int argc, char **argv) 488{ 489 int fd; 490 struct vbt_header *vbt = NULL; 491 int vbt_off, bdb_off, i; 492 char *filename = "bios"; 493 struct stat finfo; 494 struct bdb_block *block; 495 char signature[17]; 496 497 if (argc != 2) { 498 printf("usage: %s <rom file>\n", argv[0]); 499 return 1; 500 } 501 502 filename = argv[1]; 503 504 fd = open(filename, O_RDONLY); 505 if (fd == -1) { 506 printf("Couldn't open \"%s\": %s\n", filename, strerror(errno)); 507 return 1; 508 } 509 510 if (stat(filename, &finfo)) { 511 printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); 512 return 1; 513 } 514 515 pI830->VBIOS = mmap(NULL, finfo.st_size, PROT_READ, MAP_SHARED, fd, 0); 516 if (pI830->VBIOS == MAP_FAILED) { 517 printf("failed to map \"%s\": %s\n", filename, strerror(errno)); 518 return 1; 519 } 520 521 /* Scour memory looking for the VBT signature */ 522 for (i = 0; i + 4 < finfo.st_size; i++) { 523 if (!memcmp(pI830->VBIOS + i, "$VBT", 4)) { 524 vbt_off = i; 525 vbt = (struct vbt_header *)(pI830->VBIOS + i); 526 break; 527 } 528 } 529 530 if (!vbt) { 531 printf("VBT signature missing\n"); 532 return 1; 533 } 534 535 printf("VBT vers: %d.%d\n", vbt->version / 100, vbt->version % 100); 536 537 bdb_off = vbt_off + vbt->bdb_offset; 538 bdb = (struct bdb_header *)(pI830->VBIOS + bdb_off); 539 strncpy(signature, (char *)bdb->signature, 16); 540 signature[16] = 0; 541 printf("BDB sig: %s\n", signature); 542 printf("BDB vers: %d.%d\n", bdb->version / 100, bdb->version % 100); 543 544 printf("Available sections: "); 545 for (i = 0; i < 256; i++) { 546 block = find_section(i); 547 if (!block) 548 continue; 549 printf("%d ", i); 550 free(block); 551 } 552 printf("\n"); 553 554 dump_general_features(); 555 dump_general_definitions(); 556// dump_child_devices(); 557 dump_lvds_options(); 558 dump_lvds_data(); 559 dump_lvds_ptr_data(); 560 dump_backlight_info(); 561 562 dump_driver_feature(); 563 564 return 0; 565} 566