diff --git a/src/i830.h b/src/i830.h index 00a5059..13baf91 100644 --- a/src/i830.h +++ b/src/i830.h @@ -555,6 +555,10 @@ typedef struct _I830Rec { OptionInfoPtr Options; Bool lvds_24_bit_mode; + Bool lvds_use_ssc; + int lvds_ssc_freq; /* in MHz */ + + Bool tv_present; /* TV connector present (from VBIOS) */ Bool StolenOnly; diff --git a/src/i830_bios.c b/src/i830_bios.c index 57ee278..a8193fc 100644 --- a/src/i830_bios.c +++ b/src/i830_bios.c @@ -70,6 +70,32 @@ i830DumpBIOSToFile(ScrnInfoPtr pScrn, unsigned char *bios) fclose(f); } +static void * +find_section(struct bdb_header *bdb, int section_id) +{ + unsigned char *base = (unsigned char *)bdb; + int index = 0; + uint16_t total, current_size; + unsigned char current_id; + + /* skip to first section */ + index += bdb->header_size; + total = bdb->bdb_size; + + /* walk the sections looking for section_id */ + while (index < total) { + current_id = *(base + index); + index++; + current_size = *((uint16_t *)(base + index)); + index += 2; + if (current_id == section_id) + return base + index; + index += current_size; + } + + return NULL; +} + /** * Loads the Video BIOS and checks that the VBT exists. * @@ -127,6 +153,70 @@ i830_bios_get (ScrnInfoPtr pScrn) return bios; } +void +i830_bios_get_ssc(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + struct vbt_header *vbt; + struct bdb_header *bdb; + struct bdb_general_features *bdb_features; + int vbt_off, bdb_off; + unsigned char *bios; + + bios = i830_bios_get(pScrn); + + if (bios == NULL) + return; + + vbt_off = INTEL_BIOS_16(0x1a); + vbt = (struct vbt_header *)(bios + vbt_off); + bdb_off = vbt_off + vbt->bdb_offset; + bdb = (struct bdb_header *)(bios + bdb_off); + + bdb_features = find_section(bdb, BDB_GENERAL_FEATURES); + if (!bdb_features) + return; + + pI830->lvds_use_ssc = bdb_features->enable_ssc; + if (pI830->lvds_use_ssc) { + if (IS_I855(pI830)) + pI830->lvds_ssc_freq = bdb_features->ssc_freq ? 66 : 48; + else + pI830->lvds_ssc_freq = bdb_features->ssc_freq ? 100 : 96; + } + + xfree(bios); +} + +void +i830_bios_get_tv(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + struct vbt_header *vbt; + struct bdb_header *bdb; + struct bdb_general_features *bdb_features; + int vbt_off, bdb_off; + unsigned char *bios; + + bios = i830_bios_get(pScrn); + + if (bios == NULL) + return; + + vbt_off = INTEL_BIOS_16(0x1a); + vbt = (struct vbt_header *)(bios + vbt_off); + bdb_off = vbt_off + vbt->bdb_offset; + bdb = (struct bdb_header *)(bios + bdb_off); + + bdb_features = find_section(bdb, BDB_GENERAL_FEATURES); + if (!bdb_features) + return; + + pI830->tv_present = bdb_features->int_tv_support; + + xfree(bios); +} + /** * Returns the BIOS's fixed panel mode. * diff --git a/src/i830_bios.h b/src/i830_bios.h index 95230f5..c1ba50d 100644 --- a/src/i830_bios.h +++ b/src/i830_bios.h @@ -49,6 +49,97 @@ struct bdb_header { uint16_t bdb_size; /**< in bytes */ } __attribute__((packed)); +/* + * There are several types of BIOS data blocks (BDBs), each block has + * an ID and size in the first 3 bytes (ID in first, size in next 2). + * Known types are listed below. + */ +#define BDB_GENERAL_FEATURES 1 +#define BDB_GENERAL_DEFINITIONS 2 +#define BDB_OLD_TOGGLE_LIST 3 +#define BDB_MODE_SUPPORT_LIST 4 +#define BDB_GENERIC_MODE_TABLE 5 +#define BDB_EXT_MMIO_REGS 6 +#define BDB_SWF_IO 7 +#define BDB_SWF_MMIO 8 +#define BDB_DOT_CLOCK_TABLE 9 +#define BDB_MODE_REMOVAL_TABLE 10 +#define BDB_CHILD_DEVICE_TABLE 11 +#define BDB_DRIVER_FEATURES 12 +#define BDB_DRIVER_PERSISTENCE 13 +#define BDB_EXT_TABLE_PTRS 14 +#define BDB_DOT_CLOCK_OVERRIDE 15 +#define BDB_DISPLAY_SELECT 16 +/* 17 rsvd */ +#define BDB_DRIVER_ROTATION 18 +#define BDB_DISPLAY_REMOVE 19 +#define BDB_OEM_CUSTOM 20 +#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */ +#define BDB_SDVO_LVDS_OPTIONS 22 +#define BDB_SDVO_PANEL_DTDS 23 +#define BDB_SDVO_LVDS_PNP_IDS 24 +#define BDB_SDVO_LVDS_POWER_SEQ 25 +#define BDB_TV_OPTIONS 26 +#define BDB_LVDS_OPTIONS 40 +#define BDB_LVDS_LFP_DATA_PTRS 41 +#define BDB_LVDS_LFP_DATA 42 +#define BDB_LVDS_BACKLIGHT 43 +#define BDB_LVDS_POWER 44 +#define BDB_SKIP 254 /* VBIOS private block, ignore */ + +struct bdb_general_features { + /* bits 1 */ + unsigned char panel_fitting:2; + unsigned char flexaim:1; + unsigned char msg_enable:1; + unsigned char clear_screen:3; + unsigned char color_flip:1; + + /* bits 2 */ + unsigned char download_ext_vbt:1; + unsigned char enable_ssc:1; + unsigned char ssc_freq:1; + unsigned char enable_lfp_on_override:1; + unsigned char disable_ssc_ddt:1; + unsigned char rsvd8:3; /* finish byte */ + + /* bits 3 */ + unsigned char disable_smooth_vision:1; + unsigned char single_dvi:1; + unsigned char rsvd9:6; /* finish byte */ + + /* bits 4 */ + unsigned char legacy_monitor_detect; + + /* bits 5 */ + unsigned char int_crt_support:1; + unsigned char int_tv_support:1; + unsigned char rsvd11:6; /* finish byte */ +} __attribute__((packed)); + +struct bdb_general_definitions { + /* DDC GPIO */ + unsigned char crt_ddc_gmbus_pin; + + /* DPMS bits */ + unsigned char dpms_acpi:1; + unsigned char skip_boot_crt_detect:1; + unsigned char dpms_aim:1; + unsigned char rsvd1:5; /* finish byte */ + + /* boot device bits */ + unsigned char boot_display[2]; + unsigned char child_dev_size; + + /* device info */ + unsigned char tv_or_lvds_info[33]; + unsigned char dev1[33]; + unsigned char dev2[33]; + unsigned char dev3[33]; + unsigned char dev4[33]; + /* may be another device block here on some platforms */ +}; + #define LVDS_CAP_EDID (1 << 6) #define LVDS_CAP_DITHER (1 << 5) #define LVDS_CAP_PFIT_AUTO_RATIO (1 << 4) @@ -150,6 +241,8 @@ struct vch_bdb_22 { unsigned char * i830_bios_get (ScrnInfoPtr pScrn); +void i830_bios_get_ssc(ScrnInfoPtr pScrn); +void i830_bios_get_tv(ScrnInfoPtr pScrn); DisplayModePtr i830_bios_get_panel_mode(ScrnInfoPtr pScrn, Bool *wants_dither); unsigned char * diff --git a/src/i830_display.c b/src/i830_display.c index abe9875..1f7c113 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -1156,7 +1156,11 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, } } - if (IS_I9XX(pI830)) { + if (pI830->lvds_use_ssc) { + refclk = pI830->lvds_ssc_freq * 1000; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "using SSC reference clock of %d MHz\n", refclk / 1000); + } else if (IS_I9XX(pI830)) { refclk = 96000; } else { refclk = 48000; @@ -1232,10 +1236,8 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; } -#if 0 - else if (is_lvds) + else if (is_lvds && pI830->lvds_use_ssc) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; -#endif else dpll |= PLL_REF_INPUT_DREFCLK; diff --git a/src/i830_lvds.c b/src/i830_lvds.c index af82ee7..d0d0099 100644 --- a/src/i830_lvds.c +++ b/src/i830_lvds.c @@ -1341,6 +1341,9 @@ i830_lvds_init(ScrnInfoPtr pScrn) goto disable_exit; } + /* Update pI830 w/SSC info, if any */ + i830_bios_get_ssc(pScrn); + skip_panel_fixed_mode_setup: /* Blacklist machines with BIOSes that list an LVDS panel without actually diff --git a/src/i830_tv.c b/src/i830_tv.c index cde929a..651f77b 100644 --- a/src/i830_tv.c +++ b/src/i830_tv.c @@ -1715,6 +1715,10 @@ i830_tv_init(ScrnInfoPtr pScrn) (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) return; + i830_bios_get_tv(pScrn); + if (!pI830->tv_present) /* VBIOS claims no TV connector */ + return; + output = xf86OutputCreate (pScrn, &i830_tv_output_funcs, "TV"); if (!output)