28,28 → 28,14 |
//#include <linux/cpufreq.h> |
#include "i915_drv.h" |
#include "intel_drv.h" |
#include <linux/math64.h> |
//#include "../../../platform/x86/intel_ips.h" |
#include <linux/module.h> |
|
#include <drm/i915_powerwell.h> |
|
#define FORCEWAKE_ACK_TIMEOUT_MS 2 |
|
void getrawmonotonic(struct timespec *ts); |
|
union ktime { |
s64 tv64; |
}; |
|
typedef union ktime ktime_t; /* Kill this */ |
|
#define ktime_to_ns(kt) ((kt).tv64) |
|
static inline u64 ktime_get_raw_ns(void) |
{ |
return 0; //ktime_to_ns(ktime_get_raw()); |
} |
/** |
* RC6 is a special power stage which allows the GPU to enter an very |
* low-voltage mode when idle, using down to 0V while at this stage. This |
71,649 → 57,22 |
#define INTEL_RC6p_ENABLE (1<<1) |
#define INTEL_RC6pp_ENABLE (1<<2) |
|
/* FBC, or Frame Buffer Compression, is a technique employed to compress the |
* framebuffer contents in-memory, aiming at reducing the required bandwidth |
* during in-memory transfers and, therefore, reduce the power packet. |
* |
* The benefits of FBC are mostly visible with solid backgrounds and |
* variation-less patterns. |
* |
* FBC-related functionality can be enabled by the means of the |
* i915.i915_enable_fbc parameter |
*/ |
|
static void gen9_init_clock_gating(struct drm_device *dev) |
static void bxt_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* |
* WaDisableSDEUnitClockGating:skl |
* This seems to be a pre-production w/a. |
*/ |
/* WaDisableSDEUnitClockGating:bxt */ |
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | |
GEN8_SDEUNIT_CLOCK_GATE_DISABLE); |
|
/* |
* WaDisableDgMirrorFixInHalfSliceChicken5:skl |
* This is a pre-production w/a. |
* FIXME: |
* GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ applies on 3x6 GT SKUs only. |
*/ |
I915_WRITE(GEN9_HALF_SLICE_CHICKEN5, |
I915_READ(GEN9_HALF_SLICE_CHICKEN5) & |
~GEN9_DG_MIRROR_FIX_ENABLE); |
|
/* Wa4x4STCOptimizationDisable:skl */ |
I915_WRITE(CACHE_MODE_1, |
_MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE)); |
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | |
GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ); |
} |
|
static void i8xx_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 fbc_ctl; |
|
dev_priv->fbc.enabled = false; |
|
/* 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) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->primary->fb; |
struct drm_i915_gem_object *obj = intel_fb_obj(fb); |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
int cfb_pitch; |
int i; |
u32 fbc_ctl; |
|
dev_priv->fbc.enabled = true; |
|
cfb_pitch = dev_priv->fbc.size / FBC_LL_SIZE; |
if (fb->pitches[0] < cfb_pitch) |
cfb_pitch = fb->pitches[0]; |
|
/* FBC_CTL wants 32B or 64B units */ |
if (IS_GEN2(dev)) |
cfb_pitch = (cfb_pitch / 32) - 1; |
else |
cfb_pitch = (cfb_pitch / 64) - 1; |
|
/* Clear old tags */ |
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) |
I915_WRITE(FBC_TAG + (i * 4), 0); |
|
if (IS_GEN4(dev)) { |
u32 fbc_ctl2; |
|
/* Set it up... */ |
fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; |
fbc_ctl2 |= FBC_CTL_PLANE(intel_crtc->plane); |
I915_WRITE(FBC_CONTROL2, fbc_ctl2); |
I915_WRITE(FBC_FENCE_OFF, crtc->y); |
} |
|
/* enable it... */ |
fbc_ctl = I915_READ(FBC_CONTROL); |
fbc_ctl &= 0x3fff << FBC_CTL_INTERVAL_SHIFT; |
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 |= obj->fence_reg; |
I915_WRITE(FBC_CONTROL, fbc_ctl); |
|
DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c\n", |
cfb_pitch, crtc->y, plane_name(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) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->primary->fb; |
struct drm_i915_gem_object *obj = intel_fb_obj(fb); |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
u32 dpfc_ctl; |
|
dev_priv->fbc.enabled = true; |
|
dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane) | DPFC_SR_EN; |
if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) |
dpfc_ctl |= DPFC_CTL_LIMIT_2X; |
else |
dpfc_ctl |= DPFC_CTL_LIMIT_1X; |
dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg; |
|
I915_WRITE(DPFC_FENCE_YOFF, crtc->y); |
|
/* enable it... */ |
I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); |
|
DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); |
} |
|
static void g4x_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 dpfc_ctl; |
|
dev_priv->fbc.enabled = false; |
|
/* 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 */ |
|
/* Blitter is part of Media powerwell on VLV. No impact of |
* his param in other platforms for now */ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_MEDIA); |
|
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, FORCEWAKE_MEDIA); |
} |
|
static void ironlake_enable_fbc(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->primary->fb; |
struct drm_i915_gem_object *obj = intel_fb_obj(fb); |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
u32 dpfc_ctl; |
|
dev_priv->fbc.enabled = true; |
|
dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane); |
if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) |
dev_priv->fbc.threshold++; |
|
switch (dev_priv->fbc.threshold) { |
case 4: |
case 3: |
dpfc_ctl |= DPFC_CTL_LIMIT_4X; |
break; |
case 2: |
dpfc_ctl |= DPFC_CTL_LIMIT_2X; |
break; |
case 1: |
dpfc_ctl |= DPFC_CTL_LIMIT_1X; |
break; |
} |
dpfc_ctl |= DPFC_CTL_FENCE_EN; |
if (IS_GEN5(dev)) |
dpfc_ctl |= obj->fence_reg; |
|
I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y); |
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); |
|
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 %c\n", plane_name(intel_crtc->plane)); |
} |
|
static void ironlake_disable_fbc(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
u32 dpfc_ctl; |
|
dev_priv->fbc.enabled = false; |
|
/* 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; |
} |
|
static void gen7_enable_fbc(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct drm_framebuffer *fb = crtc->primary->fb; |
struct drm_i915_gem_object *obj = intel_fb_obj(fb); |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
u32 dpfc_ctl; |
|
dev_priv->fbc.enabled = true; |
|
dpfc_ctl = IVB_DPFC_CTL_PLANE(intel_crtc->plane); |
if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) |
dev_priv->fbc.threshold++; |
|
switch (dev_priv->fbc.threshold) { |
case 4: |
case 3: |
dpfc_ctl |= DPFC_CTL_LIMIT_4X; |
break; |
case 2: |
dpfc_ctl |= DPFC_CTL_LIMIT_2X; |
break; |
case 1: |
dpfc_ctl |= DPFC_CTL_LIMIT_1X; |
break; |
} |
|
dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN; |
|
if (dev_priv->fbc.false_color) |
dpfc_ctl |= FBC_CTL_FALSE_COLOR; |
|
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); |
|
if (IS_IVYBRIDGE(dev)) { |
/* WaFbcAsynchFlipDisableFbcQueue:ivb */ |
I915_WRITE(ILK_DISPLAY_CHICKEN1, |
I915_READ(ILK_DISPLAY_CHICKEN1) | |
ILK_FBCQ_DIS); |
} else { |
/* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ |
I915_WRITE(CHICKEN_PIPESL_1(intel_crtc->pipe), |
I915_READ(CHICKEN_PIPESL_1(intel_crtc->pipe)) | |
HSW_FBCQ_DIS); |
} |
|
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 %c\n", plane_name(intel_crtc->plane)); |
} |
|
bool intel_fbc_enabled(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
return dev_priv->fbc.enabled; |
} |
|
void bdw_fbc_sw_flush(struct drm_device *dev, u32 value) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (!IS_GEN8(dev)) |
return; |
|
if (!intel_fbc_enabled(dev)) |
return; |
|
I915_WRITE(MSG_FBC_REND_STATE, value); |
} |
|
static void intel_fbc_work_fn(struct work_struct *__work) |
{ |
struct intel_fbc_work *work = |
container_of(to_delayed_work(__work), |
struct intel_fbc_work, work); |
struct drm_device *dev = work->crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
mutex_lock(&dev->struct_mutex); |
if (work == dev_priv->fbc.fbc_work) { |
/* Double check that we haven't switched fb without cancelling |
* the prior work. |
*/ |
if (work->crtc->primary->fb == work->fb) { |
dev_priv->display.enable_fbc(work->crtc); |
|
dev_priv->fbc.plane = to_intel_crtc(work->crtc)->plane; |
dev_priv->fbc.fb_id = work->crtc->primary->fb->base.id; |
dev_priv->fbc.y = work->crtc->y; |
} |
|
dev_priv->fbc.fbc_work = NULL; |
} |
mutex_unlock(&dev->struct_mutex); |
|
kfree(work); |
} |
|
static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv) |
{ |
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.fbc_work, so we can perform the cancellation |
* entirely asynchronously. |
*/ |
if (cancel_delayed_work(&dev_priv->fbc.fbc_work->work)) |
/* tasklet was killed before being run, clean up */ |
kfree(dev_priv->fbc.fbc_work); |
|
/* Mark the work as no longer wanted so that if it does |
* wake-up (because the work was already running and waiting |
* for our mutex), it will discover that is no longer |
* necessary to run. |
*/ |
dev_priv->fbc.fbc_work = NULL; |
} |
|
static void intel_enable_fbc(struct drm_crtc *crtc) |
{ |
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) { |
DRM_ERROR("Failed to allocate FBC work structure\n"); |
dev_priv->display.enable_fbc(crtc); |
return; |
} |
|
work->crtc = crtc; |
work->fb = crtc->primary->fb; |
INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn); |
|
dev_priv->fbc.fbc_work = work; |
|
/* 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. |
* |
* WaFbcWaitForVBlankBeforeEnable:ilk,snb |
*/ |
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->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 |
* |
* 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 <= 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 |
* one. It also must reside (along with the line length buffer) in |
* stolen memory. |
* |
* We need to enable/disable FBC on a global basis. |
*/ |
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 drm_i915_gem_object *obj; |
const struct drm_display_mode *adjusted_mode; |
unsigned int max_width, max_height; |
|
if (!HAS_FBC(dev)) { |
set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED); |
return; |
} |
|
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 |
* 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.) |
*/ |
for_each_crtc(dev, tmp_crtc) { |
if (intel_crtc_active(tmp_crtc) && |
to_intel_crtc(tmp_crtc)->primary_enabled) { |
if (crtc) { |
if (set_no_fbc_reason(dev_priv, FBC_MULTIPLE_PIPES)) |
DRM_DEBUG_KMS("more than one pipe active, disabling compression\n"); |
goto out_disable; |
} |
crtc = tmp_crtc; |
} |
} |
|
if (!crtc || crtc->primary->fb == NULL) { |
if (set_no_fbc_reason(dev_priv, FBC_NO_OUTPUT)) |
DRM_DEBUG_KMS("no output, disabling\n"); |
goto out_disable; |
} |
|
intel_crtc = to_intel_crtc(crtc); |
fb = crtc->primary->fb; |
obj = intel_fb_obj(fb); |
adjusted_mode = &intel_crtc->config.adjusted_mode; |
|
if (i915.enable_fbc < 0) { |
if (set_no_fbc_reason(dev_priv, FBC_CHIP_DEFAULT)) |
DRM_DEBUG_KMS("disabled per chip default\n"); |
goto out_disable; |
} |
if (!i915.enable_fbc) { |
if (set_no_fbc_reason(dev_priv, FBC_MODULE_PARAM)) |
DRM_DEBUG_KMS("fbc disabled per module param\n"); |
goto out_disable; |
} |
if ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) || |
(adjusted_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"); |
goto out_disable; |
} |
|
if (INTEL_INFO(dev)->gen >= 8 || IS_HASWELL(dev)) { |
max_width = 4096; |
max_height = 4096; |
} else if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { |
max_width = 4096; |
max_height = 2048; |
} else { |
max_width = 2048; |
max_height = 1536; |
} |
if (intel_crtc->config.pipe_src_w > max_width || |
intel_crtc->config.pipe_src_h > max_height) { |
if (set_no_fbc_reason(dev_priv, FBC_MODE_TOO_LARGE)) |
DRM_DEBUG_KMS("mode too large for compression, disabling\n"); |
goto out_disable; |
} |
if ((INTEL_INFO(dev)->gen < 4 || HAS_DDI(dev)) && |
intel_crtc->plane != PLANE_A) { |
if (set_no_fbc_reason(dev_priv, FBC_BAD_PLANE)) |
DRM_DEBUG_KMS("plane not A, disabling compression\n"); |
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) { |
if (set_no_fbc_reason(dev_priv, FBC_NOT_TILED)) |
DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); |
goto out_disable; |
} |
if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && |
to_intel_plane(crtc->primary)->rotation != BIT(DRM_ROTATE_0)) { |
if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE)) |
DRM_DEBUG_KMS("Rotation unsupported, disabling\n"); |
goto out_disable; |
} |
|
/* If the kernel debugger is active, always disable compression */ |
if (in_dbg_master()) |
goto out_disable; |
|
if (i915_gem_stolen_setup_compression(dev, obj->base.size, |
drm_format_plane_cpp(fb->pixel_format, 0))) { |
if (set_no_fbc_reason(dev_priv, FBC_STOLEN_TOO_SMALL)) |
DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); |
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->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)) { |
/* 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); |
dev_priv->fbc.no_fbc_reason = FBC_OK; |
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); |
} |
i915_gem_stolen_cleanup_compression(dev); |
} |
|
static void i915_pineview_get_mem_freq(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
883,6 → 242,47 |
return NULL; |
} |
|
static void chv_set_memory_dvfs(struct drm_i915_private *dev_priv, bool enable) |
{ |
u32 val; |
|
mutex_lock(&dev_priv->rps.hw_lock); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); |
if (enable) |
val &= ~FORCE_DDR_HIGH_FREQ; |
else |
val |= FORCE_DDR_HIGH_FREQ; |
val &= ~FORCE_DDR_LOW_FREQ; |
val |= FORCE_DDR_FREQ_REQ_ACK; |
vlv_punit_write(dev_priv, PUNIT_REG_DDR_SETUP2, val); |
|
if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2) & |
FORCE_DDR_FREQ_REQ_ACK) == 0, 3)) |
DRM_ERROR("timed out waiting for Punit DDR DVFS request\n"); |
|
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
|
static void chv_set_memory_pm5(struct drm_i915_private *dev_priv, bool enable) |
{ |
u32 val; |
|
mutex_lock(&dev_priv->rps.hw_lock); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ); |
if (enable) |
val |= DSP_MAXFIFO_PM5_ENABLE; |
else |
val &= ~DSP_MAXFIFO_PM5_ENABLE; |
vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, val); |
|
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
|
#define FW_WM(value, plane) \ |
(((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK) |
|
void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) |
{ |
struct drm_device *dev = dev_priv->dev; |
890,20 → 290,26 |
|
if (IS_VALLEYVIEW(dev)) { |
I915_WRITE(FW_BLC_SELF_VLV, enable ? FW_CSPWRDWNEN : 0); |
POSTING_READ(FW_BLC_SELF_VLV); |
dev_priv->wm.vlv.cxsr = enable; |
} else if (IS_G4X(dev) || IS_CRESTLINE(dev)) { |
I915_WRITE(FW_BLC_SELF, enable ? FW_BLC_SELF_EN : 0); |
POSTING_READ(FW_BLC_SELF); |
} else if (IS_PINEVIEW(dev)) { |
val = I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN; |
val |= enable ? PINEVIEW_SELF_REFRESH_EN : 0; |
I915_WRITE(DSPFW3, val); |
POSTING_READ(DSPFW3); |
} else if (IS_I945G(dev) || IS_I945GM(dev)) { |
val = enable ? _MASKED_BIT_ENABLE(FW_BLC_SELF_EN) : |
_MASKED_BIT_DISABLE(FW_BLC_SELF_EN); |
I915_WRITE(FW_BLC_SELF, val); |
POSTING_READ(FW_BLC_SELF); |
} else if (IS_I915GM(dev)) { |
val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) : |
_MASKED_BIT_DISABLE(INSTPM_SELF_EN); |
I915_WRITE(INSTPM, val); |
POSTING_READ(INSTPM); |
} else { |
return; |
} |
912,6 → 318,7 |
enable ? "enabled" : "disabled"); |
} |
|
|
/* |
* Latency for FIFO fetches is dependent on several factors: |
* - memory configuration (speed, channels) |
928,6 → 335,61 |
*/ |
static const int pessimal_latency_ns = 5000; |
|
#define VLV_FIFO_START(dsparb, dsparb2, lo_shift, hi_shift) \ |
((((dsparb) >> (lo_shift)) & 0xff) | ((((dsparb2) >> (hi_shift)) & 0x1) << 8)) |
|
static int vlv_get_fifo_size(struct drm_device *dev, |
enum pipe pipe, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int sprite0_start, sprite1_start, size; |
|
switch (pipe) { |
uint32_t dsparb, dsparb2, dsparb3; |
case PIPE_A: |
dsparb = I915_READ(DSPARB); |
dsparb2 = I915_READ(DSPARB2); |
sprite0_start = VLV_FIFO_START(dsparb, dsparb2, 0, 0); |
sprite1_start = VLV_FIFO_START(dsparb, dsparb2, 8, 4); |
break; |
case PIPE_B: |
dsparb = I915_READ(DSPARB); |
dsparb2 = I915_READ(DSPARB2); |
sprite0_start = VLV_FIFO_START(dsparb, dsparb2, 16, 8); |
sprite1_start = VLV_FIFO_START(dsparb, dsparb2, 24, 12); |
break; |
case PIPE_C: |
dsparb2 = I915_READ(DSPARB2); |
dsparb3 = I915_READ(DSPARB3); |
sprite0_start = VLV_FIFO_START(dsparb3, dsparb2, 0, 16); |
sprite1_start = VLV_FIFO_START(dsparb3, dsparb2, 8, 20); |
break; |
default: |
return 0; |
} |
|
switch (plane) { |
case 0: |
size = sprite0_start; |
break; |
case 1: |
size = sprite1_start - sprite0_start; |
break; |
case 2: |
size = 512 - 1 - sprite1_start; |
break; |
default: |
return 0; |
} |
|
DRM_DEBUG_KMS("Pipe %c %s %c FIFO size: %d\n", |
pipe_name(pipe), plane == 0 ? "primary" : "sprite", |
plane == 0 ? plane_name(pipe) : sprite_name(pipe, plane - 1), |
size); |
|
return size; |
} |
|
static int i9xx_get_fifo_size(struct drm_device *dev, int plane) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
1172,13 → 634,10 |
|
crtc = single_enabled_crtc(dev); |
if (crtc) { |
const struct drm_display_mode *adjusted_mode; |
int pixel_size = crtc->primary->fb->bits_per_pixel / 8; |
int clock; |
const struct drm_display_mode *adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
int pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; |
int clock = adjusted_mode->crtc_clock; |
|
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
clock = adjusted_mode->crtc_clock; |
|
/* Display SR */ |
wm = intel_calculate_wm(clock, &pineview_display_wm, |
pineview_display_wm.fifo_size, |
1185,7 → 644,7 |
pixel_size, latency->display_sr); |
reg = I915_READ(DSPFW1); |
reg &= ~DSPFW_SR_MASK; |
reg |= wm << DSPFW_SR_SHIFT; |
reg |= FW_WM(wm, SR); |
I915_WRITE(DSPFW1, reg); |
DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg); |
|
1195,7 → 654,7 |
pixel_size, latency->cursor_sr); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_CURSOR_SR_MASK; |
reg |= (wm & 0x3f) << DSPFW_CURSOR_SR_SHIFT; |
reg |= FW_WM(wm, CURSOR_SR); |
I915_WRITE(DSPFW3, reg); |
|
/* Display HPLL off SR */ |
1204,7 → 663,7 |
pixel_size, latency->display_hpll_disable); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_HPLL_SR_MASK; |
reg |= wm & DSPFW_HPLL_SR_MASK; |
reg |= FW_WM(wm, HPLL_SR); |
I915_WRITE(DSPFW3, reg); |
|
/* cursor HPLL off SR */ |
1213,7 → 672,7 |
pixel_size, latency->cursor_hpll_disable); |
reg = I915_READ(DSPFW3); |
reg &= ~DSPFW_HPLL_CURSOR_MASK; |
reg |= (wm & 0x3f) << DSPFW_HPLL_CURSOR_SHIFT; |
reg |= FW_WM(wm, HPLL_CURSOR); |
I915_WRITE(DSPFW3, reg); |
DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg); |
|
1245,11 → 704,11 |
return false; |
} |
|
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
clock = adjusted_mode->crtc_clock; |
htotal = adjusted_mode->crtc_htotal; |
hdisplay = to_intel_crtc(crtc)->config.pipe_src_w; |
pixel_size = crtc->primary->fb->bits_per_pixel / 8; |
hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; |
pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; |
|
/* Use the small buffer method to calculate plane watermark */ |
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; |
1264,7 → 723,7 |
/* Use the large buffer method to calculate cursor watermark */ |
line_time_us = max(htotal * 1000 / clock, 1); |
line_count = (cursor_latency_ns / line_time_us + 1000) / 1000; |
entries = line_count * to_intel_crtc(crtc)->cursor_width * pixel_size; |
entries = line_count * crtc->cursor->state->crtc_w * pixel_size; |
tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8; |
if (tlb_miss > 0) |
entries += tlb_miss; |
1332,11 → 791,11 |
} |
|
crtc = intel_get_crtc_for_plane(dev, plane); |
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
clock = adjusted_mode->crtc_clock; |
htotal = adjusted_mode->crtc_htotal; |
hdisplay = to_intel_crtc(crtc)->config.pipe_src_w; |
pixel_size = crtc->primary->fb->bits_per_pixel / 8; |
hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; |
pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; |
|
line_time_us = max(htotal * 1000 / clock, 1); |
line_count = (latency_ns / line_time_us + 1000) / 1000; |
1350,7 → 809,7 |
*display_wm = entries + display->guard_size; |
|
/* calculate the self-refresh watermark for display cursor */ |
entries = line_count * pixel_size * to_intel_crtc(crtc)->cursor_width; |
entries = line_count * pixel_size * crtc->cursor->state->crtc_w; |
entries = DIV_ROUND_UP(entries, cursor->cacheline_size); |
*cursor_wm = entries + cursor->guard_size; |
|
1359,270 → 818,546 |
display, cursor); |
} |
|
static bool vlv_compute_drain_latency(struct drm_crtc *crtc, |
int pixel_size, |
int *prec_mult, |
int *drain_latency) |
#define FW_WM_VLV(value, plane) \ |
(((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK_VLV) |
|
static void vlv_write_wm_values(struct intel_crtc *crtc, |
const struct vlv_wm_values *wm) |
{ |
struct drm_device *dev = crtc->dev; |
int entries; |
int clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock; |
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); |
enum pipe pipe = crtc->pipe; |
|
if (WARN(clock == 0, "Pixel clock is zero!\n")) |
return false; |
I915_WRITE(VLV_DDL(pipe), |
(wm->ddl[pipe].cursor << DDL_CURSOR_SHIFT) | |
(wm->ddl[pipe].sprite[1] << DDL_SPRITE_SHIFT(1)) | |
(wm->ddl[pipe].sprite[0] << DDL_SPRITE_SHIFT(0)) | |
(wm->ddl[pipe].primary << DDL_PLANE_SHIFT)); |
|
if (WARN(pixel_size == 0, "Pixel size is zero!\n")) |
return false; |
I915_WRITE(DSPFW1, |
FW_WM(wm->sr.plane, SR) | |
FW_WM(wm->pipe[PIPE_B].cursor, CURSORB) | |
FW_WM_VLV(wm->pipe[PIPE_B].primary, PLANEB) | |
FW_WM_VLV(wm->pipe[PIPE_A].primary, PLANEA)); |
I915_WRITE(DSPFW2, |
FW_WM_VLV(wm->pipe[PIPE_A].sprite[1], SPRITEB) | |
FW_WM(wm->pipe[PIPE_A].cursor, CURSORA) | |
FW_WM_VLV(wm->pipe[PIPE_A].sprite[0], SPRITEA)); |
I915_WRITE(DSPFW3, |
FW_WM(wm->sr.cursor, CURSOR_SR)); |
|
entries = DIV_ROUND_UP(clock, 1000) * pixel_size; |
if (IS_CHERRYVIEW(dev)) |
*prec_mult = (entries > 128) ? DRAIN_LATENCY_PRECISION_32 : |
DRAIN_LATENCY_PRECISION_16; |
else |
*prec_mult = (entries > 128) ? DRAIN_LATENCY_PRECISION_64 : |
DRAIN_LATENCY_PRECISION_32; |
*drain_latency = (64 * (*prec_mult) * 4) / entries; |
if (IS_CHERRYVIEW(dev_priv)) { |
I915_WRITE(DSPFW7_CHV, |
FW_WM_VLV(wm->pipe[PIPE_B].sprite[1], SPRITED) | |
FW_WM_VLV(wm->pipe[PIPE_B].sprite[0], SPRITEC)); |
I915_WRITE(DSPFW8_CHV, |
FW_WM_VLV(wm->pipe[PIPE_C].sprite[1], SPRITEF) | |
FW_WM_VLV(wm->pipe[PIPE_C].sprite[0], SPRITEE)); |
I915_WRITE(DSPFW9_CHV, |
FW_WM_VLV(wm->pipe[PIPE_C].primary, PLANEC) | |
FW_WM(wm->pipe[PIPE_C].cursor, CURSORC)); |
I915_WRITE(DSPHOWM, |
FW_WM(wm->sr.plane >> 9, SR_HI) | |
FW_WM(wm->pipe[PIPE_C].sprite[1] >> 8, SPRITEF_HI) | |
FW_WM(wm->pipe[PIPE_C].sprite[0] >> 8, SPRITEE_HI) | |
FW_WM(wm->pipe[PIPE_C].primary >> 8, PLANEC_HI) | |
FW_WM(wm->pipe[PIPE_B].sprite[1] >> 8, SPRITED_HI) | |
FW_WM(wm->pipe[PIPE_B].sprite[0] >> 8, SPRITEC_HI) | |
FW_WM(wm->pipe[PIPE_B].primary >> 8, PLANEB_HI) | |
FW_WM(wm->pipe[PIPE_A].sprite[1] >> 8, SPRITEB_HI) | |
FW_WM(wm->pipe[PIPE_A].sprite[0] >> 8, SPRITEA_HI) | |
FW_WM(wm->pipe[PIPE_A].primary >> 8, PLANEA_HI)); |
} else { |
I915_WRITE(DSPFW7, |
FW_WM_VLV(wm->pipe[PIPE_B].sprite[1], SPRITED) | |
FW_WM_VLV(wm->pipe[PIPE_B].sprite[0], SPRITEC)); |
I915_WRITE(DSPHOWM, |
FW_WM(wm->sr.plane >> 9, SR_HI) | |
FW_WM(wm->pipe[PIPE_B].sprite[1] >> 8, SPRITED_HI) | |
FW_WM(wm->pipe[PIPE_B].sprite[0] >> 8, SPRITEC_HI) | |
FW_WM(wm->pipe[PIPE_B].primary >> 8, PLANEB_HI) | |
FW_WM(wm->pipe[PIPE_A].sprite[1] >> 8, SPRITEB_HI) | |
FW_WM(wm->pipe[PIPE_A].sprite[0] >> 8, SPRITEA_HI) | |
FW_WM(wm->pipe[PIPE_A].primary >> 8, PLANEA_HI)); |
} |
|
if (*drain_latency > DRAIN_LATENCY_MASK) |
*drain_latency = DRAIN_LATENCY_MASK; |
/* zero (unused) WM1 watermarks */ |
I915_WRITE(DSPFW4, 0); |
I915_WRITE(DSPFW5, 0); |
I915_WRITE(DSPFW6, 0); |
I915_WRITE(DSPHOWM1, 0); |
|
return true; |
POSTING_READ(DSPFW1); |
} |
|
#undef FW_WM_VLV |
|
enum vlv_wm_level { |
VLV_WM_LEVEL_PM2, |
VLV_WM_LEVEL_PM5, |
VLV_WM_LEVEL_DDR_DVFS, |
}; |
|
/* latency must be in 0.1us units. */ |
static unsigned int vlv_wm_method2(unsigned int pixel_rate, |
unsigned int pipe_htotal, |
unsigned int horiz_pixels, |
unsigned int bytes_per_pixel, |
unsigned int latency) |
{ |
unsigned int ret; |
|
ret = (latency * pixel_rate) / (pipe_htotal * 10000); |
ret = (ret + 1) * horiz_pixels * bytes_per_pixel; |
ret = DIV_ROUND_UP(ret, 64); |
|
return ret; |
} |
|
static void vlv_setup_wm_latency(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* all latencies in usec */ |
dev_priv->wm.pri_latency[VLV_WM_LEVEL_PM2] = 3; |
|
dev_priv->wm.max_level = VLV_WM_LEVEL_PM2; |
|
if (IS_CHERRYVIEW(dev_priv)) { |
dev_priv->wm.pri_latency[VLV_WM_LEVEL_PM5] = 12; |
dev_priv->wm.pri_latency[VLV_WM_LEVEL_DDR_DVFS] = 33; |
|
dev_priv->wm.max_level = VLV_WM_LEVEL_DDR_DVFS; |
} |
} |
|
static uint16_t vlv_compute_wm_level(struct intel_plane *plane, |
struct intel_crtc *crtc, |
const struct intel_plane_state *state, |
int level) |
{ |
struct drm_i915_private *dev_priv = to_i915(plane->base.dev); |
int clock, htotal, pixel_size, width, wm; |
|
if (dev_priv->wm.pri_latency[level] == 0) |
return USHRT_MAX; |
|
if (!state->visible) |
return 0; |
|
pixel_size = drm_format_plane_cpp(state->base.fb->pixel_format, 0); |
clock = crtc->config->base.adjusted_mode.crtc_clock; |
htotal = crtc->config->base.adjusted_mode.crtc_htotal; |
width = crtc->config->pipe_src_w; |
if (WARN_ON(htotal == 0)) |
htotal = 1; |
|
if (plane->base.type == DRM_PLANE_TYPE_CURSOR) { |
/* |
* Update drain latency registers of memory arbiter |
* |
* Valleyview SoC has a new memory arbiter and needs drain latency registers |
* to be programmed. Each plane has a drain latency multiplier and a drain |
* latency value. |
* FIXME the formula gives values that are |
* too big for the cursor FIFO, and hence we |
* would never be able to use cursors. For |
* now just hardcode the watermark. |
*/ |
wm = 63; |
} else { |
wm = vlv_wm_method2(clock, htotal, width, pixel_size, |
dev_priv->wm.pri_latency[level] * 10); |
} |
|
static void vlv_update_drain_latency(struct drm_crtc *crtc) |
return min_t(int, wm, USHRT_MAX); |
} |
|
static void vlv_compute_fifo(struct intel_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 pixel_size; |
int drain_latency; |
enum pipe pipe = intel_crtc->pipe; |
int plane_prec, prec_mult, plane_dl; |
const int high_precision = IS_CHERRYVIEW(dev) ? |
DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_64; |
struct drm_device *dev = crtc->base.dev; |
struct vlv_wm_state *wm_state = &crtc->wm_state; |
struct intel_plane *plane; |
unsigned int total_rate = 0; |
const int fifo_size = 512 - 1; |
int fifo_extra, fifo_left = fifo_size; |
|
plane_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_PLANE_PRECISION_HIGH | |
DRAIN_LATENCY_MASK | DDL_CURSOR_PRECISION_HIGH | |
(DRAIN_LATENCY_MASK << DDL_CURSOR_SHIFT)); |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
struct intel_plane_state *state = |
to_intel_plane_state(plane->base.state); |
|
if (!intel_crtc_active(crtc)) { |
I915_WRITE(VLV_DDL(pipe), plane_dl); |
return; |
if (plane->base.type == DRM_PLANE_TYPE_CURSOR) |
continue; |
|
if (state->visible) { |
wm_state->num_active_planes++; |
total_rate += drm_format_plane_cpp(state->base.fb->pixel_format, 0); |
} |
} |
|
/* Primary plane Drain Latency */ |
pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */ |
if (vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) { |
plane_prec = (prec_mult == high_precision) ? |
DDL_PLANE_PRECISION_HIGH : |
DDL_PLANE_PRECISION_LOW; |
plane_dl |= plane_prec | drain_latency; |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
struct intel_plane_state *state = |
to_intel_plane_state(plane->base.state); |
unsigned int rate; |
|
if (plane->base.type == DRM_PLANE_TYPE_CURSOR) { |
plane->wm.fifo_size = 63; |
continue; |
} |
|
/* Cursor Drain Latency |
* BPP is always 4 for cursor |
*/ |
pixel_size = 4; |
if (!state->visible) { |
plane->wm.fifo_size = 0; |
continue; |
} |
|
/* Program cursor DL only if it is enabled */ |
if (intel_crtc->cursor_base && |
vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) { |
plane_prec = (prec_mult == high_precision) ? |
DDL_CURSOR_PRECISION_HIGH : |
DDL_CURSOR_PRECISION_LOW; |
plane_dl |= plane_prec | (drain_latency << DDL_CURSOR_SHIFT); |
rate = drm_format_plane_cpp(state->base.fb->pixel_format, 0); |
plane->wm.fifo_size = fifo_size * rate / total_rate; |
fifo_left -= plane->wm.fifo_size; |
} |
|
I915_WRITE(VLV_DDL(pipe), plane_dl); |
fifo_extra = DIV_ROUND_UP(fifo_left, wm_state->num_active_planes ?: 1); |
|
/* spread the remainder evenly */ |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
int plane_extra; |
|
if (fifo_left == 0) |
break; |
|
if (plane->base.type == DRM_PLANE_TYPE_CURSOR) |
continue; |
|
/* give it all to the first plane if none are active */ |
if (plane->wm.fifo_size == 0 && |
wm_state->num_active_planes) |
continue; |
|
plane_extra = min(fifo_extra, fifo_left); |
plane->wm.fifo_size += plane_extra; |
fifo_left -= plane_extra; |
} |
|
#define single_plane_enabled(mask) is_power_of_2(mask) |
WARN_ON(fifo_left != 0); |
} |
|
static void valleyview_update_wm(struct drm_crtc *crtc) |
static void vlv_invert_wms(struct intel_crtc *crtc) |
{ |
struct drm_device *dev = crtc->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; |
int ignore_plane_sr, ignore_cursor_sr; |
unsigned int enabled = 0; |
bool cxsr_enabled; |
struct vlv_wm_state *wm_state = &crtc->wm_state; |
int level; |
|
vlv_update_drain_latency(crtc); |
for (level = 0; level < wm_state->num_levels; level++) { |
struct drm_device *dev = crtc->base.dev; |
const int sr_fifo_size = INTEL_INFO(dev)->num_pipes * 512 - 1; |
struct intel_plane *plane; |
|
if (g4x_compute_wm0(dev, PIPE_A, |
&valleyview_wm_info, pessimal_latency_ns, |
&valleyview_cursor_wm_info, pessimal_latency_ns, |
&planea_wm, &cursora_wm)) |
enabled |= 1 << PIPE_A; |
wm_state->sr[level].plane = sr_fifo_size - wm_state->sr[level].plane; |
wm_state->sr[level].cursor = 63 - wm_state->sr[level].cursor; |
|
if (g4x_compute_wm0(dev, PIPE_B, |
&valleyview_wm_info, pessimal_latency_ns, |
&valleyview_cursor_wm_info, pessimal_latency_ns, |
&planeb_wm, &cursorb_wm)) |
enabled |= 1 << PIPE_B; |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
switch (plane->base.type) { |
int sprite; |
case DRM_PLANE_TYPE_CURSOR: |
wm_state->wm[level].cursor = plane->wm.fifo_size - |
wm_state->wm[level].cursor; |
break; |
case DRM_PLANE_TYPE_PRIMARY: |
wm_state->wm[level].primary = plane->wm.fifo_size - |
wm_state->wm[level].primary; |
break; |
case DRM_PLANE_TYPE_OVERLAY: |
sprite = plane->plane; |
wm_state->wm[level].sprite[sprite] = plane->wm.fifo_size - |
wm_state->wm[level].sprite[sprite]; |
break; |
} |
} |
} |
} |
|
if (single_plane_enabled(enabled) && |
g4x_compute_srwm(dev, ffs(enabled) - 1, |
sr_latency_ns, |
&valleyview_wm_info, |
&valleyview_cursor_wm_info, |
&plane_sr, &ignore_cursor_sr) && |
g4x_compute_srwm(dev, ffs(enabled) - 1, |
2*sr_latency_ns, |
&valleyview_wm_info, |
&valleyview_cursor_wm_info, |
&ignore_plane_sr, &cursor_sr)) { |
cxsr_enabled = true; |
} else { |
cxsr_enabled = false; |
intel_set_memory_cxsr(dev_priv, false); |
plane_sr = cursor_sr = 0; |
static void vlv_compute_wm(struct intel_crtc *crtc) |
{ |
struct drm_device *dev = crtc->base.dev; |
struct vlv_wm_state *wm_state = &crtc->wm_state; |
struct intel_plane *plane; |
int sr_fifo_size = INTEL_INFO(dev)->num_pipes * 512 - 1; |
int level; |
|
memset(wm_state, 0, sizeof(*wm_state)); |
|
wm_state->cxsr = crtc->pipe != PIPE_C && crtc->wm.cxsr_allowed; |
wm_state->num_levels = to_i915(dev)->wm.max_level + 1; |
|
wm_state->num_active_planes = 0; |
|
vlv_compute_fifo(crtc); |
|
if (wm_state->num_active_planes != 1) |
wm_state->cxsr = false; |
|
if (wm_state->cxsr) { |
for (level = 0; level < wm_state->num_levels; level++) { |
wm_state->sr[level].plane = sr_fifo_size; |
wm_state->sr[level].cursor = 63; |
} |
} |
|
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); |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
struct intel_plane_state *state = |
to_intel_plane_state(plane->base.state); |
|
I915_WRITE(DSPFW1, |
(plane_sr << DSPFW_SR_SHIFT) | |
(cursorb_wm << DSPFW_CURSORB_SHIFT) | |
(planeb_wm << DSPFW_PLANEB_SHIFT) | |
(planea_wm << DSPFW_PLANEA_SHIFT)); |
I915_WRITE(DSPFW2, |
(I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | |
(cursora_wm << DSPFW_CURSORA_SHIFT)); |
I915_WRITE(DSPFW3, |
(I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) | |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
if (!state->visible) |
continue; |
|
if (cxsr_enabled) |
intel_set_memory_cxsr(dev_priv, true); |
/* normal watermarks */ |
for (level = 0; level < wm_state->num_levels; level++) { |
int wm = vlv_compute_wm_level(plane, crtc, state, level); |
int max_wm = plane->base.type == DRM_PLANE_TYPE_CURSOR ? 63 : 511; |
|
/* hack */ |
if (WARN_ON(level == 0 && wm > max_wm)) |
wm = max_wm; |
|
if (wm > plane->wm.fifo_size) |
break; |
|
switch (plane->base.type) { |
int sprite; |
case DRM_PLANE_TYPE_CURSOR: |
wm_state->wm[level].cursor = wm; |
break; |
case DRM_PLANE_TYPE_PRIMARY: |
wm_state->wm[level].primary = wm; |
break; |
case DRM_PLANE_TYPE_OVERLAY: |
sprite = plane->plane; |
wm_state->wm[level].sprite[sprite] = wm; |
break; |
} |
} |
|
static void cherryview_update_wm(struct drm_crtc *crtc) |
wm_state->num_levels = level; |
|
if (!wm_state->cxsr) |
continue; |
|
/* maxfifo watermarks */ |
switch (plane->base.type) { |
int sprite, level; |
case DRM_PLANE_TYPE_CURSOR: |
for (level = 0; level < wm_state->num_levels; level++) |
wm_state->sr[level].cursor = |
wm_state->wm[level].cursor; |
break; |
case DRM_PLANE_TYPE_PRIMARY: |
for (level = 0; level < wm_state->num_levels; level++) |
wm_state->sr[level].plane = |
min(wm_state->sr[level].plane, |
wm_state->wm[level].primary); |
break; |
case DRM_PLANE_TYPE_OVERLAY: |
sprite = plane->plane; |
for (level = 0; level < wm_state->num_levels; level++) |
wm_state->sr[level].plane = |
min(wm_state->sr[level].plane, |
wm_state->wm[level].sprite[sprite]); |
break; |
} |
} |
|
/* clear any (partially) filled invalid levels */ |
for (level = wm_state->num_levels; level < to_i915(dev)->wm.max_level + 1; level++) { |
memset(&wm_state->wm[level], 0, sizeof(wm_state->wm[level])); |
memset(&wm_state->sr[level], 0, sizeof(wm_state->sr[level])); |
} |
|
vlv_invert_wms(crtc); |
} |
|
#define VLV_FIFO(plane, value) \ |
(((value) << DSPARB_ ## plane ## _SHIFT_VLV) & DSPARB_ ## plane ## _MASK_VLV) |
|
static void vlv_pipe_set_fifo_size(struct intel_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
static const int sr_latency_ns = 12000; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int planea_wm, planeb_wm, planec_wm; |
int cursora_wm, cursorb_wm, cursorc_wm; |
int plane_sr, cursor_sr; |
int ignore_plane_sr, ignore_cursor_sr; |
unsigned int enabled = 0; |
bool cxsr_enabled; |
struct drm_device *dev = crtc->base.dev; |
struct drm_i915_private *dev_priv = to_i915(dev); |
struct intel_plane *plane; |
int sprite0_start = 0, sprite1_start = 0, fifo_size = 0; |
|
vlv_update_drain_latency(crtc); |
for_each_intel_plane_on_crtc(dev, crtc, plane) { |
if (plane->base.type == DRM_PLANE_TYPE_CURSOR) { |
WARN_ON(plane->wm.fifo_size != 63); |
continue; |
} |
|
if (g4x_compute_wm0(dev, PIPE_A, |
&valleyview_wm_info, pessimal_latency_ns, |
&valleyview_cursor_wm_info, pessimal_latency_ns, |
&planea_wm, &cursora_wm)) |
enabled |= 1 << PIPE_A; |
if (plane->base.type == DRM_PLANE_TYPE_PRIMARY) |
sprite0_start = plane->wm.fifo_size; |
else if (plane->plane == 0) |
sprite1_start = sprite0_start + plane->wm.fifo_size; |
else |
fifo_size = sprite1_start + plane->wm.fifo_size; |
} |
|
if (g4x_compute_wm0(dev, PIPE_B, |
&valleyview_wm_info, pessimal_latency_ns, |
&valleyview_cursor_wm_info, pessimal_latency_ns, |
&planeb_wm, &cursorb_wm)) |
enabled |= 1 << PIPE_B; |
WARN_ON(fifo_size != 512 - 1); |
|
if (g4x_compute_wm0(dev, PIPE_C, |
&valleyview_wm_info, pessimal_latency_ns, |
&valleyview_cursor_wm_info, pessimal_latency_ns, |
&planec_wm, &cursorc_wm)) |
enabled |= 1 << PIPE_C; |
DRM_DEBUG_KMS("Pipe %c FIFO split %d / %d / %d\n", |
pipe_name(crtc->pipe), sprite0_start, |
sprite1_start, fifo_size); |
|
if (single_plane_enabled(enabled) && |
g4x_compute_srwm(dev, ffs(enabled) - 1, |
sr_latency_ns, |
&valleyview_wm_info, |
&valleyview_cursor_wm_info, |
&plane_sr, &ignore_cursor_sr) && |
g4x_compute_srwm(dev, ffs(enabled) - 1, |
2*sr_latency_ns, |
&valleyview_wm_info, |
&valleyview_cursor_wm_info, |
&ignore_plane_sr, &cursor_sr)) { |
cxsr_enabled = true; |
} else { |
cxsr_enabled = false; |
intel_set_memory_cxsr(dev_priv, false); |
plane_sr = cursor_sr = 0; |
switch (crtc->pipe) { |
uint32_t dsparb, dsparb2, dsparb3; |
case PIPE_A: |
dsparb = I915_READ(DSPARB); |
dsparb2 = I915_READ(DSPARB2); |
|
dsparb &= ~(VLV_FIFO(SPRITEA, 0xff) | |
VLV_FIFO(SPRITEB, 0xff)); |
dsparb |= (VLV_FIFO(SPRITEA, sprite0_start) | |
VLV_FIFO(SPRITEB, sprite1_start)); |
|
dsparb2 &= ~(VLV_FIFO(SPRITEA_HI, 0x1) | |
VLV_FIFO(SPRITEB_HI, 0x1)); |
dsparb2 |= (VLV_FIFO(SPRITEA_HI, sprite0_start >> 8) | |
VLV_FIFO(SPRITEB_HI, sprite1_start >> 8)); |
|
I915_WRITE(DSPARB, dsparb); |
I915_WRITE(DSPARB2, dsparb2); |
break; |
case PIPE_B: |
dsparb = I915_READ(DSPARB); |
dsparb2 = I915_READ(DSPARB2); |
|
dsparb &= ~(VLV_FIFO(SPRITEC, 0xff) | |
VLV_FIFO(SPRITED, 0xff)); |
dsparb |= (VLV_FIFO(SPRITEC, sprite0_start) | |
VLV_FIFO(SPRITED, sprite1_start)); |
|
dsparb2 &= ~(VLV_FIFO(SPRITEC_HI, 0xff) | |
VLV_FIFO(SPRITED_HI, 0xff)); |
dsparb2 |= (VLV_FIFO(SPRITEC_HI, sprite0_start >> 8) | |
VLV_FIFO(SPRITED_HI, sprite1_start >> 8)); |
|
I915_WRITE(DSPARB, dsparb); |
I915_WRITE(DSPARB2, dsparb2); |
break; |
case PIPE_C: |
dsparb3 = I915_READ(DSPARB3); |
dsparb2 = I915_READ(DSPARB2); |
|
dsparb3 &= ~(VLV_FIFO(SPRITEE, 0xff) | |
VLV_FIFO(SPRITEF, 0xff)); |
dsparb3 |= (VLV_FIFO(SPRITEE, sprite0_start) | |
VLV_FIFO(SPRITEF, sprite1_start)); |
|
dsparb2 &= ~(VLV_FIFO(SPRITEE_HI, 0xff) | |
VLV_FIFO(SPRITEF_HI, 0xff)); |
dsparb2 |= (VLV_FIFO(SPRITEE_HI, sprite0_start >> 8) | |
VLV_FIFO(SPRITEF_HI, sprite1_start >> 8)); |
|
I915_WRITE(DSPARB3, dsparb3); |
I915_WRITE(DSPARB2, dsparb2); |
break; |
default: |
break; |
} |
} |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, " |
"B: plane=%d, cursor=%d, C: plane=%d, cursor=%d, " |
"SR: plane=%d, cursor=%d\n", |
planea_wm, cursora_wm, |
planeb_wm, cursorb_wm, |
planec_wm, cursorc_wm, |
plane_sr, cursor_sr); |
#undef VLV_FIFO |
|
I915_WRITE(DSPFW1, |
(plane_sr << DSPFW_SR_SHIFT) | |
(cursorb_wm << DSPFW_CURSORB_SHIFT) | |
(planeb_wm << DSPFW_PLANEB_SHIFT) | |
(planea_wm << DSPFW_PLANEA_SHIFT)); |
I915_WRITE(DSPFW2, |
(I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | |
(cursora_wm << DSPFW_CURSORA_SHIFT)); |
I915_WRITE(DSPFW3, |
(I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) | |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
I915_WRITE(DSPFW9_CHV, |
(I915_READ(DSPFW9_CHV) & ~(DSPFW_PLANEC_MASK | |
DSPFW_CURSORC_MASK)) | |
(planec_wm << DSPFW_PLANEC_SHIFT) | |
(cursorc_wm << DSPFW_CURSORC_SHIFT)); |
static void vlv_merge_wm(struct drm_device *dev, |
struct vlv_wm_values *wm) |
{ |
struct intel_crtc *crtc; |
int num_active_crtcs = 0; |
|
if (cxsr_enabled) |
intel_set_memory_cxsr(dev_priv, true); |
wm->level = to_i915(dev)->wm.max_level; |
wm->cxsr = true; |
|
for_each_intel_crtc(dev, crtc) { |
const struct vlv_wm_state *wm_state = &crtc->wm_state; |
|
if (!crtc->active) |
continue; |
|
if (!wm_state->cxsr) |
wm->cxsr = false; |
|
num_active_crtcs++; |
wm->level = min_t(int, wm->level, wm_state->num_levels - 1); |
} |
|
static void valleyview_update_sprite_wm(struct drm_plane *plane, |
struct drm_crtc *crtc, |
uint32_t sprite_width, |
uint32_t sprite_height, |
int pixel_size, |
bool enabled, bool scaled) |
if (num_active_crtcs != 1) |
wm->cxsr = false; |
|
if (num_active_crtcs > 1) |
wm->level = VLV_WM_LEVEL_PM2; |
|
for_each_intel_crtc(dev, crtc) { |
struct vlv_wm_state *wm_state = &crtc->wm_state; |
enum pipe pipe = crtc->pipe; |
|
if (!crtc->active) |
continue; |
|
wm->pipe[pipe] = wm_state->wm[wm->level]; |
if (wm->cxsr) |
wm->sr = wm_state->sr[wm->level]; |
|
wm->ddl[pipe].primary = DDL_PRECISION_HIGH | 2; |
wm->ddl[pipe].sprite[0] = DDL_PRECISION_HIGH | 2; |
wm->ddl[pipe].sprite[1] = DDL_PRECISION_HIGH | 2; |
wm->ddl[pipe].cursor = DDL_PRECISION_HIGH | 2; |
} |
} |
|
static void vlv_update_wm(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe = to_intel_plane(plane)->pipe; |
int sprite = to_intel_plane(plane)->plane; |
int drain_latency; |
int plane_prec; |
int sprite_dl; |
int prec_mult; |
const int high_precision = IS_CHERRYVIEW(dev) ? |
DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_64; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
enum pipe pipe = intel_crtc->pipe; |
struct vlv_wm_values wm = {}; |
|
sprite_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_SPRITE_PRECISION_HIGH(sprite) | |
(DRAIN_LATENCY_MASK << DDL_SPRITE_SHIFT(sprite))); |
vlv_compute_wm(intel_crtc); |
vlv_merge_wm(dev, &wm); |
|
if (enabled && vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, |
&drain_latency)) { |
plane_prec = (prec_mult == high_precision) ? |
DDL_SPRITE_PRECISION_HIGH(sprite) : |
DDL_SPRITE_PRECISION_LOW(sprite); |
sprite_dl |= plane_prec | |
(drain_latency << DDL_SPRITE_SHIFT(sprite)); |
if (memcmp(&dev_priv->wm.vlv, &wm, sizeof(wm)) == 0) { |
/* FIXME should be part of crtc atomic commit */ |
vlv_pipe_set_fifo_size(intel_crtc); |
return; |
} |
|
I915_WRITE(VLV_DDL(pipe), sprite_dl); |
if (wm.level < VLV_WM_LEVEL_DDR_DVFS && |
dev_priv->wm.vlv.level >= VLV_WM_LEVEL_DDR_DVFS) |
chv_set_memory_dvfs(dev_priv, false); |
|
if (wm.level < VLV_WM_LEVEL_PM5 && |
dev_priv->wm.vlv.level >= VLV_WM_LEVEL_PM5) |
chv_set_memory_pm5(dev_priv, false); |
|
if (!wm.cxsr && dev_priv->wm.vlv.cxsr) |
intel_set_memory_cxsr(dev_priv, false); |
|
/* FIXME should be part of crtc atomic commit */ |
vlv_pipe_set_fifo_size(intel_crtc); |
|
vlv_write_wm_values(intel_crtc, &wm); |
|
DRM_DEBUG_KMS("Setting FIFO watermarks - %c: plane=%d, cursor=%d, " |
"sprite0=%d, sprite1=%d, SR: plane=%d, cursor=%d level=%d cxsr=%d\n", |
pipe_name(pipe), wm.pipe[pipe].primary, wm.pipe[pipe].cursor, |
wm.pipe[pipe].sprite[0], wm.pipe[pipe].sprite[1], |
wm.sr.plane, wm.sr.cursor, wm.level, wm.cxsr); |
|
if (wm.cxsr && !dev_priv->wm.vlv.cxsr) |
intel_set_memory_cxsr(dev_priv, true); |
|
if (wm.level >= VLV_WM_LEVEL_PM5 && |
dev_priv->wm.vlv.level < VLV_WM_LEVEL_PM5) |
chv_set_memory_pm5(dev_priv, true); |
|
if (wm.level >= VLV_WM_LEVEL_DDR_DVFS && |
dev_priv->wm.vlv.level < VLV_WM_LEVEL_DDR_DVFS) |
chv_set_memory_dvfs(dev_priv, true); |
|
dev_priv->wm.vlv = wm; |
} |
|
#define single_plane_enabled(mask) is_power_of_2(mask) |
|
static void g4x_update_wm(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
1665,17 → 1400,17 |
plane_sr, cursor_sr); |
|
I915_WRITE(DSPFW1, |
(plane_sr << DSPFW_SR_SHIFT) | |
(cursorb_wm << DSPFW_CURSORB_SHIFT) | |
(planeb_wm << DSPFW_PLANEB_SHIFT) | |
(planea_wm << DSPFW_PLANEA_SHIFT)); |
FW_WM(plane_sr, SR) | |
FW_WM(cursorb_wm, CURSORB) | |
FW_WM(planeb_wm, PLANEB) | |
FW_WM(planea_wm, PLANEA)); |
I915_WRITE(DSPFW2, |
(I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | |
(cursora_wm << DSPFW_CURSORA_SHIFT)); |
FW_WM(cursora_wm, CURSORA)); |
/* HPLL off in SR has some issues on G4x... disable it */ |
I915_WRITE(DSPFW3, |
(I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) | |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
FW_WM(cursor_sr, CURSOR_SR)); |
|
if (cxsr_enabled) |
intel_set_memory_cxsr(dev_priv, true); |
1695,12 → 1430,11 |
if (crtc) { |
/* self-refresh has much higher latency */ |
static const int sr_latency_ns = 12000; |
const struct drm_display_mode *adjusted_mode = |
&to_intel_crtc(crtc)->config.adjusted_mode; |
const struct drm_display_mode *adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
int clock = adjusted_mode->crtc_clock; |
int htotal = adjusted_mode->crtc_htotal; |
int hdisplay = to_intel_crtc(crtc)->config.pipe_src_w; |
int pixel_size = crtc->primary->fb->bits_per_pixel / 8; |
int hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; |
int pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; |
unsigned long line_time_us; |
int entries; |
|
1718,7 → 1452,7 |
entries, srwm); |
|
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * |
pixel_size * to_intel_crtc(crtc)->cursor_width; |
pixel_size * crtc->cursor->state->crtc_w; |
entries = DIV_ROUND_UP(entries, |
i965_cursor_wm_info.cacheline_size); |
cursor_sr = i965_cursor_wm_info.fifo_size - |
1741,19 → 1475,21 |
srwm); |
|
/* 965 has limitations... */ |
I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | |
(8 << DSPFW_CURSORB_SHIFT) | |
(8 << DSPFW_PLANEB_SHIFT) | |
(8 << DSPFW_PLANEA_SHIFT)); |
I915_WRITE(DSPFW2, (8 << DSPFW_CURSORA_SHIFT) | |
(8 << DSPFW_PLANEC_SHIFT_OLD)); |
I915_WRITE(DSPFW1, FW_WM(srwm, SR) | |
FW_WM(8, CURSORB) | |
FW_WM(8, PLANEB) | |
FW_WM(8, PLANEA)); |
I915_WRITE(DSPFW2, FW_WM(8, CURSORA) | |
FW_WM(8, PLANEC_OLD)); |
/* update cursor SR watermark */ |
I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); |
I915_WRITE(DSPFW3, FW_WM(cursor_sr, CURSOR_SR)); |
|
if (cxsr_enabled) |
intel_set_memory_cxsr(dev_priv, true); |
} |
|
#undef FW_WM |
|
static void i9xx_update_wm(struct drm_crtc *unused_crtc) |
{ |
struct drm_device *dev = unused_crtc->dev; |
1777,11 → 1513,11 |
crtc = intel_get_crtc_for_plane(dev, 0); |
if (intel_crtc_active(crtc)) { |
const struct drm_display_mode *adjusted_mode; |
int cpp = crtc->primary->fb->bits_per_pixel / 8; |
int cpp = crtc->primary->state->fb->bits_per_pixel / 8; |
if (IS_GEN2(dev)) |
cpp = 4; |
|
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, |
wm_info, fifo_size, cpp, |
pessimal_latency_ns); |
1799,11 → 1535,11 |
crtc = intel_get_crtc_for_plane(dev, 1); |
if (intel_crtc_active(crtc)) { |
const struct drm_display_mode *adjusted_mode; |
int cpp = crtc->primary->fb->bits_per_pixel / 8; |
int cpp = crtc->primary->state->fb->bits_per_pixel / 8; |
if (IS_GEN2(dev)) |
cpp = 4; |
|
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock, |
wm_info, fifo_size, cpp, |
pessimal_latency_ns); |
1822,7 → 1558,7 |
if (IS_I915GM(dev) && enabled) { |
struct drm_i915_gem_object *obj; |
|
obj = intel_fb_obj(enabled->primary->fb); |
obj = intel_fb_obj(enabled->primary->state->fb); |
|
/* self-refresh seems busted with untiled */ |
if (obj->tiling_mode == I915_TILING_NONE) |
1841,12 → 1577,11 |
if (HAS_FW_BLC(dev) && enabled) { |
/* self-refresh has much higher latency */ |
static const int sr_latency_ns = 6000; |
const struct drm_display_mode *adjusted_mode = |
&to_intel_crtc(enabled)->config.adjusted_mode; |
const struct drm_display_mode *adjusted_mode = &to_intel_crtc(enabled)->config->base.adjusted_mode; |
int clock = adjusted_mode->crtc_clock; |
int htotal = adjusted_mode->crtc_htotal; |
int hdisplay = to_intel_crtc(enabled)->config.pipe_src_w; |
int pixel_size = enabled->primary->fb->bits_per_pixel / 8; |
int hdisplay = to_intel_crtc(enabled)->config->pipe_src_w; |
int pixel_size = enabled->primary->state->fb->bits_per_pixel / 8; |
unsigned long line_time_us; |
int entries; |
|
1898,7 → 1633,7 |
if (crtc == NULL) |
return; |
|
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; |
adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; |
planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, |
&i845_wm_info, |
dev_priv->display.get_fifo_size(dev, 0), |
1911,23 → 1646,22 |
I915_WRITE(FW_BLC, fwater_lo); |
} |
|
static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev, |
struct drm_crtc *crtc) |
uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
uint32_t pixel_rate; |
|
pixel_rate = intel_crtc->config.adjusted_mode.crtc_clock; |
pixel_rate = pipe_config->base.adjusted_mode.crtc_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) { |
if (pipe_config->pch_pfit.enabled) { |
uint64_t pipe_w, pipe_h, pfit_w, pfit_h; |
uint32_t pfit_size = intel_crtc->config.pch_pfit.size; |
uint32_t pfit_size = pipe_config->pch_pfit.size; |
|
pipe_w = intel_crtc->config.pipe_src_w; |
pipe_h = intel_crtc->config.pipe_src_h; |
pipe_w = pipe_config->pipe_src_w; |
pipe_h = pipe_config->pipe_src_h; |
|
pfit_w = (pfit_size >> 16) & 0xFFFF; |
pfit_h = pfit_size & 0xFFFF; |
if (pipe_w < pfit_w) |
1984,18 → 1718,8 |
uint32_t pipe_htotal; |
uint32_t pixel_rate; /* in KHz */ |
struct intel_plane_wm_parameters plane[I915_MAX_PLANES]; |
struct intel_plane_wm_parameters cursor; |
}; |
|
struct ilk_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 ilk_wm_maximums { |
uint16_t pri; |
uint16_t spr; |
2014,26 → 1738,26 |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_pri_wm(const struct ilk_pipe_wm_parameters *params, |
static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, |
const struct intel_plane_state *pstate, |
uint32_t mem_value, |
bool is_lp) |
{ |
int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; |
uint32_t method1, method2; |
|
if (!params->active || !params->pri.enabled) |
if (!cstate->base.active || !pstate->visible) |
return 0; |
|
method1 = ilk_wm_method1(params->pixel_rate, |
params->pri.bytes_per_pixel, |
mem_value); |
method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), bpp, 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, |
method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), |
cstate->base.adjusted_mode.crtc_htotal, |
drm_rect_width(&pstate->dst), |
bpp, |
mem_value); |
|
return min(method1, method2); |
2043,22 → 1767,22 |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_spr_wm(const struct ilk_pipe_wm_parameters *params, |
static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate, |
const struct intel_plane_state *pstate, |
uint32_t mem_value) |
{ |
int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; |
uint32_t method1, method2; |
|
if (!params->active || !params->spr.enabled) |
if (!cstate->base.active || !pstate->visible) |
return 0; |
|
method1 = ilk_wm_method1(params->pixel_rate, |
params->spr.bytes_per_pixel, |
method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), bpp, mem_value); |
method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), |
cstate->base.adjusted_mode.crtc_htotal, |
drm_rect_width(&pstate->dst), |
bpp, |
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); |
} |
|
2066,29 → 1790,33 |
* For both WM_PIPE and WM_LP. |
* mem_value must be in 0.1us units. |
*/ |
static uint32_t ilk_compute_cur_wm(const struct ilk_pipe_wm_parameters *params, |
static uint32_t ilk_compute_cur_wm(const struct intel_crtc_state *cstate, |
const struct intel_plane_state *pstate, |
uint32_t mem_value) |
{ |
if (!params->active || !params->cur.enabled) |
int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; |
|
if (!cstate->base.active || !pstate->visible) |
return 0; |
|
return ilk_wm_method2(params->pixel_rate, |
params->pipe_htotal, |
params->cur.horiz_pixels, |
params->cur.bytes_per_pixel, |
return ilk_wm_method2(ilk_pipe_pixel_rate(cstate), |
cstate->base.adjusted_mode.crtc_htotal, |
drm_rect_width(&pstate->dst), |
bpp, |
mem_value); |
} |
|
/* Only for WM_LP. */ |
static uint32_t ilk_compute_fbc_wm(const struct ilk_pipe_wm_parameters *params, |
static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate, |
const struct intel_plane_state *pstate, |
uint32_t pri_val) |
{ |
if (!params->active || !params->pri.enabled) |
int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; |
|
if (!cstate->base.active || !pstate->visible) |
return 0; |
|
return ilk_wm_fbc(pri_val, |
params->pri.horiz_pixels, |
params->pri.bytes_per_pixel); |
return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->dst), bpp); |
} |
|
static unsigned int ilk_display_fifo_size(const struct drm_device *dev) |
2253,10 → 1981,12 |
} |
|
static void ilk_compute_wm_level(const struct drm_i915_private *dev_priv, |
const struct intel_crtc *intel_crtc, |
int level, |
const struct ilk_pipe_wm_parameters *p, |
struct intel_crtc_state *cstate, |
struct intel_wm_level *result) |
{ |
struct intel_plane *intel_plane; |
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]; |
2268,10 → 1998,29 |
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); |
for_each_intel_plane_on_crtc(dev_priv->dev, intel_crtc, intel_plane) { |
struct intel_plane_state *pstate = |
to_intel_plane_state(intel_plane->base.state); |
|
switch (intel_plane->base.type) { |
case DRM_PLANE_TYPE_PRIMARY: |
result->pri_val = ilk_compute_pri_wm(cstate, pstate, |
pri_latency, |
level); |
result->fbc_val = ilk_compute_fbc_wm(cstate, pstate, |
result->pri_val); |
break; |
case DRM_PLANE_TYPE_OVERLAY: |
result->spr_val = ilk_compute_spr_wm(cstate, pstate, |
spr_latency); |
break; |
case DRM_PLANE_TYPE_CURSOR: |
result->cur_val = ilk_compute_cur_wm(cstate, pstate, |
cur_latency); |
break; |
} |
} |
|
result->enable = true; |
} |
|
2280,19 → 2029,19 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode; |
const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode; |
u32 linetime, ips_linetime; |
|
if (!intel_crtc_active(crtc)) |
if (!intel_crtc->active) |
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. |
* */ |
linetime = DIV_ROUND_CLOSEST(mode->crtc_htotal * 1000 * 8, |
mode->crtc_clock); |
ips_linetime = DIV_ROUND_CLOSEST(mode->crtc_htotal * 1000 * 8, |
intel_ddi_get_cdclk_freq(dev_priv)); |
linetime = DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, |
adjusted_mode->crtc_clock); |
ips_linetime = DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, |
dev_priv->cdclk_freq); |
|
return PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) | |
PIPE_WM_LINETIME_TIME(linetime); |
2349,6 → 2098,8 |
GEN9_MEM_LATENCY_LEVEL_MASK; |
|
/* |
* WaWmMemoryReadLatency:skl |
* |
* punit doesn't take into account the read latency so we need |
* to add 2us to the various latency levels we retrieve from |
* the punit. |
2421,7 → 2172,7 |
int ilk_wm_max_level(const struct drm_device *dev) |
{ |
/* how many WM levels are we expecting */ |
if (IS_GEN9(dev)) |
if (INTEL_INFO(dev)->gen >= 9) |
return 7; |
else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
return 4; |
2528,38 → 2279,6 |
intel_print_wm_latency(dev, "Gen9 Plane", dev_priv->wm.skl_latency); |
} |
|
static void ilk_compute_wm_parameters(struct drm_crtc *crtc, |
struct ilk_pipe_wm_parameters *p) |
{ |
struct drm_device *dev = crtc->dev; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
enum pipe pipe = intel_crtc->pipe; |
struct drm_plane *plane; |
|
if (!intel_crtc_active(crtc)) |
return; |
|
p->active = true; |
p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal; |
p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc); |
p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8; |
p->cur.bytes_per_pixel = 4; |
p->pri.horiz_pixels = intel_crtc->config.pipe_src_w; |
p->cur.horiz_pixels = intel_crtc->cursor_width; |
/* TODO: for now, assume primary and cursor planes are always enabled. */ |
p->pri.enabled = true; |
p->cur.enabled = true; |
|
drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) { |
struct intel_plane *intel_plane = to_intel_plane(plane); |
|
if (intel_plane->pipe == pipe) { |
p->spr = intel_plane->wm; |
break; |
} |
} |
} |
|
static void ilk_compute_wm_config(struct drm_device *dev, |
struct intel_wm_config *config) |
{ |
2579,34 → 2298,47 |
} |
|
/* Compute new watermarks for the pipe */ |
static bool intel_compute_pipe_wm(struct drm_crtc *crtc, |
const struct ilk_pipe_wm_parameters *params, |
static bool intel_compute_pipe_wm(struct intel_crtc_state *cstate, |
struct intel_pipe_wm *pipe_wm) |
{ |
struct drm_crtc *crtc = cstate->base.crtc; |
struct drm_device *dev = crtc->dev; |
const struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_plane *intel_plane; |
struct intel_plane_state *sprstate = NULL; |
int level, max_level = ilk_wm_max_level(dev); |
/* LP0 watermark maximums depend on this pipe alone */ |
struct intel_wm_config config = { |
.num_pipes_active = 1, |
.sprites_enabled = params->spr.enabled, |
.sprites_scaled = params->spr.scaled, |
}; |
struct ilk_wm_maximums max; |
|
pipe_wm->pipe_enabled = params->active; |
pipe_wm->sprites_enabled = params->spr.enabled; |
pipe_wm->sprites_scaled = params->spr.scaled; |
for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { |
if (intel_plane->base.type == DRM_PLANE_TYPE_OVERLAY) { |
sprstate = to_intel_plane_state(intel_plane->base.state); |
break; |
} |
} |
|
config.sprites_enabled = sprstate->visible; |
config.sprites_scaled = sprstate->visible && |
(drm_rect_width(&sprstate->dst) != drm_rect_width(&sprstate->src) >> 16 || |
drm_rect_height(&sprstate->dst) != drm_rect_height(&sprstate->src) >> 16); |
|
pipe_wm->pipe_enabled = cstate->base.active; |
pipe_wm->sprites_enabled = sprstate->visible; |
pipe_wm->sprites_scaled = config.sprites_scaled; |
|
/* ILK/SNB: LP2+ watermarks only w/o sprites */ |
if (INTEL_INFO(dev)->gen <= 6 && params->spr.enabled) |
if (INTEL_INFO(dev)->gen <= 6 && sprstate->visible) |
max_level = 1; |
|
/* ILK/SNB/IVB: LP1+ watermarks only w/o scaling */ |
if (params->spr.scaled) |
if (config.sprites_scaled) |
max_level = 0; |
|
ilk_compute_wm_level(dev_priv, 0, params, &pipe_wm->wm[0]); |
ilk_compute_wm_level(dev_priv, intel_crtc, 0, cstate, &pipe_wm->wm[0]); |
|
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc); |
2623,7 → 2355,7 |
for (level = 1; level <= max_level; level++) { |
struct intel_wm_level wm = {}; |
|
ilk_compute_wm_level(dev_priv, level, params, &wm); |
ilk_compute_wm_level(dev_priv, intel_crtc, level, cstate, &wm); |
|
/* |
* Disable any watermark level that exceeds the |
2680,6 → 2412,7 |
const struct ilk_wm_maximums *max, |
struct intel_pipe_wm *merged) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int level, max_level = ilk_wm_max_level(dev); |
int last_enabled_level = max_level; |
|
2720,7 → 2453,8 |
* What we should check here is whether FBC can be |
* enabled sometime later. |
*/ |
if (IS_GEN5(dev) && !merged->fbc_wm_enabled && intel_fbc_enabled(dev)) { |
if (IS_GEN5(dev) && !merged->fbc_wm_enabled && |
intel_fbc_enabled(dev_priv)) { |
for (level = 2; level <= max_level; level++) { |
struct intel_wm_level *wm = &merged->wm[level]; |
|
3024,6 → 2758,7 |
*/ |
|
#define SKL_DDB_SIZE 896 /* in blocks */ |
#define BXT_DDB_SIZE 512 |
|
static void |
skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, |
3042,6 → 2777,9 |
return; |
} |
|
if (IS_BROXTON(dev)) |
ddb_size = BXT_DDB_SIZE; |
else |
ddb_size = SKL_DDB_SIZE; |
|
ddb_size -= 4; /* 4 blocks for bypass path allocation */ |
3048,7 → 2786,7 |
|
nth_active_pipe = 0; |
for_each_crtc(dev, crtc) { |
if (!intel_crtc_active(crtc)) |
if (!to_intel_crtc(crtc)->active) |
continue; |
|
if (crtc == for_crtc) |
3081,13 → 2819,17 |
void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, |
struct skl_ddb_allocation *ddb /* out */) |
{ |
struct drm_device *dev = dev_priv->dev; |
enum pipe pipe; |
int plane; |
u32 val; |
|
memset(ddb, 0, sizeof(*ddb)); |
|
for_each_pipe(dev_priv, pipe) { |
for_each_plane(pipe, plane) { |
if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) |
continue; |
|
for_each_plane(dev_priv, pipe, plane) { |
val = I915_READ(PLANE_BUF_CFG(pipe, plane)); |
skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane], |
val); |
3094,13 → 2836,24 |
} |
|
val = I915_READ(CUR_BUF_CFG(pipe)); |
skl_ddb_entry_init_from_hw(&ddb->cursor[pipe], val); |
skl_ddb_entry_init_from_hw(&ddb->plane[pipe][PLANE_CURSOR], |
val); |
} |
} |
|
static unsigned int |
skl_plane_relative_data_rate(const struct intel_plane_wm_parameters *p) |
skl_plane_relative_data_rate(const struct intel_plane_wm_parameters *p, int y) |
{ |
|
/* for planar format */ |
if (p->y_bytes_per_pixel) { |
if (y) /* y-plane data rate */ |
return p->horiz_pixels * p->vert_pixels * p->y_bytes_per_pixel; |
else /* uv-plane data rate */ |
return (p->horiz_pixels/2) * (p->vert_pixels/2) * p->bytes_per_pixel; |
} |
|
/* for packed formats */ |
return p->horiz_pixels * p->vert_pixels * p->bytes_per_pixel; |
} |
|
3123,8 → 2876,11 |
if (!p->enabled) |
continue; |
|
total_data_rate += skl_plane_relative_data_rate(p); |
total_data_rate += skl_plane_relative_data_rate(p, 0); /* packed/uv */ |
if (p->y_bytes_per_pixel) { |
total_data_rate += skl_plane_relative_data_rate(p, 1); /* y-plane */ |
} |
} |
|
return total_data_rate; |
} |
3136,10 → 2892,13 |
struct skl_ddb_allocation *ddb /* out */) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
enum pipe pipe = intel_crtc->pipe; |
struct skl_ddb_entry *alloc = &ddb->pipe[pipe]; |
uint16_t alloc_size, start, cursor_blocks; |
uint16_t minimum[I915_MAX_PLANES]; |
uint16_t y_minimum[I915_MAX_PLANES]; |
unsigned int total_data_rate; |
int plane; |
|
3147,20 → 2906,35 |
alloc_size = skl_ddb_entry_size(alloc); |
if (alloc_size == 0) { |
memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe])); |
memset(&ddb->cursor[pipe], 0, sizeof(ddb->cursor[pipe])); |
memset(&ddb->plane[pipe][PLANE_CURSOR], 0, |
sizeof(ddb->plane[pipe][PLANE_CURSOR])); |
return; |
} |
|
cursor_blocks = skl_cursor_allocation(config); |
ddb->cursor[pipe].start = alloc->end - cursor_blocks; |
ddb->cursor[pipe].end = alloc->end; |
ddb->plane[pipe][PLANE_CURSOR].start = alloc->end - cursor_blocks; |
ddb->plane[pipe][PLANE_CURSOR].end = alloc->end; |
|
alloc_size -= cursor_blocks; |
alloc->end -= cursor_blocks; |
|
/* 1. Allocate the mininum required blocks for each active plane */ |
for_each_plane(dev_priv, pipe, plane) { |
const struct intel_plane_wm_parameters *p; |
|
p = ¶ms->plane[plane]; |
if (!p->enabled) |
continue; |
|
minimum[plane] = 8; |
alloc_size -= minimum[plane]; |
y_minimum[plane] = p->y_bytes_per_pixel ? 8 : 0; |
alloc_size -= y_minimum[plane]; |
} |
|
/* |
* Each active plane get a portion of the remaining space, in |
* proportion to the amount of data they need to fetch from memory. |
* 2. Distribute the remaining space in proportion to the amount of |
* data each plane needs to fetch from memory. |
* |
* FIXME: we may not allocate every single block here. |
*/ |
3169,20 → 2943,22 |
start = alloc->start; |
for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) { |
const struct intel_plane_wm_parameters *p; |
unsigned int data_rate; |
uint16_t plane_blocks; |
unsigned int data_rate, y_data_rate; |
uint16_t plane_blocks, y_plane_blocks = 0; |
|
p = ¶ms->plane[plane]; |
if (!p->enabled) |
continue; |
|
data_rate = skl_plane_relative_data_rate(p); |
data_rate = skl_plane_relative_data_rate(p, 0); |
|
/* |
* allocation for (packed formats) or (uv-plane part of planar format): |
* promote the expression to 64 bits to avoid overflowing, the |
* result is < available as data_rate / total_data_rate < 1 |
*/ |
plane_blocks = div_u64((uint64_t)alloc_size * data_rate, |
plane_blocks = minimum[plane]; |
plane_blocks += div_u64((uint64_t)alloc_size * data_rate, |
total_data_rate); |
|
ddb->plane[pipe][plane].start = start; |
3189,14 → 2965,30 |
ddb->plane[pipe][plane].end = start + plane_blocks; |
|
start += plane_blocks; |
|
/* |
* allocation for y_plane part of planar format: |
*/ |
if (p->y_bytes_per_pixel) { |
y_data_rate = skl_plane_relative_data_rate(p, 1); |
y_plane_blocks = y_minimum[plane]; |
y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate, |
total_data_rate); |
|
ddb->y_plane[pipe][plane].start = start; |
ddb->y_plane[pipe][plane].end = start + y_plane_blocks; |
|
start += y_plane_blocks; |
} |
|
} |
|
static uint32_t skl_pipe_pixel_rate(const struct intel_crtc_config *config) |
} |
|
static uint32_t skl_pipe_pixel_rate(const struct intel_crtc_state *config) |
{ |
/* TODO: Take into account the scalers once we support them */ |
return config->adjusted_mode.crtc_clock; |
return config->base.adjusted_mode.crtc_clock; |
} |
|
/* |
3213,7 → 3005,7 |
if (latency == 0) |
return UINT_MAX; |
|
wm_intermediate_val = latency * pixel_rate * bytes_per_pixel; |
wm_intermediate_val = latency * pixel_rate * bytes_per_pixel / 512; |
ret = DIV_ROUND_UP(wm_intermediate_val, 1000); |
|
return ret; |
3221,17 → 3013,29 |
|
static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, |
uint32_t horiz_pixels, uint8_t bytes_per_pixel, |
uint32_t latency) |
uint64_t tiling, uint32_t latency) |
{ |
uint32_t ret, plane_bytes_per_line, wm_intermediate_val; |
uint32_t ret; |
uint32_t plane_bytes_per_line, plane_blocks_per_line; |
uint32_t wm_intermediate_val; |
|
if (latency == 0) |
return UINT_MAX; |
|
plane_bytes_per_line = horiz_pixels * bytes_per_pixel; |
|
if (tiling == I915_FORMAT_MOD_Y_TILED || |
tiling == I915_FORMAT_MOD_Yf_TILED) { |
plane_bytes_per_line *= 4; |
plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); |
plane_blocks_per_line /= 4; |
} else { |
plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); |
} |
|
wm_intermediate_val = latency * pixel_rate; |
ret = DIV_ROUND_UP(wm_intermediate_val, pipe_htotal * 1000) * |
plane_bytes_per_line; |
plane_blocks_per_line; |
|
return ret; |
} |
3248,8 → 3052,8 |
sizeof(new_ddb->plane[pipe]))) |
return true; |
|
if (memcmp(&new_ddb->cursor[pipe], &cur_ddb->cursor[pipe], |
sizeof(new_ddb->cursor[pipe]))) |
if (memcmp(&new_ddb->plane[pipe][PLANE_CURSOR], &cur_ddb->plane[pipe][PLANE_CURSOR], |
sizeof(new_ddb->plane[pipe][PLANE_CURSOR]))) |
return true; |
|
return false; |
3262,7 → 3066,7 |
struct drm_plane *plane; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
config->num_pipes_active += intel_crtc_active(crtc); |
config->num_pipes_active += to_intel_crtc(crtc)->active; |
|
/* FIXME: I don't think we need those two global parameters on SKL */ |
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { |
3280,71 → 3084,129 |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
enum pipe pipe = intel_crtc->pipe; |
struct drm_plane *plane; |
struct drm_framebuffer *fb; |
int i = 1; /* Index for sprite planes start */ |
|
p->active = intel_crtc_active(crtc); |
p->active = intel_crtc->active; |
if (p->active) { |
p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal; |
p->pixel_rate = skl_pipe_pixel_rate(&intel_crtc->config); |
p->pipe_htotal = intel_crtc->config->base.adjusted_mode.crtc_htotal; |
p->pixel_rate = skl_pipe_pixel_rate(intel_crtc->config); |
|
/* |
* For now, assume primary and cursor planes are always enabled. |
*/ |
fb = crtc->primary->state->fb; |
/* For planar: Bpp is for uv plane, y_Bpp is for y plane */ |
if (fb) { |
p->plane[0].enabled = true; |
p->plane[0].bytes_per_pixel = |
crtc->primary->fb->bits_per_pixel / 8; |
p->plane[0].horiz_pixels = intel_crtc->config.pipe_src_w; |
p->plane[0].vert_pixels = intel_crtc->config.pipe_src_h; |
p->plane[0].bytes_per_pixel = fb->pixel_format == DRM_FORMAT_NV12 ? |
drm_format_plane_cpp(fb->pixel_format, 1) : |
drm_format_plane_cpp(fb->pixel_format, 0); |
p->plane[0].y_bytes_per_pixel = fb->pixel_format == DRM_FORMAT_NV12 ? |
drm_format_plane_cpp(fb->pixel_format, 0) : 0; |
p->plane[0].tiling = fb->modifier[0]; |
} else { |
p->plane[0].enabled = false; |
p->plane[0].bytes_per_pixel = 0; |
p->plane[0].y_bytes_per_pixel = 0; |
p->plane[0].tiling = DRM_FORMAT_MOD_NONE; |
} |
p->plane[0].horiz_pixels = intel_crtc->config->pipe_src_w; |
p->plane[0].vert_pixels = intel_crtc->config->pipe_src_h; |
p->plane[0].rotation = crtc->primary->state->rotation; |
|
p->cursor.enabled = true; |
p->cursor.bytes_per_pixel = 4; |
p->cursor.horiz_pixels = intel_crtc->cursor_width ? |
intel_crtc->cursor_width : 64; |
fb = crtc->cursor->state->fb; |
p->plane[PLANE_CURSOR].y_bytes_per_pixel = 0; |
if (fb) { |
p->plane[PLANE_CURSOR].enabled = true; |
p->plane[PLANE_CURSOR].bytes_per_pixel = fb->bits_per_pixel / 8; |
p->plane[PLANE_CURSOR].horiz_pixels = crtc->cursor->state->crtc_w; |
p->plane[PLANE_CURSOR].vert_pixels = crtc->cursor->state->crtc_h; |
} else { |
p->plane[PLANE_CURSOR].enabled = false; |
p->plane[PLANE_CURSOR].bytes_per_pixel = 0; |
p->plane[PLANE_CURSOR].horiz_pixels = 64; |
p->plane[PLANE_CURSOR].vert_pixels = 64; |
} |
} |
|
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { |
struct intel_plane *intel_plane = to_intel_plane(plane); |
|
if (intel_plane->pipe == pipe) |
if (intel_plane->pipe == pipe && |
plane->type == DRM_PLANE_TYPE_OVERLAY) |
p->plane[i++] = intel_plane->wm; |
} |
} |
|
static bool skl_compute_plane_wm(struct skl_pipe_wm_parameters *p, |
static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, |
struct skl_pipe_wm_parameters *p, |
struct intel_plane_wm_parameters *p_params, |
uint16_t ddb_allocation, |
uint32_t mem_value, |
int level, |
uint16_t *out_blocks, /* out */ |
uint8_t *out_lines /* out */) |
{ |
uint32_t method1, method2, plane_bytes_per_line, res_blocks, res_lines; |
uint32_t result_bytes; |
uint32_t latency = dev_priv->wm.skl_latency[level]; |
uint32_t method1, method2; |
uint32_t plane_bytes_per_line, plane_blocks_per_line; |
uint32_t res_blocks, res_lines; |
uint32_t selected_result; |
uint8_t bytes_per_pixel; |
|
if (mem_value == 0 || !p->active || !p_params->enabled) |
if (latency == 0 || !p->active || !p_params->enabled) |
return false; |
|
bytes_per_pixel = p_params->y_bytes_per_pixel ? |
p_params->y_bytes_per_pixel : |
p_params->bytes_per_pixel; |
method1 = skl_wm_method1(p->pixel_rate, |
p_params->bytes_per_pixel, |
mem_value); |
bytes_per_pixel, |
latency); |
method2 = skl_wm_method2(p->pixel_rate, |
p->pipe_htotal, |
p_params->horiz_pixels, |
p_params->bytes_per_pixel, |
mem_value); |
bytes_per_pixel, |
p_params->tiling, |
latency); |
|
plane_bytes_per_line = p_params->horiz_pixels * |
p_params->bytes_per_pixel; |
plane_bytes_per_line = p_params->horiz_pixels * bytes_per_pixel; |
plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); |
|
/* For now xtile and linear */ |
if (((ddb_allocation * 512) / plane_bytes_per_line) >= 1) |
result_bytes = min(method1, method2); |
if (p_params->tiling == I915_FORMAT_MOD_Y_TILED || |
p_params->tiling == I915_FORMAT_MOD_Yf_TILED) { |
uint32_t min_scanlines = 4; |
uint32_t y_tile_minimum; |
if (intel_rotation_90_or_270(p_params->rotation)) { |
switch (p_params->bytes_per_pixel) { |
case 1: |
min_scanlines = 16; |
break; |
case 2: |
min_scanlines = 8; |
break; |
case 8: |
WARN(1, "Unsupported pixel depth for rotation"); |
} |
} |
y_tile_minimum = plane_blocks_per_line * min_scanlines; |
selected_result = max(method2, y_tile_minimum); |
} else { |
if ((ddb_allocation / plane_blocks_per_line) >= 1) |
selected_result = min(method1, method2); |
else |
result_bytes = method1; |
selected_result = method1; |
} |
|
res_blocks = DIV_ROUND_UP(result_bytes, 512) + 1; |
res_lines = DIV_ROUND_UP(result_bytes, plane_bytes_per_line); |
res_blocks = selected_result + 1; |
res_lines = DIV_ROUND_UP(selected_result, plane_blocks_per_line); |
|
if (res_blocks > ddb_allocation || res_lines > 31) |
if (level >= 1 && level <= 7) { |
if (p_params->tiling == I915_FORMAT_MOD_Y_TILED || |
p_params->tiling == I915_FORMAT_MOD_Yf_TILED) |
res_lines += 4; |
else |
res_blocks++; |
} |
|
if (res_blocks >= ddb_allocation || res_lines > 31) |
return false; |
|
*out_blocks = res_blocks; |
3361,7 → 3223,6 |
int num_planes, |
struct skl_wm_level *result) |
{ |
uint16_t latency = dev_priv->wm.skl_latency[level]; |
uint16_t ddb_blocks; |
int i; |
|
3368,27 → 3229,32 |
for (i = 0; i < num_planes; i++) { |
ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][i]); |
|
result->plane_en[i] = skl_compute_plane_wm(p, &p->plane[i], |
result->plane_en[i] = skl_compute_plane_wm(dev_priv, |
p, &p->plane[i], |
ddb_blocks, |
latency, |
level, |
&result->plane_res_b[i], |
&result->plane_res_l[i]); |
} |
|
ddb_blocks = skl_ddb_entry_size(&ddb->cursor[pipe]); |
result->cursor_en = skl_compute_plane_wm(p, &p->cursor, ddb_blocks, |
latency, &result->cursor_res_b, |
&result->cursor_res_l); |
ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][PLANE_CURSOR]); |
result->plane_en[PLANE_CURSOR] = skl_compute_plane_wm(dev_priv, p, |
&p->plane[PLANE_CURSOR], |
ddb_blocks, level, |
&result->plane_res_b[PLANE_CURSOR], |
&result->plane_res_l[PLANE_CURSOR]); |
} |
|
static uint32_t |
skl_compute_linetime_wm(struct drm_crtc *crtc, struct skl_pipe_wm_parameters *p) |
{ |
if (!intel_crtc_active(crtc)) |
if (!to_intel_crtc(crtc)->active) |
return 0; |
|
if (WARN_ON(p->pixel_rate == 0)) |
return 0; |
|
return DIV_ROUND_UP(8 * p->pipe_htotal * 1000, p->pixel_rate); |
|
} |
|
static void skl_compute_transition_wm(struct drm_crtc *crtc, |
3404,7 → 3270,7 |
/* Until we know more, just disable transition WMs */ |
for (i = 0; i < intel_num_planes(intel_crtc); i++) |
trans_wm->plane_en[i] = false; |
trans_wm->cursor_en = false; |
trans_wm->plane_en[PLANE_CURSOR] = false; |
} |
|
static void skl_compute_pipe_wm(struct drm_crtc *crtc, |
3453,13 → 3319,13 |
|
temp = 0; |
|
temp |= p_wm->wm[level].cursor_res_l << PLANE_WM_LINES_SHIFT; |
temp |= p_wm->wm[level].cursor_res_b; |
temp |= p_wm->wm[level].plane_res_l[PLANE_CURSOR] << PLANE_WM_LINES_SHIFT; |
temp |= p_wm->wm[level].plane_res_b[PLANE_CURSOR]; |
|
if (p_wm->wm[level].cursor_en) |
if (p_wm->wm[level].plane_en[PLANE_CURSOR]) |
temp |= PLANE_WM_EN; |
|
r->cursor[pipe][level] = temp; |
r->plane[pipe][PLANE_CURSOR][level] = temp; |
|
} |
|
3475,12 → 3341,12 |
} |
|
temp = 0; |
temp |= p_wm->trans_wm.cursor_res_l << PLANE_WM_LINES_SHIFT; |
temp |= p_wm->trans_wm.cursor_res_b; |
if (p_wm->trans_wm.cursor_en) |
temp |= p_wm->trans_wm.plane_res_l[PLANE_CURSOR] << PLANE_WM_LINES_SHIFT; |
temp |= p_wm->trans_wm.plane_res_b[PLANE_CURSOR]; |
if (p_wm->trans_wm.plane_en[PLANE_CURSOR]) |
temp |= PLANE_WM_EN; |
|
r->cursor_trans[pipe] = temp; |
r->plane_trans[pipe][PLANE_CURSOR] = temp; |
|
r->wm_linetime[pipe] = p_wm->linetime; |
} |
3514,20 → 3380,25 |
I915_WRITE(PLANE_WM(pipe, i, level), |
new->plane[pipe][i][level]); |
I915_WRITE(CUR_WM(pipe, level), |
new->cursor[pipe][level]); |
new->plane[pipe][PLANE_CURSOR][level]); |
} |
for (i = 0; i < intel_num_planes(crtc); i++) |
I915_WRITE(PLANE_WM_TRANS(pipe, i), |
new->plane_trans[pipe][i]); |
I915_WRITE(CUR_WM_TRANS(pipe), new->cursor_trans[pipe]); |
I915_WRITE(CUR_WM_TRANS(pipe), |
new->plane_trans[pipe][PLANE_CURSOR]); |
|
for (i = 0; i < intel_num_planes(crtc); i++) |
for (i = 0; i < intel_num_planes(crtc); i++) { |
skl_ddb_entry_write(dev_priv, |
PLANE_BUF_CFG(pipe, i), |
&new->ddb.plane[pipe][i]); |
skl_ddb_entry_write(dev_priv, |
PLANE_NV12_BUF_CFG(pipe, i), |
&new->ddb.y_plane[pipe][i]); |
} |
|
skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe), |
&new->ddb.cursor[pipe]); |
&new->ddb.plane[pipe][PLANE_CURSOR]); |
} |
} |
|
3558,12 → 3429,11 |
static void |
skl_wm_flush_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int pass) |
{ |
struct drm_device *dev = dev_priv->dev; |
int plane; |
|
DRM_DEBUG_KMS("flush pipe %c (pass %d)\n", pipe_name(pipe), pass); |
|
for_each_plane(pipe, plane) { |
for_each_plane(dev_priv, pipe, plane) { |
I915_WRITE(PLANE_SURF(pipe, plane), |
I915_READ(PLANE_SURF(pipe, plane))); |
} |
3590,7 → 3460,7 |
{ |
struct drm_device *dev = dev_priv->dev; |
struct skl_ddb_allocation *cur_ddb, *new_ddb; |
bool reallocated[I915_MAX_PIPES] = {false, false, false}; |
bool reallocated[I915_MAX_PIPES] = {}; |
struct intel_crtc *crtc; |
enum pipe pipe; |
|
3640,10 → 3510,9 |
skl_ddb_entry_size(&cur_ddb->pipe[pipe])) { |
skl_wm_flush_pipe(dev_priv, pipe, 2); |
intel_wait_for_vblank(dev, pipe); |
} |
|
reallocated[pipe] = true; |
} |
} |
|
/* |
* Third pass: flush the pipes that got more space allocated. |
3684,6 → 3553,7 |
return false; |
|
intel_crtc->wm.skl_active = *pipe_wm; |
|
return true; |
} |
|
3736,6 → 3606,26 |
} |
} |
|
static void skl_clear_wm(struct skl_wm_values *watermarks, enum pipe pipe) |
{ |
watermarks->wm_linetime[pipe] = 0; |
memset(watermarks->plane[pipe], 0, |
sizeof(uint32_t) * 8 * I915_MAX_PLANES); |
memset(watermarks->plane_trans[pipe], |
0, sizeof(uint32_t) * I915_MAX_PLANES); |
watermarks->plane_trans[pipe][PLANE_CURSOR] = 0; |
|
/* Clear ddb entries for pipe */ |
memset(&watermarks->ddb.pipe[pipe], 0, sizeof(struct skl_ddb_entry)); |
memset(&watermarks->ddb.plane[pipe], 0, |
sizeof(struct skl_ddb_entry) * I915_MAX_PLANES); |
memset(&watermarks->ddb.y_plane[pipe], 0, |
sizeof(struct skl_ddb_entry) * I915_MAX_PLANES); |
memset(&watermarks->ddb.plane[pipe][PLANE_CURSOR], 0, |
sizeof(struct skl_ddb_entry)); |
|
} |
|
static void skl_update_wm(struct drm_crtc *crtc) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
3746,8 → 3636,12 |
struct skl_pipe_wm pipe_wm = {}; |
struct intel_wm_config config = {}; |
|
memset(results, 0, sizeof(*results)); |
|
/* Clear all dirty flags */ |
memset(results->dirty, 0, sizeof(bool) * I915_MAX_PIPES); |
|
skl_clear_wm(results, intel_crtc->pipe); |
|
skl_compute_wm_global_parameters(dev, &config); |
|
if (!skl_update_pipe_wm(crtc, ¶ms, &config, |
3771,13 → 3665,30 |
int pixel_size, bool enabled, bool scaled) |
{ |
struct intel_plane *intel_plane = to_intel_plane(plane); |
struct drm_framebuffer *fb = plane->state->fb; |
|
intel_plane->wm.enabled = enabled; |
intel_plane->wm.scaled = scaled; |
intel_plane->wm.horiz_pixels = sprite_width; |
intel_plane->wm.vert_pixels = sprite_height; |
intel_plane->wm.bytes_per_pixel = pixel_size; |
intel_plane->wm.tiling = DRM_FORMAT_MOD_NONE; |
|
/* For planar: Bpp is for UV plane, y_Bpp is for Y plane */ |
intel_plane->wm.bytes_per_pixel = |
(fb && fb->pixel_format == DRM_FORMAT_NV12) ? |
drm_format_plane_cpp(plane->state->fb->pixel_format, 1) : pixel_size; |
intel_plane->wm.y_bytes_per_pixel = |
(fb && fb->pixel_format == DRM_FORMAT_NV12) ? |
drm_format_plane_cpp(plane->state->fb->pixel_format, 0) : 0; |
|
/* |
* Framebuffer can be NULL on plane disable, but it does not |
* matter for watermarks if we assume no tiling in that case. |
*/ |
if (fb) |
intel_plane->wm.tiling = fb->modifier[0]; |
intel_plane->wm.rotation = plane->state->rotation; |
|
skl_update_wm(crtc); |
} |
|
3784,10 → 3695,10 |
static void ilk_update_wm(struct drm_crtc *crtc) |
{ |
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); |
struct drm_device *dev = crtc->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct ilk_wm_maximums max; |
struct ilk_pipe_wm_parameters params = {}; |
struct ilk_wm_values results = {}; |
enum intel_ddb_partitioning partitioning; |
struct intel_pipe_wm pipe_wm = {}; |
3794,9 → 3705,9 |
struct intel_pipe_wm lp_wm_1_2 = {}, lp_wm_5_6 = {}, *best_lp_wm; |
struct intel_wm_config config = {}; |
|
ilk_compute_wm_parameters(crtc, ¶ms); |
WARN_ON(cstate->base.active != intel_crtc->active); |
|
intel_compute_pipe_wm(crtc, ¶ms, &pipe_wm); |
intel_compute_pipe_wm(cstate, &pipe_wm); |
|
if (!memcmp(&intel_crtc->wm.active, &pipe_wm, sizeof(pipe_wm))) |
return; |
3836,12 → 3747,6 |
struct drm_device *dev = plane->dev; |
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.vert_pixels = sprite_width; |
intel_plane->wm.bytes_per_pixel = pixel_size; |
|
/* |
* IVB workaround: must disable low power watermarks for at least |
* one frame before enabling scaling. LP watermarks can be re-enabled |
3873,10 → 3778,10 |
(val >> PLANE_WM_LINES_SHIFT) & |
PLANE_WM_LINES_MASK; |
} else { |
active->wm[level].cursor_en = is_enabled; |
active->wm[level].cursor_res_b = |
active->wm[level].plane_en[PLANE_CURSOR] = is_enabled; |
active->wm[level].plane_res_b[PLANE_CURSOR] = |
val & PLANE_WM_BLOCKS_MASK; |
active->wm[level].cursor_res_l = |
active->wm[level].plane_res_l[PLANE_CURSOR] = |
(val >> PLANE_WM_LINES_SHIFT) & |
PLANE_WM_LINES_MASK; |
} |
3889,10 → 3794,10 |
(val >> PLANE_WM_LINES_SHIFT) & |
PLANE_WM_LINES_MASK; |
} else { |
active->trans_wm.cursor_en = is_enabled; |
active->trans_wm.cursor_res_b = |
active->trans_wm.plane_en[PLANE_CURSOR] = is_enabled; |
active->trans_wm.plane_res_b[PLANE_CURSOR] = |
val & PLANE_WM_BLOCKS_MASK; |
active->trans_wm.cursor_res_l = |
active->trans_wm.plane_res_l[PLANE_CURSOR] = |
(val >> PLANE_WM_LINES_SHIFT) & |
PLANE_WM_LINES_MASK; |
} |
3918,14 → 3823,14 |
for (i = 0; i < intel_num_planes(intel_crtc); i++) |
hw->plane[pipe][i][level] = |
I915_READ(PLANE_WM(pipe, i, level)); |
hw->cursor[pipe][level] = I915_READ(CUR_WM(pipe, level)); |
hw->plane[pipe][PLANE_CURSOR][level] = I915_READ(CUR_WM(pipe, level)); |
} |
|
for (i = 0; i < intel_num_planes(intel_crtc); i++) |
hw->plane_trans[pipe][i] = I915_READ(PLANE_WM_TRANS(pipe, i)); |
hw->cursor_trans[pipe] = I915_READ(CUR_WM_TRANS(pipe)); |
hw->plane_trans[pipe][PLANE_CURSOR] = I915_READ(CUR_WM_TRANS(pipe)); |
|
if (!intel_crtc_active(crtc)) |
if (!intel_crtc->active) |
return; |
|
hw->dirty[pipe] = true; |
3938,7 → 3843,7 |
skl_pipe_wm_active_state(temp, active, false, |
false, i, level); |
} |
temp = hw->cursor[pipe][level]; |
temp = hw->plane[pipe][PLANE_CURSOR][level]; |
skl_pipe_wm_active_state(temp, active, false, true, i, level); |
} |
|
3947,7 → 3852,7 |
skl_pipe_wm_active_state(temp, active, true, false, i, 0); |
} |
|
temp = hw->cursor_trans[pipe]; |
temp = hw->plane_trans[pipe][PLANE_CURSOR]; |
skl_pipe_wm_active_state(temp, active, true, true, i, 0); |
} |
|
3980,7 → 3885,7 |
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe)); |
|
active->pipe_enabled = intel_crtc_active(crtc); |
active->pipe_enabled = intel_crtc->active; |
|
if (active->pipe_enabled) { |
u32 tmp = hw->wm_pipe[pipe]; |
4009,6 → 3914,159 |
} |
} |
|
#define _FW_WM(value, plane) \ |
(((value) & DSPFW_ ## plane ## _MASK) >> DSPFW_ ## plane ## _SHIFT) |
#define _FW_WM_VLV(value, plane) \ |
(((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT) |
|
static void vlv_read_wm_values(struct drm_i915_private *dev_priv, |
struct vlv_wm_values *wm) |
{ |
enum pipe pipe; |
uint32_t tmp; |
|
for_each_pipe(dev_priv, pipe) { |
tmp = I915_READ(VLV_DDL(pipe)); |
|
wm->ddl[pipe].primary = |
(tmp >> DDL_PLANE_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); |
wm->ddl[pipe].cursor = |
(tmp >> DDL_CURSOR_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); |
wm->ddl[pipe].sprite[0] = |
(tmp >> DDL_SPRITE_SHIFT(0)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); |
wm->ddl[pipe].sprite[1] = |
(tmp >> DDL_SPRITE_SHIFT(1)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); |
} |
|
tmp = I915_READ(DSPFW1); |
wm->sr.plane = _FW_WM(tmp, SR); |
wm->pipe[PIPE_B].cursor = _FW_WM(tmp, CURSORB); |
wm->pipe[PIPE_B].primary = _FW_WM_VLV(tmp, PLANEB); |
wm->pipe[PIPE_A].primary = _FW_WM_VLV(tmp, PLANEA); |
|
tmp = I915_READ(DSPFW2); |
wm->pipe[PIPE_A].sprite[1] = _FW_WM_VLV(tmp, SPRITEB); |
wm->pipe[PIPE_A].cursor = _FW_WM(tmp, CURSORA); |
wm->pipe[PIPE_A].sprite[0] = _FW_WM_VLV(tmp, SPRITEA); |
|
tmp = I915_READ(DSPFW3); |
wm->sr.cursor = _FW_WM(tmp, CURSOR_SR); |
|
if (IS_CHERRYVIEW(dev_priv)) { |
tmp = I915_READ(DSPFW7_CHV); |
wm->pipe[PIPE_B].sprite[1] = _FW_WM_VLV(tmp, SPRITED); |
wm->pipe[PIPE_B].sprite[0] = _FW_WM_VLV(tmp, SPRITEC); |
|
tmp = I915_READ(DSPFW8_CHV); |
wm->pipe[PIPE_C].sprite[1] = _FW_WM_VLV(tmp, SPRITEF); |
wm->pipe[PIPE_C].sprite[0] = _FW_WM_VLV(tmp, SPRITEE); |
|
tmp = I915_READ(DSPFW9_CHV); |
wm->pipe[PIPE_C].primary = _FW_WM_VLV(tmp, PLANEC); |
wm->pipe[PIPE_C].cursor = _FW_WM(tmp, CURSORC); |
|
tmp = I915_READ(DSPHOWM); |
wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; |
wm->pipe[PIPE_C].sprite[1] |= _FW_WM(tmp, SPRITEF_HI) << 8; |
wm->pipe[PIPE_C].sprite[0] |= _FW_WM(tmp, SPRITEE_HI) << 8; |
wm->pipe[PIPE_C].primary |= _FW_WM(tmp, PLANEC_HI) << 8; |
wm->pipe[PIPE_B].sprite[1] |= _FW_WM(tmp, SPRITED_HI) << 8; |
wm->pipe[PIPE_B].sprite[0] |= _FW_WM(tmp, SPRITEC_HI) << 8; |
wm->pipe[PIPE_B].primary |= _FW_WM(tmp, PLANEB_HI) << 8; |
wm->pipe[PIPE_A].sprite[1] |= _FW_WM(tmp, SPRITEB_HI) << 8; |
wm->pipe[PIPE_A].sprite[0] |= _FW_WM(tmp, SPRITEA_HI) << 8; |
wm->pipe[PIPE_A].primary |= _FW_WM(tmp, PLANEA_HI) << 8; |
} else { |
tmp = I915_READ(DSPFW7); |
wm->pipe[PIPE_B].sprite[1] = _FW_WM_VLV(tmp, SPRITED); |
wm->pipe[PIPE_B].sprite[0] = _FW_WM_VLV(tmp, SPRITEC); |
|
tmp = I915_READ(DSPHOWM); |
wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; |
wm->pipe[PIPE_B].sprite[1] |= _FW_WM(tmp, SPRITED_HI) << 8; |
wm->pipe[PIPE_B].sprite[0] |= _FW_WM(tmp, SPRITEC_HI) << 8; |
wm->pipe[PIPE_B].primary |= _FW_WM(tmp, PLANEB_HI) << 8; |
wm->pipe[PIPE_A].sprite[1] |= _FW_WM(tmp, SPRITEB_HI) << 8; |
wm->pipe[PIPE_A].sprite[0] |= _FW_WM(tmp, SPRITEA_HI) << 8; |
wm->pipe[PIPE_A].primary |= _FW_WM(tmp, PLANEA_HI) << 8; |
} |
} |
|
#undef _FW_WM |
#undef _FW_WM_VLV |
|
void vlv_wm_get_hw_state(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = to_i915(dev); |
struct vlv_wm_values *wm = &dev_priv->wm.vlv; |
struct intel_plane *plane; |
enum pipe pipe; |
u32 val; |
|
vlv_read_wm_values(dev_priv, wm); |
|
for_each_intel_plane(dev, plane) { |
switch (plane->base.type) { |
int sprite; |
case DRM_PLANE_TYPE_CURSOR: |
plane->wm.fifo_size = 63; |
break; |
case DRM_PLANE_TYPE_PRIMARY: |
plane->wm.fifo_size = vlv_get_fifo_size(dev, plane->pipe, 0); |
break; |
case DRM_PLANE_TYPE_OVERLAY: |
sprite = plane->plane; |
plane->wm.fifo_size = vlv_get_fifo_size(dev, plane->pipe, sprite + 1); |
break; |
} |
} |
|
wm->cxsr = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; |
wm->level = VLV_WM_LEVEL_PM2; |
|
if (IS_CHERRYVIEW(dev_priv)) { |
mutex_lock(&dev_priv->rps.hw_lock); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ); |
if (val & DSP_MAXFIFO_PM5_ENABLE) |
wm->level = VLV_WM_LEVEL_PM5; |
|
/* |
* If DDR DVFS is disabled in the BIOS, Punit |
* will never ack the request. So if that happens |
* assume we don't have to enable/disable DDR DVFS |
* dynamically. To test that just set the REQ_ACK |
* bit to poke the Punit, but don't change the |
* HIGH/LOW bits so that we don't actually change |
* the current state. |
*/ |
val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); |
val |= FORCE_DDR_FREQ_REQ_ACK; |
vlv_punit_write(dev_priv, PUNIT_REG_DDR_SETUP2, val); |
|
if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2) & |
FORCE_DDR_FREQ_REQ_ACK) == 0, 3)) { |
DRM_DEBUG_KMS("Punit not acking DDR DVFS request, " |
"assuming DDR DVFS is disabled\n"); |
dev_priv->wm.max_level = VLV_WM_LEVEL_PM5; |
} else { |
val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); |
if ((val & FORCE_DDR_HIGH_FREQ) == 0) |
wm->level = VLV_WM_LEVEL_DDR_DVFS; |
} |
|
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
|
for_each_pipe(dev_priv, pipe) |
DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite0=%d, sprite1=%d\n", |
pipe_name(pipe), wm->pipe[pipe].primary, wm->pipe[pipe].cursor, |
wm->pipe[pipe].sprite[0], wm->pipe[pipe].sprite[1]); |
|
DRM_DEBUG_KMS("Initial watermarks: SR plane=%d, SR cursor=%d level=%d cxsr=%d\n", |
wm->sr.plane, wm->sr.cursor, wm->level, wm->cxsr); |
} |
|
void ilk_wm_get_hw_state(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
4094,41 → 4152,6 |
pixel_size, enabled, scaled); |
} |
|
static struct drm_i915_gem_object * |
intel_alloc_context_page(struct drm_device *dev) |
{ |
struct drm_i915_gem_object *ctx; |
int ret; |
|
WARN_ON(!mutex_is_locked(&dev->struct_mutex)); |
|
ctx = i915_gem_alloc_object(dev, 4096); |
if (!ctx) { |
DRM_DEBUG("failed to alloc power context, RC6 disabled\n"); |
return NULL; |
} |
|
ret = i915_gem_obj_ggtt_pin(ctx, 4096, 0); |
if (ret) { |
DRM_ERROR("failed to pin power context: %d\n", ret); |
goto err_unref; |
} |
|
ret = i915_gem_object_set_to_gtt_domain(ctx, 1); |
if (ret) { |
DRM_ERROR("failed to set-domain on power context: %d\n", ret); |
goto err_unpin; |
} |
|
return ctx; |
|
err_unpin: |
i915_gem_object_ggtt_unpin(ctx); |
err_unref: |
drm_gem_object_unreference(&ctx->base); |
return NULL; |
} |
|
/** |
* Lock protecting IPS related data structures |
*/ |
4190,7 → 4213,7 |
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> |
MEMMODE_FSTART_SHIFT; |
|
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> |
vstart = (I915_READ(PXVFREQ(fstart)) & PXVFREQ_PX_MASK) >> |
PXVFREQ_PX_SHIFT; |
|
dev_priv->ips.fmax = fmax; /* IPS callback will increase this */ |
4221,10 → 4244,10 |
|
ironlake_set_drps(dev, fstart); |
|
dev_priv->ips.last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) + |
I915_READ(0x112e0); |
dev_priv->ips.last_count1 = I915_READ(DMIEC) + |
I915_READ(DDREC) + I915_READ(CSIEC); |
dev_priv->ips.last_time1 = jiffies_to_msecs(jiffies); |
dev_priv->ips.last_count2 = I915_READ(0x112f4); |
dev_priv->ips.last_count2 = I915_READ(GFXEC); |
dev_priv->ips.last_time2 = ktime_get_raw_ns(); |
|
spin_unlock_irq(&mchdev_lock); |
4261,7 → 4284,7 |
* ourselves, instead of doing a rmw cycle (which might result in us clearing |
* all limits and the gpu stuck at whatever frequency it is at atm). |
*/ |
static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) |
static u32 intel_rps_limits(struct drm_i915_private *dev_priv, u8 val) |
{ |
u32 limits; |
|
4271,9 → 4294,15 |
* the hw runs at the minimal clock before selecting the desired |
* frequency, if the down threshold expires in that window we will not |
* receive a down interrupt. */ |
if (IS_GEN9(dev_priv->dev)) { |
limits = (dev_priv->rps.max_freq_softlimit) << 23; |
if (val <= dev_priv->rps.min_freq_softlimit) |
limits |= (dev_priv->rps.min_freq_softlimit) << 14; |
} else { |
limits = dev_priv->rps.max_freq_softlimit << 24; |
if (val <= dev_priv->rps.min_freq_softlimit) |
limits |= dev_priv->rps.min_freq_softlimit << 16; |
} |
|
return limits; |
} |
4281,6 → 4310,8 |
static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val) |
{ |
int new_power; |
u32 threshold_up = 0, threshold_down = 0; /* in % */ |
u32 ei_up = 0, ei_down = 0; |
|
new_power = dev_priv->rps.power; |
switch (dev_priv->rps.power) { |
4302,9 → 4333,9 |
break; |
} |
/* Max/min bins are special */ |
if (val == dev_priv->rps.min_freq_softlimit) |
if (val <= dev_priv->rps.min_freq_softlimit) |
new_power = LOW_POWER; |
if (val == dev_priv->rps.max_freq_softlimit) |
if (val >= dev_priv->rps.max_freq_softlimit) |
new_power = HIGH_POWER; |
if (new_power == dev_priv->rps.power) |
return; |
4313,49 → 4344,45 |
switch (new_power) { |
case LOW_POWER: |
/* Upclock if more than 95% busy over 16ms */ |
I915_WRITE(GEN6_RP_UP_EI, 12500); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 11800); |
ei_up = 16000; |
threshold_up = 95; |
|
/* Downclock if less than 85% busy over 32ms */ |
I915_WRITE(GEN6_RP_DOWN_EI, 25000); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 21250); |
|
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_AVG); |
ei_down = 32000; |
threshold_down = 85; |
break; |
|
case BETWEEN: |
/* Upclock if more than 90% busy over 13ms */ |
I915_WRITE(GEN6_RP_UP_EI, 10250); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 9225); |
ei_up = 13000; |
threshold_up = 90; |
|
/* Downclock if less than 75% busy over 32ms */ |
I915_WRITE(GEN6_RP_DOWN_EI, 25000); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 18750); |
|
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_AVG); |
ei_down = 32000; |
threshold_down = 75; |
break; |
|
case HIGH_POWER: |
/* Upclock if more than 85% busy over 10ms */ |
I915_WRITE(GEN6_RP_UP_EI, 8000); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 6800); |
ei_up = 10000; |
threshold_up = 85; |
|
/* Downclock if less than 60% busy over 32ms */ |
I915_WRITE(GEN6_RP_DOWN_EI, 25000); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 15000); |
ei_down = 32000; |
threshold_down = 60; |
break; |
} |
|
I915_WRITE(GEN6_RP_UP_EI, |
GT_INTERVAL_FROM_US(dev_priv, ei_up)); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, |
GT_INTERVAL_FROM_US(dev_priv, (ei_up * threshold_up / 100))); |
|
I915_WRITE(GEN6_RP_DOWN_EI, |
GT_INTERVAL_FROM_US(dev_priv, ei_down)); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, |
GT_INTERVAL_FROM_US(dev_priv, (ei_down * threshold_down / 100))); |
|
I915_WRITE(GEN6_RP_CONTROL, |
GEN6_RP_MEDIA_TURBO | |
GEN6_RP_MEDIA_HW_NORMAL_MODE | |
4363,10 → 4390,10 |
GEN6_RP_ENABLE | |
GEN6_RP_UP_BUSY_AVG | |
GEN6_RP_DOWN_IDLE_AVG); |
break; |
} |
|
dev_priv->rps.power = new_power; |
dev_priv->rps.up_threshold = threshold_up; |
dev_priv->rps.down_threshold = threshold_down; |
dev_priv->rps.last_adj = 0; |
} |
|
4375,35 → 4402,29 |
u32 mask = 0; |
|
if (val > dev_priv->rps.min_freq_softlimit) |
mask |= GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT; |
mask |= GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT; |
if (val < dev_priv->rps.max_freq_softlimit) |
mask |= GEN6_PM_RP_UP_THRESHOLD; |
mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_UP_THRESHOLD; |
|
mask |= dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED); |
mask &= dev_priv->pm_rps_events; |
|
/* IVB and SNB hard hangs on looping batchbuffer |
* if GEN6_PM_UP_EI_EXPIRED is masked. |
*/ |
if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev)) |
mask |= GEN6_PM_RP_UP_EI_EXPIRED; |
|
if (IS_GEN8(dev_priv->dev)) |
mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP; |
|
return ~mask; |
return gen6_sanitize_rps_pm_mask(dev_priv, ~mask); |
} |
|
/* gen6_set_rps is called to update the frequency request, but should also be |
* called when the range (min_delay and max_delay) is modified so that we can |
* update the GEN6_RP_INTERRUPT_LIMITS register accordingly. */ |
void gen6_set_rps(struct drm_device *dev, u8 val) |
static void gen6_set_rps(struct drm_device *dev, u8 val) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* WaGsvDisableTurbo: Workaround to disable turbo on BXT A* */ |
if (IS_BROXTON(dev) && (INTEL_REVID(dev) < BXT_REVID_B0)) |
return; |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
WARN_ON(val > dev_priv->rps.max_freq_softlimit); |
WARN_ON(val < dev_priv->rps.min_freq_softlimit); |
WARN_ON(val > dev_priv->rps.max_freq); |
WARN_ON(val < dev_priv->rps.min_freq); |
|
/* min/max delay may still have been modified so be sure to |
* write the limits value. |
4411,8 → 4432,11 |
if (val != dev_priv->rps.cur_freq) { |
gen6_set_rps_thresholds(dev_priv, val); |
|
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
if (IS_GEN9(dev)) |
I915_WRITE(GEN6_RPNSWREQ, |
GEN9_FREQUENCY(val)); |
else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
I915_WRITE(GEN6_RPNSWREQ, |
HSW_FREQUENCY(val)); |
else |
I915_WRITE(GEN6_RPNSWREQ, |
4424,79 → 4448,73 |
/* Make sure we continue to get interrupts |
* until we hit the minimum or maximum frequencies. |
*/ |
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, gen6_rps_limits(dev_priv, val)); |
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, intel_rps_limits(dev_priv, val)); |
I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val)); |
|
POSTING_READ(GEN6_RPNSWREQ); |
|
dev_priv->rps.cur_freq = val; |
trace_intel_gpu_freq_change(val * 50); |
trace_intel_gpu_freq_change(intel_gpu_freq(dev_priv, val)); |
} |
|
/* vlv_set_rps_idle: Set the frequency to Rpn if Gfx clocks are down |
* |
* * If Gfx is Idle, then |
* 1. Mask Turbo interrupts |
* 2. Bring up Gfx clock |
* 3. Change the freq to Rpn and wait till P-Unit updates freq |
* 4. Clear the Force GFX CLK ON bit so that Gfx can down |
* 5. Unmask Turbo interrupts |
*/ |
static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) |
static void valleyview_set_rps(struct drm_device *dev, u8 val) |
{ |
struct drm_device *dev = dev_priv->dev; |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* Latest VLV doesn't need to force the gfx clock */ |
if (dev->pdev->revision >= 0xd) { |
valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
return; |
} |
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
WARN_ON(val > dev_priv->rps.max_freq); |
WARN_ON(val < dev_priv->rps.min_freq); |
|
/* |
* When we are idle. Drop to min voltage state. |
*/ |
if (WARN_ONCE(IS_CHERRYVIEW(dev) && (val & 1), |
"Odd GPU freq value\n")) |
val &= ~1; |
|
if (dev_priv->rps.cur_freq <= dev_priv->rps.min_freq_softlimit) |
return; |
I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val)); |
|
/* Mask turbo interrupt so that they will not come in between */ |
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); |
if (val != dev_priv->rps.cur_freq) { |
vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); |
if (!IS_CHERRYVIEW(dev_priv)) |
gen6_set_rps_thresholds(dev_priv, val); |
} |
|
vlv_force_gfx_clock(dev_priv, true); |
dev_priv->rps.cur_freq = val; |
trace_intel_gpu_freq_change(intel_gpu_freq(dev_priv, val)); |
} |
|
dev_priv->rps.cur_freq = dev_priv->rps.min_freq_softlimit; |
/* vlv_set_rps_idle: Set the frequency to idle, if Gfx clocks are down |
* |
* * If Gfx is Idle, then |
* 1. Forcewake Media well. |
* 2. Request idle freq. |
* 3. Release Forcewake of Media well. |
*/ |
static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) |
{ |
u32 val = dev_priv->rps.idle_freq; |
|
vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, |
dev_priv->rps.min_freq_softlimit); |
if (dev_priv->rps.cur_freq <= val) |
return; |
|
if (wait_for(((vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS)) |
& GENFREQSTATUS) == 0, 100)) |
DRM_ERROR("timed out waiting for Punit\n"); |
|
vlv_force_gfx_clock(dev_priv, false); |
|
I915_WRITE(GEN6_PMINTRMSK, |
gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); |
/* Wake up the media well, as that takes a lot less |
* power than the Render well. */ |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_MEDIA); |
valleyview_set_rps(dev_priv->dev, val); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_MEDIA); |
} |
|
void gen6_rps_idle(struct drm_i915_private *dev_priv) |
void gen6_rps_busy(struct drm_i915_private *dev_priv) |
{ |
struct drm_device *dev = dev_priv->dev; |
|
mutex_lock(&dev_priv->rps.hw_lock); |
if (dev_priv->rps.enabled) { |
if (IS_CHERRYVIEW(dev)) |
valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
else if (IS_VALLEYVIEW(dev)) |
vlv_set_rps_idle(dev_priv); |
else |
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
dev_priv->rps.last_adj = 0; |
if (dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED)) |
gen6_rps_reset_ei(dev_priv); |
I915_WRITE(GEN6_PMINTRMSK, |
gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); |
} |
mutex_unlock(&dev_priv->rps.hw_lock); |
} |
|
void gen6_rps_boost(struct drm_i915_private *dev_priv) |
void gen6_rps_idle(struct drm_i915_private *dev_priv) |
{ |
struct drm_device *dev = dev_priv->dev; |
|
4503,33 → 4521,62 |
mutex_lock(&dev_priv->rps.hw_lock); |
if (dev_priv->rps.enabled) { |
if (IS_VALLEYVIEW(dev)) |
valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); |
vlv_set_rps_idle(dev_priv); |
else |
gen6_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); |
gen6_set_rps(dev_priv->dev, dev_priv->rps.idle_freq); |
dev_priv->rps.last_adj = 0; |
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); |
} |
mutex_unlock(&dev_priv->rps.hw_lock); |
|
spin_lock(&dev_priv->rps.client_lock); |
while (!list_empty(&dev_priv->rps.clients)) |
list_del_init(dev_priv->rps.clients.next); |
spin_unlock(&dev_priv->rps.client_lock); |
} |
|
void valleyview_set_rps(struct drm_device *dev, u8 val) |
void gen6_rps_boost(struct drm_i915_private *dev_priv, |
struct intel_rps_client *rps, |
unsigned long submitted) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
/* This is intentionally racy! We peek at the state here, then |
* validate inside the RPS worker. |
*/ |
if (!(dev_priv->mm.busy && |
dev_priv->rps.enabled && |
dev_priv->rps.cur_freq < dev_priv->rps.max_freq_softlimit)) |
return; |
|
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); |
WARN_ON(val > dev_priv->rps.max_freq_softlimit); |
WARN_ON(val < dev_priv->rps.min_freq_softlimit); |
/* Force a RPS boost (and don't count it against the client) if |
* the GPU is severely congested. |
*/ |
if (rps && time_after(jiffies, submitted + DRM_I915_THROTTLE_JIFFIES)) |
rps = NULL; |
|
if (WARN_ONCE(IS_CHERRYVIEW(dev) && (val & 1), |
"Odd GPU freq value\n")) |
val &= ~1; |
spin_lock(&dev_priv->rps.client_lock); |
if (rps == NULL || list_empty(&rps->link)) { |
spin_lock_irq(&dev_priv->irq_lock); |
if (dev_priv->rps.interrupts_enabled) { |
dev_priv->rps.client_boost = true; |
queue_work(dev_priv->wq, &dev_priv->rps.work); |
} |
spin_unlock_irq(&dev_priv->irq_lock); |
|
if (val != dev_priv->rps.cur_freq) |
vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); |
if (rps != NULL) { |
list_add(&rps->link, &dev_priv->rps.clients); |
rps->boosts++; |
} else |
dev_priv->rps.boosts++; |
} |
spin_unlock(&dev_priv->rps.client_lock); |
} |
|
I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val)); |
|
dev_priv->rps.cur_freq = val; |
trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv, val)); |
void intel_set_rps(struct drm_device *dev, u8 val) |
{ |
if (IS_VALLEYVIEW(dev)) |
valleyview_set_rps(dev, val); |
else |
gen6_set_rps(dev, val); |
} |
|
static void gen9_disable_rps(struct drm_device *dev) |
4537,6 → 4584,7 |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(GEN6_RC_CONTROL, 0); |
I915_WRITE(GEN9_PG_ENABLE, 0); |
} |
|
static void gen6_disable_rps(struct drm_device *dev) |
4560,11 → 4608,11 |
|
/* we're doing forcewake before Disabling RC6, |
* This what the BIOS expects when going into suspend */ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
I915_WRITE(GEN6_RC_CONTROL, 0); |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
static void intel_print_rc6_info(struct drm_device *dev, u32 mode) |
4588,14 → 4636,10 |
|
static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6) |
{ |
/* No RC6 before Ironlake */ |
if (INTEL_INFO(dev)->gen < 5) |
/* No RC6 before Ironlake and code is gone for ilk. */ |
if (INTEL_INFO(dev)->gen < 6) |
return 0; |
|
/* RC6 is only on Ironlake mobile not on desktop */ |
if (INTEL_INFO(dev)->gen == 5 && !IS_IRONLAKE_M(dev)) |
return 0; |
|
/* Respect the kernel parameter if it is set */ |
if (enable_rc6 >= 0) { |
int mask; |
4613,10 → 4657,6 |
return enable_rc6 & mask; |
} |
|
/* Disable RC6 on Ironlake */ |
if (INTEL_INFO(dev)->gen == 5) |
return 0; |
|
if (IS_IVYBRIDGE(dev)) |
return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE); |
|
4635,26 → 4675,49 |
u32 ddcc_status = 0; |
int ret; |
|
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); |
/* All of these values are in units of 50MHz */ |
dev_priv->rps.cur_freq = 0; |
/* static values from HW: RP0 > RP1 > RPn (min_freq) */ |
if (IS_BROXTON(dev)) { |
rp_state_cap = I915_READ(BXT_RP_STATE_CAP); |
dev_priv->rps.rp0_freq = (rp_state_cap >> 16) & 0xff; |
dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; |
dev_priv->rps.min_freq = (rp_state_cap >> 0) & 0xff; |
} else { |
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); |
dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff; |
dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; |
dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff; |
} |
|
/* hw_max = RP0 until we check for overclocking */ |
dev_priv->rps.max_freq = dev_priv->rps.rp0_freq; |
|
dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq; |
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { |
if (IS_HASWELL(dev) || IS_BROADWELL(dev) || IS_SKYLAKE(dev)) { |
ret = sandybridge_pcode_read(dev_priv, |
HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL, |
&ddcc_status); |
if (0 == ret) |
dev_priv->rps.efficient_freq = |
(ddcc_status >> 8) & 0xff; |
clamp_t(u8, |
((ddcc_status >> 8) & 0xff), |
dev_priv->rps.min_freq, |
dev_priv->rps.max_freq); |
} |
|
if (IS_SKYLAKE(dev)) { |
/* Store the frequency values in 16.66 MHZ units, which is |
the natural hardware unit for SKL */ |
dev_priv->rps.rp0_freq *= GEN9_FREQ_SCALER; |
dev_priv->rps.rp1_freq *= GEN9_FREQ_SCALER; |
dev_priv->rps.min_freq *= GEN9_FREQ_SCALER; |
dev_priv->rps.max_freq *= GEN9_FREQ_SCALER; |
dev_priv->rps.efficient_freq *= GEN9_FREQ_SCALER; |
} |
|
dev_priv->rps.idle_freq = dev_priv->rps.min_freq; |
|
/* Preserve min/max settings in case of re-init */ |
if (dev_priv->rps.max_freq_softlimit == 0) |
dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; |
4662,8 → 4725,8 |
if (dev_priv->rps.min_freq_softlimit == 0) { |
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) |
dev_priv->rps.min_freq_softlimit = |
/* max(RPe, 450 MHz) */ |
max(dev_priv->rps.efficient_freq, (u8) 9); |
max_t(int, dev_priv->rps.efficient_freq, |
intel_freq_opcode(dev_priv, 450)); |
else |
dev_priv->rps.min_freq_softlimit = |
dev_priv->rps.min_freq; |
4670,9 → 4733,43 |
} |
} |
|
/* See the Gen9_GT_PM_Programming_Guide doc for the below */ |
static void gen9_enable_rps(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
gen6_init_rps_frequencies(dev); |
|
/* WaGsvDisableTurbo: Workaround to disable turbo on BXT A* */ |
if (IS_BROXTON(dev) && (INTEL_REVID(dev) < BXT_REVID_B0)) { |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
return; |
} |
|
/* Program defaults and thresholds for RPS*/ |
I915_WRITE(GEN6_RC_VIDEO_FREQ, |
GEN9_FREQUENCY(dev_priv->rps.rp1_freq)); |
|
/* 1 second timeout*/ |
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, |
GT_INTERVAL_FROM_US(dev_priv, 1000000)); |
|
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 0xa); |
|
/* Leaning on the below call to gen6_set_rps to program/setup the |
* Up/Down EI & threshold registers, as well as the RP_CONTROL, |
* RP_INTERRUPT_LIMITS & RPNSWREQ registers */ |
dev_priv->rps.power = HIGH_POWER; /* force a reset */ |
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
|
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
static void gen9_enable_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_engine_cs *ring; |
uint32_t rc6_mask = 0; |
int unused; |
4682,31 → 4779,64 |
|
/* 1b: Get forcewake during program sequence. Although the driver |
* hasn't enabled a state yet where we need forcewake, BIOS may have.*/ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
/* 2a: Disable RC states. */ |
I915_WRITE(GEN6_RC_CONTROL, 0); |
|
/* 2b: Program RC6 thresholds.*/ |
|
/* WaRsDoubleRc6WrlWithCoarsePowerGating: Doubling WRL only when CPG is enabled */ |
if (IS_SKYLAKE(dev)) |
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 108 << 16); |
else |
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 54 << 16); |
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */ |
I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); /* 25 * 1280ns */ |
for_each_ring(ring, dev_priv, unused) |
I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); |
|
if (HAS_GUC_UCODE(dev)) |
I915_WRITE(GUC_MAX_IDLE_COUNT, 0xA); |
|
I915_WRITE(GEN6_RC_SLEEP, 0); |
I915_WRITE(GEN6_RC6_THRESHOLD, 37500); /* 37.5/125ms per EI */ |
|
/* 2c: Program Coarse Power Gating Policies. */ |
I915_WRITE(GEN9_MEDIA_PG_IDLE_HYSTERESIS, 25); |
I915_WRITE(GEN9_RENDER_PG_IDLE_HYSTERESIS, 25); |
|
/* 3a: Enable RC6 */ |
if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE) |
rc6_mask = GEN6_RC_CTL_RC6_ENABLE; |
DRM_INFO("RC6 %s\n", (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? |
"on" : "off"); |
/* WaRsUseTimeoutMode */ |
if ((IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_D0) || |
(IS_BROXTON(dev) && INTEL_REVID(dev) <= BXT_REVID_A0)) { |
I915_WRITE(GEN6_RC6_THRESHOLD, 625); /* 800us */ |
I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | |
GEN7_RC_CTL_TO_MODE | |
rc6_mask); |
} else { |
I915_WRITE(GEN6_RC6_THRESHOLD, 37500); /* 37.5/125ms per EI */ |
I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | |
GEN6_RC_CTL_EI_MODE(1) | |
rc6_mask); |
} |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
/* |
* 3b: Enable Coarse Power Gating only when RC6 is enabled. |
* WaRsDisableCoarsePowerGating:skl,bxt - Render/Media PG need to be disabled with RC6. |
*/ |
if ((IS_BROXTON(dev) && (INTEL_REVID(dev) < BXT_REVID_B0)) || |
((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) && (INTEL_REVID(dev) <= SKL_REVID_F0))) |
I915_WRITE(GEN9_PG_ENABLE, 0); |
else |
I915_WRITE(GEN9_PG_ENABLE, (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? |
(GEN9_RENDER_PG_ENABLE | GEN9_MEDIA_PG_ENABLE) : 0); |
|
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
|
} |
|
static void gen8_enable_rps(struct drm_device *dev) |
4721,7 → 4851,7 |
|
/* 1c & 1d: Get forcewake during program sequence. Although the driver |
* hasn't enabled a state yet where we need forcewake, BIOS may have.*/ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
/* 2a: Disable RC states. */ |
I915_WRITE(GEN6_RC_CONTROL, 0); |
4786,9 → 4916,9 |
/* 6: Ring frequency + overclocking (our driver does this later */ |
|
dev_priv->rps.power = HIGH_POWER; /* force a reset */ |
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
gen6_set_rps(dev_priv->dev, dev_priv->rps.idle_freq); |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
static void gen6_enable_rps(struct drm_device *dev) |
4816,7 → 4946,7 |
I915_WRITE(GTFIFODBG, gtfifodbg); |
} |
|
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
/* Initialize rps frequencies */ |
gen6_init_rps_frequencies(dev); |
4880,7 → 5010,7 |
} |
|
dev_priv->rps.power = HIGH_POWER; /* force a reset */ |
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); |
gen6_set_rps(dev_priv->dev, dev_priv->rps.idle_freq); |
|
rc6vids = 0; |
ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids); |
4896,7 → 5026,7 |
DRM_ERROR("Couldn't fix incorrect rc6 voltage\n"); |
} |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
static void __gen6_update_ring_freq(struct drm_device *dev) |
4905,6 → 5035,7 |
int min_freq = 15; |
unsigned int gpu_freq; |
unsigned int max_ia_freq, min_ring_freq; |
unsigned int max_gpu_freq, min_gpu_freq; |
int scaling_factor = 180; |
struct cpufreq_policy *policy; |
|
4924,17 → 5055,31 |
/* convert DDR frequency from units of 266.6MHz to bandwidth */ |
min_ring_freq = mult_frac(min_ring_freq, 8, 3); |
|
if (IS_SKYLAKE(dev)) { |
/* Convert GT frequency to 50 HZ units */ |
min_gpu_freq = dev_priv->rps.min_freq / GEN9_FREQ_SCALER; |
max_gpu_freq = dev_priv->rps.max_freq / GEN9_FREQ_SCALER; |
} else { |
min_gpu_freq = dev_priv->rps.min_freq; |
max_gpu_freq = dev_priv->rps.max_freq; |
} |
|
/* |
* 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->rps.max_freq; gpu_freq >= dev_priv->rps.min_freq; |
gpu_freq--) { |
int diff = dev_priv->rps.max_freq - gpu_freq; |
for (gpu_freq = max_gpu_freq; gpu_freq >= min_gpu_freq; gpu_freq--) { |
int diff = max_gpu_freq - gpu_freq; |
unsigned int ia_freq = 0, ring_freq = 0; |
|
if (INTEL_INFO(dev)->gen >= 8) { |
if (IS_SKYLAKE(dev)) { |
/* |
* ring_freq = 2 * GT. ring_freq is in 100MHz units |
* No floor required for ring frequency on SKL. |
*/ |
ring_freq = gpu_freq; |
} else if (INTEL_INFO(dev)->gen >= 8) { |
/* max(2 * GT, DDR). NB: GT is 50MHz units */ |
ring_freq = max(min_ring_freq, gpu_freq); |
} else if (IS_HASWELL(dev)) { |
4968,7 → 5113,7 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (INTEL_INFO(dev)->gen < 6 || IS_VALLEYVIEW(dev)) |
if (!HAS_CORE_RING_FREQ(dev)) |
return; |
|
mutex_lock(&dev_priv->rps.hw_lock); |
4978,11 → 5123,30 |
|
static int cherryview_rps_max_freq(struct drm_i915_private *dev_priv) |
{ |
struct drm_device *dev = dev_priv->dev; |
u32 val, rp0; |
|
val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG); |
rp0 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK; |
val = vlv_punit_read(dev_priv, FB_GFX_FMAX_AT_VMAX_FUSE); |
|
switch (INTEL_INFO(dev)->eu_total) { |
case 8: |
/* (2 * 4) config */ |
rp0 = (val >> FB_GFX_FMAX_AT_VMAX_2SS4EU_FUSE_SHIFT); |
break; |
case 12: |
/* (2 * 6) config */ |
rp0 = (val >> FB_GFX_FMAX_AT_VMAX_2SS6EU_FUSE_SHIFT); |
break; |
case 16: |
/* (2 * 8) config */ |
default: |
/* Setting (2 * 8) Min RP0 for any other combination */ |
rp0 = (val >> FB_GFX_FMAX_AT_VMAX_2SS8EU_FUSE_SHIFT); |
break; |
} |
|
rp0 = (rp0 & FB_GFX_FREQ_FUSE_MASK); |
|
return rp0; |
} |
|
5000,21 → 5164,12 |
{ |
u32 val, rp1; |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); |
rp1 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK; |
val = vlv_punit_read(dev_priv, FB_GFX_FMAX_AT_VMAX_FUSE); |
rp1 = (val & FB_GFX_FREQ_FUSE_MASK); |
|
return rp1; |
} |
|
static int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) |
{ |
u32 val, rpn; |
|
val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG); |
rpn = (val >> PUNIT_GPU_STATIS_GFX_MIN_FREQ_SHIFT) & PUNIT_GPU_STATUS_GFX_MIN_FREQ_MASK; |
return rpn; |
} |
|
static int valleyview_rps_guar_freq(struct drm_i915_private *dev_priv) |
{ |
u32 val, rp1; |
5182,24 → 5337,26 |
dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv); |
dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; |
DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.max_freq), |
dev_priv->rps.max_freq); |
|
dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv); |
DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
dev_priv->rps.efficient_freq); |
|
dev_priv->rps.rp1_freq = valleyview_rps_guar_freq(dev_priv); |
DRM_DEBUG_DRIVER("RP1(Guar Freq) GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), |
dev_priv->rps.rp1_freq); |
|
dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv); |
DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.min_freq), |
dev_priv->rps.min_freq); |
|
dev_priv->rps.idle_freq = dev_priv->rps.min_freq; |
|
/* Preserve min/max settings in case of re-init */ |
if (dev_priv->rps.max_freq_softlimit == 0) |
dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; |
5219,32 → 5376,17 |
|
mutex_lock(&dev_priv->rps.hw_lock); |
|
mutex_lock(&dev_priv->dpio_lock); |
mutex_lock(&dev_priv->sb_lock); |
val = vlv_cck_read(dev_priv, CCK_FUSE_REG); |
mutex_unlock(&dev_priv->dpio_lock); |
mutex_unlock(&dev_priv->sb_lock); |
|
switch ((val >> 2) & 0x7) { |
case 0: |
case 1: |
dev_priv->rps.cz_freq = 200; |
dev_priv->mem_freq = 1600; |
break; |
case 2: |
dev_priv->rps.cz_freq = 267; |
dev_priv->mem_freq = 1600; |
break; |
case 3: |
dev_priv->rps.cz_freq = 333; |
dev_priv->mem_freq = 2000; |
break; |
case 4: |
dev_priv->rps.cz_freq = 320; |
default: |
dev_priv->mem_freq = 1600; |
break; |
case 5: |
dev_priv->rps.cz_freq = 400; |
dev_priv->mem_freq = 1600; |
break; |
} |
DRM_DEBUG_DRIVER("DDR speed: %d MHz\n", dev_priv->mem_freq); |
|
5251,22 → 5393,23 |
dev_priv->rps.max_freq = cherryview_rps_max_freq(dev_priv); |
dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; |
DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.max_freq), |
dev_priv->rps.max_freq); |
|
dev_priv->rps.efficient_freq = cherryview_rps_rpe_freq(dev_priv); |
DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
dev_priv->rps.efficient_freq); |
|
dev_priv->rps.rp1_freq = cherryview_rps_guar_freq(dev_priv); |
DRM_DEBUG_DRIVER("RP1(Guar) GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), |
dev_priv->rps.rp1_freq); |
|
dev_priv->rps.min_freq = cherryview_rps_min_freq(dev_priv); |
/* PUnit validated range is only [RPe, RP0] */ |
dev_priv->rps.min_freq = dev_priv->rps.efficient_freq; |
DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.min_freq), |
dev_priv->rps.min_freq); |
|
WARN_ONCE((dev_priv->rps.max_freq | |
5275,6 → 5418,8 |
dev_priv->rps.min_freq) & 1, |
"Odd GPU freq values\n"); |
|
dev_priv->rps.idle_freq = dev_priv->rps.min_freq; |
|
/* Preserve min/max settings in case of re-init */ |
if (dev_priv->rps.max_freq_softlimit == 0) |
dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; |
5310,8 → 5455,11 |
|
/* 1a & 1b: Get forcewake during program sequence. Although the driver |
* hasn't enabled a state yet where we need forcewake, BIOS may have.*/ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
/* Disable RC states. */ |
I915_WRITE(GEN6_RC_CONTROL, 0); |
|
/* 2a: Program RC6 thresholds.*/ |
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16); |
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */ |
5321,7 → 5469,8 |
I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); |
I915_WRITE(GEN6_RC_SLEEP, 0); |
|
I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */ |
/* TO threshold set to 500 us ( 0x186 * 1.28 us) */ |
I915_WRITE(GEN6_RC6_THRESHOLD, 0x186); |
|
/* allows RC6 residency counter to work */ |
I915_WRITE(VLV_COUNTER_CONTROL, |
5335,11 → 5484,12 |
/* 3: Enable RC6 */ |
if ((intel_enable_rc6(dev) & INTEL_RC6_ENABLE) && |
(pcbr >> VLV_PCBR_ADDR_SHIFT)) |
rc6_mode = GEN6_RC_CTL_EI_MODE(1); |
rc6_mode = GEN7_RC_CTL_TO_MODE; |
|
I915_WRITE(GEN6_RC_CONTROL, rc6_mode); |
|
/* 4 Program defaults and thresholds for RPS*/ |
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000); |
I915_WRITE(GEN6_RP_UP_EI, 66000); |
5347,38 → 5497,40 |
|
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); |
|
/* WaDisablePwrmtrEvent:chv (pre-production hw) */ |
I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff); |
I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00); |
|
/* 5: Enable RPS */ |
I915_WRITE(GEN6_RP_CONTROL, |
GEN6_RP_MEDIA_HW_NORMAL_MODE | |
GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */ |
GEN6_RP_MEDIA_IS_GFX | |
GEN6_RP_ENABLE | |
GEN6_RP_UP_BUSY_AVG | |
GEN6_RP_DOWN_IDLE_AVG); |
|
/* Setting Fixed Bias */ |
val = VLV_OVERRIDE_EN | |
VLV_SOC_TDP_EN | |
CHV_BIAS_CPU_50_SOC_50; |
vlv_punit_write(dev_priv, VLV_TURBO_SOC_OVERRIDE, val); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); |
|
/* RPS code assumes GPLL is used */ |
WARN_ONCE((val & GPLLENABLE) == 0, "GPLL not enabled\n"); |
|
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & GPLLENABLE ? "yes" : "no"); |
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); |
DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); |
|
dev_priv->rps.cur_freq = (val >> 8) & 0xff; |
DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), |
dev_priv->rps.cur_freq); |
|
DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
dev_priv->rps.efficient_freq); |
|
valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq); |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
static void valleyview_enable_rps(struct drm_device *dev) |
5399,8 → 5551,12 |
} |
|
/* If VLV, Forcewake all wells, else re-direct to regular path */ |
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); |
|
/* Disable RC states. */ |
I915_WRITE(GEN6_RC_CONTROL, 0); |
|
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000); |
I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); |
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000); |
I915_WRITE(GEN6_RP_UP_EI, 66000); |
5407,7 → 5563,6 |
I915_WRITE(GEN6_RP_DOWN_EI, 350000); |
|
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); |
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 0xf4240); |
|
I915_WRITE(GEN6_RP_CONTROL, |
GEN6_RP_MEDIA_TURBO | |
5440,146 → 5595,34 |
|
I915_WRITE(GEN6_RC_CONTROL, rc6_mode); |
|
/* Setting Fixed Bias */ |
val = VLV_OVERRIDE_EN | |
VLV_SOC_TDP_EN | |
VLV_BIAS_CPU_125_SOC_875; |
vlv_punit_write(dev_priv, VLV_TURBO_SOC_OVERRIDE, val); |
|
val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); |
|
/* RPS code assumes GPLL is used */ |
WARN_ONCE((val & GPLLENABLE) == 0, "GPLL not enabled\n"); |
|
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & GPLLENABLE ? "yes" : "no"); |
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); |
DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); |
|
dev_priv->rps.cur_freq = (val >> 8) & 0xff; |
DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), |
dev_priv->rps.cur_freq); |
|
DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", |
vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), |
dev_priv->rps.efficient_freq); |
|
valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq); |
|
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); |
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); |
} |
|
void ironlake_teardown_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->ips.renderctx) { |
i915_gem_object_ggtt_unpin(dev_priv->ips.renderctx); |
drm_gem_object_unreference(&dev_priv->ips.renderctx->base); |
dev_priv->ips.renderctx = NULL; |
} |
|
if (dev_priv->ips.pwrctx) { |
i915_gem_object_ggtt_unpin(dev_priv->ips.pwrctx); |
drm_gem_object_unreference(&dev_priv->ips.pwrctx->base); |
dev_priv->ips.pwrctx = NULL; |
} |
} |
|
static void ironlake_disable_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
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); |
|
I915_WRITE(PWRCTXA, 0); |
POSTING_READ(PWRCTXA); |
|
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); |
POSTING_READ(RSTDBYCTL); |
} |
} |
|
static int ironlake_setup_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->ips.renderctx == NULL) |
dev_priv->ips.renderctx = intel_alloc_context_page(dev); |
if (!dev_priv->ips.renderctx) |
return -ENOMEM; |
|
if (dev_priv->ips.pwrctx == NULL) |
dev_priv->ips.pwrctx = intel_alloc_context_page(dev); |
if (!dev_priv->ips.pwrctx) { |
ironlake_teardown_rc6(dev); |
return -ENOMEM; |
} |
|
return 0; |
} |
|
static void ironlake_enable_rc6(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
struct intel_engine_cs *ring = &dev_priv->ring[RCS]; |
bool was_interruptible; |
int ret; |
|
/* rc6 disabled by default due to repeated reports of hanging during |
* boot and resume. |
*/ |
if (!intel_enable_rc6(dev)) |
return; |
|
WARN_ON(!mutex_is_locked(&dev->struct_mutex)); |
|
ret = ironlake_setup_rc6(dev); |
if (ret) |
return; |
|
was_interruptible = dev_priv->mm.interruptible; |
dev_priv->mm.interruptible = false; |
|
/* |
* GPU can automatically power down the render unit if given a page |
* to save state. |
*/ |
ret = intel_ring_begin(ring, 6); |
if (ret) { |
ironlake_teardown_rc6(dev); |
dev_priv->mm.interruptible = was_interruptible; |
return; |
} |
|
intel_ring_emit(ring, MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); |
intel_ring_emit(ring, MI_SET_CONTEXT); |
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 | |
MI_RESTORE_INHIBIT); |
intel_ring_emit(ring, MI_SUSPEND_FLUSH); |
intel_ring_emit(ring, MI_NOOP); |
intel_ring_emit(ring, MI_FLUSH); |
intel_ring_advance(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_ring_idle(ring); |
dev_priv->mm.interruptible = was_interruptible; |
if (ret) { |
DRM_ERROR("failed to enable ironlake power savings\n"); |
ironlake_teardown_rc6(dev); |
return; |
} |
|
I915_WRITE(PWRCTXA, i915_gem_obj_ggtt_offset(dev_priv->ips.pwrctx) | PWRCTX_EN); |
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT); |
|
intel_print_rc6_info(dev, GEN6_RC_CTL_RC6_ENABLE); |
} |
|
static unsigned long intel_pxfreq(u32 vidfreq) |
{ |
unsigned long freq; |
5695,146 → 5738,27 |
return ((m * x) / 127) - b; |
} |
|
static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) |
static int _pxvid_to_vd(u8 pxvid) |
{ |
if (pxvid == 0) |
return 0; |
|
if (pxvid >= 8 && pxvid < 31) |
pxvid = 31; |
|
return (pxvid + 2) * 125; |
} |
|
static u32 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) |
{ |
struct drm_device *dev = dev_priv->dev; |
static const struct v_table { |
u16 vd; /* in .1 mil */ |
u16 vm; /* in .1 mil */ |
} v_table[] = { |
{ 0, 0, }, |
{ 375, 0, }, |
{ 500, 0, }, |
{ 625, 0, }, |
{ 750, 0, }, |
{ 875, 0, }, |
{ 1000, 0, }, |
{ 1125, 0, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4125, 3000, }, |
{ 4250, 3125, }, |
{ 4375, 3250, }, |
{ 4500, 3375, }, |
{ 4625, 3500, }, |
{ 4750, 3625, }, |
{ 4875, 3750, }, |
{ 5000, 3875, }, |
{ 5125, 4000, }, |
{ 5250, 4125, }, |
{ 5375, 4250, }, |
{ 5500, 4375, }, |
{ 5625, 4500, }, |
{ 5750, 4625, }, |
{ 5875, 4750, }, |
{ 6000, 4875, }, |
{ 6125, 5000, }, |
{ 6250, 5125, }, |
{ 6375, 5250, }, |
{ 6500, 5375, }, |
{ 6625, 5500, }, |
{ 6750, 5625, }, |
{ 6875, 5750, }, |
{ 7000, 5875, }, |
{ 7125, 6000, }, |
{ 7250, 6125, }, |
{ 7375, 6250, }, |
{ 7500, 6375, }, |
{ 7625, 6500, }, |
{ 7750, 6625, }, |
{ 7875, 6750, }, |
{ 8000, 6875, }, |
{ 8125, 7000, }, |
{ 8250, 7125, }, |
{ 8375, 7250, }, |
{ 8500, 7375, }, |
{ 8625, 7500, }, |
{ 8750, 7625, }, |
{ 8875, 7750, }, |
{ 9000, 7875, }, |
{ 9125, 8000, }, |
{ 9250, 8125, }, |
{ 9375, 8250, }, |
{ 9500, 8375, }, |
{ 9625, 8500, }, |
{ 9750, 8625, }, |
{ 9875, 8750, }, |
{ 10000, 8875, }, |
{ 10125, 9000, }, |
{ 10250, 9125, }, |
{ 10375, 9250, }, |
{ 10500, 9375, }, |
{ 10625, 9500, }, |
{ 10750, 9625, }, |
{ 10875, 9750, }, |
{ 11000, 9875, }, |
{ 11125, 10000, }, |
{ 11250, 10125, }, |
{ 11375, 10250, }, |
{ 11500, 10375, }, |
{ 11625, 10500, }, |
{ 11750, 10625, }, |
{ 11875, 10750, }, |
{ 12000, 10875, }, |
{ 12125, 11000, }, |
{ 12250, 11125, }, |
{ 12375, 11250, }, |
{ 12500, 11375, }, |
{ 12625, 11500, }, |
{ 12750, 11625, }, |
{ 12875, 11750, }, |
{ 13000, 11875, }, |
{ 13125, 12000, }, |
{ 13250, 12125, }, |
{ 13375, 12250, }, |
{ 13500, 12375, }, |
{ 13625, 12500, }, |
{ 13750, 12625, }, |
{ 13875, 12750, }, |
{ 14000, 12875, }, |
{ 14125, 13000, }, |
{ 14250, 13125, }, |
{ 14375, 13250, }, |
{ 14500, 13375, }, |
{ 14625, 13500, }, |
{ 14750, 13625, }, |
{ 14875, 13750, }, |
{ 15000, 13875, }, |
{ 15125, 14000, }, |
{ 15250, 14125, }, |
{ 15375, 14250, }, |
{ 15500, 14375, }, |
{ 15625, 14500, }, |
{ 15750, 14625, }, |
{ 15875, 14750, }, |
{ 16000, 14875, }, |
{ 16125, 15000, }, |
}; |
const int vd = _pxvid_to_vd(pxvid); |
const int vm = vd - 1125; |
|
if (INTEL_INFO(dev)->is_mobile) |
return v_table[pxvid].vm; |
else |
return v_table[pxvid].vd; |
return vm > 0 ? vm : 0; |
|
return vd; |
} |
|
static void __i915_update_gfx_val(struct drm_i915_private *dev_priv) |
5891,7 → 5815,7 |
|
assert_spin_locked(&mchdev_lock); |
|
pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_freq * 4)); |
pxvid = I915_READ(PXVFREQ(dev_priv->rps.cur_freq)); |
pxvid = (pxvid >> 24) & 0x7f; |
ext_v = pvid_to_extvid(dev_priv, pxvid); |
|
6134,13 → 6058,13 |
I915_WRITE(CSIEW2, 0x04000004); |
|
for (i = 0; i < 5; i++) |
I915_WRITE(PEW + (i * 4), 0); |
I915_WRITE(PEW(i), 0); |
for (i = 0; i < 3; i++) |
I915_WRITE(DEW + (i * 4), 0); |
I915_WRITE(DEW(i), 0); |
|
/* Program P-state weights to account for frequency power adjustment */ |
for (i = 0; i < 16; i++) { |
u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4)); |
u32 pxvidfreq = I915_READ(PXVFREQ(i)); |
unsigned long freq = intel_pxfreq(pxvidfreq); |
unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >> |
PXVFREQ_PX_SHIFT; |
6161,7 → 6085,7 |
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); |
I915_WRITE(PXW(i), val); |
} |
|
/* Adjust magic regs to magic values (more experimental results) */ |
6177,7 → 6101,7 |
I915_WRITE(EG7, 0); |
|
for (i = 0; i < 8; i++) |
I915_WRITE(PXWL + (i * 4), 0); |
I915_WRITE(PXWL(i), 0); |
|
/* Enable PMON + select events */ |
I915_WRITE(ECR, 0x80000019); |
6211,11 → 6135,6 |
|
// flush_delayed_work(&dev_priv->rps.delayed_resume_work); |
|
/* |
* TODO: disable RPS interrupts on GEN9+ too once RPS support |
* is added for it. |
*/ |
if (INTEL_INFO(dev)->gen < 9) |
gen6_disable_rps_interrupts(dev); |
} |
|
6246,7 → 6165,6 |
|
if (IS_IRONLAKE_M(dev)) { |
ironlake_disable_drps(dev); |
ironlake_disable_rc6(dev); |
} else if (INTEL_INFO(dev)->gen >= 6) { |
intel_suspend_gt_powersave(dev); |
|
6274,11 → 6192,6 |
|
mutex_lock(&dev_priv->rps.hw_lock); |
|
/* |
* TODO: reset/enable RPS interrupts on GEN9+ too, once RPS support is |
* added for it. |
*/ |
if (INTEL_INFO(dev)->gen < 9) |
gen6_reset_rps_interrupts(dev); |
|
if (IS_CHERRYVIEW(dev)) { |
6286,7 → 6199,10 |
} else if (IS_VALLEYVIEW(dev)) { |
valleyview_enable_rps(dev); |
} else if (INTEL_INFO(dev)->gen >= 9) { |
gen9_enable_rc6(dev); |
gen9_enable_rps(dev); |
if (IS_SKYLAKE(dev)) |
__gen6_update_ring_freq(dev); |
} else if (IS_BROADWELL(dev)) { |
gen8_enable_rps(dev); |
__gen6_update_ring_freq(dev); |
6294,9 → 6210,15 |
gen6_enable_rps(dev); |
__gen6_update_ring_freq(dev); |
} |
|
WARN_ON(dev_priv->rps.max_freq < dev_priv->rps.min_freq); |
WARN_ON(dev_priv->rps.idle_freq > dev_priv->rps.max_freq); |
|
WARN_ON(dev_priv->rps.efficient_freq < dev_priv->rps.min_freq); |
WARN_ON(dev_priv->rps.efficient_freq > dev_priv->rps.max_freq); |
|
dev_priv->rps.enabled = true; |
|
if (INTEL_INFO(dev)->gen < 9) |
gen6_enable_rps_interrupts(dev); |
|
mutex_unlock(&dev_priv->rps.hw_lock); |
6308,10 → 6230,13 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
/* Powersaving is controlled by the host when inside a VM */ |
if (intel_vgpu_active(dev)) |
return; |
|
if (IS_IRONLAKE_M(dev)) { |
mutex_lock(&dev->struct_mutex); |
ironlake_enable_drps(dev); |
ironlake_enable_rc6(dev); |
intel_init_emon(dev); |
mutex_unlock(&dev->struct_mutex); |
} else if (INTEL_INFO(dev)->gen >= 6) { |
6359,13 → 6284,15 |
static void g4x_disable_trickle_feed(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
int pipe; |
enum pipe pipe; |
|
for_each_pipe(dev_priv, pipe) { |
I915_WRITE(DSPCNTR(pipe), |
I915_READ(DSPCNTR(pipe)) | |
DISPPLANE_TRICKLE_FEED_DISABLE); |
intel_flush_primary_plane(dev_priv, pipe); |
|
I915_WRITE(DSPSURF(pipe), I915_READ(DSPSURF(pipe))); |
POSTING_READ(DSPSURF(pipe)); |
} |
} |
|
6628,14 → 6555,14 |
* TODO: this bit should only be enabled when really needed, then |
* disabled when not needed anymore in order to save power. |
*/ |
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) |
if (HAS_PCH_LPT_LP(dev)) |
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) | |
I915_WRITE(TRANS_CHICKEN1(PIPE_A), |
I915_READ(TRANS_CHICKEN1(PIPE_A)) | |
TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); |
} |
|
6643,7 → 6570,7 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { |
if (HAS_PCH_LPT_LP(dev)) { |
uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D); |
|
val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; |
6655,10 → 6582,9 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
enum pipe pipe; |
uint32_t misccpctl; |
|
I915_WRITE(WM3_LP_ILK, 0); |
I915_WRITE(WM2_LP_ILK, 0); |
I915_WRITE(WM1_LP_ILK, 0); |
ilk_init_lp_watermarks(dev); |
|
/* WaSwitchSolVfFArbitrationPriority:bdw */ |
I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); |
6687,6 → 6613,22 |
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | |
GEN8_SDEUNIT_CLOCK_GATE_DISABLE); |
|
/* |
* WaProgramL3SqcReg1Default:bdw |
* WaTempDisableDOPClkGating:bdw |
*/ |
misccpctl = I915_READ(GEN7_MISCCPCTL); |
I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE); |
I915_WRITE(GEN8_L3SQCREG1, BDW_WA_L3SQCREG1_DEFAULT); |
I915_WRITE(GEN7_MISCCPCTL, misccpctl); |
|
/* |
* WaGttCachingOffByDefault:bdw |
* GTT cache may not work with big pages, so if those |
* are ever enabled GTT cache may need to be disabled. |
*/ |
I915_WRITE(HSW_GTT_CACHE_EN, GTT_CACHE_EN_ALL); |
|
lpt_init_clock_gating(dev); |
} |
|
6732,6 → 6674,10 |
I915_WRITE(GEN7_GT_MODE, |
_MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); |
|
/* WaSampleCChickenBitEnable:hsw */ |
I915_WRITE(HALF_SLICE_CHICKEN3, |
_MASKED_BIT_ENABLE(HSW_SAMPLE_C_PERFORMANCE)); |
|
/* WaSwitchSolVfFArbitrationPriority:hsw */ |
I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); |
|
6840,11 → 6786,22 |
gen6_check_mch_setup(dev); |
} |
|
static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) |
{ |
I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); |
|
/* |
* Disable trickle feed and enable pnd deadline calculation |
*/ |
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); |
I915_WRITE(CBR1_VLV, 0); |
} |
|
static void valleyview_init_clock_gating(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); |
vlv_init_display_clock_gating(dev_priv); |
|
/* WaDisableEarlyCull:vlv */ |
I915_WRITE(_3D_CHICKEN3, |
6892,8 → 6849,6 |
I915_WRITE(GEN7_UCGCTL4, |
I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE); |
|
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); |
|
/* |
* BSpec says this must be set, even though |
* WaDisable4x2SubspanOptimization isn't listed for VLV. |
6902,6 → 6857,17 |
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); |
|
/* |
* BSpec recommends 8x4 when MSAA is used, |
* however in practice 16x4 seems fastest. |
* |
* Note that PS/WM thread counts depend on the WIZ hashing |
* disable bit, which we don't touch here, but it's good |
* to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). |
*/ |
I915_WRITE(GEN7_GT_MODE, |
_MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); |
|
/* |
* WaIncreaseL3CreditsForVLVB0:vlv |
* This is the hardware default actually. |
*/ |
6919,10 → 6885,8 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); |
vlv_init_display_clock_gating(dev_priv); |
|
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); |
|
/* WaVSRefCountFullforceMissDisable:chv */ |
/* WaDSRefCountFullforceMissDisable:chv */ |
I915_WRITE(GEN7_FF_THREAD_MODE, |
6940,6 → 6904,12 |
/* WaDisableSDEUnitClockGating:chv */ |
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | |
GEN8_SDEUNIT_CLOCK_GATE_DISABLE); |
|
/* |
* GTT cache may not work with big pages, so if those |
* are ever enabled GTT cache may need to be disabled. |
*/ |
I915_WRITE(HSW_GTT_CACHE_EN, GTT_CACHE_EN_ALL); |
} |
|
static void g4x_init_clock_gating(struct drm_device *dev) |
7056,6 → 7026,7 |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
if (dev_priv->display.init_clock_gating) |
dev_priv->display.init_clock_gating(dev); |
} |
|
7065,43 → 7036,12 |
lpt_suspend_hw(dev); |
} |
|
static void intel_init_fbc(struct drm_i915_private *dev_priv) |
{ |
if (!HAS_FBC(dev_priv)) { |
dev_priv->fbc.enabled = false; |
return; |
} |
|
if (INTEL_INFO(dev_priv)->gen >= 7) { |
dev_priv->display.fbc_enabled = ironlake_fbc_enabled; |
dev_priv->display.enable_fbc = gen7_enable_fbc; |
dev_priv->display.disable_fbc = ironlake_disable_fbc; |
} else if (INTEL_INFO(dev_priv)->gen >= 5) { |
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_priv)) { |
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 { |
dev_priv->display.fbc_enabled = i8xx_fbc_enabled; |
dev_priv->display.enable_fbc = i8xx_enable_fbc; |
dev_priv->display.disable_fbc = i8xx_disable_fbc; |
|
/* This value was pulled out of someone's hat */ |
I915_WRITE(FBC_CONTROL, 500 << FBC_CTL_INTERVAL_SHIFT); |
} |
|
dev_priv->fbc.enabled = dev_priv->display.fbc_enabled(dev_priv->dev); |
} |
|
/* Set up chip specific power management-related functions */ |
void intel_init_pm(struct drm_device *dev) |
{ |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
intel_init_fbc(dev_priv); |
intel_fbc_init(dev_priv); |
|
/* For cxsr */ |
if (IS_PINEVIEW(dev)) |
7113,7 → 7053,9 |
if (INTEL_INFO(dev)->gen >= 9) { |
skl_setup_wm_latency(dev); |
|
dev_priv->display.init_clock_gating = gen9_init_clock_gating; |
if (IS_BROXTON(dev)) |
dev_priv->display.init_clock_gating = |
bxt_init_clock_gating; |
dev_priv->display.update_wm = skl_update_wm; |
dev_priv->display.update_sprite_wm = skl_update_sprite_wm; |
} else if (HAS_PCH_SPLIT(dev)) { |
7141,13 → 7083,15 |
else if (INTEL_INFO(dev)->gen == 8) |
dev_priv->display.init_clock_gating = broadwell_init_clock_gating; |
} else if (IS_CHERRYVIEW(dev)) { |
dev_priv->display.update_wm = cherryview_update_wm; |
dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm; |
vlv_setup_wm_latency(dev); |
|
dev_priv->display.update_wm = vlv_update_wm; |
dev_priv->display.init_clock_gating = |
cherryview_init_clock_gating; |
} else if (IS_VALLEYVIEW(dev)) { |
dev_priv->display.update_wm = valleyview_update_wm; |
dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm; |
vlv_setup_wm_latency(dev); |
|
dev_priv->display.update_wm = vlv_update_wm; |
dev_priv->display.init_clock_gating = |
valleyview_init_clock_gating; |
} else if (IS_PINEVIEW(dev)) { |
7264,7 → 7208,7 |
|
static int byt_gpu_freq(struct drm_i915_private *dev_priv, int val) |
{ |
int div, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->mem_freq, 4); |
int div, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); |
|
div = vlv_gpu_freq_div(czclk_freq); |
if (div < 0) |
7275,7 → 7219,7 |
|
static int byt_freq_opcode(struct drm_i915_private *dev_priv, int val) |
{ |
int mul, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->mem_freq, 4); |
int mul, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); |
|
mul = vlv_gpu_freq_div(czclk_freq); |
if (mul < 0) |
7286,7 → 7230,7 |
|
static int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) |
{ |
int div, czclk_freq = dev_priv->rps.cz_freq; |
int div, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); |
|
div = vlv_gpu_freq_div(czclk_freq) / 2; |
if (div < 0) |
7297,7 → 7241,7 |
|
static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) |
{ |
int mul, czclk_freq = dev_priv->rps.cz_freq; |
int mul, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); |
|
mul = vlv_gpu_freq_div(czclk_freq) / 2; |
if (mul < 0) |
7307,28 → 7251,70 |
return DIV_ROUND_CLOSEST(val * 2 * mul, czclk_freq) * 2; |
} |
|
int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val) |
int intel_gpu_freq(struct drm_i915_private *dev_priv, int val) |
{ |
int ret = -1; |
if (IS_GEN9(dev_priv->dev)) |
return DIV_ROUND_CLOSEST(val * GT_FREQUENCY_MULTIPLIER, |
GEN9_FREQ_SCALER); |
else if (IS_CHERRYVIEW(dev_priv->dev)) |
return chv_gpu_freq(dev_priv, val); |
else if (IS_VALLEYVIEW(dev_priv->dev)) |
return byt_gpu_freq(dev_priv, val); |
else |
return val * GT_FREQUENCY_MULTIPLIER; |
} |
|
if (IS_CHERRYVIEW(dev_priv->dev)) |
ret = chv_gpu_freq(dev_priv, val); |
int intel_freq_opcode(struct drm_i915_private *dev_priv, int val) |
{ |
if (IS_GEN9(dev_priv->dev)) |
return DIV_ROUND_CLOSEST(val * GEN9_FREQ_SCALER, |
GT_FREQUENCY_MULTIPLIER); |
else if (IS_CHERRYVIEW(dev_priv->dev)) |
return chv_freq_opcode(dev_priv, val); |
else if (IS_VALLEYVIEW(dev_priv->dev)) |
ret = byt_gpu_freq(dev_priv, val); |
return byt_freq_opcode(dev_priv, val); |
else |
return DIV_ROUND_CLOSEST(val, GT_FREQUENCY_MULTIPLIER); |
} |
|
return ret; |
struct request_boost { |
struct work_struct work; |
struct drm_i915_gem_request *req; |
}; |
|
static void __intel_rps_boost_work(struct work_struct *work) |
{ |
struct request_boost *boost = container_of(work, struct request_boost, work); |
struct drm_i915_gem_request *req = boost->req; |
|
if (!i915_gem_request_completed(req, true)) |
gen6_rps_boost(to_i915(req->ring->dev), NULL, |
req->emitted_jiffies); |
|
i915_gem_request_unreference__unlocked(req); |
kfree(boost); |
} |
|
int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val) |
void intel_queue_rps_boost_for_request(struct drm_device *dev, |
struct drm_i915_gem_request *req) |
{ |
int ret = -1; |
struct request_boost *boost; |
|
if (IS_CHERRYVIEW(dev_priv->dev)) |
ret = chv_freq_opcode(dev_priv, val); |
else if (IS_VALLEYVIEW(dev_priv->dev)) |
ret = byt_freq_opcode(dev_priv, val); |
if (req == NULL || INTEL_INFO(dev)->gen < 6) |
return; |
|
return ret; |
if (i915_gem_request_completed(req, true)) |
return; |
|
boost = kmalloc(sizeof(*boost), GFP_ATOMIC); |
if (boost == NULL) |
return; |
|
i915_gem_request_reference(req); |
boost->req = req; |
|
INIT_WORK(&boost->work, __intel_rps_boost_work); |
queue_work(to_i915(dev)->wq, &boost->work); |
} |
|
void intel_pm_setup(struct drm_device *dev) |
7336,9 → 7322,13 |
struct drm_i915_private *dev_priv = dev->dev_private; |
|
mutex_init(&dev_priv->rps.hw_lock); |
spin_lock_init(&dev_priv->rps.client_lock); |
|
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, |
intel_gen6_powersave_work); |
INIT_LIST_HEAD(&dev_priv->rps.clients); |
INIT_LIST_HEAD(&dev_priv->rps.semaphores.link); |
INIT_LIST_HEAD(&dev_priv->rps.mmioflips.link); |
|
dev_priv->pm.suspended = false; |
} |