Commit 2c07245f authored by Zhenyu Wang's avatar Zhenyu Wang Committed by Eric Anholt

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: default avatarZhenyu Wang <zhenyuw@linux.intel.com>
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 2cce0d87
......@@ -922,7 +922,7 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
* Some of the preallocated space is taken by the GTT
* and popup. GTT is 1K per MB of aperture size, and popup is 4K.
*/
if (IS_G4X(dev) || IS_IGD(dev))
if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
overhead = 4096;
else
overhead = (*aperture_size / 1024) + 4096;
......
......@@ -37,9 +37,14 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 temp;
u32 temp, reg;
temp = I915_READ(ADPA);
if (IS_IGDNG(dev))
reg = PCH_ADPA;
else
reg = ADPA;
temp = I915_READ(reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp |= ADPA_DAC_ENABLE;
......@@ -58,7 +63,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
break;
}
I915_WRITE(ADPA, temp);
I915_WRITE(reg, temp);
}
static int intel_crt_mode_valid(struct drm_connector *connector,
......@@ -101,17 +106,23 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
struct drm_i915_private *dev_priv = dev->dev_private;
int dpll_md_reg;
u32 adpa, dpll_md;
u32 adpa_reg;
if (intel_crtc->pipe == 0)
dpll_md_reg = DPLL_A_MD;
else
dpll_md_reg = DPLL_B_MD;
if (IS_IGDNG(dev))
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(dev)) {
if (IS_I965G(dev) && !IS_IGDNG(dev)) {
dpll_md = I915_READ(dpll_md_reg);
I915_WRITE(dpll_md_reg,
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
......@@ -125,13 +136,53 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (intel_crtc->pipe == 0) {
adpa |= ADPA_PIPE_A_SELECT;
I915_WRITE(BCLRPAT_A, 0);
if (!IS_IGDNG(dev))
I915_WRITE(BCLRPAT_A, 0);
} else {
adpa |= ADPA_PIPE_B_SELECT;
I915_WRITE(BCLRPAT_B, 0);
if (!IS_IGDNG(dev))
I915_WRITE(BCLRPAT_B, 0);
}
I915_WRITE(ADPA, adpa);
I915_WRITE(adpa_reg, adpa);
}
static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 adpa, temp;
bool ret;
temp = adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
ADPA_CRT_HOTPLUG_WARMUP_10MS |
ADPA_CRT_HOTPLUG_SAMPLE_4S |
ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */
ADPA_CRT_HOTPLUG_VOLREF_325MV |
ADPA_CRT_HOTPLUG_ENABLE |
ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
DRM_DEBUG("pch crt adpa 0x%x", adpa);
I915_WRITE(PCH_ADPA, adpa);
/* This might not be needed as not specified in spec...*/
udelay(1000);
/* Check the status to see if both blue and green are on now */
adpa = I915_READ(PCH_ADPA);
if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
ADPA_CRT_HOTPLUG_MONITOR_COLOR)
ret = true;
else
ret = false;
/* restore origin register */
I915_WRITE(PCH_ADPA, temp);
return ret;
}
/**
......@@ -148,6 +199,10 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 hotplug_en;
int i, tries = 0;
if (IS_IGDNG(dev))
return intel_igdng_crt_detect_hotplug(connector);
/*
* On 4 series desktop, CRT detect sequence need to be done twice
* to get a reliable result.
......@@ -427,6 +482,7 @@ void intel_crt_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct intel_output *intel_output;
u32 i2c_reg;
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
if (!intel_output)
......@@ -443,7 +499,11 @@ void intel_crt_init(struct drm_device *dev)
&intel_output->enc);
/* Set up the DDC bus. */
intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
if (IS_IGDNG(dev))
i2c_reg = PCH_GPIOA;
else
i2c_reg = GPIOA;
intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
if (!intel_output->ddc_bus) {
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
"failed.\n");
......
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment