32,13 → 32,14 |
#include <linux/slab.h> |
//#include <linux/vgaarb.h> |
#include <drm/drm_edid.h> |
#include "drmP.h" |
#include <drm/drmP.h> |
#include "intel_drv.h" |
#include "i915_drm.h" |
#include <drm/i915_drm.h> |
#include "i915_drv.h" |
#include "i915_trace.h" |
#include "drm_dp_helper.h" |
#include "drm_crtc_helper.h" |
#include <drm/drm_dp_helper.h> |
#include <drm/drm_crtc_helper.h> |
//#include <linux/dma_remapping.h> |
|
phys_addr_t get_bus_addr(void); |
|
63,20 → 64,11 |
} |
|
|
static inline int pci_read_config_word(struct pci_dev *dev, int where, |
u16 *val) |
{ |
*val = PciRead16(dev->busnr, dev->devfn, where); |
return 1; |
} |
|
|
#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) |
|
bool intel_pipe_has_type(struct drm_crtc *crtc, int type); |
static void intel_update_watermarks(struct drm_device *dev); |
static void intel_increase_pllclock(struct drm_crtc *crtc); |
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); |
//static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); |
|
typedef struct { |
/* given values */ |
105,7 → 97,7 |
intel_range_t dot, vco, n, m, m1, m2, p, p1; |
intel_p2_t p2; |
bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, |
int, int, intel_clock_t *); |
int, int, intel_clock_t *, intel_clock_t *); |
}; |
|
/* FDI */ |
113,18 → 105,27 |
|
static bool |
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock); |
int target, int refclk, intel_clock_t *match_clock, |
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); |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock); |
|
static bool |
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock); |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock); |
static bool |
intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock); |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock); |
|
static bool |
intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock); |
|
static inline u32 /* units of 100MHz */ |
intel_fdi_link_freq(struct drm_device *dev) |
{ |
386,6 → 387,152 |
.find_pll = intel_find_pll_ironlake_dp, |
}; |
|
static const intel_limit_t intel_limits_vlv_dac = { |
.dot = { .min = 25000, .max = 270000 }, |
.vco = { .min = 4000000, .max = 6000000 }, |
.n = { .min = 1, .max = 7 }, |
.m = { .min = 22, .max = 450 }, /* guess */ |
.m1 = { .min = 2, .max = 3 }, |
.m2 = { .min = 11, .max = 156 }, |
.p = { .min = 10, .max = 30 }, |
.p1 = { .min = 2, .max = 3 }, |
.p2 = { .dot_limit = 270000, |
.p2_slow = 2, .p2_fast = 20 }, |
.find_pll = intel_vlv_find_best_pll, |
}; |
|
static const intel_limit_t intel_limits_vlv_hdmi = { |
.dot = { .min = 20000, .max = 165000 }, |
.vco = { .min = 5994000, .max = 4000000 }, |
.n = { .min = 1, .max = 7 }, |
.m = { .min = 60, .max = 300 }, /* guess */ |
.m1 = { .min = 2, .max = 3 }, |
.m2 = { .min = 11, .max = 156 }, |
.p = { .min = 10, .max = 30 }, |
.p1 = { .min = 2, .max = 3 }, |
.p2 = { .dot_limit = 270000, |
.p2_slow = 2, .p2_fast = 20 }, |
.find_pll = intel_vlv_find_best_pll, |
}; |
|
static const intel_limit_t intel_limits_vlv_dp = { |
.dot = { .min = 162000, .max = 270000 }, |
.vco = { .min = 5994000, .max = 4000000 }, |
.n = { .min = 1, .max = 7 }, |
.m = { .min = 60, .max = 300 }, /* guess */ |
.m1 = { .min = 2, .max = 3 }, |
.m2 = { .min = 11, .max = 156 }, |
.p = { .min = 10, .max = 30 }, |
.p1 = { .min = 2, .max = 3 }, |
.p2 = { .dot_limit = 270000, |
.p2_slow = 2, .p2_fast = 20 }, |
.find_pll = intel_vlv_find_best_pll, |
}; |
|
u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) |
{ |
unsigned long flags; |
u32 val = 0; |
|
spin_lock_irqsave(&dev_priv->dpio_lock, flags); |
if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { |
DRM_ERROR("DPIO idle wait timed out\n"); |
goto out_unlock; |
} |
|
I915_WRITE(DPIO_REG, reg); |
I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_READ | DPIO_PORTID | |
DPIO_BYTE); |
if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { |
DRM_ERROR("DPIO read wait timed out\n"); |
goto out_unlock; |
} |
val = I915_READ(DPIO_DATA); |
|
out_unlock: |
spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); |
return val; |
} |
|
static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, |
u32 val) |
{ |
unsigned long flags; |
|
spin_lock_irqsave(&dev_priv->dpio_lock, flags); |
if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { |
DRM_ERROR("DPIO idle wait timed out\n"); |
goto out_unlock; |
} |
|
I915_WRITE(DPIO_DATA, val); |
I915_WRITE(DPIO_REG, reg); |
I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | |
DPIO_BYTE); |
if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) |
DRM_ERROR("DPIO write wait timed out\n"); |
|
out_unlock: |
spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); |
} |
|
static void vlv_init_dpio(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* Reset the DPIO config */ |
I915_WRITE(DPIO_CTL, 0); |
POSTING_READ(DPIO_CTL); |
I915_WRITE(DPIO_CTL, 1); |
POSTING_READ(DPIO_CTL); |
} |
|
static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) |
{ |
DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); |
return 1; |
} |
|
static const struct dmi_system_id intel_dual_link_lvds[] = { |
{ |
.callback = intel_dual_link_lvds_callback, |
.ident = "Apple MacBook Pro (Core i5/i7 Series)", |
.matches = { |
DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), |
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), |
}, |
}, |
{ } /* terminating entry */ |
}; |
|
static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, |
unsigned int reg) |
{ |
unsigned int val; |
|
/* use the module option value if specified */ |
if (i915_lvds_channel_mode > 0) |
return i915_lvds_channel_mode == 2; |
|
// if (dmi_check_system(intel_dual_link_lvds)) |
// return true; |
|
if (dev_priv->lvds_val) |
val = dev_priv->lvds_val; |
else { |
/* BIOS should set the proper LVDS register value at boot, but |
* in reality, it doesn't set the value when the lid is closed; |
* we need to check "the value to be set" in VBT when LVDS |
* register is uninitialized. |
*/ |
val = I915_READ(reg); |
if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) |
val = dev_priv->bios_lvds_val; |
dev_priv->lvds_val = val; |
} |
return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; |
} |
|
static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, |
int refclk) |
{ |
394,8 → 541,7 |
const intel_limit_t *limit; |
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { |
if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == |
LVDS_CLKB_POWER_UP) { |
if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { |
/* LVDS dual channel */ |
if (refclk == 100000) |
limit = &intel_limits_ironlake_dual_lvds_100m; |
423,8 → 569,7 |
const intel_limit_t *limit; |
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { |
if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == |
LVDS_CLKB_POWER_UP) |
if (is_dual_link_lvds(dev_priv, LVDS)) |
/* LVDS with dual channel */ |
limit = &intel_limits_g4x_dual_channel_lvds; |
else |
457,6 → 602,13 |
limit = &intel_limits_pineview_lvds; |
else |
limit = &intel_limits_pineview_sdvo; |
} else if (IS_VALLEYVIEW(dev)) { |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) |
limit = &intel_limits_vlv_dac; |
else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) |
limit = &intel_limits_vlv_hdmi; |
else |
limit = &intel_limits_vlv_dp; |
} else if (!IS_GEN2(dev)) { |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
limit = &intel_limits_i9xx_lvds; |
498,11 → 650,10 |
bool intel_pipe_has_type(struct drm_crtc *crtc, int type) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_mode_config *mode_config = &dev->mode_config; |
struct intel_encoder *encoder; |
|
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) |
if (encoder->base.crtc == crtc && encoder->type == type) |
for_each_encoder_on_crtc(dev, crtc, encoder) |
if (encoder->type == type) |
return true; |
|
return false; |
545,7 → 696,8 |
|
static bool |
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock) |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock) |
|
{ |
struct drm_device *dev = crtc->dev; |
561,8 → 713,7 |
* reliably set up different single/dual channel state, if we |
* even can. |
*/ |
if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == |
LVDS_CLKB_POWER_UP) |
if (is_dual_link_lvds(dev_priv, LVDS)) |
clock.p2 = limit->p2.p2_fast; |
else |
clock.p2 = limit->p2.p2_slow; |
592,6 → 743,9 |
if (!intel_PLL_is_valid(dev, limit, |
&clock)) |
continue; |
if (match_clock && |
clock.p != match_clock->p) |
continue; |
|
this_err = abs(clock.dot - target); |
if (this_err < err) { |
608,7 → 762,8 |
|
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) |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
655,6 → 810,9 |
if (!intel_PLL_is_valid(dev, limit, |
&clock)) |
continue; |
if (match_clock && |
clock.p != match_clock->p) |
continue; |
|
this_err = abs(clock.dot - target); |
if (this_err < err_most) { |
672,7 → 830,8 |
|
static bool |
intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock) |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock) |
{ |
struct drm_device *dev = crtc->dev; |
intel_clock_t clock; |
698,7 → 857,8 |
/* DisplayPort has only two frequencies, 162MHz and 270MHz */ |
static bool |
intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *best_clock) |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock) |
{ |
intel_clock_t clock; |
if (target < 200000) { |
721,7 → 881,86 |
memcpy(best_clock, &clock, sizeof(intel_clock_t)); |
return true; |
} |
static bool |
intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, |
int target, int refclk, intel_clock_t *match_clock, |
intel_clock_t *best_clock) |
{ |
u32 p1, p2, m1, m2, vco, bestn, bestm1, bestm2, bestp1, bestp2; |
u32 m, n, fastclk; |
u32 updrate, minupdate, fracbits, p; |
unsigned long bestppm, ppm, absppm; |
int dotclk, flag; |
|
flag = 0; |
dotclk = target * 1000; |
bestppm = 1000000; |
ppm = absppm = 0; |
fastclk = dotclk / (2*100); |
updrate = 0; |
minupdate = 19200; |
fracbits = 1; |
n = p = p1 = p2 = m = m1 = m2 = vco = bestn = 0; |
bestm1 = bestm2 = bestp1 = bestp2 = 0; |
|
/* based on hardware requirement, prefer smaller n to precision */ |
for (n = limit->n.min; n <= ((refclk) / minupdate); n++) { |
updrate = refclk / n; |
for (p1 = limit->p1.max; p1 > limit->p1.min; p1--) { |
for (p2 = limit->p2.p2_fast+1; p2 > 0; p2--) { |
if (p2 > 10) |
p2 = p2 - 1; |
p = p1 * p2; |
/* based on hardware requirement, prefer bigger m1,m2 values */ |
for (m1 = limit->m1.min; m1 <= limit->m1.max; m1++) { |
m2 = (((2*(fastclk * p * n / m1 )) + |
refclk) / (2*refclk)); |
m = m1 * m2; |
vco = updrate * m; |
if (vco >= limit->vco.min && vco < limit->vco.max) { |
ppm = 1000000 * ((vco / p) - fastclk) / fastclk; |
absppm = (ppm > 0) ? ppm : (-ppm); |
if (absppm < 100 && ((p1 * p2) > (bestp1 * bestp2))) { |
bestppm = 0; |
flag = 1; |
} |
if (absppm < bestppm - 10) { |
bestppm = absppm; |
flag = 1; |
} |
if (flag) { |
bestn = n; |
bestm1 = m1; |
bestm2 = m2; |
bestp1 = p1; |
bestp2 = p2; |
flag = 0; |
} |
} |
} |
} |
} |
} |
best_clock->n = bestn; |
best_clock->m1 = bestm1; |
best_clock->m2 = bestm2; |
best_clock->p1 = bestp1; |
best_clock->p2 = bestp2; |
|
return true; |
} |
|
static void ironlake_wait_for_vblank(struct drm_device *dev, int pipe) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 frame, frame_reg = PIPEFRAME(pipe); |
|
frame = I915_READ(frame_reg); |
|
if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50)) |
DRM_DEBUG_KMS("vblank wait timed out\n"); |
} |
|
/** |
* intel_wait_for_vblank - wait for vblank on a given pipe |
* @dev: drm device |
735,6 → 974,11 |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipestat_reg = PIPESTAT(pipe); |
|
if (INTEL_INFO(dev)->gen >= 5) { |
ironlake_wait_for_vblank(dev, pipe); |
return; |
} |
|
/* Clear existing vblank status. Note this will clear any other |
* sticky status fields as well. |
* |
785,20 → 1029,25 |
/* Wait for the Pipe State to go off */ |
if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0, |
100)) |
DRM_DEBUG_KMS("pipe_off wait timed out\n"); |
WARN(1, "pipe_off wait timed out\n"); |
} else { |
u32 last_line; |
u32 last_line, line_mask; |
int reg = PIPEDSL(pipe); |
unsigned long timeout = jiffies + msecs_to_jiffies(100); |
unsigned long timeout = GetTimerTicks() + msecs_to_jiffies(100); |
|
if (IS_GEN2(dev)) |
line_mask = DSL_LINEMASK_GEN2; |
else |
line_mask = DSL_LINEMASK_GEN3; |
|
/* Wait for the display line to settle */ |
do { |
last_line = I915_READ(reg) & DSL_LINEMASK; |
last_line = I915_READ(reg) & line_mask; |
mdelay(5); |
} while (((I915_READ(reg) & DSL_LINEMASK) != last_line) && |
time_after(timeout, jiffies)); |
if (time_after(jiffies, timeout)) |
DRM_DEBUG_KMS("pipe_off wait timed out\n"); |
} while (((I915_READ(reg) & line_mask) != last_line) && |
time_after(timeout, GetTimerTicks())); |
if (time_after(GetTimerTicks(), timeout)) |
WARN(1, "pipe_off wait timed out\n"); |
} |
} |
|
827,34 → 1076,49 |
|
/* For ILK+ */ |
static void assert_pch_pll(struct drm_i915_private *dev_priv, |
enum pipe pipe, bool state) |
struct intel_pch_pll *pll, |
struct intel_crtc *crtc, |
bool state) |
{ |
int reg; |
u32 val; |
bool cur_state; |
|
if (HAS_PCH_CPT(dev_priv->dev)) { |
u32 pch_dpll; |
if (HAS_PCH_LPT(dev_priv->dev)) { |
DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); |
return; |
} |
|
pch_dpll = I915_READ(PCH_DPLL_SEL); |
if (WARN (!pll, |
"asserting PCH PLL %s with no PLL\n", state_string(state))) |
return; |
|
/* Make sure the selected PLL is enabled to the transcoder */ |
WARN(!((pch_dpll >> (4 * pipe)) & 8), |
"transcoder %d PLL not enabled\n", pipe); |
val = I915_READ(pll->pll_reg); |
cur_state = !!(val & DPLL_VCO_ENABLE); |
WARN(cur_state != state, |
"PCH PLL state for reg %x assertion failure (expected %s, current %s), val=%08x\n", |
pll->pll_reg, state_string(state), state_string(cur_state), val); |
|
/* Convert the transcoder pipe number to a pll pipe number */ |
pipe = (pch_dpll >> (4 * pipe)) & 1; |
} |
/* Make sure the selected PLL is correctly attached to the transcoder */ |
if (crtc && HAS_PCH_CPT(dev_priv->dev)) { |
u32 pch_dpll; |
|
reg = PCH_DPLL(pipe); |
val = I915_READ(reg); |
cur_state = !!(val & DPLL_VCO_ENABLE); |
pch_dpll = I915_READ(PCH_DPLL_SEL); |
cur_state = pll->pll_reg == _PCH_DPLL_B; |
if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state, |
"PLL[%d] not attached to this transcoder %d: %08x\n", |
cur_state, crtc->pipe, pch_dpll)) { |
cur_state = !!(val >> (4*crtc->pipe + 3)); |
WARN(cur_state != state, |
"PCH PLL state assertion failure (expected %s, current %s)\n", |
state_string(state), state_string(cur_state)); |
"PLL[%d] not %s on this transcoder %d: %08x\n", |
pll->pll_reg == _PCH_DPLL_B, |
state_string(state), |
crtc->pipe, |
val); |
} |
#define assert_pch_pll_enabled(d, p) assert_pch_pll(d, p, true) |
#define assert_pch_pll_disabled(d, p) assert_pch_pll(d, p, false) |
} |
} |
#define assert_pch_pll_enabled(d, p, c) assert_pch_pll(d, p, c, true) |
#define assert_pch_pll_disabled(d, p, c) assert_pch_pll(d, p, c, false) |
|
static void assert_fdi_tx(struct drm_i915_private *dev_priv, |
enum pipe pipe, bool state) |
863,9 → 1127,16 |
u32 val; |
bool cur_state; |
|
if (IS_HASWELL(dev_priv->dev)) { |
/* On Haswell, DDI is used instead of FDI_TX_CTL */ |
reg = DDI_FUNC_CTL(pipe); |
val = I915_READ(reg); |
cur_state = !!(val & PIPE_DDI_FUNC_ENABLE); |
} else { |
reg = FDI_TX_CTL(pipe); |
val = I915_READ(reg); |
cur_state = !!(val & FDI_TX_ENABLE); |
} |
WARN(cur_state != state, |
"FDI TX state assertion failure (expected %s, current %s)\n", |
state_string(state), state_string(cur_state)); |
880,9 → 1151,14 |
u32 val; |
bool cur_state; |
|
if (IS_HASWELL(dev_priv->dev) && pipe > 0) { |
DRM_ERROR("Attempting to enable FDI_RX on Haswell pipe > 0\n"); |
return; |
} else { |
reg = FDI_RX_CTL(pipe); |
val = I915_READ(reg); |
cur_state = !!(val & FDI_RX_ENABLE); |
} |
WARN(cur_state != state, |
"FDI RX state assertion failure (expected %s, current %s)\n", |
state_string(state), state_string(cur_state)); |
900,6 → 1176,10 |
if (dev_priv->info->gen == 5) |
return; |
|
/* On Haswell, DDI ports are responsible for the FDI PLL setup */ |
if (IS_HASWELL(dev_priv->dev)) |
return; |
|
reg = FDI_TX_CTL(pipe); |
val = I915_READ(reg); |
WARN(!(val & FDI_TX_PLL_ENABLE), "FDI TX PLL assertion failure, should be active but is disabled\n"); |
911,6 → 1191,10 |
int reg; |
u32 val; |
|
if (IS_HASWELL(dev_priv->dev) && pipe > 0) { |
DRM_ERROR("Attempting to enable FDI on Haswell with pipe > 0\n"); |
return; |
} |
reg = FDI_RX_CTL(pipe); |
val = I915_READ(reg); |
WARN(!(val & FDI_RX_PLL_ENABLE), "FDI RX PLL assertion failure, should be active but is disabled\n"); |
952,6 → 1236,10 |
u32 val; |
bool cur_state; |
|
/* if we need the pipe A quirk it must be always on */ |
if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) |
state = true; |
|
reg = PIPECONF(pipe); |
val = I915_READ(reg); |
cur_state = !!(val & PIPECONF_ENABLE); |
960,19 → 1248,24 |
pipe_name(pipe), state_string(state), state_string(cur_state)); |
} |
|
static void assert_plane_enabled(struct drm_i915_private *dev_priv, |
enum plane plane) |
static void assert_plane(struct drm_i915_private *dev_priv, |
enum plane plane, bool state) |
{ |
int reg; |
u32 val; |
bool cur_state; |
|
reg = DSPCNTR(plane); |
val = I915_READ(reg); |
WARN(!(val & DISPLAY_PLANE_ENABLE), |
"plane %c assertion failure, should be active but is disabled\n", |
plane_name(plane)); |
cur_state = !!(val & DISPLAY_PLANE_ENABLE); |
WARN(cur_state != state, |
"plane %c assertion failure (expected %s, current %s)\n", |
plane_name(plane), state_string(state), state_string(cur_state)); |
} |
|
#define assert_plane_enabled(d, p) assert_plane(d, p, true) |
#define assert_plane_disabled(d, p) assert_plane(d, p, false) |
|
static void assert_planes_disabled(struct drm_i915_private *dev_priv, |
enum pipe pipe) |
{ |
981,8 → 1274,14 |
int cur_pipe; |
|
/* Planes are fixed to pipes on ILK+ */ |
if (HAS_PCH_SPLIT(dev_priv->dev)) |
if (HAS_PCH_SPLIT(dev_priv->dev)) { |
reg = DSPCNTR(pipe); |
val = I915_READ(reg); |
WARN((val & DISPLAY_PLANE_ENABLE), |
"plane %c assertion failure, should be disabled but not\n", |
plane_name(pipe)); |
return; |
} |
|
/* Need to check both planes against the pipe */ |
for (i = 0; i < 2; i++) { |
1001,6 → 1300,11 |
u32 val; |
bool enabled; |
|
if (HAS_PCH_LPT(dev_priv->dev)) { |
DRM_DEBUG_DRIVER("LPT does not has PCH refclk, skipping check\n"); |
return; |
} |
|
val = I915_READ(PCH_DREF_CONTROL); |
enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK | |
DREF_SUPERSPREAD_SOURCE_MASK)); |
1094,6 → 1398,10 |
WARN(dp_pipe_enabled(dev_priv, pipe, port_sel, val), |
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n", |
reg, pipe_name(pipe)); |
|
WARN(HAS_PCH_IBX(dev_priv->dev) && (val & DP_PORT_EN) == 0 |
&& (val & DP_PIPEB_SELECT), |
"IBX PCH dp port still using transcoder B\n"); |
} |
|
static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv, |
1100,9 → 1408,13 |
enum pipe pipe, int reg) |
{ |
u32 val = I915_READ(reg); |
WARN(hdmi_pipe_enabled(dev_priv, val, pipe), |
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n", |
WARN(hdmi_pipe_enabled(dev_priv, pipe, val), |
"PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n", |
reg, pipe_name(pipe)); |
|
WARN(HAS_PCH_IBX(dev_priv->dev) && (val & PORT_ENABLE) == 0 |
&& (val & SDVO_PIPE_B_SELECT), |
"IBX PCH hdmi port still using transcoder B\n"); |
} |
|
static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, |
1117,13 → 1429,13 |
|
reg = PCH_ADPA; |
val = I915_READ(reg); |
WARN(adpa_pipe_enabled(dev_priv, val, pipe), |
WARN(adpa_pipe_enabled(dev_priv, pipe, val), |
"PCH VGA enabled on transcoder %c, should be disabled\n", |
pipe_name(pipe)); |
|
reg = PCH_LVDS; |
val = I915_READ(reg); |
WARN(lvds_pipe_enabled(dev_priv, val, pipe), |
WARN(lvds_pipe_enabled(dev_priv, pipe, val), |
"PCH LVDS enabled on transcoder %c, should be disabled\n", |
pipe_name(pipe)); |
|
1142,6 → 1454,8 |
* protect mechanism may be enabled. |
* |
* Note! This is for pre-ILK only. |
* |
* Unfortunately needed by dvo_ns2501 since the dvo depends on it running. |
*/ |
static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) |
{ |
1149,7 → 1463,7 |
u32 val; |
|
/* No really, not for ILK+ */ |
BUG_ON(dev_priv->info->gen >= 5); |
BUG_ON(!IS_VALLEYVIEW(dev_priv->dev) && dev_priv->info->gen >= 5); |
|
/* PLL is protected by panel, make sure we can write it */ |
if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) |
1199,6 → 1513,69 |
POSTING_READ(reg); |
} |
|
/* SBI access */ |
static void |
intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) |
{ |
unsigned long flags; |
|
spin_lock_irqsave(&dev_priv->dpio_lock, flags); |
if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, |
100)) { |
DRM_ERROR("timeout waiting for SBI to become ready\n"); |
goto out_unlock; |
} |
|
I915_WRITE(SBI_ADDR, |
(reg << 16)); |
I915_WRITE(SBI_DATA, |
value); |
I915_WRITE(SBI_CTL_STAT, |
SBI_BUSY | |
SBI_CTL_OP_CRWR); |
|
if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, |
100)) { |
DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); |
goto out_unlock; |
} |
|
out_unlock: |
spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); |
} |
|
static u32 |
intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) |
{ |
unsigned long flags; |
u32 value = 0; |
|
spin_lock_irqsave(&dev_priv->dpio_lock, flags); |
if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, |
100)) { |
DRM_ERROR("timeout waiting for SBI to become ready\n"); |
goto out_unlock; |
} |
|
I915_WRITE(SBI_ADDR, |
(reg << 16)); |
I915_WRITE(SBI_CTL_STAT, |
SBI_BUSY | |
SBI_CTL_OP_CRRD); |
|
if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, |
100)) { |
DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); |
goto out_unlock; |
} |
|
value = I915_READ(SBI_DATA); |
|
out_unlock: |
spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); |
return value; |
} |
|
/** |
* intel_enable_pch_pll - enable PCH PLL |
* @dev_priv: i915 private structure |
1207,60 → 1584,88 |
* The PCH PLL needs to be enabled before the PCH transcoder, since it |
* drives the transcoder clock. |
*/ |
static void intel_enable_pch_pll(struct drm_i915_private *dev_priv, |
enum pipe pipe) |
static void intel_enable_pch_pll(struct intel_crtc *intel_crtc) |
{ |
struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; |
struct intel_pch_pll *pll; |
int reg; |
u32 val; |
|
if (pipe > 1) |
/* PCH PLLs only available on ILK, SNB and IVB */ |
BUG_ON(dev_priv->info->gen < 5); |
pll = intel_crtc->pch_pll; |
if (pll == NULL) |
return; |
|
/* PCH only available on ILK+ */ |
BUG_ON(dev_priv->info->gen < 5); |
if (WARN_ON(pll->refcount == 0)) |
return; |
|
DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", |
pll->pll_reg, pll->active, pll->on, |
intel_crtc->base.base.id); |
|
/* PCH refclock must be enabled first */ |
assert_pch_refclk_enabled(dev_priv); |
|
reg = PCH_DPLL(pipe); |
if (pll->active++ && pll->on) { |
assert_pch_pll_enabled(dev_priv, pll, NULL); |
return; |
} |
|
DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg); |
|
reg = pll->pll_reg; |
val = I915_READ(reg); |
val |= DPLL_VCO_ENABLE; |
I915_WRITE(reg, val); |
POSTING_READ(reg); |
udelay(200); |
|
pll->on = true; |
} |
|
static void intel_disable_pch_pll(struct drm_i915_private *dev_priv, |
enum pipe pipe) |
static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) |
{ |
struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; |
struct intel_pch_pll *pll = intel_crtc->pch_pll; |
int reg; |
u32 val, pll_mask = TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL, |
pll_sel = TRANSC_DPLL_ENABLE; |
u32 val; |
|
if (pipe > 1) |
return; |
|
/* PCH only available on ILK+ */ |
BUG_ON(dev_priv->info->gen < 5); |
if (pll == NULL) |
return; |
|
/* Make sure transcoder isn't still depending on us */ |
assert_transcoder_disabled(dev_priv, pipe); |
if (WARN_ON(pll->refcount == 0)) |
return; |
|
if (pipe == 0) |
pll_sel |= TRANSC_DPLLA_SEL; |
else if (pipe == 1) |
pll_sel |= TRANSC_DPLLB_SEL; |
DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", |
pll->pll_reg, pll->active, pll->on, |
intel_crtc->base.base.id); |
|
if (WARN_ON(pll->active == 0)) { |
assert_pch_pll_disabled(dev_priv, pll, NULL); |
return; |
} |
|
if ((I915_READ(PCH_DPLL_SEL) & pll_mask) == pll_sel) |
if (--pll->active) { |
assert_pch_pll_enabled(dev_priv, pll, NULL); |
return; |
} |
|
reg = PCH_DPLL(pipe); |
DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); |
|
/* Make sure transcoder isn't still depending on us */ |
assert_transcoder_disabled(dev_priv, intel_crtc->pipe); |
|
reg = pll->pll_reg; |
val = I915_READ(reg); |
val &= ~DPLL_VCO_ENABLE; |
I915_WRITE(reg, val); |
POSTING_READ(reg); |
udelay(200); |
|
pll->on = false; |
} |
|
static void intel_enable_transcoder(struct drm_i915_private *dev_priv, |
1267,20 → 1672,28 |
enum pipe pipe) |
{ |
int reg; |
u32 val; |
u32 val, pipeconf_val; |
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; |
|
/* PCH only available on ILK+ */ |
BUG_ON(dev_priv->info->gen < 5); |
|
/* Make sure PCH DPLL is enabled */ |
assert_pch_pll_enabled(dev_priv, pipe); |
assert_pch_pll_enabled(dev_priv, |
to_intel_crtc(crtc)->pch_pll, |
to_intel_crtc(crtc)); |
|
/* FDI must be feeding us bits for PCH ports */ |
assert_fdi_tx_enabled(dev_priv, pipe); |
assert_fdi_rx_enabled(dev_priv, pipe); |
|
if (IS_HASWELL(dev_priv->dev) && pipe > 0) { |
DRM_ERROR("Attempting to enable transcoder on Haswell with pipe > 0\n"); |
return; |
} |
reg = TRANSCONF(pipe); |
val = I915_READ(reg); |
pipeconf_val = I915_READ(PIPECONF(pipe)); |
|
if (HAS_PCH_IBX(dev_priv->dev)) { |
/* |
1288,8 → 1701,19 |
* that in pipeconf reg. |
*/ |
val &= ~PIPE_BPC_MASK; |
val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; |
val |= pipeconf_val & PIPE_BPC_MASK; |
} |
|
val &= ~TRANS_INTERLACE_MASK; |
if ((pipeconf_val & PIPECONF_INTERLACE_MASK) == PIPECONF_INTERLACED_ILK) |
if (HAS_PCH_IBX(dev_priv->dev) && |
intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) |
val |= TRANS_LEGACY_INTERLACED_ILK; |
else |
val |= TRANS_INTERLACED; |
else |
val |= TRANS_PROGRESSIVE; |
|
I915_WRITE(reg, val | TRANS_ENABLE); |
if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) |
DRM_ERROR("failed to enable transcoder %d\n", pipe); |
1397,6 → 1821,7 |
|
I915_WRITE(reg, val & ~PIPECONF_ENABLE); |
intel_wait_for_pipe_off(dev_priv->dev, pipe); |
|
} |
|
/* |
1403,7 → 1828,7 |
* Plane regs are double buffered, going from enabled->disabled needs a |
* trigger in order to latch. The display address reg provides this. |
*/ |
static void intel_flush_display_plane(struct drm_i915_private *dev_priv, |
void intel_flush_display_plane(struct drm_i915_private *dev_priv, |
enum plane plane) |
{ |
I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane))); |
1461,500 → 1886,6 |
intel_wait_for_vblank(dev_priv->dev, pipe); |
} |
|
static void disable_pch_dp(struct drm_i915_private *dev_priv, |
enum pipe pipe, int reg, u32 port_sel) |
{ |
u32 val = I915_READ(reg); |
if (dp_pipe_enabled(dev_priv, pipe, port_sel, val)) { |
DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe); |
I915_WRITE(reg, val & ~DP_PORT_EN); |
} |
} |
|
static void disable_pch_hdmi(struct drm_i915_private *dev_priv, |
enum pipe pipe, int reg) |
{ |
u32 val = I915_READ(reg); |
if (hdmi_pipe_enabled(dev_priv, val, pipe)) { |
DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n", |
reg, pipe); |
I915_WRITE(reg, val & ~PORT_ENABLE); |
} |
} |
|
/* Disable any ports connected to this transcoder */ |
static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, |
enum pipe pipe) |
{ |
u32 reg, val; |
|
val = I915_READ(PCH_PP_CONTROL); |
I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS); |
|
disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B); |
disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C); |
disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D); |
|
reg = PCH_ADPA; |
val = I915_READ(reg); |
if (adpa_pipe_enabled(dev_priv, val, pipe)) |
I915_WRITE(reg, val & ~ADPA_DAC_ENABLE); |
|
reg = PCH_LVDS; |
val = I915_READ(reg); |
if (lvds_pipe_enabled(dev_priv, val, pipe)) { |
DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val); |
I915_WRITE(reg, val & ~LVDS_PORT_EN); |
POSTING_READ(reg); |
udelay(100); |
} |
|
disable_pch_hdmi(dev_priv, pipe, HDMIB); |
disable_pch_hdmi(dev_priv, pipe, HDMIC); |
disable_pch_hdmi(dev_priv, pipe, HDMID); |
} |
|
static void i8xx_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 fbc_ctl; |
|
/* Disable compression */ |
fbc_ctl = I915_READ(FBC_CONTROL); |
if ((fbc_ctl & FBC_CTL_EN) == 0) |
return; |
|
fbc_ctl &= ~FBC_CTL_EN; |
I915_WRITE(FBC_CONTROL, fbc_ctl); |
|
/* Wait for compressing bit to clear */ |
if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { |
DRM_DEBUG_KMS("FBC idle timed out\n"); |
return; |
} |
|
DRM_DEBUG_KMS("disabled FBC\n"); |
} |
|
static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->fb; |
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); |
struct drm_i915_gem_object *obj = intel_fb->obj; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int cfb_pitch; |
int plane, i; |
u32 fbc_ctl, fbc_ctl2; |
|
cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; |
if (fb->pitches[0] < cfb_pitch) |
cfb_pitch = fb->pitches[0]; |
|
/* FBC_CTL wants 64B units */ |
cfb_pitch = (cfb_pitch / 64) - 1; |
plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; |
|
/* Clear old tags */ |
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) |
I915_WRITE(FBC_TAG + (i * 4), 0); |
|
/* Set it up... */ |
fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; |
fbc_ctl2 |= plane; |
I915_WRITE(FBC_CONTROL2, fbc_ctl2); |
I915_WRITE(FBC_FENCE_OFF, crtc->y); |
|
/* enable it... */ |
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; |
if (IS_I945GM(dev)) |
fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ |
fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; |
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; |
fbc_ctl |= obj->fence_reg; |
I915_WRITE(FBC_CONTROL, fbc_ctl); |
|
DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ", |
cfb_pitch, crtc->y, intel_crtc->plane); |
} |
|
static bool i8xx_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
return I915_READ(FBC_CONTROL) & FBC_CTL_EN; |
} |
|
static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->fb; |
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); |
struct drm_i915_gem_object *obj = intel_fb->obj; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB; |
unsigned long stall_watermark = 200; |
u32 dpfc_ctl; |
|
dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; |
dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg; |
I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); |
|
I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | |
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); |
I915_WRITE(DPFC_FENCE_YOFF, crtc->y); |
|
/* enable it... */ |
I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN); |
|
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); |
} |
|
static void g4x_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 dpfc_ctl; |
|
/* Disable compression */ |
dpfc_ctl = I915_READ(DPFC_CONTROL); |
if (dpfc_ctl & DPFC_CTL_EN) { |
dpfc_ctl &= ~DPFC_CTL_EN; |
I915_WRITE(DPFC_CONTROL, dpfc_ctl); |
|
DRM_DEBUG_KMS("disabled FBC\n"); |
} |
} |
|
static bool g4x_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN; |
} |
|
static void sandybridge_blit_fbc_update(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 blt_ecoskpd; |
|
/* Make sure blitter notifies FBC of writes */ |
gen6_gt_force_wake_get(dev_priv); |
blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD); |
blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY << |
GEN6_BLITTER_LOCK_SHIFT; |
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd); |
blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY; |
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd); |
blt_ecoskpd &= ~(GEN6_BLITTER_FBC_NOTIFY << |
GEN6_BLITTER_LOCK_SHIFT); |
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd); |
POSTING_READ(GEN6_BLITTER_ECOSKPD); |
gen6_gt_force_wake_put(dev_priv); |
} |
|
static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->fb; |
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); |
struct drm_i915_gem_object *obj = intel_fb->obj; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB; |
unsigned long stall_watermark = 200; |
u32 dpfc_ctl; |
|
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); |
dpfc_ctl &= DPFC_RESERVED; |
dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); |
/* Set persistent mode for front-buffer rendering, ala X. */ |
dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE; |
dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg); |
I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); |
|
I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | |
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); |
I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y); |
I915_WRITE(ILK_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID); |
/* enable it... */ |
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); |
|
if (IS_GEN6(dev)) { |
I915_WRITE(SNB_DPFC_CTL_SA, |
SNB_CPU_FENCE_ENABLE | obj->fence_reg); |
I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); |
sandybridge_blit_fbc_update(dev); |
} |
|
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); |
} |
|
static void ironlake_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 dpfc_ctl; |
|
/* Disable compression */ |
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); |
if (dpfc_ctl & DPFC_CTL_EN) { |
dpfc_ctl &= ~DPFC_CTL_EN; |
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); |
|
DRM_DEBUG_KMS("disabled FBC\n"); |
} |
} |
|
static bool ironlake_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN; |
} |
|
bool intel_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (!dev_priv->display.fbc_enabled) |
return false; |
|
return dev_priv->display.fbc_enabled(dev); |
} |
|
|
|
|
|
|
|
|
|
|
static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
{ |
struct intel_fbc_work *work; |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (!dev_priv->display.enable_fbc) |
return; |
|
// intel_cancel_fbc_work(dev_priv); |
|
// work = kzalloc(sizeof *work, GFP_KERNEL); |
// if (work == NULL) { |
// dev_priv->display.enable_fbc(crtc, interval); |
// return; |
// } |
|
// work->crtc = crtc; |
// work->fb = crtc->fb; |
// work->interval = interval; |
// INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn); |
|
// dev_priv->fbc_work = work; |
|
DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); |
|
/* Delay the actual enabling to let pageflipping cease and the |
* display to settle before starting the compression. Note that |
* this delay also serves a second purpose: it allows for a |
* vblank to pass after disabling the FBC before we attempt |
* to modify the control registers. |
* |
* A more complicated solution would involve tracking vblanks |
* following the termination of the page-flipping sequence |
* and indeed performing the enable as a co-routine and not |
* waiting synchronously upon the vblank. |
*/ |
// schedule_delayed_work(&work->work, msecs_to_jiffies(50)); |
} |
|
void intel_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
// intel_cancel_fbc_work(dev_priv); |
|
if (!dev_priv->display.disable_fbc) |
return; |
|
dev_priv->display.disable_fbc(dev); |
dev_priv->cfb_plane = -1; |
} |
|
/** |
* intel_update_fbc - enable/disable FBC as needed |
* @dev: the drm_device |
* |
* Set up the framebuffer compression hardware at mode set time. We |
* enable it if possible: |
* - plane A only (on pre-965) |
* - no pixel mulitply/line duplication |
* - no alpha buffer discard |
* - no dual wide |
* - framebuffer <= 2048 in width, 1536 in height |
* |
* We can't assume that any compression will take place (worst case), |
* so the compressed buffer has to be the same size as the uncompressed |
* one. It also must reside (along with the line length buffer) in |
* stolen memory. |
* |
* We need to enable/disable FBC on a global basis. |
*/ |
static void intel_update_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc = NULL, *tmp_crtc; |
struct intel_crtc *intel_crtc; |
struct drm_framebuffer *fb; |
struct intel_framebuffer *intel_fb; |
struct drm_i915_gem_object *obj; |
int enable_fbc; |
|
DRM_DEBUG_KMS("\n"); |
|
if (!i915_powersave) |
return; |
|
if (!I915_HAS_FBC(dev)) |
return; |
|
/* |
* If FBC is already on, we just have to verify that we can |
* keep it that way... |
* Need to disable if: |
* - more than one pipe is active |
* - changing FBC params (stride, fence, mode) |
* - new fb is too large to fit in compressed buffer |
* - going to an unsupported config (interlace, pixel multiply, etc.) |
*/ |
list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) { |
if (tmp_crtc->enabled && tmp_crtc->fb) { |
if (crtc) { |
DRM_DEBUG_KMS("more than one pipe active, disabling compression\n"); |
dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES; |
goto out_disable; |
} |
crtc = tmp_crtc; |
} |
} |
|
if (!crtc || crtc->fb == NULL) { |
DRM_DEBUG_KMS("no output, disabling\n"); |
dev_priv->no_fbc_reason = FBC_NO_OUTPUT; |
goto out_disable; |
} |
|
intel_crtc = to_intel_crtc(crtc); |
fb = crtc->fb; |
intel_fb = to_intel_framebuffer(fb); |
obj = intel_fb->obj; |
|
enable_fbc = i915_enable_fbc; |
if (enable_fbc < 0) { |
DRM_DEBUG_KMS("fbc set to per-chip default\n"); |
enable_fbc = 1; |
if (INTEL_INFO(dev)->gen <= 6) |
enable_fbc = 0; |
} |
if (!enable_fbc) { |
DRM_DEBUG_KMS("fbc disabled per module param\n"); |
dev_priv->no_fbc_reason = FBC_MODULE_PARAM; |
goto out_disable; |
} |
if (intel_fb->obj->base.size > dev_priv->cfb_size) { |
DRM_DEBUG_KMS("framebuffer too large, disabling " |
"compression\n"); |
dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; |
goto out_disable; |
} |
if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || |
(crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { |
DRM_DEBUG_KMS("mode incompatible with compression, " |
"disabling\n"); |
dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE; |
goto out_disable; |
} |
if ((crtc->mode.hdisplay > 2048) || |
(crtc->mode.vdisplay > 1536)) { |
DRM_DEBUG_KMS("mode too large for compression, disabling\n"); |
dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; |
goto out_disable; |
} |
if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) { |
DRM_DEBUG_KMS("plane not 0, disabling compression\n"); |
dev_priv->no_fbc_reason = FBC_BAD_PLANE; |
goto out_disable; |
} |
|
/* The use of a CPU fence is mandatory in order to detect writes |
* by the CPU to the scanout and trigger updates to the FBC. |
*/ |
// if (obj->tiling_mode != I915_TILING_X || |
// obj->fence_reg == I915_FENCE_REG_NONE) { |
// DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); |
// dev_priv->no_fbc_reason = FBC_NOT_TILED; |
// goto out_disable; |
// } |
|
/* If the kernel debugger is active, always disable compression */ |
if (in_dbg_master()) |
goto out_disable; |
|
/* If the scanout has not changed, don't modify the FBC settings. |
* Note that we make the fundamental assumption that the fb->obj |
* cannot be unpinned (and have its GTT offset and fence revoked) |
* without first being decoupled from the scanout and FBC disabled. |
*/ |
if (dev_priv->cfb_plane == intel_crtc->plane && |
dev_priv->cfb_fb == fb->base.id && |
dev_priv->cfb_y == crtc->y) |
return; |
|
if (intel_fbc_enabled(dev)) { |
/* We update FBC along two paths, after changing fb/crtc |
* configuration (modeswitching) and after page-flipping |
* finishes. For the latter, we know that not only did |
* we disable the FBC at the start of the page-flip |
* sequence, but also more than one vblank has passed. |
* |
* For the former case of modeswitching, it is possible |
* to switch between two FBC valid configurations |
* instantaneously so we do need to disable the FBC |
* before we can modify its control registers. We also |
* have to wait for the next vblank for that to take |
* effect. However, since we delay enabling FBC we can |
* assume that a vblank has passed since disabling and |
* that we can safely alter the registers in the deferred |
* callback. |
* |
* In the scenario that we go from a valid to invalid |
* and then back to valid FBC configuration we have |
* no strict enforcement that a vblank occurred since |
* disabling the FBC. However, along all current pipe |
* disabling paths we do need to wait for a vblank at |
* some point. And we wait before enabling FBC anyway. |
*/ |
DRM_DEBUG_KMS("disabling active FBC for update\n"); |
intel_disable_fbc(dev); |
} |
|
intel_enable_fbc(crtc, 500); |
return; |
|
out_disable: |
/* Multiple disables should be harmless */ |
if (intel_fbc_enabled(dev)) { |
DRM_DEBUG_KMS("unsupported config, disabling FBC\n"); |
intel_disable_fbc(dev); |
} |
} |
|
int |
intel_pin_and_fence_fb_obj(struct drm_device *dev, |
struct drm_i915_gem_object *obj, |
2011,6 → 1942,28 |
return ret; |
} |
|
void intel_unpin_fb_obj(struct drm_i915_gem_object *obj) |
{ |
// i915_gem_object_unpin_fence(obj); |
// i915_gem_object_unpin(obj); |
} |
|
/* Computes the linear offset to the base tile and adjusts x, y. bytes per pixel |
* is assumed to be a power-of-two. */ |
static unsigned long gen4_compute_dspaddr_offset_xtiled(int *x, int *y, |
unsigned int bpp, |
unsigned int pitch) |
{ |
int tile_rows, tiles; |
|
tile_rows = *y / 8; |
*y %= 8; |
tiles = *x / (512/bpp); |
*x %= 512/bpp; |
|
return tile_rows * pitch * 8 + tiles * 4096; |
} |
|
static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, |
int x, int y) |
{ |
2020,7 → 1973,7 |
struct intel_framebuffer *intel_fb; |
struct drm_i915_gem_object *obj; |
int plane = intel_crtc->plane; |
unsigned long Start, Offset; |
unsigned long linear_offset; |
u32 dspcntr; |
u32 reg; |
|
2067,18 → 2020,28 |
|
I915_WRITE(reg, dspcntr); |
|
Start = obj->gtt_offset; |
Offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); |
linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); |
|
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", |
Start, Offset, x, y, fb->pitches[0]); |
if (INTEL_INFO(dev)->gen >= 4) { |
intel_crtc->dspaddr_offset = |
gen4_compute_dspaddr_offset_xtiled(&x, &y, |
fb->bits_per_pixel / 8, |
fb->pitches[0]); |
linear_offset -= intel_crtc->dspaddr_offset; |
} else { |
intel_crtc->dspaddr_offset = linear_offset; |
} |
|
DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n", |
obj->gtt_offset, linear_offset, x, y, fb->pitches[0]); |
I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); |
if (INTEL_INFO(dev)->gen >= 4) { |
I915_WRITE(DSPSURF(plane), Start); |
I915_MODIFY_DISPBASE(DSPSURF(plane), |
obj->gtt_offset + intel_crtc->dspaddr_offset); |
I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); |
I915_WRITE(DSPADDR(plane), Offset); |
I915_WRITE(DSPLINOFF(plane), linear_offset); |
} else |
I915_WRITE(DSPADDR(plane), Start + Offset); |
I915_WRITE(DSPADDR(plane), obj->gtt_offset + linear_offset); |
POSTING_READ(reg); |
|
return 0; |
2093,7 → 2056,7 |
struct intel_framebuffer *intel_fb; |
struct drm_i915_gem_object *obj; |
int plane = intel_crtc->plane; |
unsigned long Start, Offset; |
unsigned long linear_offset; |
u32 dspcntr; |
u32 reg; |
|
2148,15 → 2111,20 |
|
I915_WRITE(reg, dspcntr); |
|
Start = obj->gtt_offset; |
Offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); |
linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); |
intel_crtc->dspaddr_offset = |
gen4_compute_dspaddr_offset_xtiled(&x, &y, |
fb->bits_per_pixel / 8, |
fb->pitches[0]); |
linear_offset -= intel_crtc->dspaddr_offset; |
|
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", |
Start, Offset, x, y, fb->pitches[0]); |
DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n", |
obj->gtt_offset, linear_offset, x, y, fb->pitches[0]); |
I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); |
I915_WRITE(DSPSURF(plane), Start); |
I915_MODIFY_DISPBASE(DSPSURF(plane), |
obj->gtt_offset + intel_crtc->dspaddr_offset); |
I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); |
I915_WRITE(DSPADDR(plane), Offset); |
I915_WRITE(DSPLINOFF(plane), linear_offset); |
POSTING_READ(reg); |
|
return 0; |
2169,54 → 2137,83 |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->display.disable_fbc) |
dev_priv->display.disable_fbc(dev); |
intel_increase_pllclock(crtc); |
|
return dev_priv->display.update_plane(crtc, fb, x, y); |
} |
|
#if 0 |
static int |
intel_finish_fb(struct drm_framebuffer *old_fb) |
{ |
struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; |
struct drm_i915_private *dev_priv = obj->base.dev->dev_private; |
bool was_interruptible = dev_priv->mm.interruptible; |
int ret; |
|
ret = dev_priv->display.update_plane(crtc, fb, x, y); |
if (ret) |
return ret; |
wait_event(dev_priv->pending_flip_queue, |
atomic_read(&dev_priv->mm.wedged) || |
atomic_read(&obj->pending_flip) == 0); |
|
intel_update_fbc(dev); |
intel_increase_pllclock(crtc); |
/* Big Hammer, we also need to ensure that any pending |
* MI_WAIT_FOR_EVENT inside a user batch buffer on the |
* current scanout is retired before unpinning the old |
* framebuffer. |
* |
* This should only fail upon a hung GPU, in which case we |
* can safely continue. |
*/ |
dev_priv->mm.interruptible = false; |
ret = i915_gem_object_finish_gpu(obj); |
dev_priv->mm.interruptible = was_interruptible; |
|
return 0; |
return ret; |
} |
#endif |
|
static int |
intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb) |
struct drm_framebuffer *fb) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_i915_master_private *master_priv; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct drm_framebuffer *old_fb; |
int ret; |
|
ENTER(); |
|
/* no fb bound */ |
if (!crtc->fb) { |
if (!fb) { |
DRM_ERROR("No FB bound\n"); |
return 0; |
} |
|
switch (intel_crtc->plane) { |
case 0: |
case 1: |
break; |
case 2: |
if (IS_IVYBRIDGE(dev)) |
break; |
/* fall through otherwise */ |
default: |
DRM_ERROR("no plane for crtc\n"); |
if(intel_crtc->plane > dev_priv->num_pipe) { |
DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", |
intel_crtc->plane, |
dev_priv->num_pipe); |
return -EINVAL; |
} |
|
mutex_lock(&dev->struct_mutex); |
// ret = intel_pin_and_fence_fb_obj(dev, |
// to_intel_framebuffer(fb)->obj, |
// NULL); |
// if (ret != 0) { |
// mutex_unlock(&dev->struct_mutex); |
// DRM_ERROR("pin & fence failed\n"); |
// return ret; |
// } |
|
ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y, |
LEAVE_ATOMIC_MODE_SET); |
// if (crtc->fb) |
// intel_finish_fb(crtc->fb); |
|
ret = dev_priv->display.update_plane(crtc, fb, x, y); |
if (ret) { |
i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); |
intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj); |
mutex_unlock(&dev->struct_mutex); |
DRM_ERROR("failed to update base address\n"); |
LEAVE(); |
2223,13 → 2220,20 |
return ret; |
} |
|
old_fb = crtc->fb; |
crtc->fb = fb; |
crtc->x = x; |
crtc->y = y; |
|
if (old_fb) { |
intel_wait_for_vblank(dev, intel_crtc->pipe); |
intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); |
} |
|
intel_update_fbc(dev); |
mutex_unlock(&dev->struct_mutex); |
|
|
LEAVE(); |
return 0; |
|
|
} |
|
static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) |
2433,7 → 2437,7 |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
u32 reg, temp, i; |
u32 reg, temp, i, retry; |
|
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit |
for train result */ |
2485,16 → 2489,20 |
POSTING_READ(reg); |
udelay(500); |
|
for (retry = 0; retry < 5; retry++) { |
reg = FDI_RX_IIR(pipe); |
temp = I915_READ(reg); |
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); |
|
if (temp & FDI_RX_BIT_LOCK) { |
I915_WRITE(reg, temp | FDI_RX_BIT_LOCK); |
DRM_DEBUG_KMS("FDI train 1 done.\n"); |
break; |
} |
udelay(50); |
} |
if (retry < 5) |
break; |
} |
if (i == 4) |
DRM_ERROR("FDI train 1 fail!\n"); |
|
2534,16 → 2542,20 |
POSTING_READ(reg); |
udelay(500); |
|
for (retry = 0; retry < 5; retry++) { |
reg = FDI_RX_IIR(pipe); |
temp = I915_READ(reg); |
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); |
|
if (temp & FDI_RX_SYMBOL_LOCK) { |
I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK); |
DRM_DEBUG_KMS("FDI train 2 done.\n"); |
break; |
} |
udelay(50); |
} |
if (retry < 5) |
break; |
} |
if (i == 4) |
DRM_ERROR("FDI train 2 fail!\n"); |
|
2664,11 → 2676,10 |
DRM_DEBUG_KMS("FDI train done.\n"); |
} |
|
static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) |
static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_device *dev = intel_crtc->base.dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
u32 reg, temp; |
|
2694,6 → 2705,9 |
POSTING_READ(reg); |
udelay(200); |
|
/* On Haswell, the PLL configuration for ports and pipes is handled |
* separately, as part of DDI setup */ |
if (!IS_HASWELL(dev)) { |
/* Enable CPU FDI TX PLL, always on for Ironlake */ |
reg = FDI_TX_CTL(pipe); |
temp = I915_READ(reg); |
2704,7 → 2718,37 |
udelay(100); |
} |
} |
} |
|
static void ironlake_fdi_pll_disable(struct intel_crtc *intel_crtc) |
{ |
struct drm_device *dev = intel_crtc->base.dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe = intel_crtc->pipe; |
u32 reg, temp; |
|
/* Switch from PCDclk to Rawclk */ |
reg = FDI_RX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_PCDCLK); |
|
/* Disable CPU FDI TX PLL */ |
reg = FDI_TX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); |
|
POSTING_READ(reg); |
udelay(100); |
|
reg = FDI_RX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); |
|
/* Wait for the clocks to turn off. */ |
POSTING_READ(reg); |
udelay(100); |
} |
|
static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
2774,57 → 2818,71 |
udelay(100); |
} |
|
/* |
* When we disable a pipe, we need to clear any pending scanline wait events |
* to avoid hanging the ring, which we assume we are waiting on. |
*/ |
static void intel_clear_scanline_wait(struct drm_device *dev) |
static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_ring_buffer *ring; |
u32 tmp; |
unsigned long flags; |
bool pending; |
|
if (IS_GEN2(dev)) |
/* Can't break the hang on i8xx */ |
return; |
if (atomic_read(&dev_priv->mm.wedged)) |
return false; |
|
ring = LP_RING(dev_priv); |
tmp = I915_READ_CTL(ring); |
if (tmp & RING_WAIT) |
I915_WRITE_CTL(ring, tmp); |
spin_lock_irqsave(&dev->event_lock, flags); |
pending = to_intel_crtc(crtc)->unpin_work != NULL; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
|
return pending; |
} |
|
#if 0 |
static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) |
{ |
struct drm_i915_gem_object *obj; |
struct drm_i915_private *dev_priv; |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (crtc->fb == NULL) |
return; |
|
obj = to_intel_framebuffer(crtc->fb)->obj; |
dev_priv = crtc->dev->dev_private; |
wait_event(dev_priv->pending_flip_queue, |
atomic_read(&obj->pending_flip) == 0); |
!intel_crtc_has_pending_flip(crtc)); |
|
mutex_lock(&dev->struct_mutex); |
intel_finish_fb(crtc->fb); |
mutex_unlock(&dev->struct_mutex); |
} |
#endif |
|
static bool intel_crtc_driving_pch(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_mode_config *mode_config = &dev->mode_config; |
struct intel_encoder *encoder; |
struct intel_encoder *intel_encoder; |
|
/* |
* If there's a non-PCH eDP on this crtc, it must be DP_A, and that |
* must be driven by its own crtc; no sharing is possible. |
*/ |
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { |
if (encoder->base.crtc != crtc) |
continue; |
for_each_encoder_on_crtc(dev, crtc, intel_encoder) { |
|
switch (encoder->type) { |
/* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell |
* CPU handles all others */ |
if (IS_HASWELL(dev)) { |
/* It is still unclear how this will work on PPT, so throw up a warning */ |
WARN_ON(!HAS_PCH_LPT(dev)); |
|
if (intel_encoder->type == INTEL_OUTPUT_ANALOG) { |
DRM_DEBUG_KMS("Haswell detected DAC encoder, assuming is PCH\n"); |
return true; |
} else { |
DRM_DEBUG_KMS("Haswell detected encoder %d, assuming is CPU\n", |
intel_encoder->type); |
return false; |
} |
} |
|
switch (intel_encoder->type) { |
case INTEL_OUTPUT_EDP: |
if (!intel_encoder_is_pch_edp(&encoder->base)) |
if (!intel_encoder_is_pch_edp(&intel_encoder->base)) |
return false; |
continue; |
} |
2833,6 → 2891,97 |
return true; |
} |
|
/* Program iCLKIP clock to the desired frequency */ |
static void lpt_program_iclkip(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 divsel, phaseinc, auxdiv, phasedir = 0; |
u32 temp; |
|
/* It is necessary to ungate the pixclk gate prior to programming |
* the divisors, and gate it back when it is done. |
*/ |
I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_GATE); |
|
/* Disable SSCCTL */ |
intel_sbi_write(dev_priv, SBI_SSCCTL6, |
intel_sbi_read(dev_priv, SBI_SSCCTL6) | |
SBI_SSCCTL_DISABLE); |
|
/* 20MHz is a corner case which is out of range for the 7-bit divisor */ |
if (crtc->mode.clock == 20000) { |
auxdiv = 1; |
divsel = 0x41; |
phaseinc = 0x20; |
} else { |
/* The iCLK virtual clock root frequency is in MHz, |
* but the crtc->mode.clock in in KHz. To get the divisors, |
* it is necessary to divide one by another, so we |
* convert the virtual clock precision to KHz here for higher |
* precision. |
*/ |
u32 iclk_virtual_root_freq = 172800 * 1000; |
u32 iclk_pi_range = 64; |
u32 desired_divisor, msb_divisor_value, pi_value; |
|
desired_divisor = (iclk_virtual_root_freq / crtc->mode.clock); |
msb_divisor_value = desired_divisor / iclk_pi_range; |
pi_value = desired_divisor % iclk_pi_range; |
|
auxdiv = 0; |
divsel = msb_divisor_value - 2; |
phaseinc = pi_value; |
} |
|
/* This should not happen with any sane values */ |
WARN_ON(SBI_SSCDIVINTPHASE_DIVSEL(divsel) & |
~SBI_SSCDIVINTPHASE_DIVSEL_MASK); |
WARN_ON(SBI_SSCDIVINTPHASE_DIR(phasedir) & |
~SBI_SSCDIVINTPHASE_INCVAL_MASK); |
|
DRM_DEBUG_KMS("iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n", |
crtc->mode.clock, |
auxdiv, |
divsel, |
phasedir, |
phaseinc); |
|
/* Program SSCDIVINTPHASE6 */ |
temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6); |
temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK; |
temp |= SBI_SSCDIVINTPHASE_DIVSEL(divsel); |
temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK; |
temp |= SBI_SSCDIVINTPHASE_INCVAL(phaseinc); |
temp |= SBI_SSCDIVINTPHASE_DIR(phasedir); |
temp |= SBI_SSCDIVINTPHASE_PROPAGATE; |
|
intel_sbi_write(dev_priv, |
SBI_SSCDIVINTPHASE6, |
temp); |
|
/* Program SSCAUXDIV */ |
temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6); |
temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1); |
temp |= SBI_SSCAUXDIV_FINALDIV2SEL(auxdiv); |
intel_sbi_write(dev_priv, |
SBI_SSCAUXDIV6, |
temp); |
|
|
/* Enable modulator and associated divider */ |
temp = intel_sbi_read(dev_priv, SBI_SSCCTL6); |
temp &= ~SBI_SSCCTL_DISABLE; |
intel_sbi_write(dev_priv, |
SBI_SSCCTL6, |
temp); |
|
/* Wait for initialization time */ |
udelay(24); |
|
I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); |
} |
|
/* |
* Enable PCH resources required for PCH ports: |
* - PCH PLLs |
2847,29 → 2996,41 |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
u32 reg, temp, transc_sel; |
u32 reg, temp; |
|
assert_transcoder_disabled(dev_priv, pipe); |
|
/* For PCH output, training FDI link */ |
dev_priv->display.fdi_link_train(crtc); |
|
intel_enable_pch_pll(dev_priv, pipe); |
intel_enable_pch_pll(intel_crtc); |
|
if (HAS_PCH_CPT(dev)) { |
transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : |
TRANSC_DPLLB_SEL; |
if (HAS_PCH_LPT(dev)) { |
DRM_DEBUG_KMS("LPT detected: programming iCLKIP\n"); |
lpt_program_iclkip(crtc); |
} else if (HAS_PCH_CPT(dev)) { |
u32 sel; |
|
/* Be sure PCH DPLL SEL is set */ |
temp = I915_READ(PCH_DPLL_SEL); |
if (pipe == 0) { |
temp &= ~(TRANSA_DPLLB_SEL); |
temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); |
} else if (pipe == 1) { |
temp &= ~(TRANSB_DPLLB_SEL); |
temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); |
} else if (pipe == 2) { |
temp &= ~(TRANSC_DPLLB_SEL); |
temp |= (TRANSC_DPLL_ENABLE | transc_sel); |
switch (pipe) { |
default: |
case 0: |
temp |= TRANSA_DPLL_ENABLE; |
sel = TRANSA_DPLLB_SEL; |
break; |
case 1: |
temp |= TRANSB_DPLL_ENABLE; |
sel = TRANSB_DPLLB_SEL; |
break; |
case 2: |
temp |= TRANSC_DPLL_ENABLE; |
sel = TRANSC_DPLLB_SEL; |
break; |
} |
if (intel_crtc->pch_pll->pll_reg == _PCH_DPLL_B) |
temp |= sel; |
else |
temp &= ~sel; |
I915_WRITE(PCH_DPLL_SEL, temp); |
} |
|
2882,7 → 3043,9 |
I915_WRITE(TRANS_VTOTAL(pipe), I915_READ(VTOTAL(pipe))); |
I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe))); |
I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); |
I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe))); |
|
if (!IS_HASWELL(dev)) |
intel_fdi_normal_train(crtc); |
|
/* For PCH DP, enable TRANS_DP_CTL */ |
2926,6 → 3089,93 |
intel_enable_transcoder(dev_priv, pipe); |
} |
|
static void intel_put_pch_pll(struct intel_crtc *intel_crtc) |
{ |
struct intel_pch_pll *pll = intel_crtc->pch_pll; |
|
if (pll == NULL) |
return; |
|
if (pll->refcount == 0) { |
WARN(1, "bad PCH PLL refcount\n"); |
return; |
} |
|
--pll->refcount; |
intel_crtc->pch_pll = NULL; |
} |
|
static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) |
{ |
struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; |
struct intel_pch_pll *pll; |
int i; |
|
pll = intel_crtc->pch_pll; |
if (pll) { |
DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n", |
intel_crtc->base.base.id, pll->pll_reg); |
goto prepare; |
} |
|
if (HAS_PCH_IBX(dev_priv->dev)) { |
/* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ |
i = intel_crtc->pipe; |
pll = &dev_priv->pch_plls[i]; |
|
DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", |
intel_crtc->base.base.id, pll->pll_reg); |
|
goto found; |
} |
|
for (i = 0; i < dev_priv->num_pch_pll; i++) { |
pll = &dev_priv->pch_plls[i]; |
|
/* Only want to check enabled timings first */ |
if (pll->refcount == 0) |
continue; |
|
if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && |
fp == I915_READ(pll->fp0_reg)) { |
DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n", |
intel_crtc->base.base.id, |
pll->pll_reg, pll->refcount, pll->active); |
|
goto found; |
} |
} |
|
/* Ok no matching timings, maybe there's a free one? */ |
for (i = 0; i < dev_priv->num_pch_pll; i++) { |
pll = &dev_priv->pch_plls[i]; |
if (pll->refcount == 0) { |
DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", |
intel_crtc->base.base.id, pll->pll_reg); |
goto found; |
} |
} |
|
return NULL; |
|
found: |
intel_crtc->pch_pll = pll; |
pll->refcount++; |
DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); |
prepare: /* separate function? */ |
DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); |
|
/* Wait for the clocks to stabilize before rewriting the regs */ |
I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); |
POSTING_READ(pll->pll_reg); |
udelay(150); |
|
I915_WRITE(pll->fp0_reg, fp); |
I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); |
pll->on = false; |
return pll; |
} |
|
void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
2949,11 → 3199,14 |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_encoder *encoder; |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
u32 temp; |
bool is_pch_port; |
|
WARN_ON(!crtc->enabled); |
|
if (intel_crtc->active) |
return; |
|
2968,11 → 3221,17 |
|
is_pch_port = intel_crtc_driving_pch(crtc); |
|
if (is_pch_port) |
ironlake_fdi_pll_enable(crtc); |
else |
ironlake_fdi_disable(crtc); |
if (is_pch_port) { |
ironlake_fdi_pll_enable(intel_crtc); |
} else { |
assert_fdi_tx_disabled(dev_priv, pipe); |
assert_fdi_rx_disabled(dev_priv, pipe); |
} |
|
for_each_encoder_on_crtc(dev, crtc, encoder) |
if (encoder->pre_enable) |
encoder->pre_enable(encoder); |
|
/* Enable panel fitting for LVDS */ |
if (dev_priv->pch_pf_size && |
(intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || HAS_eDP)) { |
3002,6 → 3261,22 |
mutex_unlock(&dev->struct_mutex); |
|
// intel_crtc_update_cursor(crtc, true); |
|
for_each_encoder_on_crtc(dev, crtc, encoder) |
encoder->enable(encoder); |
|
if (HAS_PCH_CPT(dev)) |
intel_cpt_verify_modeset(dev, intel_crtc->pipe); |
|
/* |
* There seems to be a race in PCH platform hw (at least on some |
* outputs) where an enabled pipe still completes any pageflip right |
* away (as if the pipe is off) instead of waiting for vblank. As soon |
* as the first vblank happend, everything works as expected. Hence just |
* wait for one vblank before returning to avoid strange things |
* happening. |
*/ |
intel_wait_for_vblank(dev, intel_crtc->pipe); |
} |
|
static void ironlake_crtc_disable(struct drm_crtc *crtc) |
3009,16 → 3284,19 |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_encoder *encoder; |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
u32 reg, temp; |
|
|
if (!intel_crtc->active) |
return; |
|
ENTER(); |
for_each_encoder_on_crtc(dev, crtc, encoder) |
encoder->disable(encoder); |
|
intel_crtc_wait_for_pending_flips(crtc); |
// intel_crtc_wait_for_pending_flips(crtc); |
// drm_vblank_off(dev, pipe); |
// intel_crtc_update_cursor(crtc, false); |
|
3033,15 → 3311,12 |
I915_WRITE(PF_CTL(pipe), 0); |
I915_WRITE(PF_WIN_SZ(pipe), 0); |
|
for_each_encoder_on_crtc(dev, crtc, encoder) |
if (encoder->post_disable) |
encoder->post_disable(encoder); |
|
ironlake_fdi_disable(crtc); |
|
/* This is a horrible layering violation; we should be doing this in |
* the connector/encoder ->prepare instead, but we don't always have |
* enough information there about the config to know whether it will |
* actually be necessary or just cause undesired flicker. |
*/ |
intel_disable_pch_ports(dev_priv, pipe); |
|
intel_disable_transcoder(dev_priv, pipe); |
|
if (HAS_PCH_CPT(dev)) { |
3072,65 → 3347,24 |
} |
|
/* disable PCH DPLL */ |
if (!intel_crtc->no_pll) |
intel_disable_pch_pll(dev_priv, pipe); |
intel_disable_pch_pll(intel_crtc); |
|
/* Switch from PCDclk to Rawclk */ |
reg = FDI_RX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_PCDCLK); |
ironlake_fdi_pll_disable(intel_crtc); |
|
/* Disable CPU FDI TX PLL */ |
reg = FDI_TX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); |
|
POSTING_READ(reg); |
udelay(100); |
|
reg = FDI_RX_CTL(pipe); |
temp = I915_READ(reg); |
I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); |
|
/* Wait for the clocks to turn off. */ |
POSTING_READ(reg); |
udelay(100); |
|
intel_crtc->active = false; |
intel_update_watermarks(dev); |
|
mutex_lock(&dev->struct_mutex); |
intel_update_fbc(dev); |
intel_clear_scanline_wait(dev); |
mutex_unlock(&dev->struct_mutex); |
|
LEAVE(); |
|
} |
|
static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) |
static void ironlake_crtc_off(struct drm_crtc *crtc) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
|
/* 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_KMS("crtc %d/%d dpms on\n", pipe, plane); |
ironlake_crtc_enable(crtc); |
break; |
|
case DRM_MODE_DPMS_OFF: |
DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane); |
ironlake_crtc_disable(crtc); |
break; |
intel_put_pch_pll(intel_crtc); |
} |
} |
|
static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) |
{ |
3155,9 → 3389,12 |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_encoder *encoder; |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
|
WARN_ON(!crtc->enabled); |
|
if (intel_crtc->active) |
return; |
|
3174,6 → 3411,9 |
/* Give the overlay scaler a chance to enable if it's on this pipe */ |
intel_crtc_dpms_overlay(intel_crtc, true); |
// intel_crtc_update_cursor(crtc, true); |
|
for_each_encoder_on_crtc(dev, crtc, encoder) |
encoder->enable(encoder); |
} |
|
static void i9xx_crtc_disable(struct drm_crtc *crtc) |
3181,14 → 3421,19 |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_encoder *encoder; |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
|
|
if (!intel_crtc->active) |
return; |
|
for_each_encoder_on_crtc(dev, crtc, encoder) |
encoder->disable(encoder); |
|
/* Give the overlay scaler a chance to disable if it's on this pipe */ |
intel_crtc_wait_for_pending_flips(crtc); |
// intel_crtc_wait_for_pending_flips(crtc); |
// drm_vblank_off(dev, pipe); |
intel_crtc_dpms_overlay(intel_crtc, false); |
// intel_crtc_update_cursor(crtc, false); |
3203,45 → 3448,21 |
intel_crtc->active = false; |
intel_update_fbc(dev); |
intel_update_watermarks(dev); |
intel_clear_scanline_wait(dev); |
} |
|
static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) |
static void i9xx_crtc_off(struct drm_crtc *crtc) |
{ |
/* 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: |
i9xx_crtc_enable(crtc); |
break; |
case DRM_MODE_DPMS_OFF: |
i9xx_crtc_disable(crtc); |
break; |
} |
} |
|
/** |
* Sets the power management mode of the pipe and plane. |
*/ |
static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) |
static void intel_crtc_update_sarea(struct drm_crtc *crtc, |
bool enabled) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_i915_master_private *master_priv; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
bool enabled; |
|
if (intel_crtc->dpms_mode == mode) |
return; |
|
intel_crtc->dpms_mode = mode; |
|
dev_priv->display.dpms(crtc, mode); |
|
#if 0 |
if (!dev->primary->master) |
return; |
3250,8 → 3471,6 |
if (!master_priv->sarea_priv) |
return; |
|
enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; |
|
switch (pipe) { |
case 0: |
master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0; |
3269,79 → 3488,177 |
|
} |
|
/** |
* Sets the power management mode of the pipe and plane. |
*/ |
void intel_crtc_update_dpms(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_encoder *intel_encoder; |
bool enable = false; |
|
for_each_encoder_on_crtc(dev, crtc, intel_encoder) |
enable |= intel_encoder->connectors_active; |
|
if (enable) |
dev_priv->display.crtc_enable(crtc); |
else |
dev_priv->display.crtc_disable(crtc); |
|
intel_crtc_update_sarea(crtc, enable); |
} |
|
static void intel_crtc_noop(struct drm_crtc *crtc) |
{ |
} |
|
static void intel_crtc_disable(struct drm_crtc *crtc) |
{ |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
struct drm_device *dev = crtc->dev; |
struct drm_connector *connector; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
/* crtc should still be enabled when we disable it. */ |
WARN_ON(!crtc->enabled); |
|
if (crtc->fb) { |
mutex_lock(&dev->struct_mutex); |
i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); |
mutex_unlock(&dev->struct_mutex); |
dev_priv->display.crtc_disable(crtc); |
intel_crtc_update_sarea(crtc, false); |
dev_priv->display.off(crtc); |
|
assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); |
assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe); |
|
// if (crtc->fb) { |
// mutex_lock(&dev->struct_mutex); |
// intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj); |
// mutex_unlock(&dev->struct_mutex); |
// crtc->fb = NULL; |
// } |
|
/* Update computed state. */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder || !connector->encoder->crtc) |
continue; |
|
if (connector->encoder->crtc != crtc) |
continue; |
|
connector->dpms = DRM_MODE_DPMS_OFF; |
to_intel_encoder(connector->encoder)->connectors_active = false; |
} |
} |
|
/* Prepare for a mode set. |
* |
* Note we could be a lot smarter here. We need to figure out which outputs |
* will be enabled, which disabled (in short, how the config will changes) |
* and perform the minimum necessary steps to accomplish that, e.g. updating |
* watermarks, FBC configuration, making sure PLLs are programmed correctly, |
* panel fitting is in the proper state, etc. |
*/ |
static void i9xx_crtc_prepare(struct drm_crtc *crtc) |
void intel_modeset_disable(struct drm_device *dev) |
{ |
i9xx_crtc_disable(crtc); |
struct drm_crtc *crtc; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->enabled) |
intel_crtc_disable(crtc); |
} |
} |
|
static void i9xx_crtc_commit(struct drm_crtc *crtc) |
void intel_encoder_noop(struct drm_encoder *encoder) |
{ |
i9xx_crtc_enable(crtc); |
} |
|
static void ironlake_crtc_prepare(struct drm_crtc *crtc) |
void intel_encoder_destroy(struct drm_encoder *encoder) |
{ |
ironlake_crtc_disable(crtc); |
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); |
|
drm_encoder_cleanup(encoder); |
kfree(intel_encoder); |
} |
|
static void ironlake_crtc_commit(struct drm_crtc *crtc) |
/* Simple dpms helper for encodres with just one connector, no cloning and only |
* one kind of off state. It clamps all !ON modes to fully OFF and changes the |
* state of the entire output pipe. */ |
void intel_encoder_dpms(struct intel_encoder *encoder, int mode) |
{ |
ironlake_crtc_enable(crtc); |
if (mode == DRM_MODE_DPMS_ON) { |
encoder->connectors_active = true; |
|
intel_crtc_update_dpms(encoder->base.crtc); |
} else { |
encoder->connectors_active = false; |
|
intel_crtc_update_dpms(encoder->base.crtc); |
} |
} |
|
void intel_encoder_prepare(struct drm_encoder *encoder) |
/* Cross check the actual hw state with our own modeset state tracking (and it's |
* internal consistency). */ |
static void intel_connector_check_state(struct intel_connector *connector) |
{ |
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
/* lvds has its own version of prepare see intel_lvds_prepare */ |
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); |
if (connector->get_hw_state(connector)) { |
struct intel_encoder *encoder = connector->encoder; |
struct drm_crtc *crtc; |
bool encoder_enabled; |
enum pipe pipe; |
|
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
connector->base.base.id, |
drm_get_connector_name(&connector->base)); |
|
WARN(connector->base.dpms == DRM_MODE_DPMS_OFF, |
"wrong connector dpms state\n"); |
WARN(connector->base.encoder != &encoder->base, |
"active connector not linked to encoder\n"); |
WARN(!encoder->connectors_active, |
"encoder->connectors_active not set\n"); |
|
encoder_enabled = encoder->get_hw_state(encoder, &pipe); |
WARN(!encoder_enabled, "encoder not enabled\n"); |
if (WARN_ON(!encoder->base.crtc)) |
return; |
|
crtc = encoder->base.crtc; |
|
WARN(!crtc->enabled, "crtc not enabled\n"); |
WARN(!to_intel_crtc(crtc)->active, "crtc not active\n"); |
WARN(pipe != to_intel_crtc(crtc)->pipe, |
"encoder active on the wrong pipe\n"); |
} |
} |
|
void intel_encoder_commit(struct drm_encoder *encoder) |
/* Even simpler default implementation, if there's really no special case to |
* consider. */ |
void intel_connector_dpms(struct drm_connector *connector, int mode) |
{ |
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
struct drm_device *dev = encoder->dev; |
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); |
struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc); |
struct intel_encoder *encoder = intel_attached_encoder(connector); |
|
/* lvds has its own version of commit see intel_lvds_commit */ |
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); |
/* All the simple cases only support two dpms states. */ |
if (mode != DRM_MODE_DPMS_ON) |
mode = DRM_MODE_DPMS_OFF; |
|
if (HAS_PCH_CPT(dev)) |
intel_cpt_verify_modeset(dev, intel_crtc->pipe); |
if (mode == connector->dpms) |
return; |
|
connector->dpms = mode; |
|
/* Only need to change hw state when actually enabled */ |
if (encoder->base.crtc) |
intel_encoder_dpms(encoder, mode); |
else |
WARN_ON(encoder->connectors_active != false); |
|
intel_modeset_check_state(connector->dev); |
} |
|
void intel_encoder_destroy(struct drm_encoder *encoder) |
/* Simple connector->get_hw_state implementation for encoders that support only |
* one connector and no cloning and hence the encoder state determines the state |
* of the connector. */ |
bool intel_connector_get_hw_state(struct intel_connector *connector) |
{ |
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); |
enum pipe pipe = 0; |
struct intel_encoder *encoder = connector->encoder; |
|
drm_encoder_cleanup(encoder); |
kfree(intel_encoder); |
return encoder->get_hw_state(encoder, &pipe); |
} |
|
static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
const struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = crtc->dev; |
3352,15 → 3669,27 |
return false; |
} |
|
/* XXX some encoders set the crtcinfo, others don't. |
* Obviously we need some form of conflict resolution here... |
*/ |
if (adjusted_mode->crtc_htotal == 0) |
/* All interlaced capable intel hw wants timings in frames. Note though |
* that intel_lvds_mode_fixup does some funny tricks with the crtc |
* timings, so we need to be careful not to clobber these.*/ |
if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET)) |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
|
/* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes |
* with a hsync front porch of 0. |
*/ |
if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && |
adjusted_mode->hsync_start == adjusted_mode->hdisplay) |
return false; |
|
return true; |
} |
|
static int valleyview_get_display_clock_speed(struct drm_device *dev) |
{ |
return 400000; /* FIXME */ |
} |
|
static int i945_get_display_clock_speed(struct drm_device *dev) |
{ |
return 400000; |
3458,1323 → 3787,6 |
fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); |
} |
|
|
struct intel_watermark_params { |
unsigned long fifo_size; |
unsigned long max_wm; |
unsigned long default_wm; |
unsigned long guard_size; |
unsigned long cacheline_size; |
}; |
|
/* Pineview has different values for various configs */ |
static const struct intel_watermark_params pineview_display_wm = { |
PINEVIEW_DISPLAY_FIFO, |
PINEVIEW_MAX_WM, |
PINEVIEW_DFT_WM, |
PINEVIEW_GUARD_WM, |
PINEVIEW_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params pineview_display_hplloff_wm = { |
PINEVIEW_DISPLAY_FIFO, |
PINEVIEW_MAX_WM, |
PINEVIEW_DFT_HPLLOFF_WM, |
PINEVIEW_GUARD_WM, |
PINEVIEW_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params pineview_cursor_wm = { |
PINEVIEW_CURSOR_FIFO, |
PINEVIEW_CURSOR_MAX_WM, |
PINEVIEW_CURSOR_DFT_WM, |
PINEVIEW_CURSOR_GUARD_WM, |
PINEVIEW_FIFO_LINE_SIZE, |
}; |
static const struct intel_watermark_params pineview_cursor_hplloff_wm = { |
PINEVIEW_CURSOR_FIFO, |
PINEVIEW_CURSOR_MAX_WM, |
PINEVIEW_CURSOR_DFT_WM, |
PINEVIEW_CURSOR_GUARD_WM, |
PINEVIEW_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params g4x_wm_info = { |
G4X_FIFO_SIZE, |
G4X_MAX_WM, |
G4X_MAX_WM, |
2, |
G4X_FIFO_LINE_SIZE, |
}; |
static const struct intel_watermark_params g4x_cursor_wm_info = { |
I965_CURSOR_FIFO, |
I965_CURSOR_MAX_WM, |
I965_CURSOR_DFT_WM, |
2, |
G4X_FIFO_LINE_SIZE, |
}; |
static const struct intel_watermark_params i965_cursor_wm_info = { |
I965_CURSOR_FIFO, |
I965_CURSOR_MAX_WM, |
I965_CURSOR_DFT_WM, |
2, |
I915_FIFO_LINE_SIZE, |
}; |
static const struct intel_watermark_params i945_wm_info = { |
I945_FIFO_SIZE, |
I915_MAX_WM, |
1, |
2, |
I915_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params i915_wm_info = { |
I915_FIFO_SIZE, |
I915_MAX_WM, |
1, |
2, |
I915_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params i855_wm_info = { |
I855GM_FIFO_SIZE, |
I915_MAX_WM, |
1, |
2, |
I830_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params i830_wm_info = { |
I830_FIFO_SIZE, |
I915_MAX_WM, |
1, |
2, |
I830_FIFO_LINE_SIZE |
}; |
|
static const struct intel_watermark_params ironlake_display_wm_info = { |
ILK_DISPLAY_FIFO, |
ILK_DISPLAY_MAXWM, |
ILK_DISPLAY_DFTWM, |
2, |
ILK_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params ironlake_cursor_wm_info = { |
ILK_CURSOR_FIFO, |
ILK_CURSOR_MAXWM, |
ILK_CURSOR_DFTWM, |
2, |
ILK_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params ironlake_display_srwm_info = { |
ILK_DISPLAY_SR_FIFO, |
ILK_DISPLAY_MAX_SRWM, |
ILK_DISPLAY_DFT_SRWM, |
2, |
ILK_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params ironlake_cursor_srwm_info = { |
ILK_CURSOR_SR_FIFO, |
ILK_CURSOR_MAX_SRWM, |
ILK_CURSOR_DFT_SRWM, |
2, |
ILK_FIFO_LINE_SIZE |
}; |
|
static const struct intel_watermark_params sandybridge_display_wm_info = { |
SNB_DISPLAY_FIFO, |
SNB_DISPLAY_MAXWM, |
SNB_DISPLAY_DFTWM, |
2, |
SNB_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params sandybridge_cursor_wm_info = { |
SNB_CURSOR_FIFO, |
SNB_CURSOR_MAXWM, |
SNB_CURSOR_DFTWM, |
2, |
SNB_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params sandybridge_display_srwm_info = { |
SNB_DISPLAY_SR_FIFO, |
SNB_DISPLAY_MAX_SRWM, |
SNB_DISPLAY_DFT_SRWM, |
2, |
SNB_FIFO_LINE_SIZE |
}; |
static const struct intel_watermark_params sandybridge_cursor_srwm_info = { |
SNB_CURSOR_SR_FIFO, |
SNB_CURSOR_MAX_SRWM, |
SNB_CURSOR_DFT_SRWM, |
2, |
SNB_FIFO_LINE_SIZE |
}; |
|
|
/** |
* intel_calculate_wm - calculate watermark level |
* @clock_in_khz: pixel clock |
* @wm: chip FIFO params |
* @pixel_size: display pixel size |
* @latency_ns: memory latency for the platform |
* |
* Calculate the watermark level (the level at which the display plane will |
* start fetching from memory again). Each chip has a different display |
* FIFO size and allocation, so the caller needs to figure that out and pass |
* in the correct intel_watermark_params structure. |
* |
* As the pixel clock runs, the FIFO will be drained at a rate that depends |
* on the pixel size. When it reaches the watermark level, it'll start |
* fetching FIFO line sized based chunks from memory until the FIFO fills |
* past the watermark point. If the FIFO drains completely, a FIFO underrun |
* will occur, and a display engine hang could result. |
*/ |
static unsigned long intel_calculate_wm(unsigned long clock_in_khz, |
const struct intel_watermark_params *wm, |
int fifo_size, |
int pixel_size, |
unsigned long latency_ns) |
{ |
long entries_required, wm_size; |
|
/* |
* Note: we need to make sure we don't overflow for various clock & |
* latency values. |
* clocks go from a few thousand to several hundred thousand. |
* latency is usually a few thousand |
*/ |
entries_required = ((clock_in_khz / 1000) * pixel_size * latency_ns) / |
1000; |
entries_required = DIV_ROUND_UP(entries_required, wm->cacheline_size); |
|
DRM_DEBUG_KMS("FIFO entries required for mode: %ld\n", entries_required); |
|
wm_size = fifo_size - (entries_required + wm->guard_size); |
|
DRM_DEBUG_KMS("FIFO watermark level: %ld\n", wm_size); |
|
/* Don't promote wm_size to unsigned... */ |
if (wm_size > (long)wm->max_wm) |
wm_size = wm->max_wm; |
if (wm_size <= 0) |
wm_size = wm->default_wm; |
return wm_size; |
} |
|
struct cxsr_latency { |
int is_desktop; |
int is_ddr3; |
unsigned long fsb_freq; |
unsigned long mem_freq; |
unsigned long display_sr; |
unsigned long display_hpll_disable; |
unsigned long cursor_sr; |
unsigned long cursor_hpll_disable; |
}; |
|
static const struct cxsr_latency cxsr_latency_table[] = { |
{1, 0, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */ |
{1, 0, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */ |
{1, 0, 800, 800, 3347, 33347, 3763, 33763}, /* DDR2-800 SC */ |
{1, 1, 800, 667, 6420, 36420, 6873, 36873}, /* DDR3-667 SC */ |
{1, 1, 800, 800, 5902, 35902, 6318, 36318}, /* DDR3-800 SC */ |
|
{1, 0, 667, 400, 3400, 33400, 4021, 34021}, /* DDR2-400 SC */ |
{1, 0, 667, 667, 3372, 33372, 3845, 33845}, /* DDR2-667 SC */ |
{1, 0, 667, 800, 3386, 33386, 3822, 33822}, /* DDR2-800 SC */ |
{1, 1, 667, 667, 6438, 36438, 6911, 36911}, /* DDR3-667 SC */ |
{1, 1, 667, 800, 5941, 35941, 6377, 36377}, /* DDR3-800 SC */ |
|
{1, 0, 400, 400, 3472, 33472, 4173, 34173}, /* DDR2-400 SC */ |
{1, 0, 400, 667, 3443, 33443, 3996, 33996}, /* DDR2-667 SC */ |
{1, 0, 400, 800, 3430, 33430, 3946, 33946}, /* DDR2-800 SC */ |
{1, 1, 400, 667, 6509, 36509, 7062, 37062}, /* DDR3-667 SC */ |
{1, 1, 400, 800, 5985, 35985, 6501, 36501}, /* DDR3-800 SC */ |
|
{0, 0, 800, 400, 3438, 33438, 4065, 34065}, /* DDR2-400 SC */ |
{0, 0, 800, 667, 3410, 33410, 3889, 33889}, /* DDR2-667 SC */ |
{0, 0, 800, 800, 3403, 33403, 3845, 33845}, /* DDR2-800 SC */ |
{0, 1, 800, 667, 6476, 36476, 6955, 36955}, /* DDR3-667 SC */ |
{0, 1, 800, 800, 5958, 35958, 6400, 36400}, /* DDR3-800 SC */ |
|
{0, 0, 667, 400, 3456, 33456, 4103, 34106}, /* DDR2-400 SC */ |
{0, 0, 667, 667, 3428, 33428, 3927, 33927}, /* DDR2-667 SC */ |
{0, 0, 667, 800, 3443, 33443, 3905, 33905}, /* DDR2-800 SC */ |
{0, 1, 667, 667, 6494, 36494, 6993, 36993}, /* DDR3-667 SC */ |
{0, 1, 667, 800, 5998, 35998, 6460, 36460}, /* DDR3-800 SC */ |
|
{0, 0, 400, 400, 3528, 33528, 4255, 34255}, /* DDR2-400 SC */ |
{0, 0, 400, 667, 3500, 33500, 4079, 34079}, /* DDR2-667 SC */ |
{0, 0, 400, 800, 3487, 33487, 4029, 34029}, /* DDR2-800 SC */ |
{0, 1, 400, 667, 6566, 36566, 7145, 37145}, /* DDR3-667 SC */ |
{0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */ |
}; |
|
static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, |
int is_ddr3, |
int fsb, |
int mem) |
{ |
const struct cxsr_latency *latency; |
int i; |
|
if (fsb == 0 || mem == 0) |
return NULL; |
|
for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) { |
latency = &cxsr_latency_table[i]; |
if (is_desktop == latency->is_desktop && |
is_ddr3 == latency->is_ddr3 && |
fsb == latency->fsb_freq && mem == latency->mem_freq) |
return latency; |
} |
|
DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n"); |
|
return NULL; |
} |
|
static void pineview_disable_cxsr(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* deactivate cxsr */ |
I915_WRITE(DSPFW3, I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN); |
} |
|
/* |
* Latency for FIFO fetches is dependent on several factors: |
* - memory configuration (speed, channels) |
* - chipset |
* - current MCH state |
* It can be fairly high in some situations, so here we assume a fairly |
* pessimal value. It's a tradeoff between extra memory fetches (if we |
* set this value too high, the FIFO will fetch frequently to stay full) |
* and power consumption (set it too low to save power and we might see |
* FIFO underruns and display "flicker"). |
* |
* A value of 5us seems to be a good balance; safe for very low end |
* platforms but not overly aggressive on lower latency configs. |
*/ |
static const int latency_ns = 5000; |
|
static int i9xx_get_fifo_size(struct drm_device *dev, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dsparb = I915_READ(DSPARB); |
int size; |
|
size = dsparb & 0x7f; |
if (plane) |
size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size; |
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, |
plane ? "B" : "A", size); |
|
return size; |
} |
|
static int i85x_get_fifo_size(struct drm_device *dev, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dsparb = I915_READ(DSPARB); |
int size; |
|
size = dsparb & 0x1ff; |
if (plane) |
size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - size; |
size >>= 1; /* Convert to cachelines */ |
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, |
plane ? "B" : "A", size); |
|
return size; |
} |
|
static int i845_get_fifo_size(struct drm_device *dev, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dsparb = I915_READ(DSPARB); |
int size; |
|
size = dsparb & 0x7f; |
size >>= 2; /* Convert to cachelines */ |
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, |
plane ? "B" : "A", |
size); |
|
return size; |
} |
|
static int i830_get_fifo_size(struct drm_device *dev, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dsparb = I915_READ(DSPARB); |
int size; |
|
size = dsparb & 0x7f; |
size >>= 1; /* Convert to cachelines */ |
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, |
plane ? "B" : "A", size); |
|
return size; |
} |
|
static struct drm_crtc *single_enabled_crtc(struct drm_device *dev) |
{ |
struct drm_crtc *crtc, *enabled = NULL; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->enabled && crtc->fb) { |
if (enabled) |
return NULL; |
enabled = crtc; |
} |
} |
|
return enabled; |
} |
|
static void pineview_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc; |
const struct cxsr_latency *latency; |
u32 reg; |
unsigned long wm; |
|
latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->is_ddr3, |
dev_priv->fsb_freq, dev_priv->mem_freq); |
if (!latency) { |
DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n"); |
pineview_disable_cxsr(dev); |
return; |
} |
|
crtc = single_enabled_crtc(dev); |
if (crtc) { |
int clock = crtc->mode.clock; |
int pixel_size = crtc->fb->bits_per_pixel / 8; |
|
/* Display SR */ |
wm = intel_calculate_wm(clock, &pineview_display_wm, |
pineview_display_wm.fifo_size, |
pixel_size, latency->display_sr); |
reg = I915_READ(DSPFW1); |
reg &= ~DSPFW_SR_MASK; |
reg |= wm << DSPFW_SR_SHIFT; |
I915_WRITE(DSPFW1, reg); |
DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg); |
|
/* cursor SR */ |
wm = intel_calculate_wm(clock, &pineview_cursor_wm, |
pineview_display_wm.fifo_size, |
pixel_size, latency->cursor_sr); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_CURSOR_SR_MASK; |
reg |= (wm & 0x3f) << DSPFW_CURSOR_SR_SHIFT; |
I915_WRITE(DSPFW3, reg); |
|
/* Display HPLL off SR */ |
wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm, |
pineview_display_hplloff_wm.fifo_size, |
pixel_size, latency->display_hpll_disable); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_HPLL_SR_MASK; |
reg |= wm & DSPFW_HPLL_SR_MASK; |
I915_WRITE(DSPFW3, reg); |
|
/* cursor HPLL off SR */ |
wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, |
pineview_display_hplloff_wm.fifo_size, |
pixel_size, latency->cursor_hpll_disable); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_HPLL_CURSOR_MASK; |
reg |= (wm & 0x3f) << DSPFW_HPLL_CURSOR_SHIFT; |
I915_WRITE(DSPFW3, reg); |
DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg); |
|
/* activate cxsr */ |
I915_WRITE(DSPFW3, |
I915_READ(DSPFW3) | PINEVIEW_SELF_REFRESH_EN); |
DRM_DEBUG_KMS("Self-refresh is enabled\n"); |
} else { |
pineview_disable_cxsr(dev); |
DRM_DEBUG_KMS("Self-refresh is disabled\n"); |
} |
} |
|
static bool g4x_compute_wm0(struct drm_device *dev, |
int plane, |
const struct intel_watermark_params *display, |
int display_latency_ns, |
const struct intel_watermark_params *cursor, |
int cursor_latency_ns, |
int *plane_wm, |
int *cursor_wm) |
{ |
struct drm_crtc *crtc; |
int htotal, hdisplay, clock, pixel_size; |
int line_time_us, line_count; |
int entries, tlb_miss; |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
if (crtc->fb == NULL || !crtc->enabled) { |
*cursor_wm = cursor->guard_size; |
*plane_wm = display->guard_size; |
return false; |
} |
|
htotal = crtc->mode.htotal; |
hdisplay = crtc->mode.hdisplay; |
clock = crtc->mode.clock; |
pixel_size = crtc->fb->bits_per_pixel / 8; |
|
/* Use the small buffer method to calculate plane watermark */ |
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; |
tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8; |
if (tlb_miss > 0) |
entries += tlb_miss; |
entries = DIV_ROUND_UP(entries, display->cacheline_size); |
*plane_wm = entries + display->guard_size; |
if (*plane_wm > (int)display->max_wm) |
*plane_wm = display->max_wm; |
|
/* Use the large buffer method to calculate cursor watermark */ |
line_time_us = ((htotal * 1000) / clock); |
line_count = (cursor_latency_ns / line_time_us + 1000) / 1000; |
entries = line_count * 64 * pixel_size; |
tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8; |
if (tlb_miss > 0) |
entries += tlb_miss; |
entries = DIV_ROUND_UP(entries, cursor->cacheline_size); |
*cursor_wm = entries + cursor->guard_size; |
if (*cursor_wm > (int)cursor->max_wm) |
*cursor_wm = (int)cursor->max_wm; |
|
return true; |
} |
|
/* |
* Check the wm result. |
* |
* If any calculated watermark values is larger than the maximum value that |
* can be programmed into the associated watermark register, that watermark |
* must be disabled. |
*/ |
static bool g4x_check_srwm(struct drm_device *dev, |
int display_wm, int cursor_wm, |
const struct intel_watermark_params *display, |
const struct intel_watermark_params *cursor) |
{ |
DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n", |
display_wm, cursor_wm); |
|
if (display_wm > display->max_wm) { |
DRM_DEBUG_KMS("display watermark is too large(%d/%ld), disabling\n", |
display_wm, display->max_wm); |
return false; |
} |
|
if (cursor_wm > cursor->max_wm) { |
DRM_DEBUG_KMS("cursor watermark is too large(%d/%ld), disabling\n", |
cursor_wm, cursor->max_wm); |
return false; |
} |
|
if (!(display_wm || cursor_wm)) { |
DRM_DEBUG_KMS("SR latency is 0, disabling\n"); |
return false; |
} |
|
return true; |
} |
|
static bool g4x_compute_srwm(struct drm_device *dev, |
int plane, |
int latency_ns, |
const struct intel_watermark_params *display, |
const struct intel_watermark_params *cursor, |
int *display_wm, int *cursor_wm) |
{ |
struct drm_crtc *crtc; |
int hdisplay, htotal, pixel_size, clock; |
unsigned long line_time_us; |
int line_count, line_size; |
int small, large; |
int entries; |
|
if (!latency_ns) { |
*display_wm = *cursor_wm = 0; |
return false; |
} |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
hdisplay = crtc->mode.hdisplay; |
htotal = crtc->mode.htotal; |
clock = crtc->mode.clock; |
pixel_size = crtc->fb->bits_per_pixel / 8; |
|
line_time_us = (htotal * 1000) / clock; |
line_count = (latency_ns / line_time_us + 1000) / 1000; |
line_size = hdisplay * pixel_size; |
|
/* Use the minimum of the small and large buffer method for primary */ |
small = ((clock * pixel_size / 1000) * latency_ns) / 1000; |
large = line_count * line_size; |
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size); |
*display_wm = entries + display->guard_size; |
|
/* calculate the self-refresh watermark for display cursor */ |
entries = line_count * pixel_size * 64; |
entries = DIV_ROUND_UP(entries, cursor->cacheline_size); |
*cursor_wm = entries + cursor->guard_size; |
|
return g4x_check_srwm(dev, |
*display_wm, *cursor_wm, |
display, cursor); |
} |
|
#define single_plane_enabled(mask) is_power_of_2(mask) |
|
static void g4x_update_wm(struct drm_device *dev) |
{ |
static const int sr_latency_ns = 12000; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int planea_wm, planeb_wm, cursora_wm, cursorb_wm; |
int plane_sr, cursor_sr; |
unsigned int enabled = 0; |
|
if (g4x_compute_wm0(dev, 0, |
&g4x_wm_info, latency_ns, |
&g4x_cursor_wm_info, latency_ns, |
&planea_wm, &cursora_wm)) |
enabled |= 1; |
|
if (g4x_compute_wm0(dev, 1, |
&g4x_wm_info, latency_ns, |
&g4x_cursor_wm_info, latency_ns, |
&planeb_wm, &cursorb_wm)) |
enabled |= 2; |
|
plane_sr = cursor_sr = 0; |
if (single_plane_enabled(enabled) && |
g4x_compute_srwm(dev, ffs(enabled) - 1, |
sr_latency_ns, |
&g4x_wm_info, |
&g4x_cursor_wm_info, |
&plane_sr, &cursor_sr)) |
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); |
else |
I915_WRITE(FW_BLC_SELF, |
I915_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN); |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n", |
planea_wm, cursora_wm, |
planeb_wm, cursorb_wm, |
plane_sr, cursor_sr); |
|
I915_WRITE(DSPFW1, |
(plane_sr << DSPFW_SR_SHIFT) | |
(cursorb_wm << DSPFW_CURSORB_SHIFT) | |
(planeb_wm << DSPFW_PLANEB_SHIFT) | |
planea_wm); |
I915_WRITE(DSPFW2, |
(I915_READ(DSPFW2) & DSPFW_CURSORA_MASK) | |
(cursora_wm << DSPFW_CURSORA_SHIFT)); |
/* HPLL off in SR has some issues on G4x... disable it */ |
I915_WRITE(DSPFW3, |
(I915_READ(DSPFW3) & ~DSPFW_HPLL_SR_EN) | |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
} |
|
static void i965_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc; |
int srwm = 1; |
int cursor_sr = 16; |
|
/* Calc sr entries for one plane configs */ |
crtc = single_enabled_crtc(dev); |
if (crtc) { |
/* self-refresh has much higher latency */ |
static const int sr_latency_ns = 12000; |
int clock = crtc->mode.clock; |
int htotal = crtc->mode.htotal; |
int hdisplay = crtc->mode.hdisplay; |
int pixel_size = crtc->fb->bits_per_pixel / 8; |
unsigned long line_time_us; |
int entries; |
|
line_time_us = ((htotal * 1000) / clock); |
|
/* Use ns/us then divide to preserve precision */ |
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * |
pixel_size * hdisplay; |
entries = DIV_ROUND_UP(entries, I915_FIFO_LINE_SIZE); |
srwm = I965_FIFO_SIZE - entries; |
if (srwm < 0) |
srwm = 1; |
srwm &= 0x1ff; |
DRM_DEBUG_KMS("self-refresh entries: %d, wm: %d\n", |
entries, srwm); |
|
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * |
pixel_size * 64; |
entries = DIV_ROUND_UP(entries, |
i965_cursor_wm_info.cacheline_size); |
cursor_sr = i965_cursor_wm_info.fifo_size - |
(entries + i965_cursor_wm_info.guard_size); |
|
if (cursor_sr > i965_cursor_wm_info.max_wm) |
cursor_sr = i965_cursor_wm_info.max_wm; |
|
DRM_DEBUG_KMS("self-refresh watermark: display plane %d " |
"cursor %d\n", srwm, cursor_sr); |
|
if (IS_CRESTLINE(dev)) |
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); |
} else { |
/* Turn off self refresh if both pipes are enabled */ |
if (IS_CRESTLINE(dev)) |
I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) |
& ~FW_BLC_SELF_EN); |
} |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n", |
srwm); |
|
/* 965 has limitations... */ |
I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | |
(8 << 16) | (8 << 8) | (8 << 0)); |
I915_WRITE(DSPFW2, (8 << 8) | (8 << 0)); |
/* update cursor SR watermark */ |
I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
} |
|
static void i9xx_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
const struct intel_watermark_params *wm_info; |
uint32_t fwater_lo; |
uint32_t fwater_hi; |
int cwm, srwm = 1; |
int fifo_size; |
int planea_wm, planeb_wm; |
struct drm_crtc *crtc, *enabled = NULL; |
|
if (IS_I945GM(dev)) |
wm_info = &i945_wm_info; |
else if (!IS_GEN2(dev)) |
wm_info = &i915_wm_info; |
else |
wm_info = &i855_wm_info; |
|
fifo_size = dev_priv->display.get_fifo_size(dev, 0); |
crtc = intel_get_crtc_for_plane(dev, 0); |
if (crtc->enabled && crtc->fb) { |
planea_wm = intel_calculate_wm(crtc->mode.clock, |
wm_info, fifo_size, |
crtc->fb->bits_per_pixel / 8, |
latency_ns); |
enabled = crtc; |
} else |
planea_wm = fifo_size - wm_info->guard_size; |
|
fifo_size = dev_priv->display.get_fifo_size(dev, 1); |
crtc = intel_get_crtc_for_plane(dev, 1); |
if (crtc->enabled && crtc->fb) { |
planeb_wm = intel_calculate_wm(crtc->mode.clock, |
wm_info, fifo_size, |
crtc->fb->bits_per_pixel / 8, |
latency_ns); |
if (enabled == NULL) |
enabled = crtc; |
else |
enabled = NULL; |
} else |
planeb_wm = fifo_size - wm_info->guard_size; |
|
DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); |
|
/* |
* Overlay gets an aggressive default since video jitter is bad. |
*/ |
cwm = 2; |
|
/* Play safe and disable self-refresh before adjusting watermarks. */ |
if (IS_I945G(dev) || IS_I945GM(dev)) |
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | 0); |
else if (IS_I915GM(dev)) |
I915_WRITE(INSTPM, I915_READ(INSTPM) & ~INSTPM_SELF_EN); |
|
/* Calc sr entries for one plane configs */ |
if (HAS_FW_BLC(dev) && enabled) { |
/* self-refresh has much higher latency */ |
static const int sr_latency_ns = 6000; |
int clock = enabled->mode.clock; |
int htotal = enabled->mode.htotal; |
int hdisplay = enabled->mode.hdisplay; |
int pixel_size = enabled->fb->bits_per_pixel / 8; |
unsigned long line_time_us; |
int entries; |
|
line_time_us = (htotal * 1000) / clock; |
|
/* Use ns/us then divide to preserve precision */ |
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * |
pixel_size * hdisplay; |
entries = DIV_ROUND_UP(entries, wm_info->cacheline_size); |
DRM_DEBUG_KMS("self-refresh entries: %d\n", entries); |
srwm = wm_info->fifo_size - entries; |
if (srwm < 0) |
srwm = 1; |
|
if (IS_I945G(dev) || IS_I945GM(dev)) |
I915_WRITE(FW_BLC_SELF, |
FW_BLC_SELF_FIFO_MASK | (srwm & 0xff)); |
else if (IS_I915GM(dev)) |
I915_WRITE(FW_BLC_SELF, srwm & 0x3f); |
} |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", |
planea_wm, planeb_wm, cwm, srwm); |
|
fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f); |
fwater_hi = (cwm & 0x1f); |
|
/* Set request length to 8 cachelines per fetch */ |
fwater_lo = fwater_lo | (1 << 24) | (1 << 8); |
fwater_hi = fwater_hi | (1 << 8); |
|
I915_WRITE(FW_BLC, fwater_lo); |
I915_WRITE(FW_BLC2, fwater_hi); |
|
if (HAS_FW_BLC(dev)) { |
if (enabled) { |
if (IS_I945G(dev) || IS_I945GM(dev)) |
I915_WRITE(FW_BLC_SELF, |
FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN); |
else if (IS_I915GM(dev)) |
I915_WRITE(INSTPM, I915_READ(INSTPM) | INSTPM_SELF_EN); |
DRM_DEBUG_KMS("memory self refresh enabled\n"); |
} else |
DRM_DEBUG_KMS("memory self refresh disabled\n"); |
} |
} |
|
static void i830_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc; |
uint32_t fwater_lo; |
int planea_wm; |
|
crtc = single_enabled_crtc(dev); |
if (crtc == NULL) |
return; |
|
planea_wm = intel_calculate_wm(crtc->mode.clock, &i830_wm_info, |
dev_priv->display.get_fifo_size(dev, 0), |
crtc->fb->bits_per_pixel / 8, |
latency_ns); |
fwater_lo = I915_READ(FW_BLC) & ~0xfff; |
fwater_lo |= (3<<8) | planea_wm; |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d\n", planea_wm); |
|
I915_WRITE(FW_BLC, fwater_lo); |
} |
|
#define ILK_LP0_PLANE_LATENCY 700 |
#define ILK_LP0_CURSOR_LATENCY 1300 |
|
/* |
* Check the wm result. |
* |
* If any calculated watermark values is larger than the maximum value that |
* can be programmed into the associated watermark register, that watermark |
* must be disabled. |
*/ |
static bool ironlake_check_srwm(struct drm_device *dev, int level, |
int fbc_wm, int display_wm, int cursor_wm, |
const struct intel_watermark_params *display, |
const struct intel_watermark_params *cursor) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d," |
" cursor %d\n", level, display_wm, fbc_wm, cursor_wm); |
|
if (fbc_wm > SNB_FBC_MAX_SRWM) { |
DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n", |
fbc_wm, SNB_FBC_MAX_SRWM, level); |
|
/* fbc has it's own way to disable FBC WM */ |
I915_WRITE(DISP_ARB_CTL, |
I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS); |
return false; |
} |
|
if (display_wm > display->max_wm) { |
DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n", |
display_wm, SNB_DISPLAY_MAX_SRWM, level); |
return false; |
} |
|
if (cursor_wm > cursor->max_wm) { |
DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n", |
cursor_wm, SNB_CURSOR_MAX_SRWM, level); |
return false; |
} |
|
if (!(fbc_wm || display_wm || cursor_wm)) { |
DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level); |
return false; |
} |
|
return true; |
} |
|
/* |
* Compute watermark values of WM[1-3], |
*/ |
static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane, |
int latency_ns, |
const struct intel_watermark_params *display, |
const struct intel_watermark_params *cursor, |
int *fbc_wm, int *display_wm, int *cursor_wm) |
{ |
struct drm_crtc *crtc; |
unsigned long line_time_us; |
int hdisplay, htotal, pixel_size, clock; |
int line_count, line_size; |
int small, large; |
int entries; |
|
if (!latency_ns) { |
*fbc_wm = *display_wm = *cursor_wm = 0; |
return false; |
} |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
hdisplay = crtc->mode.hdisplay; |
htotal = crtc->mode.htotal; |
clock = crtc->mode.clock; |
pixel_size = crtc->fb->bits_per_pixel / 8; |
|
line_time_us = (htotal * 1000) / clock; |
line_count = (latency_ns / line_time_us + 1000) / 1000; |
line_size = hdisplay * pixel_size; |
|
/* Use the minimum of the small and large buffer method for primary */ |
small = ((clock * pixel_size / 1000) * latency_ns) / 1000; |
large = line_count * line_size; |
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size); |
*display_wm = entries + display->guard_size; |
|
/* |
* Spec says: |
* FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2 |
*/ |
*fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2; |
|
/* calculate the self-refresh watermark for display cursor */ |
entries = line_count * pixel_size * 64; |
entries = DIV_ROUND_UP(entries, cursor->cacheline_size); |
*cursor_wm = entries + cursor->guard_size; |
|
return ironlake_check_srwm(dev, level, |
*fbc_wm, *display_wm, *cursor_wm, |
display, cursor); |
} |
|
static void ironlake_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int fbc_wm, plane_wm, cursor_wm; |
unsigned int enabled; |
|
enabled = 0; |
if (g4x_compute_wm0(dev, 0, |
&ironlake_display_wm_info, |
ILK_LP0_PLANE_LATENCY, |
&ironlake_cursor_wm_info, |
ILK_LP0_CURSOR_LATENCY, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEA_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
DRM_DEBUG_KMS("FIFO watermarks For pipe A -" |
" plane %d, " "cursor: %d\n", |
plane_wm, cursor_wm); |
enabled |= 1; |
} |
|
if (g4x_compute_wm0(dev, 1, |
&ironlake_display_wm_info, |
ILK_LP0_PLANE_LATENCY, |
&ironlake_cursor_wm_info, |
ILK_LP0_CURSOR_LATENCY, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEB_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
DRM_DEBUG_KMS("FIFO watermarks For pipe B -" |
" plane %d, cursor: %d\n", |
plane_wm, cursor_wm); |
enabled |= 2; |
} |
|
/* |
* Calculate and update the self-refresh watermark only when one |
* display plane is used. |
*/ |
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
|
if (!single_plane_enabled(enabled)) |
return; |
enabled = ffs(enabled) - 1; |
|
/* WM1 */ |
if (!ironlake_compute_srwm(dev, 1, enabled, |
ILK_READ_WM1_LATENCY() * 500, |
&ironlake_display_srwm_info, |
&ironlake_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
return; |
|
I915_WRITE(WM1_LP_ILK, |
WM1_LP_SR_EN | |
(ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
|
/* WM2 */ |
if (!ironlake_compute_srwm(dev, 2, enabled, |
ILK_READ_WM2_LATENCY() * 500, |
&ironlake_display_srwm_info, |
&ironlake_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
return; |
|
I915_WRITE(WM2_LP_ILK, |
WM2_LP_EN | |
(ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
|
/* |
* WM3 is unsupported on ILK, probably because we don't have latency |
* data for that power state |
*/ |
} |
|
void sandybridge_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ |
int fbc_wm, plane_wm, cursor_wm; |
unsigned int enabled; |
|
enabled = 0; |
if (g4x_compute_wm0(dev, 0, |
&sandybridge_display_wm_info, latency, |
&sandybridge_cursor_wm_info, latency, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEA_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
DRM_DEBUG_KMS("FIFO watermarks For pipe A -" |
" plane %d, " "cursor: %d\n", |
plane_wm, cursor_wm); |
enabled |= 1; |
} |
|
if (g4x_compute_wm0(dev, 1, |
&sandybridge_display_wm_info, latency, |
&sandybridge_cursor_wm_info, latency, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEB_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
DRM_DEBUG_KMS("FIFO watermarks For pipe B -" |
" plane %d, cursor: %d\n", |
plane_wm, cursor_wm); |
enabled |= 2; |
} |
|
/* IVB has 3 pipes */ |
if (IS_IVYBRIDGE(dev) && |
g4x_compute_wm0(dev, 2, |
&sandybridge_display_wm_info, latency, |
&sandybridge_cursor_wm_info, latency, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEC_IVB, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
DRM_DEBUG_KMS("FIFO watermarks For pipe C -" |
" plane %d, cursor: %d\n", |
plane_wm, cursor_wm); |
enabled |= 3; |
} |
|
/* |
* Calculate and update the self-refresh watermark only when one |
* display plane is used. |
* |
* SNB support 3 levels of watermark. |
* |
* WM1/WM2/WM2 watermarks have to be enabled in the ascending order, |
* and disabled in the descending order |
* |
*/ |
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
|
if (!single_plane_enabled(enabled) || |
dev_priv->sprite_scaling_enabled) |
return; |
enabled = ffs(enabled) - 1; |
|
/* WM1 */ |
if (!ironlake_compute_srwm(dev, 1, enabled, |
SNB_READ_WM1_LATENCY() * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
return; |
|
I915_WRITE(WM1_LP_ILK, |
WM1_LP_SR_EN | |
(SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
|
/* WM2 */ |
if (!ironlake_compute_srwm(dev, 2, enabled, |
SNB_READ_WM2_LATENCY() * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
return; |
|
I915_WRITE(WM2_LP_ILK, |
WM2_LP_EN | |
(SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
|
/* WM3 */ |
if (!ironlake_compute_srwm(dev, 3, enabled, |
SNB_READ_WM3_LATENCY() * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
return; |
|
I915_WRITE(WM3_LP_ILK, |
WM3_LP_EN | |
(SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
} |
|
static bool |
sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, |
uint32_t sprite_width, int pixel_size, |
const struct intel_watermark_params *display, |
int display_latency_ns, int *sprite_wm) |
{ |
struct drm_crtc *crtc; |
int clock; |
int entries, tlb_miss; |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
if (crtc->fb == NULL || !crtc->enabled) { |
*sprite_wm = display->guard_size; |
return false; |
} |
|
clock = crtc->mode.clock; |
|
/* Use the small buffer method to calculate the sprite watermark */ |
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; |
tlb_miss = display->fifo_size*display->cacheline_size - |
sprite_width * 8; |
if (tlb_miss > 0) |
entries += tlb_miss; |
entries = DIV_ROUND_UP(entries, display->cacheline_size); |
*sprite_wm = entries + display->guard_size; |
if (*sprite_wm > (int)display->max_wm) |
*sprite_wm = display->max_wm; |
|
return true; |
} |
|
static bool |
sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane, |
uint32_t sprite_width, int pixel_size, |
const struct intel_watermark_params *display, |
int latency_ns, int *sprite_wm) |
{ |
struct drm_crtc *crtc; |
unsigned long line_time_us; |
int clock; |
int line_count, line_size; |
int small, large; |
int entries; |
|
if (!latency_ns) { |
*sprite_wm = 0; |
return false; |
} |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
clock = crtc->mode.clock; |
|
line_time_us = (sprite_width * 1000) / clock; |
line_count = (latency_ns / line_time_us + 1000) / 1000; |
line_size = sprite_width * pixel_size; |
|
/* Use the minimum of the small and large buffer method for primary */ |
small = ((clock * pixel_size / 1000) * latency_ns) / 1000; |
large = line_count * line_size; |
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size); |
*sprite_wm = entries + display->guard_size; |
|
return *sprite_wm > 0x3ff ? false : true; |
} |
|
static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, |
uint32_t sprite_width, int pixel_size) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ |
int sprite_wm, reg; |
int ret; |
|
switch (pipe) { |
case 0: |
reg = WM0_PIPEA_ILK; |
break; |
case 1: |
reg = WM0_PIPEB_ILK; |
break; |
case 2: |
reg = WM0_PIPEC_IVB; |
break; |
default: |
return; /* bad pipe */ |
} |
|
ret = sandybridge_compute_sprite_wm(dev, pipe, sprite_width, pixel_size, |
&sandybridge_display_wm_info, |
latency, &sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n", |
pipe); |
return; |
} |
|
I915_WRITE(reg, I915_READ(reg) | (sprite_wm << WM0_PIPE_SPRITE_SHIFT)); |
DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm); |
|
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM1_LATENCY() * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n", |
pipe); |
return; |
} |
I915_WRITE(WM1S_LP_ILK, sprite_wm); |
|
/* Only IVB has two more LP watermarks for sprite */ |
if (!IS_IVYBRIDGE(dev)) |
return; |
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM2_LATENCY() * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n", |
pipe); |
return; |
} |
I915_WRITE(WM2S_LP_IVB, sprite_wm); |
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM3_LATENCY() * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n", |
pipe); |
return; |
} |
I915_WRITE(WM3S_LP_IVB, sprite_wm); |
} |
|
/** |
* intel_update_watermarks - update FIFO watermark values based on current modes |
* |
* Calculate watermark values for the various WM regs based on current mode |
* and plane configuration. |
* |
* There are several cases to deal with here: |
* - normal (i.e. non-self-refresh) |
* - self-refresh (SR) mode |
* - lines are large relative to FIFO size (buffer can hold up to 2) |
* - lines are small relative to FIFO size (buffer can hold more than 2 |
* lines), so need to account for TLB latency |
* |
* The normal calculation is: |
* watermark = dotclock * bytes per pixel * latency |
* where latency is platform & configuration dependent (we assume pessimal |
* values here). |
* |
* The SR calculation is: |
* watermark = (trunc(latency/line time)+1) * surface width * |
* bytes per pixel |
* where |
* line time = htotal / dotclock |
* surface width = hdisplay for normal plane and 64 for cursor |
* and latency is assumed to be high, as above. |
* |
* The final value programmed to the register should always be rounded up, |
* and include an extra 2 entries to account for clock crossings. |
* |
* We don't use the sprite, so we can ignore that. And on Crestline we have |
* to set the non-SR watermarks to 8. |
*/ |
static void intel_update_watermarks(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->display.update_wm) |
dev_priv->display.update_wm(dev); |
} |
|
void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, |
uint32_t sprite_width, int pixel_size) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->display.update_sprite_wm) |
dev_priv->display.update_sprite_wm(dev, pipe, sprite_width, |
pixel_size); |
} |
|
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) |
{ |
if (i915_panel_use_ssc >= 0) |
4805,22 → 3817,19 |
* true if they don't match). |
*/ |
static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
unsigned int *pipe_bpp, |
struct drm_display_mode *mode) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
struct intel_encoder *intel_encoder; |
unsigned int display_bpc = UINT_MAX, bpc; |
|
/* Walk the encoders & connectors on this crtc, get min bpc */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); |
for_each_encoder_on_crtc(dev, crtc, intel_encoder) { |
|
if (encoder->crtc != crtc) |
continue; |
|
if (intel_encoder->type == INTEL_OUTPUT_LVDS) { |
unsigned int lvds_bpc; |
|
4837,21 → 3846,10 |
continue; |
} |
|
if (intel_encoder->type == INTEL_OUTPUT_EDP) { |
/* Use VBT settings if we have an eDP panel */ |
unsigned int edp_bpc = dev_priv->edp.bpp / 3; |
|
if (edp_bpc < display_bpc) { |
DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc); |
display_bpc = edp_bpc; |
} |
continue; |
} |
|
/* Not one of the known troublemakers, check the EDID */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
head) { |
if (connector->encoder != encoder) |
if (connector->encoder != &intel_encoder->base) |
continue; |
|
/* Don't use an invalid EDID bpc value */ |
4889,7 → 3887,7 |
* also stays within the max display bpc discovered above. |
*/ |
|
switch (crtc->fb->depth) { |
switch (fb->depth) { |
case 8: |
bpc = 8; /* since we go through a colormap */ |
break; |
4922,61 → 3920,38 |
return display_bpc != bpc; |
} |
|
static int i9xx_crtc_mode_set(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, |
struct drm_framebuffer *old_fb) |
static int vlv_get_refclk(struct drm_crtc *crtc) |
{ |
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->plane; |
int refclk, num_connectors = 0; |
intel_clock_t clock, reduced_clock; |
u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf; |
bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false; |
bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false; |
struct drm_mode_config *mode_config = &dev->mode_config; |
struct intel_encoder *encoder; |
const intel_limit_t *limit; |
int ret; |
u32 temp; |
u32 lvds_sync = 0; |
int refclk = 27000; /* for DP & HDMI */ |
|
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { |
if (encoder->base.crtc != crtc) |
continue; |
return 100000; /* only one validated so far */ |
|
switch (encoder->type) { |
case INTEL_OUTPUT_LVDS: |
is_lvds = true; |
break; |
case INTEL_OUTPUT_SDVO: |
case INTEL_OUTPUT_HDMI: |
is_sdvo = true; |
if (encoder->needs_tv_clock) |
is_tv = true; |
break; |
case INTEL_OUTPUT_DVO: |
is_dvo = true; |
break; |
case INTEL_OUTPUT_TVOUT: |
is_tv = true; |
break; |
case INTEL_OUTPUT_ANALOG: |
is_crt = true; |
break; |
case INTEL_OUTPUT_DISPLAYPORT: |
is_dp = true; |
break; |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { |
refclk = 96000; |
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { |
if (intel_panel_use_ssc(dev_priv)) |
refclk = 100000; |
else |
refclk = 96000; |
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { |
refclk = 100000; |
} |
|
num_connectors++; |
return refclk; |
} |
|
if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { |
static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int refclk; |
|
if (IS_VALLEYVIEW(dev)) { |
refclk = vlv_get_refclk(crtc); |
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && |
intel_panel_use_ssc(dev_priv) && num_connectors < 2) { |
refclk = dev_priv->lvds_ssc_freq * 1000; |
DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", |
refclk / 1000); |
4986,74 → 3961,195 |
refclk = 48000; |
} |
|
/* |
* Returns a set of divisors for the desired target clock with the given |
* refclk, or FALSE. The returned values represent the clock equation: |
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. |
*/ |
limit = intel_limit(crtc, refclk); |
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); |
if (!ok) { |
DRM_ERROR("Couldn't find PLL settings for mode!\n"); |
return -EINVAL; |
return refclk; |
} |
|
/* Ensure that the cursor is valid for the new mode before changing... */ |
// intel_crtc_update_cursor(crtc, true); |
|
if (is_lvds && dev_priv->lvds_downclock_avail) { |
has_reduced_clock = limit->find_pll(limit, crtc, |
dev_priv->lvds_downclock, |
refclk, |
&reduced_clock); |
if (has_reduced_clock && (clock.p != reduced_clock.p)) { |
/* |
* If the different P is found, it means that we can't |
* switch the display clock by using the FP0/FP1. |
* In such case we will disable the LVDS downclock |
* feature. |
*/ |
DRM_DEBUG_KMS("Different P is found for " |
"LVDS clock/downclock\n"); |
has_reduced_clock = 0; |
} |
} |
static void i9xx_adjust_sdvo_tv_clock(struct drm_display_mode *adjusted_mode, |
intel_clock_t *clock) |
{ |
/* SDVO TV has fixed PLL values depend on its clock range, |
this mirrors vbios setting. */ |
if (is_sdvo && is_tv) { |
if (adjusted_mode->clock >= 100000 |
&& adjusted_mode->clock < 140500) { |
clock.p1 = 2; |
clock.p2 = 10; |
clock.n = 3; |
clock.m1 = 16; |
clock.m2 = 8; |
clock->p1 = 2; |
clock->p2 = 10; |
clock->n = 3; |
clock->m1 = 16; |
clock->m2 = 8; |
} else if (adjusted_mode->clock >= 140500 |
&& adjusted_mode->clock <= 200000) { |
clock.p1 = 1; |
clock.p2 = 10; |
clock.n = 6; |
clock.m1 = 12; |
clock.m2 = 8; |
clock->p1 = 1; |
clock->p2 = 10; |
clock->n = 6; |
clock->m1 = 12; |
clock->m2 = 8; |
} |
} |
|
static void i9xx_update_pll_dividers(struct drm_crtc *crtc, |
intel_clock_t *clock, |
intel_clock_t *reduced_clock) |
{ |
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; |
u32 fp, fp2 = 0; |
|
if (IS_PINEVIEW(dev)) { |
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2; |
if (has_reduced_clock) |
fp2 = (1 << reduced_clock.n) << 16 | |
reduced_clock.m1 << 8 | reduced_clock.m2; |
fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2; |
if (reduced_clock) |
fp2 = (1 << reduced_clock->n) << 16 | |
reduced_clock->m1 << 8 | reduced_clock->m2; |
} else { |
fp = clock.n << 16 | clock.m1 << 8 | clock.m2; |
if (has_reduced_clock) |
fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | |
reduced_clock.m2; |
fp = clock->n << 16 | clock->m1 << 8 | clock->m2; |
if (reduced_clock) |
fp2 = reduced_clock->n << 16 | reduced_clock->m1 << 8 | |
reduced_clock->m2; |
} |
|
I915_WRITE(FP0(pipe), fp); |
|
intel_crtc->lowfreq_avail = false; |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && |
reduced_clock && i915_powersave) { |
I915_WRITE(FP1(pipe), fp2); |
intel_crtc->lowfreq_avail = true; |
} else { |
I915_WRITE(FP1(pipe), fp); |
} |
} |
|
static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock, |
struct drm_display_mode *adjusted_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; |
u32 temp; |
|
temp = I915_READ(LVDS); |
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; |
if (pipe == 1) { |
temp |= LVDS_PIPEB_SELECT; |
} else { |
temp &= ~LVDS_PIPEB_SELECT; |
} |
/* set the corresponsding LVDS_BORDER bit */ |
temp |= dev_priv->lvds_border_bits; |
/* Set the B0-B3 data pairs corresponding to whether we're going to |
* set the DPLLs for dual-channel mode or not. |
*/ |
if (clock->p2 == 7) |
temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; |
else |
temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); |
|
/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) |
* appropriately here, but we need to look more thoroughly into how |
* panels behave in the two modes. |
*/ |
/* set the dithering flag on LVDS as needed */ |
if (INTEL_INFO(dev)->gen >= 4) { |
if (dev_priv->lvds_dither) |
temp |= LVDS_ENABLE_DITHER; |
else |
temp &= ~LVDS_ENABLE_DITHER; |
} |
temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); |
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) |
temp |= LVDS_HSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) |
temp |= LVDS_VSYNC_POLARITY; |
I915_WRITE(LVDS, temp); |
} |
|
static void vlv_update_pll(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
intel_clock_t *clock, intel_clock_t *reduced_clock, |
int refclk, int num_connectors) |
{ |
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; |
u32 dpll, mdiv, pdiv; |
u32 bestn, bestm1, bestm2, bestp1, bestp2; |
bool is_hdmi; |
|
is_hdmi = intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); |
|
bestn = clock->n; |
bestm1 = clock->m1; |
bestm2 = clock->m2; |
bestp1 = clock->p1; |
bestp2 = clock->p2; |
|
/* Enable DPIO clock input */ |
dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | |
DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; |
I915_WRITE(DPLL(pipe), dpll); |
POSTING_READ(DPLL(pipe)); |
|
mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); |
mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); |
mdiv |= ((bestn << DPIO_N_SHIFT)); |
mdiv |= (1 << DPIO_POST_DIV_SHIFT); |
mdiv |= (1 << DPIO_K_SHIFT); |
mdiv |= DPIO_ENABLE_CALIBRATION; |
intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); |
|
intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), 0x01000000); |
|
pdiv = DPIO_REFSEL_OVERRIDE | (5 << DPIO_PLL_MODESEL_SHIFT) | |
(3 << DPIO_BIAS_CURRENT_CTL_SHIFT) | (1<<20) | |
(8 << DPIO_DRIVER_CTL_SHIFT) | (5 << DPIO_CLK_BIAS_CTL_SHIFT); |
intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), pdiv); |
|
intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x009f0051); |
|
dpll |= DPLL_VCO_ENABLE; |
I915_WRITE(DPLL(pipe), dpll); |
POSTING_READ(DPLL(pipe)); |
if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) |
DRM_ERROR("DPLL %d failed to lock\n", pipe); |
|
if (is_hdmi) { |
u32 temp = intel_mode_get_pixel_multiplier(adjusted_mode); |
|
if (temp > 1) |
temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; |
else |
temp = 0; |
|
I915_WRITE(DPLL_MD(pipe), temp); |
POSTING_READ(DPLL_MD(pipe)); |
} |
|
intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x641); /* ??? */ |
} |
|
static void i9xx_update_pll(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
intel_clock_t *clock, intel_clock_t *reduced_clock, |
int num_connectors) |
{ |
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; |
u32 dpll; |
bool is_sdvo; |
|
is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || |
intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); |
|
dpll = DPLL_VGA_MODE_DIS; |
|
if (!IS_GEN2(dev)) { |
if (is_lvds) |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
dpll |= DPLLB_MODE_LVDS; |
else |
dpll |= DPLLB_MODE_DAC_SERIAL; |
5065,18 → 4161,18 |
} |
dpll |= DPLL_DVO_HIGH_SPEED; |
} |
if (is_dp) |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) |
dpll |= DPLL_DVO_HIGH_SPEED; |
|
/* compute bitmask from p1 value */ |
if (IS_PINEVIEW(dev)) |
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; |
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; |
else { |
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
if (IS_G4X(dev) && has_reduced_clock) |
dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; |
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
if (IS_G4X(dev) && reduced_clock) |
dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; |
} |
switch (clock.p2) { |
switch (clock->p2) { |
case 5: |
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; |
break; |
5092,30 → 4188,210 |
} |
if (INTEL_INFO(dev)->gen >= 4) |
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); |
|
if (is_sdvo && intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) |
dpll |= PLL_REF_INPUT_TVCLKINBC; |
else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) |
/* XXX: just matching BIOS for now */ |
/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ |
dpll |= 3; |
else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && |
intel_panel_use_ssc(dev_priv) && num_connectors < 2) |
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; |
else |
dpll |= PLL_REF_INPUT_DREFCLK; |
|
dpll |= DPLL_VCO_ENABLE; |
I915_WRITE(DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); |
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
/* 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. |
*/ |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
intel_update_lvds(crtc, clock, adjusted_mode); |
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) |
intel_dp_set_m_n(crtc, mode, adjusted_mode); |
|
I915_WRITE(DPLL(pipe), dpll); |
|
/* Wait for the clocks to stabilize. */ |
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
if (INTEL_INFO(dev)->gen >= 4) { |
u32 temp = 0; |
if (is_sdvo) { |
temp = intel_mode_get_pixel_multiplier(adjusted_mode); |
if (temp > 1) |
temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; |
else |
temp = 0; |
} |
I915_WRITE(DPLL_MD(pipe), temp); |
} else { |
if (is_lvds) { |
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
/* The pixel multiplier can only be updated once the |
* DPLL is enabled and the clocks are stable. |
* |
* So write it again. |
*/ |
I915_WRITE(DPLL(pipe), dpll); |
} |
} |
|
static void i8xx_update_pll(struct drm_crtc *crtc, |
struct drm_display_mode *adjusted_mode, |
intel_clock_t *clock, |
int num_connectors) |
{ |
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; |
u32 dpll; |
|
dpll = DPLL_VGA_MODE_DIS; |
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { |
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
} else { |
if (clock.p1 == 2) |
if (clock->p1 == 2) |
dpll |= PLL_P1_DIVIDE_BY_TWO; |
else |
dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
if (clock.p2 == 4) |
dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; |
if (clock->p2 == 4) |
dpll |= PLL_P2_DIVIDE_BY_4; |
} |
} |
|
if (is_sdvo && is_tv) |
dpll |= PLL_REF_INPUT_TVCLKINBC; |
else if (is_tv) |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_TVOUT)) |
/* XXX: just matching BIOS for now */ |
/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ |
dpll |= 3; |
else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) |
else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && |
intel_panel_use_ssc(dev_priv) && num_connectors < 2) |
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; |
else |
dpll |= PLL_REF_INPUT_DREFCLK; |
|
dpll |= DPLL_VCO_ENABLE; |
I915_WRITE(DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); |
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
/* 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. |
*/ |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
intel_update_lvds(crtc, clock, adjusted_mode); |
|
I915_WRITE(DPLL(pipe), dpll); |
|
/* Wait for the clocks to stabilize. */ |
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
/* The pixel multiplier can only be updated once the |
* DPLL is enabled and the clocks are stable. |
* |
* So write it again. |
*/ |
I915_WRITE(DPLL(pipe), dpll); |
} |
|
static int i9xx_crtc_mode_set(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, |
struct drm_framebuffer *fb) |
{ |
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->plane; |
int refclk, num_connectors = 0; |
intel_clock_t clock, reduced_clock; |
u32 dspcntr, pipeconf, vsyncshift; |
bool ok, has_reduced_clock = false, is_sdvo = false; |
bool is_lvds = false, is_tv = false, is_dp = false; |
struct intel_encoder *encoder; |
const intel_limit_t *limit; |
int ret; |
|
for_each_encoder_on_crtc(dev, crtc, encoder) { |
switch (encoder->type) { |
case INTEL_OUTPUT_LVDS: |
is_lvds = true; |
break; |
case INTEL_OUTPUT_SDVO: |
case INTEL_OUTPUT_HDMI: |
is_sdvo = true; |
if (encoder->needs_tv_clock) |
is_tv = true; |
break; |
case INTEL_OUTPUT_TVOUT: |
is_tv = true; |
break; |
case INTEL_OUTPUT_DISPLAYPORT: |
is_dp = true; |
break; |
} |
|
num_connectors++; |
} |
|
refclk = i9xx_get_refclk(crtc, num_connectors); |
|
/* |
* Returns a set of divisors for the desired target clock with the given |
* refclk, or FALSE. The returned values represent the clock equation: |
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. |
*/ |
limit = intel_limit(crtc, refclk); |
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, |
&clock); |
if (!ok) { |
DRM_ERROR("Couldn't find PLL settings for mode!\n"); |
return -EINVAL; |
} |
|
/* Ensure that the cursor is valid for the new mode before changing... */ |
// intel_crtc_update_cursor(crtc, true); |
|
if (is_lvds && dev_priv->lvds_downclock_avail) { |
/* |
* Ensure we match the reduced clock's P to the target clock. |
* If the clocks don't match, we can't switch the display clock |
* by using the FP0/FP1. In such case we will disable the LVDS |
* downclock feature. |
*/ |
has_reduced_clock = limit->find_pll(limit, crtc, |
dev_priv->lvds_downclock, |
refclk, |
&clock, |
&reduced_clock); |
} |
|
if (is_sdvo && is_tv) |
i9xx_adjust_sdvo_tv_clock(adjusted_mode, &clock); |
|
i9xx_update_pll_dividers(crtc, &clock, has_reduced_clock ? |
&reduced_clock : NULL); |
|
if (IS_GEN2(dev)) |
i8xx_update_pll(crtc, adjusted_mode, &clock, num_connectors); |
else if (IS_VALLEYVIEW(dev)) |
vlv_update_pll(crtc, mode,adjusted_mode, &clock, NULL, |
refclk, num_connectors); |
else |
i9xx_update_pll(crtc, mode, adjusted_mode, &clock, |
has_reduced_clock ? &reduced_clock : NULL, |
num_connectors); |
|
/* setup pipeconf */ |
pipeconf = I915_READ(PIPECONF(pipe)); |
|
5122,8 → 4398,6 |
/* Set up the display plane register */ |
dspcntr = DISPPLANE_GAMMA_ENABLE; |
|
/* Ironlake's plane is forced to pipe, bit 24 is to |
enable color space conversion */ |
if (pipe == 0) |
dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; |
else |
5146,7 → 4420,7 |
/* default to 8bpc */ |
pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN); |
if (is_dp) { |
if (mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { |
if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { |
pipeconf |= PIPECONF_BPP_6 | |
PIPECONF_DITHER_EN | |
PIPECONF_DITHER_TYPE_SP; |
5153,109 → 4427,14 |
} |
} |
|
dpll |= DPLL_VCO_ENABLE; |
|
DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); |
drm_mode_debug_printmodeline(mode); |
|
I915_WRITE(FP0(pipe), fp); |
I915_WRITE(DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); |
|
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
/* 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. |
*/ |
if (is_lvds) { |
temp = I915_READ(LVDS); |
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; |
if (pipe == 1) { |
temp |= LVDS_PIPEB_SELECT; |
} else { |
temp &= ~LVDS_PIPEB_SELECT; |
} |
/* set the corresponsding LVDS_BORDER bit */ |
temp |= dev_priv->lvds_border_bits; |
/* Set the B0-B3 data pairs corresponding to whether we're going to |
* set the DPLLs for dual-channel mode or not. |
*/ |
if (clock.p2 == 7) |
temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; |
else |
temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); |
|
/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) |
* appropriately here, but we need to look more thoroughly into how |
* panels behave in the two modes. |
*/ |
/* set the dithering flag on LVDS as needed */ |
if (INTEL_INFO(dev)->gen >= 4) { |
if (dev_priv->lvds_dither) |
temp |= LVDS_ENABLE_DITHER; |
else |
temp &= ~LVDS_ENABLE_DITHER; |
} |
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) |
lvds_sync |= LVDS_HSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) |
lvds_sync |= LVDS_VSYNC_POLARITY; |
if ((temp & (LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY)) |
!= lvds_sync) { |
char flags[2] = "-+"; |
DRM_INFO("Changing LVDS panel from " |
"(%chsync, %cvsync) to (%chsync, %cvsync)\n", |
flags[!(temp & LVDS_HSYNC_POLARITY)], |
flags[!(temp & LVDS_VSYNC_POLARITY)], |
flags[!(lvds_sync & LVDS_HSYNC_POLARITY)], |
flags[!(lvds_sync & LVDS_VSYNC_POLARITY)]); |
temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); |
temp |= lvds_sync; |
} |
I915_WRITE(LVDS, temp); |
} |
|
if (is_dp) { |
intel_dp_set_m_n(crtc, mode, adjusted_mode); |
} |
|
I915_WRITE(DPLL(pipe), dpll); |
|
/* Wait for the clocks to stabilize. */ |
POSTING_READ(DPLL(pipe)); |
udelay(150); |
|
if (INTEL_INFO(dev)->gen >= 4) { |
temp = 0; |
if (is_sdvo) { |
temp = intel_mode_get_pixel_multiplier(adjusted_mode); |
if (temp > 1) |
temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; |
else |
temp = 0; |
} |
I915_WRITE(DPLL_MD(pipe), temp); |
} else { |
/* The pixel multiplier can only be updated once the |
* DPLL is enabled and the clocks are stable. |
* |
* So write it again. |
*/ |
I915_WRITE(DPLL(pipe), dpll); |
} |
|
intel_crtc->lowfreq_avail = false; |
if (is_lvds && has_reduced_clock && i915_powersave) { |
I915_WRITE(FP1(pipe), fp2); |
intel_crtc->lowfreq_avail = true; |
if (HAS_PIPE_CXSR(dev)) { |
if (intel_crtc->lowfreq_avail) { |
DRM_DEBUG_KMS("enabling CxSR downclocking\n"); |
pipeconf |= PIPECONF_CXSR_DOWNCLOCK; |
} |
} else { |
I915_WRITE(FP1(pipe), fp); |
if (HAS_PIPE_CXSR(dev)) { |
DRM_DEBUG_KMS("disabling CxSR downclocking\n"); |
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; |
} |
5262,18 → 4441,22 |
} |
|
pipeconf &= ~PIPECONF_INTERLACE_MASK; |
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { |
if (!IS_GEN2(dev) && |
adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { |
pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; |
/* the chip adds 2 halflines automatically */ |
adjusted_mode->crtc_vdisplay -= 1; |
adjusted_mode->crtc_vtotal -= 1; |
adjusted_mode->crtc_vblank_start -= 1; |
adjusted_mode->crtc_vblank_end -= 1; |
adjusted_mode->crtc_vsync_end -= 1; |
adjusted_mode->crtc_vsync_start -= 1; |
} else |
vsyncshift = adjusted_mode->crtc_hsync_start |
- adjusted_mode->crtc_htotal/2; |
} else { |
pipeconf |= PIPECONF_PROGRESSIVE; |
vsyncshift = 0; |
} |
|
if (!IS_GEN3(dev)) |
I915_WRITE(VSYNCSHIFT(pipe), vsyncshift); |
|
I915_WRITE(HTOTAL(pipe), |
(adjusted_mode->crtc_hdisplay - 1) | |
((adjusted_mode->crtc_htotal - 1) << 16)); |
5312,9 → 4495,8 |
|
I915_WRITE(DSPCNTR(plane), dspcntr); |
POSTING_READ(DSPCNTR(plane)); |
intel_enable_plane(dev_priv, plane, pipe); |
|
ret = intel_pipe_set_base(crtc, x, y, old_fb); |
ret = intel_pipe_set_base(crtc, x, y, fb); |
|
intel_update_watermarks(dev); |
|
5389,7 → 4571,8 |
if (intel_panel_use_ssc(dev_priv) && can_ssc) { |
DRM_DEBUG_KMS("Using SSC on panel\n"); |
temp |= DREF_SSC1_ENABLE; |
} |
} else |
temp &= ~DREF_SSC1_ENABLE; |
|
/* Get SSC going before enabling the outputs */ |
I915_WRITE(PCH_DREF_CONTROL, temp); |
5442,15 → 4625,11 |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_encoder *encoder; |
struct drm_mode_config *mode_config = &dev->mode_config; |
struct intel_encoder *edp_encoder = NULL; |
int num_connectors = 0; |
bool is_lvds = false; |
|
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { |
if (encoder->base.crtc != crtc) |
continue; |
|
for_each_encoder_on_crtc(dev, crtc, encoder) { |
switch (encoder->type) { |
case INTEL_OUTPUT_LVDS: |
is_lvds = true; |
5471,11 → 4650,118 |
return 120000; |
} |
|
static void ironlake_set_pipeconf(struct drm_crtc *crtc, |
struct drm_display_mode *adjusted_mode, |
bool dither) |
{ |
struct drm_i915_private *dev_priv = crtc->dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
uint32_t val; |
|
val = I915_READ(PIPECONF(pipe)); |
|
val &= ~PIPE_BPC_MASK; |
switch (intel_crtc->bpp) { |
case 18: |
val |= PIPE_6BPC; |
break; |
case 24: |
val |= PIPE_8BPC; |
break; |
case 30: |
val |= PIPE_10BPC; |
break; |
case 36: |
val |= PIPE_12BPC; |
break; |
default: |
val |= PIPE_8BPC; |
break; |
} |
|
val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); |
if (dither) |
val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); |
|
val &= ~PIPECONF_INTERLACE_MASK; |
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) |
val |= PIPECONF_INTERLACED_ILK; |
else |
val |= PIPECONF_PROGRESSIVE; |
|
I915_WRITE(PIPECONF(pipe), val); |
POSTING_READ(PIPECONF(pipe)); |
} |
|
static bool ironlake_compute_clocks(struct drm_crtc *crtc, |
struct drm_display_mode *adjusted_mode, |
intel_clock_t *clock, |
bool *has_reduced_clock, |
intel_clock_t *reduced_clock) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_encoder *intel_encoder; |
int refclk; |
const intel_limit_t *limit; |
bool ret, is_sdvo = false, is_tv = false, is_lvds = false; |
|
for_each_encoder_on_crtc(dev, crtc, intel_encoder) { |
switch (intel_encoder->type) { |
case INTEL_OUTPUT_LVDS: |
is_lvds = true; |
break; |
case INTEL_OUTPUT_SDVO: |
case INTEL_OUTPUT_HDMI: |
is_sdvo = true; |
if (intel_encoder->needs_tv_clock) |
is_tv = true; |
break; |
case INTEL_OUTPUT_TVOUT: |
is_tv = true; |
break; |
} |
} |
|
refclk = ironlake_get_refclk(crtc); |
|
/* |
* Returns a set of divisors for the desired target clock with the given |
* refclk, or FALSE. The returned values represent the clock equation: |
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. |
*/ |
limit = intel_limit(crtc, refclk); |
ret = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, |
clock); |
if (!ret) |
return false; |
|
if (is_lvds && dev_priv->lvds_downclock_avail) { |
/* |
* Ensure we match the reduced clock's P to the target clock. |
* If the clocks don't match, we can't switch the display clock |
* by using the FP0/FP1. In such case we will disable the LVDS |
* downclock feature. |
*/ |
*has_reduced_clock = limit->find_pll(limit, crtc, |
dev_priv->lvds_downclock, |
refclk, |
clock, |
reduced_clock); |
} |
|
if (is_sdvo && is_tv) |
i9xx_adjust_sdvo_tv_clock(adjusted_mode, clock); |
|
return true; |
} |
|
static int ironlake_crtc_mode_set(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, |
struct drm_framebuffer *old_fb) |
struct drm_framebuffer *fb) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
5482,29 → 4768,21 |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int pipe = intel_crtc->pipe; |
int plane = intel_crtc->plane; |
int refclk, num_connectors = 0; |
int num_connectors = 0; |
intel_clock_t clock, reduced_clock; |
u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf; |
u32 dpll, fp = 0, fp2 = 0; |
bool ok, has_reduced_clock = false, is_sdvo = false; |
bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false; |
struct intel_encoder *has_edp_encoder = NULL; |
struct drm_mode_config *mode_config = &dev->mode_config; |
struct intel_encoder *encoder; |
const intel_limit_t *limit; |
struct intel_encoder *encoder, *edp_encoder = NULL; |
int ret; |
struct fdi_m_n m_n = {0}; |
u32 temp; |
u32 lvds_sync = 0; |
int target_clock, pixel_multiplier, lane, link_bw, factor; |
unsigned int pipe_bpp; |
bool dither; |
bool is_cpu_edp = false, is_pch_edp = false; |
|
ENTER(); |
|
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { |
if (encoder->base.crtc != crtc) |
continue; |
|
for_each_encoder_on_crtc(dev, crtc, encoder) { |
switch (encoder->type) { |
case INTEL_OUTPUT_LVDS: |
is_lvds = true; |
5525,7 → 4803,12 |
is_dp = true; |
break; |
case INTEL_OUTPUT_EDP: |
has_edp_encoder = encoder; |
is_dp = true; |
if (intel_encoder_is_pch_edp(&encoder->base)) |
is_pch_edp = true; |
else |
is_cpu_edp = true; |
edp_encoder = encoder; |
break; |
} |
|
5532,15 → 4815,8 |
num_connectors++; |
} |
|
refclk = ironlake_get_refclk(crtc); |
|
/* |
* Returns a set of divisors for the desired target clock with the given |
* refclk, or FALSE. The returned values represent the clock equation: |
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. |
*/ |
limit = intel_limit(crtc, refclk); |
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); |
ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, |
&has_reduced_clock, &reduced_clock); |
if (!ok) { |
DRM_ERROR("Couldn't find PLL settings for mode!\n"); |
return -EINVAL; |
5549,61 → 4825,14 |
/* Ensure that the cursor is valid for the new mode before changing... */ |
// intel_crtc_update_cursor(crtc, true); |
|
if (is_lvds && dev_priv->lvds_downclock_avail) { |
has_reduced_clock = limit->find_pll(limit, crtc, |
dev_priv->lvds_downclock, |
refclk, |
&reduced_clock); |
if (has_reduced_clock && (clock.p != reduced_clock.p)) { |
/* |
* If the different P is found, it means that we can't |
* switch the display clock by using the FP0/FP1. |
* In such case we will disable the LVDS downclock |
* feature. |
*/ |
DRM_DEBUG_KMS("Different P is found for " |
"LVDS clock/downclock\n"); |
has_reduced_clock = 0; |
} |
} |
/* SDVO TV has fixed PLL values depend on its clock range, |
this mirrors vbios setting. */ |
if (is_sdvo && is_tv) { |
if (adjusted_mode->clock >= 100000 |
&& adjusted_mode->clock < 140500) { |
clock.p1 = 2; |
clock.p2 = 10; |
clock.n = 3; |
clock.m1 = 16; |
clock.m2 = 8; |
} else if (adjusted_mode->clock >= 140500 |
&& adjusted_mode->clock <= 200000) { |
clock.p1 = 1; |
clock.p2 = 10; |
clock.n = 6; |
clock.m1 = 12; |
clock.m2 = 8; |
} |
} |
|
/* FDI link */ |
pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); |
lane = 0; |
/* CPU eDP doesn't require FDI link, so just set DP M/N |
according to current link config */ |
if (has_edp_encoder && |
!intel_encoder_is_pch_edp(&has_edp_encoder->base)) { |
target_clock = mode->clock; |
intel_edp_link_config(has_edp_encoder, |
&lane, &link_bw); |
if (is_cpu_edp) { |
intel_edp_link_config(edp_encoder, &lane, &link_bw); |
} else { |
/* [e]DP over FDI requires target mode clock |
instead of link clock */ |
if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) |
target_clock = mode->clock; |
else |
target_clock = adjusted_mode->clock; |
|
/* FDI is a binary signal running at ~2.7GHz, encoding |
* each output octet as 10 bits. The actual frequency |
* is stored as a divider into a 100MHz clock, and the |
5614,33 → 4843,27 |
link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; |
} |
|
/* [e]DP over FDI requires target mode clock instead of link clock. */ |
if (edp_encoder) |
target_clock = intel_edp_target_clock(edp_encoder, mode); |
else if (is_dp) |
target_clock = mode->clock; |
else |
target_clock = adjusted_mode->clock; |
|
/* determine panel color depth */ |
temp = I915_READ(PIPECONF(pipe)); |
temp &= ~PIPE_BPC_MASK; |
dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp, mode); |
switch (pipe_bpp) { |
case 18: |
temp |= PIPE_6BPC; |
break; |
case 24: |
temp |= PIPE_8BPC; |
break; |
case 30: |
temp |= PIPE_10BPC; |
break; |
case 36: |
temp |= PIPE_12BPC; |
break; |
default: |
dither = intel_choose_pipe_bpp_dither(crtc, fb, &pipe_bpp, |
adjusted_mode); |
if (is_lvds && dev_priv->lvds_dither) |
dither = true; |
|
if (pipe_bpp != 18 && pipe_bpp != 24 && pipe_bpp != 30 && |
pipe_bpp != 36) { |
WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n", |
pipe_bpp); |
temp |= PIPE_8BPC; |
pipe_bpp = 24; |
break; |
} |
|
intel_crtc->bpp = pipe_bpp; |
I915_WRITE(PIPECONF(pipe), temp); |
|
if (!lane) { |
/* |
5690,7 → 4913,7 |
} |
dpll |= DPLL_DVO_HIGH_SPEED; |
} |
if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) |
if (is_dp && !is_cpu_edp) |
dpll |= DPLL_DVO_HIGH_SPEED; |
|
/* compute bitmask from p1 value */ |
5724,39 → 4947,25 |
else |
dpll |= PLL_REF_INPUT_DREFCLK; |
|
/* setup pipeconf */ |
pipeconf = I915_READ(PIPECONF(pipe)); |
|
/* Set up the display plane register */ |
dspcntr = DISPPLANE_GAMMA_ENABLE; |
|
DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); |
drm_mode_debug_printmodeline(mode); |
|
/* PCH eDP needs FDI, but CPU eDP does not */ |
if (!intel_crtc->no_pll) { |
if (!has_edp_encoder || |
intel_encoder_is_pch_edp(&has_edp_encoder->base)) { |
I915_WRITE(PCH_FP0(pipe), fp); |
I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); |
/* CPU eDP is the only output that doesn't need a PCH PLL of its own on |
* pre-Haswell/LPT generation */ |
if (HAS_PCH_LPT(dev)) { |
DRM_DEBUG_KMS("LPT detected: no PLL for pipe %d necessary\n", |
pipe); |
} else if (!is_cpu_edp) { |
struct intel_pch_pll *pll; |
|
POSTING_READ(PCH_DPLL(pipe)); |
udelay(150); |
} |
} else { |
if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) && |
fp == I915_READ(PCH_FP0(0))) { |
intel_crtc->use_pll_a = true; |
DRM_DEBUG_KMS("using pipe a dpll\n"); |
} else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) && |
fp == I915_READ(PCH_FP0(1))) { |
intel_crtc->use_pll_a = false; |
DRM_DEBUG_KMS("using pipe b dpll\n"); |
} else { |
DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n"); |
pll = intel_get_pch_pll(intel_crtc, dpll, fp); |
if (pll == NULL) { |
DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", |
pipe); |
return -EINVAL; |
} |
} |
} else |
intel_put_pch_pll(intel_crtc); |
|
/* 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 |
5789,32 → 4998,15 |
* appropriately here, but we need to look more thoroughly into how |
* panels behave in the two modes. |
*/ |
temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); |
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) |
lvds_sync |= LVDS_HSYNC_POLARITY; |
temp |= LVDS_HSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) |
lvds_sync |= LVDS_VSYNC_POLARITY; |
if ((temp & (LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY)) |
!= lvds_sync) { |
char flags[2] = "-+"; |
DRM_INFO("Changing LVDS panel from " |
"(%chsync, %cvsync) to (%chsync, %cvsync)\n", |
flags[!(temp & LVDS_HSYNC_POLARITY)], |
flags[!(temp & LVDS_VSYNC_POLARITY)], |
flags[!(lvds_sync & LVDS_HSYNC_POLARITY)], |
flags[!(lvds_sync & LVDS_VSYNC_POLARITY)]); |
temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); |
temp |= lvds_sync; |
} |
temp |= LVDS_VSYNC_POLARITY; |
I915_WRITE(PCH_LVDS, temp); |
} |
|
pipeconf &= ~PIPECONF_DITHER_EN; |
pipeconf &= ~PIPECONF_DITHER_TYPE_MASK; |
if ((is_lvds && dev_priv->lvds_dither) || dither) { |
pipeconf |= PIPECONF_DITHER_EN; |
pipeconf |= PIPECONF_DITHER_TYPE_SP; |
} |
if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { |
if (is_dp && !is_cpu_edp) { |
intel_dp_set_m_n(crtc, mode, adjusted_mode); |
} else { |
/* For non-DP output, clear any trans DP clock recovery setting.*/ |
5824,13 → 5016,11 |
I915_WRITE(TRANSDPLINK_N1(pipe), 0); |
} |
|
if (!intel_crtc->no_pll && |
(!has_edp_encoder || |
intel_encoder_is_pch_edp(&has_edp_encoder->base))) { |
I915_WRITE(PCH_DPLL(pipe), dpll); |
if (intel_crtc->pch_pll) { |
I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); |
|
/* Wait for the clocks to stabilize. */ |
POSTING_READ(PCH_DPLL(pipe)); |
POSTING_READ(intel_crtc->pch_pll->pll_reg); |
udelay(150); |
|
/* The pixel multiplier can only be updated once the |
5838,39 → 5028,29 |
* |
* So write it again. |
*/ |
I915_WRITE(PCH_DPLL(pipe), dpll); |
I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); |
} |
|
intel_crtc->lowfreq_avail = false; |
if (!intel_crtc->no_pll) { |
if (intel_crtc->pch_pll) { |
if (is_lvds && has_reduced_clock && i915_powersave) { |
I915_WRITE(PCH_FP1(pipe), fp2); |
I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); |
intel_crtc->lowfreq_avail = true; |
if (HAS_PIPE_CXSR(dev)) { |
DRM_DEBUG_KMS("enabling CxSR downclocking\n"); |
pipeconf |= PIPECONF_CXSR_DOWNCLOCK; |
} |
} else { |
I915_WRITE(PCH_FP1(pipe), fp); |
if (HAS_PIPE_CXSR(dev)) { |
DRM_DEBUG_KMS("disabling CxSR downclocking\n"); |
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; |
I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); |
} |
} |
} |
|
pipeconf &= ~PIPECONF_INTERLACE_MASK; |
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { |
pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; |
/* the chip adds 2 halflines automatically */ |
adjusted_mode->crtc_vdisplay -= 1; |
adjusted_mode->crtc_vtotal -= 1; |
adjusted_mode->crtc_vblank_start -= 1; |
adjusted_mode->crtc_vblank_end -= 1; |
adjusted_mode->crtc_vsync_end -= 1; |
adjusted_mode->crtc_vsync_start -= 1; |
} else |
pipeconf |= PIPECONF_PROGRESSIVE; |
I915_WRITE(VSYNCSHIFT(pipe), |
adjusted_mode->crtc_hsync_start |
- adjusted_mode->crtc_htotal/2); |
} else { |
I915_WRITE(VSYNCSHIFT(pipe), 0); |
} |
|
I915_WRITE(HTOTAL(pipe), |
(adjusted_mode->crtc_hdisplay - 1) | |
5903,32 → 5083,22 |
I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m); |
I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n); |
|
if (has_edp_encoder && |
!intel_encoder_is_pch_edp(&has_edp_encoder->base)) { |
if (is_cpu_edp) |
ironlake_set_pll_edp(crtc, adjusted_mode->clock); |
} |
|
I915_WRITE(PIPECONF(pipe), pipeconf); |
POSTING_READ(PIPECONF(pipe)); |
ironlake_set_pipeconf(crtc, adjusted_mode, dither); |
|
intel_wait_for_vblank(dev, pipe); |
|
if (IS_GEN5(dev)) { |
/* enable address swizzle for tiling buffer */ |
temp = I915_READ(DISP_ARB_CTL); |
I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING); |
} |
|
I915_WRITE(DSPCNTR(plane), dspcntr); |
/* Set up the display plane register */ |
I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); |
POSTING_READ(DSPCNTR(plane)); |
|
ret = intel_pipe_set_base(crtc, x, y, old_fb); |
ret = intel_pipe_set_base(crtc, x, y, fb); |
|
dbgprintf("Set base\n"); |
|
intel_update_watermarks(dev); |
|
LEAVE(); |
intel_update_linetime_watermarks(dev, pipe, adjusted_mode); |
|
return ret; |
} |
5937,7 → 5107,7 |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, |
struct drm_framebuffer *old_fb) |
struct drm_framebuffer *fb) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
5945,17 → 5115,12 |
int pipe = intel_crtc->pipe; |
int ret; |
|
// drm_vblank_pre_modeset(dev, pipe); |
ENTER(); |
drm_vblank_pre_modeset(dev, pipe); |
|
ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, |
x, y, old_fb); |
x, y, fb); |
drm_vblank_post_modeset(dev, pipe); |
|
// drm_vblank_post_modeset(dev, pipe); |
|
intel_crtc->dpms_mode = DRM_MODE_DPMS_ON; |
LEAVE(); |
|
return ret; |
} |
|
6028,6 → 5193,91 |
I915_WRITE(G4X_AUD_CNTL_ST, i); |
} |
|
static void haswell_write_eld(struct drm_connector *connector, |
struct drm_crtc *crtc) |
{ |
struct drm_i915_private *dev_priv = connector->dev->dev_private; |
uint8_t *eld = connector->eld; |
struct drm_device *dev = crtc->dev; |
uint32_t eldv; |
uint32_t i; |
int len; |
int pipe = to_intel_crtc(crtc)->pipe; |
int tmp; |
|
int hdmiw_hdmiedid = HSW_AUD_EDID_DATA(pipe); |
int aud_cntl_st = HSW_AUD_DIP_ELD_CTRL(pipe); |
int aud_config = HSW_AUD_CFG(pipe); |
int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD; |
|
|
DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n"); |
|
/* Audio output enable */ |
DRM_DEBUG_DRIVER("HDMI audio: enable codec\n"); |
tmp = I915_READ(aud_cntrl_st2); |
tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4)); |
I915_WRITE(aud_cntrl_st2, tmp); |
|
/* Wait for 1 vertical blank */ |
intel_wait_for_vblank(dev, pipe); |
|
/* Set ELD valid state */ |
tmp = I915_READ(aud_cntrl_st2); |
DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp); |
tmp |= (AUDIO_ELD_VALID_A << (pipe * 4)); |
I915_WRITE(aud_cntrl_st2, tmp); |
tmp = I915_READ(aud_cntrl_st2); |
DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp); |
|
/* Enable HDMI mode */ |
tmp = I915_READ(aud_config); |
DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp); |
/* clear N_programing_enable and N_value_index */ |
tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE); |
I915_WRITE(aud_config, tmp); |
|
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); |
|
eldv = AUDIO_ELD_VALID_A << (pipe * 4); |
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { |
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); |
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */ |
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */ |
} else |
I915_WRITE(aud_config, 0); |
|
if (intel_eld_uptodate(connector, |
aud_cntrl_st2, eldv, |
aud_cntl_st, IBX_ELD_ADDRESS, |
hdmiw_hdmiedid)) |
return; |
|
i = I915_READ(aud_cntrl_st2); |
i &= ~eldv; |
I915_WRITE(aud_cntrl_st2, i); |
|
if (!eld[0]) |
return; |
|
i = I915_READ(aud_cntl_st); |
i &= ~IBX_ELD_ADDRESS; |
I915_WRITE(aud_cntl_st, i); |
i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ |
DRM_DEBUG_DRIVER("port num:%d\n", i); |
|
len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */ |
DRM_DEBUG_DRIVER("ELD size %d\n", len); |
for (i = 0; i < len; i++) |
I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i)); |
|
i = I915_READ(aud_cntrl_st2); |
i |= eldv; |
I915_WRITE(aud_cntrl_st2, i); |
|
} |
|
static void ironlake_write_eld(struct drm_connector *connector, |
struct drm_crtc *crtc) |
{ |
6037,27 → 5287,27 |
uint32_t i; |
int len; |
int hdmiw_hdmiedid; |
int aud_config; |
int aud_cntl_st; |
int aud_cntrl_st2; |
int pipe = to_intel_crtc(crtc)->pipe; |
|
if (HAS_PCH_IBX(connector->dev)) { |
hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A; |
aud_cntl_st = IBX_AUD_CNTL_ST_A; |
hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID(pipe); |
aud_config = IBX_AUD_CFG(pipe); |
aud_cntl_st = IBX_AUD_CNTL_ST(pipe); |
aud_cntrl_st2 = IBX_AUD_CNTL_ST2; |
} else { |
hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A; |
aud_cntl_st = CPT_AUD_CNTL_ST_A; |
hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe); |
aud_config = CPT_AUD_CFG(pipe); |
aud_cntl_st = CPT_AUD_CNTL_ST(pipe); |
aud_cntrl_st2 = CPT_AUD_CNTRL_ST2; |
} |
|
i = to_intel_crtc(crtc)->pipe; |
hdmiw_hdmiedid += i * 0x100; |
aud_cntl_st += i * 0x100; |
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); |
|
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i)); |
|
i = I915_READ(aud_cntl_st); |
i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */ |
i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ |
if (!i) { |
DRM_DEBUG_DRIVER("Audio directed to unknown port\n"); |
/* operate blindly on all ports */ |
6072,7 → 5322,9 |
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { |
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); |
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */ |
} |
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */ |
} else |
I915_WRITE(aud_config, 0); |
|
if (intel_eld_uptodate(connector, |
aud_cntrl_st2, eldv, |
6135,7 → 5387,7 |
int i; |
|
/* The clocks have to be on to load the palette. */ |
if (!crtc->enabled) |
if (!crtc->enabled || !intel_crtc->active) |
return; |
|
/* use legacy palette for Ironlake */ |
6150,42 → 5402,263 |
} |
} |
|
#if 0 |
static void i845_update_cursor(struct drm_crtc *crtc, u32 base) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
bool visible = base != 0; |
u32 cntl; |
|
if (intel_crtc->cursor_visible == visible) |
return; |
|
cntl = I915_READ(_CURACNTR); |
if (visible) { |
/* On these chipsets we can only modify the base whilst |
* the cursor is disabled. |
*/ |
I915_WRITE(_CURABASE, base); |
|
cntl &= ~(CURSOR_FORMAT_MASK); |
/* XXX width must be 64, stride 256 => 0x00 << 28 */ |
cntl |= CURSOR_ENABLE | |
CURSOR_GAMMA_ENABLE | |
CURSOR_FORMAT_ARGB; |
} else |
cntl &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE); |
I915_WRITE(_CURACNTR, cntl); |
|
intel_crtc->cursor_visible = visible; |
} |
|
static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) |
{ |
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; |
bool visible = base != 0; |
|
if (intel_crtc->cursor_visible != visible) { |
uint32_t cntl = I915_READ(CURCNTR(pipe)); |
if (base) { |
cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT); |
cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; |
cntl |= pipe << 28; /* Connect to correct pipe */ |
} else { |
cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); |
cntl |= CURSOR_MODE_DISABLE; |
} |
I915_WRITE(CURCNTR(pipe), cntl); |
|
intel_crtc->cursor_visible = visible; |
} |
/* and commit changes on next vblank */ |
I915_WRITE(CURBASE(pipe), base); |
} |
|
static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) |
{ |
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; |
bool visible = base != 0; |
|
if (intel_crtc->cursor_visible != visible) { |
uint32_t cntl = I915_READ(CURCNTR_IVB(pipe)); |
if (base) { |
cntl &= ~CURSOR_MODE; |
cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; |
} else { |
cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); |
cntl |= CURSOR_MODE_DISABLE; |
} |
I915_WRITE(CURCNTR_IVB(pipe), cntl); |
|
intel_crtc->cursor_visible = visible; |
} |
/* and commit changes on next vblank */ |
I915_WRITE(CURBASE_IVB(pipe), base); |
} |
|
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */ |
static void intel_crtc_update_cursor(struct drm_crtc *crtc, |
bool on) |
{ |
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 x = intel_crtc->cursor_x; |
int y = intel_crtc->cursor_y; |
u32 base, pos; |
bool visible; |
|
pos = 0; |
|
if (on && crtc->enabled && crtc->fb) { |
base = intel_crtc->cursor_addr; |
if (x > (int) crtc->fb->width) |
base = 0; |
|
if (y > (int) crtc->fb->height) |
base = 0; |
} else |
base = 0; |
|
if (x < 0) { |
if (x + intel_crtc->cursor_width < 0) |
base = 0; |
|
pos |= CURSOR_POS_SIGN << CURSOR_X_SHIFT; |
x = -x; |
} |
pos |= x << CURSOR_X_SHIFT; |
|
if (y < 0) { |
if (y + intel_crtc->cursor_height < 0) |
base = 0; |
|
pos |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT; |
y = -y; |
} |
pos |= y << CURSOR_Y_SHIFT; |
|
visible = base != 0; |
if (!visible && !intel_crtc->cursor_visible) |
return; |
|
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { |
I915_WRITE(CURPOS_IVB(pipe), pos); |
ivb_update_cursor(crtc, base); |
} else { |
I915_WRITE(CURPOS(pipe), pos); |
if (IS_845G(dev) || IS_I865G(dev)) |
i845_update_cursor(crtc, base); |
else |
i9xx_update_cursor(crtc, base); |
} |
} |
|
static int intel_crtc_cursor_set(struct drm_crtc *crtc, |
struct drm_file *file, |
uint32_t handle, |
uint32_t width, uint32_t height) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct drm_i915_gem_object *obj; |
uint32_t addr; |
int ret; |
|
/* if we want to turn off the cursor ignore width and height */ |
if (!handle) { |
DRM_DEBUG_KMS("cursor off\n"); |
addr = 0; |
obj = NULL; |
mutex_lock(&dev->struct_mutex); |
goto finish; |
} |
|
/* Currently we only support 64x64 cursors */ |
if (width != 64 || height != 64) { |
DRM_ERROR("we currently only support 64x64 cursors\n"); |
return -EINVAL; |
} |
|
obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); |
if (&obj->base == NULL) |
return -ENOENT; |
|
if (obj->base.size < width * height * 4) { |
DRM_ERROR("buffer is to small\n"); |
ret = -ENOMEM; |
goto fail; |
} |
|
/* we only need to pin inside GTT if cursor is non-phy */ |
mutex_lock(&dev->struct_mutex); |
if (!dev_priv->info->cursor_needs_physical) { |
if (obj->tiling_mode) { |
DRM_ERROR("cursor cannot be tiled\n"); |
ret = -EINVAL; |
goto fail_locked; |
} |
|
ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL); |
if (ret) { |
DRM_ERROR("failed to move cursor bo into the GTT\n"); |
goto fail_locked; |
} |
|
ret = i915_gem_object_put_fence(obj); |
if (ret) { |
DRM_ERROR("failed to release fence for cursor"); |
goto fail_unpin; |
} |
|
addr = obj->gtt_offset; |
} else { |
int align = IS_I830(dev) ? 16 * 1024 : 256; |
ret = i915_gem_attach_phys_object(dev, obj, |
(intel_crtc->pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1, |
align); |
if (ret) { |
DRM_ERROR("failed to attach phys object\n"); |
goto fail_locked; |
} |
addr = obj->phys_obj->handle->busaddr; |
} |
|
if (IS_GEN2(dev)) |
I915_WRITE(CURSIZE, (height << 12) | width); |
|
finish: |
if (intel_crtc->cursor_bo) { |
if (dev_priv->info->cursor_needs_physical) { |
if (intel_crtc->cursor_bo != obj) |
i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo); |
} else |
i915_gem_object_unpin(intel_crtc->cursor_bo); |
drm_gem_object_unreference(&intel_crtc->cursor_bo->base); |
} |
|
mutex_unlock(&dev->struct_mutex); |
|
intel_crtc->cursor_addr = addr; |
intel_crtc->cursor_bo = obj; |
intel_crtc->cursor_width = width; |
intel_crtc->cursor_height = height; |
|
// intel_crtc_update_cursor(crtc, true); |
|
return 0; |
fail_unpin: |
i915_gem_object_unpin(obj); |
fail_locked: |
mutex_unlock(&dev->struct_mutex); |
fail: |
drm_gem_object_unreference_unlocked(&obj->base); |
return ret; |
} |
|
static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
|
intel_crtc->cursor_x = x; |
intel_crtc->cursor_y = y; |
|
// intel_crtc_update_cursor(crtc, true); |
|
return 0; |
} |
#endif |
|
/** Sets the color ramps on behalf of RandR */ |
void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, |
u16 blue, int regno) |
6242,9 → 5715,29 |
704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), |
}; |
|
static struct drm_framebuffer * |
intel_framebuffer_create(struct drm_device *dev, |
struct drm_mode_fb_cmd2 *mode_cmd, |
struct drm_i915_gem_object *obj) |
{ |
struct intel_framebuffer *intel_fb; |
int ret; |
|
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); |
if (!intel_fb) { |
drm_gem_object_unreference_unlocked(&obj->base); |
return ERR_PTR(-ENOMEM); |
} |
|
ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj); |
if (ret) { |
drm_gem_object_unreference_unlocked(&obj->base); |
kfree(intel_fb); |
return ERR_PTR(ret); |
} |
|
return &intel_fb->base; |
} |
|
static u32 |
intel_framebuffer_pitch_for_width(int width, int bpp) |
6295,30 → 5788,31 |
|
// obj = dev_priv->fbdev->ifb.obj; |
// if (obj == NULL) |
// return NULL; |
|
// fb = &dev_priv->fbdev->ifb.base; |
// if (fb->pitch < intel_framebuffer_pitch_for_width(mode->hdisplay, |
// fb->bits_per_pixel)) |
return NULL; |
|
// if (obj->base.size < mode->vdisplay * fb->pitch) |
if (fb->pitches[0] < intel_framebuffer_pitch_for_width(mode->hdisplay, |
fb->bits_per_pixel)) |
// return NULL; |
|
if (obj->base.size < mode->vdisplay * fb->pitches[0]) |
return NULL; |
|
// return fb; |
} |
|
bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, |
struct drm_connector *connector, |
bool intel_get_load_detect_pipe(struct drm_connector *connector, |
struct drm_display_mode *mode, |
struct intel_load_detect_pipe *old) |
{ |
struct intel_crtc *intel_crtc; |
struct intel_encoder *intel_encoder = |
intel_attached_encoder(connector); |
struct drm_crtc *possible_crtc; |
struct drm_encoder *encoder = &intel_encoder->base; |
struct drm_crtc *crtc = NULL; |
struct drm_device *dev = encoder->dev; |
struct drm_framebuffer *old_fb; |
struct drm_framebuffer *fb; |
int i = -1; |
|
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", |
6339,22 → 5833,13 |
if (encoder->crtc) { |
crtc = encoder->crtc; |
|
intel_crtc = to_intel_crtc(crtc); |
old->dpms_mode = intel_crtc->dpms_mode; |
old->dpms_mode = connector->dpms; |
old->load_detect_temp = false; |
|
/* Make sure the crtc and connector are running */ |
if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct drm_crtc_helper_funcs *crtc_funcs; |
if (connector->dpms != DRM_MODE_DPMS_ON) |
connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); |
|
crtc_funcs = crtc->helper_private; |
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); |
|
encoder_funcs = encoder->helper_private; |
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); |
} |
|
return true; |
} |
|
6377,11 → 5862,11 |
return false; |
} |
|
encoder->crtc = crtc; |
connector->encoder = encoder; |
intel_encoder->new_crtc = to_intel_crtc(crtc); |
to_intel_connector(connector)->new_encoder = intel_encoder; |
|
intel_crtc = to_intel_crtc(crtc); |
old->dpms_mode = intel_crtc->dpms_mode; |
old->dpms_mode = connector->dpms; |
old->load_detect_temp = true; |
old->release_fb = NULL; |
|
6388,8 → 5873,6 |
if (!mode) |
mode = &load_detect_mode; |
|
old_fb = crtc->fb; |
|
/* We need a framebuffer large enough to accommodate all accesses |
* that the plane may generate whilst we perform load detection. |
* We can not rely on the fbcon either being present (we get called |
6397,25 → 5880,23 |
* not even exist) or that it is large enough to satisfy the |
* requested mode. |
*/ |
crtc->fb = mode_fits_in_fbdev(dev, mode); |
if (crtc->fb == NULL) { |
fb = mode_fits_in_fbdev(dev, mode); |
if (fb == NULL) { |
DRM_DEBUG_KMS("creating tmp fb for load-detection\n"); |
crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); |
old->release_fb = crtc->fb; |
fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); |
old->release_fb = fb; |
} else |
DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); |
if (IS_ERR(crtc->fb)) { |
if (IS_ERR(fb)) { |
DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); |
crtc->fb = old_fb; |
return false; |
goto fail; |
} |
|
if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) { |
if (!intel_set_mode(crtc, mode, 0, 0, fb)) { |
DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); |
if (old->release_fb) |
old->release_fb->funcs->destroy(old->release_fb); |
crtc->fb = old_fb; |
return false; |
goto fail; |
} |
|
/* let the connector get through one full cycle before testing */ |
6422,17 → 5903,18 |
intel_wait_for_vblank(dev, intel_crtc->pipe); |
|
return true; |
fail: |
connector->encoder = NULL; |
encoder->crtc = NULL; |
return false; |
} |
|
void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, |
struct drm_connector *connector, |
void intel_release_load_detect_pipe(struct drm_connector *connector, |
struct intel_load_detect_pipe *old) |
{ |
struct intel_encoder *intel_encoder = |
intel_attached_encoder(connector); |
struct drm_encoder *encoder = &intel_encoder->base; |
struct drm_device *dev = encoder->dev; |
struct drm_crtc *crtc = encoder->crtc; |
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
|
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", |
connector->base.id, drm_get_connector_name(connector), |
6439,9 → 5921,12 |
encoder->base.id, drm_get_encoder_name(encoder)); |
|
if (old->load_detect_temp) { |
connector->encoder = NULL; |
drm_helper_disable_unused_functions(dev); |
struct drm_crtc *crtc = encoder->crtc; |
|
to_intel_connector(connector)->new_encoder = NULL; |
intel_encoder->new_crtc = NULL; |
intel_set_mode(crtc, NULL, 0, 0, NULL); |
|
if (old->release_fb) |
old->release_fb->funcs->destroy(old->release_fb); |
|
6449,11 → 5934,9 |
} |
|
/* Switch crtc and encoder back off if necessary */ |
if (old->dpms_mode != DRM_MODE_DPMS_ON) { |
encoder_funcs->dpms(encoder, old->dpms_mode); |
crtc_funcs->dpms(crtc, old->dpms_mode); |
if (old->dpms_mode != DRM_MODE_DPMS_ON) |
connector->funcs->dpms(connector, old->dpms_mode); |
} |
} |
|
/* Returns the clock of the currently programmed mode of the given pipe. */ |
static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) |
6570,21 → 6053,10 |
mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; |
|
drm_mode_set_name(mode); |
drm_mode_set_crtcinfo(mode, 0); |
|
return mode; |
} |
|
#define GPU_IDLE_TIMEOUT 500 /* ms */ |
|
|
|
|
#define CRTC_IDLE_TIMEOUT 1000 /* ms */ |
|
|
|
|
static void intel_increase_pllclock(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
6594,8 → 6066,6 |
int dpll_reg = DPLL(pipe); |
int dpll; |
|
ENTER(); |
|
if (HAS_PCH_SPLIT(dev)) |
return; |
|
6606,9 → 6076,7 |
if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { |
DRM_DEBUG_DRIVER("upclocking LVDS\n"); |
|
/* Unlock panel regs */ |
I915_WRITE(PP_CONTROL, |
I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); |
assert_panel_unlocked(dev_priv, pipe); |
|
dpll &= ~DISPLAY_RATE_SELECT_FPA1; |
I915_WRITE(dpll_reg, dpll); |
6617,37 → 6085,88 |
dpll = I915_READ(dpll_reg); |
if (dpll & DISPLAY_RATE_SELECT_FPA1) |
DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); |
|
/* ...and lock them again */ |
I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); |
} |
|
LEAVE(); |
|
/* Schedule downclock */ |
} |
|
static void intel_decrease_pllclock(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
|
if (HAS_PCH_SPLIT(dev)) |
return; |
|
if (!dev_priv->lvds_downclock_avail) |
return; |
|
/* |
* Since this is called by a timer, we should never get here in |
* the manual case. |
*/ |
if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) { |
int pipe = intel_crtc->pipe; |
int dpll_reg = DPLL(pipe); |
int dpll; |
|
DRM_DEBUG_DRIVER("downclocking LVDS\n"); |
|
assert_panel_unlocked(dev_priv, pipe); |
|
dpll = I915_READ(dpll_reg); |
dpll |= DISPLAY_RATE_SELECT_FPA1; |
I915_WRITE(dpll_reg, dpll); |
intel_wait_for_vblank(dev, pipe); |
dpll = I915_READ(dpll_reg); |
if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) |
DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); |
} |
|
} |
|
void intel_mark_busy(struct drm_device *dev) |
{ |
i915_update_gfx_val(dev->dev_private); |
} |
|
void intel_mark_idle(struct drm_device *dev) |
{ |
} |
|
void intel_mark_fb_busy(struct drm_i915_gem_object *obj) |
{ |
struct drm_device *dev = obj->base.dev; |
struct drm_crtc *crtc; |
|
if (!i915_powersave) |
return; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (!crtc->fb) |
continue; |
|
if (to_intel_framebuffer(crtc->fb)->obj == obj) |
intel_increase_pllclock(crtc); |
} |
} |
|
void intel_mark_fb_idle(struct drm_i915_gem_object *obj) |
{ |
struct drm_device *dev = obj->base.dev; |
struct drm_crtc *crtc; |
|
if (!i915_powersave) |
return; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (!crtc->fb) |
continue; |
|
if (to_intel_framebuffer(crtc->fb)->obj == obj) |
intel_decrease_pllclock(crtc); |
} |
} |
|
|
|
|
static void intel_crtc_destroy(struct drm_crtc *crtc) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
6670,1176 → 6189,1570 |
kfree(intel_crtc); |
} |
|
#if 0 |
static void intel_unpin_work_fn(struct work_struct *__work) |
{ |
struct intel_unpin_work *work = |
container_of(__work, struct intel_unpin_work, work); |
|
mutex_lock(&work->dev->struct_mutex); |
intel_unpin_fb_obj(work->old_fb_obj); |
drm_gem_object_unreference(&work->pending_flip_obj->base); |
drm_gem_object_unreference(&work->old_fb_obj->base); |
|
intel_update_fbc(work->dev); |
mutex_unlock(&work->dev->struct_mutex); |
kfree(work); |
} |
|
static void do_intel_finish_page_flip(struct drm_device *dev, |
struct drm_crtc *crtc) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_unpin_work *work; |
struct drm_i915_gem_object *obj; |
struct drm_pending_vblank_event *e; |
struct timeval tvbl; |
unsigned long flags; |
|
/* Ignore early vblank irqs */ |
if (intel_crtc == NULL) |
return; |
|
spin_lock_irqsave(&dev->event_lock, flags); |
work = intel_crtc->unpin_work; |
if (work == NULL || !work->pending) { |
spin_unlock_irqrestore(&dev->event_lock, flags); |
return; |
} |
|
intel_crtc->unpin_work = NULL; |
|
if (work->event) { |
e = work->event; |
e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &tvbl); |
|
e->event.tv_sec = tvbl.tv_sec; |
e->event.tv_usec = tvbl.tv_usec; |
|
list_add_tail(&e->base.link, |
&e->base.file_priv->event_list); |
wake_up_interruptible(&e->base.file_priv->event_wait); |
} |
|
drm_vblank_put(dev, intel_crtc->pipe); |
|
spin_unlock_irqrestore(&dev->event_lock, flags); |
|
obj = work->old_fb_obj; |
|
atomic_clear_mask(1 << intel_crtc->plane, |
&obj->pending_flip.counter); |
|
wake_up(&dev_priv->pending_flip_queue); |
schedule_work(&work->work); |
|
trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); |
} |
|
void intel_finish_page_flip(struct drm_device *dev, int pipe) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; |
|
do_intel_finish_page_flip(dev, crtc); |
} |
|
void intel_finish_page_flip_plane(struct drm_device *dev, int plane) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane]; |
|
do_intel_finish_page_flip(dev, crtc); |
} |
|
void intel_prepare_page_flip(struct drm_device *dev, int plane) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = |
to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]); |
unsigned long flags; |
|
spin_lock_irqsave(&dev->event_lock, flags); |
if (intel_crtc->unpin_work) { |
if ((++intel_crtc->unpin_work->pending) > 1) |
DRM_ERROR("Prepared flip multiple times\n"); |
} else { |
DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n"); |
} |
spin_unlock_irqrestore(&dev->event_lock, flags); |
} |
|
static int intel_gen2_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
u32 flip_mask; |
struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; |
int ret; |
|
ret = intel_pin_and_fence_fb_obj(dev, obj, ring); |
if (ret) |
goto err; |
|
ret = intel_ring_begin(ring, 6); |
if (ret) |
goto err_unpin; |
|
/* Can't queue multiple flips, so wait for the previous |
* one to finish before executing the next. |
*/ |
if (intel_crtc->plane) |
flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; |
else |
flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; |
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); |
intel_ring_emit(ring, MI_NOOP); |
intel_ring_emit(ring, MI_DISPLAY_FLIP | |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); |
intel_ring_emit(ring, fb->pitches[0]); |
intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); |
intel_ring_emit(ring, 0); /* aux display base address, unused */ |
intel_ring_advance(ring); |
return 0; |
|
err_unpin: |
intel_unpin_fb_obj(obj); |
err: |
return ret; |
} |
|
static int intel_gen3_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
u32 flip_mask; |
struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; |
int ret; |
|
ret = intel_pin_and_fence_fb_obj(dev, obj, ring); |
if (ret) |
goto err; |
|
ret = intel_ring_begin(ring, 6); |
if (ret) |
goto err_unpin; |
|
if (intel_crtc->plane) |
flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; |
else |
flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; |
intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); |
intel_ring_emit(ring, MI_NOOP); |
intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); |
intel_ring_emit(ring, fb->pitches[0]); |
intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); |
intel_ring_emit(ring, MI_NOOP); |
|
intel_ring_advance(ring); |
return 0; |
|
err_unpin: |
intel_unpin_fb_obj(obj); |
err: |
return ret; |
} |
|
static int intel_gen4_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
uint32_t pf, pipesrc; |
struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; |
int ret; |
|
ret = intel_pin_and_fence_fb_obj(dev, obj, ring); |
if (ret) |
goto err; |
|
ret = intel_ring_begin(ring, 4); |
if (ret) |
goto err_unpin; |
|
/* i965+ uses the linear or tiled offsets from the |
* Display Registers (which do not change across a page-flip) |
* so we need only reprogram the base address. |
*/ |
intel_ring_emit(ring, MI_DISPLAY_FLIP | |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); |
intel_ring_emit(ring, fb->pitches[0]); |
intel_ring_emit(ring, |
(obj->gtt_offset + intel_crtc->dspaddr_offset) | |
obj->tiling_mode); |
|
/* XXX Enabling the panel-fitter across page-flip is so far |
* untested on non-native modes, so ignore it for now. |
* pf = I915_READ(pipe == 0 ? PFA_CTL_1 : PFB_CTL_1) & PF_ENABLE; |
*/ |
pf = 0; |
pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; |
intel_ring_emit(ring, pf | pipesrc); |
intel_ring_advance(ring); |
return 0; |
|
err_unpin: |
intel_unpin_fb_obj(obj); |
err: |
return ret; |
} |
|
static int intel_gen6_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; |
uint32_t pf, pipesrc; |
int ret; |
|
ret = intel_pin_and_fence_fb_obj(dev, obj, ring); |
if (ret) |
goto err; |
|
ret = intel_ring_begin(ring, 4); |
if (ret) |
goto err_unpin; |
|
intel_ring_emit(ring, MI_DISPLAY_FLIP | |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); |
intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode); |
intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); |
|
/* Contrary to the suggestions in the documentation, |
* "Enable Panel Fitter" does not seem to be required when page |
* flipping with a non-native mode, and worse causes a normal |
* modeset to fail. |
* pf = I915_READ(PF_CTL(intel_crtc->pipe)) & PF_ENABLE; |
*/ |
pf = 0; |
pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; |
intel_ring_emit(ring, pf | pipesrc); |
intel_ring_advance(ring); |
return 0; |
|
err_unpin: |
intel_unpin_fb_obj(obj); |
err: |
return ret; |
} |
|
/* |
* On gen7 we currently use the blit ring because (in early silicon at least) |
* the render ring doesn't give us interrpts for page flip completion, which |
* means clients will hang after the first flip is queued. Fortunately the |
* blit ring generates interrupts properly, so use it instead. |
*/ |
static int intel_gen7_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_ring_buffer *ring = &dev_priv->ring[BCS]; |
uint32_t plane_bit = 0; |
int ret; |
|
ret = intel_pin_and_fence_fb_obj(dev, obj, ring); |
if (ret) |
goto err; |
|
switch(intel_crtc->plane) { |
case PLANE_A: |
plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_A; |
break; |
case PLANE_B: |
plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_B; |
break; |
case PLANE_C: |
plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_C; |
break; |
default: |
WARN_ONCE(1, "unknown plane in flip command\n"); |
ret = -ENODEV; |
goto err_unpin; |
} |
|
ret = intel_ring_begin(ring, 4); |
if (ret) |
goto err_unpin; |
|
intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit); |
intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode)); |
intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); |
intel_ring_emit(ring, (MI_NOOP)); |
intel_ring_advance(ring); |
return 0; |
|
err_unpin: |
intel_unpin_fb_obj(obj); |
err: |
return ret; |
} |
|
static int intel_default_queue_flip(struct drm_device *dev, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_i915_gem_object *obj) |
{ |
return -ENODEV; |
} |
|
static int intel_crtc_page_flip(struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
struct drm_pending_vblank_event *event) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_framebuffer *intel_fb; |
struct drm_i915_gem_object *obj; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_unpin_work *work; |
unsigned long flags; |
int ret; |
|
/* Can't change pixel format via MI display flips. */ |
if (fb->pixel_format != crtc->fb->pixel_format) |
return -EINVAL; |
|
/* |
* TILEOFF/LINOFF registers can't be changed via MI display flips. |
* Note that pitch changes could also affect these register. |
*/ |
if (INTEL_INFO(dev)->gen > 3 && |
(fb->offsets[0] != crtc->fb->offsets[0] || |
fb->pitches[0] != crtc->fb->pitches[0])) |
return -EINVAL; |
|
work = kzalloc(sizeof *work, GFP_KERNEL); |
if (work == NULL) |
return -ENOMEM; |
|
work->event = event; |
work->dev = crtc->dev; |
intel_fb = to_intel_framebuffer(crtc->fb); |
work->old_fb_obj = intel_fb->obj; |
INIT_WORK(&work->work, intel_unpin_work_fn); |
|
ret = drm_vblank_get(dev, intel_crtc->pipe); |
if (ret) |
goto free_work; |
|
/* We borrow the event spin lock for protecting unpin_work */ |
spin_lock_irqsave(&dev->event_lock, flags); |
if (intel_crtc->unpin_work) { |
spin_unlock_irqrestore(&dev->event_lock, flags); |
kfree(work); |
drm_vblank_put(dev, intel_crtc->pipe); |
|
DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); |
return -EBUSY; |
} |
intel_crtc->unpin_work = work; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
|
intel_fb = to_intel_framebuffer(fb); |
obj = intel_fb->obj; |
|
ret = i915_mutex_lock_interruptible(dev); |
if (ret) |
goto cleanup; |
|
/* Reference the objects for the scheduled work. */ |
drm_gem_object_reference(&work->old_fb_obj->base); |
drm_gem_object_reference(&obj->base); |
|
crtc->fb = fb; |
|
work->pending_flip_obj = obj; |
|
static void intel_sanitize_modesetting(struct drm_device *dev, |
int pipe, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 reg, val; |
work->enable_stall_check = true; |
|
if (HAS_PCH_SPLIT(dev)) |
return; |
|
/* Who knows what state these registers were left in by the BIOS or |
* grub? |
* |
* If we leave the registers in a conflicting state (e.g. with the |
* display plane reading from the other pipe than the one we intend |
* to use) then when we attempt to teardown the active mode, we will |
* not disable the pipes and planes in the correct order -- leaving |
* a plane reading from a disabled pipe and possibly leading to |
* undefined behaviour. |
/* Block clients from rendering to the new back buffer until |
* the flip occurs and the object is no longer visible. |
*/ |
atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); |
|
reg = DSPCNTR(plane); |
val = I915_READ(reg); |
ret = dev_priv->display.queue_flip(dev, crtc, fb, obj); |
if (ret) |
goto cleanup_pending; |
|
if ((val & DISPLAY_PLANE_ENABLE) == 0) |
return; |
if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe) |
return; |
intel_disable_fbc(dev); |
intel_mark_fb_busy(obj); |
mutex_unlock(&dev->struct_mutex); |
|
/* This display plane is active and attached to the other CPU pipe. */ |
pipe = !pipe; |
trace_i915_flip_request(intel_crtc->plane, obj); |
|
/* Disable the plane and wait for it to stop reading from the pipe. */ |
intel_disable_plane(dev_priv, plane, pipe); |
intel_disable_pipe(dev_priv, pipe); |
} |
return 0; |
|
static void intel_crtc_reset(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
cleanup_pending: |
atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); |
drm_gem_object_unreference(&work->old_fb_obj->base); |
drm_gem_object_unreference(&obj->base); |
mutex_unlock(&dev->struct_mutex); |
|
/* Reset flags back to the 'unknown' status so that they |
* will be correctly set on the initial modeset. |
*/ |
intel_crtc->dpms_mode = -1; |
cleanup: |
spin_lock_irqsave(&dev->event_lock, flags); |
intel_crtc->unpin_work = NULL; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
|
/* We need to fix up any BIOS configuration that conflicts with |
* our expectations. |
*/ |
intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); |
drm_vblank_put(dev, intel_crtc->pipe); |
free_work: |
kfree(work); |
|
return ret; |
} |
|
#endif |
|
static struct drm_crtc_helper_funcs intel_helper_funcs = { |
.dpms = intel_crtc_dpms, |
.mode_fixup = intel_crtc_mode_fixup, |
.mode_set = intel_crtc_mode_set, |
.mode_set_base = intel_pipe_set_base, |
.mode_set_base_atomic = intel_pipe_set_base_atomic, |
.load_lut = intel_crtc_load_lut, |
.disable = intel_crtc_disable, |
.disable = intel_crtc_noop, |
}; |
|
static const struct drm_crtc_funcs intel_crtc_funcs = { |
.reset = intel_crtc_reset, |
// .cursor_set = intel_crtc_cursor_set, |
// .cursor_move = intel_crtc_cursor_move, |
.gamma_set = intel_crtc_gamma_set, |
.set_config = drm_crtc_helper_set_config, |
.destroy = intel_crtc_destroy, |
// .page_flip = intel_crtc_page_flip, |
}; |
|
static void intel_crtc_init(struct drm_device *dev, int pipe) |
bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc; |
int i; |
struct intel_encoder *other_encoder; |
struct drm_crtc *crtc = &encoder->new_crtc->base; |
|
intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); |
if (intel_crtc == NULL) |
return; |
if (WARN_ON(!crtc)) |
return false; |
|
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); |
list_for_each_entry(other_encoder, |
&crtc->dev->mode_config.encoder_list, |
base.head) { |
|
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); |
for (i = 0; i < 256; i++) { |
intel_crtc->lut_r[i] = i; |
intel_crtc->lut_g[i] = i; |
intel_crtc->lut_b[i] = i; |
if (&other_encoder->new_crtc->base != crtc || |
encoder == other_encoder) |
continue; |
else |
return true; |
} |
|
/* Swap pipes & planes for FBC on pre-965 */ |
intel_crtc->pipe = pipe; |
intel_crtc->plane = pipe; |
if (IS_MOBILE(dev) && IS_GEN3(dev)) { |
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n"); |
intel_crtc->plane = !pipe; |
return false; |
} |
|
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || |
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); |
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; |
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; |
static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, |
struct drm_crtc *crtc) |
{ |
struct drm_device *dev; |
struct drm_crtc *tmp; |
int crtc_mask = 1; |
|
intel_crtc_reset(&intel_crtc->base); |
intel_crtc->active = true; /* force the pipe off on setup_init_config */ |
intel_crtc->bpp = 24; /* default for pre-Ironlake */ |
WARN(!crtc, "checking null crtc?\n"); |
|
if (HAS_PCH_SPLIT(dev)) { |
if (pipe == 2 && IS_IVYBRIDGE(dev)) |
intel_crtc->no_pll = true; |
intel_helper_funcs.prepare = ironlake_crtc_prepare; |
intel_helper_funcs.commit = ironlake_crtc_commit; |
} else { |
intel_helper_funcs.prepare = i9xx_crtc_prepare; |
intel_helper_funcs.commit = i9xx_crtc_commit; |
dev = crtc->dev; |
|
list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
if (tmp == crtc) |
break; |
crtc_mask <<= 1; |
} |
|
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); |
if (encoder->possible_crtcs & crtc_mask) |
return true; |
return false; |
} |
|
intel_crtc->busy = false; |
/** |
* intel_modeset_update_staged_output_state |
* |
* Updates the staged output configuration state, e.g. after we've read out the |
* current hw state. |
*/ |
static void intel_modeset_update_staged_output_state(struct drm_device *dev) |
{ |
struct intel_encoder *encoder; |
struct intel_connector *connector; |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
connector->new_encoder = |
to_intel_encoder(connector->base.encoder); |
} |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
encoder->new_crtc = |
to_intel_crtc(encoder->base.crtc); |
} |
} |
|
|
|
|
|
|
static int intel_encoder_clones(struct drm_device *dev, int type_mask) |
/** |
* intel_modeset_commit_output_state |
* |
* This function copies the stage display pipe configuration to the real one. |
*/ |
static void intel_modeset_commit_output_state(struct drm_device *dev) |
{ |
struct intel_encoder *encoder; |
int index_mask = 0; |
int entry = 0; |
struct intel_connector *connector; |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { |
if (type_mask & encoder->clone_mask) |
index_mask |= (1 << entry); |
entry++; |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
connector->base.encoder = &connector->new_encoder->base; |
} |
|
return index_mask; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
encoder->base.crtc = &encoder->new_crtc->base; |
} |
} |
|
static bool has_edp_a(struct drm_device *dev) |
static struct drm_display_mode * |
intel_modeset_adjusted_mode(struct drm_crtc *crtc, |
struct drm_display_mode *mode) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_device *dev = crtc->dev; |
struct drm_display_mode *adjusted_mode; |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct intel_encoder *encoder; |
|
if (!IS_MOBILE(dev)) |
return false; |
adjusted_mode = drm_mode_duplicate(dev, mode); |
if (!adjusted_mode) |
return ERR_PTR(-ENOMEM); |
|
if ((I915_READ(DP_A) & DP_DETECTED) == 0) |
return false; |
/* Pass our mode to the connectors and the CRTC to give them a chance to |
* adjust it according to limitations or connector properties, and also |
* a chance to reject the mode entirely. |
*/ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
|
if (IS_GEN5(dev) && |
(I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE)) |
return false; |
if (&encoder->new_crtc->base != crtc) |
continue; |
encoder_funcs = encoder->base.helper_private; |
if (!(encoder_funcs->mode_fixup(&encoder->base, mode, |
adjusted_mode))) { |
DRM_DEBUG_KMS("Encoder fixup failed\n"); |
goto fail; |
} |
} |
|
return true; |
if (!(intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) { |
DRM_DEBUG_KMS("CRTC fixup failed\n"); |
goto fail; |
} |
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); |
|
static void intel_setup_outputs(struct drm_device *dev) |
return adjusted_mode; |
fail: |
drm_mode_destroy(dev, adjusted_mode); |
return ERR_PTR(-EINVAL); |
} |
|
/* Computes which crtcs are affected and sets the relevant bits in the mask. For |
* simplicity we use the crtc's pipe number (because it's easier to obtain). */ |
static void |
intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, |
unsigned *prepare_pipes, unsigned *disable_pipes) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc; |
struct drm_device *dev = crtc->dev; |
struct intel_encoder *encoder; |
bool dpd_is_edp = false; |
bool has_lvds = false; |
struct intel_connector *connector; |
struct drm_crtc *tmp_crtc; |
|
ENTER(); |
*disable_pipes = *modeset_pipes = *prepare_pipes = 0; |
|
if (IS_MOBILE(dev) && !IS_I830(dev)) |
has_lvds = intel_lvds_init(dev); |
if (!has_lvds && !HAS_PCH_SPLIT(dev)) { |
/* disable the panel fitter on everything but LVDS */ |
I915_WRITE(PFIT_CONTROL, 0); |
} |
/* Check which crtcs have changed outputs connected to them, these need |
* to be part of the prepare_pipes mask. We don't (yet) support global |
* modeset across multiple crtcs, so modeset_pipes will only have one |
* bit set at most. */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
if (connector->base.encoder == &connector->new_encoder->base) |
continue; |
|
if (HAS_PCH_SPLIT(dev)) { |
dpd_is_edp = intel_dpd_is_edp(dev); |
if (connector->base.encoder) { |
tmp_crtc = connector->base.encoder->crtc; |
|
if (has_edp_a(dev)) |
intel_dp_init(dev, DP_A); |
*prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe; |
} |
|
if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_D); |
if (connector->new_encoder) |
*prepare_pipes |= |
1 << connector->new_encoder->new_crtc->pipe; |
} |
|
intel_crt_init(dev); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
if (encoder->base.crtc == &encoder->new_crtc->base) |
continue; |
|
if (HAS_PCH_SPLIT(dev)) { |
int found; |
if (encoder->base.crtc) { |
tmp_crtc = encoder->base.crtc; |
|
if (I915_READ(HDMIB) & PORT_DETECTED) { |
/* PCH SDVOB multiplex with HDMIB */ |
found = intel_sdvo_init(dev, PCH_SDVOB); |
if (!found) |
intel_hdmi_init(dev, HDMIB); |
if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_B); |
*prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe; |
} |
|
if (I915_READ(HDMIC) & PORT_DETECTED) |
intel_hdmi_init(dev, HDMIC); |
if (encoder->new_crtc) |
*prepare_pipes |= 1 << encoder->new_crtc->pipe; |
} |
|
if (I915_READ(HDMID) & PORT_DETECTED) |
intel_hdmi_init(dev, HDMID); |
/* Check for any pipes that will be fully disabled ... */ |
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, |
base.head) { |
bool used = false; |
|
if (I915_READ(PCH_DP_C) & DP_DETECTED) |
intel_dp_init(dev, PCH_DP_C); |
/* Don't try to disable disabled crtcs. */ |
if (!intel_crtc->base.enabled) |
continue; |
|
if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_D); |
|
} else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) { |
bool found = false; |
|
if (I915_READ(SDVOB) & SDVO_DETECTED) { |
DRM_DEBUG_KMS("probing SDVOB\n"); |
found = intel_sdvo_init(dev, SDVOB); |
if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) { |
DRM_DEBUG_KMS("probing HDMI on SDVOB\n"); |
intel_hdmi_init(dev, SDVOB); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
if (encoder->new_crtc == intel_crtc) |
used = true; |
} |
|
if (!found && SUPPORTS_INTEGRATED_DP(dev)) { |
DRM_DEBUG_KMS("probing DP_B\n"); |
intel_dp_init(dev, DP_B); |
if (!used) |
*disable_pipes |= 1 << intel_crtc->pipe; |
} |
} |
|
/* Before G4X SDVOC doesn't have its own detect register */ |
|
if (I915_READ(SDVOB) & SDVO_DETECTED) { |
DRM_DEBUG_KMS("probing SDVOC\n"); |
found = intel_sdvo_init(dev, SDVOC); |
} |
/* set_mode is also used to update properties on life display pipes. */ |
intel_crtc = to_intel_crtc(crtc); |
if (crtc->enabled) |
*prepare_pipes |= 1 << intel_crtc->pipe; |
|
if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) { |
/* We only support modeset on one single crtc, hence we need to do that |
* only for the passed in crtc iff we change anything else than just |
* disable crtcs. |
* |
* This is actually not true, to be fully compatible with the old crtc |
* helper we automatically disable _any_ output (i.e. doesn't need to be |
* connected to the crtc we're modesetting on) if it's disconnected. |
* Which is a rather nutty api (since changed the output configuration |
* without userspace's explicit request can lead to confusion), but |
* alas. Hence we currently need to modeset on all pipes we prepare. */ |
if (*prepare_pipes) |
*modeset_pipes = *prepare_pipes; |
|
if (SUPPORTS_INTEGRATED_HDMI(dev)) { |
DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); |
intel_hdmi_init(dev, SDVOC); |
/* ... and mask these out. */ |
*modeset_pipes &= ~(*disable_pipes); |
*prepare_pipes &= ~(*disable_pipes); |
} |
if (SUPPORTS_INTEGRATED_DP(dev)) { |
DRM_DEBUG_KMS("probing DP_C\n"); |
intel_dp_init(dev, DP_C); |
} |
} |
|
if (SUPPORTS_INTEGRATED_DP(dev) && |
(I915_READ(DP_D) & DP_DETECTED)) { |
DRM_DEBUG_KMS("probing DP_D\n"); |
intel_dp_init(dev, DP_D); |
} |
} else if (IS_GEN2(dev)) |
intel_dvo_init(dev); |
static bool intel_crtc_in_use(struct drm_crtc *crtc) |
{ |
struct drm_encoder *encoder; |
struct drm_device *dev = crtc->dev; |
|
// if (SUPPORTS_TV(dev)) |
// intel_tv_init(dev); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
if (encoder->crtc == crtc) |
return true; |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { |
encoder->base.possible_crtcs = encoder->crtc_mask; |
encoder->base.possible_clones = |
intel_encoder_clones(dev, encoder->clone_mask); |
return false; |
} |
|
/* disable all the possible outputs/crtcs before entering KMS mode */ |
// drm_helper_disable_unused_functions(dev); |
static void |
intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) |
{ |
struct intel_encoder *intel_encoder; |
struct intel_crtc *intel_crtc; |
struct drm_connector *connector; |
|
if (HAS_PCH_SPLIT(dev)) |
ironlake_init_pch_refclk(dev); |
list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, |
base.head) { |
if (!intel_encoder->base.crtc) |
continue; |
|
LEAVE(); |
intel_crtc = to_intel_crtc(intel_encoder->base.crtc); |
|
if (prepare_pipes & (1 << intel_crtc->pipe)) |
intel_encoder->connectors_active = false; |
} |
|
intel_modeset_commit_output_state(dev); |
|
/* Update computed state. */ |
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, |
base.head) { |
intel_crtc->base.enabled = intel_crtc_in_use(&intel_crtc->base); |
} |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder || !connector->encoder->crtc) |
continue; |
|
intel_crtc = to_intel_crtc(connector->encoder->crtc); |
|
if (prepare_pipes & (1 << intel_crtc->pipe)) { |
struct drm_property *dpms_property = |
dev->mode_config.dpms_property; |
|
connector->dpms = DRM_MODE_DPMS_ON; |
drm_connector_property_set_value(connector, |
dpms_property, |
DRM_MODE_DPMS_ON); |
|
intel_encoder = to_intel_encoder(connector->encoder); |
intel_encoder->connectors_active = true; |
} |
} |
|
static const struct drm_framebuffer_funcs intel_fb_funcs = { |
// .destroy = intel_user_framebuffer_destroy, |
// .create_handle = intel_user_framebuffer_create_handle, |
}; |
} |
|
int intel_framebuffer_init(struct drm_device *dev, |
struct intel_framebuffer *intel_fb, |
struct drm_mode_fb_cmd2 *mode_cmd, |
struct drm_i915_gem_object *obj) |
#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \ |
list_for_each_entry((intel_crtc), \ |
&(dev)->mode_config.crtc_list, \ |
base.head) \ |
if (mask & (1 <<(intel_crtc)->pipe)) \ |
|
void |
intel_modeset_check_state(struct drm_device *dev) |
{ |
int ret; |
struct intel_crtc *crtc; |
struct intel_encoder *encoder; |
struct intel_connector *connector; |
|
if (obj->tiling_mode == I915_TILING_Y) |
return -EINVAL; |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
/* This also checks the encoder/connector hw state with the |
* ->get_hw_state callbacks. */ |
intel_connector_check_state(connector); |
|
if (mode_cmd->pitches[0] & 63) |
return -EINVAL; |
|
switch (mode_cmd->pixel_format) { |
case DRM_FORMAT_RGB332: |
case DRM_FORMAT_RGB565: |
case DRM_FORMAT_XRGB8888: |
case DRM_FORMAT_ARGB8888: |
case DRM_FORMAT_XRGB2101010: |
case DRM_FORMAT_ARGB2101010: |
/* RGB formats are common across chipsets */ |
break; |
case DRM_FORMAT_YUYV: |
case DRM_FORMAT_UYVY: |
case DRM_FORMAT_YVYU: |
case DRM_FORMAT_VYUY: |
break; |
default: |
DRM_ERROR("unsupported pixel format\n"); |
return -EINVAL; |
WARN(&connector->new_encoder->base != connector->base.encoder, |
"connector's staged encoder doesn't match current encoder\n"); |
} |
|
ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); |
if (ret) { |
DRM_ERROR("framebuffer init failed %d\n", ret); |
return ret; |
} |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
bool enabled = false; |
bool active = false; |
enum pipe pipe, tracked_pipe; |
|
drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); |
intel_fb->obj = obj; |
return 0; |
DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", |
encoder->base.base.id, |
drm_get_encoder_name(&encoder->base)); |
|
WARN(&encoder->new_crtc->base != encoder->base.crtc, |
"encoder's stage crtc doesn't match current crtc\n"); |
WARN(encoder->connectors_active && !encoder->base.crtc, |
"encoder's active_connectors set, but no crtc\n"); |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
if (connector->base.encoder != &encoder->base) |
continue; |
enabled = true; |
if (connector->base.dpms != DRM_MODE_DPMS_OFF) |
active = true; |
} |
WARN(!!encoder->base.crtc != enabled, |
"encoder's enabled state mismatch " |
"(expected %i, found %i)\n", |
!!encoder->base.crtc, enabled); |
WARN(active && !encoder->base.crtc, |
"active encoder with no crtc\n"); |
|
WARN(encoder->connectors_active != active, |
"encoder's computed active state doesn't match tracked active state " |
"(expected %i, found %i)\n", active, encoder->connectors_active); |
|
static const struct drm_mode_config_funcs intel_mode_funcs = { |
.fb_create = NULL /*intel_user_framebuffer_create*/, |
.output_poll_changed = NULL /*intel_fb_output_poll_changed*/, |
}; |
active = encoder->get_hw_state(encoder, &pipe); |
WARN(active != encoder->connectors_active, |
"encoder's hw state doesn't match sw tracking " |
"(expected %i, found %i)\n", |
encoder->connectors_active, active); |
|
if (!encoder->base.crtc) |
continue; |
|
tracked_pipe = to_intel_crtc(encoder->base.crtc)->pipe; |
WARN(active && pipe != tracked_pipe, |
"active encoder's pipe doesn't match" |
"(expected %i, found %i)\n", |
tracked_pipe, pipe); |
|
} |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, |
base.head) { |
bool enabled = false; |
bool active = false; |
|
DRM_DEBUG_KMS("[CRTC:%d]\n", |
crtc->base.base.id); |
|
WARN(crtc->active && !crtc->base.enabled, |
"active crtc, but not enabled in sw tracking\n"); |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
if (encoder->base.crtc != &crtc->base) |
continue; |
enabled = true; |
if (encoder->connectors_active) |
active = true; |
} |
WARN(active != crtc->active, |
"crtc's computed active state doesn't match tracked active state " |
"(expected %i, found %i)\n", active, crtc->active); |
WARN(enabled != crtc->base.enabled, |
"crtc's computed enabled state doesn't match tracked enabled state " |
"(expected %i, found %i)\n", enabled, crtc->base.enabled); |
|
assert_pipe(dev->dev_private, crtc->pipe, crtc->active); |
} |
} |
|
bool ironlake_set_drps(struct drm_device *dev, u8 val) |
bool intel_set_mode(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
int x, int y, struct drm_framebuffer *fb) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u16 rgvswctl; |
struct drm_device *dev = crtc->dev; |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct drm_encoder *encoder; |
struct intel_crtc *intel_crtc; |
unsigned disable_pipes, prepare_pipes, modeset_pipes; |
bool ret = true; |
|
rgvswctl = I915_READ16(MEMSWCTL); |
if (rgvswctl & MEMCTL_CMD_STS) { |
DRM_DEBUG("gpu busy, RCS change rejected\n"); |
return false; /* still busy with another command */ |
} |
intel_modeset_affected_pipes(crtc, &modeset_pipes, |
&prepare_pipes, &disable_pipes); |
|
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | |
(val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; |
I915_WRITE16(MEMSWCTL, rgvswctl); |
POSTING_READ16(MEMSWCTL); |
DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", |
modeset_pipes, prepare_pipes, disable_pipes); |
|
rgvswctl |= MEMCTL_CMD_STS; |
I915_WRITE16(MEMSWCTL, rgvswctl); |
for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) |
intel_crtc_disable(&intel_crtc->base); |
|
return true; |
saved_hwmode = crtc->hwmode; |
saved_mode = crtc->mode; |
|
/* Hack: Because we don't (yet) support global modeset on multiple |
* crtcs, we don't keep track of the new mode for more than one crtc. |
* Hence simply check whether any bit is set in modeset_pipes in all the |
* pieces of code that are not yet converted to deal with mutliple crtcs |
* changing their mode at the same time. */ |
adjusted_mode = NULL; |
if (modeset_pipes) { |
adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); |
if (IS_ERR(adjusted_mode)) { |
return false; |
} |
} |
|
void ironlake_enable_drps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 rgvmodectl = I915_READ(MEMMODECTL); |
u8 fmax, fmin, fstart, vstart; |
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) { |
if (intel_crtc->base.enabled) |
dev_priv->display.crtc_disable(&intel_crtc->base); |
} |
|
/* Enable temp reporting */ |
I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN); |
I915_WRITE16(TSC1, I915_READ(TSC1) | TSE); |
/* crtc->mode is already used by the ->mode_set callbacks, hence we need |
* to set it here already despite that we pass it down the callchain. |
*/ |
if (modeset_pipes) |
crtc->mode = *mode; |
|
/* 100ms RC evaluation intervals */ |
I915_WRITE(RCUPEI, 100000); |
I915_WRITE(RCDNEI, 100000); |
/* Only after disabling all output pipelines that will be changed can we |
* update the the output configuration. */ |
intel_modeset_update_state(dev, prepare_pipes); |
|
/* Set max/min thresholds to 90ms and 80ms respectively */ |
I915_WRITE(RCBMAXAVG, 90000); |
I915_WRITE(RCBMINAVG, 80000); |
/* Set up the DPLL and any encoders state that needs to adjust or depend |
* on the DPLL. |
*/ |
for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { |
ret = !intel_crtc_mode_set(&intel_crtc->base, |
mode, adjusted_mode, |
x, y, fb); |
if (!ret) |
goto done; |
|
I915_WRITE(MEMIHYST, 1); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
|
/* Set up min, max, and cur for interrupt handling */ |
fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT; |
fmin = (rgvmodectl & MEMMODE_FMIN_MASK); |
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> |
MEMMODE_FSTART_SHIFT; |
if (encoder->crtc != &intel_crtc->base) |
continue; |
|
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> |
PXVFREQ_PX_SHIFT; |
DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", |
encoder->base.id, drm_get_encoder_name(encoder), |
mode->base.id, mode->name); |
encoder_funcs = encoder->helper_private; |
encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
} |
} |
|
dev_priv->fmax = fmax; /* IPS callback will increase this */ |
dev_priv->fstart = fstart; |
/* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) |
dev_priv->display.crtc_enable(&intel_crtc->base); |
|
dev_priv->max_delay = fstart; |
dev_priv->min_delay = fmin; |
dev_priv->cur_delay = fstart; |
if (modeset_pipes) { |
/* Store real post-adjustment hardware mode. */ |
crtc->hwmode = *adjusted_mode; |
|
DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", |
fmax, fmin, fstart); |
|
I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN); |
|
/* |
* Interrupts will be enabled in ironlake_irq_postinstall |
/* Calculate and store various constants which |
* are later needed by vblank and swap-completion |
* timestamping. They are derived from true hwmode. |
*/ |
drm_calc_timestamping_constants(crtc); |
} |
|
I915_WRITE(VIDSTART, vstart); |
POSTING_READ(VIDSTART); |
/* FIXME: add subpixel order */ |
done: |
drm_mode_destroy(dev, adjusted_mode); |
if (!ret && crtc->enabled) { |
crtc->hwmode = saved_hwmode; |
crtc->mode = saved_mode; |
} else { |
intel_modeset_check_state(dev); |
} |
|
rgvmodectl |= MEMMODE_SWMODE_EN; |
I915_WRITE(MEMMODECTL, rgvmodectl); |
return ret; |
} |
|
if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10)) |
DRM_ERROR("stuck trying to change perf mode\n"); |
msleep(1); |
#undef for_each_intel_crtc_masked |
|
ironlake_set_drps(dev, fstart); |
static void intel_set_config_free(struct intel_set_config *config) |
{ |
if (!config) |
return; |
|
dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) + |
I915_READ(0x112e0); |
// dev_priv->last_time1 = jiffies_to_msecs(jiffies); |
dev_priv->last_count2 = I915_READ(0x112f4); |
// getrawmonotonic(&dev_priv->last_time2); |
kfree(config->save_connector_encoders); |
kfree(config->save_encoder_crtcs); |
kfree(config); |
} |
|
static int intel_set_config_save_state(struct drm_device *dev, |
struct intel_set_config *config) |
{ |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
int count; |
|
config->save_encoder_crtcs = |
kcalloc(dev->mode_config.num_encoder, |
sizeof(struct drm_crtc *), GFP_KERNEL); |
if (!config->save_encoder_crtcs) |
return -ENOMEM; |
|
config->save_connector_encoders = |
kcalloc(dev->mode_config.num_connector, |
sizeof(struct drm_encoder *), GFP_KERNEL); |
if (!config->save_connector_encoders) |
return -ENOMEM; |
|
/* Copy data. Note that driver private data is not affected. |
* Should anything bad happen only the expected state is |
* restored, not the drivers personal bookkeeping. |
*/ |
count = 0; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
config->save_encoder_crtcs[count++] = encoder->crtc; |
} |
|
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
config->save_connector_encoders[count++] = connector->encoder; |
} |
|
return 0; |
} |
|
static void intel_set_config_restore_state(struct drm_device *dev, |
struct intel_set_config *config) |
{ |
struct intel_encoder *encoder; |
struct intel_connector *connector; |
int count; |
|
count = 0; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { |
encoder->new_crtc = |
to_intel_crtc(config->save_encoder_crtcs[count++]); |
} |
|
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { |
connector->new_encoder = |
to_intel_encoder(config->save_connector_encoders[count++]); |
} |
} |
|
|
|
static unsigned long intel_pxfreq(u32 vidfreq) |
static void |
intel_set_config_compute_mode_changes(struct drm_mode_set *set, |
struct intel_set_config *config) |
{ |
unsigned long freq; |
int div = (vidfreq & 0x3f0000) >> 16; |
int post = (vidfreq & 0x3000) >> 12; |
int pre = (vidfreq & 0x7); |
|
if (!pre) |
return 0; |
/* We should be able to check here if the fb has the same properties |
* and then just flip_or_move it */ |
if (set->crtc->fb != set->fb) { |
/* If we have no fb then treat it as a full mode set */ |
if (set->crtc->fb == NULL) { |
DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
config->mode_changed = true; |
} else if (set->fb == NULL) { |
config->mode_changed = true; |
} else if (set->fb->depth != set->crtc->fb->depth) { |
config->mode_changed = true; |
} else if (set->fb->bits_per_pixel != |
set->crtc->fb->bits_per_pixel) { |
config->mode_changed = true; |
} else |
config->fb_changed = true; |
} |
|
freq = ((div * 133333) / ((1<<post) * pre)); |
if (set->fb && (set->x != set->crtc->x || set->y != set->crtc->y)) |
config->fb_changed = true; |
|
return freq; |
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
DRM_DEBUG_KMS("modes are different, full mode set\n"); |
drm_mode_debug_printmodeline(&set->crtc->mode); |
drm_mode_debug_printmodeline(set->mode); |
config->mode_changed = true; |
} |
} |
|
void intel_init_emon(struct drm_device *dev) |
static int |
intel_modeset_stage_output_state(struct drm_device *dev, |
struct drm_mode_set *set, |
struct intel_set_config *config) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 lcfuse; |
u8 pxw[16]; |
int i; |
struct drm_crtc *new_crtc; |
struct intel_connector *connector; |
struct intel_encoder *encoder; |
int count, ro; |
|
/* Disable to program */ |
I915_WRITE(ECR, 0); |
POSTING_READ(ECR); |
/* The upper layers ensure that we either disabl a crtc or have a list |
* of connectors. For paranoia, double-check this. */ |
WARN_ON(!set->fb && (set->num_connectors != 0)); |
WARN_ON(set->fb && (set->num_connectors == 0)); |
|
/* Program energy weights for various events */ |
I915_WRITE(SDEW, 0x15040d00); |
I915_WRITE(CSIEW0, 0x007f0000); |
I915_WRITE(CSIEW1, 0x1e220004); |
I915_WRITE(CSIEW2, 0x04000004); |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
/* Otherwise traverse passed in connector list and get encoders |
* for them. */ |
for (ro = 0; ro < set->num_connectors; ro++) { |
if (set->connectors[ro] == &connector->base) { |
connector->new_encoder = connector->encoder; |
break; |
} |
} |
|
for (i = 0; i < 5; i++) |
I915_WRITE(PEW + (i * 4), 0); |
for (i = 0; i < 3; i++) |
I915_WRITE(DEW + (i * 4), 0); |
/* If we disable the crtc, disable all its connectors. Also, if |
* the connector is on the changing crtc but not on the new |
* connector list, disable it. */ |
if ((!set->fb || ro == set->num_connectors) && |
connector->base.encoder && |
connector->base.encoder->crtc == set->crtc) { |
connector->new_encoder = NULL; |
|
/* Program P-state weights to account for frequency power adjustment */ |
for (i = 0; i < 16; i++) { |
u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4)); |
unsigned long freq = intel_pxfreq(pxvidfreq); |
unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >> |
PXVFREQ_PX_SHIFT; |
unsigned long val; |
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", |
connector->base.base.id, |
drm_get_connector_name(&connector->base)); |
} |
|
val = vid * vid; |
val *= (freq / 1000); |
val *= 255; |
val /= (127*127*900); |
if (val > 0xff) |
DRM_ERROR("bad pxval: %ld\n", val); |
pxw[i] = val; |
|
if (&connector->new_encoder->base != connector->base.encoder) { |
DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
config->mode_changed = true; |
} |
/* Render standby states get 0 weight */ |
pxw[14] = 0; |
pxw[15] = 0; |
|
for (i = 0; i < 4; i++) { |
u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) | |
(pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]); |
I915_WRITE(PXW + (i * 4), val); |
/* Disable all disconnected encoders. */ |
if (connector->base.status == connector_status_disconnected) |
connector->new_encoder = NULL; |
} |
/* connector->new_encoder is now updated for all connectors. */ |
|
/* Adjust magic regs to magic values (more experimental results) */ |
I915_WRITE(OGW0, 0); |
I915_WRITE(OGW1, 0); |
I915_WRITE(EG0, 0x00007f00); |
I915_WRITE(EG1, 0x0000000e); |
I915_WRITE(EG2, 0x000e0000); |
I915_WRITE(EG3, 0x68000300); |
I915_WRITE(EG4, 0x42000000); |
I915_WRITE(EG5, 0x00140031); |
I915_WRITE(EG6, 0); |
I915_WRITE(EG7, 0); |
/* Update crtc of enabled connectors. */ |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
if (!connector->new_encoder) |
continue; |
|
for (i = 0; i < 8; i++) |
I915_WRITE(PXWL + (i * 4), 0); |
new_crtc = connector->new_encoder->base.crtc; |
|
/* Enable PMON + select events */ |
I915_WRITE(ECR, 0x80000019); |
for (ro = 0; ro < set->num_connectors; ro++) { |
if (set->connectors[ro] == &connector->base) |
new_crtc = set->crtc; |
} |
|
lcfuse = I915_READ(LCFUSE02); |
/* Make sure the new CRTC will work with the encoder */ |
if (!intel_encoder_crtc_ok(&connector->new_encoder->base, |
new_crtc)) { |
return -EINVAL; |
} |
connector->encoder->new_crtc = to_intel_crtc(new_crtc); |
|
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK); |
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", |
connector->base.base.id, |
drm_get_connector_name(&connector->base), |
new_crtc->base.id); |
} |
|
static bool intel_enable_rc6(struct drm_device *dev) |
{ |
/* |
* Respect the kernel parameter if it is set |
*/ |
if (i915_enable_rc6 >= 0) |
return i915_enable_rc6; |
/* Check for any encoders that needs to be disabled. */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
base.head) { |
if (connector->new_encoder == encoder) { |
WARN_ON(!connector->new_encoder->new_crtc); |
|
/* |
* Disable RC6 on Ironlake |
*/ |
if (INTEL_INFO(dev)->gen == 5) |
return 0; |
goto next_encoder; |
} |
} |
encoder->new_crtc = NULL; |
next_encoder: |
/* Only now check for crtc changes so we don't miss encoders |
* that will be disabled. */ |
if (&encoder->new_crtc->base != encoder->base.crtc) { |
DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
config->mode_changed = true; |
} |
} |
/* Now we've also updated encoder->new_crtc for all encoders. */ |
|
/* |
* Disable rc6 on Sandybridge |
*/ |
if (INTEL_INFO(dev)->gen == 6) { |
DRM_DEBUG_DRIVER("Sandybridge: RC6 disabled\n"); |
return 0; |
} |
DRM_DEBUG_DRIVER("RC6 enabled\n"); |
return 1; |
} |
|
void gen6_enable_rps(struct drm_i915_private *dev_priv) |
static int intel_crtc_set_config(struct drm_mode_set *set) |
{ |
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); |
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); |
u32 pcu_mbox, rc6_mask = 0; |
int cur_freq, min_freq, max_freq; |
int i; |
struct drm_device *dev; |
struct drm_mode_set save_set; |
struct intel_set_config *config; |
int ret; |
|
/* Here begins a magic sequence of register writes to enable |
* auto-downclocking. |
* |
* Perhaps there might be some value in exposing these to |
* userspace... |
*/ |
I915_WRITE(GEN6_RC_STATE, 0); |
mutex_lock(&dev_priv->dev->struct_mutex); |
gen6_gt_force_wake_get(dev_priv); |
BUG_ON(!set); |
BUG_ON(!set->crtc); |
BUG_ON(!set->crtc->helper_private); |
|
/* disable the counters and set deterministic thresholds */ |
I915_WRITE(GEN6_RC_CONTROL, 0); |
if (!set->mode) |
set->fb = NULL; |
|
I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16); |
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30); |
I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30); |
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); |
I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); |
/* The fb helper likes to play gross jokes with ->mode_set_config. |
* Unfortunately the crtc helper doesn't do much at all for this case, |
* so we have to cope with this madness until the fb helper is fixed up. */ |
if (set->fb && set->num_connectors == 0) |
return 0; |
|
for (i = 0; i < I915_NUM_RINGS; i++) |
I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10); |
if (set->fb) { |
DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", |
set->crtc->base.id, set->fb->base.id, |
(int)set->num_connectors, set->x, set->y); |
} else { |
DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); |
} |
|
I915_WRITE(GEN6_RC_SLEEP, 0); |
I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); |
I915_WRITE(GEN6_RC6_THRESHOLD, 50000); |
I915_WRITE(GEN6_RC6p_THRESHOLD, 100000); |
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ |
dev = set->crtc->dev; |
|
if (intel_enable_rc6(dev_priv->dev)) |
rc6_mask = GEN6_RC_CTL_RC6p_ENABLE | |
GEN6_RC_CTL_RC6_ENABLE; |
ret = -ENOMEM; |
config = kzalloc(sizeof(*config), GFP_KERNEL); |
if (!config) |
goto out_config; |
|
I915_WRITE(GEN6_RC_CONTROL, |
rc6_mask | |
GEN6_RC_CTL_EI_MODE(1) | |
GEN6_RC_CTL_HW_ENABLE); |
ret = intel_set_config_save_state(dev, config); |
if (ret) |
goto out_config; |
|
I915_WRITE(GEN6_RPNSWREQ, |
GEN6_FREQUENCY(10) | |
GEN6_OFFSET(0) | |
GEN6_AGGRESSIVE_TURBO); |
I915_WRITE(GEN6_RC_VIDEO_FREQ, |
GEN6_FREQUENCY(12)); |
save_set.crtc = set->crtc; |
save_set.mode = &set->crtc->mode; |
save_set.x = set->crtc->x; |
save_set.y = set->crtc->y; |
save_set.fb = set->crtc->fb; |
|
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000); |
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, |
18 << 24 | |
6 << 16); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000); |
I915_WRITE(GEN6_RP_UP_EI, 100000); |
I915_WRITE(GEN6_RP_DOWN_EI, 5000000); |
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); |
I915_WRITE(GEN6_RP_CONTROL, |
GEN6_RP_MEDIA_TURBO | |
GEN6_RP_MEDIA_HW_MODE | |
GEN6_RP_MEDIA_IS_GFX | |
GEN6_RP_ENABLE | |
GEN6_RP_UP_BUSY_AVG | |
GEN6_RP_DOWN_IDLE_CONT); |
/* Compute whether we need a full modeset, only an fb base update or no |
* change at all. In the future we might also check whether only the |
* mode changed, e.g. for LVDS where we only change the panel fitter in |
* such cases. */ |
intel_set_config_compute_mode_changes(set, config); |
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, |
500)) |
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n"); |
ret = intel_modeset_stage_output_state(dev, set, config); |
if (ret) |
goto fail; |
|
I915_WRITE(GEN6_PCODE_DATA, 0); |
I915_WRITE(GEN6_PCODE_MAILBOX, |
GEN6_PCODE_READY | |
GEN6_PCODE_WRITE_MIN_FREQ_TABLE); |
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, |
500)) |
DRM_ERROR("timeout waiting for pcode mailbox to finish\n"); |
if (config->mode_changed) { |
if (set->mode) { |
DRM_DEBUG_KMS("attempting to set mode from" |
" userspace\n"); |
drm_mode_debug_printmodeline(set->mode); |
} |
|
min_freq = (rp_state_cap & 0xff0000) >> 16; |
max_freq = rp_state_cap & 0xff; |
cur_freq = (gt_perf_status & 0xff00) >> 8; |
|
/* Check for overclock support */ |
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, |
500)) |
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n"); |
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS); |
pcu_mbox = I915_READ(GEN6_PCODE_DATA); |
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0, |
500)) |
DRM_ERROR("timeout waiting for pcode mailbox to finish\n"); |
if (pcu_mbox & (1<<31)) { /* OC supported */ |
max_freq = pcu_mbox & 0xff; |
DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50); |
if (!intel_set_mode(set->crtc, set->mode, |
set->x, set->y, set->fb)) { |
DRM_ERROR("failed to set mode on [CRTC:%d]\n", |
set->crtc->base.id); |
ret = -EINVAL; |
goto fail; |
} |
} else if (config->fb_changed) { |
ret = intel_pipe_set_base(set->crtc, |
set->x, set->y, set->fb); |
} |
|
/* In units of 100MHz */ |
dev_priv->max_delay = max_freq; |
dev_priv->min_delay = min_freq; |
dev_priv->cur_delay = cur_freq; |
intel_set_config_free(config); |
|
/* requires MSI enabled */ |
I915_WRITE(GEN6_PMIER, |
GEN6_PM_MBOX_EVENT | |
GEN6_PM_THERMAL_EVENT | |
GEN6_PM_RP_DOWN_TIMEOUT | |
GEN6_PM_RP_UP_THRESHOLD | |
GEN6_PM_RP_DOWN_THRESHOLD | |
GEN6_PM_RP_UP_EI_EXPIRED | |
GEN6_PM_RP_DOWN_EI_EXPIRED); |
// spin_lock_irq(&dev_priv->rps_lock); |
// WARN_ON(dev_priv->pm_iir != 0); |
I915_WRITE(GEN6_PMIMR, 0); |
// spin_unlock_irq(&dev_priv->rps_lock); |
/* enable all PM interrupts */ |
I915_WRITE(GEN6_PMINTRMSK, 0); |
return 0; |
|
gen6_gt_force_wake_put(dev_priv); |
mutex_unlock(&dev_priv->dev->struct_mutex); |
fail: |
intel_set_config_restore_state(dev, config); |
|
/* Try to restore the config */ |
if (config->mode_changed && |
!intel_set_mode(save_set.crtc, save_set.mode, |
save_set.x, save_set.y, save_set.fb)) |
DRM_ERROR("failed to restore config after modeset failure\n"); |
|
out_config: |
intel_set_config_free(config); |
return ret; |
} |
|
void gen6_update_ring_freq(struct drm_i915_private *dev_priv) |
static const struct drm_crtc_funcs intel_crtc_funcs = { |
// .cursor_set = intel_crtc_cursor_set, |
// .cursor_move = intel_crtc_cursor_move, |
.gamma_set = intel_crtc_gamma_set, |
.set_config = intel_crtc_set_config, |
.destroy = intel_crtc_destroy, |
// .page_flip = intel_crtc_page_flip, |
}; |
|
static void intel_pch_pll_init(struct drm_device *dev) |
{ |
int min_freq = 15; |
int gpu_freq, ia_freq, max_ia_freq; |
int scaling_factor = 180; |
drm_i915_private_t *dev_priv = dev->dev_private; |
int i; |
|
// max_ia_freq = cpufreq_quick_get_max(0); |
/* |
* Default to measured freq if none found, PCU will ensure we don't go |
* over |
*/ |
// if (!max_ia_freq) |
max_ia_freq = 3000000; //tsc_khz; |
if (dev_priv->num_pch_pll == 0) { |
DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); |
return; |
} |
|
/* Convert from kHz to MHz */ |
max_ia_freq /= 1000; |
for (i = 0; i < dev_priv->num_pch_pll; i++) { |
dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i); |
dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i); |
dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i); |
} |
} |
|
mutex_lock(&dev_priv->dev->struct_mutex); |
static void intel_crtc_init(struct drm_device *dev, int pipe) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc; |
int i; |
|
/* |
* For each potential GPU frequency, load a ring frequency we'd like |
* to use for memory access. We do this by specifying the IA frequency |
* the PCU should use as a reference to determine the ring frequency. |
*/ |
for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay; |
gpu_freq--) { |
int diff = dev_priv->max_delay - gpu_freq; |
intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); |
if (intel_crtc == NULL) |
return; |
|
/* |
* For GPU frequencies less than 750MHz, just use the lowest |
* ring freq. |
*/ |
if (gpu_freq < min_freq) |
ia_freq = 800; |
else |
ia_freq = max_ia_freq - ((diff * scaling_factor) / 2); |
ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100); |
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); |
|
I915_WRITE(GEN6_PCODE_DATA, |
(ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) | |
gpu_freq); |
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | |
GEN6_PCODE_WRITE_MIN_FREQ_TABLE); |
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & |
GEN6_PCODE_READY) == 0, 10)) { |
DRM_ERROR("pcode write of freq table timed out\n"); |
continue; |
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); |
for (i = 0; i < 256; i++) { |
intel_crtc->lut_r[i] = i; |
intel_crtc->lut_g[i] = i; |
intel_crtc->lut_b[i] = i; |
} |
} |
|
mutex_unlock(&dev_priv->dev->struct_mutex); |
/* Swap pipes & planes for FBC on pre-965 */ |
intel_crtc->pipe = pipe; |
intel_crtc->plane = pipe; |
if (IS_MOBILE(dev) && IS_GEN3(dev)) { |
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n"); |
intel_crtc->plane = !pipe; |
} |
|
static void ironlake_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE; |
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || |
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); |
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; |
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; |
|
/* Required for FBC */ |
dspclk_gate |= DPFCUNIT_CLOCK_GATE_DISABLE | |
DPFCRUNIT_CLOCK_GATE_DISABLE | |
DPFDUNIT_CLOCK_GATE_DISABLE; |
/* Required for CxSR */ |
dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE; |
intel_crtc->bpp = 24; /* default for pre-Ironlake */ |
|
I915_WRITE(PCH_3DCGDIS0, |
MARIUNIT_CLOCK_GATE_DISABLE | |
SVSMUNIT_CLOCK_GATE_DISABLE); |
I915_WRITE(PCH_3DCGDIS1, |
VFMUNIT_CLOCK_GATE_DISABLE); |
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); |
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate); |
dbgprintf("CRTC %d mode %x FB %x enable %d\n", |
intel_crtc->base.base.id, intel_crtc->base.mode, |
intel_crtc->base.fb, intel_crtc->base.enabled); |
|
/* |
* According to the spec the following bits should be set in |
* order to enable memory self-refresh |
* The bit 22/21 of 0x42004 |
* The bit 5 of 0x42020 |
* The bit 15 of 0x45000 |
*/ |
I915_WRITE(ILK_DISPLAY_CHICKEN2, |
(I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_DPARB_GATE | ILK_VSDPFD_FULL)); |
I915_WRITE(ILK_DSPCLK_GATE, |
(I915_READ(ILK_DSPCLK_GATE) | |
ILK_DPARB_CLK_GATE)); |
I915_WRITE(DISP_ARB_CTL, |
(I915_READ(DISP_ARB_CTL) | |
DISP_FBC_WM_DIS)); |
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
} |
|
/* |
* Based on the document from hardware guys the following bits |
* should be set unconditionally in order to enable FBC. |
* The bit 22 of 0x42000 |
* The bit 22 of 0x42004 |
* The bit 7,8,9 of 0x42020. |
*/ |
if (IS_IRONLAKE_M(dev)) { |
I915_WRITE(ILK_DISPLAY_CHICKEN1, |
I915_READ(ILK_DISPLAY_CHICKEN1) | |
ILK_FBCQ_DIS); |
I915_WRITE(ILK_DISPLAY_CHICKEN2, |
I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_DPARB_GATE); |
I915_WRITE(ILK_DSPCLK_GATE, |
I915_READ(ILK_DSPCLK_GATE) | |
ILK_DPFC_DIS1 | |
ILK_DPFC_DIS2 | |
ILK_CLK_FBC); |
int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, |
struct drm_file *file) |
{ |
struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data; |
struct drm_mode_object *drmmode_obj; |
struct intel_crtc *crtc; |
|
drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
|
if (!drmmode_obj) { |
DRM_ERROR("no such CRTC id\n"); |
return -EINVAL; |
} |
|
I915_WRITE(ILK_DISPLAY_CHICKEN2, |
I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_ELPIN_409_SELECT); |
I915_WRITE(_3D_CHICKEN2, |
_3D_CHICKEN2_WM_READ_PIPELINED << 16 | |
_3D_CHICKEN2_WM_READ_PIPELINED); |
crtc = to_intel_crtc(obj_to_crtc(drmmode_obj)); |
pipe_from_crtc_id->pipe = crtc->pipe; |
|
return 0; |
} |
|
static void gen6_init_clock_gating(struct drm_device *dev) |
static int intel_encoder_clones(struct intel_encoder *encoder) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE; |
struct drm_device *dev = encoder->base.dev; |
struct intel_encoder *source_encoder; |
int index_mask = 0; |
int entry = 0; |
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate); |
list_for_each_entry(source_encoder, |
&dev->mode_config.encoder_list, base.head) { |
|
I915_WRITE(ILK_DISPLAY_CHICKEN2, |
I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_ELPIN_409_SELECT); |
if (encoder == source_encoder) |
index_mask |= (1 << entry); |
|
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
/* Intel hw has only one MUX where enocoders could be cloned. */ |
if (encoder->cloneable && source_encoder->cloneable) |
index_mask |= (1 << entry); |
|
/* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock |
* gating disable must be set. Failure to set it results in |
* flickering pixels due to Z write ordering failures after |
* some amount of runtime in the Mesa "fire" demo, and Unigine |
* Sanctuary and Tropics, and apparently anything else with |
* alpha test or pixel discard. |
* |
* According to the spec, bit 11 (RCCUNIT) must also be set, |
* but we didn't debug actual testcases to find it out. |
*/ |
I915_WRITE(GEN6_UCGCTL2, |
GEN6_RCPBUNIT_CLOCK_GATE_DISABLE | |
GEN6_RCCUNIT_CLOCK_GATE_DISABLE); |
entry++; |
} |
|
/* |
* According to the spec the following bits should be |
* set in order to enable memory self-refresh and fbc: |
* The bit21 and bit22 of 0x42000 |
* The bit21 and bit22 of 0x42004 |
* The bit5 and bit7 of 0x42020 |
* The bit14 of 0x70180 |
* The bit14 of 0x71180 |
*/ |
I915_WRITE(ILK_DISPLAY_CHICKEN1, |
I915_READ(ILK_DISPLAY_CHICKEN1) | |
ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS); |
I915_WRITE(ILK_DISPLAY_CHICKEN2, |
I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_DPARB_GATE | ILK_VSDPFD_FULL); |
I915_WRITE(ILK_DSPCLK_GATE, |
I915_READ(ILK_DSPCLK_GATE) | |
ILK_DPARB_CLK_GATE | |
ILK_DPFD_CLK_GATE); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
return index_mask; |
} |
} |
|
static void ivybridge_init_clock_gating(struct drm_device *dev) |
static bool has_edp_a(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE; |
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate); |
if (!IS_MOBILE(dev)) |
return false; |
|
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
if ((I915_READ(DP_A) & DP_DETECTED) == 0) |
return false; |
|
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE); |
if (IS_GEN5(dev) && |
(I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE)) |
return false; |
|
I915_WRITE(IVB_CHICKEN3, |
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | |
CHICKEN3_DGMG_DONE_FIX_DISABLE); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
return true; |
} |
} |
|
static void g4x_init_clock_gating(struct drm_device *dev) |
static void intel_setup_outputs(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dspclk_gate; |
struct intel_encoder *encoder; |
bool dpd_is_edp = false; |
bool has_lvds; |
|
I915_WRITE(RENCLK_GATE_D1, 0); |
I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE | |
GS_UNIT_CLOCK_GATE_DISABLE | |
CL_UNIT_CLOCK_GATE_DISABLE); |
I915_WRITE(RAMCLK_GATE_D, 0); |
dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE | |
OVRUNIT_CLOCK_GATE_DISABLE | |
OVCUNIT_CLOCK_GATE_DISABLE; |
if (IS_GM45(dev)) |
dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE; |
I915_WRITE(DSPCLK_GATE_D, dspclk_gate); |
has_lvds = intel_lvds_init(dev); |
if (!has_lvds && !HAS_PCH_SPLIT(dev)) { |
/* disable the panel fitter on everything but LVDS */ |
I915_WRITE(PFIT_CONTROL, 0); |
} |
|
static void crestline_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
if (HAS_PCH_SPLIT(dev)) { |
dpd_is_edp = intel_dpd_is_edp(dev); |
|
I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE); |
I915_WRITE(RENCLK_GATE_D2, 0); |
I915_WRITE(DSPCLK_GATE_D, 0); |
I915_WRITE(RAMCLK_GATE_D, 0); |
I915_WRITE16(DEUC, 0); |
if (has_edp_a(dev)) |
intel_dp_init(dev, DP_A, PORT_A); |
|
if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_D, PORT_D); |
} |
|
static void broadwater_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
intel_crt_init(dev); |
|
I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE | |
I965_RCC_CLOCK_GATE_DISABLE | |
I965_RCPB_CLOCK_GATE_DISABLE | |
I965_ISC_CLOCK_GATE_DISABLE | |
I965_FBC_CLOCK_GATE_DISABLE); |
I915_WRITE(RENCLK_GATE_D2, 0); |
} |
if (IS_HASWELL(dev)) { |
int found; |
|
static void gen3_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 dstate = I915_READ(D_STATE); |
/* Haswell uses DDI functions to detect digital outputs */ |
found = I915_READ(DDI_BUF_CTL_A) & DDI_INIT_DISPLAY_DETECTED; |
/* DDI A only supports eDP */ |
if (found) |
intel_ddi_init(dev, PORT_A); |
|
dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING | |
DSTATE_DOT_CLOCK_GATING; |
I915_WRITE(D_STATE, dstate); |
} |
/* DDI B, C and D detection is indicated by the SFUSE_STRAP |
* register */ |
found = I915_READ(SFUSE_STRAP); |
|
static void i85x_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
if (found & SFUSE_STRAP_DDIB_DETECTED) |
intel_ddi_init(dev, PORT_B); |
if (found & SFUSE_STRAP_DDIC_DETECTED) |
intel_ddi_init(dev, PORT_C); |
if (found & SFUSE_STRAP_DDID_DETECTED) |
intel_ddi_init(dev, PORT_D); |
} else if (HAS_PCH_SPLIT(dev)) { |
int found; |
|
I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE); |
if (I915_READ(HDMIB) & PORT_DETECTED) { |
/* PCH SDVOB multiplex with HDMIB */ |
found = intel_sdvo_init(dev, PCH_SDVOB, true); |
if (!found) |
intel_hdmi_init(dev, HDMIB, PORT_B); |
if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_B, PORT_B); |
} |
|
static void i830_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
if (I915_READ(HDMIC) & PORT_DETECTED) |
intel_hdmi_init(dev, HDMIC, PORT_C); |
|
I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); |
} |
if (!dpd_is_edp && I915_READ(HDMID) & PORT_DETECTED) |
intel_hdmi_init(dev, HDMID, PORT_D); |
|
static void ibx_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
if (I915_READ(PCH_DP_C) & DP_DETECTED) |
intel_dp_init(dev, PCH_DP_C, PORT_C); |
|
/* |
* On Ibex Peak and Cougar Point, we need to disable clock |
* gating for the panel power sequencer or it will fail to |
* start up when no ports are active. |
*/ |
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); |
if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) |
intel_dp_init(dev, PCH_DP_D, PORT_D); |
} else if (IS_VALLEYVIEW(dev)) { |
int found; |
|
if (I915_READ(SDVOB) & PORT_DETECTED) { |
/* SDVOB multiplex with HDMIB */ |
found = intel_sdvo_init(dev, SDVOB, true); |
if (!found) |
intel_hdmi_init(dev, SDVOB, PORT_B); |
if (!found && (I915_READ(DP_B) & DP_DETECTED)) |
intel_dp_init(dev, DP_B, PORT_B); |
} |
|
static void cpt_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
if (I915_READ(SDVOC) & PORT_DETECTED) |
intel_hdmi_init(dev, SDVOC, PORT_C); |
|
/* |
* On Ibex Peak and Cougar Point, we need to disable clock |
* gating for the panel power sequencer or it will fail to |
* start up when no ports are active. |
*/ |
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); |
I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) | |
DPLS_EDP_PPS_FIX_DIS); |
/* Without this, mode sets may fail silently on FDI */ |
for_each_pipe(pipe) |
I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS); |
} |
/* Shares lanes with HDMI on SDVOC */ |
if (I915_READ(DP_C) & DP_DETECTED) |
intel_dp_init(dev, DP_C, PORT_C); |
} else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) { |
bool found = false; |
|
static void ironlake_teardown_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->renderctx) { |
// i915_gem_object_unpin(dev_priv->renderctx); |
// drm_gem_object_unreference(&dev_priv->renderctx->base); |
dev_priv->renderctx = NULL; |
if (I915_READ(SDVOB) & SDVO_DETECTED) { |
DRM_DEBUG_KMS("probing SDVOB\n"); |
found = intel_sdvo_init(dev, SDVOB, true); |
if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) { |
DRM_DEBUG_KMS("probing HDMI on SDVOB\n"); |
intel_hdmi_init(dev, SDVOB, PORT_B); |
} |
|
if (dev_priv->pwrctx) { |
// i915_gem_object_unpin(dev_priv->pwrctx); |
// drm_gem_object_unreference(&dev_priv->pwrctx->base); |
dev_priv->pwrctx = NULL; |
if (!found && SUPPORTS_INTEGRATED_DP(dev)) { |
DRM_DEBUG_KMS("probing DP_B\n"); |
intel_dp_init(dev, DP_B, PORT_B); |
} |
} |
|
static void ironlake_disable_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
/* Before G4X SDVOC doesn't have its own detect register */ |
|
if (I915_READ(PWRCTXA)) { |
/* Wake the GPU, prevent RC6, then restore RSTDBYCTL */ |
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT); |
wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == RSX_STATUS_ON), |
50); |
if (I915_READ(SDVOB) & SDVO_DETECTED) { |
DRM_DEBUG_KMS("probing SDVOC\n"); |
found = intel_sdvo_init(dev, SDVOC, false); |
} |
|
I915_WRITE(PWRCTXA, 0); |
POSTING_READ(PWRCTXA); |
if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) { |
|
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); |
POSTING_READ(RSTDBYCTL); |
if (SUPPORTS_INTEGRATED_HDMI(dev)) { |
DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); |
intel_hdmi_init(dev, SDVOC, PORT_C); |
} |
if (SUPPORTS_INTEGRATED_DP(dev)) { |
DRM_DEBUG_KMS("probing DP_C\n"); |
intel_dp_init(dev, DP_C, PORT_C); |
} |
} |
|
ironlake_teardown_rc6(dev); |
if (SUPPORTS_INTEGRATED_DP(dev) && |
(I915_READ(DP_D) & DP_DETECTED)) { |
DRM_DEBUG_KMS("probing DP_D\n"); |
intel_dp_init(dev, DP_D, PORT_D); |
} |
} else if (IS_GEN2(dev)) |
intel_dvo_init(dev); |
|
static int ironlake_setup_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
// if (SUPPORTS_TV(dev)) |
// intel_tv_init(dev); |
|
if (dev_priv->renderctx == NULL) |
// dev_priv->renderctx = intel_alloc_context_page(dev); |
if (!dev_priv->renderctx) |
return -ENOMEM; |
|
if (dev_priv->pwrctx == NULL) |
// dev_priv->pwrctx = intel_alloc_context_page(dev); |
if (!dev_priv->pwrctx) { |
ironlake_teardown_rc6(dev); |
return -ENOMEM; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { |
encoder->base.possible_crtcs = encoder->crtc_mask; |
encoder->base.possible_clones = |
intel_encoder_clones(encoder); |
} |
|
return 0; |
if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) |
ironlake_init_pch_refclk(dev); |
} |
|
void ironlake_enable_rc6(struct drm_device *dev) |
|
|
static const struct drm_framebuffer_funcs intel_fb_funcs = { |
// .destroy = intel_user_framebuffer_destroy, |
// .create_handle = intel_user_framebuffer_create_handle, |
}; |
|
int intel_framebuffer_init(struct drm_device *dev, |
struct intel_framebuffer *intel_fb, |
struct drm_mode_fb_cmd2 *mode_cmd, |
struct drm_i915_gem_object *obj) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int ret; |
|
/* rc6 disabled by default due to repeated reports of hanging during |
* boot and resume. |
*/ |
if (!intel_enable_rc6(dev)) |
return; |
if (obj->tiling_mode == I915_TILING_Y) |
return -EINVAL; |
|
mutex_lock(&dev->struct_mutex); |
ret = ironlake_setup_rc6(dev); |
if (ret) { |
mutex_unlock(&dev->struct_mutex); |
return; |
} |
if (mode_cmd->pitches[0] & 63) |
return -EINVAL; |
|
/* |
* GPU can automatically power down the render unit if given a page |
* to save state. |
*/ |
#if 0 |
ret = BEGIN_LP_RING(6); |
if (ret) { |
ironlake_teardown_rc6(dev); |
mutex_unlock(&dev->struct_mutex); |
return; |
switch (mode_cmd->pixel_format) { |
case DRM_FORMAT_RGB332: |
case DRM_FORMAT_RGB565: |
case DRM_FORMAT_XRGB8888: |
case DRM_FORMAT_XBGR8888: |
case DRM_FORMAT_ARGB8888: |
case DRM_FORMAT_XRGB2101010: |
case DRM_FORMAT_ARGB2101010: |
/* RGB formats are common across chipsets */ |
break; |
case DRM_FORMAT_YUYV: |
case DRM_FORMAT_UYVY: |
case DRM_FORMAT_YVYU: |
case DRM_FORMAT_VYUY: |
break; |
default: |
DRM_DEBUG_KMS("unsupported pixel format %u\n", |
mode_cmd->pixel_format); |
return -EINVAL; |
} |
|
OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); |
OUT_RING(MI_SET_CONTEXT); |
OUT_RING(dev_priv->renderctx->gtt_offset | |
MI_MM_SPACE_GTT | |
MI_SAVE_EXT_STATE_EN | |
MI_RESTORE_EXT_STATE_EN | |
MI_RESTORE_INHIBIT); |
OUT_RING(MI_SUSPEND_FLUSH); |
OUT_RING(MI_NOOP); |
OUT_RING(MI_FLUSH); |
ADVANCE_LP_RING(); |
|
/* |
* Wait for the command parser to advance past MI_SET_CONTEXT. The HW |
* does an implicit flush, combined with MI_FLUSH above, it should be |
* safe to assume that renderctx is valid |
*/ |
ret = intel_wait_ring_idle(LP_RING(dev_priv)); |
ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); |
if (ret) { |
DRM_ERROR("failed to enable ironlake power power savings\n"); |
ironlake_teardown_rc6(dev); |
mutex_unlock(&dev->struct_mutex); |
return; |
DRM_ERROR("framebuffer init failed %d\n", ret); |
return ret; |
} |
#endif |
|
I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN); |
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); |
mutex_unlock(&dev->struct_mutex); |
drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); |
intel_fb->obj = obj; |
return 0; |
} |
|
void intel_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
dev_priv->display.init_clock_gating(dev); |
static const struct drm_mode_config_funcs intel_mode_funcs = { |
.fb_create = NULL /*intel_user_framebuffer_create*/, |
.output_poll_changed = NULL /*intel_fb_output_poll_changed*/, |
}; |
|
if (dev_priv->display.init_pch_clock_gating) |
dev_priv->display.init_pch_clock_gating(dev); |
} |
|
/* Set up chip specific display functions */ |
static void intel_init_display(struct drm_device *dev) |
{ |
7847,35 → 7760,25 |
|
/* We always want a DPMS function */ |
if (HAS_PCH_SPLIT(dev)) { |
dev_priv->display.dpms = ironlake_crtc_dpms; |
dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; |
dev_priv->display.crtc_enable = ironlake_crtc_enable; |
dev_priv->display.crtc_disable = ironlake_crtc_disable; |
dev_priv->display.off = ironlake_crtc_off; |
dev_priv->display.update_plane = ironlake_update_plane; |
} else { |
dev_priv->display.dpms = i9xx_crtc_dpms; |
dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; |
dev_priv->display.crtc_enable = i9xx_crtc_enable; |
dev_priv->display.crtc_disable = i9xx_crtc_disable; |
dev_priv->display.off = i9xx_crtc_off; |
dev_priv->display.update_plane = i9xx_update_plane; |
} |
|
if (I915_HAS_FBC(dev)) { |
if (HAS_PCH_SPLIT(dev)) { |
dev_priv->display.fbc_enabled = ironlake_fbc_enabled; |
dev_priv->display.enable_fbc = ironlake_enable_fbc; |
dev_priv->display.disable_fbc = ironlake_disable_fbc; |
} else if (IS_GM45(dev)) { |
dev_priv->display.fbc_enabled = g4x_fbc_enabled; |
dev_priv->display.enable_fbc = g4x_enable_fbc; |
dev_priv->display.disable_fbc = g4x_disable_fbc; |
} else if (IS_CRESTLINE(dev)) { |
dev_priv->display.fbc_enabled = i8xx_fbc_enabled; |
dev_priv->display.enable_fbc = i8xx_enable_fbc; |
dev_priv->display.disable_fbc = i8xx_disable_fbc; |
} |
/* 855GM needs testing */ |
} |
|
/* Returns the core display clock speed */ |
if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev))) |
if (IS_VALLEYVIEW(dev)) |
dev_priv->display.get_display_clock_speed = |
valleyview_get_display_clock_speed; |
else if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev))) |
dev_priv->display.get_display_clock_speed = |
i945_get_display_clock_speed; |
else if (IS_I915G(dev)) |
dev_priv->display.get_display_clock_speed = |
7896,153 → 7799,33 |
dev_priv->display.get_display_clock_speed = |
i830_get_display_clock_speed; |
|
/* For FIFO watermark updates */ |
if (HAS_PCH_SPLIT(dev)) { |
dev_priv->display.force_wake_get = __gen6_gt_force_wake_get; |
dev_priv->display.force_wake_put = __gen6_gt_force_wake_put; |
|
/* IVB configs may use multi-threaded forcewake */ |
if (IS_IVYBRIDGE(dev)) { |
u32 ecobus; |
|
/* A small trick here - if the bios hasn't configured MT forcewake, |
* and if the device is in RC6, then force_wake_mt_get will not wake |
* the device and the ECOBUS read will return zero. Which will be |
* (correctly) interpreted by the test below as MT forcewake being |
* disabled. |
*/ |
mutex_lock(&dev->struct_mutex); |
__gen6_gt_force_wake_mt_get(dev_priv); |
ecobus = I915_READ_NOTRACE(ECOBUS); |
__gen6_gt_force_wake_mt_put(dev_priv); |
mutex_unlock(&dev->struct_mutex); |
|
if (ecobus & FORCEWAKE_MT_ENABLE) { |
DRM_DEBUG_KMS("Using MT version of forcewake\n"); |
dev_priv->display.force_wake_get = |
__gen6_gt_force_wake_mt_get; |
dev_priv->display.force_wake_put = |
__gen6_gt_force_wake_mt_put; |
} |
} |
|
if (HAS_PCH_IBX(dev)) |
dev_priv->display.init_pch_clock_gating = ibx_init_clock_gating; |
else if (HAS_PCH_CPT(dev)) |
dev_priv->display.init_pch_clock_gating = cpt_init_clock_gating; |
|
if (IS_GEN5(dev)) { |
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK) |
dev_priv->display.update_wm = ironlake_update_wm; |
else { |
DRM_DEBUG_KMS("Failed to get proper latency. " |
"Disable CxSR\n"); |
dev_priv->display.update_wm = NULL; |
} |
dev_priv->display.fdi_link_train = ironlake_fdi_link_train; |
dev_priv->display.init_clock_gating = ironlake_init_clock_gating; |
dev_priv->display.write_eld = ironlake_write_eld; |
} else if (IS_GEN6(dev)) { |
if (SNB_READ_WM0_LATENCY()) { |
dev_priv->display.update_wm = sandybridge_update_wm; |
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; |
} else { |
DRM_DEBUG_KMS("Failed to read display plane latency. " |
"Disable CxSR\n"); |
dev_priv->display.update_wm = NULL; |
} |
dev_priv->display.fdi_link_train = gen6_fdi_link_train; |
dev_priv->display.init_clock_gating = gen6_init_clock_gating; |
dev_priv->display.write_eld = ironlake_write_eld; |
} else if (IS_IVYBRIDGE(dev)) { |
/* FIXME: detect B0+ stepping and use auto training */ |
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; |
if (SNB_READ_WM0_LATENCY()) { |
dev_priv->display.update_wm = sandybridge_update_wm; |
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; |
} else { |
DRM_DEBUG_KMS("Failed to read display plane latency. " |
"Disable CxSR\n"); |
dev_priv->display.update_wm = NULL; |
} |
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; |
dev_priv->display.write_eld = ironlake_write_eld; |
} else if (IS_HASWELL(dev)) { |
dev_priv->display.fdi_link_train = hsw_fdi_link_train; |
dev_priv->display.write_eld = haswell_write_eld; |
} else |
dev_priv->display.update_wm = NULL; |
} else if (IS_PINEVIEW(dev)) { |
if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev), |
dev_priv->is_ddr3, |
dev_priv->fsb_freq, |
dev_priv->mem_freq)) { |
DRM_INFO("failed to find known CxSR latency " |
"(found ddr%s fsb freq %d, mem freq %d), " |
"disabling CxSR\n", |
(dev_priv->is_ddr3 == 1) ? "3" : "2", |
dev_priv->fsb_freq, dev_priv->mem_freq); |
/* Disable CxSR and never update its watermark again */ |
pineview_disable_cxsr(dev); |
dev_priv->display.update_wm = NULL; |
} else |
dev_priv->display.update_wm = pineview_update_wm; |
dev_priv->display.init_clock_gating = gen3_init_clock_gating; |
} else if (IS_G4X(dev)) { |
dev_priv->display.write_eld = g4x_write_eld; |
dev_priv->display.update_wm = g4x_update_wm; |
dev_priv->display.init_clock_gating = g4x_init_clock_gating; |
} else if (IS_GEN4(dev)) { |
dev_priv->display.update_wm = i965_update_wm; |
if (IS_CRESTLINE(dev)) |
dev_priv->display.init_clock_gating = crestline_init_clock_gating; |
else if (IS_BROADWATER(dev)) |
dev_priv->display.init_clock_gating = broadwater_init_clock_gating; |
} else if (IS_GEN3(dev)) { |
dev_priv->display.update_wm = i9xx_update_wm; |
dev_priv->display.get_fifo_size = i9xx_get_fifo_size; |
dev_priv->display.init_clock_gating = gen3_init_clock_gating; |
} else if (IS_I865G(dev)) { |
dev_priv->display.update_wm = i830_update_wm; |
dev_priv->display.init_clock_gating = i85x_init_clock_gating; |
dev_priv->display.get_fifo_size = i830_get_fifo_size; |
} else if (IS_I85X(dev)) { |
dev_priv->display.update_wm = i9xx_update_wm; |
dev_priv->display.get_fifo_size = i85x_get_fifo_size; |
dev_priv->display.init_clock_gating = i85x_init_clock_gating; |
} else { |
dev_priv->display.update_wm = i830_update_wm; |
dev_priv->display.init_clock_gating = i830_init_clock_gating; |
if (IS_845G(dev)) |
dev_priv->display.get_fifo_size = i845_get_fifo_size; |
else |
dev_priv->display.get_fifo_size = i830_get_fifo_size; |
} |
|
/* Default just returns -ENODEV to indicate unsupported */ |
// dev_priv->display.queue_flip = intel_default_queue_flip; |
|
#if 0 |
switch (INTEL_INFO(dev)->gen) { |
case 2: |
dev_priv->display.queue_flip = intel_gen2_queue_flip; |
break; |
|
case 3: |
dev_priv->display.queue_flip = intel_gen3_queue_flip; |
break; |
|
case 4: |
case 5: |
dev_priv->display.queue_flip = intel_gen4_queue_flip; |
break; |
|
case 6: |
dev_priv->display.queue_flip = intel_gen6_queue_flip; |
break; |
case 7: |
dev_priv->display.queue_flip = intel_gen7_queue_flip; |
break; |
} |
#endif |
} |
|
/* |
* Some BIOSes insist on assuming the GPU's pipe A is enabled at suspend, |
8054,7 → 7837,7 |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
dev_priv->quirks |= QUIRK_PIPEA_FORCE; |
DRM_DEBUG_DRIVER("applying pipe a force quirk\n"); |
DRM_INFO("applying pipe a force quirk\n"); |
} |
|
/* |
8064,8 → 7847,20 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE; |
DRM_INFO("applying lvds SSC disable quirk\n"); |
} |
|
/* |
* A machine (e.g. Acer Aspire 5734Z) may need to invert the panel backlight |
* brightness value |
*/ |
static void quirk_invert_brightness(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
dev_priv->quirks |= QUIRK_INVERT_BRIGHTNESS; |
DRM_INFO("applying inverted panel brightness quirk\n"); |
} |
|
struct intel_quirk { |
int device; |
int subsystem_vendor; |
8073,27 → 7868,47 |
void (*hook)(struct drm_device *dev); |
}; |
|
struct intel_quirk intel_quirks[] = { |
/* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */ |
{ 0x2a42, 0x103c, 0x30eb, quirk_pipea_force }, |
/* For systems that don't have a meaningful PCI subdevice/subvendor ID */ |
struct intel_dmi_quirk { |
void (*hook)(struct drm_device *dev); |
const struct dmi_system_id (*dmi_id_list)[]; |
}; |
|
static int intel_dmi_reverse_brightness(const struct dmi_system_id *id) |
{ |
DRM_INFO("Backlight polarity reversed on %s\n", id->ident); |
return 1; |
} |
|
static const struct intel_dmi_quirk intel_dmi_quirks[] = { |
{ |
.dmi_id_list = &(const struct dmi_system_id[]) { |
{ |
.callback = intel_dmi_reverse_brightness, |
.ident = "NCR Corporation", |
.matches = {DMI_MATCH(DMI_SYS_VENDOR, "NCR Corporation"), |
DMI_MATCH(DMI_PRODUCT_NAME, ""), |
}, |
}, |
{ } /* terminating entry */ |
}, |
.hook = quirk_invert_brightness, |
}, |
}; |
|
static struct intel_quirk intel_quirks[] = { |
/* HP Mini needs pipe A force quirk (LP: #322104) */ |
{ 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, |
|
/* Thinkpad R31 needs pipe A force quirk */ |
{ 0x3577, 0x1014, 0x0505, quirk_pipea_force }, |
/* Toshiba Protege R-205, S-209 needs pipe A force quirk */ |
{ 0x2592, 0x1179, 0x0001, quirk_pipea_force }, |
|
/* ThinkPad X30 needs pipe A force quirk (LP: #304614) */ |
{ 0x3577, 0x1014, 0x0513, quirk_pipea_force }, |
/* ThinkPad X40 needs pipe A force quirk */ |
|
/* ThinkPad T60 needs pipe A force quirk (bug #16494) */ |
{ 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, |
|
/* 855 & before need to leave pipe A & dpll A up */ |
{ 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, |
/* 830/845 need to leave pipe A & dpll A up */ |
{ 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, |
{ 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, |
|
/* Lenovo U160 cannot use SSC on LVDS */ |
{ 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, |
8100,6 → 7915,9 |
|
/* Sony Vaio Y cannot use SSC on LVDS */ |
{ 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable }, |
|
/* Acer Aspire 5734Z must invert backlight brightness */ |
{ 0x2a42, 0x1025, 0x0459, quirk_invert_brightness }, |
}; |
|
static void intel_init_quirks(struct drm_device *dev) |
8117,6 → 7935,10 |
q->subsystem_device == PCI_ANY_ID)) |
q->hook(dev); |
} |
// for (i = 0; i < ARRAY_SIZE(intel_dmi_quirks); i++) { |
// if (dmi_check_system(*intel_dmi_quirks[i].dmi_id_list) != 0) |
// intel_dmi_quirks[i].hook(dev); |
// } |
} |
|
/* Disable the VGA plane that we never use */ |
8132,9 → 7954,9 |
vga_reg = VGACNTRL; |
|
// vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); |
out8(VGA_SR_INDEX, 1); |
out8(SR01, VGA_SR_INDEX); |
sr1 = in8(VGA_SR_DATA); |
out8(VGA_SR_DATA,sr1 | 1<<5); |
out8(sr1 | 1<<5, VGA_SR_DATA); |
// vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); |
udelay(300); |
|
8142,6 → 7964,22 |
POSTING_READ(vga_reg); |
} |
|
void intel_modeset_init_hw(struct drm_device *dev) |
{ |
/* We attempt to init the necessary power wells early in the initialization |
* time, so the subsystems that expect power to be enabled can work. |
*/ |
intel_init_power_wells(dev); |
|
intel_prepare_ddi(dev); |
|
intel_init_clock_gating(dev); |
|
// mutex_lock(&dev->struct_mutex); |
// intel_enable_gt_powersave(dev); |
// mutex_unlock(&dev->struct_mutex); |
} |
|
void intel_modeset_init(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
8152,10 → 7990,15 |
dev->mode_config.min_width = 0; |
dev->mode_config.min_height = 0; |
|
dev->mode_config.funcs = (void *)&intel_mode_funcs; |
dev->mode_config.preferred_depth = 24; |
dev->mode_config.prefer_shadow = 1; |
|
dev->mode_config.funcs = &intel_mode_funcs; |
|
intel_init_quirks(dev); |
|
intel_init_pm(dev); |
|
intel_init_display(dev); |
|
if (IS_GEN2(dev)) { |
8168,7 → 8011,7 |
dev->mode_config.max_width = 8192; |
dev->mode_config.max_height = 8192; |
} |
dev->mode_config.fb_base = get_bus_addr(); |
dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; |
|
DRM_DEBUG_KMS("%d display pipe%s available.\n", |
dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); |
8180,36 → 8023,325 |
DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); |
} |
|
intel_pch_pll_init(dev); |
|
/* Just disable it once at startup */ |
i915_disable_vga(dev); |
intel_setup_outputs(dev); |
} |
|
intel_init_clock_gating(dev); |
static void |
intel_connector_break_all_links(struct intel_connector *connector) |
{ |
connector->base.dpms = DRM_MODE_DPMS_OFF; |
connector->base.encoder = NULL; |
connector->encoder->connectors_active = false; |
connector->encoder->base.crtc = NULL; |
} |
|
if (IS_IRONLAKE_M(dev)) { |
ironlake_enable_drps(dev); |
intel_init_emon(dev); |
static void intel_enable_pipe_a(struct drm_device *dev) |
{ |
struct intel_connector *connector; |
struct drm_connector *crt = NULL; |
struct intel_load_detect_pipe load_detect_temp; |
|
/* We can't just switch on the pipe A, we need to set things up with a |
* proper mode and output configuration. As a gross hack, enable pipe A |
* by enabling the load detect pipe once. */ |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
base.head) { |
if (connector->encoder->type == INTEL_OUTPUT_ANALOG) { |
crt = &connector->base; |
break; |
} |
} |
|
if (IS_GEN6(dev) || IS_GEN7(dev)) { |
gen6_enable_rps(dev_priv); |
gen6_update_ring_freq(dev_priv); |
if (!crt) |
return; |
|
if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp)) |
intel_release_load_detect_pipe(crt, &load_detect_temp); |
|
|
} |
|
// INIT_WORK(&dev_priv->idle_work, intel_idle_update); |
// setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, |
// (unsigned long)dev); |
static bool |
intel_check_plane_mapping(struct intel_crtc *crtc) |
{ |
struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; |
u32 reg, val; |
|
if (dev_priv->num_pipe == 1) |
return true; |
|
reg = DSPCNTR(!crtc->plane); |
val = I915_READ(reg); |
|
if ((val & DISPLAY_PLANE_ENABLE) && |
(!!(val & DISPPLANE_SEL_PIPE_MASK) == crtc->pipe)) |
return false; |
|
return true; |
} |
|
static void intel_sanitize_crtc(struct intel_crtc *crtc) |
{ |
struct drm_device *dev = crtc->base.dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 reg; |
|
/* Clear any frame start delays used for debugging left by the BIOS */ |
reg = PIPECONF(crtc->pipe); |
I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); |
|
/* We need to sanitize the plane -> pipe mapping first because this will |
* disable the crtc (and hence change the state) if it is wrong. Note |
* that gen4+ has a fixed plane -> pipe mapping. */ |
if (INTEL_INFO(dev)->gen < 4 && !intel_check_plane_mapping(crtc)) { |
struct intel_connector *connector; |
bool plane; |
|
DRM_DEBUG_KMS("[CRTC:%d] wrong plane connection detected!\n", |
crtc->base.base.id); |
|
/* Pipe has the wrong plane attached and the plane is active. |
* Temporarily change the plane mapping and disable everything |
* ... */ |
plane = crtc->plane; |
crtc->plane = !plane; |
dev_priv->display.crtc_disable(&crtc->base); |
crtc->plane = plane; |
|
/* ... and break all links. */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
if (connector->encoder->base.crtc != &crtc->base) |
continue; |
|
intel_connector_break_all_links(connector); |
} |
|
WARN_ON(crtc->active); |
crtc->base.enabled = false; |
} |
|
if (dev_priv->quirks & QUIRK_PIPEA_FORCE && |
crtc->pipe == PIPE_A && !crtc->active) { |
/* BIOS forgot to enable pipe A, this mostly happens after |
* resume. Force-enable the pipe to fix this, the update_dpms |
* call below we restore the pipe to the right state, but leave |
* the required bits on. */ |
intel_enable_pipe_a(dev); |
} |
|
/* Adjust the state of the output pipe according to whether we |
* have active connectors/encoders. */ |
intel_crtc_update_dpms(&crtc->base); |
|
if (crtc->active != crtc->base.enabled) { |
struct intel_encoder *encoder; |
|
/* This can happen either due to bugs in the get_hw_state |
* functions or because the pipe is force-enabled due to the |
* pipe A quirk. */ |
DRM_DEBUG_KMS("[CRTC:%d] hw state adjusted, was %s, now %s\n", |
crtc->base.base.id, |
crtc->base.enabled ? "enabled" : "disabled", |
crtc->active ? "enabled" : "disabled"); |
|
crtc->base.enabled = crtc->active; |
|
/* Because we only establish the connector -> encoder -> |
* crtc links if something is active, this means the |
* crtc is now deactivated. Break the links. connector |
* -> encoder links are only establish when things are |
* actually up, hence no need to break them. */ |
WARN_ON(crtc->active); |
|
for_each_encoder_on_crtc(dev, &crtc->base, encoder) { |
WARN_ON(encoder->connectors_active); |
encoder->base.crtc = NULL; |
} |
} |
} |
|
static void intel_sanitize_encoder(struct intel_encoder *encoder) |
{ |
struct intel_connector *connector; |
struct drm_device *dev = encoder->base.dev; |
|
/* We need to check both for a crtc link (meaning that the |
* encoder is active and trying to read from a pipe) and the |
* pipe itself being active. */ |
bool has_active_crtc = encoder->base.crtc && |
to_intel_crtc(encoder->base.crtc)->active; |
|
if (encoder->connectors_active && !has_active_crtc) { |
DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n", |
encoder->base.base.id, |
drm_get_encoder_name(&encoder->base)); |
|
/* Connector is active, but has no active pipe. This is |
* fallout from our resume register restoring. Disable |
* the encoder manually again. */ |
if (encoder->base.crtc) { |
DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n", |
encoder->base.base.id, |
drm_get_encoder_name(&encoder->base)); |
encoder->disable(encoder); |
} |
|
/* Inconsistent output/port/pipe state happens presumably due to |
* a bug in one of the get_hw_state functions. Or someplace else |
* in our code, like the register restore mess on resume. Clamp |
* things to off as a safer default. */ |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
base.head) { |
if (connector->encoder != encoder) |
continue; |
|
intel_connector_break_all_links(connector); |
} |
} |
/* Enabled encoders without active connectors will be fixed in |
* the crtc fixup. */ |
} |
|
/* Scan out the current hw modeset state, sanitizes it and maps it into the drm |
* and i915 state tracking structures. */ |
void intel_modeset_setup_hw_state(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
enum pipe pipe; |
u32 tmp; |
struct intel_crtc *crtc; |
struct intel_encoder *encoder; |
struct intel_connector *connector; |
|
for_each_pipe(pipe) { |
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); |
|
tmp = I915_READ(PIPECONF(pipe)); |
if (tmp & PIPECONF_ENABLE) |
crtc->active = true; |
else |
crtc->active = false; |
|
crtc->base.enabled = crtc->active; |
|
DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n", |
crtc->base.base.id, |
crtc->active ? "enabled" : "disabled"); |
} |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
pipe = 0; |
|
if (encoder->get_hw_state(encoder, &pipe)) { |
encoder->base.crtc = |
dev_priv->pipe_to_crtc_mapping[pipe]; |
} else { |
encoder->base.crtc = NULL; |
} |
|
encoder->connectors_active = false; |
DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n", |
encoder->base.base.id, |
drm_get_encoder_name(&encoder->base), |
encoder->base.crtc ? "enabled" : "disabled", |
pipe); |
} |
|
list_for_each_entry(connector, &dev->mode_config.connector_list, |
base.head) { |
if (connector->get_hw_state(connector)) { |
connector->base.dpms = DRM_MODE_DPMS_ON; |
connector->encoder->connectors_active = true; |
connector->base.encoder = &connector->encoder->base; |
} else { |
connector->base.dpms = DRM_MODE_DPMS_OFF; |
connector->base.encoder = NULL; |
} |
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n", |
connector->base.base.id, |
drm_get_connector_name(&connector->base), |
connector->base.encoder ? "enabled" : "disabled"); |
} |
|
/* HW state is read out, now we need to sanitize this mess. */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, |
base.head) { |
intel_sanitize_encoder(encoder); |
} |
|
for_each_pipe(pipe) { |
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); |
intel_sanitize_crtc(crtc); |
} |
|
intel_modeset_update_staged_output_state(dev); |
|
intel_modeset_check_state(dev); |
} |
|
void intel_modeset_gem_init(struct drm_device *dev) |
{ |
if (IS_IRONLAKE_M(dev)) |
ironlake_enable_rc6(dev); |
intel_modeset_init_hw(dev); |
|
// intel_setup_overlay(dev); |
|
intel_modeset_setup_hw_state(dev); |
} |
|
void intel_modeset_cleanup(struct drm_device *dev) |
{ |
#if 0 |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc; |
struct intel_crtc *intel_crtc; |
|
// drm_kms_helper_poll_fini(dev); |
mutex_lock(&dev->struct_mutex); |
|
// intel_unregister_dsm_handler(); |
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
/* Skip inactive CRTCs */ |
if (!crtc->fb) |
continue; |
|
intel_crtc = to_intel_crtc(crtc); |
intel_increase_pllclock(crtc); |
} |
|
intel_disable_fbc(dev); |
|
intel_disable_gt_powersave(dev); |
|
ironlake_teardown_rc6(dev); |
|
if (IS_VALLEYVIEW(dev)) |
vlv_init_dpio(dev); |
|
mutex_unlock(&dev->struct_mutex); |
|
/* Disable the irq before mode object teardown, for the irq might |
* enqueue unpin/hotplug work. */ |
// drm_irq_uninstall(dev); |
// cancel_work_sync(&dev_priv->hotplug_work); |
// cancel_work_sync(&dev_priv->rps.work); |
|
/* flush any delayed tasks or pending work */ |
// flush_scheduled_work(); |
|
drm_mode_config_cleanup(dev); |
#endif |
} |
|
/* |
* Return which encoder is currently attached for connector. |
*/ |
8226,4 → 8358,131 |
&encoder->base); |
} |
|
/* |
* set vga decode state - true == enable VGA decode |
*/ |
int intel_modeset_vga_set_state(struct drm_device *dev, bool state) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u16 gmch_ctrl; |
|
pci_read_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, &gmch_ctrl); |
if (state) |
gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE; |
else |
gmch_ctrl |= INTEL_GMCH_VGA_DISABLE; |
pci_write_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, gmch_ctrl); |
return 0; |
} |
|
#ifdef CONFIG_DEBUG_FS |
#include <linux/seq_file.h> |
|
struct intel_display_error_state { |
struct intel_cursor_error_state { |
u32 control; |
u32 position; |
u32 base; |
u32 size; |
} cursor[I915_MAX_PIPES]; |
|
struct intel_pipe_error_state { |
u32 conf; |
u32 source; |
|
u32 htotal; |
u32 hblank; |
u32 hsync; |
u32 vtotal; |
u32 vblank; |
u32 vsync; |
} pipe[I915_MAX_PIPES]; |
|
struct intel_plane_error_state { |
u32 control; |
u32 stride; |
u32 size; |
u32 pos; |
u32 addr; |
u32 surface; |
u32 tile_offset; |
} plane[I915_MAX_PIPES]; |
}; |
|
struct intel_display_error_state * |
intel_display_capture_error_state(struct drm_device *dev) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
struct intel_display_error_state *error; |
int i; |
|
error = kmalloc(sizeof(*error), GFP_ATOMIC); |
if (error == NULL) |
return NULL; |
|
for_each_pipe(i) { |
error->cursor[i].control = I915_READ(CURCNTR(i)); |
error->cursor[i].position = I915_READ(CURPOS(i)); |
error->cursor[i].base = I915_READ(CURBASE(i)); |
|
error->plane[i].control = I915_READ(DSPCNTR(i)); |
error->plane[i].stride = I915_READ(DSPSTRIDE(i)); |
error->plane[i].size = I915_READ(DSPSIZE(i)); |
error->plane[i].pos = I915_READ(DSPPOS(i)); |
error->plane[i].addr = I915_READ(DSPADDR(i)); |
if (INTEL_INFO(dev)->gen >= 4) { |
error->plane[i].surface = I915_READ(DSPSURF(i)); |
error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i)); |
} |
|
error->pipe[i].conf = I915_READ(PIPECONF(i)); |
error->pipe[i].source = I915_READ(PIPESRC(i)); |
error->pipe[i].htotal = I915_READ(HTOTAL(i)); |
error->pipe[i].hblank = I915_READ(HBLANK(i)); |
error->pipe[i].hsync = I915_READ(HSYNC(i)); |
error->pipe[i].vtotal = I915_READ(VTOTAL(i)); |
error->pipe[i].vblank = I915_READ(VBLANK(i)); |
error->pipe[i].vsync = I915_READ(VSYNC(i)); |
} |
|
return error; |
} |
|
void |
intel_display_print_error_state(struct seq_file *m, |
struct drm_device *dev, |
struct intel_display_error_state *error) |
{ |
drm_i915_private_t *dev_priv = dev->dev_private; |
int i; |
|
seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe); |
for_each_pipe(i) { |
seq_printf(m, "Pipe [%d]:\n", i); |
seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); |
seq_printf(m, " SRC: %08x\n", error->pipe[i].source); |
seq_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); |
seq_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank); |
seq_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync); |
seq_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal); |
seq_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank); |
seq_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync); |
|
seq_printf(m, "Plane [%d]:\n", i); |
seq_printf(m, " CNTR: %08x\n", error->plane[i].control); |
seq_printf(m, " STRIDE: %08x\n", error->plane[i].stride); |
seq_printf(m, " SIZE: %08x\n", error->plane[i].size); |
seq_printf(m, " POS: %08x\n", error->plane[i].pos); |
seq_printf(m, " ADDR: %08x\n", error->plane[i].addr); |
if (INTEL_INFO(dev)->gen >= 4) { |
seq_printf(m, " SURF: %08x\n", error->plane[i].surface); |
seq_printf(m, " TILEOFF: %08x\n", error->plane[i].tile_offset); |
} |
|
seq_printf(m, "Cursor [%d]:\n", i); |
seq_printf(m, " CNTR: %08x\n", error->cursor[i].control); |
seq_printf(m, " POS: %08x\n", error->cursor[i].position); |
seq_printf(m, " BASE: %08x\n", error->cursor[i].base); |
} |
} |
#endif |