diff options
author | Owain G. Ainsworth <oga@openbsd.org> | 2010-05-29 14:50:40 +0100 |
---|---|---|
committer | Owain G. Ainsworth <oga@openbsd.org> | 2010-06-07 21:49:21 +0100 |
commit | 1b8b09896bb8e052fb2c045a28d1488d6950a7d6 (patch) | |
tree | d9664fb1c2a1a410a6d95a646856a719994d8ec1 | |
parent | 7098b4c93e80a11e2b44134c9771b4dfaeca2b34 (diff) |
Initial Ironlake support
VGA works. LVDS is still screwy. panel fitting hasn't been messed with
(yet). Mostly from RHEL5's 2.2.1 branch, courtesy of airlied.
-rw-r--r-- | src/i810_reg.h | 62 | ||||
-rw-r--r-- | src/i830.h | 12 | ||||
-rw-r--r-- | src/i830_crt.c | 86 | ||||
-rw-r--r-- | src/i830_display.c | 887 | ||||
-rw-r--r-- | src/i830_driver.c | 71 | ||||
-rw-r--r-- | src/i830_hdmi.c | 52 | ||||
-rw-r--r-- | src/i830_lvds.c | 164 |
7 files changed, 1262 insertions, 72 deletions
diff --git a/src/i810_reg.h b/src/i810_reg.h index 191ddb2a..9f78eb1d 100644 --- a/src/i810_reg.h +++ b/src/i810_reg.h @@ -2214,6 +2214,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # define SVBLANK_INT_STATUS (1 << 2) # define VBLANK_INT_STATUS (1 << 1) # define OREG_UPDATE_STATUS (1 << 0) +#define PIPE_BPC_MASK (7 << 5) /* Ironlake */ +#define PIPE_8BPC (0 << 5) +#define PIPE_10BPC (1 << 5) +#define PIPE_6BPC (2 << 5) +#define PIPE_12BPC (3 << 5) + #define DSPARB 0x70030 @@ -2350,6 +2356,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* IGDNG */ #define DISPPLANE_X_TILE (1<<10) #define DISPPLANE_LINEAR (0<<10) +#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) #define DSPABASE 0x70184 /* IGDNG */ @@ -3067,6 +3074,10 @@ typedef enum { #define PFA_CTL_1 0x68080 #define PFB_CTL_1 0x68880 #define PF_ENABLE (1<<31) +#define PFA_WIN_SZ 0x68074 +#define PFB_WIN_SZ 0x68874 +#define PFA_WIN_POS 0x68070 +#define PFB_WIN_POS 0x68870 #define PFA_WIN_POS 0x68070 #define PFB_WIN_POS 0x68870 @@ -3118,6 +3129,10 @@ typedef enum { #define GTIIR 0x44018 #define GTIER 0x4401c + +#define DISP_ARB_CTL 0x45000 +#define DISP_TILE_SURFACE_SWIZZLING (1<<13) + /* PCH */ /* south display engine interrupt */ @@ -3189,8 +3204,11 @@ typedef enum { #define DREF_CPU_SOURCE_OUTPUT_NONSPREAD (3<<13) #define DREF_SSC_SOURCE_DISABLE (0<<11) #define DREF_SSC_SOURCE_ENABLE (2<<11) +#define DREF_SSC_SOURCE_MASK (2<<11) #define DREF_NONSPREAD_SOURCE_DISABLE (0<<9) +#define DREF_NONSPREAD_CK505_ENABLE (1<<9) #define DREF_NONSPREAD_SOURCE_ENABLE (2<<9) +#define DREF_NONSPREAD_SOURCE_MASK (2<<9) #define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7) #define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7) #define DREF_SSC4_DOWNSPREAD (0<<6) @@ -3302,6 +3320,7 @@ typedef enum { #define FDI_DP_PORT_WIDTH_X2 (1<<19) #define FDI_DP_PORT_WIDTH_X3 (2<<19) #define FDI_DP_PORT_WIDTH_X4 (3<<19) +#define FDI_DP_PORT_WIDTH_MASK (7<<19) #define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18) /* IGDNG: hardwired to 1 */ #define FDI_TX_PLL_ENABLE (1<<14) @@ -3320,6 +3339,7 @@ typedef enum { #define FDI_10BPC (1<<16) #define FDI_6BPC (2<<16) #define FDI_12BPC (3<<16) +#define FDI_BPC_MASK (3<<16) #define FDI_LINK_REVERSE_OVERWRITE (1<<15) #define FDI_DMI_LINK_REVERSE_MASK (1<<14) #define FDI_RX_PLL_ENABLE (1<<13) @@ -3406,6 +3426,48 @@ typedef enum { #define HDMIC 0xe1150 #define HDMID 0xe1160 #define PCH_LVDS 0xe1180 +#define LVDS_DETECTED (1 << 1) + +#define PCH_DP_B 0xe4100 +#define PCH_DP_C 0xe4200 +#define PCH_DP_D 0xe4200 + +#define BLC_PWM_CPU_CTL2 0x48250 +#define PWM_ENABLE (1 << 31) +#define PWM_PIPE_A (0 << 29) +#define PWM_PIPE_B (1 << 29) +#define BLC_PWM_CPU_CTL 0x48254 + +#define BLC_PWM_PCH_CTL1 0xc8250 +#define PWM_PCH_ENABLE (1 << 31) +#define PWM_POLARITY_ACTIVE_LOW (1 << 29) +#define PWM_POLARITY_ACTIVE_HIGH (0 << 29) +#define PWM_POLARITY_ACTIVE_LOW2 (1 << 28) +#define PWM_POLARITY_ACTIVE_HIGH2 (0 << 28) + +#define BLC_PWM_PCH_CTL2 0xc8254 + +#define PCH_PP_STATUS 0xc7200 +#define PCH_PP_CONTROL 0xc7204 +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) +#define PCH_PP_ON_DELAYS 0xc7208 +#define EDP_PANEL (1 << 30) +#define PCH_PP_OFF_DELAYS 0xc720c +#define PCH_PP_DIVISOR 0xc7210 + +#define DE_POWER1 0x42400 +#define WM0_PIPE_A 0x45100 +#define WM0_PIPE_B 0x45104 +#define WM1 0x45108 +#define WM2 0x4510C +#define WM3 0x45110 +#define WM1S 0x45120 + + #define AUD_CONFIG 0x62000 #define AUD_DEBUG 0x62010 @@ -58,6 +58,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "xf86Crtc.h" #include "xf86RandR12.h" +#include "xf86int10.h" + #include "xorg-server.h" #include <pciaccess.h> @@ -328,6 +330,7 @@ typedef struct _I830CrtcPrivateRec { uint64_t cursor_addr; unsigned long cursor_argb_addr; Bool cursor_is_argb; + int bpc; } I830CrtcPrivateRec, *I830CrtcPrivatePtr; #define I830CrtcPrivate(c) ((I830CrtcPrivatePtr) (c)->driver_private) @@ -398,6 +401,10 @@ enum last_3d { * BCM_KERNEL: use kernel methods for controlling the backlight * This is only available on some platforms, but where present this can * provide the best user experience. + * + * And, if you're in EL5, a fifth! + * BCM_IRONLAKE_NULL: just don't do anything and be quiet about it. This is + * a workaround for an RHGB interaction; you won't hit this at runtime. */ enum backlight_control { @@ -405,6 +412,7 @@ enum backlight_control { BCM_LEGACY, BCM_COMBO, BCM_KERNEL, + BCM_IRONLAKE_NULL }; enum dri_type { @@ -720,6 +728,10 @@ typedef struct intel_screen_private { Bool fallback_debug; struct sdvo_device_mapping sdvo_mappings[2]; unsigned debug_flush; + + /* ironlake vt restore hack */ + xf86Int10InfoPtr int10; + int int10Mode; } intel_screen_private; enum { diff --git a/src/i830_crt.c b/src/i830_crt.c index e2e3694b..26c9d412 100644 --- a/src/i830_crt.c +++ b/src/i830_crt.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#include <unistd.h> + #include "xf86.h" #include "i830.h" #include "xf86Modes.h" @@ -39,9 +41,14 @@ i830_crt_dpms(xf86OutputPtr output, int mode) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); - uint32_t temp; + uint32_t temp, reg; + + if (IS_IGDNG(intel)) + reg = PCH_ADPA; + else + reg = ADPA; - temp = INREG(ADPA); + temp = INREG(reg); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~ADPA_DAC_ENABLE; @@ -60,7 +67,7 @@ i830_crt_dpms(xf86OutputPtr output, int mode) break; } - OUTREG(ADPA, temp); + OUTREG(reg, temp); } static void @@ -68,8 +75,9 @@ i830_crt_save (xf86OutputPtr output) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); + uint32_t reg = IS_IGDNG(intel) ? PCH_ADPA : ADPA; - intel->saveADPA = INREG(ADPA); + intel->saveADPA = INREG(reg); } static void @@ -77,8 +85,9 @@ i830_crt_restore (xf86OutputPtr output) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); + uint32_t reg = IS_IGDNG(intel) ? PCH_ADPA : ADPA; - OUTREG(ADPA, intel->saveADPA); + OUTREG(reg, intel->saveADPA); } static int @@ -122,16 +131,23 @@ i830_crt_mode_set(xf86OutputPtr output, DisplayModePtr mode, I830CrtcPrivatePtr i830_crtc = crtc->driver_private; int dpll_md_reg; uint32_t adpa, dpll_md; + uint32_t adpa_reg; if (i830_crtc->pipe == 0) dpll_md_reg = DPLL_A_MD; else dpll_md_reg = DPLL_B_MD; + + if (IS_IGDNG(intel)) + adpa_reg = PCH_ADPA; + else + adpa_reg = ADPA; + /* * Disable separate mode multiplier used when cloning SDVO to CRT * XXX this needs to be adjusted when we really are cloning */ - if (IS_I965G(intel)) + if (IS_I965G(intel) && !IS_IGDNG(intel)) { dpll_md = INREG(dpll_md_reg); OUTREG(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); @@ -146,15 +162,55 @@ i830_crt_mode_set(xf86OutputPtr output, DisplayModePtr mode, if (i830_crtc->pipe == 0) { adpa |= ADPA_PIPE_A_SELECT; - OUTREG(BCLRPAT_A, 0); + if (!IS_IGDNG(intel)) + OUTREG(BCLRPAT_A, 0); } else { adpa |= ADPA_PIPE_B_SELECT; - OUTREG(BCLRPAT_B, 0); + if (!IS_IGDNG(intel)) + OUTREG(BCLRPAT_B, 0); } - OUTREG(ADPA, adpa); + OUTREG(adpa_reg, adpa); +} + +static Bool intel_igdng_crt_detect_hotplug(xf86OutputPtr output) +{ + ScrnInfoPtr scrn = output->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + uint32_t adpa; + Bool ret; + + adpa = INREG(PCH_ADPA); + + adpa &= ~ADPA_CRT_HOTPLUG_MASK; + + adpa |= (ADPA_CRT_HOTPLUG_PERIOD_64 | + ADPA_CRT_HOTPLUG_WARMUP_5MS | + ADPA_CRT_HOTPLUG_SAMPLE_2S | + ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */ + ADPA_CRT_HOTPLUG_VOLREF_325MV); + OUTREG(PCH_ADPA, adpa); + + usleep(6000); /* warmup */ + + adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; + + OUTREG(PCH_ADPA, adpa); + + while (INREG(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) + ; + + /* Check the status to see if both blue and green are on now */ + adpa = INREG(PCH_ADPA) & ADPA_CRT_HOTPLUG_MONITOR_MASK; + if (adpa == ADPA_CRT_HOTPLUG_MONITOR_COLOR || + adpa == ADPA_CRT_HOTPLUG_MONITOR_MONO) + ret = TRUE; + else + ret = FALSE; + + return ret; } /** @@ -176,6 +232,9 @@ i830_crt_detect_hotplug(xf86OutputPtr output) int tries = 1; int try; + if (IS_IGDNG(intel)) + return intel_igdng_crt_detect_hotplug(output); + /* On 4 series desktop, CRT detect sequence need to be done twice * to get a reliable result. */ if (IS_G4X(intel) && !IS_GM45(intel)) @@ -459,11 +518,18 @@ i830_crt_get_crtc(xf86OutputPtr output) static xf86MonPtr i830_get_edid(xf86OutputPtr output, int gpio_reg, char *gpio_str) { + ScrnInfoPtr scrn = output->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); I830OutputPrivatePtr intel_output = output->driver_private; xf86MonPtr edid_mon = NULL; + uint32_t i2c_reg; /* Set up the DDC bus. */ - I830I2CInit(output->scrn, &intel_output->pDDCBus, gpio_reg, gpio_str); + if (IS_IGDNG(intel)) + i2c_reg = PCH_GPIOA; + else + i2c_reg = GPIOA; + I830I2CInit(scrn, &intel_output->pDDCBus, i2c_reg, "CRTDDC_A"); edid_mon = xf86OutputGetEDID (output, intel_output->pDDCBus); diff --git a/src/i830_display.c b/src/i830_display.c index 736ed043..fc7dceb6 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -42,6 +42,7 @@ #include "i830_bios.h" #include "i830_display.h" #include "xf86Modes.h" +#include "i810_reg.h" typedef struct { /* given values */ @@ -149,6 +150,16 @@ struct intel_limit { #define I9XX_P2_LVDS_FAST 7 #define I9XX_P2_LVDS_SLOW_LIMIT 112000 +#define IRONLAKE_P2_DOT_LIMIT 225000 /* 225Mhz */ +#define IRONLAKE_DOT_MIN 25000 +#define IRONLAKE_DOT_MAX 350000 +#define IRONLAKE_VCO_MIN 1760000 +#define IRONLAKE_VCO_MAX 3510000 +#define IRONLAKE_M1_MIN 12 +#define IRONLAKE_M1_MAX 22 +#define IRONLAKE_M2_MIN 5 +#define IRONLAKE_M2_MAX 9 + #define INTEL_LIMIT_I8XX_DVO_DAC 0 #define INTEL_LIMIT_I8XX_LVDS 1 #define INTEL_LIMIT_I9XX_SDVO_DAC 2 @@ -238,12 +249,75 @@ struct intel_limit { #define G4X_P2_DUAL_LVDS_FAST 7 #define G4X_P2_DUAL_LVDS_LIMIT 0 +/* DAC & HDMI Refclk 120Mhz */ +#define IRONLAKE_DAC_N_MIN 1 +#define IRONLAKE_DAC_N_MAX 5 +#define IRONLAKE_DAC_M_MIN 79 +#define IRONLAKE_DAC_M_MAX 127 +#define IRONLAKE_DAC_P_MIN 5 +#define IRONLAKE_DAC_P_MAX 80 +#define IRONLAKE_DAC_P1_MIN 1 +#define IRONLAKE_DAC_P1_MAX 8 +#define IRONLAKE_DAC_P2_SLOW 10 +#define IRONLAKE_DAC_P2_FAST 5 + +/* LVDS single-channel 120Mhz refclk */ +#define IRONLAKE_LVDS_S_N_MIN 1 +#define IRONLAKE_LVDS_S_N_MAX 3 +#define IRONLAKE_LVDS_S_M_MIN 79 +#define IRONLAKE_LVDS_S_M_MAX 118 +#define IRONLAKE_LVDS_S_P_MIN 28 +#define IRONLAKE_LVDS_S_P_MAX 112 +#define IRONLAKE_LVDS_S_P1_MIN 2 +#define IRONLAKE_LVDS_S_P1_MAX 8 +#define IRONLAKE_LVDS_S_P2_SLOW 14 +#define IRONLAKE_LVDS_S_P2_FAST 14 + +/* LVDS dual-channel 120Mhz refclk */ +#define IRONLAKE_LVDS_D_N_MIN 1 +#define IRONLAKE_LVDS_D_N_MAX 3 +#define IRONLAKE_LVDS_D_M_MIN 79 +#define IRONLAKE_LVDS_D_M_MAX 127 +#define IRONLAKE_LVDS_D_P_MIN 14 +#define IRONLAKE_LVDS_D_P_MAX 56 +#define IRONLAKE_LVDS_D_P1_MIN 2 +#define IRONLAKE_LVDS_D_P1_MAX 8 +#define IRONLAKE_LVDS_D_P2_SLOW 7 +#define IRONLAKE_LVDS_D_P2_FAST 7 + +/* LVDS single-channel 100Mhz refclk */ +#define IRONLAKE_LVDS_S_SSC_N_MIN 1 +#define IRONLAKE_LVDS_S_SSC_N_MAX 2 +#define IRONLAKE_LVDS_S_SSC_M_MIN 79 +#define IRONLAKE_LVDS_S_SSC_M_MAX 126 +#define IRONLAKE_LVDS_S_SSC_P_MIN 28 +#define IRONLAKE_LVDS_S_SSC_P_MAX 112 +#define IRONLAKE_LVDS_S_SSC_P1_MIN 2 +#define IRONLAKE_LVDS_S_SSC_P1_MAX 8 +#define IRONLAKE_LVDS_S_SSC_P2_SLOW 14 +#define IRONLAKE_LVDS_S_SSC_P2_FAST 14 + +/* LVDS dual-channel 100Mhz refclk */ +#define IRONLAKE_LVDS_D_SSC_N_MIN 1 +#define IRONLAKE_LVDS_D_SSC_N_MAX 3 +#define IRONLAKE_LVDS_D_SSC_M_MIN 79 +#define IRONLAKE_LVDS_D_SSC_M_MAX 126 +#define IRONLAKE_LVDS_D_SSC_P_MIN 14 +#define IRONLAKE_LVDS_D_SSC_P_MAX 42 +#define IRONLAKE_LVDS_D_SSC_P1_MIN 2 +#define IRONLAKE_LVDS_D_SSC_P1_MAX 6 +#define IRONLAKE_LVDS_D_SSC_P2_SLOW 7 +#define IRONLAKE_LVDS_D_SSC_P2_FAST 7 + static Bool intel_find_pll_i8xx_and_i9xx(const intel_limit_t *, xf86CrtcPtr, int, int, intel_clock_t *); static Bool intel_find_pll_g4x(const intel_limit_t *, xf86CrtcPtr, int, int, intel_clock_t *); +static Bool +intel_igdng_find_best_PLL(const intel_limit_t *, xf86CrtcPtr, + int, int, intel_clock_t *); static void i830_crtc_load_lut(xf86CrtcPtr crtc); @@ -405,6 +479,112 @@ static const intel_limit_t intel_limits[] = { }, }; +static const intel_limit_t intel_limits_ironlake_dac = { + .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX }, + .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX }, + .n = { .min = IRONLAKE_DAC_N_MIN, .max = IRONLAKE_DAC_N_MAX }, + .m = { .min = IRONLAKE_DAC_M_MIN, .max = IRONLAKE_DAC_M_MAX }, + .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX }, + .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX }, + .p = { .min = IRONLAKE_DAC_P_MIN, .max = IRONLAKE_DAC_P_MAX }, + .p1 = { .min = IRONLAKE_DAC_P1_MIN, .max = IRONLAKE_DAC_P1_MAX }, + .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT, + .p2_slow = IRONLAKE_DAC_P2_SLOW, + .p2_fast = IRONLAKE_DAC_P2_FAST }, + .find_pll = intel_igdng_find_best_PLL, +}; + +static const intel_limit_t intel_limits_ironlake_single_lvds = { + .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX }, + .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX }, + .n = { .min = IRONLAKE_LVDS_S_N_MIN, .max = IRONLAKE_LVDS_S_N_MAX }, + .m = { .min = IRONLAKE_LVDS_S_M_MIN, .max = IRONLAKE_LVDS_S_M_MAX }, + .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX }, + .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX }, + .p = { .min = IRONLAKE_LVDS_S_P_MIN, .max = IRONLAKE_LVDS_S_P_MAX }, + .p1 = { .min = IRONLAKE_LVDS_S_P1_MIN, .max = IRONLAKE_LVDS_S_P1_MAX }, + .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT, + .p2_slow = IRONLAKE_LVDS_S_P2_SLOW, + .p2_fast = IRONLAKE_LVDS_S_P2_FAST }, + .find_pll = intel_igdng_find_best_PLL, +}; + +static const intel_limit_t intel_limits_ironlake_dual_lvds = { + .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX }, + .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX }, + .n = { .min = IRONLAKE_LVDS_D_N_MIN, .max = IRONLAKE_LVDS_D_N_MAX }, + .m = { .min = IRONLAKE_LVDS_D_M_MIN, .max = IRONLAKE_LVDS_D_M_MAX }, + .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX }, + .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX }, + .p = { .min = IRONLAKE_LVDS_D_P_MIN, .max = IRONLAKE_LVDS_D_P_MAX }, + .p1 = { .min = IRONLAKE_LVDS_D_P1_MIN, .max = IRONLAKE_LVDS_D_P1_MAX }, + .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT, + .p2_slow = IRONLAKE_LVDS_D_P2_SLOW, + .p2_fast = IRONLAKE_LVDS_D_P2_FAST }, + .find_pll = intel_igdng_find_best_PLL, +}; + +static const intel_limit_t intel_limits_ironlake_single_lvds_100m = { + .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX }, + .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX }, + .n = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX }, + .m = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX }, + .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX }, + .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX }, + .p = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX }, + .p1 = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX }, + .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT, + .p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW, + .p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST }, + .find_pll = intel_igdng_find_best_PLL, +}; + +static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { + .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX }, + .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX }, + .n = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX }, + .m = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX }, + .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX }, + .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX }, + .p = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX }, + .p1 = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX }, + .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT, + .p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW, + .p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST }, + .find_pll = intel_igdng_find_best_PLL, +}; + + +static const intel_limit_t *intel_igdng_limit(xf86CrtcPtr crtc) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + const intel_limit_t *limit; + + if (i830PipeHasType(crtc, I830_OUTPUT_LVDS)) { + int refclk = 120; + + if (intel->lvds_use_ssc && intel->lvds_ssc_freq) + refclk = 100; + + if ((INREG(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) { + if (refclk == 100) + limit = &intel_limits_ironlake_dual_lvds_100m; + else + limit = &intel_limits_ironlake_dual_lvds; + } else { + if (refclk == 100) + limit = &intel_limits_ironlake_single_lvds_100m; + else + limit = &intel_limits_ironlake_single_lvds; + } + } else + limit = &intel_limits_ironlake_dac; + + return limit; +} + + static const intel_limit_t *intel_limit_g4x (xf86CrtcPtr crtc) { ScrnInfoPtr scrn = crtc->scrn; @@ -433,7 +613,9 @@ static const intel_limit_t *intel_limit (xf86CrtcPtr crtc) intel_screen_private *intel = intel_get_screen_private(scrn); const intel_limit_t *limit; - if (IS_G4X(intel)) { + if (IS_IGDNG(intel)) { + limit = intel_igdng_limit(crtc); + } else if (IS_G4X(intel)) { limit = intel_limit_g4x(crtc); } else if (IS_I9XX(intel) && !IS_IGD(intel)) { if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) @@ -507,6 +689,61 @@ i830PrintPll(ScrnInfoPtr scrn, char *prefix, intel_clock_t *clock) clock->p, clock->p1, clock->p2); } +static Bool +i830PllIsValid(xf86CrtcPtr crtc, intel_clock_t *clock); + +static Bool +intel_igdng_find_best_PLL(const intel_limit_t *limit, xf86CrtcPtr crtc, + int target, int refclk, intel_clock_t *best_clock) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + intel_clock_t clock; + int max_n; + Bool found = FALSE; + int err_most = (target >> 8) + (target >> 10); + + if (i830PipeHasType(crtc, I830_OUTPUT_LVDS)) { + if ((INREG(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset(best_clock, 0, sizeof(*best_clock)); + max_n = limit->n.max; + /* based on hardware requriment prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + /* based on hardware requirment prefere larger m1,m2 */ + for (clock.m1 = limit->m1.max; clock.m1 >= limit->m1.min; clock.m1--) { + for (clock.m2 = limit->m2.max; + clock.m2 >= limit->m2.min; clock.m2--) { + for (clock.p1 = limit->p1.max; + clock.p1 >= limit->p1.min; clock.p1--) { + int this_err; + + intel_clock(intel, refclk, &clock); + if (!i830PllIsValid(crtc, &clock)) + continue; + this_err = abs(clock.dot - target) ; + if (this_err < err_most) { + *best_clock = clock; + err_most = this_err; + max_n = clock.n; + found = TRUE; + } + } + } + } + } + return found; +} + /** * Returns whether any output on the specified pipe is of the specified type */ @@ -530,7 +767,12 @@ i830PipeHasType (xf86CrtcPtr crtc, int type) return FALSE; } +#if 0 +#if 1 #define i830PllInvalid(s) { /* ErrorF (s) */; return FALSE; } +#endif +#endif +#define i830PllInvalid(s) { ErrorF (s) ; return FALSE; } /** * Returns whether the given set of divisors are valid for a given refclk with * the given outputs. @@ -770,6 +1012,7 @@ Bool i830_pipe_a_require_activate (ScrnInfoPtr scrn) { xf86CrtcPtr crtc = i830_crtc_for_pipe (scrn, 0); + intel_screen_private *intel = intel_get_screen_private(scrn); /* VESA 640x480x72Hz mode to set on the pipe */ static DisplayModeRec mode = { NULL, NULL, "640x480", MODE_OK, M_T_DEFAULT, @@ -783,6 +1026,9 @@ i830_pipe_a_require_activate (ScrnInfoPtr scrn) FALSE, FALSE, 0, NULL, 0, 0.0, 0.0 }; + if (IS_IGDNG(intel)) + return FALSE; + if (!crtc) return FALSE; if (crtc->enabled) @@ -1076,6 +1322,16 @@ i830_disable_vga_plane (xf86CrtcPtr crtc) ScrnInfoPtr scrn = crtc->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); uint8_t sr01; + uint32_t vga_reg, vgacntrl; + + if (IS_IGDNG(intel)) + vga_reg = CPU_VGACNTRL; + else + vga_reg = VGACNTRL; + + vgacntrl = INREG(vga_reg); + if (vgacntrl & VGA_DISP_DISABLE) + return; /* * Bug #17235: G4X machine needs following steps @@ -1092,8 +1348,11 @@ i830_disable_vga_plane (xf86CrtcPtr crtc) usleep(30); } - OUTREG(VGACNTRL, VGA_DISP_DISABLE); - i830WaitForVblank(scrn); + while (!(INREG(vga_reg) & VGA_DISP_DISABLE)) { + vgacntrl |= VGA_DISP_DISABLE; + OUTREG(vga_reg, vgacntrl); + i830WaitForVblank(scrn); + } /* restore SR01 */ if (IS_G4X(intel)) { @@ -1230,6 +1489,396 @@ i830_crtc_disable(xf86CrtcPtr crtc, Bool disable_pipe) i830_disable_vga_plane (crtc); } +static void igdng_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (plane == 0) ? DSPABASE : DSPBBASE; + int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; + int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; + int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR; + int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR; + int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF; + int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1; + int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ; + int pf_win_pos = (pipe == 0) ? PFA_WIN_POS : PFB_WIN_POS; + int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B; + int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B; + int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B; + int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B; + int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B; + int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B; + uint32_t temp; + int tries = 5, i, j, n; + uint32_t pipe_bpc; + + if (intel_crtc->pipe != intel_crtc->plane) + FatalError("pipe/plane mismatch, aborting\n"); + + temp = INREG(pipeconf_reg); + pipe_bpc = temp & PIPE_BPC_MASK; + + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DPMSModeOn: + case DPMSModeStandby: + case DPMSModeSuspend: + + /* XXX no LVDS port force */ + + ErrorF("PCH DPLL enable\n"); + /* enable PCH DPLL */ + while (!((temp = INREG(pch_dpll_reg)) & DPLL_VCO_ENABLE)) { + OUTREG(pch_dpll_reg, temp | DPLL_VCO_ENABLE); + INREG(pch_dpll_reg); + usleep(10); + } + + ErrorF("PCH FDI RX PLL enable\n"); + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ + while (!((temp = INREG(fdi_rx_reg)) & FDI_RX_PLL_ENABLE)) { + temp &= (~FDI_DP_PORT_WIDTH_MASK | FDI_BPC_MASK); + temp |= FDI_RX_PLL_ENABLE | FDI_SEL_PCDCLK | FDI_DP_PORT_WIDTH_X4; + temp |= (pipe_bpc << 11); + OUTREG(fdi_rx_reg, temp); /* default 4 lanes */ + usleep(200); + } + + ErrorF("PCH FDI TX PLL enable\n"); + /* Enable CPU FDI TX PLL, always on for IGDNG */ + while (!((temp = INREG(fdi_tx_reg)) & FDI_TX_PLL_ENABLE)) { + temp &= (~FDI_DP_PORT_WIDTH_MASK); + temp |= FDI_TX_PLL_ENABLE | FDI_DP_PORT_WIDTH_X4; + OUTREG(fdi_tx_reg, temp); + usleep(100); + } + +#if 0 + ErrorF("PFIT enable\n"); + /* Enable panel fitting for LVDS */ + for (i = 0; i < xf86_config->num_output; i++) { + extern DisplayModePtr i830_lvds_panel_fixed_mode(xf86OutputPtr output); + xf86OutputPtr output = xf86_config->output[i]; + if (output->crtc == crtc) { + I830OutputPrivatePtr iout = output->driver_private; + if (iout->type == I830_OUTPUT_LVDS) { + DisplayModePtr mode = i830_lvds_panel_fixed_mode(output); + temp = INREG(pf_ctl_reg); + + /* filter force */ + temp &= ~0x00c00000; + temp |= 0x00800000; + + OUTREG(pf_ctl_reg, temp | PF_ENABLE); + + /* currently full aspect */ + OUTREG(pf_win_pos, 0); + + OUTREG(pf_win_size, (mode->HDisplay << 16) | + (mode->VDisplay)); + break; + } + } + } +#endif + + ErrorF("Pipe enable\n"); + /* Enable CPU pipe */ + while (!((temp = INREG(pipeconf_reg)) & PIPEACONF_ENABLE)) { + OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE); + INREG(pipeconf_reg); + usleep(100); + } + + ErrorF("Plane enable\n"); + /* configure and enable CPU plane */ + while (!((temp = INREG(dspcntr_reg)) & DISPLAY_PLANE_ENABLE)) { + OUTREG(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + OUTREG(dspbase_reg, INREG(dspbase_reg)); + usleep(10); + } + /* twice, like the BIOS */ + OUTREG(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + + /* Train FDI. */ + + ErrorF("FDI TX enable\n"); + /* enable CPU FDI TX and PCH FDI RX */ + while (!((temp = INREG(fdi_tx_reg)) & FDI_TX_ENABLE)) { + temp |= FDI_TX_ENABLE; + temp |= FDI_DP_PORT_WIDTH_X4; /* default */ + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + OUTREG(fdi_tx_reg, temp); + usleep(10); + } + + ErrorF("FDI RX enable\n"); + while (!((temp = INREG(fdi_rx_reg)) & FDI_RX_ENABLE)) { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + OUTREG(fdi_rx_reg, temp | FDI_RX_ENABLE); + usleep(10); + } + + usleep(150); + + ErrorF("FDI link train 1 start\n"); + /* unmask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + while ((temp = INREG(fdi_rx_imr_reg)) & + (FDI_RX_SYMBOL_LOCK|FDI_RX_BIT_LOCK)) { + temp &= ~(FDI_RX_SYMBOL_LOCK | FDI_RX_BIT_LOCK); + OUTREG(fdi_rx_imr_reg, temp); + usleep(150); + } + + + ErrorF("FDI link train 1 wait\n"); + for (j = 0; j < tries; j++) { + temp = INREG(fdi_rx_iir_reg); + if (temp & FDI_RX_BIT_LOCK) + break; + usleep(200); + } + if (j == tries) + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "train 1 failed\n"); + + ErrorF("FDI link train 2 start TX\n"); + while (!((temp = INREG(fdi_tx_reg)) & FDI_LINK_TRAIN_PATTERN_2)) { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + OUTREG(fdi_tx_reg, temp); + usleep(10); + } + + ErrorF("FDI link train 2 start TX\n"); + while (!((temp = INREG(fdi_rx_reg)) & FDI_LINK_TRAIN_PATTERN_2)) { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + OUTREG(fdi_rx_reg, temp); + usleep(10); + } + + usleep(500); + + ErrorF("FDI link train 2 wait\n"); + for (j = 0; j < tries; j++) { + temp = INREG(fdi_rx_iir_reg); + if (temp & FDI_RX_SYMBOL_LOCK) + break; + usleep(200); + } + if (j == tries) + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "train 2 failed\n"); + + usleep(500); + + ErrorF("transcoder timing\n"); + /* set transcoder timing */ + OUTREG(trans_htot_reg, INREG(cpu_htot_reg)); + OUTREG(trans_hblank_reg, INREG(cpu_hblank_reg)); + OUTREG(trans_hsync_reg, INREG(cpu_hsync_reg)); + + OUTREG(trans_vtot_reg, INREG(cpu_vtot_reg)); + OUTREG(trans_vblank_reg, INREG(cpu_vblank_reg)); + OUTREG(trans_vsync_reg, INREG(cpu_vsync_reg)); + + /* enable normal */ + + ErrorF("FDI TX link normal\n"); + while (((temp = INREG(fdi_tx_reg)) & FDI_LINK_TRAIN_NONE) != FDI_LINK_TRAIN_NONE) { + temp &= ~FDI_LINK_TRAIN_NONE; + OUTREG(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE | + FDI_TX_ENHANCE_FRAME_ENABLE); + usleep(10); + } + + ErrorF("FDI RX link normal\n"); + while (((temp = INREG(fdi_rx_reg)) & FDI_LINK_TRAIN_NONE) != FDI_LINK_TRAIN_NONE) { + temp &= ~FDI_LINK_TRAIN_NONE; + OUTREG(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE | + FDI_RX_ENHANCE_FRAME_ENABLE); + usleep(10); + } + + usleep(500); + + ErrorF("transcoder enable\n"); + /* enable transcoder */ + n = 0; + while (!((temp = INREG(transconf_reg)) & TRANS_STATE_ENABLE)) { + temp |= TRANS_ENABLE; + temp &= ~FDI_BPC_MASK; + temp |= pipe_bpc; + OUTREG(transconf_reg, temp | TRANS_ENABLE); + n++; + usleep(500); + if (n > 20) { + ErrorF("aborting transcoder %x enable\n", transconf_reg); + break; + } + } + + /* wait one idle pattern time */ + usleep(100); + + ErrorF("LUT load\n"); + i830_crtc_load_lut(crtc); + + ErrorF("DPMS on done\n"); + + break; + case DPMSModeOff: + + ErrorF("Plane disable\n"); + /* Disable display plane */ + while ((temp = INREG(dspcntr_reg)) & DISPLAY_PLANE_ENABLE) { + OUTREG(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + OUTREG(dspbase_reg, INREG(dspbase_reg)); + INREG(dspbase_reg); + usleep(10); + } + i830_disable_vga_plane (crtc); + + ErrorF("Pipe disable\n"); + /* disable cpu pipe, disable after all planes disabled */ + temp = INREG(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + OUTREG(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + INREG(pipeconf_reg); + n = 0; + /* wait for cpu pipe off, pipe state */ + while ((INREG(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0) { + n++; + if (n < 60) { + usleep(5000); + continue; + } else { + ErrorF("aborting pipeconf disable early\n"); + break; + } + } + } + + ErrorF("PFIT disable\n"); + /* Disable PF */ + while ((temp = INREG(pf_ctl_reg)) & PF_ENABLE) { + OUTREG(pf_ctl_reg, temp & ~PF_ENABLE); + usleep(10); + } + OUTREG(pf_win_size, 0); + + ErrorF("FDI TX disable\n"); + /* disable CPU FDI tx and PCH FDI rx */ + while ((temp = INREG(fdi_tx_reg)) & FDI_TX_ENABLE) { + OUTREG(fdi_tx_reg, temp & ~FDI_TX_ENABLE); + usleep(10); + } + + ErrorF("FDI RX disable\n"); + while ((temp = INREG(fdi_rx_reg)) & FDI_RX_ENABLE) { + OUTREG(fdi_rx_reg, temp & ~FDI_RX_ENABLE); + usleep(10); + } + + usleep(100); + + ErrorF("FDI TX train 1 preload\n"); + /* still set train pattern 1 */ + temp = INREG(fdi_tx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + OUTREG(fdi_tx_reg, temp); + + ErrorF("FDI RX train 1 preload\n"); + temp = INREG(fdi_rx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + OUTREG(fdi_rx_reg, temp); + + usleep(100); + + ErrorF("LVDS port force off\n"); + if (i830PipeHasType(crtc, I830_OUTPUT_LVDS)) { + while ((temp = INREG(PCH_LVDS)) & PORT_ENABLE) { + OUTREG(PCH_LVDS, temp & ~PORT_ENABLE); + usleep(100); + } + } + + ErrorF("Transcoder disable\n"); + /* disable PCH transcoder */ + temp = INREG(transconf_reg); + if ((temp & TRANS_STATE_ENABLE) != 0) { + OUTREG(transconf_reg, temp & ~TRANS_ENABLE); + INREG(transconf_reg); + n = 0; + /* wait for PCH transcoder off, transcoder state */ + while ((INREG(transconf_reg) & TRANS_STATE_ENABLE) != 0) { + n++; + if (n < 600) { + usleep(500); + continue; + } else { + ErrorF("aborting transcoder disable early, 0x%08x\n", INREG(transconf_reg)); + break; + } + } + } + + ErrorF("PCH DPLL disable\n"); + /* disable PCH DPLL */ + while ((temp = INREG(pch_dpll_reg)) & DPLL_VCO_ENABLE) { + OUTREG(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE); + usleep(10); + } + + ErrorF("FDI RX PLL PCD disable\n"); + while ((temp = INREG(fdi_rx_reg)) & FDI_SEL_PCDCLK) { + temp &= ~FDI_SEL_PCDCLK; + OUTREG(fdi_rx_reg, temp); + usleep(10); + } + + ErrorF("FDI RX PLL disable\n"); + while ((temp = INREG(fdi_rx_reg)) & FDI_RX_PLL_ENABLE) { + temp &= ~FDI_RX_PLL_ENABLE; + OUTREG(fdi_rx_reg, temp); + usleep(10); + } + + ErrorF("FDI TX PLL disable\n"); + while ((temp = INREG(fdi_tx_reg)) & FDI_RX_PLL_ENABLE) { + OUTREG(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE); + usleep(10); + } + + ErrorF("DPMS off done\n"); + /* Wait for the clocks to turn off. */ + usleep(150); + + break; + } +} + /** * Sets the power management mode of the pipe and plane. * @@ -1237,7 +1886,7 @@ i830_crtc_disable(xf86CrtcPtr crtc, Bool disable_pipe) * on appropriately at the same time as we're turning the pipe off/on. */ static void -i830_crtc_dpms(xf86CrtcPtr crtc, int mode) +i9xx_crtc_dpms(xf86CrtcPtr crtc, int mode) { ScrnInfoPtr scrn = crtc->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); @@ -1261,6 +1910,19 @@ i830_crtc_dpms(xf86CrtcPtr crtc, int mode) intel_crtc->enabled = FALSE; break; } +} + +static void +i830_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + + if (IS_IGDNG(intel)) + igdng_crtc_dpms(crtc, mode); + else + i9xx_crtc_dpms(crtc, mode); intel_crtc->dpms_mode = mode; } @@ -1331,6 +1993,15 @@ static Bool i830_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode) { + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); + + if (IS_IGDNG(intel)) { + /* FDI link clock is fixed at 2.7G */ + if (mode->Clock * 3 > 27000 * 4) + return MODE_CLOCK_HIGH; + } + return TRUE; } @@ -1479,6 +2150,48 @@ i830_update_dsparb(ScrnInfoPtr scrn) OUTREG(DSPARB, planea_entries << DSPARB_AEND_SHIFT); } +struct fdi_m_n { + CARD32 tu; + CARD32 gmch_m; + CARD32 gmch_n; + CARD32 link_m; + CARD32 link_n; +}; + +static void +fdi_reduce_ratio(CARD32 *num, CARD32 *den) +{ + while (*num > 0xffffff || *den > 0xffffff) { + *num >>= 1; + *den >>= 1; + } +} + +#define DATA_N 0x800000 +#define LINK_N 0x80000 + +static void +igdng_compute_m_n(int bits_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct fdi_m_n *m_n) +{ + uint64_t temp; + + m_n->tu = 64; /* default size */ + + temp = (uint64_t) DATA_N * pixel_clock; + temp = temp / link_clock; + m_n->gmch_m = (temp * bits_per_pixel) / nlanes; + m_n->gmch_m >>= 3; /* convert to bytes per pixel */ + m_n->gmch_n = DATA_N; + fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + + temp = (uint64_t) LINK_N * pixel_clock; + m_n->link_m = temp / link_clock; + m_n->link_n = LINK_N; + fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); +} + /** * Sets up registers for the given mode/adjusted_mode pair. * @@ -1521,6 +2234,18 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE; const intel_limit_t *limit; + struct fdi_m_n m_n = {0}; + int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1; + int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1; + int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1; + int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1; + int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0; + int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; + int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; + int lvds_reg = LVDS; + uint32_t temp; + int sdvo_pixel_multiply; + /* Set up some convenient bools for what outputs are connected to * our pipe, used in DPLL setup. */ @@ -1533,6 +2258,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, switch (intel_output->type) { case I830_OUTPUT_LVDS: + ErrorF("is lvds\n"); is_lvds = TRUE; lvds_bits = intel_output->lvds_bits; break; @@ -1551,6 +2277,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, is_tv = TRUE; break; case I830_OUTPUT_ANALOG: + ErrorF("is crt\n"); is_crt = TRUE; break; } @@ -1568,6 +2295,8 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, "using SSC reference clock of %d MHz\n", refclk / 1000); } else if (IS_I9XX(intel)) { refclk = 96000; + if (IS_IGDNG(intel)) + refclk = 120000; /* 120Mhz refclk */ } else { refclk = 48000; } @@ -1610,12 +2339,46 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, } } + if (IS_IGDNG(intel)) { + int bpp = 24; + if (is_lvds) { + uint32_t lvds_reg = INREG(PCH_LVDS); + + if (!((lvds_reg & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)) + bpp = 18; + } + + igdng_compute_m_n(bpp, 4, /* lane num 4 */ + adjusted_mode->Clock, + 270000, /* lane clock */ + &m_n); + ErrorF("bpp %d\n", bpp / 3); + intel_crtc->bpc = bpp / 3; + } + + if (IS_IGDNG(intel)) { + uint32_t temp; + + temp = INREG(PCH_DREF_CONTROL); + /* Always enable nonspread source */ + temp &= ~DREF_NONSPREAD_SOURCE_MASK; + temp |= DREF_NONSPREAD_SOURCE_ENABLE; + OUTREG(PCH_DREF_CONTROL, temp); + temp = INREG(PCH_DREF_CONTROL); + + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_ENABLE; + OUTREG(PCH_DREF_CONTROL, temp); + temp = INREG(PCH_DREF_CONTROL); + } + if (IS_IGD(intel)) fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2; else fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - dpll = DPLL_VGA_MODE_DIS; + if (!IS_IGDNG(intel)) + dpll = DPLL_VGA_MODE_DIS; if (IS_I9XX(intel)) { if (is_lvds) dpll |= DPLLB_MODE_LVDS; @@ -1624,11 +2387,12 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, if (is_sdvo) { dpll |= DPLL_DVO_HIGH_SPEED; + sdvo_pixel_multiply = adjusted_mode->Clock / mode->Clock; if ((IS_I945G(intel) || IS_I945GM(intel) || IS_G33CLASS(intel))) - { - int sdvo_pixel_multiply = adjusted_mode->Clock / mode->Clock; dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - } + else if (IS_IGDNG(intel)) + dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; + } /* compute bitmask from p1 value */ @@ -1650,7 +2414,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; break; } - if (IS_I965G(intel) && !IS_GM45(intel)) + if (IS_I965G(intel) && !IS_GM45(intel) && !IS_IGDNG(intel)) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); } else { if (is_lvds) { @@ -1680,6 +2444,10 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; + /* this is "must be enabled" in the docs, but not set by bios */ + if (IS_IGDNG(intel)) + dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; + switch (scrn->bitsPerPixel) { case 8: dspcntr |= DISPPLANE_8BPP; @@ -1697,10 +2465,14 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, FatalError("unknown display bpp\n"); } - if (pipe == 0) - dspcntr |= DISPPLANE_SEL_PIPE_A; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; + /* IGDNG's plane is forced to pipe, bit 24 is to + enable color space conversion */ + if (!IS_IGDNG(intel)) { + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + } if (IS_I965G(intel) && i830_display_tiled(crtc)) dspcntr |= DISPLAY_PLANE_TILED; @@ -1719,12 +2491,20 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, else pipeconf &= ~PIPEACONF_DOUBLE_WIDE; } + + if (IS_IGDNG(intel)) { + if (intel_crtc->bpc == 6) + pipeconf |= (1 << 6); /* 0 is 8bpc */ + if (intel_crtc->bpc != 8) + pipeconf |= (1 << 4); /* enable dithering */ + } + /* * This "shouldn't" be needed as the dpms on code * will be run after the mode is set. On 9xx, it helps. * On 855, it can lock up the chip (and the entire machine) */ - if (!IS_I85X (intel)) + if (!IS_I85X (intel) && !IS_IGDNG(intel)) { dspcntr |= DISPLAY_PLANE_ENABLE; pipeconf |= PIPEACONF_ENABLE; @@ -1732,7 +2512,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, } /* Disable the panel fitter if it was on our pipe */ - if (i830_panel_fitter_pipe (intel) == pipe) + if (!IS_IGDNG(intel) && i830_panel_fitter_pipe (intel) == pipe) OUTREG(PFIT_CONTROL, 0); if (intel->debug_modes) { @@ -1747,6 +2527,12 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, i830PrintPll(scrn, "chosen", &clock); } + /* assign to IGDNG registers */ + if (IS_IGDNG(intel)) { + fp_reg = pch_fp_reg; + dpll_reg = pch_dpll_reg; + } + if (dpll & DPLL_VCO_ENABLE) { OUTREG(fp_reg, fp); @@ -1761,9 +2547,17 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, */ if (is_lvds) { - uint32_t lvds = INREG(LVDS); + uint32_t lvds; + + if (IS_IGDNG(intel)) + lvds_reg = PCH_LVDS; - lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; + lvds = INREG(lvds_reg); + lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + if (IS_IGDNG(intel)) + lvds |= (pipe == 1) ? LVDS_PIPEB_SELECT : 0; + else + lvds |= LVDS_PIPEB_SELECT; /* Set the B0-B3 data pairs corresponding to whether we're going to * set the DPLLs for dual-channel mode or not. */ @@ -1799,17 +2593,18 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, lvds |= lvds_bits; - OUTREG(LVDS, lvds); - POSTING_READ(LVDS); + OUTREG(lvds_reg, lvds); + POSTING_READ(lvds_reg); } OUTREG(fp_reg, fp); +/* OUTREG(fp_reg + 4, fp); RHEL had this... wtf? */ OUTREG(dpll_reg, dpll); POSTING_READ(dpll_reg); /* Wait for the clocks to stabilize. */ usleep(150); - if (IS_I965G(intel)) { + if (IS_I965G(intel) && !IS_IGDNG(intel)) { int sdvo_pixel_multiply = adjusted_mode->Clock / mode->Clock; OUTREG(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); @@ -1832,7 +2627,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, ((adjusted_mode->CrtcHSyncEnd - 1) << 16)); OUTREG(vtot_reg, (adjusted_mode->CrtcVDisplay - 1) | ((adjusted_mode->CrtcVTotal - 1) << 16)); - + OUTREG(vblank_reg, (adjusted_mode->CrtcVBlankStart - 1) | ((adjusted_mode->CrtcVBlankEnd - 1) << 16)); OUTREG(vsync_reg, (adjusted_mode->CrtcVSyncStart - 1) | @@ -1840,13 +2635,37 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, /* pipesrc and dspsize control the size that is scaled from, which should * always be the user's requested size. */ - OUTREG(dspsize_reg, ((mode->VDisplay - 1) << 16) | (mode->HDisplay - 1)); - OUTREG(dsppos_reg, 0); + if (!IS_IGDNG(intel)) { + OUTREG(dspsize_reg, ((mode->VDisplay - 1) << 16) | (mode->HDisplay - 1)); + OUTREG(dsppos_reg, 0); + } OUTREG(pipesrc_reg, ((mode->HDisplay - 1) << 16) | (mode->VDisplay - 1)); + + if (IS_IGDNG(intel)) { + OUTREG(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m); + OUTREG(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n); + OUTREG(link_m1_reg, m_n.link_m); + OUTREG(link_n1_reg, m_n.link_n); + +#if 0 + /* enable FDI RX PLL too */ + /* XXX this doesn't work */ + temp = INREG(fdi_rx_reg); + OUTREG(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE); + usleep(200); +#endif + } + OUTREG(pipeconf_reg, pipeconf); POSTING_READ(pipeconf_reg); i830WaitForVblank(scrn); + if (IS_IGDNG(intel)) { + /* enable address swizzle for tiling buffer */ + temp = INREG(DISP_ARB_CTL); + OUTREG(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING); + } + OUTREG(dspcntr_reg, dspcntr); /* Flush the plane changes */ i830PipeSetBase(crtc, x, y); @@ -1869,6 +2688,11 @@ i830_crtc_load_lut(xf86CrtcPtr crtc) if (!crtc->enabled) return; + /* use legacy palette for IGDNG */ + if (IS_IGDNG(intel)) + palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A : + LGC_PALETTE_B; + for (i = 0; i < 256; i++) { OUTREG(palreg + 4 * i, (intel_crtc->lut_r[i] << 16) | @@ -2024,6 +2848,12 @@ i830DescribeOutputConfiguration(ScrnInfoPtr scrn) INREG(PIPEBCONF); Bool hw_plane_enable = (dspcntr & DISPLAY_PLANE_ENABLE) != 0; Bool hw_pipe_enable = (pipeconf & PIPEACONF_ENABLE) != 0; + int pipe; + + if (IS_IGDNG(intel)) + pipe = intel_crtc->plane; + else + pipe = !!(dspcntr & DISPPLANE_SEL_PIPE_MASK); xf86DrvMsg(scrn->scrnIndex, X_INFO, " Pipe %c is %s\n", @@ -2032,7 +2862,7 @@ i830DescribeOutputConfiguration(ScrnInfoPtr scrn) " Display plane %c is now %s and connected to pipe %c.\n", 'A' + intel_crtc->plane, hw_plane_enable ? "enabled" : "disabled", - dspcntr & DISPPLANE_SEL_PIPE_MASK ? 'B' : 'A'); + 'A' + pipe); if (hw_pipe_enable != crtc->enabled) { xf86DrvMsg(scrn->scrnIndex, X_WARNING, " Hardware claims pipe %c is %s while software " @@ -2235,19 +3065,22 @@ i830_crtc_clock_get(ScrnInfoPtr scrn, xf86CrtcPtr crtc) return 0; } - if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) + if (IS_IGDNG(intel)) + i9xx_clock(120000, &clock); + else if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) intel_clock(intel, 100000, &clock); else intel_clock(intel, 96000, &clock); } else { - Bool is_lvds = (pipe == 1) && (INREG(LVDS) & LVDS_PORT_EN); + CARD32 lvds = IS_IGDNG(intel) ? PCH_LVDS : LVDS; + Bool is_lvds = (pipe == 1) && (INREG(lvds) & LVDS_PORT_EN); if (is_lvds) { clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> DPLL_FPA01_P1_POST_DIV_SHIFT); /* if LVDS is dual-channel, p2 = 7 */ - if ((INREG(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + if ((INREG(lvds) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) clock.p2 = 7; else clock.p2 = 14; diff --git a/src/i830_driver.c b/src/i830_driver.c index 9da62204..a7933e93 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -659,7 +659,29 @@ static void I830SetupOutputs(ScrnInfoPtr scrn) if (IS_MOBILE(intel) && !IS_I830(intel)) i830_lvds_init(scrn); - if (IS_I9XX(intel)) { + if (IS_IGDNG(intel)) { + int found; + + if (INREG(HDMIB) & PORT_DETECTED) { + /* check SDVOB */ + /* found = intel_sdvo_init(dev, HDMIB); */ + found = 0; + if (!found) + i830_hdmi_init(scrn, HDMIB); + } + + if (INREG(HDMIC) & PORT_DETECTED) + i830_hdmi_init(scrn, HDMIC); + + if (INREG(HDMID) & PORT_DETECTED) + i830_hdmi_init(scrn, HDMID); + + /* Disable DP by force */ + OUTREG(PCH_DP_B, INREG(PCH_DP_B) & ~PORT_ENABLE); + OUTREG(PCH_DP_C, INREG(PCH_DP_C) & ~PORT_ENABLE); + OUTREG(PCH_DP_D, INREG(PCH_DP_D) & ~PORT_ENABLE); + + } else if (IS_I9XX(intel)) { Bool found = FALSE; if ((INREG(SDVOB) & SDVO_DETECTED)) { found = i830_sdvo_init(scrn, SDVOB); @@ -678,7 +700,7 @@ static void I830SetupOutputs(ScrnInfoPtr scrn) } else { i830_dvo_init(scrn); } - if (IS_I9XX(intel) && IS_MOBILE(intel)) + if (IS_I9XX(intel) && IS_MOBILE(intel) && !IS_IGDNG(intel)) i830_tv_init(scrn); for (o = 0; o < config->num_output; o++) { @@ -1617,6 +1639,30 @@ static Bool I830PreInit(ScrnInfoPtr scrn, int flags) } if (!intel->use_drm_mode) { + /* console hack, stolen from G80 */ + if (IS_IGDNG(intel)) { + if (xf86LoadSubModule(scrn, "int10")) { + intel->int10 = xf86InitInt10(pEnt->index); + if (intel->int10) { + intel->int10->num = 0x10; + intel->int10->ax = 0x4f03; + intel->int10->bx = + intel->int10->cx = + intel->int10->dx = 0; + xf86ExecX86int10(intel->int10); + intel->int10Mode = intel->int10->bx & 0x3fff; + xf86DrvMsg(scrn->scrnIndex, X_PROBED, + "Console VGA mode is 0x%x\n", intel->int10Mode); + } else { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Failed int10 setup, VT switch won't work\n"); + } + } else { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Failed to load int10module, ironlake vt switch broken"); + } + } + I830UnmapMMIO(scrn); /* We won't be using the VGA access after the probe. */ @@ -1760,6 +1806,9 @@ static Bool SaveHWState(ScrnInfoPtr scrn) vgaRegPtr vgaReg = &hwp->SavedReg; int i; + if (IS_IGDNG(intel)) + return TRUE; + if (intel->fb_compression) { intel->saveFBC_CFB_BASE = INREG(FBC_CFB_BASE); intel->saveFBC_LL_BASE = INREG(FBC_LL_BASE); @@ -1886,6 +1935,9 @@ static Bool RestoreHWState(ScrnInfoPtr scrn) vgaRegPtr vgaReg = &hwp->SavedReg; int i; + if (IS_IGDNG(intel)) + return TRUE; + DPRINTF(PFX, "RestoreHWState\n"); /* Disable outputs */ @@ -2443,7 +2495,8 @@ I830SwapPipes(ScrnInfoPtr scrn) * alone in that case. * Also make sure the DRM can handle the swap. */ - if (I830LVDSPresent(scrn) && !IS_I965GM(intel) && !IS_GM45(intel)) { + if (I830LVDSPresent(scrn) && !IS_I965GM(intel) && !IS_GM45(intel) && + !IS_IGDNG(intel)) { xf86DrvMsg(scrn->scrnIndex, X_INFO, "adjusting plane->pipe " "mappings to allow for framebuffer compression\n"); for (c = 0; c < config->num_crtc; c++) { @@ -2848,6 +2901,18 @@ static void I830LeaveVT(int scrnIndex, int flags) i830_stop_ring(scrn, TRUE); } + /* console restore hack */ + if (IS_IGDNG(intel) && intel->int10 && intel->int10Mode) { + xf86Int10InfoPtr int10 = intel->int10; + + /* Use int10 to restore the console mode */ + int10->num = 0x10; + int10->ax = 0x4f02; + int10->bx = intel->int10Mode | 0x8000; + int10->cx = int10->dx = 0; + xf86ExecX86int10(int10); + } + } intel_batch_teardown(scrn); diff --git a/src/i830_hdmi.c b/src/i830_hdmi.c index d2f95376..3b04b0f5 100644 --- a/src/i830_hdmi.c +++ b/src/i830_hdmi.c @@ -136,6 +136,22 @@ i830_hdmi_restore(xf86OutputPtr output) OUTREG(dev_priv->output_reg, dev_priv->save_SDVO); } +static xf86OutputStatus +igdng_hdmi_detect(xf86OutputPtr output) +{ + DisplayModePtr modes; + xf86OutputStatus status; + + modes = i830_ddc_get_modes(output); + + if (modes == NULL) + status = XF86OutputStatusDisconnected; + else + status = XF86OutputStatusConnected; + + return status; +} + /** * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect HDMI connection. * @@ -155,6 +171,9 @@ i830_hdmi_detect(xf86OutputPtr output) dev_priv->has_hdmi_sink = FALSE; + if (IS_IGDNG(intel)) + return igdng_hdmi_detect(output); + /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written 0xd. * Failure to do so will result in spurious interrupts being * generated on the port when a cable is not attached. @@ -332,9 +351,27 @@ i830_hdmi_init(ScrnInfoPtr scrn, int output_reg) xf86OutputPtr output; I830OutputPrivatePtr intel_output; struct i830_hdmi_priv *dev_priv; + const char *name; - output = xf86OutputCreate(scrn, &i830_hdmi_output_funcs, - (output_reg == SDVOB) ? "HDMI-1" : "HDMI-2"); + switch (output_reg) { + case SDVOB: + name = "HDMI-1"; + break; + case SDVOC: + name = "HDMI-2"; + break; + case HDMIB: + name = "HDMI-3"; + break; + case HDMIC: + name = "HDMI-4"; + break; + case HDMID: + name = "HDMI-5"; + break; + } + + output = xf86OutputCreate(scrn, &i830_hdmi_output_funcs, name); if (!output) return; intel_output = xnfcalloc(sizeof (I830OutputPrivateRec) + @@ -359,10 +396,15 @@ i830_hdmi_init(ScrnInfoPtr scrn, int output_reg) /* Set up the DDC bus. */ if (output_reg == SDVOB) I830I2CInit(scrn, &intel_output->pDDCBus, GPIOE, "HDMIDDC_B"); - else + else if (output_reg == SDVOC) I830I2CInit(scrn, &intel_output->pDDCBus, GPIOD, "HDMIDDC_C"); + else if (output_reg == HDMIB) + I830I2CInit(scrn, &intel_output->pDDCBus, PCH_GPIOE, "HDMIB"); + else if (output_reg == HDMIC) + I830I2CInit(scrn, &intel_output->pDDCBus, PCH_GPIOD, "HDMIC"); + else if (output_reg == HDMID) + I830I2CInit(scrn, &intel_output->pDDCBus, PCH_GPIOF, "HDMID"); xf86DrvMsg(scrn->scrnIndex, X_INFO, - "HDMI output %d detected\n", - (output_reg == SDVOB) ? 1 : 2); + "%s output detected\n", output->name); } diff --git a/src/i830_lvds.c b/src/i830_lvds.c index b33b461d..34b3cbce 100644 --- a/src/i830_lvds.c +++ b/src/i830_lvds.c @@ -140,6 +140,10 @@ i830_set_lvds_backlight_method(xf86OutputPtr output) if (i830_kernel_backlight_available(output)) { method = BCM_KERNEL; +#if 0 + } else if (IS_IGDNG(intel)) { + method = BCM_IRONLAKE_NULL; +#endif } else if (IS_I965GM(intel) || IS_GM45(intel)) { blc_pwm_ctl2 = INREG(BLC_PWM_CTL2); if (blc_pwm_ctl2 & BLM_LEGACY_MODE2) @@ -161,11 +165,16 @@ i830_lvds_set_backlight_native(xf86OutputPtr output, int level) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); - uint32_t blc_pwm_ctl; + uint32_t blc_pwm_ctl, reg; - blc_pwm_ctl = INREG(BLC_PWM_CTL); + if (IS_IGDNG(intel)) + reg = BLC_PWM_CPU_CTL; + else + reg = BLC_PWM_CTL; + + blc_pwm_ctl = INREG(reg); blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; - OUTREG(BLC_PWM_CTL, blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); + OUTREG(reg, blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); } static int @@ -185,8 +194,15 @@ i830_lvds_get_backlight_max_native(xf86OutputPtr output) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); - uint32_t pwm_ctl = INREG(BLC_PWM_CTL); - int val; + uint32_t pwm_ctl; + int val, reg; + + if (IS_IGDNG(intel)) + reg = BLC_PWM_PCH_CTL2; + else + reg = BLC_PWM_CTL; + + pwm_ctl = INREG(reg); if (IS_I965GM(intel) || IS_GM45(intel)) { val = ((pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK2) >> @@ -278,6 +294,18 @@ i830_lvds_get_backlight_max_combo(xf86OutputPtr output) return i830_lvds_get_backlight_max_native(output) >> 1; } +/* null methods */ +static int +i830_lvds_get_backlight_null(xf86OutputPtr output) +{ + return 1; +} + +static void +i830_lvds_set_backlight_null(xf86OutputPtr output, int level) +{ +} + /* * Kernel methods */ @@ -482,11 +510,33 @@ i830SetLVDSPanelPower(xf86OutputPtr output, Bool on) struct i830_lvds_priv *dev_priv = intel_output->dev_priv; ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); - uint32_t pp_status; + uint32_t pp_status, ctl_reg, status_reg; + + if (IS_IGDNG(intel)) { + ctl_reg = PCH_PP_CONTROL; + status_reg = PCH_PP_STATUS; + } else { + ctl_reg = PP_CONTROL; + status_reg = PP_STATUS; + } + + if (IS_IGDNG(intel)) { + CARD32 temp; + if (on) { + temp = INREG(PCH_LVDS); + OUTREG(PCH_LVDS, temp | PORT_ENABLE); + temp = INREG(PCH_LVDS); + } else { + temp = INREG(PCH_LVDS); + OUTREG(PCH_LVDS, temp & ~PORT_ENABLE); + temp = INREG(PCH_LVDS); + } + usleep(100); + } if (on) { /* if we're going from on->on, be aware to current level. */ - if ((INREG(PP_CONTROL) & POWER_TARGET_ON) && !dev_priv->dpmsoff) + if ((INREG(ctl_reg) & POWER_TARGET_ON) && !dev_priv->dpmsoff) dev_priv->backlight_duty_cycle = dev_priv->get_backlight(output); /* @@ -496,14 +546,14 @@ i830SetLVDSPanelPower(xf86OutputPtr output, Bool on) * controller for example), so on them, when turning LVDS back on, * they'll always re-maximize the brightness. */ - if (!(INREG(PP_CONTROL) & POWER_TARGET_ON) && + if (!(INREG(ctl_reg) & POWER_TARGET_ON) && dev_priv->backlight_duty_cycle == 0 && intel->backlight_control_method < BCM_KERNEL) dev_priv->backlight_duty_cycle = dev_priv->backlight_max; - OUTREG(PP_CONTROL, INREG(PP_CONTROL) | POWER_TARGET_ON); + OUTREG(ctl_reg, INREG(ctl_reg) | POWER_TARGET_ON); do { - pp_status = INREG(PP_STATUS); + pp_status = INREG(status_reg); } while ((pp_status & PP_ON) == 0); dev_priv->set_backlight(output, dev_priv->backlight_duty_cycle); @@ -513,13 +563,13 @@ i830SetLVDSPanelPower(xf86OutputPtr output, Bool on) * Only save the current backlight value if we're going from * on to off. */ - if ((INREG(PP_CONTROL) & POWER_TARGET_ON) && !dev_priv->dpmsoff) + if ((INREG(ctl_reg) & POWER_TARGET_ON) && !dev_priv->dpmsoff) dev_priv->backlight_duty_cycle = dev_priv->get_backlight(output); dev_priv->set_backlight(output, 0); - OUTREG(PP_CONTROL, INREG(PP_CONTROL) & ~POWER_TARGET_ON); + OUTREG(ctl_reg, INREG(ctl_reg) & ~POWER_TARGET_ON); do { - pp_status = INREG(PP_STATUS); + pp_status = INREG(status_reg); } while (pp_status & PP_ON); dev_priv->dpmsoff = TRUE; @@ -544,14 +594,29 @@ i830_lvds_save (xf86OutputPtr output) struct i830_lvds_priv *dev_priv = intel_output->dev_priv; ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); + uint32_t pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg, pwm_ctl_reg; + + if (IS_IGDNG(intel)) { + pp_on_reg = PCH_PP_ON_DELAYS; + pp_off_reg = PCH_PP_OFF_DELAYS; + pp_ctl_reg = PCH_PP_CONTROL; + pp_div_reg = PCH_PP_DIVISOR; + pwm_ctl_reg = BLC_PWM_CPU_CTL; + } else { + pp_on_reg = PP_ON_DELAYS; + pp_off_reg = PP_OFF_DELAYS; + pp_ctl_reg = PP_CONTROL; + pp_div_reg = PP_DIVISOR; + pwm_ctl_reg = BLC_PWM_CTL; + } if (IS_I965GM(intel) || IS_GM45(intel)) intel->saveBLC_PWM_CTL2 = INREG(BLC_PWM_CTL2); - intel->savePP_ON = INREG(PP_ON_DELAYS); - intel->savePP_OFF = INREG(PP_OFF_DELAYS); - intel->savePP_CONTROL = INREG(PP_CONTROL); - intel->savePP_DIVISOR = INREG(PP_DIVISOR); - intel->saveBLC_PWM_CTL = INREG(BLC_PWM_CTL); + intel->savePP_ON = INREG(pp_on_reg); + intel->savePP_OFF = INREG(pp_off_reg); + intel->savePP_CONTROL = INREG(pp_ctl_reg); + intel->savePP_DIVISOR = INREG(pp_div_reg); + intel->saveBLC_PWM_CTL = INREG(pwm_ctl_reg); if ((INREG(PP_CONTROL) & POWER_TARGET_ON) && !dev_priv->dpmsoff) dev_priv->backlight_duty_cycle = dev_priv->get_backlight(output); } @@ -561,14 +626,31 @@ i830_lvds_restore(xf86OutputPtr output) { ScrnInfoPtr scrn = output->scrn; intel_screen_private *intel = intel_get_screen_private(scrn); + uint32_t pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg; + uint32_t pwm_ctl_reg; + + if (IS_IGDNG(intel)) { + pp_on_reg = PCH_PP_ON_DELAYS; + pp_off_reg = PCH_PP_OFF_DELAYS; + pp_ctl_reg = PCH_PP_CONTROL; + pp_div_reg = PCH_PP_DIVISOR; + pwm_ctl_reg = BLC_PWM_CPU_CTL; + } else { + pp_on_reg = PP_ON_DELAYS; + pp_off_reg = PP_OFF_DELAYS; + pp_ctl_reg = PP_CONTROL; + pp_div_reg = PP_DIVISOR; + pwm_ctl_reg = BLC_PWM_CTL; + } if (IS_I965GM(intel) || IS_GM45(intel)) OUTREG(BLC_PWM_CTL2, intel->saveBLC_PWM_CTL2); - OUTREG(BLC_PWM_CTL, intel->saveBLC_PWM_CTL); - OUTREG(PP_ON_DELAYS, intel->savePP_ON); - OUTREG(PP_OFF_DELAYS, intel->savePP_OFF); - OUTREG(PP_DIVISOR, intel->savePP_DIVISOR); - OUTREG(PP_CONTROL, intel->savePP_CONTROL); + OUTREG(pwm_ctl_reg, intel->saveBLC_PWM_CTL); + OUTREG(pp_on_reg, intel->savePP_ON); + OUTREG(pp_off_reg, intel->savePP_OFF); + OUTREG(pp_div_reg, intel->savePP_DIVISOR); + OUTREG(pp_ctl_reg, intel->savePP_CONTROL); + if (intel->savePP_CONTROL & POWER_TARGET_ON) i830SetLVDSPanelPower(output, TRUE); else @@ -624,7 +706,7 @@ i830_lvds_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, } } - if (intel_crtc->pipe == 0) { + if (!IS_IGDNG(intel) && intel_crtc->pipe == 0) { xf86DrvMsg(scrn->scrnIndex, X_ERROR, "Can't support LVDS on pipe A\n"); return FALSE; @@ -1070,12 +1152,13 @@ static Atom backlight_atom; * or not at all. */ #define BACKLIGHT_CONTROL_NAME "BACKLIGHT_CONTROL" -#define NUM_BACKLIGHT_CONTROL_METHODS 4 +#define NUM_BACKLIGHT_CONTROL_METHODS 5 static char *backlight_control_names[] = { "native", "legacy", "combination", "kernel", + "null", }; static Atom backlight_control_atom; static Atom backlight_control_name_atoms[NUM_BACKLIGHT_CONTROL_METHODS]; @@ -1135,6 +1218,11 @@ i830_lvds_set_backlight_control(xf86OutputPtr output) dev_priv->backlight_max = i830_lvds_get_backlight_max_kernel(output); break; + case BCM_IRONLAKE_NULL: + dev_priv->set_backlight = i830_lvds_set_backlight_null; + dev_priv->get_backlight = i830_lvds_get_backlight_null; + dev_priv->backlight_max = 1; + break; default: /* * Should be impossible to get here unless the caller set a bogus @@ -1447,6 +1535,7 @@ i830_lvds_init(ScrnInfoPtr scrn) DisplayModePtr modes, scan; DisplayModePtr lvds_ddc_mode = NULL; struct i830_lvds_priv *dev_priv; + int gpio = GPIOC; if (!intel->integrated_lvds) { if (intel->debug_modes) @@ -1458,6 +1547,12 @@ i830_lvds_init(ScrnInfoPtr scrn) if (intel->quirk_flag & QUIRK_IGNORE_LVDS) return; + if (IS_IGDNG(intel)) { + if ((INREG(PCH_LVDS) & LVDS_DETECTED) == 0) + return; + gpio = PCH_GPIOC; + } + output = xf86OutputCreate (scrn, &i830_lvds_output_funcs, "LVDS"); if (!output) return; @@ -1470,6 +1565,8 @@ i830_lvds_init(ScrnInfoPtr scrn) } intel_output->type = I830_OUTPUT_LVDS; intel_output->pipe_mask = (1 << 1); + if (0 && IS_IGDNG(intel)) /* XXX put me back */ + intel_output->pipe_mask |= (1 << 0); intel_output->clone_mask = (1 << I830_OUTPUT_LVDS); output->driver_private = intel_output; @@ -1492,7 +1589,7 @@ i830_lvds_init(ScrnInfoPtr scrn) /* Set up the LVDS DDC channel. Most panels won't support it, but it can * be useful if available. */ - I830I2CInit(scrn, &intel_output->pDDCBus, GPIOC, "LVDSDDC_C"); + I830I2CInit(scrn, &intel_output->pDDCBus, gpio, "LVDSDDC_C"); if (intel->skip_panel_detect) { xf86DrvMsg(scrn->scrnIndex, X_INFO, @@ -1541,7 +1638,7 @@ i830_lvds_init(ScrnInfoPtr scrn) * turned on. If so, assume that whatever is currently programmed is the * correct mode. */ - if (!intel->lvds_fixed_mode) { + if (!intel->lvds_fixed_mode && !IS_IGDNG(intel)) { uint32_t lvds = INREG(LVDS); int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); @@ -1619,6 +1716,19 @@ found_mode: */ dev_priv->fitting_mode = FULL_ASPECT; + if (IS_IGDNG(intel)) { + CARD32 pwm; + /* make sure PWM is enabled */ + pwm = INREG(BLC_PWM_CPU_CTL2); + pwm |= (PWM_ENABLE | PWM_PIPE_B); + OUTREG(BLC_PWM_CPU_CTL2, pwm); + + pwm = INREG(BLC_PWM_PCH_CTL1); + pwm |= PWM_PCH_ENABLE; + OUTREG(BLC_PWM_PCH_CTL1, pwm); + } + + return; disable_exit: |