From 2c07245fb8f7f0a282282e5a9747e46defdb2cc7 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Fri, 5 Jun 2009 15:38:42 +0800 Subject: drm/i915: enable kernel modesetting on IGDNG This adds kernel mode setting on IGDNG with VGA output support. Note that suspend/resume doesn't work yet. Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 603 +++++++++++++++++++++++++++++++++-- 1 file changed, 574 insertions(+), 29 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c9d6f10ba92..2cd6ba6523d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -137,6 +137,8 @@ struct intel_limit { #define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS 7 #define INTEL_LIMIT_IGD_SDVO_DAC 8 #define INTEL_LIMIT_IGD_LVDS 9 +#define INTEL_LIMIT_IGDNG_SDVO_DAC 10 +#define INTEL_LIMIT_IGDNG_LVDS 11 /*The parameter is for SDVO on G4x platform*/ #define G4X_DOT_SDVO_MIN 25000 @@ -216,12 +218,43 @@ struct intel_limit { #define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 +/* IGDNG */ +/* as we calculate clock using (register_value + 2) for + N/M1/M2, so here the range value for them is (actual_value-2). + */ +#define IGDNG_DOT_MIN 25000 +#define IGDNG_DOT_MAX 350000 +#define IGDNG_VCO_MIN 1760000 +#define IGDNG_VCO_MAX 3510000 +#define IGDNG_N_MIN 1 +#define IGDNG_N_MAX 5 +#define IGDNG_M_MIN 79 +#define IGDNG_M_MAX 118 +#define IGDNG_M1_MIN 12 +#define IGDNG_M1_MAX 23 +#define IGDNG_M2_MIN 5 +#define IGDNG_M2_MAX 9 +#define IGDNG_P_SDVO_DAC_MIN 5 +#define IGDNG_P_SDVO_DAC_MAX 80 +#define IGDNG_P_LVDS_MIN 28 +#define IGDNG_P_LVDS_MAX 112 +#define IGDNG_P1_MIN 1 +#define IGDNG_P1_MAX 8 +#define IGDNG_P2_SDVO_DAC_SLOW 10 +#define IGDNG_P2_SDVO_DAC_FAST 5 +#define IGDNG_P2_LVDS_SLOW 14 /* single channel */ +#define IGDNG_P2_LVDS_FAST 7 /* double channel */ +#define IGDNG_P2_DOT_LIMIT 225000 /* 225Mhz */ + static bool intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); static bool intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); +static bool +intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *best_clock); static const intel_limit_t intel_limits[] = { { /* INTEL_LIMIT_I8XX_DVO_DAC */ @@ -383,9 +416,47 @@ static const intel_limit_t intel_limits[] = { .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW }, .find_pll = intel_find_best_PLL, }, - + { /* INTEL_LIMIT_IGDNG_SDVO_DAC */ + .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX }, + .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX }, + .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX }, + .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX }, + .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX }, + .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX }, + .p = { .min = IGDNG_P_SDVO_DAC_MIN, .max = IGDNG_P_SDVO_DAC_MAX }, + .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX }, + .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT, + .p2_slow = IGDNG_P2_SDVO_DAC_SLOW, + .p2_fast = IGDNG_P2_SDVO_DAC_FAST }, + .find_pll = intel_igdng_find_best_PLL, + }, + { /* INTEL_LIMIT_IGDNG_LVDS */ + .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX }, + .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX }, + .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX }, + .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX }, + .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX }, + .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX }, + .p = { .min = IGDNG_P_LVDS_MIN, .max = IGDNG_P_LVDS_MAX }, + .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX }, + .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT, + .p2_slow = IGDNG_P2_LVDS_SLOW, + .p2_fast = IGDNG_P2_LVDS_FAST }, + .find_pll = intel_igdng_find_best_PLL, + }, }; +static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc) +{ + const intel_limit_t *limit; + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_IGDNG_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_IGDNG_SDVO_DAC]; + + return limit; +} + static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -418,7 +489,9 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; const intel_limit_t *limit; - if (IS_G4X(dev)) { + if (IS_IGDNG(dev)) + limit = intel_igdng_limit(crtc); + else if (IS_G4X(dev)) { limit = intel_g4x_limit(crtc); } else if (IS_I9XX(dev) && !IS_IGD(dev)) { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) @@ -630,7 +703,64 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, } } } + return found; +} + +static bool +intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + intel_clock_t clock; + int max_n; + bool found; + int err_most = 47; + found = false; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if ((I915_READ(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, p1 */ + 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(dev, refclk, &clock); + if (!intel_PLL_is_valid(crtc, &clock)) + continue; + this_err = abs((10000 - (target*10000/clock.dot))); + if (this_err < err_most) { + *best_clock = clock; + err_most = this_err; + max_n = clock.n; + found = true; + /* found on first matching */ + goto out; + } + } + } + } + } +out: return found; } @@ -785,18 +915,292 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } +static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->pipe; + 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) ? DSPAADDR : DSPBADDR; + 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 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; + u32 temp; + int tries = 5, j; + /* 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 DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + DRM_DEBUG("crtc %d dpms on\n", pipe); + /* enable PCH DPLL */ + temp = I915_READ(pch_dpll_reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE); + I915_READ(pch_dpll_reg); + } -/** - * Sets the power management mode of the pipe and plane. - * - * This code should probably grow support for turning the cursor off and back - * on appropriately at the same time as we're turning the pipe off/on. - */ -static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ + temp = I915_READ(fdi_rx_reg); + I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE | + FDI_SEL_PCDCLK | + FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */ + I915_READ(fdi_rx_reg); + udelay(200); + + /* Enable CPU FDI TX PLL, always on for IGDNG */ + temp = I915_READ(fdi_tx_reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE); + I915_READ(fdi_tx_reg); + udelay(100); + } + + /* Enable CPU pipe */ + temp = I915_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) { + I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + I915_READ(pipeconf_reg); + udelay(100); + } + + /* configure and enable CPU plane */ + temp = I915_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + } + + /* enable CPU FDI TX and PCH FDI RX */ + temp = I915_READ(fdi_tx_reg); + temp |= FDI_TX_ENABLE; + temp |= FDI_DP_PORT_WIDTH_X4; /* default */ + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(fdi_tx_reg, temp); + I915_READ(fdi_tx_reg); + + temp = I915_READ(fdi_rx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE); + I915_READ(fdi_rx_reg); + + udelay(150); + + /* Train FDI. */ + /* umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + temp = I915_READ(fdi_rx_imr_reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + I915_WRITE(fdi_rx_imr_reg, temp); + I915_READ(fdi_rx_imr_reg); + udelay(150); + + temp = I915_READ(fdi_rx_iir_reg); + DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); + + if ((temp & FDI_RX_BIT_LOCK) == 0) { + for (j = 0; j < tries; j++) { + temp = I915_READ(fdi_rx_iir_reg); + DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_BIT_LOCK) + break; + udelay(200); + } + if (j != tries) + I915_WRITE(fdi_rx_iir_reg, + temp | FDI_RX_BIT_LOCK); + else + DRM_DEBUG("train 1 fail\n"); + } else { + I915_WRITE(fdi_rx_iir_reg, + temp | FDI_RX_BIT_LOCK); + DRM_DEBUG("train 1 ok 2!\n"); + } + temp = I915_READ(fdi_tx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + I915_WRITE(fdi_tx_reg, temp); + + temp = I915_READ(fdi_rx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + I915_WRITE(fdi_rx_reg, temp); + + udelay(150); + + temp = I915_READ(fdi_rx_iir_reg); + DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); + + if ((temp & FDI_RX_SYMBOL_LOCK) == 0) { + for (j = 0; j < tries; j++) { + temp = I915_READ(fdi_rx_iir_reg); + DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_SYMBOL_LOCK) + break; + udelay(200); + } + if (j != tries) { + I915_WRITE(fdi_rx_iir_reg, + temp | FDI_RX_SYMBOL_LOCK); + DRM_DEBUG("train 2 ok 1!\n"); + } else + DRM_DEBUG("train 2 fail\n"); + } else { + I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK); + DRM_DEBUG("train 2 ok 2!\n"); + } + DRM_DEBUG("train done\n"); + + /* set transcoder timing */ + I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg)); + I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg)); + I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg)); + + I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg)); + I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg)); + I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg)); + + /* enable PCH transcoder */ + temp = I915_READ(transconf_reg); + I915_WRITE(transconf_reg, temp | TRANS_ENABLE); + I915_READ(transconf_reg); + + while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0) + ; + + /* enable normal */ + + temp = I915_READ(fdi_tx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE | + FDI_TX_ENHANCE_FRAME_ENABLE); + I915_READ(fdi_tx_reg); + + temp = I915_READ(fdi_rx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE | + FDI_RX_ENHANCE_FRAME_ENABLE); + I915_READ(fdi_rx_reg); + + /* wait one idle pattern time */ + udelay(100); + + intel_crtc_load_lut(crtc); + + break; + case DRM_MODE_DPMS_OFF: + DRM_DEBUG("crtc %d dpms off\n", pipe); + + /* Disable the VGA plane that we never use */ + I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = I915_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + I915_READ(dspbase_reg); + } + + /* disable cpu pipe, disable after all planes disabled */ + temp = I915_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + I915_READ(pipeconf_reg); + /* wait for cpu pipe off, pipe state */ + while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0) + ; + } else + DRM_DEBUG("crtc %d is disabled\n", pipe); + + /* IGDNG-A : disable cpu panel fitter ? */ + temp = I915_READ(pf_ctl_reg); + if ((temp & PF_ENABLE) != 0) { + I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE); + I915_READ(pf_ctl_reg); + } + + /* disable CPU FDI tx and PCH FDI rx */ + temp = I915_READ(fdi_tx_reg); + I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE); + I915_READ(fdi_tx_reg); + + temp = I915_READ(fdi_rx_reg); + I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE); + I915_READ(fdi_rx_reg); + + /* still set train pattern 1 */ + temp = I915_READ(fdi_tx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(fdi_tx_reg, temp); + + temp = I915_READ(fdi_rx_reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(fdi_rx_reg, temp); + + /* disable PCH transcoder */ + temp = I915_READ(transconf_reg); + if ((temp & TRANS_ENABLE) != 0) { + I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE); + I915_READ(transconf_reg); + /* wait for PCH transcoder off, transcoder state */ + while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0) + ; + } + + /* disable PCH DPLL */ + temp = I915_READ(pch_dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE); + I915_READ(pch_dpll_reg); + } + + temp = I915_READ(fdi_rx_reg); + if ((temp & FDI_RX_PLL_ENABLE) != 0) { + temp &= ~FDI_SEL_PCDCLK; + temp &= ~FDI_RX_PLL_ENABLE; + I915_WRITE(fdi_rx_reg, temp); + I915_READ(fdi_rx_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } +} + +static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; - struct drm_i915_master_private *master_priv; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; @@ -805,7 +1209,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; - bool enabled; /* 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. @@ -890,6 +1293,26 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) udelay(150); break; } +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_master_private *master_priv; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + bool enabled; + + if (IS_IGDNG(dev)) + igdng_crtc_dpms(crtc, mode); + else + i9xx_crtc_dpms(crtc, mode); if (!dev->primary->master) return; @@ -947,6 +1370,12 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_device *dev = crtc->dev; + if (IS_IGDNG(dev)) { + /* FDI link clock is fixed at 2.7G */ + if (mode->clock * 3 > 27000 * 4) + return MODE_CLOCK_HIGH; + } return true; } @@ -1030,6 +1459,48 @@ static int intel_panel_fitter_pipe (struct drm_device *dev) return 1; } +struct fdi_m_n { + u32 tu; + u32 gmch_m; + u32 gmch_n; + u32 link_m; + u32 link_n; +}; + +static void +fdi_reduce_ratio(u32 *num, u32 *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 bytes_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct fdi_m_n *m_n) +{ + u64 temp; + + m_n->tu = 64; /* default size */ + + temp = (u64) DATA_N * pixel_clock; + temp = div_u64(temp, link_clock); + m_n->gmch_m = (temp * bytes_per_pixel) / nlanes; + m_n->gmch_n = DATA_N; + fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + + temp = (u64) LINK_N * pixel_clock; + m_n->link_m = div_u64(temp, link_clock); + m_n->link_n = LINK_N; + fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); +} + + static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -1063,6 +1534,16 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_connector *connector; const intel_limit_t *limit; int ret; + 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; + u32 temp; + int sdvo_pixel_multiply; drm_vblank_pre_modeset(dev, pipe); @@ -1101,6 +1582,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000); } else if (IS_I9XX(dev)) { refclk = 96000; + if (IS_IGDNG(dev)) + refclk = 120000; /* 120Mhz refclk */ } else { refclk = 48000; } @@ -1137,12 +1620,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } } + /* FDI link */ + if (IS_IGDNG(dev)) + igdng_compute_m_n(3, 4, /* lane num 4 */ + adjusted_mode->clock, + 270000, /* lane clock */ + &m_n); + if (IS_IGD(dev)) 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(dev)) + dpll = DPLL_VGA_MODE_DIS; + if (IS_I9XX(dev)) { if (is_lvds) dpll |= DPLLB_MODE_LVDS; @@ -1150,17 +1642,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= DPLLB_MODE_DAC_SERIAL; if (is_sdvo) { dpll |= DPLL_DVO_HIGH_SPEED; - if (IS_I945G(dev) || IS_I945GM(dev)) { - int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + if (IS_I945G(dev) || IS_I945GM(dev)) dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - } + else if (IS_IGDNG(dev)) + dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } /* compute bitmask from p1 value */ if (IS_IGD(dev)) dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD; - else + else { dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + /* also FPA1 */ + if (IS_IGDNG(dev)) + dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + } switch (clock.p2) { case 5: dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; @@ -1175,7 +1672,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; break; } - if (IS_I965G(dev)) + if (IS_I965G(dev) && !IS_IGDNG(dev)) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); } else { if (is_lvds) { @@ -1207,10 +1704,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; - 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(dev)) { + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + } if (pipe == 0 && !IS_I965G(dev)) { /* Enable pixel doubling when the dot clock is > 90% of the (display) @@ -1231,12 +1732,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Disable the panel fitter if it was on our pipe */ - if (intel_panel_fitter_pipe(dev) == pipe) + if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe) I915_WRITE(PFIT_CONTROL, 0); DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); + /* assign to IGDNG registers */ + if (IS_IGDNG(dev)) { + fp_reg = pch_fp_reg; + dpll_reg = pch_dpll_reg; + } if (dpll & DPLL_VCO_ENABLE) { I915_WRITE(fp_reg, fp); @@ -1245,6 +1751,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, udelay(150); } + if (IS_IGDNG(dev)) { + /* enable PCH clock reference source */ + /* XXX need to change the setting for other outputs */ + u32 temp; + temp = I915_READ(PCH_DREF_CONTROL); + temp &= ~DREF_NONSPREAD_SOURCE_MASK; + temp |= DREF_NONSPREAD_CK505_ENABLE; + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_ENABLE; + temp &= ~DREF_SSC1_ENABLE; + /* if no eDP, disable source output to CPU */ + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + I915_WRITE(PCH_DREF_CONTROL, temp); + } + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -1276,8 +1798,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Wait for the clocks to stabilize. */ udelay(150); - if (IS_I965G(dev)) { - int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + if (IS_I965G(dev) && !IS_IGDNG(dev)) { + sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); } else { @@ -1303,9 +1825,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* pipesrc and dspsize control the size that is scaled from, which should * always be the user's requested size. */ - I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - I915_WRITE(dsppos_reg, 0); + if (!IS_IGDNG(dev)) { + I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | + (mode->hdisplay - 1)); + I915_WRITE(dsppos_reg, 0); + } I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + + if (IS_IGDNG(dev)) { + I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m); + I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n); + I915_WRITE(link_m1_reg, m_n.link_m); + I915_WRITE(link_n1_reg, m_n.link_n); + + /* enable FDI RX PLL too */ + temp = I915_READ(fdi_rx_reg); + I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE); + udelay(200); + } + I915_WRITE(pipeconf_reg, pipeconf); I915_READ(pipeconf_reg); @@ -1336,6 +1874,11 @@ void intel_crtc_load_lut(struct drm_crtc *crtc) if (!crtc->enabled) return; + /* use legacy palette for IGDNG */ + if (IS_IGDNG(dev)) + palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A : + LGC_PALETTE_B; + for (i = 0; i < 256; i++) { I915_WRITE(palreg + 4 * i, (intel_crtc->lut_r[i] << 16) | @@ -1885,10 +2428,12 @@ static void intel_setup_outputs(struct drm_device *dev) intel_crt_init(dev); /* Set up integrated LVDS */ - if (IS_MOBILE(dev) && !IS_I830(dev)) + if (IS_MOBILE(dev) && !IS_I830(dev) && !IS_IGDNG(dev)) intel_lvds_init(dev); - if (IS_I9XX(dev)) { + if (IS_IGDNG(dev)) { + /* ignore for other outputs */ + } else if (IS_I9XX(dev)) { int found; u32 reg; @@ -1912,7 +2457,7 @@ static void intel_setup_outputs(struct drm_device *dev) } else intel_dvo_init(dev); - if (IS_I9XX(dev) && IS_MOBILE(dev)) + if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev)) intel_tv_init(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -- cgit v1.2.3-70-g09d2 From 30ad48b7334a2eb2edf22f6c91f7b3f22a22a837 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Fri, 5 Jun 2009 15:38:43 +0800 Subject: drm/i915: Add HDMI support on IGDNG Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 17 ++++++++++++++++- drivers/gpu/drm/i915/intel_hdmi.c | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2cd6ba6523d..53cf6efa67b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2432,7 +2432,22 @@ static void intel_setup_outputs(struct drm_device *dev) intel_lvds_init(dev); if (IS_IGDNG(dev)) { - /* ignore for other outputs */ + int found; + + if (I915_READ(HDMIB) & PORT_DETECTED) { + /* check SDVOB */ + /* found = intel_sdvo_init(dev, HDMIB); */ + found = 0; + if (!found) + intel_hdmi_init(dev, HDMIB); + } + + if (I915_READ(HDMIC) & PORT_DETECTED) + intel_hdmi_init(dev, HDMIC); + + if (I915_READ(HDMID) & PORT_DETECTED) + intel_hdmi_init(dev, HDMID); + } else if (IS_I9XX(dev)) { int found; u32 reg; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index d0983bb93a1..d874b0c4b06 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -56,7 +56,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE | SDVO_VSYNC_ACTIVE_HIGH | - SDVO_HSYNC_ACTIVE_HIGH; + SDVO_HSYNC_ACTIVE_HIGH | + SDVO_NULL_PACKETS_DURING_VSYNC; if (hdmi_priv->has_hdmi_sink) sdvox |= SDVO_AUDIO_ENABLE; @@ -144,6 +145,22 @@ intel_hdmi_sink_detect(struct drm_connector *connector) } } +static enum drm_connector_status +igdng_hdmi_detect(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; + + /* FIXME hotplug detect */ + + hdmi_priv->has_hdmi_sink = false; + intel_hdmi_sink_detect(connector); + if (hdmi_priv->has_hdmi_sink) + return connector_status_connected; + else + return connector_status_disconnected; +} + static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector) { @@ -153,6 +170,9 @@ intel_hdmi_detect(struct drm_connector *connector) struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; u32 temp, bit; + if (IS_IGDNG(dev)) + return igdng_hdmi_detect(connector); + temp = I915_READ(PORT_HOTPLUG_EN); switch (hdmi_priv->sdvox_reg) { @@ -268,8 +288,17 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB"); - else + else if (sdvox_reg == SDVOC) intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC"); + else if (sdvox_reg == HDMIB) + intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOE, + "HDMIB"); + else if (sdvox_reg == HDMIC) + intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOD, + "HDMIC"); + else if (sdvox_reg == HDMID) + intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOF, + "HDMID"); if (!intel_output->ddc_bus) goto err_connector; -- cgit v1.2.3-70-g09d2 From 541998a18b72d2cac48b3369fa4540116ff3f0a8 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Fri, 5 Jun 2009 15:38:44 +0800 Subject: drm/i915: Add LVDS support for IGDNG Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 13 ++-- drivers/gpu/drm/i915/intel_lvds.c | 127 +++++++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 53cf6efa67b..05bd97e3e3e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1542,6 +1542,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, 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; u32 temp; int sdvo_pixel_multiply; @@ -1772,8 +1773,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * things on. */ if (is_lvds) { - u32 lvds = I915_READ(LVDS); + u32 lvds; + if (IS_IGDNG(dev)) + lvds_reg = PCH_LVDS; + + lvds = I915_READ(lvds_reg); lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | 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. @@ -1788,8 +1793,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * panels behave in the two modes. */ - I915_WRITE(LVDS, lvds); - I915_READ(LVDS); + I915_WRITE(lvds_reg, lvds); + I915_READ(lvds_reg); } I915_WRITE(fp_reg, fp); @@ -2428,7 +2433,7 @@ static void intel_setup_outputs(struct drm_device *dev) intel_crt_init(dev); /* Set up integrated LVDS */ - if (IS_MOBILE(dev) && !IS_I830(dev) && !IS_IGDNG(dev)) + if (IS_MOBILE(dev) && !IS_I830(dev)) intel_lvds_init(dev); if (IS_IGDNG(dev)) { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 53731f0ffcb..eea3a548b82 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -45,10 +45,15 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl; + u32 blc_pwm_ctl, reg; - blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | + if (IS_IGDNG(dev)) + reg = BLC_PWM_CPU_CTL; + else + reg = BLC_PWM_CTL; + + blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(reg, (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); } @@ -58,8 +63,14 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level) static u32 intel_lvds_get_max_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + + if (IS_IGDNG(dev)) + reg = BLC_PWM_PCH_CTL2; + else + reg = BLC_PWM_CTL; - return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >> + return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; } @@ -69,23 +80,31 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev) static void intel_lvds_set_power(struct drm_device *dev, bool on) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_status; + u32 pp_status, ctl_reg, status_reg; + + if (IS_IGDNG(dev)) { + ctl_reg = PCH_PP_CONTROL; + status_reg = PCH_PP_STATUS; + } else { + ctl_reg = PP_CONTROL; + status_reg = PP_STATUS; + } if (on) { - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | + I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); do { - pp_status = I915_READ(PP_STATUS); + pp_status = I915_READ(status_reg); } while ((pp_status & PP_ON) == 0); intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); } else { intel_lvds_set_backlight(dev, 0); - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & + I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); do { - pp_status = I915_READ(PP_STATUS); + pp_status = I915_READ(status_reg); } while (pp_status & PP_ON); } } @@ -106,12 +125,28 @@ static void intel_lvds_save(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg; + u32 pwm_ctl_reg; + + if (IS_IGDNG(dev)) { + 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; + } - dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS); - dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS); - dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); - dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); - dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->savePP_ON = I915_READ(pp_on_reg); + dev_priv->savePP_OFF = I915_READ(pp_off_reg); + dev_priv->savePP_CONTROL = I915_READ(pp_ctl_reg); + dev_priv->savePP_DIVISOR = I915_READ(pp_div_reg); + dev_priv->saveBLC_PWM_CTL = I915_READ(pwm_ctl_reg); dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & BACKLIGHT_DUTY_CYCLE_MASK); @@ -127,12 +162,28 @@ static void intel_lvds_restore(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg; + u32 pwm_ctl_reg; + + if (IS_IGDNG(dev)) { + 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; + } - I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); - I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON); - I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF); - I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); - I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); + I915_WRITE(pwm_ctl_reg, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(pp_on_reg, dev_priv->savePP_ON); + I915_WRITE(pp_off_reg, dev_priv->savePP_OFF); + I915_WRITE(pp_div_reg, dev_priv->savePP_DIVISOR); + I915_WRITE(pp_ctl_reg, dev_priv->savePP_CONTROL); if (dev_priv->savePP_CONTROL & POWER_TARGET_ON) intel_lvds_set_power(dev, true); else @@ -216,8 +267,14 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; - dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + if (IS_IGDNG(dev)) + reg = BLC_PWM_CPU_CTL; + else + reg = BLC_PWM_CTL; + + dev_priv->saveBLC_PWM_CTL = I915_READ(reg); dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & BACKLIGHT_DUTY_CYCLE_MASK); @@ -251,6 +308,10 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, * settings. */ + /* No panel fitting yet, fixme */ + if (IS_IGDNG(dev)) + return; + /* * Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to @@ -446,12 +507,18 @@ void intel_lvds_init(struct drm_device *dev) struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; u32 lvds; - int pipe; + int pipe, gpio = GPIOC; /* Skip init on machines we know falsely report LVDS */ if (dmi_check_system(intel_no_lvds)) return; + if (IS_IGDNG(dev)) { + if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) + return; + gpio = PCH_GPIOC; + } + intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); if (!intel_output) { return; @@ -486,7 +553,7 @@ void intel_lvds_init(struct drm_device *dev) */ /* Set up the DDC bus. */ - intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); + intel_output->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C"); if (!intel_output->ddc_bus) { dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " "failed.\n"); @@ -528,6 +595,11 @@ void intel_lvds_init(struct drm_device *dev) * on. If so, assume that whatever is currently programmed is the * correct mode. */ + + /* IGDNG: FIXME if still fail, not try pipe mode now */ + if (IS_IGDNG(dev)) + goto failed; + lvds = I915_READ(LVDS); pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; crtc = intel_get_crtc_from_pipe(dev, pipe); @@ -546,6 +618,17 @@ void intel_lvds_init(struct drm_device *dev) goto failed; out: + if (IS_IGDNG(dev)) { + u32 pwm; + /* make sure PWM is enabled */ + pwm = I915_READ(BLC_PWM_CPU_CTL2); + pwm |= (PWM_ENABLE | PWM_PIPE_B); + I915_WRITE(BLC_PWM_CPU_CTL2, pwm); + + pwm = I915_READ(BLC_PWM_PCH_CTL1); + pwm |= PWM_PCH_ENABLE; + I915_WRITE(BLC_PWM_PCH_CTL1, pwm); + } drm_sysfs_connector_add(connector); return; -- cgit v1.2.3-70-g09d2 From 2245fda810f870dce9b030e6aa604320abba53a5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 30 May 2009 20:42:29 -0700 Subject: drm/i915: Don't trim cursor addresses to 11 bits We can safely assume that cursor addresses will not extend beyond the addressable screen dimensions; setting the additional bits is harmless in any case. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 05bd97e3e3e..c5c45827ca0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2012,16 +2012,16 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) uint32_t adder; if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT; x = -x; } if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + temp |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT; y = -y; } - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + temp |= x << CURSOR_X_SHIFT; + temp |= y << CURSOR_Y_SHIFT; adder = intel_crtc->cursor_addr; I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); -- cgit v1.2.3-70-g09d2 From 1f803ee5cea67d2387aeedb4b07e645a743729de Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 6 Jun 2009 09:45:59 +0100 Subject: drm/i915: Call drm_vblank_post_modeset() on error paths. Ensure that the drm_vblank_pre_modeset() is always balanced by drm_vblank_post_modeset() within intel_crtc_mode_set(). Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c5c45827ca0..a87eeffc2c8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1598,6 +1598,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { DRM_ERROR("Couldn't find PLL settings for mode!\n"); + drm_vblank_post_modeset(dev, pipe); return -EINVAL; } @@ -1858,12 +1859,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Flush the plane changes */ ret = intel_pipe_set_base(crtc, x, y, old_fb); - if (ret != 0) - return ret; - drm_vblank_post_modeset(dev, pipe); - return 0; + return ret; } /** Loads the palette/gamma unit for the CRTC with the prepared values */ -- cgit v1.2.3-70-g09d2 From 03d6069912babc07a3da20e715dd6a5dc8f0f867 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 5 Jun 2009 18:19:56 -0700 Subject: drm/i915: Hook connector to encoder during load detection (fixes tv/vga detect) With the DRM-driven DPMS code, encoders are considered idle unless a connector is hooked to them, so mode setting is skipped. This makes load detection fail as none of the hardware is enabled. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a87eeffc2c8..b32a51f2a91 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2136,6 +2136,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output, } encoder->crtc = crtc; + intel_output->base.encoder = encoder; intel_output->load_detect_temp = true; intel_crtc = to_intel_crtc(crtc); @@ -2171,6 +2172,7 @@ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_ if (intel_output->load_detect_temp) { encoder->crtc = NULL; + intel_output->base.encoder = NULL; intel_output->load_detect_temp = false; crtc->enabled = drm_helper_crtc_in_use(crtc); drm_helper_disable_unused_functions(dev); -- cgit v1.2.3-70-g09d2 From 7ff145593d808a371924652c8d6a15fb75ce2250 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 22 Apr 2009 18:52:14 +1000 Subject: drm/i915: duplicate desired mode for use by fbcon. duplicate the mode into fbcon storage, so when we free modes later we don't just lose this. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_display.c | 2 ++ drivers/gpu/drm/i915/intel_fb.c | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b32a51f2a91..028f5b66e3d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2310,6 +2310,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + if (intel_crtc->mode_set.mode) + drm_mode_destroy(crtc->dev, intel_crtc->mode_set.mode); drm_crtc_cleanup(crtc); kfree(intel_crtc); } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index cbd2ba828c7..0ecf6b76a40 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -674,8 +674,12 @@ static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc * par->crtc_ids[0] = crtc->base.id; modeset->num_connectors = conn_count; - if (modeset->mode != modeset->crtc->desired_mode) - modeset->mode = modeset->crtc->desired_mode; + if (modeset->crtc->desired_mode) { + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = drm_mode_duplicate(dev, + modeset->crtc->desired_mode); + } par->crtc_count = 1; @@ -824,8 +828,12 @@ static int intelfb_single_fb_probe(struct drm_device *dev) par->crtc_ids[crtc_count++] = crtc->base.id; modeset->num_connectors = conn_count; - if (modeset->mode != modeset->crtc->desired_mode) - modeset->mode = modeset->crtc->desired_mode; + if (modeset->crtc->desired_mode) { + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = drm_mode_duplicate(dev, + modeset->crtc->desired_mode); + } } par->crtc_count = crtc_count; -- cgit v1.2.3-70-g09d2 From 8c4b8c3f34de4e2da20df042bba173fe557f8b45 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 17 Jun 2009 22:08:52 +0100 Subject: drm/i915: Install fence register for tiled scanout on i915 With the work by Jesse Barnes to eliminate allocation of fences during execbuffer, it becomes possible to write to the scan-out buffer with it never acquiring a fence (simply by only ever writing to the object using tiled GPU commands and never writing to it via the GTT). So for pre-i965 chipsets which require fenced access for tiled scan-out buffers, we need to obtain a fence register. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 10 ++++------ drivers/gpu/drm/i915/intel_display.c | 20 ++++++++++++++++---- 3 files changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_display.c') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8ef6bcec211..451b547352b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -646,6 +646,7 @@ void i915_gem_object_unpin(struct drm_gem_object *obj); int i915_gem_object_unbind(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); +int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_work_handler(struct work_struct *work); void i915_gem_clflush_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a5f95ad3c07..4f345414fe7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -46,7 +46,6 @@ static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *o static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); -static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); static int i915_gem_evict_something(struct drm_device *dev); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, @@ -1158,7 +1157,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Need a new fence register? */ if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { - ret = i915_gem_object_get_fence_reg(obj, write); + ret = i915_gem_object_get_fence_reg(obj); if (ret) { mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; @@ -2169,7 +2168,6 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) /** * i915_gem_object_get_fence_reg - set up a fence reg for an object * @obj: object to map through a fence reg - * @write: object is about to be written * * When mapping objects through the GTT, userspace wants to be able to write * to them without having to worry about swizzling if the object is tiled. @@ -2180,8 +2178,8 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) * It then sets up the reg based on the object's properties: address, pitch * and tiling format. */ -static int -i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write) +int +i915_gem_object_get_fence_reg(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3550,7 +3548,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) if (!IS_I965G(dev) && obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { - ret = i915_gem_object_get_fence_reg(obj, true); + ret = i915_gem_object_get_fence_reg(obj); if (ret != 0) { if (ret != -EBUSY && ret != -ERESTARTSYS) DRM_ERROR("Failure to install fence: %d\n", diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 028f5b66e3d..3e1c7816211 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -828,19 +828,31 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, } mutex_lock(&dev->struct_mutex); - ret = i915_gem_object_pin(intel_fb->obj, alignment); + ret = i915_gem_object_pin(obj, alignment); if (ret != 0) { mutex_unlock(&dev->struct_mutex); return ret; } - ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1); + ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret != 0) { - i915_gem_object_unpin(intel_fb->obj); + i915_gem_object_unpin(obj); mutex_unlock(&dev->struct_mutex); return ret; } + /* Pre-i965 needs to install a fence for tiled scan-out */ + if (!IS_I965G(dev) && + obj_priv->fence_reg == I915_FENCE_REG_NONE && + obj_priv->tiling_mode != I915_TILING_NONE) { + ret = i915_gem_object_get_fence_reg(obj); + if (ret != 0) { + i915_gem_object_unpin(obj); + mutex_unlock(&dev->struct_mutex); + return ret; + } + } + dspcntr = I915_READ(dspcntr_reg); /* Mask out pixel format bits in case we change it */ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; @@ -860,7 +872,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, break; default: DRM_ERROR("Unknown color depth\n"); - i915_gem_object_unpin(intel_fb->obj); + i915_gem_object_unpin(obj); mutex_unlock(&dev->struct_mutex); return -EINVAL; } -- cgit v1.2.3-70-g09d2