37,16 → 37,7 |
#define assert_spin_locked(x) |
|
void getrawmonotonic(struct timespec *ts); |
void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec); |
|
static inline struct timespec timespec_sub(struct timespec lhs, |
struct timespec rhs) |
{ |
struct timespec ts_delta; |
set_normalized_timespec(&ts_delta, lhs.tv_sec - rhs.tv_sec, |
lhs.tv_nsec - rhs.tv_nsec); |
return ts_delta; |
} |
|
|
/* FBC, or Frame Buffer Compression, is a technique employed to compress the |
102,7 → 93,7 |
int plane, i; |
u32 fbc_ctl, fbc_ctl2; |
|
cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; |
cfb_pitch = dev_priv->fbc.size / FBC_LL_SIZE; |
if (fb->pitches[0] < cfb_pitch) |
cfb_pitch = fb->pitches[0]; |
|
129,8 → 120,8 |
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); |
DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c, ", |
cfb_pitch, crtc->y, plane_name(intel_crtc->plane)); |
} |
|
static bool i8xx_fbc_enabled(struct drm_device *dev) |
164,7 → 155,7 |
/* 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); |
DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); |
} |
|
static void g4x_disable_fbc(struct drm_device *dev) |
233,7 → 224,7 |
(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); |
I915_WRITE(ILK_FBC_RT_BASE, i915_gem_obj_ggtt_offset(obj) | ILK_FBC_RT_VALID); |
/* enable it... */ |
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); |
|
244,7 → 235,7 |
sandybridge_blit_fbc_update(dev); |
} |
|
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); |
DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); |
} |
|
static void ironlake_disable_fbc(struct drm_device *dev) |
258,6 → 249,18 |
dpfc_ctl &= ~DPFC_CTL_EN; |
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); |
|
if (IS_IVYBRIDGE(dev)) |
/* WaFbcDisableDpfcClockGating:ivb */ |
I915_WRITE(ILK_DSPCLK_GATE_D, |
I915_READ(ILK_DSPCLK_GATE_D) & |
~ILK_DPFCUNIT_CLOCK_GATE_DISABLE); |
|
if (IS_HASWELL(dev)) |
/* WaFbcDisableDpfcClockGating:hsw */ |
I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, |
I915_READ(HSW_CLKGATE_DISABLE_PART_1) & |
~HSW_DPFC_GATING_DISABLE); |
|
DRM_DEBUG_KMS("disabled FBC\n"); |
} |
} |
269,6 → 272,47 |
return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN; |
} |
|
static void gen7_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); |
|
I915_WRITE(IVB_FBC_RT_BASE, i915_gem_obj_ggtt_offset(obj)); |
|
I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X | |
IVB_DPFC_CTL_FENCE_EN | |
intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); |
|
if (IS_IVYBRIDGE(dev)) { |
/* WaFbcAsynchFlipDisableFbcQueue:ivb */ |
I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); |
/* WaFbcDisableDpfcClockGating:ivb */ |
I915_WRITE(ILK_DSPCLK_GATE_D, |
I915_READ(ILK_DSPCLK_GATE_D) | |
ILK_DPFCUNIT_CLOCK_GATE_DISABLE); |
} else { |
/* WaFbcAsynchFlipDisableFbcQueue:hsw */ |
I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe), |
HSW_BYPASS_FBC_QUEUE); |
/* WaFbcDisableDpfcClockGating:hsw */ |
I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, |
I915_READ(HSW_CLKGATE_DISABLE_PART_1) | |
HSW_DPFC_GATING_DISABLE); |
} |
|
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); |
} |
|
bool intel_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
288,7 → 332,7 |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
mutex_lock(&dev->struct_mutex); |
if (work == dev_priv->fbc_work) { |
if (work == dev_priv->fbc.fbc_work) { |
/* Double check that we haven't switched fb without cancelling |
* the prior work. |
*/ |
296,12 → 340,12 |
dev_priv->display.enable_fbc(work->crtc, |
work->interval); |
|
dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane; |
dev_priv->cfb_fb = work->crtc->fb->base.id; |
dev_priv->cfb_y = work->crtc->y; |
dev_priv->fbc.plane = to_intel_crtc(work->crtc)->plane; |
dev_priv->fbc.fb_id = work->crtc->fb->base.id; |
dev_priv->fbc.y = work->crtc->y; |
} |
|
dev_priv->fbc_work = NULL; |
dev_priv->fbc.fbc_work = NULL; |
} |
mutex_unlock(&dev->struct_mutex); |
|
310,13 → 354,13 |
|
static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv) |
{ |
if (dev_priv->fbc_work == NULL) |
if (dev_priv->fbc.fbc_work == NULL) |
return; |
|
DRM_DEBUG_KMS("cancelling pending FBC enable\n"); |
|
/* Synchronisation is provided by struct_mutex and checking of |
* dev_priv->fbc_work, so we can perform the cancellation |
* dev_priv->fbc.fbc_work, so we can perform the cancellation |
* entirely asynchronously. |
*/ |
// if (cancel_delayed_work(&dev_priv->fbc_work->work)) |
328,10 → 372,10 |
* for our mutex), it will discover that is no longer |
* necessary to run. |
*/ |
dev_priv->fbc_work = NULL; |
dev_priv->fbc.fbc_work = NULL; |
} |
|
void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) |
{ |
struct intel_fbc_work *work; |
struct drm_device *dev = crtc->dev; |
344,6 → 388,7 |
|
work = kzalloc(sizeof *work, GFP_KERNEL); |
if (work == NULL) { |
DRM_ERROR("Failed to allocate FBC work structure\n"); |
dev_priv->display.enable_fbc(crtc, interval); |
return; |
} |
353,7 → 398,7 |
work->interval = interval; |
INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn); |
|
dev_priv->fbc_work = work; |
dev_priv->fbc.fbc_work = work; |
|
DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); |
|
367,6 → 412,8 |
* following the termination of the page-flipping sequence |
* and indeed performing the enable as a co-routine and not |
* waiting synchronously upon the vblank. |
* |
* WaFbcWaitForVBlankBeforeEnable:ilk,snb |
*/ |
schedule_delayed_work(&work->work, msecs_to_jiffies(50)); |
} |
381,9 → 428,19 |
return; |
|
dev_priv->display.disable_fbc(dev); |
dev_priv->cfb_plane = -1; |
dev_priv->fbc.plane = -1; |
} |
|
static bool set_no_fbc_reason(struct drm_i915_private *dev_priv, |
enum no_fbc_reason reason) |
{ |
if (dev_priv->fbc.no_fbc_reason == reason) |
return false; |
|
dev_priv->fbc.no_fbc_reason = reason; |
return true; |
} |
|
/** |
* intel_update_fbc - enable/disable FBC as needed |
* @dev: the drm_device |
394,7 → 451,7 |
* - no pixel mulitply/line duplication |
* - no alpha buffer discard |
* - no dual wide |
* - framebuffer <= 2048 in width, 1536 in height |
* - framebuffer <= max_hdisplay in width, max_vdisplay 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 |
411,15 → 468,18 |
struct drm_framebuffer *fb; |
struct intel_framebuffer *intel_fb; |
struct drm_i915_gem_object *obj; |
int enable_fbc; |
unsigned int max_hdisplay, max_vdisplay; |
|
ENTER(); |
|
if (!i915_powersave) |
if (!I915_HAS_FBC(dev)) { |
set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED); |
return; |
} |
|
if (!I915_HAS_FBC(dev)) |
if (!i915_powersave) { |
if (set_no_fbc_reason(dev_priv, FBC_MODULE_PARAM)) |
DRM_DEBUG_KMS("fbc disabled per module param\n"); |
return; |
} |
|
/* |
* If FBC is already on, we just have to verify that we can |
434,8 → 494,8 |
if (intel_crtc_active(tmp_crtc) && |
!to_intel_crtc(tmp_crtc)->primary_disabled) { |
if (crtc) { |
if (set_no_fbc_reason(dev_priv, FBC_MULTIPLE_PIPES)) |
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; |
443,8 → 503,8 |
} |
|
if (!crtc || crtc->fb == NULL) { |
if (set_no_fbc_reason(dev_priv, FBC_NO_OUTPUT)) |
DRM_DEBUG_KMS("no output, disabling\n"); |
dev_priv->no_fbc_reason = FBC_NO_OUTPUT; |
goto out_disable; |
} |
|
453,34 → 513,42 |
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 (i915_enable_fbc < 0 && |
INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) { |
if (set_no_fbc_reason(dev_priv, FBC_CHIP_DEFAULT)) |
DRM_DEBUG_KMS("disabled per chip default\n"); |
goto out_disable; |
} |
if (!enable_fbc) { |
if (!i915_enable_fbc) { |
if (set_no_fbc_reason(dev_priv, FBC_MODULE_PARAM)) |
DRM_DEBUG_KMS("fbc disabled per module param\n"); |
dev_priv->no_fbc_reason = FBC_MODULE_PARAM; |
goto out_disable; |
} |
if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || |
(crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { |
if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE)) |
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)) { |
|
if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { |
max_hdisplay = 4096; |
max_vdisplay = 2048; |
} else { |
max_hdisplay = 2048; |
max_vdisplay = 1536; |
} |
if ((crtc->mode.hdisplay > max_hdisplay) || |
(crtc->mode.vdisplay > max_vdisplay)) { |
if (set_no_fbc_reason(dev_priv, FBC_MODE_TOO_LARGE)) |
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) { |
if ((IS_I915GM(dev) || IS_I945GM(dev) || IS_HASWELL(dev)) && |
intel_crtc->plane != 0) { |
if (set_no_fbc_reason(dev_priv, FBC_BAD_PLANE)) |
DRM_DEBUG_KMS("plane not 0, disabling compression\n"); |
dev_priv->no_fbc_reason = FBC_BAD_PLANE; |
goto out_disable; |
} |
|
489,8 → 557,8 |
*/ |
if (obj->tiling_mode != I915_TILING_X || |
obj->fence_reg == I915_FENCE_REG_NONE) { |
if (set_no_fbc_reason(dev_priv, FBC_NOT_TILED)) |
DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); |
dev_priv->no_fbc_reason = FBC_NOT_TILED; |
goto out_disable; |
} |
|
499,10 → 567,8 |
goto out_disable; |
|
if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { |
DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); |
DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); |
if (set_no_fbc_reason(dev_priv, FBC_STOLEN_TOO_SMALL)) |
DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); |
dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; |
goto out_disable; |
} |
|
511,9 → 577,9 |
* 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) |
if (dev_priv->fbc.plane == intel_crtc->plane && |
dev_priv->fbc.fb_id == fb->base.id && |
dev_priv->fbc.y == crtc->y) |
return; |
|
if (intel_fbc_enabled(dev)) { |
545,8 → 611,7 |
} |
|
intel_enable_fbc(crtc, 500); |
LEAVE(); |
|
dev_priv->fbc.no_fbc_reason = FBC_OK; |
return; |
|
out_disable: |
556,7 → 621,6 |
intel_disable_fbc(dev); |
} |
i915_gem_stolen_cleanup_compression(dev); |
LEAVE(); |
} |
|
static void i915_pineview_get_mem_freq(struct drm_device *dev) |
1626,9 → 1690,6 |
I915_WRITE(FW_BLC, fwater_lo); |
} |
|
#define ILK_LP0_PLANE_LATENCY 700 |
#define ILK_LP0_CURSOR_LATENCY 1300 |
|
/* |
* Check the wm result. |
* |
1654,6 → 1715,10 |
I915_WRITE(DISP_ARB_CTL, |
I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS); |
return false; |
} else if (INTEL_INFO(dev)->gen >= 6) { |
/* enable FBC WM (except on ILK, where it must remain off) */ |
I915_WRITE(DISP_ARB_CTL, |
I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS); |
} |
|
if (display_wm > display->max_wm) { |
1739,9 → 1804,9 |
enabled = 0; |
if (g4x_compute_wm0(dev, PIPE_A, |
&ironlake_display_wm_info, |
ILK_LP0_PLANE_LATENCY, |
dev_priv->wm.pri_latency[0] * 100, |
&ironlake_cursor_wm_info, |
ILK_LP0_CURSOR_LATENCY, |
dev_priv->wm.cur_latency[0] * 100, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEA_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
1753,9 → 1818,9 |
|
if (g4x_compute_wm0(dev, PIPE_B, |
&ironlake_display_wm_info, |
ILK_LP0_PLANE_LATENCY, |
dev_priv->wm.pri_latency[0] * 100, |
&ironlake_cursor_wm_info, |
ILK_LP0_CURSOR_LATENCY, |
dev_priv->wm.cur_latency[0] * 100, |
&plane_wm, &cursor_wm)) { |
I915_WRITE(WM0_PIPEB_ILK, |
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); |
1779,7 → 1844,7 |
|
/* WM1 */ |
if (!ironlake_compute_srwm(dev, 1, enabled, |
ILK_READ_WM1_LATENCY() * 500, |
dev_priv->wm.pri_latency[1] * 500, |
&ironlake_display_srwm_info, |
&ironlake_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1787,7 → 1852,7 |
|
I915_WRITE(WM1_LP_ILK, |
WM1_LP_SR_EN | |
(ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[1] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
1794,7 → 1859,7 |
|
/* WM2 */ |
if (!ironlake_compute_srwm(dev, 2, enabled, |
ILK_READ_WM2_LATENCY() * 500, |
dev_priv->wm.pri_latency[2] * 500, |
&ironlake_display_srwm_info, |
&ironlake_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1802,7 → 1867,7 |
|
I915_WRITE(WM2_LP_ILK, |
WM2_LP_EN | |
(ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[2] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
1816,7 → 1881,7 |
static 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 latency = dev_priv->wm.pri_latency[0] * 100; /* In unit 0.1us */ |
u32 val; |
int fbc_wm, plane_wm, cursor_wm; |
unsigned int enabled; |
1871,7 → 1936,7 |
|
/* WM1 */ |
if (!ironlake_compute_srwm(dev, 1, enabled, |
SNB_READ_WM1_LATENCY() * 500, |
dev_priv->wm.pri_latency[1] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1879,7 → 1944,7 |
|
I915_WRITE(WM1_LP_ILK, |
WM1_LP_SR_EN | |
(SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[1] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
1886,7 → 1951,7 |
|
/* WM2 */ |
if (!ironlake_compute_srwm(dev, 2, enabled, |
SNB_READ_WM2_LATENCY() * 500, |
dev_priv->wm.pri_latency[2] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1894,7 → 1959,7 |
|
I915_WRITE(WM2_LP_ILK, |
WM2_LP_EN | |
(SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[2] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
1901,7 → 1966,7 |
|
/* WM3 */ |
if (!ironlake_compute_srwm(dev, 3, enabled, |
SNB_READ_WM3_LATENCY() * 500, |
dev_priv->wm.pri_latency[3] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1909,7 → 1974,7 |
|
I915_WRITE(WM3_LP_ILK, |
WM3_LP_EN | |
(SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[3] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
1918,7 → 1983,7 |
static void ivybridge_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 latency = dev_priv->wm.pri_latency[0] * 100; /* In unit 0.1us */ |
u32 val; |
int fbc_wm, plane_wm, cursor_wm; |
int ignore_fbc_wm, ignore_plane_wm, ignore_cursor_wm; |
1988,7 → 2053,7 |
|
/* WM1 */ |
if (!ironlake_compute_srwm(dev, 1, enabled, |
SNB_READ_WM1_LATENCY() * 500, |
dev_priv->wm.pri_latency[1] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
1996,7 → 2061,7 |
|
I915_WRITE(WM1_LP_ILK, |
WM1_LP_SR_EN | |
(SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[1] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
2003,7 → 2068,7 |
|
/* WM2 */ |
if (!ironlake_compute_srwm(dev, 2, enabled, |
SNB_READ_WM2_LATENCY() * 500, |
dev_priv->wm.pri_latency[2] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &cursor_wm)) |
2011,7 → 2076,7 |
|
I915_WRITE(WM2_LP_ILK, |
WM2_LP_EN | |
(SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[2] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
2018,12 → 2083,12 |
|
/* WM3, note we have to correct the cursor latency */ |
if (!ironlake_compute_srwm(dev, 3, enabled, |
SNB_READ_WM3_LATENCY() * 500, |
dev_priv->wm.pri_latency[3] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&fbc_wm, &plane_wm, &ignore_cursor_wm) || |
!ironlake_compute_srwm(dev, 3, enabled, |
2 * SNB_READ_WM3_LATENCY() * 500, |
dev_priv->wm.cur_latency[3] * 500, |
&sandybridge_display_srwm_info, |
&sandybridge_cursor_srwm_info, |
&ignore_fbc_wm, &ignore_plane_wm, &cursor_wm)) |
2031,39 → 2096,802 |
|
I915_WRITE(WM3_LP_ILK, |
WM3_LP_EN | |
(SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) | |
(dev_priv->wm.pri_latency[3] << WM1_LP_LATENCY_SHIFT) | |
(fbc_wm << WM1_LP_FBC_SHIFT) | |
(plane_wm << WM1_LP_SR_SHIFT) | |
cursor_wm); |
} |
|
static void |
haswell_update_linetime_wm(struct drm_device *dev, int pipe, |
struct drm_display_mode *mode) |
static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev, |
struct drm_crtc *crtc) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
uint32_t pixel_rate; |
|
pixel_rate = intel_crtc->config.adjusted_mode.clock; |
|
/* We only use IF-ID interlacing. If we ever use PF-ID we'll need to |
* adjust the pixel_rate here. */ |
|
if (intel_crtc->config.pch_pfit.enabled) { |
uint64_t pipe_w, pipe_h, pfit_w, pfit_h; |
uint32_t pfit_size = intel_crtc->config.pch_pfit.size; |
|
pipe_w = intel_crtc->config.requested_mode.hdisplay; |
pipe_h = intel_crtc->config.requested_mode.vdisplay; |
pfit_w = (pfit_size >> 16) & 0xFFFF; |
pfit_h = pfit_size & 0xFFFF; |
if (pipe_w < pfit_w) |
pipe_w = pfit_w; |
if (pipe_h < pfit_h) |
pipe_h = pfit_h; |
|
pixel_rate = div_u64((uint64_t) pixel_rate * pipe_w * pipe_h, |
pfit_w * pfit_h); |
} |
|
return pixel_rate; |
} |
|
/* latency must be in 0.1us units. */ |
static uint32_t ilk_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel, |
uint32_t latency) |
{ |
uint64_t ret; |
|
if (WARN(latency == 0, "Latency value missing\n")) |
return UINT_MAX; |
|
ret = (uint64_t) pixel_rate * bytes_per_pixel * latency; |
ret = DIV_ROUND_UP_ULL(ret, 64 * 10000) + 2; |
|
return ret; |
} |
|
/* latency must be in 0.1us units. */ |
static uint32_t ilk_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, |
uint32_t horiz_pixels, uint8_t bytes_per_pixel, |
uint32_t latency) |
{ |
uint32_t ret; |
|
if (WARN(latency == 0, "Latency value missing\n")) |
return UINT_MAX; |
|
ret = (latency * pixel_rate) / (pipe_htotal * 10000); |
ret = (ret + 1) * horiz_pixels * bytes_per_pixel; |
ret = DIV_ROUND_UP(ret, 64) + 2; |
return ret; |
} |
|
static uint32_t ilk_wm_fbc(uint32_t pri_val, uint32_t horiz_pixels, |
uint8_t bytes_per_pixel) |
{ |
return DIV_ROUND_UP(pri_val * 64, horiz_pixels * bytes_per_pixel) + 2; |
} |
|
struct hsw_pipe_wm_parameters { |
bool active; |
uint32_t pipe_htotal; |
uint32_t pixel_rate; |
struct intel_plane_wm_parameters pri; |
struct intel_plane_wm_parameters spr; |
struct intel_plane_wm_parameters cur; |
}; |
|
struct hsw_wm_maximums { |
uint16_t pri; |
uint16_t spr; |
uint16_t cur; |
uint16_t fbc; |
}; |
|
struct hsw_wm_values { |
uint32_t wm_pipe[3]; |
uint32_t wm_lp[3]; |
uint32_t wm_lp_spr[3]; |
uint32_t wm_linetime[3]; |
bool enable_fbc_wm; |
}; |
|
/* used in computing the new watermarks state */ |
struct intel_wm_config { |
unsigned int num_pipes_active; |
bool sprites_enabled; |
bool sprites_scaled; |
bool fbc_wm_enabled; |
}; |
|
/* |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_pri_wm(struct hsw_pipe_wm_parameters *params, |
uint32_t mem_value, |
bool is_lp) |
{ |
uint32_t method1, method2; |
|
if (!params->active || !params->pri.enabled) |
return 0; |
|
method1 = ilk_wm_method1(params->pixel_rate, |
params->pri.bytes_per_pixel, |
mem_value); |
|
if (!is_lp) |
return method1; |
|
method2 = ilk_wm_method2(params->pixel_rate, |
params->pipe_htotal, |
params->pri.horiz_pixels, |
params->pri.bytes_per_pixel, |
mem_value); |
|
return min(method1, method2); |
} |
|
/* |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_spr_wm(struct hsw_pipe_wm_parameters *params, |
uint32_t mem_value) |
{ |
uint32_t method1, method2; |
|
if (!params->active || !params->spr.enabled) |
return 0; |
|
method1 = ilk_wm_method1(params->pixel_rate, |
params->spr.bytes_per_pixel, |
mem_value); |
method2 = ilk_wm_method2(params->pixel_rate, |
params->pipe_htotal, |
params->spr.horiz_pixels, |
params->spr.bytes_per_pixel, |
mem_value); |
return min(method1, method2); |
} |
|
/* |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_cur_wm(struct hsw_pipe_wm_parameters *params, |
uint32_t mem_value) |
{ |
if (!params->active || !params->cur.enabled) |
return 0; |
|
return ilk_wm_method2(params->pixel_rate, |
params->pipe_htotal, |
params->cur.horiz_pixels, |
params->cur.bytes_per_pixel, |
mem_value); |
} |
|
/* Only for WM_LP. */ |
static uint32_t ilk_compute_fbc_wm(struct hsw_pipe_wm_parameters *params, |
uint32_t pri_val) |
{ |
if (!params->active || !params->pri.enabled) |
return 0; |
|
return ilk_wm_fbc(pri_val, |
params->pri.horiz_pixels, |
params->pri.bytes_per_pixel); |
} |
|
static unsigned int ilk_display_fifo_size(const struct drm_device *dev) |
{ |
if (INTEL_INFO(dev)->gen >= 7) |
return 768; |
else |
return 512; |
} |
|
/* Calculate the maximum primary/sprite plane watermark */ |
static unsigned int ilk_plane_wm_max(const struct drm_device *dev, |
int level, |
const struct intel_wm_config *config, |
enum intel_ddb_partitioning ddb_partitioning, |
bool is_sprite) |
{ |
unsigned int fifo_size = ilk_display_fifo_size(dev); |
unsigned int max; |
|
/* if sprites aren't enabled, sprites get nothing */ |
if (is_sprite && !config->sprites_enabled) |
return 0; |
|
/* HSW allows LP1+ watermarks even with multiple pipes */ |
if (level == 0 || config->num_pipes_active > 1) { |
fifo_size /= INTEL_INFO(dev)->num_pipes; |
|
/* |
* For some reason the non self refresh |
* FIFO size is only half of the self |
* refresh FIFO size on ILK/SNB. |
*/ |
if (INTEL_INFO(dev)->gen <= 6) |
fifo_size /= 2; |
} |
|
if (config->sprites_enabled) { |
/* level 0 is always calculated with 1:1 split */ |
if (level > 0 && ddb_partitioning == INTEL_DDB_PART_5_6) { |
if (is_sprite) |
fifo_size *= 5; |
fifo_size /= 6; |
} else { |
fifo_size /= 2; |
} |
} |
|
/* clamp to max that the registers can hold */ |
if (INTEL_INFO(dev)->gen >= 7) |
/* IVB/HSW primary/sprite plane watermarks */ |
max = level == 0 ? 127 : 1023; |
else if (!is_sprite) |
/* ILK/SNB primary plane watermarks */ |
max = level == 0 ? 127 : 511; |
else |
/* ILK/SNB sprite plane watermarks */ |
max = level == 0 ? 63 : 255; |
|
return min(fifo_size, max); |
} |
|
/* Calculate the maximum cursor plane watermark */ |
static unsigned int ilk_cursor_wm_max(const struct drm_device *dev, |
int level, |
const struct intel_wm_config *config) |
{ |
/* HSW LP1+ watermarks w/ multiple pipes */ |
if (level > 0 && config->num_pipes_active > 1) |
return 64; |
|
/* otherwise just report max that registers can hold */ |
if (INTEL_INFO(dev)->gen >= 7) |
return level == 0 ? 63 : 255; |
else |
return level == 0 ? 31 : 63; |
} |
|
/* Calculate the maximum FBC watermark */ |
static unsigned int ilk_fbc_wm_max(void) |
{ |
/* max that registers can hold */ |
return 15; |
} |
|
static void ilk_wm_max(struct drm_device *dev, |
int level, |
const struct intel_wm_config *config, |
enum intel_ddb_partitioning ddb_partitioning, |
struct hsw_wm_maximums *max) |
{ |
max->pri = ilk_plane_wm_max(dev, level, config, ddb_partitioning, false); |
max->spr = ilk_plane_wm_max(dev, level, config, ddb_partitioning, true); |
max->cur = ilk_cursor_wm_max(dev, level, config); |
max->fbc = ilk_fbc_wm_max(); |
} |
|
static bool ilk_check_wm(int level, |
const struct hsw_wm_maximums *max, |
struct intel_wm_level *result) |
{ |
bool ret; |
|
/* already determined to be invalid? */ |
if (!result->enable) |
return false; |
|
result->enable = result->pri_val <= max->pri && |
result->spr_val <= max->spr && |
result->cur_val <= max->cur; |
|
ret = result->enable; |
|
/* |
* HACK until we can pre-compute everything, |
* and thus fail gracefully if LP0 watermarks |
* are exceeded... |
*/ |
if (level == 0 && !result->enable) { |
if (result->pri_val > max->pri) |
DRM_DEBUG_KMS("Primary WM%d too large %u (max %u)\n", |
level, result->pri_val, max->pri); |
if (result->spr_val > max->spr) |
DRM_DEBUG_KMS("Sprite WM%d too large %u (max %u)\n", |
level, result->spr_val, max->spr); |
if (result->cur_val > max->cur) |
DRM_DEBUG_KMS("Cursor WM%d too large %u (max %u)\n", |
level, result->cur_val, max->cur); |
|
result->pri_val = min_t(uint32_t, result->pri_val, max->pri); |
result->spr_val = min_t(uint32_t, result->spr_val, max->spr); |
result->cur_val = min_t(uint32_t, result->cur_val, max->cur); |
result->enable = true; |
} |
|
DRM_DEBUG_KMS("WM%d: %sabled\n", level, result->enable ? "en" : "dis"); |
|
return ret; |
} |
|
static void ilk_compute_wm_level(struct drm_i915_private *dev_priv, |
int level, |
struct hsw_pipe_wm_parameters *p, |
struct intel_wm_level *result) |
{ |
uint16_t pri_latency = dev_priv->wm.pri_latency[level]; |
uint16_t spr_latency = dev_priv->wm.spr_latency[level]; |
uint16_t cur_latency = dev_priv->wm.cur_latency[level]; |
|
/* WM1+ latency values stored in 0.5us units */ |
if (level > 0) { |
pri_latency *= 5; |
spr_latency *= 5; |
cur_latency *= 5; |
} |
|
result->pri_val = ilk_compute_pri_wm(p, pri_latency, level); |
result->spr_val = ilk_compute_spr_wm(p, spr_latency); |
result->cur_val = ilk_compute_cur_wm(p, cur_latency); |
result->fbc_val = ilk_compute_fbc_wm(p, result->pri_val); |
result->enable = true; |
} |
|
static bool hsw_compute_lp_wm(struct drm_i915_private *dev_priv, |
int level, struct hsw_wm_maximums *max, |
struct hsw_pipe_wm_parameters *params, |
struct intel_wm_level *result) |
{ |
enum pipe pipe; |
struct intel_wm_level res[3]; |
|
for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) |
ilk_compute_wm_level(dev_priv, level, ¶ms[pipe], &res[pipe]); |
|
result->pri_val = max3(res[0].pri_val, res[1].pri_val, res[2].pri_val); |
result->spr_val = max3(res[0].spr_val, res[1].spr_val, res[2].spr_val); |
result->cur_val = max3(res[0].cur_val, res[1].cur_val, res[2].cur_val); |
result->fbc_val = max3(res[0].fbc_val, res[1].fbc_val, res[2].fbc_val); |
result->enable = true; |
|
return ilk_check_wm(level, max, result); |
} |
|
static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv, |
enum pipe pipe, |
struct hsw_pipe_wm_parameters *params) |
{ |
uint32_t pri_val, cur_val, spr_val; |
/* WM0 latency values stored in 0.1us units */ |
uint16_t pri_latency = dev_priv->wm.pri_latency[0]; |
uint16_t spr_latency = dev_priv->wm.spr_latency[0]; |
uint16_t cur_latency = dev_priv->wm.cur_latency[0]; |
|
pri_val = ilk_compute_pri_wm(params, pri_latency, false); |
spr_val = ilk_compute_spr_wm(params, spr_latency); |
cur_val = ilk_compute_cur_wm(params, cur_latency); |
|
WARN(pri_val > 127, |
"Primary WM error, mode not supported for pipe %c\n", |
pipe_name(pipe)); |
WARN(spr_val > 127, |
"Sprite WM error, mode not supported for pipe %c\n", |
pipe_name(pipe)); |
WARN(cur_val > 63, |
"Cursor WM error, mode not supported for pipe %c\n", |
pipe_name(pipe)); |
|
return (pri_val << WM0_PIPE_PLANE_SHIFT) | |
(spr_val << WM0_PIPE_SPRITE_SHIFT) | |
cur_val; |
} |
|
static uint32_t |
hsw_compute_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 temp; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode; |
u32 linetime, ips_linetime; |
|
temp = I915_READ(PIPE_WM_LINETIME(pipe)); |
temp &= ~PIPE_WM_LINETIME_MASK; |
if (!intel_crtc_active(crtc)) |
return 0; |
|
/* The WM are computed with base on how long it takes to fill a single |
* row at the given clock rate, multiplied by 8. |
* */ |
temp |= PIPE_WM_LINETIME_TIME( |
((mode->crtc_hdisplay * 1000) / mode->clock) * 8); |
linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, mode->clock); |
ips_linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, |
intel_ddi_get_cdclk_freq(dev_priv)); |
|
/* IPS watermarks are only used by pipe A, and are ignored by |
* pipes B and C. They are calculated similarly to the common |
* linetime values, except that we are using CD clock frequency |
* in MHz instead of pixel rate for the division. |
* |
* This is a placeholder for the IPS watermark calculation code. |
return PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) | |
PIPE_WM_LINETIME_TIME(linetime); |
} |
|
static void intel_read_wm_latency(struct drm_device *dev, uint16_t wm[5]) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (IS_HASWELL(dev)) { |
uint64_t sskpd = I915_READ64(MCH_SSKPD); |
|
wm[0] = (sskpd >> 56) & 0xFF; |
if (wm[0] == 0) |
wm[0] = sskpd & 0xF; |
wm[1] = (sskpd >> 4) & 0xFF; |
wm[2] = (sskpd >> 12) & 0xFF; |
wm[3] = (sskpd >> 20) & 0x1FF; |
wm[4] = (sskpd >> 32) & 0x1FF; |
} else if (INTEL_INFO(dev)->gen >= 6) { |
uint32_t sskpd = I915_READ(MCH_SSKPD); |
|
wm[0] = (sskpd >> SSKPD_WM0_SHIFT) & SSKPD_WM_MASK; |
wm[1] = (sskpd >> SSKPD_WM1_SHIFT) & SSKPD_WM_MASK; |
wm[2] = (sskpd >> SSKPD_WM2_SHIFT) & SSKPD_WM_MASK; |
wm[3] = (sskpd >> SSKPD_WM3_SHIFT) & SSKPD_WM_MASK; |
} else if (INTEL_INFO(dev)->gen >= 5) { |
uint32_t mltr = I915_READ(MLTR_ILK); |
|
/* ILK primary LP0 latency is 700 ns */ |
wm[0] = 7; |
wm[1] = (mltr >> MLTR_WM1_SHIFT) & ILK_SRLT_MASK; |
wm[2] = (mltr >> MLTR_WM2_SHIFT) & ILK_SRLT_MASK; |
} |
} |
|
static void intel_fixup_spr_wm_latency(struct drm_device *dev, uint16_t wm[5]) |
{ |
/* ILK sprite LP0 latency is 1300 ns */ |
if (INTEL_INFO(dev)->gen == 5) |
wm[0] = 13; |
} |
|
static void intel_fixup_cur_wm_latency(struct drm_device *dev, uint16_t wm[5]) |
{ |
/* ILK cursor LP0 latency is 1300 ns */ |
if (INTEL_INFO(dev)->gen == 5) |
wm[0] = 13; |
|
/* WaDoubleCursorLP3Latency:ivb */ |
if (IS_IVYBRIDGE(dev)) |
wm[3] *= 2; |
} |
|
static void intel_print_wm_latency(struct drm_device *dev, |
const char *name, |
const uint16_t wm[5]) |
{ |
int level, max_level; |
|
/* how many WM levels are we expecting */ |
if (IS_HASWELL(dev)) |
max_level = 4; |
else if (INTEL_INFO(dev)->gen >= 6) |
max_level = 3; |
else |
max_level = 2; |
|
for (level = 0; level <= max_level; level++) { |
unsigned int latency = wm[level]; |
|
if (latency == 0) { |
DRM_ERROR("%s WM%d latency not provided\n", |
name, level); |
continue; |
} |
|
/* WM1+ latency values in 0.5us units */ |
if (level > 0) |
latency *= 5; |
|
DRM_DEBUG_KMS("%s WM%d latency %u (%u.%u usec)\n", |
name, level, wm[level], |
latency / 10, latency % 10); |
} |
} |
|
static void intel_setup_wm_latency(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
intel_read_wm_latency(dev, dev_priv->wm.pri_latency); |
|
memcpy(dev_priv->wm.spr_latency, dev_priv->wm.pri_latency, |
sizeof(dev_priv->wm.pri_latency)); |
memcpy(dev_priv->wm.cur_latency, dev_priv->wm.pri_latency, |
sizeof(dev_priv->wm.pri_latency)); |
|
intel_fixup_spr_wm_latency(dev, dev_priv->wm.spr_latency); |
intel_fixup_cur_wm_latency(dev, dev_priv->wm.cur_latency); |
|
intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency); |
intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency); |
intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency); |
} |
|
static void hsw_compute_wm_parameters(struct drm_device *dev, |
struct hsw_pipe_wm_parameters *params, |
struct hsw_wm_maximums *lp_max_1_2, |
struct hsw_wm_maximums *lp_max_5_6) |
{ |
struct drm_crtc *crtc; |
struct drm_plane *plane; |
enum pipe pipe; |
struct intel_wm_config config = {}; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct hsw_pipe_wm_parameters *p; |
|
pipe = intel_crtc->pipe; |
p = ¶ms[pipe]; |
|
p->active = intel_crtc_active(crtc); |
if (!p->active) |
continue; |
|
config.num_pipes_active++; |
|
p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal; |
p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc); |
p->pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8; |
p->cur.bytes_per_pixel = 4; |
p->pri.horiz_pixels = |
intel_crtc->config.requested_mode.hdisplay; |
p->cur.horiz_pixels = 64; |
/* TODO: for now, assume primary and cursor planes are always enabled. */ |
p->pri.enabled = true; |
p->cur.enabled = true; |
} |
|
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { |
struct intel_plane *intel_plane = to_intel_plane(plane); |
struct hsw_pipe_wm_parameters *p; |
|
pipe = intel_plane->pipe; |
p = ¶ms[pipe]; |
|
p->spr = intel_plane->wm; |
|
config.sprites_enabled |= p->spr.enabled; |
config.sprites_scaled |= p->spr.scaled; |
} |
|
ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, lp_max_1_2); |
|
/* 5/6 split only in single pipe config on IVB+ */ |
if (INTEL_INFO(dev)->gen >= 7 && config.num_pipes_active <= 1) |
ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, lp_max_5_6); |
else |
*lp_max_5_6 = *lp_max_1_2; |
} |
|
static void hsw_compute_wm_results(struct drm_device *dev, |
struct hsw_pipe_wm_parameters *params, |
struct hsw_wm_maximums *lp_maximums, |
struct hsw_wm_values *results) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_crtc *crtc; |
struct intel_wm_level lp_results[4] = {}; |
enum pipe pipe; |
int level, max_level, wm_lp; |
|
for (level = 1; level <= 4; level++) |
if (!hsw_compute_lp_wm(dev_priv, level, |
lp_maximums, params, |
&lp_results[level - 1])) |
break; |
max_level = level - 1; |
|
memset(results, 0, sizeof(*results)); |
|
/* The spec says it is preferred to disable FBC WMs instead of disabling |
* a WM level. */ |
results->enable_fbc_wm = true; |
for (level = 1; level <= max_level; level++) { |
if (lp_results[level - 1].fbc_val > lp_maximums->fbc) { |
results->enable_fbc_wm = false; |
lp_results[level - 1].fbc_val = 0; |
} |
} |
|
for (wm_lp = 1; wm_lp <= 3; wm_lp++) { |
const struct intel_wm_level *r; |
|
level = (max_level == 4 && wm_lp > 1) ? wm_lp + 1 : wm_lp; |
if (level > max_level) |
break; |
|
r = &lp_results[level - 1]; |
results->wm_lp[wm_lp - 1] = HSW_WM_LP_VAL(level * 2, |
r->fbc_val, |
r->pri_val, |
r->cur_val); |
results->wm_lp_spr[wm_lp - 1] = r->spr_val; |
} |
|
for_each_pipe(pipe) |
results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, pipe, |
¶ms[pipe]); |
|
for_each_pipe(pipe) { |
crtc = dev_priv->pipe_to_crtc_mapping[pipe]; |
results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc); |
} |
} |
|
/* Find the result with the highest level enabled. Check for enable_fbc_wm in |
* case both are at the same level. Prefer r1 in case they're the same. */ |
static struct hsw_wm_values *hsw_find_best_result(struct hsw_wm_values *r1, |
struct hsw_wm_values *r2) |
{ |
int i, val_r1 = 0, val_r2 = 0; |
|
for (i = 0; i < 3; i++) { |
if (r1->wm_lp[i] & WM3_LP_EN) |
val_r1 = r1->wm_lp[i] & WM1_LP_LATENCY_MASK; |
if (r2->wm_lp[i] & WM3_LP_EN) |
val_r2 = r2->wm_lp[i] & WM1_LP_LATENCY_MASK; |
} |
|
if (val_r1 == val_r2) { |
if (r2->enable_fbc_wm && !r1->enable_fbc_wm) |
return r2; |
else |
return r1; |
} else if (val_r1 > val_r2) { |
return r1; |
} else { |
return r2; |
} |
} |
|
/* |
* The spec says we shouldn't write when we don't need, because every write |
* causes WMs to be re-evaluated, expending some power. |
*/ |
static void hsw_write_wm_values(struct drm_i915_private *dev_priv, |
struct hsw_wm_values *results, |
enum intel_ddb_partitioning partitioning) |
{ |
struct hsw_wm_values previous; |
uint32_t val; |
enum intel_ddb_partitioning prev_partitioning; |
bool prev_enable_fbc_wm; |
|
I915_WRITE(PIPE_WM_LINETIME(pipe), temp); |
previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK); |
previous.wm_pipe[1] = I915_READ(WM0_PIPEB_ILK); |
previous.wm_pipe[2] = I915_READ(WM0_PIPEC_IVB); |
previous.wm_lp[0] = I915_READ(WM1_LP_ILK); |
previous.wm_lp[1] = I915_READ(WM2_LP_ILK); |
previous.wm_lp[2] = I915_READ(WM3_LP_ILK); |
previous.wm_lp_spr[0] = I915_READ(WM1S_LP_ILK); |
previous.wm_lp_spr[1] = I915_READ(WM2S_LP_IVB); |
previous.wm_lp_spr[2] = I915_READ(WM3S_LP_IVB); |
previous.wm_linetime[0] = I915_READ(PIPE_WM_LINETIME(PIPE_A)); |
previous.wm_linetime[1] = I915_READ(PIPE_WM_LINETIME(PIPE_B)); |
previous.wm_linetime[2] = I915_READ(PIPE_WM_LINETIME(PIPE_C)); |
|
prev_partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ? |
INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2; |
|
prev_enable_fbc_wm = !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS); |
|
if (memcmp(results->wm_pipe, previous.wm_pipe, |
sizeof(results->wm_pipe)) == 0 && |
memcmp(results->wm_lp, previous.wm_lp, |
sizeof(results->wm_lp)) == 0 && |
memcmp(results->wm_lp_spr, previous.wm_lp_spr, |
sizeof(results->wm_lp_spr)) == 0 && |
memcmp(results->wm_linetime, previous.wm_linetime, |
sizeof(results->wm_linetime)) == 0 && |
partitioning == prev_partitioning && |
results->enable_fbc_wm == prev_enable_fbc_wm) |
return; |
|
if (previous.wm_lp[2] != 0) |
I915_WRITE(WM3_LP_ILK, 0); |
if (previous.wm_lp[1] != 0) |
I915_WRITE(WM2_LP_ILK, 0); |
if (previous.wm_lp[0] != 0) |
I915_WRITE(WM1_LP_ILK, 0); |
|
if (previous.wm_pipe[0] != results->wm_pipe[0]) |
I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]); |
if (previous.wm_pipe[1] != results->wm_pipe[1]) |
I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]); |
if (previous.wm_pipe[2] != results->wm_pipe[2]) |
I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]); |
|
if (previous.wm_linetime[0] != results->wm_linetime[0]) |
I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]); |
if (previous.wm_linetime[1] != results->wm_linetime[1]) |
I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]); |
if (previous.wm_linetime[2] != results->wm_linetime[2]) |
I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]); |
|
if (prev_partitioning != partitioning) { |
val = I915_READ(WM_MISC); |
if (partitioning == INTEL_DDB_PART_1_2) |
val &= ~WM_MISC_DATA_PARTITION_5_6; |
else |
val |= WM_MISC_DATA_PARTITION_5_6; |
I915_WRITE(WM_MISC, val); |
} |
|
if (prev_enable_fbc_wm != results->enable_fbc_wm) { |
val = I915_READ(DISP_ARB_CTL); |
if (results->enable_fbc_wm) |
val &= ~DISP_FBC_WM_DIS; |
else |
val |= DISP_FBC_WM_DIS; |
I915_WRITE(DISP_ARB_CTL, val); |
} |
|
if (previous.wm_lp_spr[0] != results->wm_lp_spr[0]) |
I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]); |
if (previous.wm_lp_spr[1] != results->wm_lp_spr[1]) |
I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]); |
if (previous.wm_lp_spr[2] != results->wm_lp_spr[2]) |
I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]); |
|
if (results->wm_lp[0] != 0) |
I915_WRITE(WM1_LP_ILK, results->wm_lp[0]); |
if (results->wm_lp[1] != 0) |
I915_WRITE(WM2_LP_ILK, results->wm_lp[1]); |
if (results->wm_lp[2] != 0) |
I915_WRITE(WM3_LP_ILK, results->wm_lp[2]); |
} |
|
static void haswell_update_wm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct hsw_wm_maximums lp_max_1_2, lp_max_5_6; |
struct hsw_pipe_wm_parameters params[3]; |
struct hsw_wm_values results_1_2, results_5_6, *best_results; |
enum intel_ddb_partitioning partitioning; |
|
hsw_compute_wm_parameters(dev, params, &lp_max_1_2, &lp_max_5_6); |
|
hsw_compute_wm_results(dev, params, |
&lp_max_1_2, &results_1_2); |
if (lp_max_1_2.pri != lp_max_5_6.pri) { |
hsw_compute_wm_results(dev, params, |
&lp_max_5_6, &results_5_6); |
best_results = hsw_find_best_result(&results_1_2, &results_5_6); |
} else { |
best_results = &results_1_2; |
} |
|
partitioning = (best_results == &results_1_2) ? |
INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6; |
|
hsw_write_wm_values(dev_priv, best_results, partitioning); |
} |
|
static void haswell_update_sprite_wm(struct drm_plane *plane, |
struct drm_crtc *crtc, |
uint32_t sprite_width, int pixel_size, |
bool enabled, bool scaled) |
{ |
struct intel_plane *intel_plane = to_intel_plane(plane); |
|
intel_plane->wm.enabled = enabled; |
intel_plane->wm.scaled = scaled; |
intel_plane->wm.horiz_pixels = sprite_width; |
intel_plane->wm.bytes_per_pixel = pixel_size; |
|
haswell_update_wm(plane->dev); |
} |
|
static bool |
sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, |
uint32_t sprite_width, int pixel_size, |
2140,15 → 2968,22 |
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) |
static void sandybridge_update_sprite_wm(struct drm_plane *plane, |
struct drm_crtc *crtc, |
uint32_t sprite_width, int pixel_size, |
bool enabled, bool scaled) |
{ |
struct drm_device *dev = plane->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */ |
int pipe = to_intel_plane(plane)->pipe; |
int latency = dev_priv->wm.spr_latency[0] * 100; /* In unit 0.1us */ |
u32 val; |
int sprite_wm, reg; |
int ret; |
|
if (!enabled) |
return; |
|
switch (pipe) { |
case 0: |
reg = WM0_PIPEA_ILK; |
2167,8 → 3002,8 |
&sandybridge_display_wm_info, |
latency, &sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n", |
pipe); |
DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n", |
pipe_name(pipe)); |
return; |
} |
|
2175,17 → 3010,17 |
val = I915_READ(reg); |
val &= ~WM0_PIPE_SPRITE_MASK; |
I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT)); |
DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm); |
DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm); |
|
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM1_LATENCY() * 500, |
dev_priv->wm.spr_latency[1] * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n", |
pipe); |
DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n", |
pipe_name(pipe)); |
return; |
} |
I915_WRITE(WM1S_LP_ILK, sprite_wm); |
2197,11 → 3032,11 |
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM2_LATENCY() * 500, |
dev_priv->wm.spr_latency[2] * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n", |
pipe); |
DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n", |
pipe_name(pipe)); |
return; |
} |
I915_WRITE(WM2S_LP_IVB, sprite_wm); |
2209,11 → 3044,11 |
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, |
pixel_size, |
&sandybridge_display_srwm_info, |
SNB_READ_WM3_LATENCY() * 500, |
dev_priv->wm.spr_latency[3] * 500, |
&sprite_wm); |
if (!ret) { |
DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n", |
pipe); |
DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n", |
pipe_name(pipe)); |
return; |
} |
I915_WRITE(WM3S_LP_IVB, sprite_wm); |
2259,23 → 3094,16 |
dev_priv->display.update_wm(dev); |
} |
|
void intel_update_linetime_watermarks(struct drm_device *dev, |
int pipe, struct drm_display_mode *mode) |
void intel_update_sprite_watermarks(struct drm_plane *plane, |
struct drm_crtc *crtc, |
uint32_t sprite_width, int pixel_size, |
bool enabled, bool scaled) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_i915_private *dev_priv = plane->dev->dev_private; |
|
if (dev_priv->display.update_linetime_wm) |
dev_priv->display.update_linetime_wm(dev, pipe, mode); |
} |
|
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); |
dev_priv->display.update_sprite_wm(plane, crtc, sprite_width, |
pixel_size, enabled, scaled); |
} |
|
static struct drm_i915_gem_object * |
2292,7 → 3120,7 |
return NULL; |
} |
|
ret = i915_gem_object_pin(ctx, 4096, true, false); |
ret = i915_gem_obj_ggtt_pin(ctx, 4096, true, false); |
if (ret) { |
DRM_ERROR("failed to pin power context: %d\n", ret); |
goto err_unref; |
2502,28 → 3330,107 |
trace_intel_gpu_freq_change(val * 50); |
} |
|
static void gen6_disable_rps(struct drm_device *dev) |
/* |
* Wait until the previous freq change has completed, |
* or the timeout elapsed, and then update our notion |
* of the current GPU frequency. |
*/ |
static void vlv_update_rps_cur_delay(struct drm_i915_private *dev_priv) |
{ |
u32 pval; |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
|
if (wait_for(((pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS)) & GENFREQSTATUS) == 0, 10)) |
DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); |
|
pval >>= 8; |
|
if (pval != dev_priv->rps.cur_delay) |
DRM_DEBUG_DRIVER("Punit overrode GPU freq: %d MHz (%u) requested, but got %d Mhz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay), |
dev_priv->rps.cur_delay, |
vlv_gpu_freq(dev_priv->mem_freq, pval), pval); |
|
dev_priv->rps.cur_delay = pval; |
} |
|
void valleyview_set_rps(struct drm_device *dev, u8 val) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(GEN6_RC_CONTROL, 0); |
I915_WRITE(GEN6_RPNSWREQ, 1 << 31); |
gen6_rps_limits(dev_priv, &val); |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
WARN_ON(val > dev_priv->rps.max_delay); |
WARN_ON(val < dev_priv->rps.min_delay); |
|
vlv_update_rps_cur_delay(dev_priv); |
|
DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.cur_delay), |
dev_priv->rps.cur_delay, |
vlv_gpu_freq(dev_priv->mem_freq, val), val); |
|
if (val == dev_priv->rps.cur_delay) |
return; |
|
vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); |
|
dev_priv->rps.cur_delay = val; |
|
trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val)); |
} |
|
static void gen6_disable_rps_interrupts(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); |
I915_WRITE(GEN6_PMIER, 0); |
I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) & ~GEN6_PM_RPS_EVENTS); |
/* Complete PM interrupt masking here doesn't race with the rps work |
* item again unmasking PM interrupts because that is using a different |
* register (PMIMR) to mask PM interrupts. The only risk is in leaving |
* stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ |
|
spin_lock_irq(&dev_priv->rps.lock); |
spin_lock_irq(&dev_priv->irq_lock); |
dev_priv->rps.pm_iir = 0; |
spin_unlock_irq(&dev_priv->rps.lock); |
spin_unlock_irq(&dev_priv->irq_lock); |
|
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); |
I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS); |
} |
|
static void gen6_disable_rps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(GEN6_RC_CONTROL, 0); |
I915_WRITE(GEN6_RPNSWREQ, 1 << 31); |
|
gen6_disable_rps_interrupts(dev); |
} |
|
static void valleyview_disable_rps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(GEN6_RC_CONTROL, 0); |
|
gen6_disable_rps_interrupts(dev); |
|
if (dev_priv->vlv_pctx) { |
drm_gem_object_unreference(&dev_priv->vlv_pctx->base); |
dev_priv->vlv_pctx = NULL; |
} |
} |
|
int intel_enable_rc6(const struct drm_device *dev) |
{ |
/* No RC6 before Ironlake */ |
if (INTEL_INFO(dev)->gen < 5) |
return 0; |
|
/* Respect the kernel parameter if it is set */ |
if (i915_enable_rc6 >= 0) |
return i915_enable_rc6; |
2547,6 → 3454,29 |
return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE); |
} |
|
static void gen6_enable_rps_interrupts(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 enabled_intrs; |
|
spin_lock_irq(&dev_priv->irq_lock); |
WARN_ON(dev_priv->rps.pm_iir); |
snb_enable_pm_irq(dev_priv, GEN6_PM_RPS_EVENTS); |
I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS); |
spin_unlock_irq(&dev_priv->irq_lock); |
|
/* only unmask PM interrupts we need. Mask all others. */ |
enabled_intrs = GEN6_PM_RPS_EVENTS; |
|
/* IVB and SNB hard hangs on looping batchbuffer |
* if GEN6_PM_UP_EI_EXPIRED is masked. |
*/ |
if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) |
enabled_intrs |= GEN6_PM_RP_UP_EI_EXPIRED; |
|
I915_WRITE(GEN6_PMINTRMSK, ~enabled_intrs); |
} |
|
static void gen6_enable_rps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
2598,6 → 3528,9 |
|
I915_WRITE(GEN6_RC_SLEEP, 0); |
I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); |
if (INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) |
I915_WRITE(GEN6_RC6_THRESHOLD, 125000); |
else |
I915_WRITE(GEN6_RC6_THRESHOLD, 50000); |
I915_WRITE(GEN6_RC6p_THRESHOLD, 150000); |
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ |
2675,14 → 3608,7 |
|
gen6_set_rps(dev_priv->dev, (gt_perf_status & 0xff00) >> 8); |
|
/* requires MSI enabled */ |
I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS); |
spin_lock_irq(&dev_priv->rps.lock); |
WARN_ON(dev_priv->rps.pm_iir != 0); |
I915_WRITE(GEN6_PMIMR, 0); |
spin_unlock_irq(&dev_priv->rps.lock); |
/* enable all PM interrupts */ |
I915_WRITE(GEN6_PMINTRMSK, 0); |
gen6_enable_rps_interrupts(dev); |
|
rc6vids = 0; |
ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids); |
2701,7 → 3627,7 |
gen6_gt_force_wake_put(dev_priv); |
} |
|
static void gen6_update_ring_freq(struct drm_device *dev) |
void gen6_update_ring_freq(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int min_freq = 15; |
2763,6 → 3689,198 |
} |
} |
|
int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) |
{ |
u32 val, rp0; |
|
val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE); |
|
rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT; |
/* Clamp to max */ |
rp0 = min_t(u32, rp0, 0xea); |
|
return rp0; |
} |
|
static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) |
{ |
u32 val, rpe; |
|
val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO); |
rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT; |
val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI); |
rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5; |
|
return rpe; |
} |
|
int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) |
{ |
return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff; |
} |
|
static void vlv_rps_timer_work(struct work_struct *work) |
{ |
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, |
rps.vlv_work.work); |
|
/* |
* Timer fired, we must be idle. Drop to min voltage state. |
* Note: we use RPe here since it should match the |
* Vmin we were shooting for. That should give us better |
* perf when we come back out of RC6 than if we used the |
* min freq available. |
*/ |
mutex_lock(&dev_priv->rps.hw_lock); |
if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay) |
valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); |
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
|
static void valleyview_setup_pctx(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_i915_gem_object *pctx; |
unsigned long pctx_paddr; |
u32 pcbr; |
int pctx_size = 24*1024; |
|
pcbr = I915_READ(VLV_PCBR); |
if (pcbr) { |
/* BIOS set it up already, grab the pre-alloc'd space */ |
int pcbr_offset; |
|
pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; |
pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev, |
pcbr_offset, |
I915_GTT_OFFSET_NONE, |
pctx_size); |
goto out; |
} |
|
/* |
* From the Gunit register HAS: |
* The Gfx driver is expected to program this register and ensure |
* proper allocation within Gfx stolen memory. For example, this |
* register should be programmed such than the PCBR range does not |
* overlap with other ranges, such as the frame buffer, protected |
* memory, or any other relevant ranges. |
*/ |
pctx = i915_gem_object_create_stolen(dev, pctx_size); |
if (!pctx) { |
DRM_DEBUG("not enough stolen space for PCTX, disabling\n"); |
return; |
} |
|
pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start; |
I915_WRITE(VLV_PCBR, pctx_paddr); |
|
out: |
dev_priv->vlv_pctx = pctx; |
} |
|
static void valleyview_enable_rps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_ring_buffer *ring; |
u32 gtfifodbg, val; |
int i; |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
|
if ((gtfifodbg = I915_READ(GTFIFODBG))) { |
DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg); |
I915_WRITE(GTFIFODBG, gtfifodbg); |
} |
|
valleyview_setup_pctx(dev); |
|
gen6_gt_force_wake_get(dev_priv); |
|
I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000); |
I915_WRITE(GEN6_RP_UP_EI, 66000); |
I915_WRITE(GEN6_RP_DOWN_EI, 350000); |
|
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); |
|
I915_WRITE(GEN6_RP_CONTROL, |
GEN6_RP_MEDIA_TURBO | |
GEN6_RP_MEDIA_HW_NORMAL_MODE | |
GEN6_RP_MEDIA_IS_GFX | |
GEN6_RP_ENABLE | |
GEN6_RP_UP_BUSY_AVG | |
GEN6_RP_DOWN_IDLE_CONT); |
|
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 0x00280000); |
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); |
I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); |
|
for_each_ring(ring, dev_priv, i) |
I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); |
|
I915_WRITE(GEN6_RC6_THRESHOLD, 0xc350); |
|
/* allows RC6 residency counter to work */ |
I915_WRITE(0x138104, _MASKED_BIT_ENABLE(0x3)); |
I915_WRITE(GEN6_RC_CONTROL, |
GEN7_RC_CTL_TO_MODE); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); |
switch ((val >> 6) & 3) { |
case 0: |
case 1: |
dev_priv->mem_freq = 800; |
break; |
case 2: |
dev_priv->mem_freq = 1066; |
break; |
case 3: |
dev_priv->mem_freq = 1333; |
break; |
} |
DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); |
|
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); |
DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); |
|
dev_priv->rps.cur_delay = (val >> 8) & 0xff; |
DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.cur_delay), |
dev_priv->rps.cur_delay); |
|
dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv); |
dev_priv->rps.hw_max = dev_priv->rps.max_delay; |
DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.max_delay), |
dev_priv->rps.max_delay); |
|
dev_priv->rps.rpe_delay = valleyview_rps_rpe_freq(dev_priv); |
DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.rpe_delay), |
dev_priv->rps.rpe_delay); |
|
dev_priv->rps.min_delay = valleyview_rps_min_freq(dev_priv); |
DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.min_delay), |
dev_priv->rps.min_delay); |
|
DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv->mem_freq, |
dev_priv->rps.rpe_delay), |
dev_priv->rps.rpe_delay); |
|
valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); |
|
gen6_enable_rps_interrupts(dev); |
|
gen6_gt_force_wake_put(dev_priv); |
} |
|
void ironlake_teardown_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
2852,7 → 3970,7 |
|
intel_ring_emit(ring, MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); |
intel_ring_emit(ring, MI_SET_CONTEXT); |
intel_ring_emit(ring, dev_priv->ips.renderctx->gtt_offset | |
intel_ring_emit(ring, i915_gem_obj_ggtt_offset(dev_priv->ips.renderctx) | |
MI_MM_SPACE_GTT | |
MI_SAVE_EXT_STATE_EN | |
MI_RESTORE_EXT_STATE_EN | |
2875,7 → 3993,7 |
return; |
} |
|
I915_WRITE(PWRCTXA, dev_priv->ips.pwrctx->gtt_offset | PWRCTX_EN); |
I915_WRITE(PWRCTXA, i915_gem_obj_ggtt_offset(dev_priv->ips.pwrctx) | PWRCTX_EN); |
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); |
} |
|
3486,6 → 4604,9 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* Interrupts should be disabled already to avoid re-arming. */ |
WARN_ON(dev->irq_enabled); |
|
if (IS_IRONLAKE_M(dev)) { |
ironlake_disable_drps(dev); |
ironlake_disable_rc6(dev); |
3492,6 → 4613,9 |
} else if (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(dev)) { |
// cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); |
mutex_lock(&dev_priv->rps.hw_lock); |
if (IS_VALLEYVIEW(dev)) |
valleyview_disable_rps(dev); |
else |
gen6_disable_rps(dev); |
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
3507,8 → 4631,13 |
ENTER(); |
|
mutex_lock(&dev_priv->rps.hw_lock); |
|
if (IS_VALLEYVIEW(dev)) { |
valleyview_enable_rps(dev); |
} else { |
gen6_enable_rps(dev); |
gen6_update_ring_freq(dev); |
} |
mutex_unlock(&dev_priv->rps.hw_lock); |
|
LEAVE(); |
3522,7 → 4651,7 |
ironlake_enable_drps(dev); |
ironlake_enable_rc6(dev); |
intel_init_emon(dev); |
} else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) { |
} else if (IS_GEN6(dev) || IS_GEN7(dev)) { |
/* |
* PCU communication is slow and this doesn't need to be |
* done at any specific time, so do this out of our fast path |
3545,12 → 4674,28 |
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); |
} |
|
static void g4x_disable_trickle_feed(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
} |
} |
|
static void ironlake_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; |
|
/* Required for FBC */ |
/* |
* Required for FBC |
* WaFbcDisableDpfcClockGating:ilk |
*/ |
dspclk_gate |= ILK_DPFCRUNIT_CLOCK_GATE_DISABLE | |
ILK_DPFCUNIT_CLOCK_GATE_DISABLE | |
ILK_DPFDUNIT_CLOCK_GATE_ENABLE; |
3587,6 → 4732,7 |
* The bit 7,8,9 of 0x42020. |
*/ |
if (IS_IRONLAKE_M(dev)) { |
/* WaFbcAsynchFlipDisableFbcQueue:ilk */ |
I915_WRITE(ILK_DISPLAY_CHICKEN1, |
I915_READ(ILK_DISPLAY_CHICKEN1) | |
ILK_FBCQ_DIS); |
3604,10 → 4750,12 |
_3D_CHICKEN2_WM_READ_PIPELINED << 16 | |
_3D_CHICKEN2_WM_READ_PIPELINED); |
|
/* WaDisableRenderCachePipelinedFlush */ |
/* WaDisableRenderCachePipelinedFlush:ilk */ |
I915_WRITE(CACHE_MODE_0, |
_MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); |
|
g4x_disable_trickle_feed(dev); |
|
ibx_init_clock_gating(dev); |
} |
|
3632,7 → 4780,7 |
val = I915_READ(TRANS_CHICKEN2(pipe)); |
val |= TRANS_CHICKEN2_TIMING_OVERRIDE; |
val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; |
if (dev_priv->fdi_rx_polarity_inverted) |
if (dev_priv->vbt.fdi_rx_polarity_inverted) |
val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; |
val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; |
val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; |
3662,7 → 4810,6 |
static void gen6_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; |
|
I915_WRITE(ILK_DSPCLK_GATE_D, dspclk_gate); |
3671,11 → 4818,11 |
I915_READ(ILK_DISPLAY_CHICKEN2) | |
ILK_ELPIN_409_SELECT); |
|
/* WaDisableHiZPlanesWhenMSAAEnabled */ |
/* WaDisableHiZPlanesWhenMSAAEnabled:snb */ |
I915_WRITE(_3D_CHICKEN, |
_MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB)); |
|
/* WaSetupGtModeTdRowDispatch */ |
/* WaSetupGtModeTdRowDispatch:snb */ |
if (IS_SNB_GT1(dev)) |
I915_WRITE(GEN6_GT_MODE, |
_MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE)); |
3702,8 → 4849,8 |
* According to the spec, bit 11 (RCCUNIT) must also be set, |
* but we didn't debug actual testcases to find it out. |
* |
* Also apply WaDisableVDSUnitClockGating and |
* WaDisableRCPBUnitClockGating. |
* Also apply WaDisableVDSUnitClockGating:snb and |
* WaDisableRCPBUnitClockGating:snb. |
*/ |
I915_WRITE(GEN6_UCGCTL2, |
GEN7_VDSUNIT_CLOCK_GATE_DISABLE | |
3722,6 → 4869,8 |
* The bit5 and bit7 of 0x42020 |
* The bit14 of 0x70180 |
* The bit14 of 0x71180 |
* |
* WaFbcAsynchFlipDisableFbcQueue:snb |
*/ |
I915_WRITE(ILK_DISPLAY_CHICKEN1, |
I915_READ(ILK_DISPLAY_CHICKEN1) | |
3734,17 → 4883,8 |
ILK_DPARBUNIT_CLOCK_GATE_ENABLE | |
ILK_DPFDUNIT_CLOCK_GATE_ENABLE); |
|
/* WaMbcDriverBootEnable */ |
I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | |
GEN6_MBCTL_ENABLE_BOOT_FETCH); |
g4x_disable_trickle_feed(dev); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
} |
|
/* The default value should be 0x200 according to docs, but the two |
* platforms I checked have a 0 for this. (Maybe BIOS overrides?) */ |
I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_DISABLE(0xffff)); |
3764,7 → 4904,6 |
reg |= GEN7_FF_VS_SCHED_HW; |
reg |= GEN7_FF_DS_SCHED_HW; |
|
/* WaVSRefCountFullforceMissDisable */ |
if (IS_HASWELL(dev_priv->dev)) |
reg &= ~GEN7_FF_VS_REF_CNT_FFME; |
|
3783,12 → 4922,28 |
I915_WRITE(SOUTH_DSPCLK_GATE_D, |
I915_READ(SOUTH_DSPCLK_GATE_D) | |
PCH_LP_PARTITION_LEVEL_DISABLE); |
|
/* WADPOClockGatingDisable:hsw */ |
I915_WRITE(_TRANSA_CHICKEN1, |
I915_READ(_TRANSA_CHICKEN1) | |
TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); |
} |
|
static void lpt_suspend_hw(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { |
uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D); |
|
val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; |
I915_WRITE(SOUTH_DSPCLK_GATE_D, val); |
} |
} |
|
static void haswell_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
|
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
3795,53 → 4950,43 |
I915_WRITE(WM1_LP_ILK, 0); |
|
/* According to the spec, bit 13 (RCZUNIT) must be set on IVB. |
* This implements the WaDisableRCZUnitClockGating workaround. |
* This implements the WaDisableRCZUnitClockGating:hsw workaround. |
*/ |
I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); |
|
/* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ |
/* Apply the WaDisableRHWOOptimizationForRenderHang:hsw workaround. */ |
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, |
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); |
|
/* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ |
/* WaApplyL3ControlAndL3ChickenMode:hsw */ |
I915_WRITE(GEN7_L3CNTLREG1, |
GEN7_WA_FOR_GEN7_L3_CONTROL); |
I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, |
GEN7_WA_L3_CHICKEN_MODE); |
|
/* This is required by WaCatErrorRejectionIssue */ |
/* L3 caching of data atomics doesn't work -- disable it. */ |
I915_WRITE(HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE); |
I915_WRITE(HSW_ROW_CHICKEN3, |
_MASKED_BIT_ENABLE(HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE)); |
|
/* This is required by WaCatErrorRejectionIssue:hsw */ |
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, |
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | |
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
} |
|
/* WaVSRefCountFullforceMissDisable:hsw */ |
gen7_setup_fixed_func_scheduler(dev_priv); |
|
/* WaDisable4x2SubspanOptimization */ |
/* WaDisable4x2SubspanOptimization:hsw */ |
I915_WRITE(CACHE_MODE_1, |
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); |
|
/* WaMbcDriverBootEnable */ |
I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | |
GEN6_MBCTL_ENABLE_BOOT_FETCH); |
|
/* WaSwitchSolVfFArbitrationPriority */ |
/* WaSwitchSolVfFArbitrationPriority:hsw */ |
I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); |
|
/* XXX: This is a workaround for early silicon revisions and should be |
* removed later. |
*/ |
I915_WRITE(WM_DBG, |
I915_READ(WM_DBG) | |
WM_DBG_DISALLOW_MULTIPLE_LP | |
WM_DBG_DISALLOW_SPRITE | |
WM_DBG_DISALLOW_MAXFIFO); |
/* WaRsPkgCStateDisplayPMReq:hsw */ |
I915_WRITE(CHICKEN_PAR1_1, |
I915_READ(CHICKEN_PAR1_1) | FORCE_ARB_IDLE_PLANES); |
|
lpt_init_clock_gating(dev); |
} |
3849,7 → 4994,6 |
static void ivybridge_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
uint32_t snpcr; |
|
I915_WRITE(WM3_LP_ILK, 0); |
3858,16 → 5002,16 |
|
I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); |
|
/* WaDisableEarlyCull */ |
/* WaDisableEarlyCull:ivb */ |
I915_WRITE(_3D_CHICKEN3, |
_MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); |
|
/* WaDisableBackToBackFlipFix */ |
/* WaDisableBackToBackFlipFix:ivb */ |
I915_WRITE(IVB_CHICKEN3, |
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | |
CHICKEN3_DGMG_DONE_FIX_DISABLE); |
|
/* WaDisablePSDDualDispatchEnable */ |
/* WaDisablePSDDualDispatchEnable:ivb */ |
if (IS_IVB_GT1(dev)) |
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, |
_MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); |
3875,11 → 5019,11 |
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1_GT2, |
_MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); |
|
/* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ |
/* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */ |
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, |
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); |
|
/* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ |
/* WaApplyL3ControlAndL3ChickenMode:ivb */ |
I915_WRITE(GEN7_L3CNTLREG1, |
GEN7_WA_FOR_GEN7_L3_CONTROL); |
I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, |
3892,7 → 5036,7 |
_MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); |
|
|
/* WaForceL3Serialization */ |
/* WaForceL3Serialization:ivb */ |
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & |
~L3SQ_URB_READ_CAM_MATCH_DISABLE); |
|
3907,31 → 5051,23 |
* but we didn't debug actual testcases to find it out. |
* |
* According to the spec, bit 13 (RCZUNIT) must be set on IVB. |
* This implements the WaDisableRCZUnitClockGating workaround. |
* This implements the WaDisableRCZUnitClockGating:ivb workaround. |
*/ |
I915_WRITE(GEN6_UCGCTL2, |
GEN6_RCZUNIT_CLOCK_GATE_DISABLE | |
GEN6_RCCUNIT_CLOCK_GATE_DISABLE); |
|
/* This is required by WaCatErrorRejectionIssue */ |
/* This is required by WaCatErrorRejectionIssue:ivb */ |
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, |
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | |
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
} |
g4x_disable_trickle_feed(dev); |
|
/* WaMbcDriverBootEnable */ |
I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | |
GEN6_MBCTL_ENABLE_BOOT_FETCH); |
|
/* WaVSRefCountFullforceMissDisable:ivb */ |
gen7_setup_fixed_func_scheduler(dev_priv); |
|
/* WaDisable4x2SubspanOptimization */ |
/* WaDisable4x2SubspanOptimization:ivb */ |
I915_WRITE(CACHE_MODE_1, |
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); |
|
3949,58 → 5085,44 |
static void valleyview_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
|
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); |
|
I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); |
|
/* WaDisableEarlyCull */ |
/* WaDisableEarlyCull:vlv */ |
I915_WRITE(_3D_CHICKEN3, |
_MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); |
|
/* WaDisableBackToBackFlipFix */ |
/* WaDisableBackToBackFlipFix:vlv */ |
I915_WRITE(IVB_CHICKEN3, |
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | |
CHICKEN3_DGMG_DONE_FIX_DISABLE); |
|
/* WaDisablePSDDualDispatchEnable */ |
/* WaDisablePSDDualDispatchEnable:vlv */ |
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, |
_MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP | |
GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); |
|
/* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ |
/* Apply the WaDisableRHWOOptimizationForRenderHang:vlv workaround. */ |
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, |
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); |
|
/* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ |
/* WaApplyL3ControlAndL3ChickenMode:vlv */ |
I915_WRITE(GEN7_L3CNTLREG1, I915_READ(GEN7_L3CNTLREG1) | GEN7_L3AGDIS); |
I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); |
|
/* WaForceL3Serialization */ |
/* WaForceL3Serialization:vlv */ |
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & |
~L3SQ_URB_READ_CAM_MATCH_DISABLE); |
|
/* WaDisableDopClockGating */ |
/* WaDisableDopClockGating:vlv */ |
I915_WRITE(GEN7_ROW_CHICKEN2, |
_MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); |
|
/* WaForceL3Serialization */ |
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & |
~L3SQ_URB_READ_CAM_MATCH_DISABLE); |
|
/* This is required by WaCatErrorRejectionIssue */ |
/* This is required by WaCatErrorRejectionIssue:vlv */ |
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, |
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | |
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); |
|
/* WaMbcDriverBootEnable */ |
I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | |
GEN6_MBCTL_ENABLE_BOOT_FETCH); |
|
|
/* 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 |
4012,10 → 5134,10 |
* but we didn't debug actual testcases to find it out. |
* |
* According to the spec, bit 13 (RCZUNIT) must be set on IVB. |
* This implements the WaDisableRCZUnitClockGating workaround. |
* This implements the WaDisableRCZUnitClockGating:vlv workaround. |
* |
* Also apply WaDisableVDSUnitClockGating and |
* WaDisableRCPBUnitClockGating. |
* Also apply WaDisableVDSUnitClockGating:vlv and |
* WaDisableRCPBUnitClockGating:vlv. |
*/ |
I915_WRITE(GEN6_UCGCTL2, |
GEN7_VDSUNIT_CLOCK_GATE_DISABLE | |
4026,18 → 5148,13 |
|
I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE); |
|
for_each_pipe(pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_display_plane(dev_priv, pipe); |
} |
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); |
|
I915_WRITE(CACHE_MODE_1, |
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); |
|
/* |
* WaDisableVLVClockGating_VBIIssue |
* WaDisableVLVClockGating_VBIIssue:vlv |
* Disable clock gating on th GCFG unit to prevent a delay |
* in the reporting of vblank events. |
*/ |
4073,6 → 5190,8 |
/* WaDisableRenderCachePipelinedFlush */ |
I915_WRITE(CACHE_MODE_0, |
_MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); |
|
g4x_disable_trickle_feed(dev); |
} |
|
static void crestline_init_clock_gating(struct drm_device *dev) |
4084,6 → 5203,8 |
I915_WRITE(DSPCLK_GATE_D, 0); |
I915_WRITE(RAMCLK_GATE_D, 0); |
I915_WRITE16(DEUC, 0); |
I915_WRITE(MI_ARB_STATE, |
_MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); |
} |
|
static void broadwater_init_clock_gating(struct drm_device *dev) |
4096,6 → 5217,8 |
I965_ISC_CLOCK_GATE_DISABLE | |
I965_FBC_CLOCK_GATE_DISABLE); |
I915_WRITE(RENCLK_GATE_D2, 0); |
I915_WRITE(MI_ARB_STATE, |
_MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); |
} |
|
static void gen3_init_clock_gating(struct drm_device *dev) |
4135,56 → 5258,164 |
dev_priv->display.init_clock_gating(dev); |
} |
|
void intel_suspend_hw(struct drm_device *dev) |
{ |
if (HAS_PCH_LPT(dev)) |
lpt_suspend_hw(dev); |
} |
|
/** |
* We should only use the power well if we explicitly asked the hardware to |
* enable it, so check if it's enabled and also check if we've requested it to |
* be enabled. |
*/ |
bool intel_using_power_well(struct drm_device *dev) |
bool intel_display_power_enabled(struct drm_device *dev, |
enum intel_display_power_domain domain) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (IS_HASWELL(dev)) |
if (!HAS_POWER_WELL(dev)) |
return true; |
|
switch (domain) { |
case POWER_DOMAIN_PIPE_A: |
case POWER_DOMAIN_TRANSCODER_EDP: |
return true; |
case POWER_DOMAIN_PIPE_B: |
case POWER_DOMAIN_PIPE_C: |
case POWER_DOMAIN_PIPE_A_PANEL_FITTER: |
case POWER_DOMAIN_PIPE_B_PANEL_FITTER: |
case POWER_DOMAIN_PIPE_C_PANEL_FITTER: |
case POWER_DOMAIN_TRANSCODER_A: |
case POWER_DOMAIN_TRANSCODER_B: |
case POWER_DOMAIN_TRANSCODER_C: |
return I915_READ(HSW_PWR_WELL_DRIVER) == |
(HSW_PWR_WELL_ENABLE | HSW_PWR_WELL_STATE); |
else |
return true; |
(HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED); |
default: |
BUG(); |
} |
} |
|
void intel_set_power_well(struct drm_device *dev, bool enable) |
static void __intel_set_power_well(struct drm_device *dev, bool enable) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
bool is_enabled, enable_requested; |
uint32_t tmp; |
|
if (!HAS_POWER_WELL(dev)) |
return; |
|
if (!i915_disable_power_well && !enable) |
return; |
|
tmp = I915_READ(HSW_PWR_WELL_DRIVER); |
is_enabled = tmp & HSW_PWR_WELL_STATE; |
enable_requested = tmp & HSW_PWR_WELL_ENABLE; |
is_enabled = tmp & HSW_PWR_WELL_STATE_ENABLED; |
enable_requested = tmp & HSW_PWR_WELL_ENABLE_REQUEST; |
|
if (enable) { |
if (!enable_requested) |
I915_WRITE(HSW_PWR_WELL_DRIVER, HSW_PWR_WELL_ENABLE); |
I915_WRITE(HSW_PWR_WELL_DRIVER, |
HSW_PWR_WELL_ENABLE_REQUEST); |
|
if (!is_enabled) { |
DRM_DEBUG_KMS("Enabling power well\n"); |
if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) & |
HSW_PWR_WELL_STATE), 20)) |
HSW_PWR_WELL_STATE_ENABLED), 20)) |
DRM_ERROR("Timeout enabling power well\n"); |
} |
} else { |
if (enable_requested) { |
unsigned long irqflags; |
enum pipe p; |
|
I915_WRITE(HSW_PWR_WELL_DRIVER, 0); |
POSTING_READ(HSW_PWR_WELL_DRIVER); |
DRM_DEBUG_KMS("Requesting to disable the power well\n"); |
|
/* |
* After this, the registers on the pipes that are part |
* of the power well will become zero, so we have to |
* adjust our counters according to that. |
* |
* FIXME: Should we do this in general in |
* drm_vblank_post_modeset? |
*/ |
spin_lock_irqsave(&dev->vbl_lock, irqflags); |
for_each_pipe(p) |
if (p != PIPE_A) |
dev->last_vblank[p] = 0; |
spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
} |
} |
} |
|
static struct i915_power_well *hsw_pwr; |
|
/* Display audio driver power well request */ |
void i915_request_power_well(void) |
{ |
if (WARN_ON(!hsw_pwr)) |
return; |
|
spin_lock_irq(&hsw_pwr->lock); |
if (!hsw_pwr->count++ && |
!hsw_pwr->i915_request) |
__intel_set_power_well(hsw_pwr->device, true); |
spin_unlock_irq(&hsw_pwr->lock); |
} |
EXPORT_SYMBOL_GPL(i915_request_power_well); |
|
/* Display audio driver power well release */ |
void i915_release_power_well(void) |
{ |
if (WARN_ON(!hsw_pwr)) |
return; |
|
spin_lock_irq(&hsw_pwr->lock); |
WARN_ON(!hsw_pwr->count); |
if (!--hsw_pwr->count && |
!hsw_pwr->i915_request) |
__intel_set_power_well(hsw_pwr->device, false); |
spin_unlock_irq(&hsw_pwr->lock); |
} |
EXPORT_SYMBOL_GPL(i915_release_power_well); |
|
int i915_init_power_well(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
hsw_pwr = &dev_priv->power_well; |
|
hsw_pwr->device = dev; |
spin_lock_init(&hsw_pwr->lock); |
hsw_pwr->count = 0; |
|
return 0; |
} |
|
void i915_remove_power_well(struct drm_device *dev) |
{ |
hsw_pwr = NULL; |
} |
|
void intel_set_power_well(struct drm_device *dev, bool enable) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct i915_power_well *power_well = &dev_priv->power_well; |
|
if (!HAS_POWER_WELL(dev)) |
return; |
|
if (!i915_disable_power_well && !enable) |
return; |
|
spin_lock_irq(&power_well->lock); |
power_well->i915_request = enable; |
|
/* only reject "disable" power well request */ |
if (power_well->count && !enable) { |
spin_unlock_irq(&power_well->lock); |
return; |
} |
|
__intel_set_power_well(dev, enable); |
spin_unlock_irq(&power_well->lock); |
} |
|
/* |
* Starting with Haswell, we have a "Power Down Well" that can be turned off |
* when not needed anymore. We have 4 registers that can request the power well |
4203,10 → 5434,21 |
|
/* We're taking over the BIOS, so clear any requests made by it since |
* the driver is in charge now. */ |
if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE) |
if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE_REQUEST) |
I915_WRITE(HSW_PWR_WELL_BIOS, 0); |
} |
|
/* Disables PC8 so we can use the GMBUS and DP AUX interrupts. */ |
void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv) |
{ |
hsw_disable_package_c8(dev_priv); |
} |
|
void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv) |
{ |
hsw_enable_package_c8(dev_priv); |
} |
|
/* Set up chip specific power management-related functions */ |
void intel_init_pm(struct drm_device *dev) |
{ |
4215,7 → 5457,12 |
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; |
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) |
dev_priv->display.enable_fbc = |
gen7_enable_fbc; |
else |
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; |
4237,8 → 5484,12 |
|
/* For FIFO watermark updates */ |
if (HAS_PCH_SPLIT(dev)) { |
intel_setup_wm_latency(dev); |
|
if (IS_GEN5(dev)) { |
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK) |
if (dev_priv->wm.pri_latency[1] && |
dev_priv->wm.spr_latency[1] && |
dev_priv->wm.cur_latency[1]) |
dev_priv->display.update_wm = ironlake_update_wm; |
else { |
DRM_DEBUG_KMS("Failed to get proper latency. " |
4247,7 → 5498,9 |
} |
dev_priv->display.init_clock_gating = ironlake_init_clock_gating; |
} else if (IS_GEN6(dev)) { |
if (SNB_READ_WM0_LATENCY()) { |
if (dev_priv->wm.pri_latency[0] && |
dev_priv->wm.spr_latency[0] && |
dev_priv->wm.cur_latency[0]) { |
dev_priv->display.update_wm = sandybridge_update_wm; |
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; |
} else { |
4257,7 → 5510,9 |
} |
dev_priv->display.init_clock_gating = gen6_init_clock_gating; |
} else if (IS_IVYBRIDGE(dev)) { |
if (SNB_READ_WM0_LATENCY()) { |
if (dev_priv->wm.pri_latency[0] && |
dev_priv->wm.spr_latency[0] && |
dev_priv->wm.cur_latency[0]) { |
dev_priv->display.update_wm = ivybridge_update_wm; |
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; |
} else { |
4267,10 → 5522,12 |
} |
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; |
} else if (IS_HASWELL(dev)) { |
if (SNB_READ_WM0_LATENCY()) { |
dev_priv->display.update_wm = sandybridge_update_wm; |
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; |
dev_priv->display.update_linetime_wm = haswell_update_linetime_wm; |
if (dev_priv->wm.pri_latency[0] && |
dev_priv->wm.spr_latency[0] && |
dev_priv->wm.cur_latency[0]) { |
dev_priv->display.update_wm = haswell_update_wm; |
dev_priv->display.update_sprite_wm = |
haswell_update_sprite_wm; |
} else { |
DRM_DEBUG_KMS("Failed to read display plane latency. " |
"Disable CxSR\n"); |
4330,222 → 5587,6 |
} |
} |
|
static void __gen6_gt_wait_for_thread_c0(struct drm_i915_private *dev_priv) |
{ |
u32 gt_thread_status_mask; |
|
if (IS_HASWELL(dev_priv->dev)) |
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK_HSW; |
else |
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK; |
|
/* w/a for a sporadic read returning 0 by waiting for the GT |
* thread to wake up. |
*/ |
if (wait_for_atomic_us((I915_READ_NOTRACE(GEN6_GT_THREAD_STATUS_REG) & gt_thread_status_mask) == 0, 500)) |
DRM_ERROR("GT thread status wait timed out\n"); |
} |
|
static void __gen6_gt_force_wake_reset(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE, 0); |
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ |
} |
|
static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) |
{ |
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0, |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); |
|
I915_WRITE_NOTRACE(FORCEWAKE, 1); |
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ |
|
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1), |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); |
|
__gen6_gt_wait_for_thread_c0(dev_priv); |
} |
|
static void __gen6_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(0xffff)); |
/* something from same cacheline, but !FORCEWAKE_MT */ |
POSTING_READ(ECOBUS); |
} |
|
static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) |
{ |
u32 forcewake_ack; |
|
if (IS_HASWELL(dev_priv->dev)) |
forcewake_ack = FORCEWAKE_ACK_HSW; |
else |
forcewake_ack = FORCEWAKE_MT_ACK; |
|
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL) == 0, |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); |
|
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); |
/* something from same cacheline, but !FORCEWAKE_MT */ |
POSTING_READ(ECOBUS); |
|
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL), |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); |
|
__gen6_gt_wait_for_thread_c0(dev_priv); |
} |
|
/* |
* Generally this is called implicitly by the register read function. However, |
* if some sequence requires the GT to not power down then this function should |
* be called at the beginning of the sequence followed by a call to |
* gen6_gt_force_wake_put() at the end of the sequence. |
*/ |
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) |
{ |
unsigned long irqflags; |
|
spin_lock_irqsave(&dev_priv->gt_lock, irqflags); |
if (dev_priv->forcewake_count++ == 0) |
dev_priv->gt.force_wake_get(dev_priv); |
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); |
} |
|
void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv) |
{ |
u32 gtfifodbg; |
gtfifodbg = I915_READ_NOTRACE(GTFIFODBG); |
if (WARN(gtfifodbg & GT_FIFO_CPU_ERROR_MASK, |
"MMIO read or write has been dropped %x\n", gtfifodbg)) |
I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK); |
} |
|
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE, 0); |
/* something from same cacheline, but !FORCEWAKE */ |
POSTING_READ(ECOBUS); |
gen6_gt_check_fifodbg(dev_priv); |
} |
|
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); |
/* something from same cacheline, but !FORCEWAKE_MT */ |
POSTING_READ(ECOBUS); |
gen6_gt_check_fifodbg(dev_priv); |
} |
|
/* |
* see gen6_gt_force_wake_get() |
*/ |
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) |
{ |
unsigned long irqflags; |
|
spin_lock_irqsave(&dev_priv->gt_lock, irqflags); |
if (--dev_priv->forcewake_count == 0) |
dev_priv->gt.force_wake_put(dev_priv); |
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); |
} |
|
int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) |
{ |
int ret = 0; |
|
if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) { |
int loop = 500; |
u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); |
while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { |
udelay(10); |
fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); |
} |
if (WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES)) |
++ret; |
dev_priv->gt_fifo_count = fifo; |
} |
dev_priv->gt_fifo_count--; |
|
return ret; |
} |
|
static void vlv_force_wake_reset(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(0xffff)); |
/* something from same cacheline, but !FORCEWAKE_VLV */ |
POSTING_READ(FORCEWAKE_ACK_VLV); |
} |
|
static void vlv_force_wake_get(struct drm_i915_private *dev_priv) |
{ |
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL) == 0, |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); |
|
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); |
I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV, |
_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); |
|
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL), |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for GT to ack forcewake request.\n"); |
|
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_MEDIA_VLV) & |
FORCEWAKE_KERNEL), |
FORCEWAKE_ACK_TIMEOUT_MS)) |
DRM_ERROR("Timed out waiting for media to ack forcewake request.\n"); |
|
__gen6_gt_wait_for_thread_c0(dev_priv); |
} |
|
static void vlv_force_wake_put(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); |
I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV, |
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); |
/* The below doubles as a POSTING_READ */ |
gen6_gt_check_fifodbg(dev_priv); |
} |
|
void intel_gt_reset(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (IS_VALLEYVIEW(dev)) { |
vlv_force_wake_reset(dev_priv); |
} else if (INTEL_INFO(dev)->gen >= 6) { |
__gen6_gt_force_wake_reset(dev_priv); |
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) |
__gen6_gt_force_wake_mt_reset(dev_priv); |
} |
} |
|
void intel_gt_init(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
spin_lock_init(&dev_priv->gt_lock); |
|
intel_gt_reset(dev); |
|
if (IS_VALLEYVIEW(dev)) { |
dev_priv->gt.force_wake_get = vlv_force_wake_get; |
dev_priv->gt.force_wake_put = vlv_force_wake_put; |
} else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { |
dev_priv->gt.force_wake_get = __gen6_gt_force_wake_mt_get; |
dev_priv->gt.force_wake_put = __gen6_gt_force_wake_mt_put; |
} else if (IS_GEN6(dev)) { |
dev_priv->gt.force_wake_get = __gen6_gt_force_wake_get; |
dev_priv->gt.force_wake_put = __gen6_gt_force_wake_put; |
} |
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, |
intel_gen6_powersave_work); |
} |
|
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) |
{ |
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
4593,55 → 5634,68 |
return 0; |
} |
|
static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, |
u8 addr, u32 *val) |
int vlv_gpu_freq(int ddr_freq, int val) |
{ |
u32 cmd, devfn, port, be, bar; |
int mult, base; |
|
bar = 0; |
be = 0xf; |
port = IOSF_PORT_PUNIT; |
devfn = PCI_DEVFN(2, 0); |
switch (ddr_freq) { |
case 800: |
mult = 20; |
base = 120; |
break; |
case 1066: |
mult = 22; |
base = 133; |
break; |
case 1333: |
mult = 21; |
base = 125; |
break; |
default: |
return -1; |
} |
|
cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | |
(port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) | |
(bar << IOSF_BAR_SHIFT); |
return ((val - 0xbd) * mult) + base; |
} |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
int vlv_freq_opcode(int ddr_freq, int val) |
{ |
int mult, base; |
|
if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) { |
DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n", |
opcode == PUNIT_OPCODE_REG_READ ? |
"read" : "write"); |
return -EAGAIN; |
switch (ddr_freq) { |
case 800: |
mult = 20; |
base = 120; |
break; |
case 1066: |
mult = 22; |
base = 133; |
break; |
case 1333: |
mult = 21; |
base = 125; |
break; |
default: |
return -1; |
} |
|
I915_WRITE(VLV_IOSF_ADDR, addr); |
if (opcode == PUNIT_OPCODE_REG_WRITE) |
I915_WRITE(VLV_IOSF_DATA, *val); |
I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); |
val /= mult; |
val -= base / mult; |
val += 0xbd; |
|
if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, |
500)) { |
DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", |
opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", |
addr); |
return -ETIMEDOUT; |
if (val > 0xea) |
val = 0xea; |
|
return val; |
} |
|
if (opcode == PUNIT_OPCODE_REG_READ) |
*val = I915_READ(VLV_IOSF_DATA); |
I915_WRITE(VLV_IOSF_DATA, 0); |
void intel_pm_init(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
return 0; |
} |
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, |
intel_gen6_powersave_work); |
|
int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) |
{ |
return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_READ, addr, val); |
INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work); |
} |
|
int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) |
{ |
return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val); |
} |