2019-06-21 14:07:41 +07:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/*
|
|
|
|
* Copyright © 2019 Intel Corporation
|
|
|
|
*/
|
|
|
|
|
2019-12-22 21:40:46 +07:00
|
|
|
#include "debugfs_gt.h"
|
2019-06-21 14:07:42 +07:00
|
|
|
#include "i915_drv.h"
|
2019-12-22 19:07:52 +07:00
|
|
|
#include "intel_context.h"
|
2019-06-21 14:07:41 +07:00
|
|
|
#include "intel_gt.h"
|
2019-06-21 14:07:43 +07:00
|
|
|
#include "intel_gt_pm.h"
|
2019-10-04 20:40:06 +07:00
|
|
|
#include "intel_gt_requests.h"
|
2019-09-10 21:38:20 +07:00
|
|
|
#include "intel_mocs.h"
|
2019-09-27 18:08:49 +07:00
|
|
|
#include "intel_rc6.h"
|
2019-12-22 19:07:52 +07:00
|
|
|
#include "intel_renderstate.h"
|
2019-10-25 04:16:41 +07:00
|
|
|
#include "intel_rps.h"
|
2019-06-21 14:07:44 +07:00
|
|
|
#include "intel_uncore.h"
|
2019-09-05 18:14:03 +07:00
|
|
|
#include "intel_pm.h"
|
2019-06-21 14:07:41 +07:00
|
|
|
|
2019-06-21 14:07:42 +07:00
|
|
|
void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
|
2019-06-21 14:07:41 +07:00
|
|
|
{
|
2019-06-21 14:07:42 +07:00
|
|
|
gt->i915 = i915;
|
|
|
|
gt->uncore = &i915->uncore;
|
|
|
|
|
2019-08-11 21:28:00 +07:00
|
|
|
spin_lock_init(>->irq_lock);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(>->closed_vma);
|
2019-06-21 14:07:41 +07:00
|
|
|
spin_lock_init(>->closed_lock);
|
2019-06-21 14:07:43 +07:00
|
|
|
|
2019-07-13 02:29:53 +07:00
|
|
|
intel_gt_init_reset(gt);
|
2019-10-04 20:40:06 +07:00
|
|
|
intel_gt_init_requests(gt);
|
2019-11-01 20:04:06 +07:00
|
|
|
intel_gt_init_timelines(gt);
|
2019-06-21 14:07:43 +07:00
|
|
|
intel_gt_pm_init_early(gt);
|
2019-10-30 17:38:23 +07:00
|
|
|
|
|
|
|
intel_rps_init_early(>->rps);
|
2019-08-01 07:57:08 +07:00
|
|
|
intel_uc_init_early(>->uc);
|
2019-06-21 14:07:41 +07:00
|
|
|
}
|
2019-06-21 14:07:44 +07:00
|
|
|
|
2019-11-01 21:10:06 +07:00
|
|
|
void intel_gt_init_hw_early(struct intel_gt *gt, struct i915_ggtt *ggtt)
|
2019-06-21 14:08:06 +07:00
|
|
|
{
|
2019-11-01 21:10:06 +07:00
|
|
|
gt->ggtt = ggtt;
|
2019-06-21 14:08:06 +07:00
|
|
|
}
|
|
|
|
|
2019-09-10 21:38:20 +07:00
|
|
|
static void init_unused_ring(struct intel_gt *gt, u32 base)
|
|
|
|
{
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
|
|
|
|
intel_uncore_write(uncore, RING_CTL(base), 0);
|
|
|
|
intel_uncore_write(uncore, RING_HEAD(base), 0);
|
|
|
|
intel_uncore_write(uncore, RING_TAIL(base), 0);
|
|
|
|
intel_uncore_write(uncore, RING_START(base), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_unused_rings(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
|
|
|
|
if (IS_I830(i915)) {
|
|
|
|
init_unused_ring(gt, PRB1_BASE);
|
|
|
|
init_unused_ring(gt, SRB0_BASE);
|
|
|
|
init_unused_ring(gt, SRB1_BASE);
|
|
|
|
init_unused_ring(gt, SRB2_BASE);
|
|
|
|
init_unused_ring(gt, SRB3_BASE);
|
|
|
|
} else if (IS_GEN(i915, 2)) {
|
|
|
|
init_unused_ring(gt, SRB0_BASE);
|
|
|
|
init_unused_ring(gt, SRB1_BASE);
|
|
|
|
} else if (IS_GEN(i915, 3)) {
|
|
|
|
init_unused_ring(gt, PRB1_BASE);
|
|
|
|
init_unused_ring(gt, PRB2_BASE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int intel_gt_init_hw(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
gt->last_init_time = ktime_get();
|
|
|
|
|
|
|
|
/* Double layer security blanket, see i915_gem_init() */
|
|
|
|
intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL);
|
|
|
|
|
|
|
|
if (HAS_EDRAM(i915) && INTEL_GEN(i915) < 9)
|
|
|
|
intel_uncore_rmw(uncore, HSW_IDICR, 0, IDIHASHMSK(0xf));
|
|
|
|
|
|
|
|
if (IS_HASWELL(i915))
|
|
|
|
intel_uncore_write(uncore,
|
|
|
|
MI_PREDICATE_RESULT_2,
|
|
|
|
IS_HSW_GT3(i915) ?
|
|
|
|
LOWER_SLICE_ENABLED : LOWER_SLICE_DISABLED);
|
|
|
|
|
|
|
|
/* Apply the GT workarounds... */
|
|
|
|
intel_gt_apply_workarounds(gt);
|
|
|
|
/* ...and determine whether they are sticking. */
|
|
|
|
intel_gt_verify_workarounds(gt, "init");
|
|
|
|
|
|
|
|
intel_gt_init_swizzling(gt);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At least 830 can leave some of the unused rings
|
|
|
|
* "active" (ie. head != tail) after resume which
|
|
|
|
* will prevent c3 entry. Makes sure all unused rings
|
|
|
|
* are totally idle.
|
|
|
|
*/
|
|
|
|
init_unused_rings(gt);
|
|
|
|
|
|
|
|
ret = i915_ppgtt_init_hw(gt);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Enabling PPGTT failed (%d)\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We can't enable contexts until all firmware is loaded */
|
|
|
|
ret = intel_uc_init_hw(>->uc);
|
|
|
|
if (ret) {
|
|
|
|
i915_probe_error(i915, "Enabling uc failed (%d)\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
intel_mocs_init(gt);
|
|
|
|
|
|
|
|
out:
|
|
|
|
intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:07:44 +07:00
|
|
|
static void rmw_set(struct intel_uncore *uncore, i915_reg_t reg, u32 set)
|
|
|
|
{
|
|
|
|
intel_uncore_rmw(uncore, reg, 0, set);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rmw_clear(struct intel_uncore *uncore, i915_reg_t reg, u32 clr)
|
|
|
|
{
|
|
|
|
intel_uncore_rmw(uncore, reg, clr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_register(struct intel_uncore *uncore, i915_reg_t reg)
|
|
|
|
{
|
|
|
|
intel_uncore_rmw(uncore, reg, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gen8_clear_engine_error_register(struct intel_engine_cs *engine)
|
|
|
|
{
|
|
|
|
GEN6_RING_FAULT_REG_RMW(engine, RING_FAULT_VALID, 0);
|
|
|
|
GEN6_RING_FAULT_REG_POSTING_READ(engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
intel_gt_clear_error_registers(struct intel_gt *gt,
|
|
|
|
intel_engine_mask_t engine_mask)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
u32 eir;
|
|
|
|
|
|
|
|
if (!IS_GEN(i915, 2))
|
|
|
|
clear_register(uncore, PGTBL_ER);
|
|
|
|
|
|
|
|
if (INTEL_GEN(i915) < 4)
|
|
|
|
clear_register(uncore, IPEIR(RENDER_RING_BASE));
|
|
|
|
else
|
|
|
|
clear_register(uncore, IPEIR_I965);
|
|
|
|
|
|
|
|
clear_register(uncore, EIR);
|
|
|
|
eir = intel_uncore_read(uncore, EIR);
|
|
|
|
if (eir) {
|
|
|
|
/*
|
|
|
|
* some errors might have become stuck,
|
|
|
|
* mask them.
|
|
|
|
*/
|
|
|
|
DRM_DEBUG_DRIVER("EIR stuck: 0x%08x, masking\n", eir);
|
|
|
|
rmw_set(uncore, EMR, eir);
|
|
|
|
intel_uncore_write(uncore, GEN2_IIR,
|
|
|
|
I915_MASTER_ERROR_INTERRUPT);
|
|
|
|
}
|
|
|
|
|
2019-07-31 01:04:03 +07:00
|
|
|
if (INTEL_GEN(i915) >= 12) {
|
|
|
|
rmw_clear(uncore, GEN12_RING_FAULT_REG, RING_FAULT_VALID);
|
|
|
|
intel_uncore_posting_read(uncore, GEN12_RING_FAULT_REG);
|
|
|
|
} else if (INTEL_GEN(i915) >= 8) {
|
2019-06-21 14:07:44 +07:00
|
|
|
rmw_clear(uncore, GEN8_RING_FAULT_REG, RING_FAULT_VALID);
|
|
|
|
intel_uncore_posting_read(uncore, GEN8_RING_FAULT_REG);
|
|
|
|
} else if (INTEL_GEN(i915) >= 6) {
|
|
|
|
struct intel_engine_cs *engine;
|
|
|
|
enum intel_engine_id id;
|
|
|
|
|
2019-10-17 23:18:52 +07:00
|
|
|
for_each_engine_masked(engine, gt, engine_mask, id)
|
2019-06-21 14:07:44 +07:00
|
|
|
gen8_clear_engine_error_register(engine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gen6_check_faults(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct intel_engine_cs *engine;
|
|
|
|
enum intel_engine_id id;
|
|
|
|
u32 fault;
|
|
|
|
|
2019-10-17 16:45:00 +07:00
|
|
|
for_each_engine(engine, gt, id) {
|
2019-06-21 14:07:44 +07:00
|
|
|
fault = GEN6_RING_FAULT_REG_READ(engine);
|
|
|
|
if (fault & RING_FAULT_VALID) {
|
|
|
|
DRM_DEBUG_DRIVER("Unexpected fault\n"
|
|
|
|
"\tAddr: 0x%08lx\n"
|
|
|
|
"\tAddress space: %s\n"
|
|
|
|
"\tSource ID: %d\n"
|
|
|
|
"\tType: %d\n",
|
|
|
|
fault & PAGE_MASK,
|
|
|
|
fault & RING_FAULT_GTTSEL_MASK ?
|
|
|
|
"GGTT" : "PPGTT",
|
|
|
|
RING_FAULT_SRCID(fault),
|
|
|
|
RING_FAULT_FAULT_TYPE(fault));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gen8_check_faults(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
2019-07-31 01:04:03 +07:00
|
|
|
i915_reg_t fault_reg, fault_data0_reg, fault_data1_reg;
|
|
|
|
u32 fault;
|
|
|
|
|
|
|
|
if (INTEL_GEN(gt->i915) >= 12) {
|
|
|
|
fault_reg = GEN12_RING_FAULT_REG;
|
|
|
|
fault_data0_reg = GEN12_FAULT_TLB_DATA0;
|
|
|
|
fault_data1_reg = GEN12_FAULT_TLB_DATA1;
|
|
|
|
} else {
|
|
|
|
fault_reg = GEN8_RING_FAULT_REG;
|
|
|
|
fault_data0_reg = GEN8_FAULT_TLB_DATA0;
|
|
|
|
fault_data1_reg = GEN8_FAULT_TLB_DATA1;
|
|
|
|
}
|
2019-06-21 14:07:44 +07:00
|
|
|
|
2019-07-31 01:04:03 +07:00
|
|
|
fault = intel_uncore_read(uncore, fault_reg);
|
2019-06-21 14:07:44 +07:00
|
|
|
if (fault & RING_FAULT_VALID) {
|
|
|
|
u32 fault_data0, fault_data1;
|
|
|
|
u64 fault_addr;
|
|
|
|
|
2019-07-31 01:04:03 +07:00
|
|
|
fault_data0 = intel_uncore_read(uncore, fault_data0_reg);
|
|
|
|
fault_data1 = intel_uncore_read(uncore, fault_data1_reg);
|
|
|
|
|
2019-06-21 14:07:44 +07:00
|
|
|
fault_addr = ((u64)(fault_data1 & FAULT_VA_HIGH_BITS) << 44) |
|
|
|
|
((u64)fault_data0 << 12);
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("Unexpected fault\n"
|
|
|
|
"\tAddr: 0x%08x_%08x\n"
|
|
|
|
"\tAddress space: %s\n"
|
|
|
|
"\tEngine ID: %d\n"
|
|
|
|
"\tSource ID: %d\n"
|
|
|
|
"\tType: %d\n",
|
|
|
|
upper_32_bits(fault_addr),
|
|
|
|
lower_32_bits(fault_addr),
|
|
|
|
fault_data1 & FAULT_GTT_SEL ? "GGTT" : "PPGTT",
|
|
|
|
GEN8_RING_FAULT_ENGINE_ID(fault),
|
|
|
|
RING_FAULT_SRCID(fault),
|
|
|
|
RING_FAULT_FAULT_TYPE(fault));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void intel_gt_check_and_clear_faults(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
|
|
|
|
/* From GEN8 onwards we only have one 'All Engine Fault Register' */
|
|
|
|
if (INTEL_GEN(i915) >= 8)
|
|
|
|
gen8_check_faults(gt);
|
|
|
|
else if (INTEL_GEN(i915) >= 6)
|
|
|
|
gen6_check_faults(gt);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
intel_gt_clear_error_registers(gt, ALL_ENGINES);
|
|
|
|
}
|
2019-06-21 14:08:01 +07:00
|
|
|
|
|
|
|
void intel_gt_flush_ggtt_writes(struct intel_gt *gt)
|
|
|
|
{
|
2019-10-07 22:45:31 +07:00
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
2019-06-21 14:08:01 +07:00
|
|
|
intel_wakeref_t wakeref;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No actual flushing is required for the GTT write domain for reads
|
|
|
|
* from the GTT domain. Writes to it "immediately" go to main memory
|
|
|
|
* as far as we know, so there's no chipset flush. It also doesn't
|
|
|
|
* land in the GPU render cache.
|
|
|
|
*
|
|
|
|
* However, we do have to enforce the order so that all writes through
|
|
|
|
* the GTT land before any writes to the device, such as updates to
|
|
|
|
* the GATT itself.
|
|
|
|
*
|
|
|
|
* We also have to wait a bit for the writes to land from the GTT.
|
|
|
|
* An uncached read (i.e. mmio) seems to be ideal for the round-trip
|
|
|
|
* timing. This issue has only been observed when switching quickly
|
|
|
|
* between GTT writes and CPU reads from inside the kernel on recent hw,
|
|
|
|
* and it appears to only affect discrete GTT blocks (i.e. on LLC
|
|
|
|
* system agents we cannot reproduce this behaviour, until Cannonlake
|
|
|
|
* that was!).
|
|
|
|
*/
|
|
|
|
|
|
|
|
wmb();
|
|
|
|
|
2019-10-07 22:45:31 +07:00
|
|
|
if (INTEL_INFO(gt->i915)->has_coherent_ggtt)
|
2019-06-21 14:08:01 +07:00
|
|
|
return;
|
|
|
|
|
2019-06-21 14:08:02 +07:00
|
|
|
intel_gt_chipset_flush(gt);
|
2019-06-21 14:08:01 +07:00
|
|
|
|
2019-11-19 01:49:33 +07:00
|
|
|
with_intel_runtime_pm_if_in_use(uncore->rpm, wakeref) {
|
drm/i915: Pull i915_vma_pin under the vm->mutex
Replace the struct_mutex requirement for pinning the i915_vma with the
local vm->mutex instead. Note that the vm->mutex is tainted by the
shrinker (we require unbinding from inside fs-reclaim) and so we cannot
allocate while holding that mutex. Instead we have to preallocate
workers to do allocate and apply the PTE updates after we have we
reserved their slot in the drm_mm (using fences to order the PTE writes
with the GPU work and with later unbind).
In adding the asynchronous vma binding, one subtle requirement is to
avoid coupling the binding fence into the backing object->resv. That is
the asynchronous binding only applies to the vma timeline itself and not
to the pages as that is a more global timeline (the binding of one vma
does not need to be ordered with another vma, nor does the implicit GEM
fencing depend on a vma, only on writes to the backing store). Keeping
the vma binding distinct from the backing store timelines is verified by
a number of async gem_exec_fence and gem_exec_schedule tests. The way we
do this is quite simple, we keep the fence for the vma binding separate
and only wait on it as required, and never add it to the obj->resv
itself.
Another consequence in reducing the locking around the vma is the
destruction of the vma is no longer globally serialised by struct_mutex.
A natural solution would be to add a kref to i915_vma, but that requires
decoupling the reference cycles, possibly by introducing a new
i915_mm_pages object that is own by both obj->mm and vma->pages.
However, we have not taken that route due to the overshadowing lmem/ttm
discussions, and instead play a series of complicated games with
trylocks to (hopefully) ensure that only one destruction path is called!
v2: Add some commentary, and some helpers to reduce patch churn.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-4-chris@chris-wilson.co.uk
2019-10-04 20:39:58 +07:00
|
|
|
unsigned long flags;
|
2019-06-21 14:08:01 +07:00
|
|
|
|
drm/i915: Pull i915_vma_pin under the vm->mutex
Replace the struct_mutex requirement for pinning the i915_vma with the
local vm->mutex instead. Note that the vm->mutex is tainted by the
shrinker (we require unbinding from inside fs-reclaim) and so we cannot
allocate while holding that mutex. Instead we have to preallocate
workers to do allocate and apply the PTE updates after we have we
reserved their slot in the drm_mm (using fences to order the PTE writes
with the GPU work and with later unbind).
In adding the asynchronous vma binding, one subtle requirement is to
avoid coupling the binding fence into the backing object->resv. That is
the asynchronous binding only applies to the vma timeline itself and not
to the pages as that is a more global timeline (the binding of one vma
does not need to be ordered with another vma, nor does the implicit GEM
fencing depend on a vma, only on writes to the backing store). Keeping
the vma binding distinct from the backing store timelines is verified by
a number of async gem_exec_fence and gem_exec_schedule tests. The way we
do this is quite simple, we keep the fence for the vma binding separate
and only wait on it as required, and never add it to the obj->resv
itself.
Another consequence in reducing the locking around the vma is the
destruction of the vma is no longer globally serialised by struct_mutex.
A natural solution would be to add a kref to i915_vma, but that requires
decoupling the reference cycles, possibly by introducing a new
i915_mm_pages object that is own by both obj->mm and vma->pages.
However, we have not taken that route due to the overshadowing lmem/ttm
discussions, and instead play a series of complicated games with
trylocks to (hopefully) ensure that only one destruction path is called!
v2: Add some commentary, and some helpers to reduce patch churn.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-4-chris@chris-wilson.co.uk
2019-10-04 20:39:58 +07:00
|
|
|
spin_lock_irqsave(&uncore->lock, flags);
|
2019-06-21 14:08:01 +07:00
|
|
|
intel_uncore_posting_read_fw(uncore,
|
|
|
|
RING_HEAD(RENDER_RING_BASE));
|
drm/i915: Pull i915_vma_pin under the vm->mutex
Replace the struct_mutex requirement for pinning the i915_vma with the
local vm->mutex instead. Note that the vm->mutex is tainted by the
shrinker (we require unbinding from inside fs-reclaim) and so we cannot
allocate while holding that mutex. Instead we have to preallocate
workers to do allocate and apply the PTE updates after we have we
reserved their slot in the drm_mm (using fences to order the PTE writes
with the GPU work and with later unbind).
In adding the asynchronous vma binding, one subtle requirement is to
avoid coupling the binding fence into the backing object->resv. That is
the asynchronous binding only applies to the vma timeline itself and not
to the pages as that is a more global timeline (the binding of one vma
does not need to be ordered with another vma, nor does the implicit GEM
fencing depend on a vma, only on writes to the backing store). Keeping
the vma binding distinct from the backing store timelines is verified by
a number of async gem_exec_fence and gem_exec_schedule tests. The way we
do this is quite simple, we keep the fence for the vma binding separate
and only wait on it as required, and never add it to the obj->resv
itself.
Another consequence in reducing the locking around the vma is the
destruction of the vma is no longer globally serialised by struct_mutex.
A natural solution would be to add a kref to i915_vma, but that requires
decoupling the reference cycles, possibly by introducing a new
i915_mm_pages object that is own by both obj->mm and vma->pages.
However, we have not taken that route due to the overshadowing lmem/ttm
discussions, and instead play a series of complicated games with
trylocks to (hopefully) ensure that only one destruction path is called!
v2: Add some commentary, and some helpers to reduce patch churn.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-4-chris@chris-wilson.co.uk
2019-10-04 20:39:58 +07:00
|
|
|
spin_unlock_irqrestore(&uncore->lock, flags);
|
2019-06-21 14:08:01 +07:00
|
|
|
}
|
|
|
|
}
|
2019-06-21 14:08:02 +07:00
|
|
|
|
|
|
|
void intel_gt_chipset_flush(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
wmb();
|
|
|
|
if (INTEL_GEN(gt->i915) < 6)
|
|
|
|
intel_gtt_chipset_flush();
|
|
|
|
}
|
2019-06-21 14:08:11 +07:00
|
|
|
|
2019-09-05 18:14:03 +07:00
|
|
|
void intel_gt_driver_register(struct intel_gt *gt)
|
|
|
|
{
|
2019-10-25 04:16:41 +07:00
|
|
|
intel_rps_driver_register(>->rps);
|
2019-12-22 21:40:46 +07:00
|
|
|
|
|
|
|
debugfs_gt_register(gt);
|
2019-09-05 18:14:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int intel_gt_init_scratch(struct intel_gt *gt, unsigned int size)
|
2019-06-21 14:08:11 +07:00
|
|
|
{
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
struct drm_i915_gem_object *obj;
|
|
|
|
struct i915_vma *vma;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
obj = i915_gem_object_create_stolen(i915, size);
|
2019-10-05 00:04:32 +07:00
|
|
|
if (IS_ERR(obj))
|
2019-06-21 14:08:11 +07:00
|
|
|
obj = i915_gem_object_create_internal(i915, size);
|
|
|
|
if (IS_ERR(obj)) {
|
|
|
|
DRM_ERROR("Failed to allocate scratch page\n");
|
|
|
|
return PTR_ERR(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
vma = i915_vma_instance(obj, >->ggtt->vm, NULL);
|
|
|
|
if (IS_ERR(vma)) {
|
|
|
|
ret = PTR_ERR(vma);
|
|
|
|
goto err_unref;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
|
|
|
|
if (ret)
|
|
|
|
goto err_unref;
|
|
|
|
|
2019-08-03 04:21:36 +07:00
|
|
|
gt->scratch = i915_vma_make_unshrinkable(vma);
|
|
|
|
|
2019-06-21 14:08:11 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_unref:
|
|
|
|
i915_gem_object_put(obj);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:14:03 +07:00
|
|
|
static void intel_gt_fini_scratch(struct intel_gt *gt)
|
2019-06-21 14:08:11 +07:00
|
|
|
{
|
|
|
|
i915_vma_unpin_and_release(>->scratch, 0);
|
|
|
|
}
|
2019-07-13 02:29:53 +07:00
|
|
|
|
2019-12-21 23:03:24 +07:00
|
|
|
static struct i915_address_space *kernel_vm(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
if (INTEL_PPGTT(gt->i915) > INTEL_PPGTT_ALIASING)
|
2020-01-07 20:40:09 +07:00
|
|
|
return &i915_ppgtt_create(gt)->vm;
|
2019-12-21 23:03:24 +07:00
|
|
|
else
|
|
|
|
return i915_vm_get(>->ggtt->vm);
|
|
|
|
}
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
static int __intel_context_flush_retire(struct intel_context *ce)
|
|
|
|
{
|
|
|
|
struct intel_timeline *tl;
|
|
|
|
|
|
|
|
tl = intel_context_timeline_lock(ce);
|
|
|
|
if (IS_ERR(tl))
|
|
|
|
return PTR_ERR(tl);
|
|
|
|
|
|
|
|
intel_context_timeline_unlock(tl);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __engines_record_defaults(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct i915_request *requests[I915_NUM_ENGINES] = {};
|
|
|
|
struct intel_engine_cs *engine;
|
|
|
|
enum intel_engine_id id;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As we reset the gpu during very early sanitisation, the current
|
|
|
|
* register state on the GPU should reflect its defaults values.
|
|
|
|
* We load a context onto the hw (with restore-inhibit), then switch
|
|
|
|
* over to a second context to save that default register state. We
|
|
|
|
* can then prime every new context with that state so they all start
|
|
|
|
* from the same default HW values.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for_each_engine(engine, gt, id) {
|
|
|
|
struct intel_renderstate so;
|
|
|
|
struct intel_context *ce;
|
|
|
|
struct i915_request *rq;
|
|
|
|
|
2019-12-26 06:07:03 +07:00
|
|
|
/* We must be able to switch to something! */
|
|
|
|
GEM_BUG_ON(!engine->kernel_context);
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
err = intel_renderstate_init(&so, engine);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ce = intel_context_create(engine);
|
|
|
|
if (IS_ERR(ce)) {
|
|
|
|
err = PTR_ERR(ce);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rq = intel_context_create_request(ce);
|
|
|
|
if (IS_ERR(rq)) {
|
|
|
|
err = PTR_ERR(rq);
|
|
|
|
intel_context_put(ce);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = intel_engine_emit_ctx_wa(rq);
|
|
|
|
if (err)
|
|
|
|
goto err_rq;
|
|
|
|
|
|
|
|
err = intel_renderstate_emit(&so, rq);
|
|
|
|
if (err)
|
|
|
|
goto err_rq;
|
|
|
|
|
|
|
|
err_rq:
|
|
|
|
requests[id] = i915_request_get(rq);
|
|
|
|
i915_request_add(rq);
|
|
|
|
intel_renderstate_fini(&so);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush the default context image to memory, and enable powersaving. */
|
|
|
|
if (intel_gt_wait_for_idle(gt, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (id = 0; id < ARRAY_SIZE(requests); id++) {
|
|
|
|
struct i915_request *rq;
|
|
|
|
struct i915_vma *state;
|
|
|
|
void *vaddr;
|
|
|
|
|
|
|
|
rq = requests[id];
|
|
|
|
if (!rq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
GEM_BUG_ON(!test_bit(CONTEXT_ALLOC_BIT, &rq->context->flags));
|
|
|
|
state = rq->context->state;
|
|
|
|
if (!state)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Serialise with retirement on another CPU */
|
|
|
|
GEM_BUG_ON(!i915_request_completed(rq));
|
|
|
|
err = __intel_context_flush_retire(rq->context);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* We want to be able to unbind the state from the GGTT */
|
|
|
|
GEM_BUG_ON(intel_context_is_pinned(rq->context));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As we will hold a reference to the logical state, it will
|
|
|
|
* not be torn down with the context, and importantly the
|
|
|
|
* object will hold onto its vma (making it possible for a
|
|
|
|
* stray GTT write to corrupt our defaults). Unmap the vma
|
|
|
|
* from the GTT to prevent such accidents and reclaim the
|
|
|
|
* space.
|
|
|
|
*/
|
|
|
|
err = i915_vma_unbind(state);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
i915_gem_object_lock(state->obj);
|
|
|
|
err = i915_gem_object_set_to_cpu_domain(state->obj, false);
|
|
|
|
i915_gem_object_unlock(state->obj);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
i915_gem_object_set_cache_coherency(state->obj, I915_CACHE_LLC);
|
|
|
|
|
|
|
|
/* Check we can acquire the image of the context state */
|
|
|
|
vaddr = i915_gem_object_pin_map(state->obj, I915_MAP_FORCE_WB);
|
|
|
|
if (IS_ERR(vaddr)) {
|
|
|
|
err = PTR_ERR(vaddr);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rq->engine->default_state = i915_gem_object_get(state->obj);
|
|
|
|
i915_gem_object_unpin_map(state->obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
/*
|
|
|
|
* If we have to abandon now, we expect the engines to be idle
|
|
|
|
* and ready to be torn-down. The quickest way we can accomplish
|
|
|
|
* this is by declaring ourselves wedged.
|
|
|
|
*/
|
|
|
|
if (err)
|
|
|
|
intel_gt_set_wedged(gt);
|
|
|
|
|
|
|
|
for (id = 0; id < ARRAY_SIZE(requests); id++) {
|
|
|
|
struct intel_context *ce;
|
|
|
|
struct i915_request *rq;
|
|
|
|
|
|
|
|
rq = requests[id];
|
|
|
|
if (!rq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ce = rq->context;
|
|
|
|
i915_request_put(rq);
|
|
|
|
intel_context_put(ce);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __engines_verify_workarounds(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
struct intel_engine_cs *engine;
|
|
|
|
enum intel_engine_id id;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for_each_engine(engine, gt, id) {
|
|
|
|
if (intel_engine_verify_workarounds(engine, "load"))
|
|
|
|
err = -EIO;
|
|
|
|
}
|
|
|
|
|
2020-01-27 02:46:18 +07:00
|
|
|
/* Flush and restore the kernel context for safety */
|
|
|
|
if (intel_gt_wait_for_idle(gt, I915_GEM_IDLE_TIMEOUT) == -ETIME)
|
|
|
|
err = -EIO;
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __intel_gt_disable(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
intel_gt_set_wedged_on_init(gt);
|
|
|
|
|
|
|
|
intel_gt_suspend_prepare(gt);
|
|
|
|
intel_gt_suspend_late(gt);
|
|
|
|
|
|
|
|
GEM_BUG_ON(intel_gt_pm_is_awake(gt));
|
|
|
|
}
|
|
|
|
|
2019-09-05 18:14:03 +07:00
|
|
|
int intel_gt_init(struct intel_gt *gt)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
err = i915_inject_probe_error(gt->i915, -ENODEV);
|
2019-09-05 18:14:03 +07:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
/*
|
|
|
|
* This is just a security blanket to placate dragons.
|
|
|
|
* On some systems, we very sporadically observe that the first TLBs
|
|
|
|
* used by the CS may be stale, despite us poking the TLB reset. If
|
|
|
|
* we hold the forcewake during initialisation these problems
|
|
|
|
* just magically go away.
|
|
|
|
*/
|
|
|
|
intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL);
|
|
|
|
|
|
|
|
err = intel_gt_init_scratch(gt, IS_GEN(gt->i915, 2) ? SZ_256K : SZ_4K);
|
|
|
|
if (err)
|
|
|
|
goto out_fw;
|
|
|
|
|
2019-09-27 18:08:49 +07:00
|
|
|
intel_gt_pm_init(gt);
|
|
|
|
|
2019-12-21 23:03:24 +07:00
|
|
|
gt->vm = kernel_vm(gt);
|
|
|
|
if (!gt->vm) {
|
|
|
|
err = -ENOMEM;
|
2019-12-22 19:07:52 +07:00
|
|
|
goto err_pm;
|
2019-12-21 23:03:24 +07:00
|
|
|
}
|
|
|
|
|
2019-12-22 19:07:52 +07:00
|
|
|
err = intel_engines_init(gt);
|
|
|
|
if (err)
|
|
|
|
goto err_engines;
|
|
|
|
|
|
|
|
intel_uc_init(>->uc);
|
|
|
|
|
|
|
|
err = intel_gt_resume(gt);
|
|
|
|
if (err)
|
2019-12-22 21:40:44 +07:00
|
|
|
goto err_uc_init;
|
2019-12-22 19:07:52 +07:00
|
|
|
|
|
|
|
err = __engines_record_defaults(gt);
|
|
|
|
if (err)
|
|
|
|
goto err_gt;
|
|
|
|
|
|
|
|
err = __engines_verify_workarounds(gt);
|
|
|
|
if (err)
|
|
|
|
goto err_gt;
|
|
|
|
|
|
|
|
err = i915_inject_probe_error(gt->i915, -EIO);
|
|
|
|
if (err)
|
|
|
|
goto err_gt;
|
|
|
|
|
|
|
|
goto out_fw;
|
|
|
|
err_gt:
|
|
|
|
__intel_gt_disable(gt);
|
|
|
|
intel_uc_fini_hw(>->uc);
|
|
|
|
err_uc_init:
|
|
|
|
intel_uc_fini(>->uc);
|
|
|
|
err_engines:
|
|
|
|
intel_engines_release(gt);
|
|
|
|
i915_vm_put(fetch_and_zero(>->vm));
|
|
|
|
err_pm:
|
|
|
|
intel_gt_pm_fini(gt);
|
2019-12-21 23:03:24 +07:00
|
|
|
intel_gt_fini_scratch(gt);
|
2019-12-22 19:07:52 +07:00
|
|
|
out_fw:
|
|
|
|
if (err)
|
|
|
|
intel_gt_set_wedged_on_init(gt);
|
|
|
|
intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL);
|
2019-12-21 23:03:24 +07:00
|
|
|
return err;
|
2019-09-05 18:14:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void intel_gt_driver_remove(struct intel_gt *gt)
|
|
|
|
{
|
2019-12-22 19:07:52 +07:00
|
|
|
__intel_gt_disable(gt);
|
|
|
|
|
|
|
|
intel_uc_fini_hw(>->uc);
|
|
|
|
intel_uc_fini(>->uc);
|
|
|
|
|
|
|
|
intel_engines_release(gt);
|
2019-09-05 18:14:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void intel_gt_driver_unregister(struct intel_gt *gt)
|
|
|
|
{
|
2019-10-25 04:16:41 +07:00
|
|
|
intel_rps_driver_unregister(>->rps);
|
2019-09-05 18:14:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void intel_gt_driver_release(struct intel_gt *gt)
|
|
|
|
{
|
2019-12-21 23:03:24 +07:00
|
|
|
struct i915_address_space *vm;
|
|
|
|
|
|
|
|
vm = fetch_and_zero(>->vm);
|
|
|
|
if (vm) /* FIXME being called twice on error paths :( */
|
|
|
|
i915_vm_put(vm);
|
|
|
|
|
2019-09-27 18:08:49 +07:00
|
|
|
intel_gt_pm_fini(gt);
|
2019-09-05 18:14:03 +07:00
|
|
|
intel_gt_fini_scratch(gt);
|
|
|
|
}
|
|
|
|
|
2019-08-01 07:57:07 +07:00
|
|
|
void intel_gt_driver_late_release(struct intel_gt *gt)
|
2019-07-13 02:29:53 +07:00
|
|
|
{
|
2019-08-01 07:57:08 +07:00
|
|
|
intel_uc_driver_late_release(>->uc);
|
2019-11-15 22:08:39 +07:00
|
|
|
intel_gt_fini_requests(gt);
|
2019-07-13 02:29:53 +07:00
|
|
|
intel_gt_fini_reset(gt);
|
2019-11-01 20:04:06 +07:00
|
|
|
intel_gt_fini_timelines(gt);
|
2019-12-22 19:07:52 +07:00
|
|
|
intel_engines_free(gt);
|
2019-07-13 02:29:53 +07:00
|
|
|
}
|