Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/i915/gt/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 90 kB image not shown  

Quelle  intel_workarounds.c   Sprache: C

 
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2014-2018 Intel Corporation
 */


#include "i915_drv.h"
#include "i915_reg.h"
#include "intel_context.h"
#include "intel_engine_pm.h"
#include "intel_engine_regs.h"
#include "intel_gpu_commands.h"
#include "intel_gt.h"
#include "intel_gt_ccs_mode.h"
#include "intel_gt_mcr.h"
#include "intel_gt_print.h"
#include "intel_gt_regs.h"
#include "intel_ring.h"
#include "intel_workarounds.h"

#include "display/intel_fbc_regs.h"

/**
 * DOC: Hardware workarounds
 *
 * Hardware workarounds are register programming documented to be executed in
 * the driver that fall outside of the normal programming sequences for a
 * platform. There are some basic categories of workarounds, depending on
 * how/when they are applied:
 *
 * - Context workarounds: workarounds that touch registers that are
 *   saved/restored to/from the HW context image. The list is emitted (via Load
 *   Register Immediate commands) once when initializing the device and saved in
 *   the default context. That default context is then used on every context
 *   creation to have a "primed golden context", i.e. a context image that
 *   already contains the changes needed to all the registers.
 *
 *   Context workarounds should be implemented in the \*_ctx_workarounds_init()
 *   variants respective to the targeted platforms.
 *
 * - Engine workarounds: the list of these WAs is applied whenever the specific
 *   engine is reset. It's also possible that a set of engine classes share a
 *   common power domain and they are reset together. This happens on some
 *   platforms with render and compute engines. In this case (at least) one of
 *   them need to keeep the workaround programming: the approach taken in the
 *   driver is to tie those workarounds to the first compute/render engine that
 *   is registered.  When executing with GuC submission, engine resets are
 *   outside of kernel driver control, hence the list of registers involved in
 *   written once, on engine initialization, and then passed to GuC, that
 *   saves/restores their values before/after the reset takes place. See
 *   ``drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c`` for reference.
 *
 *   Workarounds for registers specific to RCS and CCS should be implemented in
 *   rcs_engine_wa_init() and ccs_engine_wa_init(), respectively; those for
 *   registers belonging to BCS, VCS or VECS should be implemented in
 *   xcs_engine_wa_init(). Workarounds for registers not belonging to a specific
 *   engine's MMIO range but that are part of of the common RCS/CCS reset domain
 *   should be implemented in general_render_compute_wa_init(). The settings
 *   about the CCS load balancing should be added in ccs_engine_wa_mode().
 *
 * - GT workarounds: the list of these WAs is applied whenever these registers
 *   revert to their default values: on GPU reset, suspend/resume [1]_, etc.
 *
 *   GT workarounds should be implemented in the \*_gt_workarounds_init()
 *   variants respective to the targeted platforms.
 *
 * - Register whitelist: some workarounds need to be implemented in userspace,
 *   but need to touch privileged registers. The whitelist in the kernel
 *   instructs the hardware to allow the access to happen. From the kernel side,
 *   this is just a special case of a MMIO workaround (as we write the list of
 *   these to/be-whitelisted registers to some special HW registers).
 *
 *   Register whitelisting should be done in the \*_whitelist_build() variants
 *   respective to the targeted platforms.
 *
 * - Workaround batchbuffers: buffers that get executed automatically by the
 *   hardware on every HW context restore. These buffers are created and
 *   programmed in the default context so the hardware always go through those
 *   programming sequences when switching contexts. The support for workaround
 *   batchbuffers is enabled these hardware mechanisms:
 *
 *   #. INDIRECT_CTX: A batchbuffer and an offset are provided in the default
 *      context, pointing the hardware to jump to that location when that offset
 *      is reached in the context restore. Workaround batchbuffer in the driver
 *      currently uses this mechanism for all platforms.
 *
 *   #. BB_PER_CTX_PTR: A batchbuffer is provided in the default context,
 *      pointing the hardware to a buffer to continue executing after the
 *      engine registers are restored in a context restore sequence. This is
 *      currently not used in the driver.
 *
 * - Other:  There are WAs that, due to their nature, cannot be applied from a
 *   central place. Those are peppered around the rest of the code, as needed.
 *   Workarounds related to the display IP are the main example.
 *
 * .. [1] Technically, some registers are powercontext saved & restored, so they
 *    survive a suspend/resume. In practice, writing them again is not too
 *    costly and simplifies things, so it's the approach taken in the driver.
 */


static void wa_init_start(struct i915_wa_list *wal, struct intel_gt *gt,
     const char *name, const char *engine_name)
{
 wal->gt = gt;
 wal->name = name;
 wal->engine_name = engine_name;
}

#define WA_LIST_CHUNK (1 << 4)

static void wa_init_finish(struct i915_wa_list *wal)
{
 /* Trim unused entries. */
 if (!IS_ALIGNED(wal->count, WA_LIST_CHUNK)) {
  struct i915_wa *list = kmemdup_array(wal->list, wal->count,
           sizeof(*list), GFP_KERNEL);

  if (list) {
   kfree(wal->list);
   wal->list = list;
  }
 }

 if (!wal->count)
  return;

 gt_dbg(wal->gt, "Initialized %u %s workarounds on %s\n",
        wal->wa_count, wal->name, wal->engine_name);
}

static enum forcewake_domains
wal_get_fw_for_rmw(struct intel_uncore *uncore, const struct i915_wa_list *wal)
{
 enum forcewake_domains fw = 0;
 struct i915_wa *wa;
 unsigned int i;

 for (i = 0, wa = wal->list; i < wal->count; i++, wa++)
  fw |= intel_uncore_forcewake_for_reg(uncore,
           wa->reg,
           FW_REG_READ |
           FW_REG_WRITE);

 return fw;
}

static void _wa_add(struct i915_wa_list *wal, const struct i915_wa *wa)
{
 unsigned int addr = i915_mmio_reg_offset(wa->reg);
 struct drm_i915_private *i915 = wal->gt->i915;
 unsigned int start = 0, end = wal->count;
 const unsigned int grow = WA_LIST_CHUNK;
 struct i915_wa *wa_;

 GEM_BUG_ON(!is_power_of_2(grow));

 if (IS_ALIGNED(wal->count, grow)) { /* Either uninitialized or full. */
  struct i915_wa *list;

  list = kmalloc_array(ALIGN(wal->count + 1, grow), sizeof(*list),
         GFP_KERNEL);
  if (!list) {
   drm_err(&i915->drm, "No space for workaround init!\n");
   return;
  }

  if (wal->list) {
   memcpy(list, wal->list, sizeof(*wa) * wal->count);
   kfree(wal->list);
  }

  wal->list = list;
 }

 while (start < end) {
  unsigned int mid = start + (end - start) / 2;

  if (i915_mmio_reg_offset(wal->list[mid].reg) < addr) {
   start = mid + 1;
  } else if (i915_mmio_reg_offset(wal->list[mid].reg) > addr) {
   end = mid;
  } else {
   wa_ = &wal->list[mid];

   if ((wa->clr | wa_->clr) && !(wa->clr & ~wa_->clr)) {
    drm_err(&i915->drm,
     "Discarding overwritten w/a for reg %04x (clear: %08x, set: %08x)\n",
     i915_mmio_reg_offset(wa_->reg),
     wa_->clr, wa_->set);

    wa_->set &= ~wa->clr;
   }

   wal->wa_count++;
   wa_->set |= wa->set;
   wa_->clr |= wa->clr;
   wa_->read |= wa->read;
   return;
  }
 }

 wal->wa_count++;
 wa_ = &wal->list[wal->count++];
 *wa_ = *wa;

 while (wa_-- > wal->list) {
  GEM_BUG_ON(i915_mmio_reg_offset(wa_[0].reg) ==
      i915_mmio_reg_offset(wa_[1].reg));
  if (i915_mmio_reg_offset(wa_[1].reg) >
      i915_mmio_reg_offset(wa_[0].reg))
   break;

  swap(wa_[1], wa_[0]);
 }
}

static void wa_add(struct i915_wa_list *wal, i915_reg_t reg,
     u32 clear, u32 set, u32 read_mask, bool masked_reg)
{
 struct i915_wa wa = {
  .reg  = reg,
  .clr  = clear,
  .set  = set,
  .read = read_mask,
  .masked_reg = masked_reg,
 };

 _wa_add(wal, &wa);
}

static void wa_mcr_add(struct i915_wa_list *wal, i915_mcr_reg_t reg,
         u32 clear, u32 set, u32 read_mask, bool masked_reg)
{
 struct i915_wa wa = {
  .mcr_reg = reg,
  .clr  = clear,
  .set  = set,
  .read = read_mask,
  .masked_reg = masked_reg,
  .is_mcr = 1,
 };

 _wa_add(wal, &wa);
}

static void
wa_write_clr_set(struct i915_wa_list *wal, i915_reg_t reg, u32 clear, u32 set)
{
 wa_add(wal, reg, clear, set, clear | set, false);
}

static void
wa_mcr_write_clr_set(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 clear, u32 set)
{
 wa_mcr_add(wal, reg, clear, set, clear | set, false);
}

static void
wa_write(struct i915_wa_list *wal, i915_reg_t reg, u32 set)
{
 wa_write_clr_set(wal, reg, ~0, set);
}

static void
wa_write_or(struct i915_wa_list *wal, i915_reg_t reg, u32 set)
{
 wa_write_clr_set(wal, reg, set, set);
}

static void
wa_mcr_write_or(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 set)
{
 wa_mcr_write_clr_set(wal, reg, set, set);
}

static void
wa_write_clr(struct i915_wa_list *wal, i915_reg_t reg, u32 clr)
{
 wa_write_clr_set(wal, reg, clr, 0);
}

static void
wa_mcr_write_clr(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 clr)
{
 wa_mcr_write_clr_set(wal, reg, clr, 0);
}

/*
 * WA operations on "masked register". A masked register has the upper 16 bits
 * documented as "masked" in b-spec. Its purpose is to allow writing to just a
 * portion of the register without a rmw: you simply write in the upper 16 bits
 * the mask of bits you are going to modify.
 *
 * The wa_masked_* family of functions already does the necessary operations to
 * calculate the mask based on the parameters passed, so user only has to
 * provide the lower 16 bits of that register.
 */


static void
wa_masked_en(struct i915_wa_list *wal, i915_reg_t reg, u32 val)
{
 wa_add(wal, reg, 0, _MASKED_BIT_ENABLE(val), val, true);
}

static void
wa_mcr_masked_en(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 val)
{
 wa_mcr_add(wal, reg, 0, _MASKED_BIT_ENABLE(val), val, true);
}

static void
wa_masked_dis(struct i915_wa_list *wal, i915_reg_t reg, u32 val)
{
 wa_add(wal, reg, 0, _MASKED_BIT_DISABLE(val), val, true);
}

static void
wa_mcr_masked_dis(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 val)
{
 wa_mcr_add(wal, reg, 0, _MASKED_BIT_DISABLE(val), val, true);
}

static void
wa_masked_field_set(struct i915_wa_list *wal, i915_reg_t reg,
      u32 mask, u32 val)
{
 wa_add(wal, reg, 0, _MASKED_FIELD(mask, val), mask, true);
}

static void
wa_mcr_masked_field_set(struct i915_wa_list *wal, i915_mcr_reg_t reg,
   u32 mask, u32 val)
{
 wa_mcr_add(wal, reg, 0, _MASKED_FIELD(mask, val), mask, true);
}

static void gen6_ctx_workarounds_init(struct intel_engine_cs *engine,
          struct i915_wa_list *wal)
{
 wa_masked_en(wal, INSTPM, INSTPM_FORCE_ORDERING);
}

static void gen7_ctx_workarounds_init(struct intel_engine_cs *engine,
          struct i915_wa_list *wal)
{
 wa_masked_en(wal, INSTPM, INSTPM_FORCE_ORDERING);
}

static void gen8_ctx_workarounds_init(struct intel_engine_cs *engine,
          struct i915_wa_list *wal)
{
 wa_masked_en(wal, INSTPM, INSTPM_FORCE_ORDERING);

 /* WaDisableAsyncFlipPerfMode:bdw,chv */
 wa_masked_en(wal, RING_MI_MODE(RENDER_RING_BASE), ASYNC_FLIP_PERF_DISABLE);

 /* WaDisablePartialInstShootdown:bdw,chv */
 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN,
    PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);

 /* Use Force Non-Coherent whenever executing a 3D context. This is a
 * workaround for a possible hang in the unlikely event a TLB
 * invalidation occurs during a PSD flush.
 */

 /* WaForceEnableNonCoherent:bdw,chv */
 /* WaHdcDisableFetchWhenMasked:bdw,chv */
 wa_masked_en(wal, HDC_CHICKEN0,
       HDC_DONOT_FETCH_MEM_WHEN_MASKED |
       HDC_FORCE_NON_COHERENT);

 /* From the Haswell PRM, Command Reference: Registers, CACHE_MODE_0:
 * "The Hierarchical Z RAW Stall Optimization allows non-overlapping
 *  polygons in the same 8x4 pixel/sample area to be processed without
 *  stalling waiting for the earlier ones to write to Hierarchical Z
 *  buffer."
 *
 * This optimization is off by default for BDW and CHV; turn it on.
 */

 wa_masked_dis(wal, CACHE_MODE_0_GEN7, HIZ_RAW_STALL_OPT_DISABLE);

 /* Wa4x4STCOptimizationDisable:bdw,chv */
 wa_masked_en(wal, CACHE_MODE_1, GEN8_4x4_STC_OPTIMIZATION_DISABLE);

 /*
 * BSpec recommends 8x4 when MSAA is used,
 * however in practice 16x4 seems fastest.
 *
 * Note that PS/WM thread counts depend on the WIZ hashing
 * disable bit, which we don't touch here, but it's good
 * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
 */

 wa_masked_field_set(wal, GEN7_GT_MODE,
       GEN6_WIZ_HASHING_MASK,
       GEN6_WIZ_HASHING_16x4);
}

static void bdw_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = engine->i915;

 gen8_ctx_workarounds_init(engine, wal);

 /* WaDisableThreadStallDopClockGating:bdw (pre-production) */
 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);

 /* WaDisableDopClockGating:bdw
 *
 * Also see the related UCGTCL1 write in bdw_init_clock_gating()
 * to disable EUTC clock gating.
 */

 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN2,
    DOP_CLOCK_GATING_DISABLE);

 wa_mcr_masked_en(wal, GEN8_HALF_SLICE_CHICKEN3,
    GEN8_SAMPLER_POWER_BYPASS_DIS);

 wa_masked_en(wal, HDC_CHICKEN0,
       /* WaForceContextSaveRestoreNonCoherent:bdw */
       HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
       /* WaDisableFenceDestinationToSLM:bdw (pre-prod) */
       (INTEL_INFO(i915)->gt == 3 ? HDC_FENCE_DEST_SLM_DISABLE : 0));
}

static void chv_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen8_ctx_workarounds_init(engine, wal);

 /* WaDisableThreadStallDopClockGating:chv */
 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);

 /* Improve HiZ throughput on CHV. */
 wa_masked_en(wal, HIZ_CHICKEN, CHV_HZ_8X8_MODE_IN_1X);
}

static void gen9_ctx_workarounds_init(struct intel_engine_cs *engine,
          struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = engine->i915;

 if (HAS_LLC(i915)) {
  /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
 *
 * Must match Display Engine. See
 * WaCompressedResourceDisplayNewHashMode.
 */

  wa_masked_en(wal, COMMON_SLICE_CHICKEN2,
        GEN9_PBE_COMPRESSED_HASH_SELECTION);
  wa_mcr_masked_en(wal, GEN9_HALF_SLICE_CHICKEN7,
     GEN9_SAMPLER_HASH_COMPRESSED_READ_ADDR);
 }

 /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk,cfl */
 /* WaDisablePartialInstShootdown:skl,bxt,kbl,glk,cfl */
 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN,
    FLOW_CONTROL_ENABLE |
    PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);

 /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl,glk,cfl */
 /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl,cfl */
 wa_mcr_masked_en(wal, GEN9_HALF_SLICE_CHICKEN7,
    GEN9_ENABLE_YV12_BUGFIX |
    GEN9_ENABLE_GPGPU_PREEMPTION);

 /* Wa4x4STCOptimizationDisable:skl,bxt,kbl,glk,cfl */
 /* WaDisablePartialResolveInVc:skl,bxt,kbl,cfl */
 wa_masked_en(wal, CACHE_MODE_1,
       GEN8_4x4_STC_OPTIMIZATION_DISABLE |
       GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE);

 /* WaCcsTlbPrefetchDisable:skl,bxt,kbl,glk,cfl */
 wa_mcr_masked_dis(wal, GEN9_HALF_SLICE_CHICKEN5,
     GEN9_CCS_TLB_PREFETCH_ENABLE);

 /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl,cfl */
 wa_masked_en(wal, HDC_CHICKEN0,
       HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
       HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE);

 /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are
 * both tied to WaForceContextSaveRestoreNonCoherent
 * in some hsds for skl. We keep the tie for all gen9. The
 * documentation is a bit hazy and so we want to get common behaviour,
 * even though there is no clear evidence we would need both on kbl/bxt.
 * This area has been source of system hangs so we play it safe
 * and mimic the skl regardless of what bspec says.
 *
 * Use Force Non-Coherent whenever executing a 3D context. This
 * is a workaround for a possible hang in the unlikely event
 * a TLB invalidation occurs during a PSD flush.
 */


 /* WaForceEnableNonCoherent:skl,bxt,kbl,cfl */
 wa_masked_en(wal, HDC_CHICKEN0,
       HDC_FORCE_NON_COHERENT);

 /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */
 if (IS_SKYLAKE(i915) ||
     IS_KABYLAKE(i915) ||
     IS_COFFEELAKE(i915) ||
     IS_COMETLAKE(i915))
  wa_mcr_masked_en(wal, GEN8_HALF_SLICE_CHICKEN3,
     GEN8_SAMPLER_POWER_BYPASS_DIS);

 /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl,glk,cfl */
 wa_mcr_masked_en(wal, HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);

 /*
 * Supporting preemption with fine-granularity requires changes in the
 * batch buffer programming. Since we can't break old userspace, we
 * need to set our default preemption level to safe value. Userspace is
 * still able to use more fine-grained preemption levels, since in
 * WaEnablePreemptionGranularityControlByUMD we're whitelisting the
 * per-ctx register. As such, WaDisable{3D,GPGPU}MidCmdPreemption are
 * not real HW workarounds, but merely a way to start using preemption
 * while maintaining old contract with userspace.
 */


 /* WaDisable3DMidCmdPreemption:skl,bxt,glk,cfl,[cnl] */
 wa_masked_dis(wal, GEN8_CS_CHICKEN1, GEN9_PREEMPT_3D_OBJECT_LEVEL);

 /* WaDisableGPGPUMidCmdPreemption:skl,bxt,blk,cfl,[cnl] */
 wa_masked_field_set(wal, GEN8_CS_CHICKEN1,
       GEN9_PREEMPT_GPGPU_LEVEL_MASK,
       GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);

 /* WaClearHIZ_WM_CHICKEN3:bxt,glk */
 if (IS_GEN9_LP(i915))
  wa_masked_en(wal, GEN9_WM_CHICKEN3, GEN9_FACTOR_IN_CLR_VAL_HIZ);
}

static void skl_tune_iz_hashing(struct intel_engine_cs *engine,
    struct i915_wa_list *wal)
{
 struct intel_gt *gt = engine->gt;
 u8 vals[3] = { 0, 0, 0 };
 unsigned int i;

 for (i = 0; i < 3; i++) {
  u8 ss;

  /*
 * Only consider slices where one, and only one, subslice has 7
 * EUs
 */

  if (!is_power_of_2(gt->info.sseu.subslice_7eu[i]))
   continue;

  /*
 * subslice_7eu[i] != 0 (because of the check above) and
 * ss_max == 4 (maximum number of subslices possible per slice)
 *
 * ->    0 <= ss <= 3;
 */

  ss = ffs(gt->info.sseu.subslice_7eu[i]) - 1;
  vals[i] = 3 - ss;
 }

 if (vals[0] == 0 && vals[1] == 0 && vals[2] == 0)
  return;

 /* Tune IZ hashing. See intel_device_info_runtime_init() */
 wa_masked_field_set(wal, GEN7_GT_MODE,
       GEN9_IZ_HASHING_MASK(2) |
       GEN9_IZ_HASHING_MASK(1) |
       GEN9_IZ_HASHING_MASK(0),
       GEN9_IZ_HASHING(2, vals[2]) |
       GEN9_IZ_HASHING(1, vals[1]) |
       GEN9_IZ_HASHING(0, vals[0]));
}

static void skl_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen9_ctx_workarounds_init(engine, wal);
 skl_tune_iz_hashing(engine, wal);
}

static void bxt_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen9_ctx_workarounds_init(engine, wal);

 /* WaDisableThreadStallDopClockGating:bxt */
 wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN,
    STALL_DOP_GATING_DISABLE);

 /* WaToEnableHwFixForPushConstHWBug:bxt */
 wa_masked_en(wal, COMMON_SLICE_CHICKEN2,
       GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
}

static void kbl_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = engine->i915;

 gen9_ctx_workarounds_init(engine, wal);

 /* WaToEnableHwFixForPushConstHWBug:kbl */
 if (IS_KABYLAKE(i915) && IS_GRAPHICS_STEP(i915, STEP_C0, STEP_FOREVER))
  wa_masked_en(wal, COMMON_SLICE_CHICKEN2,
        GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);

 /* WaDisableSbeCacheDispatchPortSharing:kbl */
 wa_mcr_masked_en(wal, GEN8_HALF_SLICE_CHICKEN1,
    GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
}

static void glk_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen9_ctx_workarounds_init(engine, wal);

 /* WaToEnableHwFixForPushConstHWBug:glk */
 wa_masked_en(wal, COMMON_SLICE_CHICKEN2,
       GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
}

static void cfl_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen9_ctx_workarounds_init(engine, wal);

 /* WaToEnableHwFixForPushConstHWBug:cfl */
 wa_masked_en(wal, COMMON_SLICE_CHICKEN2,
       GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);

 /* WaDisableSbeCacheDispatchPortSharing:cfl */
 wa_mcr_masked_en(wal, GEN8_HALF_SLICE_CHICKEN1,
    GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
}

static void icl_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = engine->i915;

 /* Wa_1406697149 (WaDisableBankHangMode:icl) */
 wa_write(wal, GEN8_L3CNTLREG, GEN8_ERRDETBCTRL);

 /* WaForceEnableNonCoherent:icl
 * This is not the same workaround as in early Gen9 platforms, where
 * lacking this could cause system hangs, but coherency performance
 * overhead is high and only a few compute workloads really need it
 * (the register is whitelisted in hardware now, so UMDs can opt in
 * for coherency if they have a good reason).
 */

 wa_mcr_masked_en(wal, ICL_HDC_MODE, HDC_FORCE_NON_COHERENT);

 /* WaEnableFloatBlendOptimization:icl */
 wa_mcr_add(wal, GEN10_CACHE_MODE_SS, 0,
     _MASKED_BIT_ENABLE(FLOAT_BLEND_OPTIMIZATION_ENABLE),
     0 /* write-only, so skip validation */,
     true);

 /* WaDisableGPGPUMidThreadPreemption:icl */
 wa_masked_field_set(wal, GEN8_CS_CHICKEN1,
       GEN9_PREEMPT_GPGPU_LEVEL_MASK,
       GEN9_PREEMPT_GPGPU_THREAD_GROUP_LEVEL);

 /* allow headerless messages for preemptible GPGPU context */
 wa_mcr_masked_en(wal, GEN10_SAMPLER_MODE,
    GEN11_SAMPLER_ENABLE_HEADLESS_MSG);

 /* Wa_1604278689:icl,ehl */
 wa_write(wal, IVB_FBC_RT_BASE, 0xFFFFFFFF & ~ILK_FBC_RT_VALID);
 wa_write_clr_set(wal, IVB_FBC_RT_BASE_UPPER,
    0,
    0xFFFFFFFF);

 /* Wa_1406306137:icl,ehl */
 wa_mcr_masked_en(wal, GEN9_ROW_CHICKEN4, GEN11_DIS_PICK_2ND_EU);

 if (IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) {
  /*
 * Disable Repacking for Compression (masked R/W access)
 * before rendering compressed surfaces for display.
 */

  wa_masked_en(wal, CACHE_MODE_0_GEN7,
        DISABLE_REPACKING_FOR_COMPRESSION);
 }
}

/*
 * These settings aren't actually workarounds, but general tuning settings that
 * need to be programmed on dg2 platform.
 */

static void dg2_ctx_gt_tuning_init(struct intel_engine_cs *engine,
       struct i915_wa_list *wal)
{
 wa_mcr_masked_en(wal, CHICKEN_RASTER_2, TBIMR_FAST_CLIP);
 wa_mcr_write_clr_set(wal, XEHP_L3SQCREG5, L3_PWM_TIMER_INIT_VAL_MASK,
        REG_FIELD_PREP(L3_PWM_TIMER_INIT_VAL_MASK, 0x7f));
 wa_mcr_write_clr_set(wal, XEHP_FF_MODE2, FF_MODE2_TDS_TIMER_MASK,
        FF_MODE2_TDS_TIMER_128);
}

static void gen12_ctx_workarounds_init(struct intel_engine_cs *engine,
           struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = engine->i915;

 /*
 * Wa_1409142259:tgl,dg1,adl-p,adl-n
 * Wa_1409347922:tgl,dg1,adl-p
 * Wa_1409252684:tgl,dg1,adl-p
 * Wa_1409217633:tgl,dg1,adl-p
 * Wa_1409207793:tgl,dg1,adl-p
 * Wa_1409178076:tgl,dg1,adl-p,adl-n
 * Wa_1408979724:tgl,dg1,adl-p,adl-n
 * Wa_14010443199:tgl,rkl,dg1,adl-p,adl-n
 * Wa_14010698770:tgl,rkl,dg1,adl-s,adl-p,adl-n
 * Wa_1409342910:tgl,rkl,dg1,adl-s,adl-p,adl-n
 * Wa_22010465259:tgl,rkl,dg1,adl-s,adl-p,adl-n
 */

 wa_masked_en(wal, GEN11_COMMON_SLICE_CHICKEN3,
       GEN12_DISABLE_CPS_AWARE_COLOR_PIPE);

 /* WaDisableGPGPUMidThreadPreemption:gen12 */
 wa_masked_field_set(wal, GEN8_CS_CHICKEN1,
       GEN9_PREEMPT_GPGPU_LEVEL_MASK,
       GEN9_PREEMPT_GPGPU_THREAD_GROUP_LEVEL);

 /*
 * Wa_16011163337 - GS_TIMER
 *
 * TDS_TIMER: Although some platforms refer to it as Wa_1604555607, we
 * need to program it even on those that don't explicitly list that
 * workaround.
 *
 * Note that the programming of GEN12_FF_MODE2 is further modified
 * according to the FF_MODE2 guidance given by Wa_1608008084.
 * Wa_1608008084 tells us the FF_MODE2 register will return the wrong
 * value when read from the CPU.
 *
 * The default value for this register is zero for all fields.
 * So instead of doing a RMW we should just write the desired values
 * for TDS and GS timers. Note that since the readback can't be trusted,
 * the clear mask is just set to ~0 to make sure other bits are not
 * inadvertently set. For the same reason read verification is ignored.
 */

 wa_add(wal,
        GEN12_FF_MODE2,
        ~0,
        FF_MODE2_TDS_TIMER_128 | FF_MODE2_GS_TIMER_224,
        0, false);

 if (!IS_DG1(i915)) {
  /* Wa_1806527549 */
  wa_masked_en(wal, HIZ_CHICKEN, HZ_DEPTH_TEST_LE_GE_OPT_DISABLE);

  /* Wa_1606376872 */
  wa_masked_en(wal, COMMON_SLICE_CHICKEN4, DISABLE_TDC_LOAD_BALANCING_CALC);
 }

 /*
 * This bit must be set to enable performance optimization for fast
 * clears.
 */

 wa_mcr_write_or(wal, GEN8_WM_CHICKEN2, WAIT_ON_DEPTH_STALL_DONE_DISABLE);
}

static void dg1_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 gen12_ctx_workarounds_init(engine, wal);

 /* Wa_1409044764 */
 wa_masked_dis(wal, GEN11_COMMON_SLICE_CHICKEN3,
        DG1_FLOAT_POINT_BLEND_OPT_STRICT_MODE_EN);

 /* Wa_22010493298 */
 wa_masked_en(wal, HIZ_CHICKEN,
       DG1_HZ_READ_SUPPRESSION_OPTIMIZATION_DISABLE);
}

static void dg2_ctx_workarounds_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 dg2_ctx_gt_tuning_init(engine, wal);

 /* Wa_16013271637:dg2 */
 wa_mcr_masked_en(wal, XEHP_SLICE_COMMON_ECO_CHICKEN1,
    MSC_MSAA_REODER_BUF_BYPASS_DISABLE);

 /* Wa_14014947963:dg2 */
 wa_masked_field_set(wal, VF_PREEMPTION, PREEMPTION_VERTEX_COUNT, 0x4000);

 /* Wa_18018764978:dg2 */
 wa_mcr_masked_en(wal, XEHP_PSS_MODE2, SCOREBOARD_STALL_FLUSH_CONTROL);

 /* Wa_18019271663:dg2 */
 wa_masked_en(wal, CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE);

 /* Wa_14019877138:dg2 */
 wa_mcr_masked_en(wal, XEHP_PSS_CHICKEN, FD_END_COLLECT);
}

static void xelpg_ctx_gt_tuning_init(struct intel_engine_cs *engine,
         struct i915_wa_list *wal)
{
 struct intel_gt *gt = engine->gt;

 dg2_ctx_gt_tuning_init(engine, wal);

 /*
 * Due to Wa_16014892111, the DRAW_WATERMARK tuning must be done in
 * gen12_emit_indirect_ctx_rcs() rather than here on some early
 * steppings.
 */

 if (!(IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) ||
       IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)))
  wa_add(wal, DRAW_WATERMARK, VERT_WM_VAL, 0x3FF, 0, false);
}

static void xelpg_ctx_workarounds_init(struct intel_engine_cs *engine,
           struct i915_wa_list *wal)
{
 struct intel_gt *gt = engine->gt;

 xelpg_ctx_gt_tuning_init(engine, wal);

 if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) ||
     IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) {
  /* Wa_14014947963 */
  wa_masked_field_set(wal, VF_PREEMPTION,
        PREEMPTION_VERTEX_COUNT, 0x4000);

  /* Wa_16013271637 */
  wa_mcr_masked_en(wal, XEHP_SLICE_COMMON_ECO_CHICKEN1,
     MSC_MSAA_REODER_BUF_BYPASS_DISABLE);

  /* Wa_18019627453 */
  wa_mcr_masked_en(wal, VFLSKPD, VF_PREFETCH_TLB_DIS);

  /* Wa_18018764978 */
  wa_mcr_masked_en(wal, XEHP_PSS_MODE2, SCOREBOARD_STALL_FLUSH_CONTROL);
 }

 /* Wa_18019271663 */
 wa_masked_en(wal, CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE);

 /* Wa_14019877138 */
 wa_mcr_masked_en(wal, XEHP_PSS_CHICKEN, FD_END_COLLECT);
}

static void fakewa_disable_nestedbb_mode(struct intel_engine_cs *engine,
      struct i915_wa_list *wal)
{
 /*
 * This is a "fake" workaround defined by software to ensure we
 * maintain reliable, backward-compatible behavior for userspace with
 * regards to how nested MI_BATCH_BUFFER_START commands are handled.
 *
 * The per-context setting of MI_MODE[12] determines whether the bits
 * of a nested MI_BATCH_BUFFER_START instruction should be interpreted
 * in the traditional manner or whether they should instead use a new
 * tgl+ meaning that breaks backward compatibility, but allows nesting
 * into 3rd-level batchbuffers.  When this new capability was first
 * added in TGL, it remained off by default unless a context
 * intentionally opted in to the new behavior.  However Xe_HPG now
 * flips this on by default and requires that we explicitly opt out if
 * we don't want the new behavior.
 *
 * From a SW perspective, we want to maintain the backward-compatible
 * behavior for userspace, so we'll apply a fake workaround to set it
 * back to the legacy behavior on platforms where the hardware default
 * is to break compatibility.  At the moment there is no Linux
 * userspace that utilizes third-level batchbuffers, so this will avoid
 * userspace from needing to make any changes.  using the legacy
 * meaning is the correct thing to do.  If/when we have userspace
 * consumers that want to utilize third-level batch nesting, we can
 * provide a context parameter to allow them to opt-in.
 */

 wa_masked_dis(wal, RING_MI_MODE(engine->mmio_base), TGL_NESTED_BB_EN);
}

static void gen12_ctx_gt_mocs_init(struct intel_engine_cs *engine,
       struct i915_wa_list *wal)
{
 u8 mocs;

 /*
 * Some blitter commands do not have a field for MOCS, those
 * commands will use MOCS index pointed by BLIT_CCTL.
 * BLIT_CCTL registers are needed to be programmed to un-cached.
 */

 if (engine->class == COPY_ENGINE_CLASS) {
  mocs = engine->gt->mocs.uc_index;
  wa_write_clr_set(wal,
     BLIT_CCTL(engine->mmio_base),
     BLIT_CCTL_MASK,
     BLIT_CCTL_MOCS(mocs, mocs));
 }
}

/*
 * gen12_ctx_gt_fake_wa_init() aren't programmingan official workaround
 * defined by the hardware team, but it programming general context registers.
 * Adding those context register programming in context workaround
 * allow us to use the wa framework for proper application and validation.
 */

static void
gen12_ctx_gt_fake_wa_init(struct intel_engine_cs *engine,
     struct i915_wa_list *wal)
{
 if (GRAPHICS_VER_FULL(engine->i915) >= IP_VER(12, 55))
  fakewa_disable_nestedbb_mode(engine, wal);

 gen12_ctx_gt_mocs_init(engine, wal);
}

static void
__intel_engine_init_ctx_wa(struct intel_engine_cs *engine,
      struct i915_wa_list *wal,
      const char *name)
{
 struct drm_i915_private *i915 = engine->i915;

 wa_init_start(wal, engine->gt, name, engine->name);

 /* Applies to all engines */
 /*
 * Fake workarounds are not the actual workaround but
 * programming of context registers using workaround framework.
 */

 if (GRAPHICS_VER(i915) >= 12)
  gen12_ctx_gt_fake_wa_init(engine, wal);

 if (engine->class != RENDER_CLASS)
  goto done;

 if (IS_GFX_GT_IP_RANGE(engine->gt, IP_VER(12, 70), IP_VER(12, 74)))
  xelpg_ctx_workarounds_init(engine, wal);
 else if (IS_DG2(i915))
  dg2_ctx_workarounds_init(engine, wal);
 else if (IS_DG1(i915))
  dg1_ctx_workarounds_init(engine, wal);
 else if (GRAPHICS_VER(i915) == 12)
  gen12_ctx_workarounds_init(engine, wal);
 else if (GRAPHICS_VER(i915) == 11)
  icl_ctx_workarounds_init(engine, wal);
 else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915))
  cfl_ctx_workarounds_init(engine, wal);
 else if (IS_GEMINILAKE(i915))
  glk_ctx_workarounds_init(engine, wal);
 else if (IS_KABYLAKE(i915))
  kbl_ctx_workarounds_init(engine, wal);
 else if (IS_BROXTON(i915))
  bxt_ctx_workarounds_init(engine, wal);
 else if (IS_SKYLAKE(i915))
  skl_ctx_workarounds_init(engine, wal);
 else if (IS_CHERRYVIEW(i915))
  chv_ctx_workarounds_init(engine, wal);
 else if (IS_BROADWELL(i915))
  bdw_ctx_workarounds_init(engine, wal);
 else if (GRAPHICS_VER(i915) == 7)
  gen7_ctx_workarounds_init(engine, wal);
 else if (GRAPHICS_VER(i915) == 6)
  gen6_ctx_workarounds_init(engine, wal);
 else if (GRAPHICS_VER(i915) < 8)
  ;
 else
  MISSING_CASE(GRAPHICS_VER(i915));

done:
 wa_init_finish(wal);
}

void intel_engine_init_ctx_wa(struct intel_engine_cs *engine)
{
 __intel_engine_init_ctx_wa(engine, &engine->ctx_wa_list, "context");
}

int intel_engine_emit_ctx_wa(struct i915_request *rq)
{
 struct i915_wa_list *wal = &rq->engine->ctx_wa_list;
 struct intel_uncore *uncore = rq->engine->uncore;
 enum forcewake_domains fw;
 unsigned long flags;
 struct i915_wa *wa;
 unsigned int i;
 u32 *cs;
 int ret;

 if (wal->count == 0)
  return 0;

 ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
 if (ret)
  return ret;

 if ((IS_GFX_GT_IP_RANGE(rq->engine->gt, IP_VER(12, 70), IP_VER(12, 74)) ||
      IS_DG2(rq->i915)) && rq->engine->class == RENDER_CLASS)
  cs = intel_ring_begin(rq, (wal->count * 2 + 6));
 else
  cs = intel_ring_begin(rq, (wal->count * 2 + 2));

 if (IS_ERR(cs))
  return PTR_ERR(cs);

 fw = wal_get_fw_for_rmw(uncore, wal);

 intel_gt_mcr_lock(wal->gt, &flags);
 spin_lock(&uncore->lock);
 intel_uncore_forcewake_get__locked(uncore, fw);

 *cs++ = MI_LOAD_REGISTER_IMM(wal->count);
 for (i = 0, wa = wal->list; i < wal->count; i++, wa++) {
  u32 val;

  /* Skip reading the register if it's not really needed */
  if (wa->masked_reg || (wa->clr | wa->set) == U32_MAX) {
   val = wa->set;
  } else {
   val = wa->is_mcr ?
    intel_gt_mcr_read_any_fw(wal->gt, wa->mcr_reg) :
    intel_uncore_read_fw(uncore, wa->reg);
   val &= ~wa->clr;
   val |= wa->set;
  }

  *cs++ = i915_mmio_reg_offset(wa->reg);
  *cs++ = val;
 }
 *cs++ = MI_NOOP;

 /* Wa_14019789679 */
 if ((IS_GFX_GT_IP_RANGE(rq->engine->gt, IP_VER(12, 70), IP_VER(12, 74)) ||
      IS_DG2(rq->i915)) && rq->engine->class == RENDER_CLASS) {
  *cs++ = CMD_3DSTATE_MESH_CONTROL;
  *cs++ = 0;
  *cs++ = 0;
  *cs++ = MI_NOOP;
 }

 intel_uncore_forcewake_put__locked(uncore, fw);
 spin_unlock(&uncore->lock);
 intel_gt_mcr_unlock(wal->gt, flags);

 intel_ring_advance(rq, cs);

 ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
 if (ret)
  return ret;

 return 0;
}

static void
gen4_gt_workarounds_init(struct intel_gt *gt,
    struct i915_wa_list *wal)
{
 /* WaDisable_RenderCache_OperationalFlush:gen4,ilk */
 wa_masked_dis(wal, CACHE_MODE_0, RC_OP_FLUSH_ENABLE);
}

static void
g4x_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen4_gt_workarounds_init(gt, wal);

 /* WaDisableRenderCachePipelinedFlush:g4x,ilk */
 wa_masked_en(wal, CACHE_MODE_0, CM0_PIPELINED_RENDER_FLUSH_DISABLE);
}

static void
ilk_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 g4x_gt_workarounds_init(gt, wal);

 wa_masked_en(wal, _3D_CHICKEN2, _3D_CHICKEN2_WM_READ_PIPELINED);
}

static void
snb_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
}

static void
ivb_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */
 wa_masked_dis(wal,
        GEN7_COMMON_SLICE_CHICKEN1,
        GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);

 /* WaApplyL3ControlAndL3ChickenMode:ivb */
 wa_write(wal, GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL);
 wa_write(wal, GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE);

 /* WaForceL3Serialization:ivb */
 wa_write_clr(wal, GEN7_L3SQCREG4, L3SQ_URB_READ_CAM_MATCH_DISABLE);
}

static void
vlv_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 /* WaForceL3Serialization:vlv */
 wa_write_clr(wal, GEN7_L3SQCREG4, L3SQ_URB_READ_CAM_MATCH_DISABLE);

 /*
 * WaIncreaseL3CreditsForVLVB0:vlv
 * This is the hardware default actually.
 */

 wa_write(wal, GEN7_L3SQCREG1, VLV_B0_WA_L3SQCREG1_VALUE);
}

static void
hsw_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 /* L3 caching of data atomics doesn't work -- disable it. */
 wa_write(wal, HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE);

 wa_add(wal,
        HSW_ROW_CHICKEN3, 0,
        _MASKED_BIT_ENABLE(HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE),
        0 /* XXX does this reg exist? */, true);

 /* WaVSRefCountFullforceMissDisable:hsw */
 wa_write_clr(wal, GEN7_FF_THREAD_MODE, GEN7_FF_VS_REF_CNT_FFME);
}

static void
gen9_wa_init_mcr(struct drm_i915_private *i915, struct i915_wa_list *wal)
{
 const struct sseu_dev_info *sseu = &to_gt(i915)->info.sseu;
 unsigned int slice, subslice;
 u32 mcr, mcr_mask;

 GEM_BUG_ON(GRAPHICS_VER(i915) != 9);

 /*
 * WaProgramMgsrForCorrectSliceSpecificMmioReads:gen9,glk,kbl,cml
 * Before any MMIO read into slice/subslice specific registers, MCR
 * packet control register needs to be programmed to point to any
 * enabled s/ss pair. Otherwise, incorrect values will be returned.
 * This means each subsequent MMIO read will be forwarded to an
 * specific s/ss combination, but this is OK since these registers
 * are consistent across s/ss in almost all cases. In the rare
 * occasions, such as INSTDONE, where this value is dependent
 * on s/ss combo, the read should be done with read_subslice_reg.
 */

 slice = ffs(sseu->slice_mask) - 1;
 GEM_BUG_ON(slice >= ARRAY_SIZE(sseu->subslice_mask.hsw));
 subslice = ffs(intel_sseu_get_hsw_subslices(sseu, slice));
 GEM_BUG_ON(!subslice);
 subslice--;

 /*
 * We use GEN8_MCR..() macros to calculate the |mcr| value for
 * Gen9 to address WaProgramMgsrForCorrectSliceSpecificMmioReads
 */

 mcr = GEN8_MCR_SLICE(slice) | GEN8_MCR_SUBSLICE(subslice);
 mcr_mask = GEN8_MCR_SLICE_MASK | GEN8_MCR_SUBSLICE_MASK;

 drm_dbg(&i915->drm, "MCR slice:%d/subslice:%d = %x\n", slice, subslice, mcr);

 wa_write_clr_set(wal, GEN8_MCR_SELECTOR, mcr_mask, mcr);
}

static void
gen9_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = gt->i915;

 /* WaProgramMgsrForCorrectSliceSpecificMmioReads:glk,kbl,cml,gen9 */
 gen9_wa_init_mcr(i915, wal);

 /* WaDisableKillLogic:bxt,skl,kbl */
 if (!IS_COFFEELAKE(i915) && !IS_COMETLAKE(i915))
  wa_write_or(wal,
       GAM_ECOCHK,
       ECOCHK_DIS_TLB);

 if (HAS_LLC(i915)) {
  /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
 *
 * Must match Display Engine. See
 * WaCompressedResourceDisplayNewHashMode.
 */

  wa_write_or(wal,
       MMCD_MISC_CTRL,
       MMCD_PCLA | MMCD_HOTSPOT_EN);
 }

 /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
 wa_write_or(wal,
      GAM_ECOCHK,
      BDW_DISABLE_HDC_INVALIDATION);
}

static void
skl_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen9_gt_workarounds_init(gt, wal);

 /* WaDisableGafsUnitClkGating:skl */
 wa_write_or(wal,
      GEN7_UCGCTL4,
      GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);

 /* WaInPlaceDecompressionHang:skl */
 if (IS_SKYLAKE(gt->i915) && IS_GRAPHICS_STEP(gt->i915, STEP_A0, STEP_H0))
  wa_write_or(wal,
       GEN9_GAMT_ECO_REG_RW_IA,
       GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
}

static void
kbl_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen9_gt_workarounds_init(gt, wal);

 /* WaDisableDynamicCreditSharing:kbl */
 if (IS_KABYLAKE(gt->i915) && IS_GRAPHICS_STEP(gt->i915, 0, STEP_C0))
  wa_write_or(wal,
       GAMT_CHKN_BIT_REG,
       GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);

 /* WaDisableGafsUnitClkGating:kbl */
 wa_write_or(wal,
      GEN7_UCGCTL4,
      GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);

 /* WaInPlaceDecompressionHang:kbl */
 wa_write_or(wal,
      GEN9_GAMT_ECO_REG_RW_IA,
      GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
}

static void
glk_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen9_gt_workarounds_init(gt, wal);
}

static void
cfl_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen9_gt_workarounds_init(gt, wal);

 /* WaDisableGafsUnitClkGating:cfl */
 wa_write_or(wal,
      GEN7_UCGCTL4,
      GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);

 /* WaInPlaceDecompressionHang:cfl */
 wa_write_or(wal,
      GEN9_GAMT_ECO_REG_RW_IA,
      GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
}

static void __set_mcr_steering(struct i915_wa_list *wal,
          i915_reg_t steering_reg,
          unsigned int slice, unsigned int subslice)
{
 u32 mcr, mcr_mask;

 mcr = GEN11_MCR_SLICE(slice) | GEN11_MCR_SUBSLICE(subslice);
 mcr_mask = GEN11_MCR_SLICE_MASK | GEN11_MCR_SUBSLICE_MASK;

 wa_write_clr_set(wal, steering_reg, mcr_mask, mcr);
}

static void debug_dump_steering(struct intel_gt *gt)
{
 struct drm_printer p = drm_dbg_printer(>->i915->drm, DRM_UT_DRIVER,
            "MCR Steering:");

 if (drm_debug_enabled(DRM_UT_DRIVER))
  intel_gt_mcr_report_steering(&p, gt, false);
}

static void __add_mcr_wa(struct intel_gt *gt, struct i915_wa_list *wal,
    unsigned int slice, unsigned int subslice)
{
 __set_mcr_steering(wal, GEN8_MCR_SELECTOR, slice, subslice);

 gt->default_steering.groupid = slice;
 gt->default_steering.instanceid = subslice;

 debug_dump_steering(gt);
}

static void
icl_wa_init_mcr(struct intel_gt *gt, struct i915_wa_list *wal)
{
 const struct sseu_dev_info *sseu = >->info.sseu;
 unsigned int subslice;

 GEM_BUG_ON(GRAPHICS_VER(gt->i915) < 11);
 GEM_BUG_ON(hweight8(sseu->slice_mask) > 1);

 /*
 * Although a platform may have subslices, we need to always steer
 * reads to the lowest instance that isn't fused off.  When Render
 * Power Gating is enabled, grabbing forcewake will only power up a
 * single subslice (the "minconfig") if there isn't a real workload
 * that needs to be run; this means that if we steer register reads to
 * one of the higher subslices, we run the risk of reading back 0's or
 * random garbage.
 */

 subslice = __ffs(intel_sseu_get_hsw_subslices(sseu, 0));

 /*
 * If the subslice we picked above also steers us to a valid L3 bank,
 * then we can just rely on the default steering and won't need to
 * worry about explicitly re-steering L3BANK reads later.
 */

 if (gt->info.l3bank_mask & BIT(subslice))
  gt->steering_table[L3BANK] = NULL;

 __add_mcr_wa(gt, wal, 0, subslice);
}

static void
xehp_init_mcr(struct intel_gt *gt, struct i915_wa_list *wal)
{
 const struct sseu_dev_info *sseu = >->info.sseu;
 unsigned long slice, subslice = 0, slice_mask = 0;
 u32 lncf_mask = 0;
 int i;

 /*
 * On Xe_HP the steering increases in complexity. There are now several
 * more units that require steering and we're not guaranteed to be able
 * to find a common setting for all of them. These are:
 * - GSLICE (fusable)
 * - DSS (sub-unit within gslice; fusable)
 * - L3 Bank (fusable)
 * - MSLICE (fusable)
 * - LNCF (sub-unit within mslice; always present if mslice is present)
 *
 * We'll do our default/implicit steering based on GSLICE (in the
 * sliceid field) and DSS (in the subsliceid field).  If we can
 * find overlap between the valid MSLICE and/or LNCF values with
 * a suitable GSLICE, then we can just reuse the default value and
 * skip and explicit steering at runtime.
 *
 * We only need to look for overlap between GSLICE/MSLICE/LNCF to find
 * a valid sliceid value.  DSS steering is the only type of steering
 * that utilizes the 'subsliceid' bits.
 *
 * Also note that, even though the steering domain is called "GSlice"
 * and it is encoded in the register using the gslice format, the spec
 * says that the combined (geometry | compute) fuse should be used to
 * select the steering.
 */


 /* Find the potential gslice candidates */
 slice_mask = intel_slicemask_from_xehp_dssmask(sseu->subslice_mask,
             GEN_DSS_PER_GSLICE);

 /*
 * Find the potential LNCF candidates.  Either LNCF within a valid
 * mslice is fine.
 */

 for_each_set_bit(i, >->info.mslice_mask, GEN12_MAX_MSLICES)
  lncf_mask |= (0x3 << (i * 2));

 /*
 * Are there any sliceid values that work for both GSLICE and LNCF
 * steering?
 */

 if (slice_mask & lncf_mask) {
  slice_mask &= lncf_mask;
  gt->steering_table[LNCF] = NULL;
 }

 /* How about sliceid values that also work for MSLICE steering? */
 if (slice_mask & gt->info.mslice_mask) {
  slice_mask &= gt->info.mslice_mask;
  gt->steering_table[MSLICE] = NULL;
 }

 slice = __ffs(slice_mask);
 subslice = intel_sseu_find_first_xehp_dss(sseu, GEN_DSS_PER_GSLICE, slice) %
  GEN_DSS_PER_GSLICE;

 __add_mcr_wa(gt, wal, slice, subslice);

 /*
 * SQIDI ranges are special because they use different steering
 * registers than everything else we work with.  On XeHP SDV and
 * DG2-G10, any value in the steering registers will work fine since
 * all instances are present, but DG2-G11 only has SQIDI instances at
 * ID's 2 and 3, so we need to steer to one of those.  For simplicity
 * we'll just steer to a hardcoded "2" since that value will work
 * everywhere.
 */

 __set_mcr_steering(wal, MCFG_MCR_SELECTOR, 0, 2);
 __set_mcr_steering(wal, SF_MCR_SELECTOR, 0, 2);

 /*
 * On DG2, GAM registers have a dedicated steering control register
 * and must always be programmed to a hardcoded groupid of "1."
 */

 if (IS_DG2(gt->i915))
  __set_mcr_steering(wal, GAM_MCR_SELECTOR, 1, 0);
}

static void
icl_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = gt->i915;

 icl_wa_init_mcr(gt, wal);

 /* WaModifyGamTlbPartitioning:icl */
 wa_write_clr_set(wal,
    GEN11_GACB_PERF_CTRL,
    GEN11_HASH_CTRL_MASK,
    GEN11_HASH_CTRL_BIT0 | GEN11_HASH_CTRL_BIT4);

 /* Wa_1405766107:icl
 * Formerly known as WaCL2SFHalfMaxAlloc
 */

 wa_write_or(wal,
      GEN11_LSN_UNSLCVC,
      GEN11_LSN_UNSLCVC_GAFS_HALF_SF_MAXALLOC |
      GEN11_LSN_UNSLCVC_GAFS_HALF_CL2_MAXALLOC);

 /* Wa_220166154:icl
 * Formerly known as WaDisCtxReload
 */

 wa_write_or(wal,
      GEN8_GAMW_ECO_DEV_RW_IA,
      GAMW_ECO_DEV_CTX_RELOAD_DISABLE);

 /* Wa_1406463099:icl
 * Formerly known as WaGamTlbPendError
 */

 wa_write_or(wal,
      GAMT_CHKN_BIT_REG,
      GAMT_CHKN_DISABLE_L3_COH_PIPE);

 /*
 * Wa_1408615072:icl,ehl  (vsunit)
 * Wa_1407596294:icl,ehl  (hsunit)
 */

 wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE,
      VSUNIT_CLKGATE_DIS | HSUNIT_CLKGATE_DIS);

 /* Wa_1407352427:icl,ehl */
 wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE2,
      PSDUNIT_CLKGATE_DIS);

 /* Wa_1406680159:icl,ehl */
 wa_mcr_write_or(wal,
   GEN11_SUBSLICE_UNIT_LEVEL_CLKGATE,
   GWUNIT_CLKGATE_DIS);

 /* Wa_1607087056:icl,ehl,jsl */
 if (IS_ICELAKE(i915) ||
  ((IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) &&
  IS_GRAPHICS_STEP(i915, STEP_A0, STEP_B0)))
  wa_write_or(wal,
       GEN11_SLICE_UNIT_LEVEL_CLKGATE,
       L3_CLKGATE_DIS | L3_CR2X_CLKGATE_DIS);

 /*
 * This is not a documented workaround, but rather an optimization
 * to reduce sampler power.
 */

 wa_mcr_write_clr(wal, GEN10_DFR_RATIO_EN_AND_CHICKEN, DFR_DISABLE);
}

/*
 * Though there are per-engine instances of these registers,
 * they retain their value through engine resets and should
 * only be provided on the GT workaround list rather than
 * the engine-specific workaround list.
 */

static void
wa_14011060649(struct intel_gt *gt, struct i915_wa_list *wal)
{
 struct intel_engine_cs *engine;
 int id;

 for_each_engine(engine, gt, id) {
  if (engine->class != VIDEO_DECODE_CLASS ||
      (engine->instance % 2))
   continue;

  wa_write_or(wal, VDBOX_CGCTL3F10(engine->mmio_base),
       IECPUNIT_CLKGATE_DIS);
 }
}

static void
gen12_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 icl_wa_init_mcr(gt, wal);

 /* Wa_14011060649:tgl,rkl,dg1,adl-s,adl-p */
 wa_14011060649(gt, wal);

 /* Wa_14011059788:tgl,rkl,adl-s,dg1,adl-p */
 wa_mcr_write_or(wal, GEN10_DFR_RATIO_EN_AND_CHICKEN, DFR_DISABLE);

 /*
 * Wa_14015795083
 *
 * Firmware on some gen12 platforms locks the MISCCPCTL register,
 * preventing i915 from modifying it for this workaround.  Skip the
 * readback verification for this workaround on debug builds; if the
 * workaround doesn't stick due to firmware behavior, it's not an error
 * that we want CI to flag.
 */

 wa_add(wal, GEN7_MISCCPCTL, GEN12_DOP_CLOCK_GATE_RENDER_ENABLE,
        0, 0, false);
}

static void
dg1_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 gen12_gt_workarounds_init(gt, wal);

 /* Wa_1409420604:dg1 */
 wa_mcr_write_or(wal, SUBSLICE_UNIT_LEVEL_CLKGATE2,
   CPSSUNIT_CLKGATE_DIS);

 /* Wa_1408615072:dg1 */
 /* Empirical testing shows this register is unaffected by engine reset. */
 wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE2, VSUNIT_CLKGATE_DIS_TGL);
}

static void
dg2_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 xehp_init_mcr(gt, wal);

 /* Wa_14011060649:dg2 */
 wa_14011060649(gt, wal);

 if (IS_DG2_G10(gt->i915)) {
  /* Wa_22010523718:dg2 */
  wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE,
       CG3DDISCFEG_CLKGATE_DIS);

  /* Wa_14011006942:dg2 */
  wa_mcr_write_or(wal, GEN11_SUBSLICE_UNIT_LEVEL_CLKGATE,
    DSS_ROUTER_CLKGATE_DIS);
 }

 /* Wa_14014830051:dg2 */
 wa_mcr_write_clr(wal, SARB_CHICKEN1, COMP_CKN_IN);

 /*
 * Wa_14015795083
 * Skip verification for possibly locked register.
 */

 wa_add(wal, GEN7_MISCCPCTL, GEN12_DOP_CLOCK_GATE_RENDER_ENABLE,
        0, 0, false);

 /* Wa_18018781329 */
 wa_mcr_write_or(wal, RENDER_MOD_CTRL, FORCE_MISS_FTLB);
 wa_mcr_write_or(wal, COMP_MOD_CTRL, FORCE_MISS_FTLB);
 wa_mcr_write_or(wal, XEHP_VDBX_MOD_CTRL, FORCE_MISS_FTLB);
 wa_mcr_write_or(wal, XEHP_VEBX_MOD_CTRL, FORCE_MISS_FTLB);

 /* Wa_1509235366:dg2 */
 wa_mcr_write_or(wal, XEHP_GAMCNTRL_CTRL,
   INVALIDATION_BROADCAST_MODE_DIS | GLOBAL_INVALIDATION_MODE);

 /* Wa_14010648519:dg2 */
 wa_mcr_write_or(wal, XEHP_L3NODEARBCFG, XEHP_LNESPARE);
}

static void
xelpg_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 /* Wa_14018575942 / Wa_18018781329 */
 wa_mcr_write_or(wal, RENDER_MOD_CTRL, FORCE_MISS_FTLB);
 wa_mcr_write_or(wal, COMP_MOD_CTRL, FORCE_MISS_FTLB);

 /* Wa_22016670082 */
 wa_write_or(wal, GEN12_SQCNT1, GEN12_STRICT_RAR_ENABLE);

 if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) ||
     IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) {
  /* Wa_14014830051 */
  wa_mcr_write_clr(wal, SARB_CHICKEN1, COMP_CKN_IN);

  /* Wa_14015795083 */
  wa_write_clr(wal, GEN7_MISCCPCTL, GEN12_DOP_CLOCK_GATE_RENDER_ENABLE);
 }

 /*
 * Unlike older platforms, we no longer setup implicit steering here;
 * all MCR accesses are explicitly steered.
 */

 debug_dump_steering(gt);
}

static void
wa_16021867713(struct intel_gt *gt, struct i915_wa_list *wal)
{
 struct intel_engine_cs *engine;
 int id;

 for_each_engine(engine, gt, id)
  if (engine->class == VIDEO_DECODE_CLASS)
   wa_write_or(wal, VDBOX_CGCTL3F1C(engine->mmio_base),
        MFXPIPE_CLKGATE_DIS);
}

static void
xelpmp_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal)
{
 wa_16021867713(gt, wal);

 /*
 * Wa_14018778641
 * Wa_18018781329
 *
 * Note that although these registers are MCR on the primary
 * GT, the media GT's versions are regular singleton registers.
 */

 wa_write_or(wal, XELPMP_GSC_MOD_CTRL, FORCE_MISS_FTLB);

 /*
 * Wa_14018575942
 *
 * Issue is seen on media KPI test running on VDBOX engine
 * especially VP9 encoding WLs
 */

 wa_write_or(wal, XELPMP_VDBX_MOD_CTRL, FORCE_MISS_FTLB);

 /* Wa_22016670082 */
 wa_write_or(wal, GEN12_SQCNT1, GEN12_STRICT_RAR_ENABLE);

 debug_dump_steering(gt);
}

/*
 * The bspec performance guide has recommended MMIO tuning settings.  These
 * aren't truly "workarounds" but we want to program them through the
 * workaround infrastructure to make sure they're (re)applied at the proper
 * times.
 *
 * The programming in this function is for settings that persist through
 * engine resets and also are not part of any engine's register state context.
 * I.e., settings that only need to be re-applied in the event of a full GT
 * reset.
 */

static void gt_tuning_settings(struct intel_gt *gt, struct i915_wa_list *wal)
{
 if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 74))) {
  wa_mcr_write_or(wal, XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS);
  wa_mcr_write_or(wal, XEHP_SQCM, EN_32B_ACCESS);
 }

 if (IS_DG2(gt->i915)) {
  wa_mcr_write_or(wal, XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS);
  wa_mcr_write_or(wal, XEHP_SQCM, EN_32B_ACCESS);
 }
}

static void
gt_init_workarounds(struct intel_gt *gt, struct i915_wa_list *wal)
{
 struct drm_i915_private *i915 = gt->i915;

 gt_tuning_settings(gt, wal);

 if (gt->type == GT_MEDIA) {
  if (MEDIA_VER_FULL(i915) == IP_VER(13, 0))
   xelpmp_gt_workarounds_init(gt, wal);
  else
   MISSING_CASE(MEDIA_VER_FULL(i915));

  return;
 }

 if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 74)))
  xelpg_gt_workarounds_init(gt, wal);
 else if (IS_DG2(i915))
  dg2_gt_workarounds_init(gt, wal);
 else if (IS_DG1(i915))
  dg1_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) == 12)
  gen12_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) == 11)
  icl_gt_workarounds_init(gt, wal);
 else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915))
  cfl_gt_workarounds_init(gt, wal);
 else if (IS_GEMINILAKE(i915))
  glk_gt_workarounds_init(gt, wal);
 else if (IS_KABYLAKE(i915))
  kbl_gt_workarounds_init(gt, wal);
 else if (IS_BROXTON(i915))
  gen9_gt_workarounds_init(gt, wal);
 else if (IS_SKYLAKE(i915))
  skl_gt_workarounds_init(gt, wal);
 else if (IS_HASWELL(i915))
  hsw_gt_workarounds_init(gt, wal);
 else if (IS_VALLEYVIEW(i915))
  vlv_gt_workarounds_init(gt, wal);
 else if (IS_IVYBRIDGE(i915))
  ivb_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) == 6)
  snb_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) == 5)
  ilk_gt_workarounds_init(gt, wal);
 else if (IS_G4X(i915))
  g4x_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) == 4)
  gen4_gt_workarounds_init(gt, wal);
 else if (GRAPHICS_VER(i915) <= 8)
  ;
 else
  MISSING_CASE(GRAPHICS_VER(i915));
}

void intel_gt_init_workarounds(struct intel_gt *gt)
{
 struct i915_wa_list *wal = >->wa_list;

 wa_init_start(wal, gt, "GT""global");
 gt_init_workarounds(gt, wal);
 wa_init_finish(wal);
}

static bool
wa_verify(struct intel_gt *gt, const struct i915_wa *wa, u32 cur,
   const char *name, const char *from)
{
 if ((cur ^ wa->set) & wa->read) {
  gt_err(gt,
         "%s workaround lost on %s! (reg[%x]=0x%x, relevant bits were 0x%x vs expected 0x%x)\n",
         name, from, i915_mmio_reg_offset(wa->reg),
         cur, cur & wa->read, wa->set & wa->read);

  return false;
 }

 return true;
}

static void wa_list_apply(const struct i915_wa_list *wal)
{
 struct intel_gt *gt = wal->gt;
 struct intel_uncore *uncore = gt->uncore;
 enum forcewake_domains fw;
 unsigned long flags;
 struct i915_wa *wa;
 unsigned int i;

 if (!wal->count)
  return;

 fw = wal_get_fw_for_rmw(uncore, wal);

 intel_gt_mcr_lock(gt, &flags);
 spin_lock(&uncore->lock);
 intel_uncore_forcewake_get__locked(uncore, fw);

 for (i = 0, wa = wal->list; i < wal->count; i++, wa++) {
  u32 val, old = 0;

  /* open-coded rmw due to steering */
  if (wa->clr)
   old = wa->is_mcr ?
    intel_gt_mcr_read_any_fw(gt, wa->mcr_reg) :
    intel_uncore_read_fw(uncore, wa->reg);
  val = (old & ~wa->clr) | wa->set;
  if (val != old || !wa->clr) {
   if (wa->is_mcr)
    intel_gt_mcr_multicast_write_fw(gt, wa->mcr_reg, val);
   else
    intel_uncore_write_fw(uncore, wa->reg, val);
  }

  if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) {
   u32 val = wa->is_mcr ?
    intel_gt_mcr_read_any_fw(gt, wa->mcr_reg) :
    intel_uncore_read_fw(uncore, wa->reg);

   wa_verify(gt, wa, val, wal->name, "application");
  }
 }

 intel_uncore_forcewake_put__locked(uncore, fw);
 spin_unlock(&uncore->lock);
 intel_gt_mcr_unlock(gt, flags);
}

void intel_gt_apply_workarounds(struct intel_gt *gt)
{
 wa_list_apply(>->wa_list);
}

static bool wa_list_verify(struct intel_gt *gt,
      const struct i915_wa_list *wal,
      const char *from)
{
 struct intel_uncore *uncore = gt->uncore;
 struct i915_wa *wa;
 enum forcewake_domains fw;
 unsigned long flags;
 unsigned int i;
 bool ok = true;

 fw = wal_get_fw_for_rmw(uncore, wal);

 intel_gt_mcr_lock(gt, &flags);
 spin_lock(&uncore->lock);
 intel_uncore_forcewake_get__locked(uncore, fw);

 for (i = 0, wa = wal->list; i < wal->count; i++, wa++)
  ok &= wa_verify(wal->gt, wa, wa->is_mcr ?
    intel_gt_mcr_read_any_fw(gt, wa->mcr_reg) :
    intel_uncore_read_fw(uncore, wa->reg),
    wal->name, from);

 intel_uncore_forcewake_put__locked(uncore, fw);
 spin_unlock(&uncore->lock);
 intel_gt_mcr_unlock(gt, flags);

 return ok;
}

bool intel_gt_verify_workarounds(struct intel_gt *gt, const char *from)
{
 return wa_list_verify(gt, >->wa_list, from);
}

__maybe_unused
static bool is_nonpriv_flags_valid(u32 flags)
{
 /* Check only valid flag bits are set */
 if (flags & ~RING_FORCE_TO_NONPRIV_MASK_VALID)
  return false;

 /* NB: Only 3 out of 4 enum values are valid for access field */
 if ((flags & RING_FORCE_TO_NONPRIV_ACCESS_MASK) ==
     RING_FORCE_TO_NONPRIV_ACCESS_INVALID)
  return false;

 return true;
}

static void
whitelist_reg_ext(struct i915_wa_list *wal, i915_reg_t reg, u32 flags)
{
 struct i915_wa wa = {
  .reg = reg
 };

 if (GEM_DEBUG_WARN_ON(wal->count >= RING_MAX_NONPRIV_SLOTS))
  return;

 if (GEM_DEBUG_WARN_ON(!is_nonpriv_flags_valid(flags)))
  return;

 wa.reg.reg |= flags;
 _wa_add(wal, &wa);
}

static void
whitelist_mcr_reg_ext(struct i915_wa_list *wal, i915_mcr_reg_t reg, u32 flags)
{
 struct i915_wa wa = {
  .mcr_reg = reg,
  .is_mcr = 1,
 };

 if (GEM_DEBUG_WARN_ON(wal->count >= RING_MAX_NONPRIV_SLOTS))
  return;

 if (GEM_DEBUG_WARN_ON(!is_nonpriv_flags_valid(flags)))
  return;

 wa.mcr_reg.reg |= flags;
 _wa_add(wal, &wa);
}

static void
whitelist_reg(struct i915_wa_list *wal, i915_reg_t reg)
{
 whitelist_reg_ext(wal, reg, RING_FORCE_TO_NONPRIV_ACCESS_RW);
}

static void
whitelist_mcr_reg(struct i915_wa_list *wal, i915_mcr_reg_t reg)
{
 whitelist_mcr_reg_ext(wal, reg, RING_FORCE_TO_NONPRIV_ACCESS_RW);
}

static void gen9_whitelist_build(struct i915_wa_list *w)
{
 /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt,glk,cfl */
 whitelist_reg(w, GEN9_CTX_PREEMPT_REG);

 /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,[cnl] */
 whitelist_reg(w, GEN8_CS_CHICKEN1);

 /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl,glk,cfl */
 whitelist_reg(w, GEN8_HDC_CHICKEN1);

 /* WaSendPushConstantsFromMMIO:skl,bxt */
 whitelist_reg(w, COMMON_SLICE_CHICKEN2);
}

static void skl_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 if (engine->class != RENDER_CLASS)
  return;

 gen9_whitelist_build(w);

 /* WaDisableLSQCROPERFforOCL:skl */
 whitelist_mcr_reg(w, GEN8_L3SQCREG4);
}

static void bxt_whitelist_build(struct intel_engine_cs *engine)
{
 if (engine->class != RENDER_CLASS)
  return;

 gen9_whitelist_build(&engine->whitelist);
}

static void kbl_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 if (engine->class != RENDER_CLASS)
  return;

 gen9_whitelist_build(w);

 /* WaDisableLSQCROPERFforOCL:kbl */
 whitelist_mcr_reg(w, GEN8_L3SQCREG4);
}

static void glk_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 if (engine->class != RENDER_CLASS)
  return;

 gen9_whitelist_build(w);

 /* WA #0862: Userspace has to set "Barrier Mode" to avoid hangs. */
 whitelist_reg(w, GEN9_SLICE_COMMON_ECO_CHICKEN1);
}

static void cfl_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 if (engine->class != RENDER_CLASS)
  return;

 gen9_whitelist_build(w);

 /*
 * WaAllowPMDepthAndInvocationCountAccessFromUMD:cfl,whl,cml,aml
 *
 * This covers 4 register which are next to one another :
 *   - PS_INVOCATION_COUNT
 *   - PS_INVOCATION_COUNT_UDW
 *   - PS_DEPTH_COUNT
 *   - PS_DEPTH_COUNT_UDW
 */

 whitelist_reg_ext(w, PS_INVOCATION_COUNT,
     RING_FORCE_TO_NONPRIV_ACCESS_RD |
     RING_FORCE_TO_NONPRIV_RANGE_4);
}

static void allow_read_ctx_timestamp(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 if (engine->class != RENDER_CLASS)
  whitelist_reg_ext(w,
      RING_CTX_TIMESTAMP(engine->mmio_base),
      RING_FORCE_TO_NONPRIV_ACCESS_RD);
}

static void cml_whitelist_build(struct intel_engine_cs *engine)
{
 allow_read_ctx_timestamp(engine);

 cfl_whitelist_build(engine);
}

static void icl_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 allow_read_ctx_timestamp(engine);

 switch (engine->class) {
 case RENDER_CLASS:
  /* WaAllowUMDToModifyHalfSliceChicken7:icl */
  whitelist_mcr_reg(w, GEN9_HALF_SLICE_CHICKEN7);

  /* WaAllowUMDToModifySamplerMode:icl */
  whitelist_mcr_reg(w, GEN10_SAMPLER_MODE);

  /* WaEnableStateCacheRedirectToCS:icl */
  whitelist_reg(w, GEN9_SLICE_COMMON_ECO_CHICKEN1);

  /*
 * WaAllowPMDepthAndInvocationCountAccessFromUMD:icl
 *
 * This covers 4 register which are next to one another :
 *   - PS_INVOCATION_COUNT
 *   - PS_INVOCATION_COUNT_UDW
 *   - PS_DEPTH_COUNT
 *   - PS_DEPTH_COUNT_UDW
 */

  whitelist_reg_ext(w, PS_INVOCATION_COUNT,
      RING_FORCE_TO_NONPRIV_ACCESS_RD |
      RING_FORCE_TO_NONPRIV_RANGE_4);
  break;

 case VIDEO_DECODE_CLASS:
  /* hucStatusRegOffset */
  whitelist_reg_ext(w, _MMIO(0x2000 + engine->mmio_base),
      RING_FORCE_TO_NONPRIV_ACCESS_RD);
  /* hucUKernelHdrInfoRegOffset */
  whitelist_reg_ext(w, _MMIO(0x2014 + engine->mmio_base),
      RING_FORCE_TO_NONPRIV_ACCESS_RD);
  /* hucStatus2RegOffset */
  whitelist_reg_ext(w, _MMIO(0x23B0 + engine->mmio_base),
      RING_FORCE_TO_NONPRIV_ACCESS_RD);
  break;

 default:
  break;
 }
}

static void tgl_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 allow_read_ctx_timestamp(engine);

 switch (engine->class) {
 case RENDER_CLASS:
  /*
 * WaAllowPMDepthAndInvocationCountAccessFromUMD:tgl
 * Wa_1408556865:tgl
 *
 * This covers 4 registers which are next to one another :
 *   - PS_INVOCATION_COUNT
 *   - PS_INVOCATION_COUNT_UDW
 *   - PS_DEPTH_COUNT
 *   - PS_DEPTH_COUNT_UDW
 */

  whitelist_reg_ext(w, PS_INVOCATION_COUNT,
      RING_FORCE_TO_NONPRIV_ACCESS_RD |
      RING_FORCE_TO_NONPRIV_RANGE_4);

  /*
 * Wa_1808121037:tgl
 * Wa_14012131227:dg1
 * Wa_1508744258:tgl,rkl,dg1,adl-s,adl-p
 */

  whitelist_reg(w, GEN7_COMMON_SLICE_CHICKEN1);

  /* Wa_1806527549:tgl */
  whitelist_reg(w, HIZ_CHICKEN);

  /* Required by recommended tuning setting (not a workaround) */
  whitelist_reg(w, GEN11_COMMON_SLICE_CHICKEN3);

  break;
 default:
  break;
 }
}

static void dg2_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 switch (engine->class) {
 case RENDER_CLASS:
  /* Required by recommended tuning setting (not a workaround) */
  whitelist_mcr_reg(w, XEHP_COMMON_SLICE_CHICKEN3);
  whitelist_reg(w, GEN7_COMMON_SLICE_CHICKEN1);
  break;
 default:
  break;
 }
}

static void xelpg_whitelist_build(struct intel_engine_cs *engine)
{
 struct i915_wa_list *w = &engine->whitelist;

 switch (engine->class) {
 case RENDER_CLASS:
  /* Required by recommended tuning setting (not a workaround) */
  whitelist_mcr_reg(w, XEHP_COMMON_SLICE_CHICKEN3);
  whitelist_reg(w, GEN7_COMMON_SLICE_CHICKEN1);
  break;
 default:
  break;
 }
}

void intel_engine_init_whitelist(struct intel_engine_cs *engine)
{
 struct drm_i915_private *i915 = engine->i915;
 struct i915_wa_list *w = &engine->whitelist;

 wa_init_start(w, engine->gt, "whitelist", engine->name);

 if (engine->gt->type == GT_MEDIA)
  ; /* none yet */
 else if (IS_GFX_GT_IP_RANGE(engine->gt, IP_VER(12, 70), IP_VER(12, 74)))
  xelpg_whitelist_build(engine);
 else if (IS_DG2(i915))
  dg2_whitelist_build(engine);
 else if (GRAPHICS_VER(i915) == 12)
  tgl_whitelist_build(engine);
 else if (GRAPHICS_VER(i915) == 11)
  icl_whitelist_build(engine);
 else if (IS_COMETLAKE(i915))
  cml_whitelist_build(engine);
 else if (IS_COFFEELAKE(i915))
  cfl_whitelist_build(engine);
 else if (IS_GEMINILAKE(i915))
  glk_whitelist_build(engine);
 else if (IS_KABYLAKE(i915))
  kbl_whitelist_build(engine);
 else if (IS_BROXTON(i915))
  bxt_whitelist_build(engine);
 else if (IS_SKYLAKE(i915))
  skl_whitelist_build(engine);
 else if (GRAPHICS_VER(i915) <= 8)
  ;
 else
  MISSING_CASE(GRAPHICS_VER(i915));

 wa_init_finish(w);
}

void intel_engine_apply_whitelist(struct intel_engine_cs *engine)
{
 const struct i915_wa_list *wal = &engine->whitelist;
 struct intel_uncore *uncore = engine->uncore;
 const u32 base = engine->mmio_base;
 struct i915_wa *wa;
 unsigned int i;

 if (!wal->count)
  return;

 for (i = 0, wa = wal->list; i < wal->count; i++, wa++)
  intel_uncore_write(uncore,
       RING_FORCE_TO_NONPRIV(base, i),
       i915_mmio_reg_offset(wa->reg));

 /* And clear the rest just in case of garbage */
 for (; i < RING_MAX_NONPRIV_SLOTS; i++)
  intel_uncore_write(uncore,
       RING_FORCE_TO_NONPRIV(base, i),
       i915_mmio_reg_offset(RING_NOPID(base)));
}

/*
 * engine_fake_wa_init(), a place holder to program the registers
 * which are not part of an official workaround defined by the
 * hardware team.
 * Adding programming of those register inside workaround will
 * allow utilizing wa framework to proper application and verification.
 */

static void
engine_fake_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal)
{
 u8 mocs_w, mocs_r;

 /*
 * RING_CMD_CCTL specifies the default MOCS entry that will be used
 * by the command streamer when executing commands that don't have
 * a way to explicitly specify a MOCS setting.  The default should
 * usually reference whichever MOCS entry corresponds to uncached
 * behavior, although use of a WB cached entry is recommended by the
 * spec in certain circumstances on specific platforms.
 */

 if (GRAPHICS_VER(engine->i915) >= 12) {
  mocs_r = engine->gt->mocs.uc_index;
  mocs_w = engine->gt->mocs.uc_index;

  if (HAS_L3_CCS_READ(engine->i915) &&
      engine->class == COMPUTE_CLASS) {
   mocs_r = engine->gt->mocs.wb_index;

   /*
 * Even on the few platforms where MOCS 0 is a
 * legitimate table entry, it's never the correct
 * setting to use here; we can assume the MOCS init
 * just forgot to initialize wb_index.
 */

   drm_WARN_ON(&engine->i915->drm, mocs_r == 0);
  }

  wa_masked_field_set(wal,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=92 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.