![Daniel Vetter](/assets/img/avatar_default.png)
... and put them to so good use. Note that there's functional change in vlv clock gating code, we now no longer spuriously read back the current value of the bit. According to Bspec the high bits should always read zero, so ORing this in should have no effect. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Eugeni Dodonov <eugeni.dodonov@intel.com> Signed-Off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
3075 lines
88 KiB
C
3075 lines
88 KiB
C
/*
|
|
* Copyright © 2012 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*
|
|
* Authors:
|
|
* Eugeni Dodonov <eugeni.dodonov@intel.com>
|
|
*
|
|
*/
|
|
|
|
#include <linux/cpufreq.h>
|
|
#include "i915_drv.h"
|
|
#include "intel_drv.h"
|
|
|
|
/* 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 i8xx_disable_fbc(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 fbc_ctl;
|
|
|
|
/* Disable compression */
|
|
fbc_ctl = I915_READ(FBC_CONTROL);
|
|
if ((fbc_ctl & FBC_CTL_EN) == 0)
|
|
return;
|
|
|
|
fbc_ctl &= ~FBC_CTL_EN;
|
|
I915_WRITE(FBC_CONTROL, fbc_ctl);
|
|
|
|
/* Wait for compressing bit to clear */
|
|
if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
|
|
DRM_DEBUG_KMS("FBC idle timed out\n");
|
|
return;
|
|
}
|
|
|
|
DRM_DEBUG_KMS("disabled FBC\n");
|
|
}
|
|
|
|
static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_framebuffer *fb = crtc->fb;
|
|
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
|
|
struct drm_i915_gem_object *obj = intel_fb->obj;
|
|
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
int cfb_pitch;
|
|
int plane, i;
|
|
u32 fbc_ctl, fbc_ctl2;
|
|
|
|
cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
|
|
if (fb->pitches[0] < cfb_pitch)
|
|
cfb_pitch = fb->pitches[0];
|
|
|
|
/* FBC_CTL wants 64B units */
|
|
cfb_pitch = (cfb_pitch / 64) - 1;
|
|
plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
|
|
|
|
/* Clear old tags */
|
|
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
|
|
I915_WRITE(FBC_TAG + (i * 4), 0);
|
|
|
|
/* Set it up... */
|
|
fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
|
|
fbc_ctl2 |= plane;
|
|
I915_WRITE(FBC_CONTROL2, fbc_ctl2);
|
|
I915_WRITE(FBC_FENCE_OFF, crtc->y);
|
|
|
|
/* enable it... */
|
|
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
|
|
if (IS_I945GM(dev))
|
|
fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
|
|
fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
|
|
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
|
|
fbc_ctl |= obj->fence_reg;
|
|
I915_WRITE(FBC_CONTROL, fbc_ctl);
|
|
|
|
DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
|
|
cfb_pitch, crtc->y, intel_crtc->plane);
|
|
}
|
|
|
|
static bool i8xx_fbc_enabled(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
return I915_READ(FBC_CONTROL) & FBC_CTL_EN;
|
|
}
|
|
|
|
static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_framebuffer *fb = crtc->fb;
|
|
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
|
|
struct drm_i915_gem_object *obj = intel_fb->obj;
|
|
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
|
|
unsigned long stall_watermark = 200;
|
|
u32 dpfc_ctl;
|
|
|
|
dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
|
|
dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
|
|
I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
|
|
|
|
I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
|
|
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
|
|
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
|
|
I915_WRITE(DPFC_FENCE_YOFF, crtc->y);
|
|
|
|
/* enable it... */
|
|
I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
|
|
|
|
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
|
|
}
|
|
|
|
static void g4x_disable_fbc(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dpfc_ctl;
|
|
|
|
/* Disable compression */
|
|
dpfc_ctl = I915_READ(DPFC_CONTROL);
|
|
if (dpfc_ctl & DPFC_CTL_EN) {
|
|
dpfc_ctl &= ~DPFC_CTL_EN;
|
|
I915_WRITE(DPFC_CONTROL, dpfc_ctl);
|
|
|
|
DRM_DEBUG_KMS("disabled FBC\n");
|
|
}
|
|
}
|
|
|
|
static bool g4x_fbc_enabled(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN;
|
|
}
|
|
|
|
static void sandybridge_blit_fbc_update(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 blt_ecoskpd;
|
|
|
|
/* Make sure blitter notifies FBC of writes */
|
|
gen6_gt_force_wake_get(dev_priv);
|
|
blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD);
|
|
blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY <<
|
|
GEN6_BLITTER_LOCK_SHIFT;
|
|
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
|
|
blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY;
|
|
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
|
|
blt_ecoskpd &= ~(GEN6_BLITTER_FBC_NOTIFY <<
|
|
GEN6_BLITTER_LOCK_SHIFT);
|
|
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
|
|
POSTING_READ(GEN6_BLITTER_ECOSKPD);
|
|
gen6_gt_force_wake_put(dev_priv);
|
|
}
|
|
|
|
static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_framebuffer *fb = crtc->fb;
|
|
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
|
|
struct drm_i915_gem_object *obj = intel_fb->obj;
|
|
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
|
|
unsigned long stall_watermark = 200;
|
|
u32 dpfc_ctl;
|
|
|
|
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
|
|
dpfc_ctl &= DPFC_RESERVED;
|
|
dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
|
|
/* Set persistent mode for front-buffer rendering, ala X. */
|
|
dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
|
|
dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
|
|
I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
|
|
|
|
I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
|
|
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
|
|
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
|
|
I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y);
|
|
I915_WRITE(ILK_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID);
|
|
/* enable it... */
|
|
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
|
|
|
|
if (IS_GEN6(dev)) {
|
|
I915_WRITE(SNB_DPFC_CTL_SA,
|
|
SNB_CPU_FENCE_ENABLE | obj->fence_reg);
|
|
I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
|
|
sandybridge_blit_fbc_update(dev);
|
|
}
|
|
|
|
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
|
|
}
|
|
|
|
static void ironlake_disable_fbc(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dpfc_ctl;
|
|
|
|
/* Disable compression */
|
|
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
|
|
if (dpfc_ctl & DPFC_CTL_EN) {
|
|
dpfc_ctl &= ~DPFC_CTL_EN;
|
|
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
|
|
|
|
DRM_DEBUG_KMS("disabled FBC\n");
|
|
}
|
|
}
|
|
|
|
static bool ironlake_fbc_enabled(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN;
|
|
}
|
|
|
|
bool intel_fbc_enabled(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (!dev_priv->display.fbc_enabled)
|
|
return false;
|
|
|
|
return dev_priv->display.fbc_enabled(dev);
|
|
}
|
|
|
|
static void intel_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_work) {
|
|
/* Double check that we haven't switched fb without cancelling
|
|
* the prior work.
|
|
*/
|
|
if (work->crtc->fb == work->fb) {
|
|
dev_priv->display.enable_fbc(work->crtc,
|
|
work->interval);
|
|
|
|
dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane;
|
|
dev_priv->cfb_fb = work->crtc->fb->base.id;
|
|
dev_priv->cfb_y = work->crtc->y;
|
|
}
|
|
|
|
dev_priv->fbc_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_work == NULL)
|
|
return;
|
|
|
|
DRM_DEBUG_KMS("cancelling pending FBC enable\n");
|
|
|
|
/* Synchronisation is provided by struct_mutex and checking of
|
|
* dev_priv->fbc_work, so we can perform the cancellation
|
|
* entirely asynchronously.
|
|
*/
|
|
if (cancel_delayed_work(&dev_priv->fbc_work->work))
|
|
/* tasklet was killed before being run, clean up */
|
|
kfree(dev_priv->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_work = NULL;
|
|
}
|
|
|
|
void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
|
{
|
|
struct intel_fbc_work *work;
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (!dev_priv->display.enable_fbc)
|
|
return;
|
|
|
|
intel_cancel_fbc_work(dev_priv);
|
|
|
|
work = kzalloc(sizeof *work, GFP_KERNEL);
|
|
if (work == NULL) {
|
|
dev_priv->display.enable_fbc(crtc, interval);
|
|
return;
|
|
}
|
|
|
|
work->crtc = crtc;
|
|
work->fb = crtc->fb;
|
|
work->interval = interval;
|
|
INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
|
|
|
|
dev_priv->fbc_work = work;
|
|
|
|
DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
|
|
|
|
/* Delay the actual enabling to let pageflipping cease and the
|
|
* display to settle before starting the compression. Note that
|
|
* this delay also serves a second purpose: it allows for a
|
|
* vblank to pass after disabling the FBC before we attempt
|
|
* to modify the control registers.
|
|
*
|
|
* A more complicated solution would involve tracking vblanks
|
|
* following the termination of the page-flipping sequence
|
|
* and indeed performing the enable as a co-routine and not
|
|
* waiting synchronously upon the vblank.
|
|
*/
|
|
schedule_delayed_work(&work->work, msecs_to_jiffies(50));
|
|
}
|
|
|
|
void intel_disable_fbc(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
intel_cancel_fbc_work(dev_priv);
|
|
|
|
if (!dev_priv->display.disable_fbc)
|
|
return;
|
|
|
|
dev_priv->display.disable_fbc(dev);
|
|
dev_priv->cfb_plane = -1;
|
|
}
|
|
|
|
/**
|
|
* intel_update_fbc - enable/disable FBC as needed
|
|
* @dev: the drm_device
|
|
*
|
|
* Set up the framebuffer compression hardware at mode set time. We
|
|
* enable it if possible:
|
|
* - plane A only (on pre-965)
|
|
* - no pixel mulitply/line duplication
|
|
* - no alpha buffer discard
|
|
* - no dual wide
|
|
* - framebuffer <= 2048 in width, 1536 in height
|
|
*
|
|
* We can't assume that any compression will take place (worst case),
|
|
* so the compressed buffer has to be the same size as the uncompressed
|
|
* one. It also must reside (along with the line length buffer) in
|
|
* stolen memory.
|
|
*
|
|
* We need to enable/disable FBC on a global basis.
|
|
*/
|
|
void intel_update_fbc(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_crtc *crtc = NULL, *tmp_crtc;
|
|
struct intel_crtc *intel_crtc;
|
|
struct drm_framebuffer *fb;
|
|
struct intel_framebuffer *intel_fb;
|
|
struct drm_i915_gem_object *obj;
|
|
int enable_fbc;
|
|
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
if (!i915_powersave)
|
|
return;
|
|
|
|
if (!I915_HAS_FBC(dev))
|
|
return;
|
|
|
|
/*
|
|
* If FBC is already on, we just have to verify that we can
|
|
* keep it that way...
|
|
* Need to disable if:
|
|
* - more than one pipe is active
|
|
* - changing FBC params (stride, fence, mode)
|
|
* - new fb is too large to fit in compressed buffer
|
|
* - going to an unsupported config (interlace, pixel multiply, etc.)
|
|
*/
|
|
list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) {
|
|
if (tmp_crtc->enabled && tmp_crtc->fb) {
|
|
if (crtc) {
|
|
DRM_DEBUG_KMS("more than one pipe active, disabling compression\n");
|
|
dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES;
|
|
goto out_disable;
|
|
}
|
|
crtc = tmp_crtc;
|
|
}
|
|
}
|
|
|
|
if (!crtc || crtc->fb == NULL) {
|
|
DRM_DEBUG_KMS("no output, disabling\n");
|
|
dev_priv->no_fbc_reason = FBC_NO_OUTPUT;
|
|
goto out_disable;
|
|
}
|
|
|
|
intel_crtc = to_intel_crtc(crtc);
|
|
fb = crtc->fb;
|
|
intel_fb = to_intel_framebuffer(fb);
|
|
obj = intel_fb->obj;
|
|
|
|
enable_fbc = i915_enable_fbc;
|
|
if (enable_fbc < 0) {
|
|
DRM_DEBUG_KMS("fbc set to per-chip default\n");
|
|
enable_fbc = 1;
|
|
if (INTEL_INFO(dev)->gen <= 6)
|
|
enable_fbc = 0;
|
|
}
|
|
if (!enable_fbc) {
|
|
DRM_DEBUG_KMS("fbc disabled per module param\n");
|
|
dev_priv->no_fbc_reason = FBC_MODULE_PARAM;
|
|
goto out_disable;
|
|
}
|
|
if (intel_fb->obj->base.size > dev_priv->cfb_size) {
|
|
DRM_DEBUG_KMS("framebuffer too large, disabling "
|
|
"compression\n");
|
|
dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
|
|
goto out_disable;
|
|
}
|
|
if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ||
|
|
(crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) {
|
|
DRM_DEBUG_KMS("mode incompatible with compression, "
|
|
"disabling\n");
|
|
dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE;
|
|
goto out_disable;
|
|
}
|
|
if ((crtc->mode.hdisplay > 2048) ||
|
|
(crtc->mode.vdisplay > 1536)) {
|
|
DRM_DEBUG_KMS("mode too large for compression, disabling\n");
|
|
dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE;
|
|
goto out_disable;
|
|
}
|
|
if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) {
|
|
DRM_DEBUG_KMS("plane not 0, disabling compression\n");
|
|
dev_priv->no_fbc_reason = FBC_BAD_PLANE;
|
|
goto out_disable;
|
|
}
|
|
|
|
/* The use of a CPU fence is mandatory in order to detect writes
|
|
* by the CPU to the scanout and trigger updates to the FBC.
|
|
*/
|
|
if (obj->tiling_mode != I915_TILING_X ||
|
|
obj->fence_reg == I915_FENCE_REG_NONE) {
|
|
DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
|
|
dev_priv->no_fbc_reason = FBC_NOT_TILED;
|
|
goto out_disable;
|
|
}
|
|
|
|
/* If the kernel debugger is active, always disable compression */
|
|
if (in_dbg_master())
|
|
goto out_disable;
|
|
|
|
/* If the scanout has not changed, don't modify the FBC settings.
|
|
* Note that we make the fundamental assumption that the fb->obj
|
|
* cannot be unpinned (and have its GTT offset and fence revoked)
|
|
* without first being decoupled from the scanout and FBC disabled.
|
|
*/
|
|
if (dev_priv->cfb_plane == intel_crtc->plane &&
|
|
dev_priv->cfb_fb == fb->base.id &&
|
|
dev_priv->cfb_y == crtc->y)
|
|
return;
|
|
|
|
if (intel_fbc_enabled(dev)) {
|
|
/* We update FBC along two paths, after changing fb/crtc
|
|
* configuration (modeswitching) and after page-flipping
|
|
* finishes. For the latter, we know that not only did
|
|
* we disable the FBC at the start of the page-flip
|
|
* sequence, but also more than one vblank has passed.
|
|
*
|
|
* For the former case of modeswitching, it is possible
|
|
* to switch between two FBC valid configurations
|
|
* instantaneously so we do need to disable the FBC
|
|
* before we can modify its control registers. We also
|
|
* have to wait for the next vblank for that to take
|
|
* effect. However, since we delay enabling FBC we can
|
|
* assume that a vblank has passed since disabling and
|
|
* that we can safely alter the registers in the deferred
|
|
* callback.
|
|
*
|
|
* In the scenario that we go from a valid to invalid
|
|
* and then back to valid FBC configuration we have
|
|
* no strict enforcement that a vblank occurred since
|
|
* disabling the FBC. However, along all current pipe
|
|
* disabling paths we do need to wait for a vblank at
|
|
* some point. And we wait before enabling FBC anyway.
|
|
*/
|
|
DRM_DEBUG_KMS("disabling active FBC for update\n");
|
|
intel_disable_fbc(dev);
|
|
}
|
|
|
|
intel_enable_fbc(crtc, 500);
|
|
return;
|
|
|
|
out_disable:
|
|
/* Multiple disables should be harmless */
|
|
if (intel_fbc_enabled(dev)) {
|
|
DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
|
|
intel_disable_fbc(dev);
|
|
}
|
|
}
|
|
|
|
static const struct cxsr_latency cxsr_latency_table[] = {
|
|
{1, 0, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */
|
|
{1, 0, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */
|
|
{1, 0, 800, 800, 3347, 33347, 3763, 33763}, /* DDR2-800 SC */
|
|
{1, 1, 800, 667, 6420, 36420, 6873, 36873}, /* DDR3-667 SC */
|
|
{1, 1, 800, 800, 5902, 35902, 6318, 36318}, /* DDR3-800 SC */
|
|
|
|
{1, 0, 667, 400, 3400, 33400, 4021, 34021}, /* DDR2-400 SC */
|
|
{1, 0, 667, 667, 3372, 33372, 3845, 33845}, /* DDR2-667 SC */
|
|
{1, 0, 667, 800, 3386, 33386, 3822, 33822}, /* DDR2-800 SC */
|
|
{1, 1, 667, 667, 6438, 36438, 6911, 36911}, /* DDR3-667 SC */
|
|
{1, 1, 667, 800, 5941, 35941, 6377, 36377}, /* DDR3-800 SC */
|
|
|
|
{1, 0, 400, 400, 3472, 33472, 4173, 34173}, /* DDR2-400 SC */
|
|
{1, 0, 400, 667, 3443, 33443, 3996, 33996}, /* DDR2-667 SC */
|
|
{1, 0, 400, 800, 3430, 33430, 3946, 33946}, /* DDR2-800 SC */
|
|
{1, 1, 400, 667, 6509, 36509, 7062, 37062}, /* DDR3-667 SC */
|
|
{1, 1, 400, 800, 5985, 35985, 6501, 36501}, /* DDR3-800 SC */
|
|
|
|
{0, 0, 800, 400, 3438, 33438, 4065, 34065}, /* DDR2-400 SC */
|
|
{0, 0, 800, 667, 3410, 33410, 3889, 33889}, /* DDR2-667 SC */
|
|
{0, 0, 800, 800, 3403, 33403, 3845, 33845}, /* DDR2-800 SC */
|
|
{0, 1, 800, 667, 6476, 36476, 6955, 36955}, /* DDR3-667 SC */
|
|
{0, 1, 800, 800, 5958, 35958, 6400, 36400}, /* DDR3-800 SC */
|
|
|
|
{0, 0, 667, 400, 3456, 33456, 4103, 34106}, /* DDR2-400 SC */
|
|
{0, 0, 667, 667, 3428, 33428, 3927, 33927}, /* DDR2-667 SC */
|
|
{0, 0, 667, 800, 3443, 33443, 3905, 33905}, /* DDR2-800 SC */
|
|
{0, 1, 667, 667, 6494, 36494, 6993, 36993}, /* DDR3-667 SC */
|
|
{0, 1, 667, 800, 5998, 35998, 6460, 36460}, /* DDR3-800 SC */
|
|
|
|
{0, 0, 400, 400, 3528, 33528, 4255, 34255}, /* DDR2-400 SC */
|
|
{0, 0, 400, 667, 3500, 33500, 4079, 34079}, /* DDR2-667 SC */
|
|
{0, 0, 400, 800, 3487, 33487, 4029, 34029}, /* DDR2-800 SC */
|
|
{0, 1, 400, 667, 6566, 36566, 7145, 37145}, /* DDR3-667 SC */
|
|
{0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */
|
|
};
|
|
|
|
static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop,
|
|
int is_ddr3,
|
|
int fsb,
|
|
int mem)
|
|
{
|
|
const struct cxsr_latency *latency;
|
|
int i;
|
|
|
|
if (fsb == 0 || mem == 0)
|
|
return NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) {
|
|
latency = &cxsr_latency_table[i];
|
|
if (is_desktop == latency->is_desktop &&
|
|
is_ddr3 == latency->is_ddr3 &&
|
|
fsb == latency->fsb_freq && mem == latency->mem_freq)
|
|
return latency;
|
|
}
|
|
|
|
DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void pineview_disable_cxsr(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
/* deactivate cxsr */
|
|
I915_WRITE(DSPFW3, I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN);
|
|
}
|
|
|
|
/*
|
|
* Latency for FIFO fetches is dependent on several factors:
|
|
* - memory configuration (speed, channels)
|
|
* - chipset
|
|
* - current MCH state
|
|
* It can be fairly high in some situations, so here we assume a fairly
|
|
* pessimal value. It's a tradeoff between extra memory fetches (if we
|
|
* set this value too high, the FIFO will fetch frequently to stay full)
|
|
* and power consumption (set it too low to save power and we might see
|
|
* FIFO underruns and display "flicker").
|
|
*
|
|
* A value of 5us seems to be a good balance; safe for very low end
|
|
* platforms but not overly aggressive on lower latency configs.
|
|
*/
|
|
static const int latency_ns = 5000;
|
|
|
|
static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dsparb = I915_READ(DSPARB);
|
|
int size;
|
|
|
|
size = dsparb & 0x7f;
|
|
if (plane)
|
|
size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size;
|
|
|
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int i85x_get_fifo_size(struct drm_device *dev, int plane)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dsparb = I915_READ(DSPARB);
|
|
int size;
|
|
|
|
size = dsparb & 0x1ff;
|
|
if (plane)
|
|
size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - size;
|
|
size >>= 1; /* Convert to cachelines */
|
|
|
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int i845_get_fifo_size(struct drm_device *dev, int plane)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dsparb = I915_READ(DSPARB);
|
|
int size;
|
|
|
|
size = dsparb & 0x7f;
|
|
size >>= 2; /* Convert to cachelines */
|
|
|
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
plane ? "B" : "A",
|
|
size);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int i830_get_fifo_size(struct drm_device *dev, int plane)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dsparb = I915_READ(DSPARB);
|
|
int size;
|
|
|
|
size = dsparb & 0x7f;
|
|
size >>= 1; /* Convert to cachelines */
|
|
|
|
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
|
|
plane ? "B" : "A", size);
|
|
|
|
return size;
|
|
}
|
|
|
|
/* Pineview has different values for various configs */
|
|
static const struct intel_watermark_params pineview_display_wm = {
|
|
PINEVIEW_DISPLAY_FIFO,
|
|
PINEVIEW_MAX_WM,
|
|
PINEVIEW_DFT_WM,
|
|
PINEVIEW_GUARD_WM,
|
|
PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params pineview_display_hplloff_wm = {
|
|
PINEVIEW_DISPLAY_FIFO,
|
|
PINEVIEW_MAX_WM,
|
|
PINEVIEW_DFT_HPLLOFF_WM,
|
|
PINEVIEW_GUARD_WM,
|
|
PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params pineview_cursor_wm = {
|
|
PINEVIEW_CURSOR_FIFO,
|
|
PINEVIEW_CURSOR_MAX_WM,
|
|
PINEVIEW_CURSOR_DFT_WM,
|
|
PINEVIEW_CURSOR_GUARD_WM,
|
|
PINEVIEW_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
|
|
PINEVIEW_CURSOR_FIFO,
|
|
PINEVIEW_CURSOR_MAX_WM,
|
|
PINEVIEW_CURSOR_DFT_WM,
|
|
PINEVIEW_CURSOR_GUARD_WM,
|
|
PINEVIEW_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params g4x_wm_info = {
|
|
G4X_FIFO_SIZE,
|
|
G4X_MAX_WM,
|
|
G4X_MAX_WM,
|
|
2,
|
|
G4X_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params g4x_cursor_wm_info = {
|
|
I965_CURSOR_FIFO,
|
|
I965_CURSOR_MAX_WM,
|
|
I965_CURSOR_DFT_WM,
|
|
2,
|
|
G4X_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params valleyview_wm_info = {
|
|
VALLEYVIEW_FIFO_SIZE,
|
|
VALLEYVIEW_MAX_WM,
|
|
VALLEYVIEW_MAX_WM,
|
|
2,
|
|
G4X_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params valleyview_cursor_wm_info = {
|
|
I965_CURSOR_FIFO,
|
|
VALLEYVIEW_CURSOR_MAX_WM,
|
|
I965_CURSOR_DFT_WM,
|
|
2,
|
|
G4X_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params i965_cursor_wm_info = {
|
|
I965_CURSOR_FIFO,
|
|
I965_CURSOR_MAX_WM,
|
|
I965_CURSOR_DFT_WM,
|
|
2,
|
|
I915_FIFO_LINE_SIZE,
|
|
};
|
|
static const struct intel_watermark_params i945_wm_info = {
|
|
I945_FIFO_SIZE,
|
|
I915_MAX_WM,
|
|
1,
|
|
2,
|
|
I915_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params i915_wm_info = {
|
|
I915_FIFO_SIZE,
|
|
I915_MAX_WM,
|
|
1,
|
|
2,
|
|
I915_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params i855_wm_info = {
|
|
I855GM_FIFO_SIZE,
|
|
I915_MAX_WM,
|
|
1,
|
|
2,
|
|
I830_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params i830_wm_info = {
|
|
I830_FIFO_SIZE,
|
|
I915_MAX_WM,
|
|
1,
|
|
2,
|
|
I830_FIFO_LINE_SIZE
|
|
};
|
|
|
|
static const struct intel_watermark_params ironlake_display_wm_info = {
|
|
ILK_DISPLAY_FIFO,
|
|
ILK_DISPLAY_MAXWM,
|
|
ILK_DISPLAY_DFTWM,
|
|
2,
|
|
ILK_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params ironlake_cursor_wm_info = {
|
|
ILK_CURSOR_FIFO,
|
|
ILK_CURSOR_MAXWM,
|
|
ILK_CURSOR_DFTWM,
|
|
2,
|
|
ILK_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params ironlake_display_srwm_info = {
|
|
ILK_DISPLAY_SR_FIFO,
|
|
ILK_DISPLAY_MAX_SRWM,
|
|
ILK_DISPLAY_DFT_SRWM,
|
|
2,
|
|
ILK_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params ironlake_cursor_srwm_info = {
|
|
ILK_CURSOR_SR_FIFO,
|
|
ILK_CURSOR_MAX_SRWM,
|
|
ILK_CURSOR_DFT_SRWM,
|
|
2,
|
|
ILK_FIFO_LINE_SIZE
|
|
};
|
|
|
|
static const struct intel_watermark_params sandybridge_display_wm_info = {
|
|
SNB_DISPLAY_FIFO,
|
|
SNB_DISPLAY_MAXWM,
|
|
SNB_DISPLAY_DFTWM,
|
|
2,
|
|
SNB_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params sandybridge_cursor_wm_info = {
|
|
SNB_CURSOR_FIFO,
|
|
SNB_CURSOR_MAXWM,
|
|
SNB_CURSOR_DFTWM,
|
|
2,
|
|
SNB_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params sandybridge_display_srwm_info = {
|
|
SNB_DISPLAY_SR_FIFO,
|
|
SNB_DISPLAY_MAX_SRWM,
|
|
SNB_DISPLAY_DFT_SRWM,
|
|
2,
|
|
SNB_FIFO_LINE_SIZE
|
|
};
|
|
static const struct intel_watermark_params sandybridge_cursor_srwm_info = {
|
|
SNB_CURSOR_SR_FIFO,
|
|
SNB_CURSOR_MAX_SRWM,
|
|
SNB_CURSOR_DFT_SRWM,
|
|
2,
|
|
SNB_FIFO_LINE_SIZE
|
|
};
|
|
|
|
|
|
/**
|
|
* intel_calculate_wm - calculate watermark level
|
|
* @clock_in_khz: pixel clock
|
|
* @wm: chip FIFO params
|
|
* @pixel_size: display pixel size
|
|
* @latency_ns: memory latency for the platform
|
|
*
|
|
* Calculate the watermark level (the level at which the display plane will
|
|
* start fetching from memory again). Each chip has a different display
|
|
* FIFO size and allocation, so the caller needs to figure that out and pass
|
|
* in the correct intel_watermark_params structure.
|
|
*
|
|
* As the pixel clock runs, the FIFO will be drained at a rate that depends
|
|
* on the pixel size. When it reaches the watermark level, it'll start
|
|
* fetching FIFO line sized based chunks from memory until the FIFO fills
|
|
* past the watermark point. If the FIFO drains completely, a FIFO underrun
|
|
* will occur, and a display engine hang could result.
|
|
*/
|
|
static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
|
|
const struct intel_watermark_params *wm,
|
|
int fifo_size,
|
|
int pixel_size,
|
|
unsigned long latency_ns)
|
|
{
|
|
long entries_required, wm_size;
|
|
|
|
/*
|
|
* Note: we need to make sure we don't overflow for various clock &
|
|
* latency values.
|
|
* clocks go from a few thousand to several hundred thousand.
|
|
* latency is usually a few thousand
|
|
*/
|
|
entries_required = ((clock_in_khz / 1000) * pixel_size * latency_ns) /
|
|
1000;
|
|
entries_required = DIV_ROUND_UP(entries_required, wm->cacheline_size);
|
|
|
|
DRM_DEBUG_KMS("FIFO entries required for mode: %ld\n", entries_required);
|
|
|
|
wm_size = fifo_size - (entries_required + wm->guard_size);
|
|
|
|
DRM_DEBUG_KMS("FIFO watermark level: %ld\n", wm_size);
|
|
|
|
/* Don't promote wm_size to unsigned... */
|
|
if (wm_size > (long)wm->max_wm)
|
|
wm_size = wm->max_wm;
|
|
if (wm_size <= 0)
|
|
wm_size = wm->default_wm;
|
|
return wm_size;
|
|
}
|
|
|
|
static struct drm_crtc *single_enabled_crtc(struct drm_device *dev)
|
|
{
|
|
struct drm_crtc *crtc, *enabled = NULL;
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
if (crtc->enabled && crtc->fb) {
|
|
if (enabled)
|
|
return NULL;
|
|
enabled = crtc;
|
|
}
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
static void pineview_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_crtc *crtc;
|
|
const struct cxsr_latency *latency;
|
|
u32 reg;
|
|
unsigned long wm;
|
|
|
|
latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->is_ddr3,
|
|
dev_priv->fsb_freq, dev_priv->mem_freq);
|
|
if (!latency) {
|
|
DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
|
|
pineview_disable_cxsr(dev);
|
|
return;
|
|
}
|
|
|
|
crtc = single_enabled_crtc(dev);
|
|
if (crtc) {
|
|
int clock = crtc->mode.clock;
|
|
int pixel_size = crtc->fb->bits_per_pixel / 8;
|
|
|
|
/* Display SR */
|
|
wm = intel_calculate_wm(clock, &pineview_display_wm,
|
|
pineview_display_wm.fifo_size,
|
|
pixel_size, latency->display_sr);
|
|
reg = I915_READ(DSPFW1);
|
|
reg &= ~DSPFW_SR_MASK;
|
|
reg |= wm << DSPFW_SR_SHIFT;
|
|
I915_WRITE(DSPFW1, reg);
|
|
DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
|
|
|
|
/* cursor SR */
|
|
wm = intel_calculate_wm(clock, &pineview_cursor_wm,
|
|
pineview_display_wm.fifo_size,
|
|
pixel_size, latency->cursor_sr);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= ~DSPFW_CURSOR_SR_MASK;
|
|
reg |= (wm & 0x3f) << DSPFW_CURSOR_SR_SHIFT;
|
|
I915_WRITE(DSPFW3, reg);
|
|
|
|
/* Display HPLL off SR */
|
|
wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm,
|
|
pineview_display_hplloff_wm.fifo_size,
|
|
pixel_size, latency->display_hpll_disable);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= ~DSPFW_HPLL_SR_MASK;
|
|
reg |= wm & DSPFW_HPLL_SR_MASK;
|
|
I915_WRITE(DSPFW3, reg);
|
|
|
|
/* cursor HPLL off SR */
|
|
wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm,
|
|
pineview_display_hplloff_wm.fifo_size,
|
|
pixel_size, latency->cursor_hpll_disable);
|
|
reg = I915_READ(DSPFW3);
|
|
reg &= ~DSPFW_HPLL_CURSOR_MASK;
|
|
reg |= (wm & 0x3f) << DSPFW_HPLL_CURSOR_SHIFT;
|
|
I915_WRITE(DSPFW3, reg);
|
|
DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
|
|
|
|
/* activate cxsr */
|
|
I915_WRITE(DSPFW3,
|
|
I915_READ(DSPFW3) | PINEVIEW_SELF_REFRESH_EN);
|
|
DRM_DEBUG_KMS("Self-refresh is enabled\n");
|
|
} else {
|
|
pineview_disable_cxsr(dev);
|
|
DRM_DEBUG_KMS("Self-refresh is disabled\n");
|
|
}
|
|
}
|
|
|
|
static bool g4x_compute_wm0(struct drm_device *dev,
|
|
int plane,
|
|
const struct intel_watermark_params *display,
|
|
int display_latency_ns,
|
|
const struct intel_watermark_params *cursor,
|
|
int cursor_latency_ns,
|
|
int *plane_wm,
|
|
int *cursor_wm)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
int htotal, hdisplay, clock, pixel_size;
|
|
int line_time_us, line_count;
|
|
int entries, tlb_miss;
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
if (crtc->fb == NULL || !crtc->enabled) {
|
|
*cursor_wm = cursor->guard_size;
|
|
*plane_wm = display->guard_size;
|
|
return false;
|
|
}
|
|
|
|
htotal = crtc->mode.htotal;
|
|
hdisplay = crtc->mode.hdisplay;
|
|
clock = crtc->mode.clock;
|
|
pixel_size = crtc->fb->bits_per_pixel / 8;
|
|
|
|
/* Use the small buffer method to calculate plane watermark */
|
|
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
|
|
tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8;
|
|
if (tlb_miss > 0)
|
|
entries += tlb_miss;
|
|
entries = DIV_ROUND_UP(entries, display->cacheline_size);
|
|
*plane_wm = entries + display->guard_size;
|
|
if (*plane_wm > (int)display->max_wm)
|
|
*plane_wm = display->max_wm;
|
|
|
|
/* Use the large buffer method to calculate cursor watermark */
|
|
line_time_us = ((htotal * 1000) / clock);
|
|
line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
|
|
entries = line_count * 64 * pixel_size;
|
|
tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
|
|
if (tlb_miss > 0)
|
|
entries += tlb_miss;
|
|
entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
|
|
*cursor_wm = entries + cursor->guard_size;
|
|
if (*cursor_wm > (int)cursor->max_wm)
|
|
*cursor_wm = (int)cursor->max_wm;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Check the wm result.
|
|
*
|
|
* If any calculated watermark values is larger than the maximum value that
|
|
* can be programmed into the associated watermark register, that watermark
|
|
* must be disabled.
|
|
*/
|
|
static bool g4x_check_srwm(struct drm_device *dev,
|
|
int display_wm, int cursor_wm,
|
|
const struct intel_watermark_params *display,
|
|
const struct intel_watermark_params *cursor)
|
|
{
|
|
DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
|
|
display_wm, cursor_wm);
|
|
|
|
if (display_wm > display->max_wm) {
|
|
DRM_DEBUG_KMS("display watermark is too large(%d/%ld), disabling\n",
|
|
display_wm, display->max_wm);
|
|
return false;
|
|
}
|
|
|
|
if (cursor_wm > cursor->max_wm) {
|
|
DRM_DEBUG_KMS("cursor watermark is too large(%d/%ld), disabling\n",
|
|
cursor_wm, cursor->max_wm);
|
|
return false;
|
|
}
|
|
|
|
if (!(display_wm || cursor_wm)) {
|
|
DRM_DEBUG_KMS("SR latency is 0, disabling\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool g4x_compute_srwm(struct drm_device *dev,
|
|
int plane,
|
|
int latency_ns,
|
|
const struct intel_watermark_params *display,
|
|
const struct intel_watermark_params *cursor,
|
|
int *display_wm, int *cursor_wm)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
int hdisplay, htotal, pixel_size, clock;
|
|
unsigned long line_time_us;
|
|
int line_count, line_size;
|
|
int small, large;
|
|
int entries;
|
|
|
|
if (!latency_ns) {
|
|
*display_wm = *cursor_wm = 0;
|
|
return false;
|
|
}
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
hdisplay = crtc->mode.hdisplay;
|
|
htotal = crtc->mode.htotal;
|
|
clock = crtc->mode.clock;
|
|
pixel_size = crtc->fb->bits_per_pixel / 8;
|
|
|
|
line_time_us = (htotal * 1000) / clock;
|
|
line_count = (latency_ns / line_time_us + 1000) / 1000;
|
|
line_size = hdisplay * pixel_size;
|
|
|
|
/* Use the minimum of the small and large buffer method for primary */
|
|
small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
|
|
large = line_count * line_size;
|
|
|
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
|
|
*display_wm = entries + display->guard_size;
|
|
|
|
/* calculate the self-refresh watermark for display cursor */
|
|
entries = line_count * pixel_size * 64;
|
|
entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
|
|
*cursor_wm = entries + cursor->guard_size;
|
|
|
|
return g4x_check_srwm(dev,
|
|
*display_wm, *cursor_wm,
|
|
display, cursor);
|
|
}
|
|
|
|
static bool vlv_compute_drain_latency(struct drm_device *dev,
|
|
int plane,
|
|
int *plane_prec_mult,
|
|
int *plane_dl,
|
|
int *cursor_prec_mult,
|
|
int *cursor_dl)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
int clock, pixel_size;
|
|
int entries;
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
if (crtc->fb == NULL || !crtc->enabled)
|
|
return false;
|
|
|
|
clock = crtc->mode.clock; /* VESA DOT Clock */
|
|
pixel_size = crtc->fb->bits_per_pixel / 8; /* BPP */
|
|
|
|
entries = (clock / 1000) * pixel_size;
|
|
*plane_prec_mult = (entries > 256) ?
|
|
DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_16;
|
|
*plane_dl = (64 * (*plane_prec_mult) * 4) / ((clock / 1000) *
|
|
pixel_size);
|
|
|
|
entries = (clock / 1000) * 4; /* BPP is always 4 for cursor */
|
|
*cursor_prec_mult = (entries > 256) ?
|
|
DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_16;
|
|
*cursor_dl = (64 * (*cursor_prec_mult) * 4) / ((clock / 1000) * 4);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
static void vlv_update_drain_latency(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int planea_prec, planea_dl, planeb_prec, planeb_dl;
|
|
int cursora_prec, cursora_dl, cursorb_prec, cursorb_dl;
|
|
int plane_prec_mult, cursor_prec_mult; /* Precision multiplier is
|
|
either 16 or 32 */
|
|
|
|
/* For plane A, Cursor A */
|
|
if (vlv_compute_drain_latency(dev, 0, &plane_prec_mult, &planea_dl,
|
|
&cursor_prec_mult, &cursora_dl)) {
|
|
cursora_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
|
|
DDL_CURSORA_PRECISION_32 : DDL_CURSORA_PRECISION_16;
|
|
planea_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
|
|
DDL_PLANEA_PRECISION_32 : DDL_PLANEA_PRECISION_16;
|
|
|
|
I915_WRITE(VLV_DDL1, cursora_prec |
|
|
(cursora_dl << DDL_CURSORA_SHIFT) |
|
|
planea_prec | planea_dl);
|
|
}
|
|
|
|
/* For plane B, Cursor B */
|
|
if (vlv_compute_drain_latency(dev, 1, &plane_prec_mult, &planeb_dl,
|
|
&cursor_prec_mult, &cursorb_dl)) {
|
|
cursorb_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
|
|
DDL_CURSORB_PRECISION_32 : DDL_CURSORB_PRECISION_16;
|
|
planeb_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
|
|
DDL_PLANEB_PRECISION_32 : DDL_PLANEB_PRECISION_16;
|
|
|
|
I915_WRITE(VLV_DDL2, cursorb_prec |
|
|
(cursorb_dl << DDL_CURSORB_SHIFT) |
|
|
planeb_prec | planeb_dl);
|
|
}
|
|
}
|
|
|
|
#define single_plane_enabled(mask) is_power_of_2(mask)
|
|
|
|
static void valleyview_update_wm(struct drm_device *dev)
|
|
{
|
|
static const int sr_latency_ns = 12000;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
|
|
int plane_sr, cursor_sr;
|
|
unsigned int enabled = 0;
|
|
|
|
vlv_update_drain_latency(dev);
|
|
|
|
if (g4x_compute_wm0(dev, 0,
|
|
&valleyview_wm_info, latency_ns,
|
|
&valleyview_cursor_wm_info, latency_ns,
|
|
&planea_wm, &cursora_wm))
|
|
enabled |= 1;
|
|
|
|
if (g4x_compute_wm0(dev, 1,
|
|
&valleyview_wm_info, latency_ns,
|
|
&valleyview_cursor_wm_info, latency_ns,
|
|
&planeb_wm, &cursorb_wm))
|
|
enabled |= 2;
|
|
|
|
plane_sr = cursor_sr = 0;
|
|
if (single_plane_enabled(enabled) &&
|
|
g4x_compute_srwm(dev, ffs(enabled) - 1,
|
|
sr_latency_ns,
|
|
&valleyview_wm_info,
|
|
&valleyview_cursor_wm_info,
|
|
&plane_sr, &cursor_sr))
|
|
I915_WRITE(FW_BLC_SELF_VLV, FW_CSPWRDWNEN);
|
|
else
|
|
I915_WRITE(FW_BLC_SELF_VLV,
|
|
I915_READ(FW_BLC_SELF_VLV) & ~FW_CSPWRDWNEN);
|
|
|
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
|
|
planea_wm, cursora_wm,
|
|
planeb_wm, cursorb_wm,
|
|
plane_sr, cursor_sr);
|
|
|
|
I915_WRITE(DSPFW1,
|
|
(plane_sr << DSPFW_SR_SHIFT) |
|
|
(cursorb_wm << DSPFW_CURSORB_SHIFT) |
|
|
(planeb_wm << DSPFW_PLANEB_SHIFT) |
|
|
planea_wm);
|
|
I915_WRITE(DSPFW2,
|
|
(I915_READ(DSPFW2) & DSPFW_CURSORA_MASK) |
|
|
(cursora_wm << DSPFW_CURSORA_SHIFT));
|
|
I915_WRITE(DSPFW3,
|
|
(I915_READ(DSPFW3) | (cursor_sr << DSPFW_CURSOR_SR_SHIFT)));
|
|
}
|
|
|
|
static void g4x_update_wm(struct drm_device *dev)
|
|
{
|
|
static const int sr_latency_ns = 12000;
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
|
|
int plane_sr, cursor_sr;
|
|
unsigned int enabled = 0;
|
|
|
|
if (g4x_compute_wm0(dev, 0,
|
|
&g4x_wm_info, latency_ns,
|
|
&g4x_cursor_wm_info, latency_ns,
|
|
&planea_wm, &cursora_wm))
|
|
enabled |= 1;
|
|
|
|
if (g4x_compute_wm0(dev, 1,
|
|
&g4x_wm_info, latency_ns,
|
|
&g4x_cursor_wm_info, latency_ns,
|
|
&planeb_wm, &cursorb_wm))
|
|
enabled |= 2;
|
|
|
|
plane_sr = cursor_sr = 0;
|
|
if (single_plane_enabled(enabled) &&
|
|
g4x_compute_srwm(dev, ffs(enabled) - 1,
|
|
sr_latency_ns,
|
|
&g4x_wm_info,
|
|
&g4x_cursor_wm_info,
|
|
&plane_sr, &cursor_sr))
|
|
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
|
|
else
|
|
I915_WRITE(FW_BLC_SELF,
|
|
I915_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN);
|
|
|
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
|
|
planea_wm, cursora_wm,
|
|
planeb_wm, cursorb_wm,
|
|
plane_sr, cursor_sr);
|
|
|
|
I915_WRITE(DSPFW1,
|
|
(plane_sr << DSPFW_SR_SHIFT) |
|
|
(cursorb_wm << DSPFW_CURSORB_SHIFT) |
|
|
(planeb_wm << DSPFW_PLANEB_SHIFT) |
|
|
planea_wm);
|
|
I915_WRITE(DSPFW2,
|
|
(I915_READ(DSPFW2) & DSPFW_CURSORA_MASK) |
|
|
(cursora_wm << DSPFW_CURSORA_SHIFT));
|
|
/* HPLL off in SR has some issues on G4x... disable it */
|
|
I915_WRITE(DSPFW3,
|
|
(I915_READ(DSPFW3) & ~DSPFW_HPLL_SR_EN) |
|
|
(cursor_sr << DSPFW_CURSOR_SR_SHIFT));
|
|
}
|
|
|
|
static void i965_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_crtc *crtc;
|
|
int srwm = 1;
|
|
int cursor_sr = 16;
|
|
|
|
/* Calc sr entries for one plane configs */
|
|
crtc = single_enabled_crtc(dev);
|
|
if (crtc) {
|
|
/* self-refresh has much higher latency */
|
|
static const int sr_latency_ns = 12000;
|
|
int clock = crtc->mode.clock;
|
|
int htotal = crtc->mode.htotal;
|
|
int hdisplay = crtc->mode.hdisplay;
|
|
int pixel_size = crtc->fb->bits_per_pixel / 8;
|
|
unsigned long line_time_us;
|
|
int entries;
|
|
|
|
line_time_us = ((htotal * 1000) / clock);
|
|
|
|
/* Use ns/us then divide to preserve precision */
|
|
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
|
|
pixel_size * hdisplay;
|
|
entries = DIV_ROUND_UP(entries, I915_FIFO_LINE_SIZE);
|
|
srwm = I965_FIFO_SIZE - entries;
|
|
if (srwm < 0)
|
|
srwm = 1;
|
|
srwm &= 0x1ff;
|
|
DRM_DEBUG_KMS("self-refresh entries: %d, wm: %d\n",
|
|
entries, srwm);
|
|
|
|
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
|
|
pixel_size * 64;
|
|
entries = DIV_ROUND_UP(entries,
|
|
i965_cursor_wm_info.cacheline_size);
|
|
cursor_sr = i965_cursor_wm_info.fifo_size -
|
|
(entries + i965_cursor_wm_info.guard_size);
|
|
|
|
if (cursor_sr > i965_cursor_wm_info.max_wm)
|
|
cursor_sr = i965_cursor_wm_info.max_wm;
|
|
|
|
DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
|
|
"cursor %d\n", srwm, cursor_sr);
|
|
|
|
if (IS_CRESTLINE(dev))
|
|
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
|
|
} else {
|
|
/* Turn off self refresh if both pipes are enabled */
|
|
if (IS_CRESTLINE(dev))
|
|
I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
|
|
& ~FW_BLC_SELF_EN);
|
|
}
|
|
|
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
|
|
srwm);
|
|
|
|
/* 965 has limitations... */
|
|
I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) |
|
|
(8 << 16) | (8 << 8) | (8 << 0));
|
|
I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
|
|
/* update cursor SR watermark */
|
|
I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
|
|
}
|
|
|
|
static void i9xx_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
const struct intel_watermark_params *wm_info;
|
|
uint32_t fwater_lo;
|
|
uint32_t fwater_hi;
|
|
int cwm, srwm = 1;
|
|
int fifo_size;
|
|
int planea_wm, planeb_wm;
|
|
struct drm_crtc *crtc, *enabled = NULL;
|
|
|
|
if (IS_I945GM(dev))
|
|
wm_info = &i945_wm_info;
|
|
else if (!IS_GEN2(dev))
|
|
wm_info = &i915_wm_info;
|
|
else
|
|
wm_info = &i855_wm_info;
|
|
|
|
fifo_size = dev_priv->display.get_fifo_size(dev, 0);
|
|
crtc = intel_get_crtc_for_plane(dev, 0);
|
|
if (crtc->enabled && crtc->fb) {
|
|
planea_wm = intel_calculate_wm(crtc->mode.clock,
|
|
wm_info, fifo_size,
|
|
crtc->fb->bits_per_pixel / 8,
|
|
latency_ns);
|
|
enabled = crtc;
|
|
} else
|
|
planea_wm = fifo_size - wm_info->guard_size;
|
|
|
|
fifo_size = dev_priv->display.get_fifo_size(dev, 1);
|
|
crtc = intel_get_crtc_for_plane(dev, 1);
|
|
if (crtc->enabled && crtc->fb) {
|
|
planeb_wm = intel_calculate_wm(crtc->mode.clock,
|
|
wm_info, fifo_size,
|
|
crtc->fb->bits_per_pixel / 8,
|
|
latency_ns);
|
|
if (enabled == NULL)
|
|
enabled = crtc;
|
|
else
|
|
enabled = NULL;
|
|
} else
|
|
planeb_wm = fifo_size - wm_info->guard_size;
|
|
|
|
DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
|
|
|
|
/*
|
|
* Overlay gets an aggressive default since video jitter is bad.
|
|
*/
|
|
cwm = 2;
|
|
|
|
/* Play safe and disable self-refresh before adjusting watermarks. */
|
|
if (IS_I945G(dev) || IS_I945GM(dev))
|
|
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | 0);
|
|
else if (IS_I915GM(dev))
|
|
I915_WRITE(INSTPM, I915_READ(INSTPM) & ~INSTPM_SELF_EN);
|
|
|
|
/* Calc sr entries for one plane configs */
|
|
if (HAS_FW_BLC(dev) && enabled) {
|
|
/* self-refresh has much higher latency */
|
|
static const int sr_latency_ns = 6000;
|
|
int clock = enabled->mode.clock;
|
|
int htotal = enabled->mode.htotal;
|
|
int hdisplay = enabled->mode.hdisplay;
|
|
int pixel_size = enabled->fb->bits_per_pixel / 8;
|
|
unsigned long line_time_us;
|
|
int entries;
|
|
|
|
line_time_us = (htotal * 1000) / clock;
|
|
|
|
/* Use ns/us then divide to preserve precision */
|
|
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
|
|
pixel_size * hdisplay;
|
|
entries = DIV_ROUND_UP(entries, wm_info->cacheline_size);
|
|
DRM_DEBUG_KMS("self-refresh entries: %d\n", entries);
|
|
srwm = wm_info->fifo_size - entries;
|
|
if (srwm < 0)
|
|
srwm = 1;
|
|
|
|
if (IS_I945G(dev) || IS_I945GM(dev))
|
|
I915_WRITE(FW_BLC_SELF,
|
|
FW_BLC_SELF_FIFO_MASK | (srwm & 0xff));
|
|
else if (IS_I915GM(dev))
|
|
I915_WRITE(FW_BLC_SELF, srwm & 0x3f);
|
|
}
|
|
|
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
|
|
planea_wm, planeb_wm, cwm, srwm);
|
|
|
|
fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
|
|
fwater_hi = (cwm & 0x1f);
|
|
|
|
/* Set request length to 8 cachelines per fetch */
|
|
fwater_lo = fwater_lo | (1 << 24) | (1 << 8);
|
|
fwater_hi = fwater_hi | (1 << 8);
|
|
|
|
I915_WRITE(FW_BLC, fwater_lo);
|
|
I915_WRITE(FW_BLC2, fwater_hi);
|
|
|
|
if (HAS_FW_BLC(dev)) {
|
|
if (enabled) {
|
|
if (IS_I945G(dev) || IS_I945GM(dev))
|
|
I915_WRITE(FW_BLC_SELF,
|
|
FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN);
|
|
else if (IS_I915GM(dev))
|
|
I915_WRITE(INSTPM, I915_READ(INSTPM) | INSTPM_SELF_EN);
|
|
DRM_DEBUG_KMS("memory self refresh enabled\n");
|
|
} else
|
|
DRM_DEBUG_KMS("memory self refresh disabled\n");
|
|
}
|
|
}
|
|
|
|
static void i830_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
struct drm_crtc *crtc;
|
|
uint32_t fwater_lo;
|
|
int planea_wm;
|
|
|
|
crtc = single_enabled_crtc(dev);
|
|
if (crtc == NULL)
|
|
return;
|
|
|
|
planea_wm = intel_calculate_wm(crtc->mode.clock, &i830_wm_info,
|
|
dev_priv->display.get_fifo_size(dev, 0),
|
|
crtc->fb->bits_per_pixel / 8,
|
|
latency_ns);
|
|
fwater_lo = I915_READ(FW_BLC) & ~0xfff;
|
|
fwater_lo |= (3<<8) | planea_wm;
|
|
|
|
DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d\n", planea_wm);
|
|
|
|
I915_WRITE(FW_BLC, fwater_lo);
|
|
}
|
|
|
|
#define ILK_LP0_PLANE_LATENCY 700
|
|
#define ILK_LP0_CURSOR_LATENCY 1300
|
|
|
|
/*
|
|
* Check the wm result.
|
|
*
|
|
* If any calculated watermark values is larger than the maximum value that
|
|
* can be programmed into the associated watermark register, that watermark
|
|
* must be disabled.
|
|
*/
|
|
static bool ironlake_check_srwm(struct drm_device *dev, int level,
|
|
int fbc_wm, int display_wm, int cursor_wm,
|
|
const struct intel_watermark_params *display,
|
|
const struct intel_watermark_params *cursor)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d,"
|
|
" cursor %d\n", level, display_wm, fbc_wm, cursor_wm);
|
|
|
|
if (fbc_wm > SNB_FBC_MAX_SRWM) {
|
|
DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
|
|
fbc_wm, SNB_FBC_MAX_SRWM, level);
|
|
|
|
/* fbc has it's own way to disable FBC WM */
|
|
I915_WRITE(DISP_ARB_CTL,
|
|
I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
|
|
return false;
|
|
}
|
|
|
|
if (display_wm > display->max_wm) {
|
|
DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
|
|
display_wm, SNB_DISPLAY_MAX_SRWM, level);
|
|
return false;
|
|
}
|
|
|
|
if (cursor_wm > cursor->max_wm) {
|
|
DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
|
|
cursor_wm, SNB_CURSOR_MAX_SRWM, level);
|
|
return false;
|
|
}
|
|
|
|
if (!(fbc_wm || display_wm || cursor_wm)) {
|
|
DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Compute watermark values of WM[1-3],
|
|
*/
|
|
static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane,
|
|
int latency_ns,
|
|
const struct intel_watermark_params *display,
|
|
const struct intel_watermark_params *cursor,
|
|
int *fbc_wm, int *display_wm, int *cursor_wm)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
unsigned long line_time_us;
|
|
int hdisplay, htotal, pixel_size, clock;
|
|
int line_count, line_size;
|
|
int small, large;
|
|
int entries;
|
|
|
|
if (!latency_ns) {
|
|
*fbc_wm = *display_wm = *cursor_wm = 0;
|
|
return false;
|
|
}
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
hdisplay = crtc->mode.hdisplay;
|
|
htotal = crtc->mode.htotal;
|
|
clock = crtc->mode.clock;
|
|
pixel_size = crtc->fb->bits_per_pixel / 8;
|
|
|
|
line_time_us = (htotal * 1000) / clock;
|
|
line_count = (latency_ns / line_time_us + 1000) / 1000;
|
|
line_size = hdisplay * pixel_size;
|
|
|
|
/* Use the minimum of the small and large buffer method for primary */
|
|
small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
|
|
large = line_count * line_size;
|
|
|
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
|
|
*display_wm = entries + display->guard_size;
|
|
|
|
/*
|
|
* Spec says:
|
|
* FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
|
|
*/
|
|
*fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
|
|
|
|
/* calculate the self-refresh watermark for display cursor */
|
|
entries = line_count * pixel_size * 64;
|
|
entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
|
|
*cursor_wm = entries + cursor->guard_size;
|
|
|
|
return ironlake_check_srwm(dev, level,
|
|
*fbc_wm, *display_wm, *cursor_wm,
|
|
display, cursor);
|
|
}
|
|
|
|
static void ironlake_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int fbc_wm, plane_wm, cursor_wm;
|
|
unsigned int enabled;
|
|
|
|
enabled = 0;
|
|
if (g4x_compute_wm0(dev, 0,
|
|
&ironlake_display_wm_info,
|
|
ILK_LP0_PLANE_LATENCY,
|
|
&ironlake_cursor_wm_info,
|
|
ILK_LP0_CURSOR_LATENCY,
|
|
&plane_wm, &cursor_wm)) {
|
|
I915_WRITE(WM0_PIPEA_ILK,
|
|
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
|
|
DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
|
|
" plane %d, " "cursor: %d\n",
|
|
plane_wm, cursor_wm);
|
|
enabled |= 1;
|
|
}
|
|
|
|
if (g4x_compute_wm0(dev, 1,
|
|
&ironlake_display_wm_info,
|
|
ILK_LP0_PLANE_LATENCY,
|
|
&ironlake_cursor_wm_info,
|
|
ILK_LP0_CURSOR_LATENCY,
|
|
&plane_wm, &cursor_wm)) {
|
|
I915_WRITE(WM0_PIPEB_ILK,
|
|
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
|
|
DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
|
|
" plane %d, cursor: %d\n",
|
|
plane_wm, cursor_wm);
|
|
enabled |= 2;
|
|
}
|
|
|
|
/*
|
|
* Calculate and update the self-refresh watermark only when one
|
|
* display plane is used.
|
|
*/
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
if (!single_plane_enabled(enabled))
|
|
return;
|
|
enabled = ffs(enabled) - 1;
|
|
|
|
/* WM1 */
|
|
if (!ironlake_compute_srwm(dev, 1, enabled,
|
|
ILK_READ_WM1_LATENCY() * 500,
|
|
&ironlake_display_srwm_info,
|
|
&ironlake_cursor_srwm_info,
|
|
&fbc_wm, &plane_wm, &cursor_wm))
|
|
return;
|
|
|
|
I915_WRITE(WM1_LP_ILK,
|
|
WM1_LP_SR_EN |
|
|
(ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
|
|
(fbc_wm << WM1_LP_FBC_SHIFT) |
|
|
(plane_wm << WM1_LP_SR_SHIFT) |
|
|
cursor_wm);
|
|
|
|
/* WM2 */
|
|
if (!ironlake_compute_srwm(dev, 2, enabled,
|
|
ILK_READ_WM2_LATENCY() * 500,
|
|
&ironlake_display_srwm_info,
|
|
&ironlake_cursor_srwm_info,
|
|
&fbc_wm, &plane_wm, &cursor_wm))
|
|
return;
|
|
|
|
I915_WRITE(WM2_LP_ILK,
|
|
WM2_LP_EN |
|
|
(ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
|
|
(fbc_wm << WM1_LP_FBC_SHIFT) |
|
|
(plane_wm << WM1_LP_SR_SHIFT) |
|
|
cursor_wm);
|
|
|
|
/*
|
|
* WM3 is unsupported on ILK, probably because we don't have latency
|
|
* data for that power state
|
|
*/
|
|
}
|
|
|
|
static void sandybridge_update_wm(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */
|
|
u32 val;
|
|
int fbc_wm, plane_wm, cursor_wm;
|
|
unsigned int enabled;
|
|
|
|
enabled = 0;
|
|
if (g4x_compute_wm0(dev, 0,
|
|
&sandybridge_display_wm_info, latency,
|
|
&sandybridge_cursor_wm_info, latency,
|
|
&plane_wm, &cursor_wm)) {
|
|
val = I915_READ(WM0_PIPEA_ILK);
|
|
val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
|
|
I915_WRITE(WM0_PIPEA_ILK, val |
|
|
((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
|
|
DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
|
|
" plane %d, " "cursor: %d\n",
|
|
plane_wm, cursor_wm);
|
|
enabled |= 1;
|
|
}
|
|
|
|
if (g4x_compute_wm0(dev, 1,
|
|
&sandybridge_display_wm_info, latency,
|
|
&sandybridge_cursor_wm_info, latency,
|
|
&plane_wm, &cursor_wm)) {
|
|
val = I915_READ(WM0_PIPEB_ILK);
|
|
val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
|
|
I915_WRITE(WM0_PIPEB_ILK, val |
|
|
((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
|
|
DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
|
|
" plane %d, cursor: %d\n",
|
|
plane_wm, cursor_wm);
|
|
enabled |= 2;
|
|
}
|
|
|
|
/* IVB has 3 pipes */
|
|
if (IS_IVYBRIDGE(dev) &&
|
|
g4x_compute_wm0(dev, 2,
|
|
&sandybridge_display_wm_info, latency,
|
|
&sandybridge_cursor_wm_info, latency,
|
|
&plane_wm, &cursor_wm)) {
|
|
val = I915_READ(WM0_PIPEC_IVB);
|
|
val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
|
|
I915_WRITE(WM0_PIPEC_IVB, val |
|
|
((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
|
|
DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
|
|
" plane %d, cursor: %d\n",
|
|
plane_wm, cursor_wm);
|
|
enabled |= 3;
|
|
}
|
|
|
|
/*
|
|
* Calculate and update the self-refresh watermark only when one
|
|
* display plane is used.
|
|
*
|
|
* SNB support 3 levels of watermark.
|
|
*
|
|
* WM1/WM2/WM2 watermarks have to be enabled in the ascending order,
|
|
* and disabled in the descending order
|
|
*
|
|
*/
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
if (!single_plane_enabled(enabled) ||
|
|
dev_priv->sprite_scaling_enabled)
|
|
return;
|
|
enabled = ffs(enabled) - 1;
|
|
|
|
/* WM1 */
|
|
if (!ironlake_compute_srwm(dev, 1, enabled,
|
|
SNB_READ_WM1_LATENCY() * 500,
|
|
&sandybridge_display_srwm_info,
|
|
&sandybridge_cursor_srwm_info,
|
|
&fbc_wm, &plane_wm, &cursor_wm))
|
|
return;
|
|
|
|
I915_WRITE(WM1_LP_ILK,
|
|
WM1_LP_SR_EN |
|
|
(SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
|
|
(fbc_wm << WM1_LP_FBC_SHIFT) |
|
|
(plane_wm << WM1_LP_SR_SHIFT) |
|
|
cursor_wm);
|
|
|
|
/* WM2 */
|
|
if (!ironlake_compute_srwm(dev, 2, enabled,
|
|
SNB_READ_WM2_LATENCY() * 500,
|
|
&sandybridge_display_srwm_info,
|
|
&sandybridge_cursor_srwm_info,
|
|
&fbc_wm, &plane_wm, &cursor_wm))
|
|
return;
|
|
|
|
I915_WRITE(WM2_LP_ILK,
|
|
WM2_LP_EN |
|
|
(SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
|
|
(fbc_wm << WM1_LP_FBC_SHIFT) |
|
|
(plane_wm << WM1_LP_SR_SHIFT) |
|
|
cursor_wm);
|
|
|
|
/* WM3 */
|
|
if (!ironlake_compute_srwm(dev, 3, enabled,
|
|
SNB_READ_WM3_LATENCY() * 500,
|
|
&sandybridge_display_srwm_info,
|
|
&sandybridge_cursor_srwm_info,
|
|
&fbc_wm, &plane_wm, &cursor_wm))
|
|
return;
|
|
|
|
I915_WRITE(WM3_LP_ILK,
|
|
WM3_LP_EN |
|
|
(SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) |
|
|
(fbc_wm << WM1_LP_FBC_SHIFT) |
|
|
(plane_wm << WM1_LP_SR_SHIFT) |
|
|
cursor_wm);
|
|
}
|
|
|
|
static bool
|
|
sandybridge_compute_sprite_wm(struct drm_device *dev, int plane,
|
|
uint32_t sprite_width, int pixel_size,
|
|
const struct intel_watermark_params *display,
|
|
int display_latency_ns, int *sprite_wm)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
int clock;
|
|
int entries, tlb_miss;
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
if (crtc->fb == NULL || !crtc->enabled) {
|
|
*sprite_wm = display->guard_size;
|
|
return false;
|
|
}
|
|
|
|
clock = crtc->mode.clock;
|
|
|
|
/* Use the small buffer method to calculate the sprite watermark */
|
|
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
|
|
tlb_miss = display->fifo_size*display->cacheline_size -
|
|
sprite_width * 8;
|
|
if (tlb_miss > 0)
|
|
entries += tlb_miss;
|
|
entries = DIV_ROUND_UP(entries, display->cacheline_size);
|
|
*sprite_wm = entries + display->guard_size;
|
|
if (*sprite_wm > (int)display->max_wm)
|
|
*sprite_wm = display->max_wm;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane,
|
|
uint32_t sprite_width, int pixel_size,
|
|
const struct intel_watermark_params *display,
|
|
int latency_ns, int *sprite_wm)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
unsigned long line_time_us;
|
|
int clock;
|
|
int line_count, line_size;
|
|
int small, large;
|
|
int entries;
|
|
|
|
if (!latency_ns) {
|
|
*sprite_wm = 0;
|
|
return false;
|
|
}
|
|
|
|
crtc = intel_get_crtc_for_plane(dev, plane);
|
|
clock = crtc->mode.clock;
|
|
if (!clock) {
|
|
*sprite_wm = 0;
|
|
return false;
|
|
}
|
|
|
|
line_time_us = (sprite_width * 1000) / clock;
|
|
if (!line_time_us) {
|
|
*sprite_wm = 0;
|
|
return false;
|
|
}
|
|
|
|
line_count = (latency_ns / line_time_us + 1000) / 1000;
|
|
line_size = sprite_width * pixel_size;
|
|
|
|
/* Use the minimum of the small and large buffer method for primary */
|
|
small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
|
|
large = line_count * line_size;
|
|
|
|
entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
|
|
*sprite_wm = entries + display->guard_size;
|
|
|
|
return *sprite_wm > 0x3ff ? false : true;
|
|
}
|
|
|
|
static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
|
|
uint32_t sprite_width, int pixel_size)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */
|
|
u32 val;
|
|
int sprite_wm, reg;
|
|
int ret;
|
|
|
|
switch (pipe) {
|
|
case 0:
|
|
reg = WM0_PIPEA_ILK;
|
|
break;
|
|
case 1:
|
|
reg = WM0_PIPEB_ILK;
|
|
break;
|
|
case 2:
|
|
reg = WM0_PIPEC_IVB;
|
|
break;
|
|
default:
|
|
return; /* bad pipe */
|
|
}
|
|
|
|
ret = sandybridge_compute_sprite_wm(dev, pipe, sprite_width, pixel_size,
|
|
&sandybridge_display_wm_info,
|
|
latency, &sprite_wm);
|
|
if (!ret) {
|
|
DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n",
|
|
pipe);
|
|
return;
|
|
}
|
|
|
|
val = I915_READ(reg);
|
|
val &= ~WM0_PIPE_SPRITE_MASK;
|
|
I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT));
|
|
DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm);
|
|
|
|
|
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
|
|
pixel_size,
|
|
&sandybridge_display_srwm_info,
|
|
SNB_READ_WM1_LATENCY() * 500,
|
|
&sprite_wm);
|
|
if (!ret) {
|
|
DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n",
|
|
pipe);
|
|
return;
|
|
}
|
|
I915_WRITE(WM1S_LP_ILK, sprite_wm);
|
|
|
|
/* Only IVB has two more LP watermarks for sprite */
|
|
if (!IS_IVYBRIDGE(dev))
|
|
return;
|
|
|
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
|
|
pixel_size,
|
|
&sandybridge_display_srwm_info,
|
|
SNB_READ_WM2_LATENCY() * 500,
|
|
&sprite_wm);
|
|
if (!ret) {
|
|
DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n",
|
|
pipe);
|
|
return;
|
|
}
|
|
I915_WRITE(WM2S_LP_IVB, sprite_wm);
|
|
|
|
ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
|
|
pixel_size,
|
|
&sandybridge_display_srwm_info,
|
|
SNB_READ_WM3_LATENCY() * 500,
|
|
&sprite_wm);
|
|
if (!ret) {
|
|
DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n",
|
|
pipe);
|
|
return;
|
|
}
|
|
I915_WRITE(WM3S_LP_IVB, sprite_wm);
|
|
}
|
|
|
|
/**
|
|
* intel_update_watermarks - update FIFO watermark values based on current modes
|
|
*
|
|
* Calculate watermark values for the various WM regs based on current mode
|
|
* and plane configuration.
|
|
*
|
|
* There are several cases to deal with here:
|
|
* - normal (i.e. non-self-refresh)
|
|
* - self-refresh (SR) mode
|
|
* - lines are large relative to FIFO size (buffer can hold up to 2)
|
|
* - lines are small relative to FIFO size (buffer can hold more than 2
|
|
* lines), so need to account for TLB latency
|
|
*
|
|
* The normal calculation is:
|
|
* watermark = dotclock * bytes per pixel * latency
|
|
* where latency is platform & configuration dependent (we assume pessimal
|
|
* values here).
|
|
*
|
|
* The SR calculation is:
|
|
* watermark = (trunc(latency/line time)+1) * surface width *
|
|
* bytes per pixel
|
|
* where
|
|
* line time = htotal / dotclock
|
|
* surface width = hdisplay for normal plane and 64 for cursor
|
|
* and latency is assumed to be high, as above.
|
|
*
|
|
* The final value programmed to the register should always be rounded up,
|
|
* and include an extra 2 entries to account for clock crossings.
|
|
*
|
|
* We don't use the sprite, so we can ignore that. And on Crestline we have
|
|
* to set the non-SR watermarks to 8.
|
|
*/
|
|
void intel_update_watermarks(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (dev_priv->display.update_wm)
|
|
dev_priv->display.update_wm(dev);
|
|
}
|
|
|
|
void intel_update_sprite_watermarks(struct drm_device *dev, int pipe,
|
|
uint32_t sprite_width, int pixel_size)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (dev_priv->display.update_sprite_wm)
|
|
dev_priv->display.update_sprite_wm(dev, pipe, sprite_width,
|
|
pixel_size);
|
|
}
|
|
|
|
static 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_object_pin(ctx, 4096, true);
|
|
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_unpin(ctx);
|
|
err_unref:
|
|
drm_gem_object_unreference(&ctx->base);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
bool ironlake_set_drps(struct drm_device *dev, u8 val)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u16 rgvswctl;
|
|
|
|
rgvswctl = I915_READ16(MEMSWCTL);
|
|
if (rgvswctl & MEMCTL_CMD_STS) {
|
|
DRM_DEBUG("gpu busy, RCS change rejected\n");
|
|
return false; /* still busy with another command */
|
|
}
|
|
|
|
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
|
|
(val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
|
|
I915_WRITE16(MEMSWCTL, rgvswctl);
|
|
POSTING_READ16(MEMSWCTL);
|
|
|
|
rgvswctl |= MEMCTL_CMD_STS;
|
|
I915_WRITE16(MEMSWCTL, rgvswctl);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ironlake_enable_drps(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 rgvmodectl = I915_READ(MEMMODECTL);
|
|
u8 fmax, fmin, fstart, vstart;
|
|
|
|
/* Enable temp reporting */
|
|
I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
|
|
I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
|
|
|
|
/* 100ms RC evaluation intervals */
|
|
I915_WRITE(RCUPEI, 100000);
|
|
I915_WRITE(RCDNEI, 100000);
|
|
|
|
/* Set max/min thresholds to 90ms and 80ms respectively */
|
|
I915_WRITE(RCBMAXAVG, 90000);
|
|
I915_WRITE(RCBMINAVG, 80000);
|
|
|
|
I915_WRITE(MEMIHYST, 1);
|
|
|
|
/* Set up min, max, and cur for interrupt handling */
|
|
fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
|
|
fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
|
|
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
|
|
MEMMODE_FSTART_SHIFT;
|
|
|
|
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
|
|
PXVFREQ_PX_SHIFT;
|
|
|
|
dev_priv->fmax = fmax; /* IPS callback will increase this */
|
|
dev_priv->fstart = fstart;
|
|
|
|
dev_priv->max_delay = fstart;
|
|
dev_priv->min_delay = fmin;
|
|
dev_priv->cur_delay = fstart;
|
|
|
|
DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
|
|
fmax, fmin, fstart);
|
|
|
|
I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
|
|
|
|
/*
|
|
* Interrupts will be enabled in ironlake_irq_postinstall
|
|
*/
|
|
|
|
I915_WRITE(VIDSTART, vstart);
|
|
POSTING_READ(VIDSTART);
|
|
|
|
rgvmodectl |= MEMMODE_SWMODE_EN;
|
|
I915_WRITE(MEMMODECTL, rgvmodectl);
|
|
|
|
if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
|
|
DRM_ERROR("stuck trying to change perf mode\n");
|
|
msleep(1);
|
|
|
|
ironlake_set_drps(dev, fstart);
|
|
|
|
dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
|
|
I915_READ(0x112e0);
|
|
dev_priv->last_time1 = jiffies_to_msecs(jiffies);
|
|
dev_priv->last_count2 = I915_READ(0x112f4);
|
|
getrawmonotonic(&dev_priv->last_time2);
|
|
}
|
|
|
|
void ironlake_disable_drps(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u16 rgvswctl = I915_READ16(MEMSWCTL);
|
|
|
|
/* Ack interrupts, disable EFC interrupt */
|
|
I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
|
|
I915_WRITE(MEMINTRSTS, MEMINT_EVAL_CHG);
|
|
I915_WRITE(DEIER, I915_READ(DEIER) & ~DE_PCU_EVENT);
|
|
I915_WRITE(DEIIR, DE_PCU_EVENT);
|
|
I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
|
|
|
|
/* Go back to the starting frequency */
|
|
ironlake_set_drps(dev, dev_priv->fstart);
|
|
msleep(1);
|
|
rgvswctl |= MEMCTL_CMD_STS;
|
|
I915_WRITE(MEMSWCTL, rgvswctl);
|
|
msleep(1);
|
|
|
|
}
|
|
|
|
void gen6_set_rps(struct drm_device *dev, u8 val)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 swreq;
|
|
|
|
swreq = (val & 0x3ff) << 25;
|
|
I915_WRITE(GEN6_RPNSWREQ, swreq);
|
|
}
|
|
|
|
void gen6_disable_rps(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
|
|
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
|
|
I915_WRITE(GEN6_PMIER, 0);
|
|
/* Complete PM interrupt masking here doesn't race with the rps work
|
|
* item again unmasking PM interrupts because that is using a different
|
|
* register (PMIMR) to mask PM interrupts. The only risk is in leaving
|
|
* stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
|
|
|
|
spin_lock_irq(&dev_priv->rps_lock);
|
|
dev_priv->pm_iir = 0;
|
|
spin_unlock_irq(&dev_priv->rps_lock);
|
|
|
|
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
|
|
}
|
|
|
|
int intel_enable_rc6(const struct drm_device *dev)
|
|
{
|
|
/*
|
|
* Respect the kernel parameter if it is set
|
|
*/
|
|
if (i915_enable_rc6 >= 0)
|
|
return i915_enable_rc6;
|
|
|
|
/*
|
|
* Disable RC6 on Ironlake
|
|
*/
|
|
if (INTEL_INFO(dev)->gen == 5)
|
|
return 0;
|
|
|
|
/* Sorry Haswell, no RC6 for you for now. */
|
|
if (IS_HASWELL(dev))
|
|
return 0;
|
|
|
|
/*
|
|
* Disable rc6 on Sandybridge
|
|
*/
|
|
if (INTEL_INFO(dev)->gen == 6) {
|
|
DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
|
|
return INTEL_RC6_ENABLE;
|
|
}
|
|
DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
|
|
return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
|
|
}
|
|
|
|
void gen6_enable_rps(struct drm_i915_private *dev_priv)
|
|
{
|
|
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
|
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
|
|
u32 pcu_mbox, rc6_mask = 0;
|
|
u32 gtfifodbg;
|
|
int cur_freq, min_freq, max_freq;
|
|
int rc6_mode;
|
|
int i;
|
|
|
|
/* Here begins a magic sequence of register writes to enable
|
|
* auto-downclocking.
|
|
*
|
|
* Perhaps there might be some value in exposing these to
|
|
* userspace...
|
|
*/
|
|
I915_WRITE(GEN6_RC_STATE, 0);
|
|
mutex_lock(&dev_priv->dev->struct_mutex);
|
|
|
|
/* Clear the DBG now so we don't confuse earlier errors */
|
|
if ((gtfifodbg = I915_READ(GTFIFODBG))) {
|
|
DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
|
|
I915_WRITE(GTFIFODBG, gtfifodbg);
|
|
}
|
|
|
|
gen6_gt_force_wake_get(dev_priv);
|
|
|
|
/* disable the counters and set deterministic thresholds */
|
|
I915_WRITE(GEN6_RC_CONTROL, 0);
|
|
|
|
I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
|
|
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30);
|
|
I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30);
|
|
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
|
|
I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
|
|
|
|
for (i = 0; i < I915_NUM_RINGS; i++)
|
|
I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10);
|
|
|
|
I915_WRITE(GEN6_RC_SLEEP, 0);
|
|
I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
|
|
I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
|
|
I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
|
|
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
|
|
|
|
rc6_mode = intel_enable_rc6(dev_priv->dev);
|
|
if (rc6_mode & INTEL_RC6_ENABLE)
|
|
rc6_mask |= GEN6_RC_CTL_RC6_ENABLE;
|
|
|
|
if (rc6_mode & INTEL_RC6p_ENABLE)
|
|
rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE;
|
|
|
|
if (rc6_mode & INTEL_RC6pp_ENABLE)
|
|
rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE;
|
|
|
|
DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
|
|
(rc6_mode & INTEL_RC6_ENABLE) ? "on" : "off",
|
|
(rc6_mode & INTEL_RC6p_ENABLE) ? "on" : "off",
|
|
(rc6_mode & INTEL_RC6pp_ENABLE) ? "on" : "off");
|
|
|
|
I915_WRITE(GEN6_RC_CONTROL,
|
|
rc6_mask |
|
|
GEN6_RC_CTL_EI_MODE(1) |
|
|
GEN6_RC_CTL_HW_ENABLE);
|
|
|
|
I915_WRITE(GEN6_RPNSWREQ,
|
|
GEN6_FREQUENCY(10) |
|
|
GEN6_OFFSET(0) |
|
|
GEN6_AGGRESSIVE_TURBO);
|
|
I915_WRITE(GEN6_RC_VIDEO_FREQ,
|
|
GEN6_FREQUENCY(12));
|
|
|
|
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
|
|
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
|
|
18 << 24 |
|
|
6 << 16);
|
|
I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
|
|
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
|
|
I915_WRITE(GEN6_RP_UP_EI, 100000);
|
|
I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
|
|
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
|
|
I915_WRITE(GEN6_RP_CONTROL,
|
|
GEN6_RP_MEDIA_TURBO |
|
|
GEN6_RP_MEDIA_HW_MODE |
|
|
GEN6_RP_MEDIA_IS_GFX |
|
|
GEN6_RP_ENABLE |
|
|
GEN6_RP_UP_BUSY_AVG |
|
|
GEN6_RP_DOWN_IDLE_CONT);
|
|
|
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
|
|
500))
|
|
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
|
|
|
|
I915_WRITE(GEN6_PCODE_DATA, 0);
|
|
I915_WRITE(GEN6_PCODE_MAILBOX,
|
|
GEN6_PCODE_READY |
|
|
GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
|
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
|
|
500))
|
|
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
|
|
|
|
min_freq = (rp_state_cap & 0xff0000) >> 16;
|
|
max_freq = rp_state_cap & 0xff;
|
|
cur_freq = (gt_perf_status & 0xff00) >> 8;
|
|
|
|
/* Check for overclock support */
|
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
|
|
500))
|
|
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
|
|
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS);
|
|
pcu_mbox = I915_READ(GEN6_PCODE_DATA);
|
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
|
|
500))
|
|
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
|
|
if (pcu_mbox & (1<<31)) { /* OC supported */
|
|
max_freq = pcu_mbox & 0xff;
|
|
DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50);
|
|
}
|
|
|
|
/* In units of 100MHz */
|
|
dev_priv->max_delay = max_freq;
|
|
dev_priv->min_delay = min_freq;
|
|
dev_priv->cur_delay = cur_freq;
|
|
|
|
/* requires MSI enabled */
|
|
I915_WRITE(GEN6_PMIER,
|
|
GEN6_PM_MBOX_EVENT |
|
|
GEN6_PM_THERMAL_EVENT |
|
|
GEN6_PM_RP_DOWN_TIMEOUT |
|
|
GEN6_PM_RP_UP_THRESHOLD |
|
|
GEN6_PM_RP_DOWN_THRESHOLD |
|
|
GEN6_PM_RP_UP_EI_EXPIRED |
|
|
GEN6_PM_RP_DOWN_EI_EXPIRED);
|
|
spin_lock_irq(&dev_priv->rps_lock);
|
|
WARN_ON(dev_priv->pm_iir != 0);
|
|
I915_WRITE(GEN6_PMIMR, 0);
|
|
spin_unlock_irq(&dev_priv->rps_lock);
|
|
/* enable all PM interrupts */
|
|
I915_WRITE(GEN6_PMINTRMSK, 0);
|
|
|
|
gen6_gt_force_wake_put(dev_priv);
|
|
mutex_unlock(&dev_priv->dev->struct_mutex);
|
|
}
|
|
|
|
void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
|
|
{
|
|
int min_freq = 15;
|
|
int gpu_freq, ia_freq, max_ia_freq;
|
|
int scaling_factor = 180;
|
|
|
|
max_ia_freq = cpufreq_quick_get_max(0);
|
|
/*
|
|
* Default to measured freq if none found, PCU will ensure we don't go
|
|
* over
|
|
*/
|
|
if (!max_ia_freq)
|
|
max_ia_freq = tsc_khz;
|
|
|
|
/* Convert from kHz to MHz */
|
|
max_ia_freq /= 1000;
|
|
|
|
mutex_lock(&dev_priv->dev->struct_mutex);
|
|
|
|
/*
|
|
* For each potential GPU frequency, load a ring frequency we'd like
|
|
* to use for memory access. We do this by specifying the IA frequency
|
|
* the PCU should use as a reference to determine the ring frequency.
|
|
*/
|
|
for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
|
|
gpu_freq--) {
|
|
int diff = dev_priv->max_delay - gpu_freq;
|
|
|
|
/*
|
|
* For GPU frequencies less than 750MHz, just use the lowest
|
|
* ring freq.
|
|
*/
|
|
if (gpu_freq < min_freq)
|
|
ia_freq = 800;
|
|
else
|
|
ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
|
|
ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
|
|
|
|
I915_WRITE(GEN6_PCODE_DATA,
|
|
(ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
|
|
gpu_freq);
|
|
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
|
|
GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
|
|
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
|
|
GEN6_PCODE_READY) == 0, 10)) {
|
|
DRM_ERROR("pcode write of freq table timed out\n");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&dev_priv->dev->struct_mutex);
|
|
}
|
|
|
|
static void ironlake_teardown_rc6(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (dev_priv->renderctx) {
|
|
i915_gem_object_unpin(dev_priv->renderctx);
|
|
drm_gem_object_unreference(&dev_priv->renderctx->base);
|
|
dev_priv->renderctx = NULL;
|
|
}
|
|
|
|
if (dev_priv->pwrctx) {
|
|
i915_gem_object_unpin(dev_priv->pwrctx);
|
|
drm_gem_object_unreference(&dev_priv->pwrctx->base);
|
|
dev_priv->pwrctx = NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
ironlake_teardown_rc6(dev);
|
|
}
|
|
|
|
static int ironlake_setup_rc6(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
if (dev_priv->renderctx == NULL)
|
|
dev_priv->renderctx = intel_alloc_context_page(dev);
|
|
if (!dev_priv->renderctx)
|
|
return -ENOMEM;
|
|
|
|
if (dev_priv->pwrctx == NULL)
|
|
dev_priv->pwrctx = intel_alloc_context_page(dev);
|
|
if (!dev_priv->pwrctx) {
|
|
ironlake_teardown_rc6(dev);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ironlake_enable_rc6(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int ret;
|
|
|
|
/* rc6 disabled by default due to repeated reports of hanging during
|
|
* boot and resume.
|
|
*/
|
|
if (!intel_enable_rc6(dev))
|
|
return;
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
ret = ironlake_setup_rc6(dev);
|
|
if (ret) {
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* GPU can automatically power down the render unit if given a page
|
|
* to save state.
|
|
*/
|
|
ret = BEGIN_LP_RING(6);
|
|
if (ret) {
|
|
ironlake_teardown_rc6(dev);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return;
|
|
}
|
|
|
|
OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN);
|
|
OUT_RING(MI_SET_CONTEXT);
|
|
OUT_RING(dev_priv->renderctx->gtt_offset |
|
|
MI_MM_SPACE_GTT |
|
|
MI_SAVE_EXT_STATE_EN |
|
|
MI_RESTORE_EXT_STATE_EN |
|
|
MI_RESTORE_INHIBIT);
|
|
OUT_RING(MI_SUSPEND_FLUSH);
|
|
OUT_RING(MI_NOOP);
|
|
OUT_RING(MI_FLUSH);
|
|
ADVANCE_LP_RING();
|
|
|
|
/*
|
|
* Wait for the command parser to advance past MI_SET_CONTEXT. The HW
|
|
* does an implicit flush, combined with MI_FLUSH above, it should be
|
|
* safe to assume that renderctx is valid
|
|
*/
|
|
ret = intel_wait_ring_idle(LP_RING(dev_priv));
|
|
if (ret) {
|
|
DRM_ERROR("failed to enable ironlake power power savings\n");
|
|
ironlake_teardown_rc6(dev);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
return;
|
|
}
|
|
|
|
I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
|
|
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
}
|
|
|
|
static unsigned long intel_pxfreq(u32 vidfreq)
|
|
{
|
|
unsigned long freq;
|
|
int div = (vidfreq & 0x3f0000) >> 16;
|
|
int post = (vidfreq & 0x3000) >> 12;
|
|
int pre = (vidfreq & 0x7);
|
|
|
|
if (!pre)
|
|
return 0;
|
|
|
|
freq = ((div * 133333) / ((1<<post) * pre));
|
|
|
|
return freq;
|
|
}
|
|
|
|
void intel_init_emon(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 lcfuse;
|
|
u8 pxw[16];
|
|
int i;
|
|
|
|
/* Disable to program */
|
|
I915_WRITE(ECR, 0);
|
|
POSTING_READ(ECR);
|
|
|
|
/* Program energy weights for various events */
|
|
I915_WRITE(SDEW, 0x15040d00);
|
|
I915_WRITE(CSIEW0, 0x007f0000);
|
|
I915_WRITE(CSIEW1, 0x1e220004);
|
|
I915_WRITE(CSIEW2, 0x04000004);
|
|
|
|
for (i = 0; i < 5; i++)
|
|
I915_WRITE(PEW + (i * 4), 0);
|
|
for (i = 0; i < 3; i++)
|
|
I915_WRITE(DEW + (i * 4), 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));
|
|
unsigned long freq = intel_pxfreq(pxvidfreq);
|
|
unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >>
|
|
PXVFREQ_PX_SHIFT;
|
|
unsigned long val;
|
|
|
|
val = vid * vid;
|
|
val *= (freq / 1000);
|
|
val *= 255;
|
|
val /= (127*127*900);
|
|
if (val > 0xff)
|
|
DRM_ERROR("bad pxval: %ld\n", val);
|
|
pxw[i] = val;
|
|
}
|
|
/* Render standby states get 0 weight */
|
|
pxw[14] = 0;
|
|
pxw[15] = 0;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) |
|
|
(pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]);
|
|
I915_WRITE(PXW + (i * 4), val);
|
|
}
|
|
|
|
/* Adjust magic regs to magic values (more experimental results) */
|
|
I915_WRITE(OGW0, 0);
|
|
I915_WRITE(OGW1, 0);
|
|
I915_WRITE(EG0, 0x00007f00);
|
|
I915_WRITE(EG1, 0x0000000e);
|
|
I915_WRITE(EG2, 0x000e0000);
|
|
I915_WRITE(EG3, 0x68000300);
|
|
I915_WRITE(EG4, 0x42000000);
|
|
I915_WRITE(EG5, 0x00140031);
|
|
I915_WRITE(EG6, 0);
|
|
I915_WRITE(EG7, 0);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
I915_WRITE(PXWL + (i * 4), 0);
|
|
|
|
/* Enable PMON + select events */
|
|
I915_WRITE(ECR, 0x80000019);
|
|
|
|
lcfuse = I915_READ(LCFUSE02);
|
|
|
|
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
|
|
}
|
|
|
|
static void ironlake_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
|
|
|
|
/* Required for FBC */
|
|
dspclk_gate |= DPFCUNIT_CLOCK_GATE_DISABLE |
|
|
DPFCRUNIT_CLOCK_GATE_DISABLE |
|
|
DPFDUNIT_CLOCK_GATE_DISABLE;
|
|
/* Required for CxSR */
|
|
dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE;
|
|
|
|
I915_WRITE(PCH_3DCGDIS0,
|
|
MARIUNIT_CLOCK_GATE_DISABLE |
|
|
SVSMUNIT_CLOCK_GATE_DISABLE);
|
|
I915_WRITE(PCH_3DCGDIS1,
|
|
VFMUNIT_CLOCK_GATE_DISABLE);
|
|
|
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
|
|
|
|
/*
|
|
* According to the spec the following bits should be set in
|
|
* order to enable memory self-refresh
|
|
* The bit 22/21 of 0x42004
|
|
* The bit 5 of 0x42020
|
|
* The bit 15 of 0x45000
|
|
*/
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN2,
|
|
(I915_READ(ILK_DISPLAY_CHICKEN2) |
|
|
ILK_DPARB_GATE | ILK_VSDPFD_FULL));
|
|
I915_WRITE(ILK_DSPCLK_GATE,
|
|
(I915_READ(ILK_DSPCLK_GATE) |
|
|
ILK_DPARB_CLK_GATE));
|
|
I915_WRITE(DISP_ARB_CTL,
|
|
(I915_READ(DISP_ARB_CTL) |
|
|
DISP_FBC_WM_DIS));
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
/*
|
|
* Based on the document from hardware guys the following bits
|
|
* should be set unconditionally in order to enable FBC.
|
|
* The bit 22 of 0x42000
|
|
* The bit 22 of 0x42004
|
|
* The bit 7,8,9 of 0x42020.
|
|
*/
|
|
if (IS_IRONLAKE_M(dev)) {
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN1,
|
|
I915_READ(ILK_DISPLAY_CHICKEN1) |
|
|
ILK_FBCQ_DIS);
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN2,
|
|
I915_READ(ILK_DISPLAY_CHICKEN2) |
|
|
ILK_DPARB_GATE);
|
|
I915_WRITE(ILK_DSPCLK_GATE,
|
|
I915_READ(ILK_DSPCLK_GATE) |
|
|
ILK_DPFC_DIS1 |
|
|
ILK_DPFC_DIS2 |
|
|
ILK_CLK_FBC);
|
|
}
|
|
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN2,
|
|
I915_READ(ILK_DISPLAY_CHICKEN2) |
|
|
ILK_ELPIN_409_SELECT);
|
|
I915_WRITE(_3D_CHICKEN2,
|
|
_3D_CHICKEN2_WM_READ_PIPELINED << 16 |
|
|
_3D_CHICKEN2_WM_READ_PIPELINED);
|
|
}
|
|
|
|
static void gen6_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int pipe;
|
|
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
|
|
|
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
|
|
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN2,
|
|
I915_READ(ILK_DISPLAY_CHICKEN2) |
|
|
ILK_ELPIN_409_SELECT);
|
|
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
/* clear masked bit */
|
|
I915_WRITE(CACHE_MODE_0,
|
|
CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT);
|
|
|
|
I915_WRITE(GEN6_UCGCTL1,
|
|
I915_READ(GEN6_UCGCTL1) |
|
|
GEN6_BLBUNIT_CLOCK_GATE_DISABLE |
|
|
GEN6_CSUNIT_CLOCK_GATE_DISABLE);
|
|
|
|
/* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock
|
|
* gating disable must be set. Failure to set it results in
|
|
* flickering pixels due to Z write ordering failures after
|
|
* some amount of runtime in the Mesa "fire" demo, and Unigine
|
|
* Sanctuary and Tropics, and apparently anything else with
|
|
* alpha test or pixel discard.
|
|
*
|
|
* According to the spec, bit 11 (RCCUNIT) must also be set,
|
|
* but we didn't debug actual testcases to find it out.
|
|
*/
|
|
I915_WRITE(GEN6_UCGCTL2,
|
|
GEN6_RCPBUNIT_CLOCK_GATE_DISABLE |
|
|
GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
|
|
|
|
/* Bspec says we need to always set all mask bits. */
|
|
I915_WRITE(_3D_CHICKEN, (0xFFFF << 16) |
|
|
_3D_CHICKEN_SF_DISABLE_FASTCLIP_CULL);
|
|
|
|
/*
|
|
* According to the spec the following bits should be
|
|
* set in order to enable memory self-refresh and fbc:
|
|
* The bit21 and bit22 of 0x42000
|
|
* The bit21 and bit22 of 0x42004
|
|
* The bit5 and bit7 of 0x42020
|
|
* The bit14 of 0x70180
|
|
* The bit14 of 0x71180
|
|
*/
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN1,
|
|
I915_READ(ILK_DISPLAY_CHICKEN1) |
|
|
ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS);
|
|
I915_WRITE(ILK_DISPLAY_CHICKEN2,
|
|
I915_READ(ILK_DISPLAY_CHICKEN2) |
|
|
ILK_DPARB_GATE | ILK_VSDPFD_FULL);
|
|
I915_WRITE(ILK_DSPCLK_GATE,
|
|
I915_READ(ILK_DSPCLK_GATE) |
|
|
ILK_DPARB_CLK_GATE |
|
|
ILK_DPFD_CLK_GATE);
|
|
|
|
for_each_pipe(pipe) {
|
|
I915_WRITE(DSPCNTR(pipe),
|
|
I915_READ(DSPCNTR(pipe)) |
|
|
DISPPLANE_TRICKLE_FEED_DISABLE);
|
|
intel_flush_display_plane(dev_priv, pipe);
|
|
}
|
|
}
|
|
|
|
static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv)
|
|
{
|
|
uint32_t reg = I915_READ(GEN7_FF_THREAD_MODE);
|
|
|
|
reg &= ~GEN7_FF_SCHED_MASK;
|
|
reg |= GEN7_FF_TS_SCHED_HW;
|
|
reg |= GEN7_FF_VS_SCHED_HW;
|
|
reg |= GEN7_FF_DS_SCHED_HW;
|
|
|
|
I915_WRITE(GEN7_FF_THREAD_MODE, reg);
|
|
}
|
|
|
|
static void ivybridge_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int pipe;
|
|
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
|
|
|
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
|
|
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
/* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
|
|
* This implements the WaDisableRCZUnitClockGating workaround.
|
|
*/
|
|
I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
|
|
|
|
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
|
|
|
|
I915_WRITE(IVB_CHICKEN3,
|
|
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
|
|
CHICKEN3_DGMG_DONE_FIX_DISABLE);
|
|
|
|
/* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
|
|
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
|
|
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
|
|
|
|
/* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
|
|
I915_WRITE(GEN7_L3CNTLREG1,
|
|
GEN7_WA_FOR_GEN7_L3_CONTROL);
|
|
I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
|
|
GEN7_WA_L3_CHICKEN_MODE);
|
|
|
|
/* This is required by WaCatErrorRejectionIssue */
|
|
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
|
|
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
|
|
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
|
|
|
|
for_each_pipe(pipe) {
|
|
I915_WRITE(DSPCNTR(pipe),
|
|
I915_READ(DSPCNTR(pipe)) |
|
|
DISPPLANE_TRICKLE_FEED_DISABLE);
|
|
intel_flush_display_plane(dev_priv, pipe);
|
|
}
|
|
|
|
gen7_setup_fixed_func_scheduler(dev_priv);
|
|
}
|
|
|
|
static void valleyview_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int pipe;
|
|
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
|
|
|
|
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
|
|
|
|
I915_WRITE(WM3_LP_ILK, 0);
|
|
I915_WRITE(WM2_LP_ILK, 0);
|
|
I915_WRITE(WM1_LP_ILK, 0);
|
|
|
|
/* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
|
|
* This implements the WaDisableRCZUnitClockGating workaround.
|
|
*/
|
|
I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
|
|
|
|
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
|
|
|
|
I915_WRITE(IVB_CHICKEN3,
|
|
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
|
|
CHICKEN3_DGMG_DONE_FIX_DISABLE);
|
|
|
|
/* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
|
|
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
|
|
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
|
|
|
|
/* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
|
|
I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL);
|
|
I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE);
|
|
|
|
/* This is required by WaCatErrorRejectionIssue */
|
|
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
|
|
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
|
|
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
|
|
|
|
for_each_pipe(pipe) {
|
|
I915_WRITE(DSPCNTR(pipe),
|
|
I915_READ(DSPCNTR(pipe)) |
|
|
DISPPLANE_TRICKLE_FEED_DISABLE);
|
|
intel_flush_display_plane(dev_priv, pipe);
|
|
}
|
|
|
|
I915_WRITE(CACHE_MODE_1,
|
|
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
|
|
}
|
|
|
|
static void g4x_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
uint32_t dspclk_gate;
|
|
|
|
I915_WRITE(RENCLK_GATE_D1, 0);
|
|
I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE |
|
|
GS_UNIT_CLOCK_GATE_DISABLE |
|
|
CL_UNIT_CLOCK_GATE_DISABLE);
|
|
I915_WRITE(RAMCLK_GATE_D, 0);
|
|
dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE |
|
|
OVRUNIT_CLOCK_GATE_DISABLE |
|
|
OVCUNIT_CLOCK_GATE_DISABLE;
|
|
if (IS_GM45(dev))
|
|
dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE;
|
|
I915_WRITE(DSPCLK_GATE_D, dspclk_gate);
|
|
}
|
|
|
|
static void crestline_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE);
|
|
I915_WRITE(RENCLK_GATE_D2, 0);
|
|
I915_WRITE(DSPCLK_GATE_D, 0);
|
|
I915_WRITE(RAMCLK_GATE_D, 0);
|
|
I915_WRITE16(DEUC, 0);
|
|
}
|
|
|
|
static void broadwater_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE |
|
|
I965_RCC_CLOCK_GATE_DISABLE |
|
|
I965_RCPB_CLOCK_GATE_DISABLE |
|
|
I965_ISC_CLOCK_GATE_DISABLE |
|
|
I965_FBC_CLOCK_GATE_DISABLE);
|
|
I915_WRITE(RENCLK_GATE_D2, 0);
|
|
}
|
|
|
|
static void gen3_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
u32 dstate = I915_READ(D_STATE);
|
|
|
|
dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
|
|
DSTATE_DOT_CLOCK_GATING;
|
|
I915_WRITE(D_STATE, dstate);
|
|
}
|
|
|
|
static void i85x_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
|
|
}
|
|
|
|
static void i830_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
|
|
}
|
|
|
|
static void ibx_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
/*
|
|
* On Ibex Peak and Cougar Point, we need to disable clock
|
|
* gating for the panel power sequencer or it will fail to
|
|
* start up when no ports are active.
|
|
*/
|
|
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
|
|
}
|
|
|
|
static void cpt_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
int pipe;
|
|
|
|
/*
|
|
* On Ibex Peak and Cougar Point, we need to disable clock
|
|
* gating for the panel power sequencer or it will fail to
|
|
* start up when no ports are active.
|
|
*/
|
|
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
|
|
I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
|
|
DPLS_EDP_PPS_FIX_DIS);
|
|
/* Without this, mode sets may fail silently on FDI */
|
|
for_each_pipe(pipe)
|
|
I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
|
|
}
|
|
|
|
void intel_init_clock_gating(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
dev_priv->display.init_clock_gating(dev);
|
|
|
|
if (dev_priv->display.init_pch_clock_gating)
|
|
dev_priv->display.init_pch_clock_gating(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;
|
|
|
|
if (I915_HAS_FBC(dev)) {
|
|
if (HAS_PCH_SPLIT(dev)) {
|
|
dev_priv->display.fbc_enabled = ironlake_fbc_enabled;
|
|
dev_priv->display.enable_fbc = ironlake_enable_fbc;
|
|
dev_priv->display.disable_fbc = ironlake_disable_fbc;
|
|
} else if (IS_GM45(dev)) {
|
|
dev_priv->display.fbc_enabled = g4x_fbc_enabled;
|
|
dev_priv->display.enable_fbc = g4x_enable_fbc;
|
|
dev_priv->display.disable_fbc = g4x_disable_fbc;
|
|
} else if (IS_CRESTLINE(dev)) {
|
|
dev_priv->display.fbc_enabled = i8xx_fbc_enabled;
|
|
dev_priv->display.enable_fbc = i8xx_enable_fbc;
|
|
dev_priv->display.disable_fbc = i8xx_disable_fbc;
|
|
}
|
|
/* 855GM needs testing */
|
|
}
|
|
|
|
/* For FIFO watermark updates */
|
|
if (HAS_PCH_SPLIT(dev)) {
|
|
dev_priv->display.force_wake_get = __gen6_gt_force_wake_get;
|
|
dev_priv->display.force_wake_put = __gen6_gt_force_wake_put;
|
|
|
|
/* IVB configs may use multi-threaded forcewake */
|
|
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
|
|
u32 ecobus;
|
|
|
|
/* A small trick here - if the bios hasn't configured MT forcewake,
|
|
* and if the device is in RC6, then force_wake_mt_get will not wake
|
|
* the device and the ECOBUS read will return zero. Which will be
|
|
* (correctly) interpreted by the test below as MT forcewake being
|
|
* disabled.
|
|
*/
|
|
mutex_lock(&dev->struct_mutex);
|
|
__gen6_gt_force_wake_mt_get(dev_priv);
|
|
ecobus = I915_READ_NOTRACE(ECOBUS);
|
|
__gen6_gt_force_wake_mt_put(dev_priv);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
if (ecobus & FORCEWAKE_MT_ENABLE) {
|
|
DRM_DEBUG_KMS("Using MT version of forcewake\n");
|
|
dev_priv->display.force_wake_get =
|
|
__gen6_gt_force_wake_mt_get;
|
|
dev_priv->display.force_wake_put =
|
|
__gen6_gt_force_wake_mt_put;
|
|
}
|
|
}
|
|
|
|
if (HAS_PCH_IBX(dev))
|
|
dev_priv->display.init_pch_clock_gating = ibx_init_clock_gating;
|
|
else if (HAS_PCH_CPT(dev))
|
|
dev_priv->display.init_pch_clock_gating = cpt_init_clock_gating;
|
|
|
|
if (IS_GEN5(dev)) {
|
|
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK)
|
|
dev_priv->display.update_wm = ironlake_update_wm;
|
|
else {
|
|
DRM_DEBUG_KMS("Failed to get proper latency. "
|
|
"Disable CxSR\n");
|
|
dev_priv->display.update_wm = NULL;
|
|
}
|
|
dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
|
|
} else if (IS_GEN6(dev)) {
|
|
if (SNB_READ_WM0_LATENCY()) {
|
|
dev_priv->display.update_wm = sandybridge_update_wm;
|
|
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm;
|
|
} else {
|
|
DRM_DEBUG_KMS("Failed to read display plane latency. "
|
|
"Disable CxSR\n");
|
|
dev_priv->display.update_wm = NULL;
|
|
}
|
|
dev_priv->display.init_clock_gating = gen6_init_clock_gating;
|
|
} else if (IS_IVYBRIDGE(dev)) {
|
|
/* FIXME: detect B0+ stepping and use auto training */
|
|
if (SNB_READ_WM0_LATENCY()) {
|
|
dev_priv->display.update_wm = sandybridge_update_wm;
|
|
dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm;
|
|
} else {
|
|
DRM_DEBUG_KMS("Failed to read display plane latency. "
|
|
"Disable CxSR\n");
|
|
dev_priv->display.update_wm = NULL;
|
|
}
|
|
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
|
|
} else
|
|
dev_priv->display.update_wm = NULL;
|
|
} else if (IS_VALLEYVIEW(dev)) {
|
|
dev_priv->display.update_wm = valleyview_update_wm;
|
|
dev_priv->display.init_clock_gating =
|
|
valleyview_init_clock_gating;
|
|
dev_priv->display.force_wake_get = vlv_force_wake_get;
|
|
dev_priv->display.force_wake_put = vlv_force_wake_put;
|
|
} else if (IS_PINEVIEW(dev)) {
|
|
if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
|
|
dev_priv->is_ddr3,
|
|
dev_priv->fsb_freq,
|
|
dev_priv->mem_freq)) {
|
|
DRM_INFO("failed to find known CxSR latency "
|
|
"(found ddr%s fsb freq %d, mem freq %d), "
|
|
"disabling CxSR\n",
|
|
(dev_priv->is_ddr3 == 1) ? "3" : "2",
|
|
dev_priv->fsb_freq, dev_priv->mem_freq);
|
|
/* Disable CxSR and never update its watermark again */
|
|
pineview_disable_cxsr(dev);
|
|
dev_priv->display.update_wm = NULL;
|
|
} else
|
|
dev_priv->display.update_wm = pineview_update_wm;
|
|
dev_priv->display.init_clock_gating = gen3_init_clock_gating;
|
|
} else if (IS_G4X(dev)) {
|
|
dev_priv->display.update_wm = g4x_update_wm;
|
|
dev_priv->display.init_clock_gating = g4x_init_clock_gating;
|
|
} else if (IS_GEN4(dev)) {
|
|
dev_priv->display.update_wm = i965_update_wm;
|
|
if (IS_CRESTLINE(dev))
|
|
dev_priv->display.init_clock_gating = crestline_init_clock_gating;
|
|
else if (IS_BROADWATER(dev))
|
|
dev_priv->display.init_clock_gating = broadwater_init_clock_gating;
|
|
} else if (IS_GEN3(dev)) {
|
|
dev_priv->display.update_wm = i9xx_update_wm;
|
|
dev_priv->display.get_fifo_size = i9xx_get_fifo_size;
|
|
dev_priv->display.init_clock_gating = gen3_init_clock_gating;
|
|
} else if (IS_I865G(dev)) {
|
|
dev_priv->display.update_wm = i830_update_wm;
|
|
dev_priv->display.init_clock_gating = i85x_init_clock_gating;
|
|
dev_priv->display.get_fifo_size = i830_get_fifo_size;
|
|
} else if (IS_I85X(dev)) {
|
|
dev_priv->display.update_wm = i9xx_update_wm;
|
|
dev_priv->display.get_fifo_size = i85x_get_fifo_size;
|
|
dev_priv->display.init_clock_gating = i85x_init_clock_gating;
|
|
} else {
|
|
dev_priv->display.update_wm = i830_update_wm;
|
|
dev_priv->display.init_clock_gating = i830_init_clock_gating;
|
|
if (IS_845G(dev))
|
|
dev_priv->display.get_fifo_size = i845_get_fifo_size;
|
|
else
|
|
dev_priv->display.get_fifo_size = i830_get_fifo_size;
|
|
}
|
|
}
|
|
|