Files
linux/drivers/gpu/drm/i915/display/intel_fbc.c
Ville Syrjälä 02689a2055 drm/1915/fbc: Replace plane->has_fbc with a pointer to the fbc instance
With multiple fbc instances we need to find the right one for each
plane. Rather than going looking for the right instance every time
let's just replace the has_fbc boolean with a pointer that gets us
there straight away.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20211104144520.22605-18-ville.syrjala@linux.intel.com
Acked-by: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: Mika Kahola <mika.kahola@intel.com>
2021-11-11 15:18:12 +02:00

1739 lines
47 KiB
C

/*
* Copyright © 2014 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.
*/
/**
* DOC: Frame Buffer Compression (FBC)
*
* FBC tries to save memory bandwidth (and so power consumption) by
* compressing the amount of memory used by the display. It is total
* transparent to user space and completely handled in the kernel.
*
* The benefits of FBC are mostly visible with solid backgrounds and
* variation-less patterns. It comes from keeping the memory footprint small
* and having fewer memory pages opened and accessed for refreshing the display.
*
* i915 is responsible to reserve stolen memory for FBC and configure its
* offset on proper registers. The hardware takes care of all
* compress/decompress. However there are many known cases where we have to
* forcibly disable it to allow proper screen updates.
*/
#include <drm/drm_fourcc.h>
#include "i915_drv.h"
#include "i915_trace.h"
#include "i915_vgpu.h"
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_fbc.h"
#include "intel_frontbuffer.h"
struct intel_fbc_funcs {
void (*activate)(struct intel_fbc *fbc);
void (*deactivate)(struct intel_fbc *fbc);
bool (*is_active)(struct intel_fbc *fbc);
bool (*is_compressing)(struct intel_fbc *fbc);
void (*nuke)(struct intel_fbc *fbc);
void (*program_cfb)(struct intel_fbc *fbc);
void (*set_false_color)(struct intel_fbc *fbc, bool enable);
};
/*
* For SKL+, the plane source size used by the hardware is based on the value we
* write to the PLANE_SIZE register. For BDW-, the hardware looks at the value
* we wrote to PIPESRC.
*/
static void intel_fbc_get_plane_source_size(const struct intel_fbc_state_cache *cache,
int *width, int *height)
{
if (width)
*width = cache->plane.src_w;
if (height)
*height = cache->plane.src_h;
}
/* plane stride in pixels */
static unsigned int intel_fbc_plane_stride(const struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->hw.fb;
unsigned int stride;
stride = plane_state->view.color_plane[0].mapping_stride;
if (!drm_rotation_90_or_270(plane_state->hw.rotation))
stride /= fb->format->cpp[0];
return stride;
}
/* plane stride based cfb stride in bytes, assuming 1:1 compression limit */
static unsigned int _intel_fbc_cfb_stride(const struct intel_fbc_state_cache *cache)
{
unsigned int cpp = 4; /* FBC always 4 bytes per pixel */
return cache->fb.stride * cpp;
}
/* minimum acceptable cfb stride in bytes, assuming 1:1 compression limit */
static unsigned int skl_fbc_min_cfb_stride(struct intel_fbc *fbc,
const struct intel_fbc_state_cache *cache)
{
struct drm_i915_private *i915 = fbc->i915;
unsigned int limit = 4; /* 1:4 compression limit is the worst case */
unsigned int cpp = 4; /* FBC always 4 bytes per pixel */
unsigned int height = 4; /* FBC segment is 4 lines */
unsigned int stride;
/* minimum segment stride we can use */
stride = cache->plane.src_w * cpp * height / limit;
/*
* Wa_16011863758: icl+
* Avoid some hardware segment address miscalculation.
*/
if (DISPLAY_VER(i915) >= 11)
stride += 64;
/*
* At least some of the platforms require each 4 line segment to
* be 512 byte aligned. Just do it always for simplicity.
*/
stride = ALIGN(stride, 512);
/* convert back to single line equivalent with 1:1 compression limit */
return stride * limit / height;
}
/* properly aligned cfb stride in bytes, assuming 1:1 compression limit */
static unsigned int intel_fbc_cfb_stride(struct intel_fbc *fbc,
const struct intel_fbc_state_cache *cache)
{
struct drm_i915_private *i915 = fbc->i915;
unsigned int stride = _intel_fbc_cfb_stride(cache);
/*
* At least some of the platforms require each 4 line segment to
* be 512 byte aligned. Aligning each line to 512 bytes guarantees
* that regardless of the compression limit we choose later.
*/
if (DISPLAY_VER(i915) >= 9)
return max(ALIGN(stride, 512), skl_fbc_min_cfb_stride(fbc, cache));
else
return stride;
}
static unsigned int intel_fbc_cfb_size(struct intel_fbc *fbc,
const struct intel_fbc_state_cache *cache)
{
struct drm_i915_private *i915 = fbc->i915;
int lines = cache->plane.src_h;
if (DISPLAY_VER(i915) == 7)
lines = min(lines, 2048);
else if (DISPLAY_VER(i915) >= 8)
lines = min(lines, 2560);
return lines * intel_fbc_cfb_stride(fbc, cache);
}
static u32 i8xx_fbc_ctl(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
unsigned int cfb_stride;
u32 fbc_ctl;
cfb_stride = params->cfb_stride / fbc->limit;
/* FBC_CTL wants 32B or 64B units */
if (DISPLAY_VER(i915) == 2)
cfb_stride = (cfb_stride / 32) - 1;
else
cfb_stride = (cfb_stride / 64) - 1;
fbc_ctl = FBC_CTL_PERIODIC |
FBC_CTL_INTERVAL(params->interval) |
FBC_CTL_STRIDE(cfb_stride);
if (IS_I945GM(i915))
fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
if (params->fence_id >= 0)
fbc_ctl |= FBC_CTL_FENCENO(params->fence_id);
return fbc_ctl;
}
static u32 i965_fbc_ctl2(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
u32 fbc_ctl2;
fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM |
FBC_CTL_PLANE(params->crtc.i9xx_plane);
if (params->fence_id >= 0)
fbc_ctl2 |= FBC_CTL_CPU_FENCE_EN;
return fbc_ctl2;
}
static void i8xx_fbc_deactivate(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
u32 fbc_ctl;
/* Disable compression */
fbc_ctl = intel_de_read(i915, FBC_CONTROL);
if ((fbc_ctl & FBC_CTL_EN) == 0)
return;
fbc_ctl &= ~FBC_CTL_EN;
intel_de_write(i915, FBC_CONTROL, fbc_ctl);
/* Wait for compressing bit to clear */
if (intel_de_wait_for_clear(i915, FBC_STATUS,
FBC_STAT_COMPRESSING, 10)) {
drm_dbg_kms(&i915->drm, "FBC idle timed out\n");
return;
}
}
static void i8xx_fbc_activate(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
int i;
/* Clear old tags */
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
intel_de_write(i915, FBC_TAG(i), 0);
if (DISPLAY_VER(i915) == 4) {
intel_de_write(i915, FBC_CONTROL2,
i965_fbc_ctl2(fbc));
intel_de_write(i915, FBC_FENCE_OFF,
params->fence_y_offset);
}
intel_de_write(i915, FBC_CONTROL,
FBC_CTL_EN | i8xx_fbc_ctl(fbc));
}
static bool i8xx_fbc_is_active(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, FBC_CONTROL) & FBC_CTL_EN;
}
static bool i8xx_fbc_is_compressing(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, FBC_STATUS) &
(FBC_STAT_COMPRESSING | FBC_STAT_COMPRESSED);
}
static void i8xx_fbc_nuke(struct intel_fbc *fbc)
{
struct intel_fbc_reg_params *params = &fbc->params;
enum i9xx_plane_id i9xx_plane = params->crtc.i9xx_plane;
struct drm_i915_private *dev_priv = fbc->i915;
spin_lock_irq(&dev_priv->uncore.lock);
intel_de_write_fw(dev_priv, DSPADDR(i9xx_plane),
intel_de_read_fw(dev_priv, DSPADDR(i9xx_plane)));
spin_unlock_irq(&dev_priv->uncore.lock);
}
static void i8xx_fbc_program_cfb(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
GEM_BUG_ON(range_overflows_end_t(u64, i915->dsm.start,
fbc->compressed_fb.start, U32_MAX));
GEM_BUG_ON(range_overflows_end_t(u64, i915->dsm.start,
fbc->compressed_llb.start, U32_MAX));
intel_de_write(i915, FBC_CFB_BASE,
i915->dsm.start + fbc->compressed_fb.start);
intel_de_write(i915, FBC_LL_BASE,
i915->dsm.start + fbc->compressed_llb.start);
}
static const struct intel_fbc_funcs i8xx_fbc_funcs = {
.activate = i8xx_fbc_activate,
.deactivate = i8xx_fbc_deactivate,
.is_active = i8xx_fbc_is_active,
.is_compressing = i8xx_fbc_is_compressing,
.nuke = i8xx_fbc_nuke,
.program_cfb = i8xx_fbc_program_cfb,
};
static void i965_fbc_nuke(struct intel_fbc *fbc)
{
struct intel_fbc_reg_params *params = &fbc->params;
enum i9xx_plane_id i9xx_plane = params->crtc.i9xx_plane;
struct drm_i915_private *dev_priv = fbc->i915;
spin_lock_irq(&dev_priv->uncore.lock);
intel_de_write_fw(dev_priv, DSPSURF(i9xx_plane),
intel_de_read_fw(dev_priv, DSPSURF(i9xx_plane)));
spin_unlock_irq(&dev_priv->uncore.lock);
}
static const struct intel_fbc_funcs i965_fbc_funcs = {
.activate = i8xx_fbc_activate,
.deactivate = i8xx_fbc_deactivate,
.is_active = i8xx_fbc_is_active,
.is_compressing = i8xx_fbc_is_compressing,
.nuke = i965_fbc_nuke,
.program_cfb = i8xx_fbc_program_cfb,
};
static u32 g4x_dpfc_ctl_limit(struct intel_fbc *fbc)
{
switch (fbc->limit) {
default:
MISSING_CASE(fbc->limit);
fallthrough;
case 1:
return DPFC_CTL_LIMIT_1X;
case 2:
return DPFC_CTL_LIMIT_2X;
case 4:
return DPFC_CTL_LIMIT_4X;
}
}
static u32 g4x_dpfc_ctl(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
u32 dpfc_ctl;
dpfc_ctl = g4x_dpfc_ctl_limit(fbc) |
DPFC_CTL_PLANE_G4X(params->crtc.i9xx_plane);
if (IS_G4X(i915))
dpfc_ctl |= DPFC_CTL_SR_EN;
if (params->fence_id >= 0) {
dpfc_ctl |= DPFC_CTL_FENCE_EN_G4X;
if (DISPLAY_VER(i915) < 6)
dpfc_ctl |= DPFC_CTL_FENCENO(params->fence_id);
}
return dpfc_ctl;
}
static void g4x_fbc_activate(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
intel_de_write(i915, DPFC_FENCE_YOFF,
params->fence_y_offset);
intel_de_write(i915, DPFC_CONTROL,
DPFC_CTL_EN | g4x_dpfc_ctl(fbc));
}
static void g4x_fbc_deactivate(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
u32 dpfc_ctl;
/* Disable compression */
dpfc_ctl = intel_de_read(i915, DPFC_CONTROL);
if (dpfc_ctl & DPFC_CTL_EN) {
dpfc_ctl &= ~DPFC_CTL_EN;
intel_de_write(i915, DPFC_CONTROL, dpfc_ctl);
}
}
static bool g4x_fbc_is_active(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, DPFC_CONTROL) & DPFC_CTL_EN;
}
static bool g4x_fbc_is_compressing(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, DPFC_STATUS) & DPFC_COMP_SEG_MASK;
}
static void g4x_fbc_program_cfb(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
intel_de_write(i915, DPFC_CB_BASE, fbc->compressed_fb.start);
}
static const struct intel_fbc_funcs g4x_fbc_funcs = {
.activate = g4x_fbc_activate,
.deactivate = g4x_fbc_deactivate,
.is_active = g4x_fbc_is_active,
.is_compressing = g4x_fbc_is_compressing,
.nuke = i965_fbc_nuke,
.program_cfb = g4x_fbc_program_cfb,
};
static void ilk_fbc_activate(struct intel_fbc *fbc)
{
struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
intel_de_write(i915, ILK_DPFC_FENCE_YOFF,
params->fence_y_offset);
intel_de_write(i915, ILK_DPFC_CONTROL,
DPFC_CTL_EN | g4x_dpfc_ctl(fbc));
}
static void ilk_fbc_deactivate(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
u32 dpfc_ctl;
/* Disable compression */
dpfc_ctl = intel_de_read(i915, ILK_DPFC_CONTROL);
if (dpfc_ctl & DPFC_CTL_EN) {
dpfc_ctl &= ~DPFC_CTL_EN;
intel_de_write(i915, ILK_DPFC_CONTROL, dpfc_ctl);
}
}
static bool ilk_fbc_is_active(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, ILK_DPFC_CONTROL) & DPFC_CTL_EN;
}
static bool ilk_fbc_is_compressing(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, ILK_DPFC_STATUS) & DPFC_COMP_SEG_MASK;
}
static void ilk_fbc_program_cfb(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
intel_de_write(i915, ILK_DPFC_CB_BASE, fbc->compressed_fb.start);
}
static const struct intel_fbc_funcs ilk_fbc_funcs = {
.activate = ilk_fbc_activate,
.deactivate = ilk_fbc_deactivate,
.is_active = ilk_fbc_is_active,
.is_compressing = ilk_fbc_is_compressing,
.nuke = i965_fbc_nuke,
.program_cfb = ilk_fbc_program_cfb,
};
static void snb_fbc_program_fence(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
u32 ctl = 0;
if (params->fence_id >= 0)
ctl = SNB_DPFC_FENCE_EN | SNB_DPFC_FENCENO(params->fence_id);
intel_de_write(i915, SNB_DPFC_CTL_SA, ctl);
intel_de_write(i915, SNB_DPFC_CPU_FENCE_OFFSET, params->fence_y_offset);
}
static void snb_fbc_activate(struct intel_fbc *fbc)
{
snb_fbc_program_fence(fbc);
ilk_fbc_activate(fbc);
}
static void snb_fbc_nuke(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
intel_de_write(i915, MSG_FBC_REND_STATE, FBC_REND_NUKE);
intel_de_posting_read(i915, MSG_FBC_REND_STATE);
}
static const struct intel_fbc_funcs snb_fbc_funcs = {
.activate = snb_fbc_activate,
.deactivate = ilk_fbc_deactivate,
.is_active = ilk_fbc_is_active,
.is_compressing = ilk_fbc_is_compressing,
.nuke = snb_fbc_nuke,
.program_cfb = ilk_fbc_program_cfb,
};
static void glk_fbc_program_cfb_stride(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
u32 val = 0;
if (params->override_cfb_stride)
val |= FBC_STRIDE_OVERRIDE |
FBC_STRIDE(params->override_cfb_stride / fbc->limit);
intel_de_write(i915, GLK_FBC_STRIDE, val);
}
static void skl_fbc_program_cfb_stride(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
u32 val = 0;
/* Display WA #0529: skl, kbl, bxt. */
if (params->override_cfb_stride)
val |= CHICKEN_FBC_STRIDE_OVERRIDE |
CHICKEN_FBC_STRIDE(params->override_cfb_stride / fbc->limit);
intel_de_rmw(i915, CHICKEN_MISC_4,
CHICKEN_FBC_STRIDE_OVERRIDE |
CHICKEN_FBC_STRIDE_MASK, val);
}
static u32 ivb_dpfc_ctl(struct intel_fbc *fbc)
{
const struct intel_fbc_reg_params *params = &fbc->params;
struct drm_i915_private *i915 = fbc->i915;
u32 dpfc_ctl;
dpfc_ctl = g4x_dpfc_ctl_limit(fbc);
if (IS_IVYBRIDGE(i915))
dpfc_ctl |= DPFC_CTL_PLANE_IVB(params->crtc.i9xx_plane);
if (params->fence_id >= 0)
dpfc_ctl |= DPFC_CTL_FENCE_EN_IVB;
if (fbc->false_color)
dpfc_ctl |= DPFC_CTL_FALSE_COLOR;
return dpfc_ctl;
}
static void ivb_fbc_activate(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
if (DISPLAY_VER(i915) >= 10)
glk_fbc_program_cfb_stride(fbc);
else if (DISPLAY_VER(i915) == 9)
skl_fbc_program_cfb_stride(fbc);
if (i915->ggtt.num_fences)
snb_fbc_program_fence(fbc);
intel_de_write(i915, ILK_DPFC_CONTROL,
DPFC_CTL_EN | ivb_dpfc_ctl(fbc));
}
static bool ivb_fbc_is_compressing(struct intel_fbc *fbc)
{
return intel_de_read(fbc->i915, ILK_DPFC_STATUS2) & DPFC_COMP_SEG_MASK_IVB;
}
static void ivb_fbc_set_false_color(struct intel_fbc *fbc,
bool enable)
{
intel_de_rmw(fbc->i915, ILK_DPFC_CONTROL,
DPFC_CTL_FALSE_COLOR, enable ? DPFC_CTL_FALSE_COLOR : 0);
}
static const struct intel_fbc_funcs ivb_fbc_funcs = {
.activate = ivb_fbc_activate,
.deactivate = ilk_fbc_deactivate,
.is_active = ilk_fbc_is_active,
.is_compressing = ivb_fbc_is_compressing,
.nuke = snb_fbc_nuke,
.program_cfb = ilk_fbc_program_cfb,
.set_false_color = ivb_fbc_set_false_color,
};
static bool intel_fbc_hw_is_active(struct intel_fbc *fbc)
{
return fbc->funcs->is_active(fbc);
}
static void intel_fbc_hw_activate(struct intel_fbc *fbc)
{
trace_intel_fbc_activate(fbc->crtc);
fbc->active = true;
fbc->activated = true;
fbc->funcs->activate(fbc);
}
static void intel_fbc_hw_deactivate(struct intel_fbc *fbc)
{
trace_intel_fbc_deactivate(fbc->crtc);
fbc->active = false;
fbc->funcs->deactivate(fbc);
}
bool intel_fbc_is_compressing(struct intel_fbc *fbc)
{
return fbc->funcs->is_compressing(fbc);
}
static void intel_fbc_nuke(struct intel_fbc *fbc)
{
trace_intel_fbc_nuke(fbc->crtc);
fbc->funcs->nuke(fbc);
}
int intel_fbc_set_false_color(struct intel_fbc *fbc, bool enable)
{
if (!fbc->funcs || !fbc->funcs->set_false_color)
return -ENODEV;
mutex_lock(&fbc->lock);
fbc->false_color = enable;
fbc->funcs->set_false_color(fbc, enable);
mutex_unlock(&fbc->lock);
return 0;
}
/**
* intel_fbc_is_active - Is FBC active?
* @i915: i915 device instance
*
* This function is used to verify the current state of FBC.
*
* FIXME: This should be tracked in the plane config eventually
* instead of queried at runtime for most callers.
*/
bool intel_fbc_is_active(struct intel_fbc *fbc)
{
return fbc->active;
}
static void intel_fbc_activate(struct intel_fbc *fbc)
{
intel_fbc_hw_activate(fbc);
intel_fbc_nuke(fbc);
}
static void intel_fbc_deactivate(struct intel_fbc *fbc, const char *reason)
{
struct drm_i915_private *i915 = fbc->i915;
drm_WARN_ON(&i915->drm, !mutex_is_locked(&fbc->lock));
if (fbc->active)
intel_fbc_hw_deactivate(fbc);
fbc->no_fbc_reason = reason;
}
static u64 intel_fbc_cfb_base_max(struct drm_i915_private *i915)
{
if (DISPLAY_VER(i915) >= 5 || IS_G4X(i915))
return BIT_ULL(28);
else
return BIT_ULL(32);
}
static u64 intel_fbc_stolen_end(struct drm_i915_private *i915)
{
u64 end;
/* The FBC hardware for BDW/SKL doesn't have access to the stolen
* reserved range size, so it always assumes the maximum (8mb) is used.
* If we enable FBC using a CFB on that memory range we'll get FIFO
* underruns, even if that range is not reserved by the BIOS. */
if (IS_BROADWELL(i915) ||
(DISPLAY_VER(i915) == 9 && !IS_BROXTON(i915)))
end = resource_size(&i915->dsm) - 8 * 1024 * 1024;
else
end = U64_MAX;
return min(end, intel_fbc_cfb_base_max(i915));
}
static int intel_fbc_min_limit(int fb_cpp)
{
return fb_cpp == 2 ? 2 : 1;
}
static int intel_fbc_max_limit(struct drm_i915_private *i915)
{
/* WaFbcOnly1to1Ratio:ctg */
if (IS_G4X(i915))
return 1;
/*
* FBC2 can only do 1:1, 1:2, 1:4, we limit
* FBC1 to the same out of convenience.
*/
return 4;
}
static int find_compression_limit(struct intel_fbc *fbc,
unsigned int size, int min_limit)
{
struct drm_i915_private *i915 = fbc->i915;
u64 end = intel_fbc_stolen_end(i915);
int ret, limit = min_limit;
size /= limit;
/* Try to over-allocate to reduce reallocations and fragmentation. */
ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb,
size <<= 1, 4096, 0, end);
if (ret == 0)
return limit;
for (; limit <= intel_fbc_max_limit(i915); limit <<= 1) {
ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb,
size >>= 1, 4096, 0, end);
if (ret == 0)
return limit;
}
return 0;
}
static int intel_fbc_alloc_cfb(struct intel_fbc *fbc,
unsigned int size, int min_limit)
{
struct drm_i915_private *i915 = fbc->i915;
int ret;
drm_WARN_ON(&i915->drm,
drm_mm_node_allocated(&fbc->compressed_fb));
drm_WARN_ON(&i915->drm,
drm_mm_node_allocated(&fbc->compressed_llb));
if (DISPLAY_VER(i915) < 5 && !IS_G4X(i915)) {
ret = i915_gem_stolen_insert_node(i915, &fbc->compressed_llb,
4096, 4096);
if (ret)
goto err;
}
ret = find_compression_limit(fbc, size, min_limit);
if (!ret)
goto err_llb;
else if (ret > min_limit)
drm_info_once(&i915->drm,
"Reducing the compressed framebuffer size. This may lead to less power savings than a non-reduced-size. Try to increase stolen memory size if available in BIOS.\n");
fbc->limit = ret;
drm_dbg_kms(&i915->drm,
"reserved %llu bytes of contiguous stolen space for FBC, limit: %d\n",
fbc->compressed_fb.size, fbc->limit);
return 0;
err_llb:
if (drm_mm_node_allocated(&fbc->compressed_llb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_llb);
err:
if (drm_mm_initialized(&i915->mm.stolen))
drm_info_once(&i915->drm, "not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
return -ENOSPC;
}
static void intel_fbc_program_cfb(struct intel_fbc *fbc)
{
fbc->funcs->program_cfb(fbc);
}
static void __intel_fbc_cleanup_cfb(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
if (WARN_ON(intel_fbc_hw_is_active(fbc)))
return;
if (drm_mm_node_allocated(&fbc->compressed_llb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_llb);
if (drm_mm_node_allocated(&fbc->compressed_fb))
i915_gem_stolen_remove_node(i915, &fbc->compressed_fb);
}
void intel_fbc_cleanup(struct drm_i915_private *i915)
{
struct intel_fbc *fbc = &i915->fbc;
if (!HAS_FBC(i915))
return;
mutex_lock(&fbc->lock);
__intel_fbc_cleanup_cfb(fbc);
mutex_unlock(&fbc->lock);
}
static bool stride_is_valid(struct drm_i915_private *i915,
u64 modifier, unsigned int stride)
{
/* This should have been caught earlier. */
if (drm_WARN_ON_ONCE(&i915->drm, (stride & (64 - 1)) != 0))
return false;
/* Below are the additional FBC restrictions. */
if (stride < 512)
return false;
if (DISPLAY_VER(i915) == 2 || DISPLAY_VER(i915) == 3)
return stride == 4096 || stride == 8192;
if (DISPLAY_VER(i915) == 4 && !IS_G4X(i915) && stride < 2048)
return false;
/* Display WA #1105: skl,bxt,kbl,cfl,glk */
if ((DISPLAY_VER(i915) == 9 || IS_GEMINILAKE(i915)) &&
modifier == DRM_FORMAT_MOD_LINEAR && stride & 511)
return false;
if (stride > 16384)
return false;
return true;
}
static bool pixel_format_is_valid(struct drm_i915_private *i915,
u32 pixel_format)
{
switch (pixel_format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
return true;
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
/* 16bpp not supported on gen2 */
if (DISPLAY_VER(i915) == 2)
return false;
/* WaFbcOnly1to1Ratio:ctg */
if (IS_G4X(i915))
return false;
return true;
default:
return false;
}
}
static bool rotation_is_valid(struct drm_i915_private *i915,
u32 pixel_format, unsigned int rotation)
{
if (DISPLAY_VER(i915) >= 9 && pixel_format == DRM_FORMAT_RGB565 &&
drm_rotation_90_or_270(rotation))
return false;
else if (DISPLAY_VER(i915) <= 4 && !IS_G4X(i915) &&
rotation != DRM_MODE_ROTATE_0)
return false;
return true;
}
/*
* For some reason, the hardware tracking starts looking at whatever we
* programmed as the display plane base address register. It does not look at
* the X and Y offset registers. That's why we include the src x/y offsets
* instead of just looking at the plane size.
*/
static bool intel_fbc_hw_tracking_covers_screen(struct intel_fbc *fbc,
struct intel_crtc *crtc)
{
struct drm_i915_private *i915 = fbc->i915;
unsigned int effective_w, effective_h, max_w, max_h;
if (DISPLAY_VER(i915) >= 10) {
max_w = 5120;
max_h = 4096;
} else if (DISPLAY_VER(i915) >= 8 || IS_HASWELL(i915)) {
max_w = 4096;
max_h = 4096;
} else if (IS_G4X(i915) || DISPLAY_VER(i915) >= 5) {
max_w = 4096;
max_h = 2048;
} else {
max_w = 2048;
max_h = 1536;
}
intel_fbc_get_plane_source_size(&fbc->state_cache, &effective_w,
&effective_h);
effective_w += fbc->state_cache.plane.adjusted_x;
effective_h += fbc->state_cache.plane.adjusted_y;
return effective_w <= max_w && effective_h <= max_h;
}
static bool tiling_is_valid(struct drm_i915_private *i915,
u64 modifier)
{
switch (modifier) {
case DRM_FORMAT_MOD_LINEAR:
case I915_FORMAT_MOD_Y_TILED:
case I915_FORMAT_MOD_Yf_TILED:
return DISPLAY_VER(i915) >= 9;
case I915_FORMAT_MOD_X_TILED:
return true;
default:
return false;
}
}
static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_fbc *fbc = &i915->fbc;
struct intel_fbc_state_cache *cache = &fbc->state_cache;
struct drm_framebuffer *fb = plane_state->hw.fb;
cache->plane.visible = plane_state->uapi.visible;
if (!cache->plane.visible)
return;
cache->crtc.mode_flags = crtc_state->hw.adjusted_mode.flags;
if (IS_HASWELL(i915) || IS_BROADWELL(i915))
cache->crtc.hsw_bdw_pixel_rate = crtc_state->pixel_rate;
cache->plane.rotation = plane_state->hw.rotation;
/*
* Src coordinates are already rotated by 270 degrees for
* the 90/270 degree plane rotation cases (to match the
* GTT mapping), hence no need to account for rotation here.
*/
cache->plane.src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
cache->plane.src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
cache->plane.adjusted_x = plane_state->view.color_plane[0].x;
cache->plane.adjusted_y = plane_state->view.color_plane[0].y;
cache->plane.pixel_blend_mode = plane_state->hw.pixel_blend_mode;
cache->fb.format = fb->format;
cache->fb.modifier = fb->modifier;
cache->fb.stride = intel_fbc_plane_stride(plane_state);
/* FBC1 compression interval: arbitrary choice of 1 second */
cache->interval = drm_mode_vrefresh(&crtc_state->hw.adjusted_mode);
cache->fence_y_offset = intel_plane_fence_y_offset(plane_state);
drm_WARN_ON(&i915->drm, plane_state->flags & PLANE_HAS_FENCE &&
!plane_state->ggtt_vma->fence);
if (plane_state->flags & PLANE_HAS_FENCE &&
plane_state->ggtt_vma->fence)
cache->fence_id = plane_state->ggtt_vma->fence->id;
else
cache->fence_id = -1;
cache->psr2_active = crtc_state->has_psr2;
}
static bool intel_fbc_cfb_size_changed(struct intel_fbc *fbc)
{
return intel_fbc_cfb_size(fbc, &fbc->state_cache) >
fbc->compressed_fb.size * fbc->limit;
}
static u16 intel_fbc_override_cfb_stride(struct intel_fbc *fbc,
const struct intel_fbc_state_cache *cache)
{
unsigned int stride = _intel_fbc_cfb_stride(cache);
unsigned int stride_aligned = intel_fbc_cfb_stride(fbc, cache);
/*
* Override stride in 64 byte units per 4 line segment.
*
* Gen9 hw miscalculates cfb stride for linear as
* PLANE_STRIDE*512 instead of PLANE_STRIDE*64, so
* we always need to use the override there.
*/
if (stride != stride_aligned ||
(DISPLAY_VER(fbc->i915) == 9 &&
cache->fb.modifier == DRM_FORMAT_MOD_LINEAR))
return stride_aligned * 4 / 64;
return 0;
}
static bool intel_fbc_can_enable(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
if (intel_vgpu_active(i915)) {
fbc->no_fbc_reason = "VGPU is active";
return false;
}
if (!i915->params.enable_fbc) {
fbc->no_fbc_reason = "disabled per module param or by default";
return false;
}
if (fbc->underrun_detected) {
fbc->no_fbc_reason = "underrun detected";
return false;
}
return true;
}
static bool intel_fbc_can_activate(struct intel_crtc *crtc)
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_fbc *fbc = &i915->fbc;
struct intel_fbc_state_cache *cache = &fbc->state_cache;
if (!intel_fbc_can_enable(fbc))
return false;
if (!cache->plane.visible) {
fbc->no_fbc_reason = "primary plane not visible";
return false;
}
/* We don't need to use a state cache here since this information is
* global for all CRTC.
*/
if (fbc->underrun_detected) {
fbc->no_fbc_reason = "underrun detected";
return false;
}
if (cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) {
fbc->no_fbc_reason = "incompatible mode";
return false;
}
if (!intel_fbc_hw_tracking_covers_screen(fbc, crtc)) {
fbc->no_fbc_reason = "mode too large for compression";
return false;
}
/* The use of a CPU fence is one of two ways to detect writes by the
* CPU to the scanout and trigger updates to the FBC.
*
* The other method is by software tracking (see
* intel_fbc_invalidate/flush()), it will manually notify FBC and nuke
* the current compressed buffer and recompress it.
*
* Note that is possible for a tiled surface to be unmappable (and
* so have no fence associated with it) due to aperture constraints
* at the time of pinning.
*
* FIXME with 90/270 degree rotation we should use the fence on
* the normal GTT view (the rotated view doesn't even have a
* fence). Would need changes to the FBC fence Y offset as well.
* For now this will effectively disable FBC with 90/270 degree
* rotation.
*/
if (DISPLAY_VER(i915) < 9 && cache->fence_id < 0) {
fbc->no_fbc_reason = "framebuffer not tiled or fenced";
return false;
}
if (!pixel_format_is_valid(i915, cache->fb.format->format)) {
fbc->no_fbc_reason = "pixel format is invalid";
return false;
}
if (!rotation_is_valid(i915, cache->fb.format->format,
cache->plane.rotation)) {
fbc->no_fbc_reason = "rotation unsupported";
return false;
}
if (!tiling_is_valid(i915, cache->fb.modifier)) {
fbc->no_fbc_reason = "tiling unsupported";
return false;
}
if (!stride_is_valid(i915, cache->fb.modifier,
cache->fb.stride * cache->fb.format->cpp[0])) {
fbc->no_fbc_reason = "framebuffer stride not supported";
return false;
}
if (cache->plane.pixel_blend_mode != DRM_MODE_BLEND_PIXEL_NONE &&
cache->fb.format->has_alpha) {
fbc->no_fbc_reason = "per-pixel alpha blending is incompatible with FBC";
return false;
}
/* WaFbcExceedCdClockThreshold:hsw,bdw */
if ((IS_HASWELL(i915) || IS_BROADWELL(i915)) &&
cache->crtc.hsw_bdw_pixel_rate >= i915->cdclk.hw.cdclk * 95 / 100) {
fbc->no_fbc_reason = "pixel rate is too big";
return false;
}
/* It is possible for the required CFB size change without a
* crtc->disable + crtc->enable since it is possible to change the
* stride without triggering a full modeset. Since we try to
* over-allocate the CFB, there's a chance we may keep FBC enabled even
* if this happens, but if we exceed the current CFB size we'll have to
* disable FBC. Notice that it would be possible to disable FBC, wait
* for a frame, free the stolen node, then try to reenable FBC in case
* we didn't get any invalidate/deactivate calls, but this would require
* a lot of tracking just for a specific case. If we conclude it's an
* important case, we can implement it later. */
if (intel_fbc_cfb_size_changed(fbc)) {
fbc->no_fbc_reason = "CFB requirements changed";
return false;
}
/*
* Work around a problem on GEN9+ HW, where enabling FBC on a plane
* having a Y offset that isn't divisible by 4 causes FIFO underrun
* and screen flicker.
*/
if (DISPLAY_VER(i915) >= 9 &&
(fbc->state_cache.plane.adjusted_y & 3)) {
fbc->no_fbc_reason = "plane Y offset is misaligned";
return false;
}
/* Wa_22010751166: icl, ehl, tgl, dg1, rkl */
if (DISPLAY_VER(i915) >= 11 &&
(cache->plane.src_h + cache->plane.adjusted_y) % 4) {
fbc->no_fbc_reason = "plane height + offset is non-modulo of 4";
return false;
}
/*
* Display 12+ is not supporting FBC with PSR2.
* Recommendation is to keep this combination disabled
* Bspec: 50422 HSD: 14010260002
*/
if (fbc->state_cache.psr2_active && DISPLAY_VER(i915) >= 12) {
fbc->no_fbc_reason = "not supported with PSR2";
return false;
}
return true;
}
static void intel_fbc_get_reg_params(struct intel_fbc *fbc,
struct intel_crtc *crtc)
{
const struct intel_fbc_state_cache *cache = &fbc->state_cache;
struct intel_fbc_reg_params *params = &fbc->params;
/* Since all our fields are integer types, use memset here so the
* comparison function can rely on memcmp because the padding will be
* zero. */
memset(params, 0, sizeof(*params));
params->fence_id = cache->fence_id;
params->fence_y_offset = cache->fence_y_offset;
params->interval = cache->interval;
params->crtc.pipe = crtc->pipe;
params->crtc.i9xx_plane = to_intel_plane(crtc->base.primary)->i9xx_plane;
params->fb.format = cache->fb.format;
params->fb.modifier = cache->fb.modifier;
params->fb.stride = cache->fb.stride;
params->cfb_stride = intel_fbc_cfb_stride(fbc, cache);
params->cfb_size = intel_fbc_cfb_size(fbc, cache);
params->override_cfb_stride = intel_fbc_override_cfb_stride(fbc, cache);
params->plane_visible = cache->plane.visible;
}
static bool intel_fbc_can_flip_nuke(const struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_fbc *fbc = &i915->fbc;
const struct intel_fbc_state_cache *cache = &fbc->state_cache;
const struct intel_fbc_reg_params *params = &fbc->params;
if (drm_atomic_crtc_needs_modeset(&crtc_state->uapi))
return false;
if (!params->plane_visible)
return false;
if (!intel_fbc_can_activate(crtc))
return false;
if (params->fb.format != cache->fb.format)
return false;
if (params->fb.modifier != cache->fb.modifier)
return false;
if (params->fb.stride != cache->fb.stride)
return false;
if (params->cfb_stride != intel_fbc_cfb_stride(fbc, cache))
return false;
if (params->cfb_size != intel_fbc_cfb_size(fbc, cache))
return false;
if (params->override_cfb_stride != intel_fbc_override_cfb_stride(fbc, cache))
return false;
return true;
}
bool intel_fbc_pre_update(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct intel_plane *plane = to_intel_plane(crtc->base.primary);
const struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane);
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_fbc *fbc = plane->fbc;
const char *reason = "update pending";
bool need_vblank_wait = false;
if (!fbc || !plane_state)
return need_vblank_wait;
mutex_lock(&fbc->lock);
if (fbc->crtc != crtc)
goto unlock;
intel_fbc_update_state_cache(crtc, crtc_state, plane_state);
fbc->flip_pending = true;
if (!intel_fbc_can_flip_nuke(crtc_state)) {
intel_fbc_deactivate(fbc, reason);
/*
* Display WA #1198: glk+
* Need an extra vblank wait between FBC disable and most plane
* updates. Bspec says this is only needed for plane disable, but
* that is not true. Touching most plane registers will cause the
* corruption to appear. Also SKL/derivatives do not seem to be
* affected.
*
* TODO: could optimize this a bit by sampling the frame
* counter when we disable FBC (if it was already done earlier)
* and skipping the extra vblank wait before the plane update
* if at least one frame has already passed.
*/
if (fbc->activated &&
DISPLAY_VER(i915) >= 10)
need_vblank_wait = true;
fbc->activated = false;
}
unlock:
mutex_unlock(&fbc->lock);
return need_vblank_wait;
}
static void __intel_fbc_disable(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
struct intel_crtc *crtc = fbc->crtc;
drm_WARN_ON(&i915->drm, !mutex_is_locked(&fbc->lock));
drm_WARN_ON(&i915->drm, !fbc->crtc);
drm_WARN_ON(&i915->drm, fbc->active);
drm_dbg_kms(&i915->drm, "Disabling FBC on pipe %c\n",
pipe_name(crtc->pipe));
__intel_fbc_cleanup_cfb(fbc);
fbc->crtc = NULL;
}
static void __intel_fbc_post_update(struct intel_crtc *crtc)
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_fbc *fbc = &i915->fbc;
drm_WARN_ON(&i915->drm, !mutex_is_locked(&fbc->lock));
if (fbc->crtc != crtc)
return;
fbc->flip_pending = false;
if (!i915->params.enable_fbc) {
intel_fbc_deactivate(fbc, "disabled at runtime per module param");
__intel_fbc_disable(fbc);
return;
}
intel_fbc_get_reg_params(fbc, crtc);
if (!intel_fbc_can_activate(crtc))
return;
if (!fbc->busy_bits)
intel_fbc_activate(fbc);
else
intel_fbc_deactivate(fbc, "frontbuffer write");
}
void intel_fbc_post_update(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct intel_plane *plane = to_intel_plane(crtc->base.primary);
const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane);
struct intel_fbc *fbc = plane->fbc;
if (!fbc || !plane_state)
return;
mutex_lock(&fbc->lock);
__intel_fbc_post_update(crtc);
mutex_unlock(&fbc->lock);
}
static unsigned int intel_fbc_get_frontbuffer_bit(struct intel_fbc *fbc)
{
if (fbc->crtc)
return to_intel_plane(fbc->crtc->base.primary)->frontbuffer_bit;
else
return fbc->possible_framebuffer_bits;
}
void intel_fbc_invalidate(struct drm_i915_private *i915,
unsigned int frontbuffer_bits,
enum fb_op_origin origin)
{
struct intel_fbc *fbc = &i915->fbc;
if (!HAS_FBC(i915))
return;
if (origin == ORIGIN_FLIP || origin == ORIGIN_CURSOR_UPDATE)
return;
mutex_lock(&fbc->lock);
fbc->busy_bits |= intel_fbc_get_frontbuffer_bit(fbc) & frontbuffer_bits;
if (fbc->crtc && fbc->busy_bits)
intel_fbc_deactivate(fbc, "frontbuffer write");
mutex_unlock(&fbc->lock);
}
void intel_fbc_flush(struct drm_i915_private *i915,
unsigned int frontbuffer_bits, enum fb_op_origin origin)
{
struct intel_fbc *fbc = &i915->fbc;
if (!HAS_FBC(i915))
return;
mutex_lock(&fbc->lock);
fbc->busy_bits &= ~frontbuffer_bits;
if (origin == ORIGIN_FLIP || origin == ORIGIN_CURSOR_UPDATE)
goto out;
if (!fbc->busy_bits && fbc->crtc &&
(frontbuffer_bits & intel_fbc_get_frontbuffer_bit(fbc))) {
if (fbc->active)
intel_fbc_nuke(fbc);
else if (!fbc->flip_pending)
__intel_fbc_post_update(fbc->crtc);
}
out:
mutex_unlock(&fbc->lock);
}
/**
* intel_fbc_choose_crtc - select a CRTC to enable FBC on
* @i915: i915 device instance
* @state: the atomic state structure
*
* This function looks at the proposed state for CRTCs and planes, then chooses
* which pipe is going to have FBC by setting intel_crtc_state->enable_fbc to
* true.
*
* Later, intel_fbc_enable is going to look for state->enable_fbc and then maybe
* enable FBC for the chosen CRTC. If it does, it will set i915->fbc.crtc.
*/
void intel_fbc_choose_crtc(struct drm_i915_private *i915,
struct intel_atomic_state *state)
{
struct intel_fbc *fbc = &i915->fbc;
struct intel_plane *plane;
struct intel_plane_state *plane_state;
bool crtc_chosen = false;
int i;
mutex_lock(&fbc->lock);
/* Does this atomic commit involve the CRTC currently tied to FBC? */
if (fbc->crtc &&
!intel_atomic_get_new_crtc_state(state, fbc->crtc))
goto out;
if (!intel_fbc_can_enable(fbc))
goto out;
/* Simply choose the first CRTC that is compatible and has a visible
* plane. We could go for fancier schemes such as checking the plane
* size, but this would just affect the few platforms that don't tie FBC
* to pipe or plane A. */
for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
struct intel_crtc_state *crtc_state;
struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
if (plane->fbc != fbc)
continue;
if (!plane_state->uapi.visible)
continue;
crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
crtc_state->enable_fbc = true;
crtc_chosen = true;
break;
}
if (!crtc_chosen)
fbc->no_fbc_reason = "no suitable CRTC for FBC";
out:
mutex_unlock(&fbc->lock);
}
/**
* intel_fbc_enable: tries to enable FBC on the CRTC
* @crtc: the CRTC
* @state: corresponding &drm_crtc_state for @crtc
*
* This function checks if the given CRTC was chosen for FBC, then enables it if
* possible. Notice that it doesn't activate FBC. It is valid to call
* intel_fbc_enable multiple times for the same pipe without an
* intel_fbc_disable in the middle, as long as it is deactivated.
*/
static void intel_fbc_enable(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_plane *plane = to_intel_plane(crtc->base.primary);
const struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane);
struct intel_fbc *fbc = plane->fbc;
struct intel_fbc_state_cache *cache;
int min_limit;
if (!fbc || !plane_state)
return;
cache = &fbc->state_cache;
min_limit = intel_fbc_min_limit(plane_state->hw.fb ?
plane_state->hw.fb->format->cpp[0] : 0);
mutex_lock(&fbc->lock);
if (fbc->crtc) {
if (fbc->crtc != crtc)
goto out;
if (fbc->limit >= min_limit &&
!intel_fbc_cfb_size_changed(fbc))
goto out;
__intel_fbc_disable(fbc);
}
drm_WARN_ON(&i915->drm, fbc->active);
intel_fbc_update_state_cache(crtc, crtc_state, plane_state);
/* FIXME crtc_state->enable_fbc lies :( */
if (!cache->plane.visible)
goto out;
if (intel_fbc_alloc_cfb(fbc, intel_fbc_cfb_size(fbc, cache), min_limit)) {
cache->plane.visible = false;
fbc->no_fbc_reason = "not enough stolen memory";
goto out;
}
drm_dbg_kms(&i915->drm, "Enabling FBC on pipe %c\n",
pipe_name(crtc->pipe));
fbc->no_fbc_reason = "FBC enabled but not active yet\n";
fbc->crtc = crtc;
intel_fbc_program_cfb(fbc);
out:
mutex_unlock(&fbc->lock);
}
/**
* intel_fbc_disable - disable FBC if it's associated with crtc
* @crtc: the CRTC
*
* This function disables FBC if it's associated with the provided CRTC.
*/
void intel_fbc_disable(struct intel_crtc *crtc)
{
struct intel_plane *plane = to_intel_plane(crtc->base.primary);
struct intel_fbc *fbc = plane->fbc;
if (!fbc)
return;
mutex_lock(&fbc->lock);
if (fbc->crtc == crtc)
__intel_fbc_disable(fbc);
mutex_unlock(&fbc->lock);
}
/**
* intel_fbc_update: enable/disable FBC on the CRTC
* @state: atomic state
* @crtc: the CRTC
*
* This function checks if the given CRTC was chosen for FBC, then enables it if
* possible. Notice that it doesn't activate FBC. It is valid to call
* intel_fbc_update multiple times for the same pipe without an
* intel_fbc_disable in the middle.
*/
void intel_fbc_update(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
const struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
if (crtc_state->update_pipe && !crtc_state->enable_fbc)
intel_fbc_disable(crtc);
else
intel_fbc_enable(state, crtc);
}
/**
* intel_fbc_global_disable - globally disable FBC
* @i915: i915 device instance
*
* This function disables FBC regardless of which CRTC is associated with it.
*/
void intel_fbc_global_disable(struct drm_i915_private *i915)
{
struct intel_fbc *fbc = &i915->fbc;
if (!HAS_FBC(i915))
return;
mutex_lock(&fbc->lock);
if (fbc->crtc) {
drm_WARN_ON(&i915->drm, fbc->crtc->active);
__intel_fbc_disable(fbc);
}
mutex_unlock(&fbc->lock);
}
static void intel_fbc_underrun_work_fn(struct work_struct *work)
{
struct drm_i915_private *i915 =
container_of(work, struct drm_i915_private, fbc.underrun_work);
struct intel_fbc *fbc = &i915->fbc;
mutex_lock(&fbc->lock);
/* Maybe we were scheduled twice. */
if (fbc->underrun_detected || !fbc->crtc)
goto out;
drm_dbg_kms(&i915->drm, "Disabling FBC due to FIFO underrun.\n");
fbc->underrun_detected = true;
intel_fbc_deactivate(fbc, "FIFO underrun");
out:
mutex_unlock(&fbc->lock);
}
/*
* intel_fbc_reset_underrun - reset FBC fifo underrun status.
* @i915: i915 device instance
*
* See intel_fbc_handle_fifo_underrun_irq(). For automated testing we
* want to re-enable FBC after an underrun to increase test coverage.
*/
int intel_fbc_reset_underrun(struct intel_fbc *fbc)
{
struct drm_i915_private *i915 = fbc->i915;
int ret;
cancel_work_sync(&fbc->underrun_work);
ret = mutex_lock_interruptible(&fbc->lock);
if (ret)
return ret;
if (fbc->underrun_detected) {
drm_dbg_kms(&i915->drm,
"Re-allowing FBC after fifo underrun\n");
fbc->no_fbc_reason = "FIFO underrun cleared";
}
fbc->underrun_detected = false;
mutex_unlock(&fbc->lock);
return 0;
}
/**
* intel_fbc_handle_fifo_underrun_irq - disable FBC when we get a FIFO underrun
* @i915: i915 device instance
*
* Without FBC, most underruns are harmless and don't really cause too many
* problems, except for an annoying message on dmesg. With FBC, underruns can
* become black screens or even worse, especially when paired with bad
* watermarks. So in order for us to be on the safe side, completely disable FBC
* in case we ever detect a FIFO underrun on any pipe. An underrun on any pipe
* already suggests that watermarks may be bad, so try to be as safe as
* possible.
*
* This function is called from the IRQ handler.
*/
void intel_fbc_handle_fifo_underrun_irq(struct intel_fbc *fbc)
{
if (!HAS_FBC(fbc->i915))
return;
/* There's no guarantee that underrun_detected won't be set to true
* right after this check and before the work is scheduled, but that's
* not a problem since we'll check it again under the work function
* while FBC is locked. This check here is just to prevent us from
* unnecessarily scheduling the work, and it relies on the fact that we
* never switch underrun_detect back to false after it's true. */
if (READ_ONCE(fbc->underrun_detected))
return;
schedule_work(&fbc->underrun_work);
}
/*
* The DDX driver changes its behavior depending on the value it reads from
* i915.enable_fbc, so sanitize it by translating the default value into either
* 0 or 1 in order to allow it to know what's going on.
*
* Notice that this is done at driver initialization and we still allow user
* space to change the value during runtime without sanitizing it again. IGT
* relies on being able to change i915.enable_fbc at runtime.
*/
static int intel_sanitize_fbc_option(struct drm_i915_private *i915)
{
if (i915->params.enable_fbc >= 0)
return !!i915->params.enable_fbc;
if (!HAS_FBC(i915))
return 0;
if (IS_BROADWELL(i915) || DISPLAY_VER(i915) >= 9)
return 1;
return 0;
}
static bool need_fbc_vtd_wa(struct drm_i915_private *i915)
{
/* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */
if (intel_vtd_active() &&
(IS_SKYLAKE(i915) || IS_BROXTON(i915))) {
drm_info(&i915->drm,
"Disabling framebuffer compression (FBC) to prevent screen flicker with VT-d enabled\n");
return true;
}
return false;
}
/**
* intel_fbc_init - Initialize FBC
* @i915: the i915 device
*
* This function might be called during PM init process.
*/
void intel_fbc_init(struct drm_i915_private *i915)
{
struct intel_fbc *fbc = &i915->fbc;
fbc->i915 = i915;
INIT_WORK(&fbc->underrun_work, intel_fbc_underrun_work_fn);
mutex_init(&fbc->lock);
fbc->active = false;
if (!drm_mm_initialized(&i915->mm.stolen))
mkwrite_device_info(i915)->display.has_fbc = false;
if (need_fbc_vtd_wa(i915))
mkwrite_device_info(i915)->display.has_fbc = false;
i915->params.enable_fbc = intel_sanitize_fbc_option(i915);
drm_dbg_kms(&i915->drm, "Sanitized enable_fbc value: %d\n",
i915->params.enable_fbc);
if (!HAS_FBC(i915)) {
fbc->no_fbc_reason = "unsupported by this chipset";
return;
}
if (DISPLAY_VER(i915) >= 7)
fbc->funcs = &ivb_fbc_funcs;
else if (DISPLAY_VER(i915) == 6)
fbc->funcs = &snb_fbc_funcs;
else if (DISPLAY_VER(i915) == 5)
fbc->funcs = &ilk_fbc_funcs;
else if (IS_G4X(i915))
fbc->funcs = &g4x_fbc_funcs;
else if (DISPLAY_VER(i915) == 4)
fbc->funcs = &i965_fbc_funcs;
else
fbc->funcs = &i8xx_fbc_funcs;
/* We still don't have any sort of hardware state readout for FBC, so
* deactivate it in case the BIOS activated it to make sure software
* matches the hardware state. */
if (intel_fbc_hw_is_active(fbc))
intel_fbc_hw_deactivate(fbc);
}