mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 13:19:56 -04:00
Merge tag 'drm-intel-next-2013-07-26-fixed' of git://people.freedesktop.org/~danvet/drm-intel into drm-next
Neat that QA (and Ben) keeps on humming along while I'm on vacation, so you already get the next feature pull request: - proper eLLC support for HSW from Ben - more interrupt refactoring - add w/a tags where we implement them already (Damien) - hangcheck fixes (Chris) + hangcheck stats (Mika) - flesh out the new vm structs for ppgtt and ggtt (Ben) - PSR for Haswell, still disabled by default (Rodrigo et al.) - pc8+ refclock sequence code from Paulo - more interrupt refactoring from Paulo, unifying ilk/snb with the ivb/hsw interrupt code - full solution for the Haswell concurrent reg access issues (Chris) - fix racy object accounting, used by some new leak tests - fix sync polarity settings on ch7xxx dvo encoder - random bits&pieces, little fixes and better debug output all over [airlied: fix conflict with drm_mm cleanups] * tag 'drm-intel-next-2013-07-26-fixed' of git://people.freedesktop.org/~danvet/drm-intel: (289 commits) drm/i915: Do not dereference NULL crtc or fb until after checking drm/i915: fix pnv display core clock readout out drm/i915: Replace open-coded offset_in_page() drm/i915: Retry DP aux_ch communications with a different clock after failure drm/i915: Add messages useful for HPD storm detection debugging (v2) drm/i915: dvo_ch7xxx: fix vsync polarity setting drm/i915: fix the racy object accounting drm/i915: Convert the register access tracepoint to be conditional drm/i915: Squash gen lookup through multiple indirections inside GT access drm/i915: Use the common register access functions for NOTRACE variants drm/i915: Use a private interface for register access within GT drm/i915: Colocate all GT access routines in the same file drm/i915: fix reference counting in i915_gem_create drm/i915: Use Graphics Base of Stolen Memory on all gen3+ drm/i915: disable stolen mem for OVERLAY_NEEDS_PHYSICAL drm/i915: add functions to disable and restore LCPLL drm/i915: disable CLKOUT_DP when it's not needed drm/i915: extend lpt_enable_clkout_dp drm/i915: fix up error cleanup in i915_gem_object_bind_to_gtt drm/i915: Add some debug breadcrumbs to connector detection ...
This commit is contained in:
@@ -677,6 +677,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
/* don't break so fail path works correct */
|
||||
fail = 1;
|
||||
break;
|
||||
|
||||
if (connector->dpms != DRM_MODE_DPMS_ON) {
|
||||
DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
|
||||
mode_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,6 +759,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
|
||||
drm_get_connector_name(set->connectors[i]));
|
||||
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
|
||||
}
|
||||
}
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
} else if (fb_changed) {
|
||||
@@ -771,22 +782,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* crtc set_config helpers implicit set the crtc and all connected
|
||||
* encoders to DPMS on for a full mode set. But for just an fb update it
|
||||
* doesn't do that. To not confuse userspace, do an explicit DPMS_ON
|
||||
* unconditionally. This will also ensure driver internal dpms state is
|
||||
* consistent again.
|
||||
*/
|
||||
if (set->crtc->enabled) {
|
||||
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
|
||||
drm_get_connector_name(set->connectors[i]));
|
||||
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(save_connectors);
|
||||
kfree(save_encoders);
|
||||
kfree(save_crtcs);
|
||||
|
||||
@@ -38,6 +38,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
|
||||
intel_sprite.o \
|
||||
intel_opregion.o \
|
||||
intel_sideband.o \
|
||||
intel_uncore.o \
|
||||
dvo_ch7xxx.o \
|
||||
dvo_ch7017.o \
|
||||
dvo_ivch.o \
|
||||
|
||||
@@ -307,7 +307,7 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
|
||||
idf |= CH7xxx_IDF_HSP;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
idf |= CH7xxx_IDF_HSP;
|
||||
idf |= CH7xxx_IDF_VSP;
|
||||
|
||||
ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,8 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
|
||||
uintptr_t list = (uintptr_t) node->info_ent->data;
|
||||
struct list_head *head;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
size_t total_obj_size, total_gtt_size;
|
||||
int count, ret;
|
||||
@@ -147,11 +148,11 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
|
||||
switch (list) {
|
||||
case ACTIVE_LIST:
|
||||
seq_puts(m, "Active:\n");
|
||||
head = &dev_priv->mm.active_list;
|
||||
head = &vm->active_list;
|
||||
break;
|
||||
case INACTIVE_LIST:
|
||||
seq_puts(m, "Inactive:\n");
|
||||
head = &dev_priv->mm.inactive_list;
|
||||
head = &vm->inactive_list;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
@@ -219,6 +220,7 @@ static int i915_gem_object_info(struct seq_file *m, void *data)
|
||||
u32 count, mappable_count, purgeable_count;
|
||||
size_t size, mappable_size, purgeable_size;
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_file *file;
|
||||
int ret;
|
||||
|
||||
@@ -236,12 +238,12 @@ static int i915_gem_object_info(struct seq_file *m, void *data)
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
size = count = mappable_size = mappable_count = 0;
|
||||
count_objects(&dev_priv->mm.active_list, mm_list);
|
||||
count_objects(&vm->active_list, mm_list);
|
||||
seq_printf(m, " %u [%u] active objects, %zu [%zu] bytes\n",
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
size = count = mappable_size = mappable_count = 0;
|
||||
count_objects(&dev_priv->mm.inactive_list, mm_list);
|
||||
count_objects(&vm->inactive_list, mm_list);
|
||||
seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n",
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
@@ -276,8 +278,8 @@ static int i915_gem_object_info(struct seq_file *m, void *data)
|
||||
count, size);
|
||||
|
||||
seq_printf(m, "%zu [%lu] gtt total\n",
|
||||
dev_priv->gtt.total,
|
||||
dev_priv->gtt.mappable_end - dev_priv->gtt.start);
|
||||
dev_priv->gtt.base.total,
|
||||
dev_priv->gtt.mappable_end - dev_priv->gtt.base.start);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
list_for_each_entry_reverse(file, &dev->filelist, lhead) {
|
||||
@@ -987,9 +989,9 @@ static int gen6_drpc_info(struct seq_file *m)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irq(&dev_priv->gt_lock);
|
||||
forcewake_count = dev_priv->forcewake_count;
|
||||
spin_unlock_irq(&dev_priv->gt_lock);
|
||||
spin_lock_irq(&dev_priv->uncore.lock);
|
||||
forcewake_count = dev_priv->uncore.forcewake_count;
|
||||
spin_unlock_irq(&dev_priv->uncore.lock);
|
||||
|
||||
if (forcewake_count) {
|
||||
seq_puts(m, "RC information inaccurate because somebody "
|
||||
@@ -1002,7 +1004,7 @@ static int gen6_drpc_info(struct seq_file *m)
|
||||
}
|
||||
|
||||
gt_core_status = readl(dev_priv->regs + GEN6_GT_CORE_STATUS);
|
||||
trace_i915_reg_rw(false, GEN6_GT_CORE_STATUS, gt_core_status, 4);
|
||||
trace_i915_reg_rw(false, GEN6_GT_CORE_STATUS, gt_core_status, 4, true);
|
||||
|
||||
rpmodectl1 = I915_READ(GEN6_RP_CONTROL);
|
||||
rcctl1 = I915_READ(GEN6_RC_CONTROL);
|
||||
@@ -1373,9 +1375,9 @@ static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
unsigned forcewake_count;
|
||||
|
||||
spin_lock_irq(&dev_priv->gt_lock);
|
||||
forcewake_count = dev_priv->forcewake_count;
|
||||
spin_unlock_irq(&dev_priv->gt_lock);
|
||||
spin_lock_irq(&dev_priv->uncore.lock);
|
||||
forcewake_count = dev_priv->uncore.forcewake_count;
|
||||
spin_unlock_irq(&dev_priv->uncore.lock);
|
||||
|
||||
seq_printf(m, "forcewake count = %u\n", forcewake_count);
|
||||
|
||||
@@ -1530,6 +1532,148 @@ static int i915_dpio_info(struct seq_file *m, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_llc(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* Size calculation for LLC is a bit of a pain. Ignore for now. */
|
||||
seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev)));
|
||||
seq_printf(m, "eLLC: %zuMB\n", dev_priv->ellc_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_edp_psr_status(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 psrstat, psrperf;
|
||||
|
||||
if (!IS_HASWELL(dev)) {
|
||||
seq_puts(m, "PSR not supported on this platform\n");
|
||||
} else if (IS_HASWELL(dev) && I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE) {
|
||||
seq_puts(m, "PSR enabled\n");
|
||||
} else {
|
||||
seq_puts(m, "PSR disabled: ");
|
||||
switch (dev_priv->no_psr_reason) {
|
||||
case PSR_NO_SOURCE:
|
||||
seq_puts(m, "not supported on this platform");
|
||||
break;
|
||||
case PSR_NO_SINK:
|
||||
seq_puts(m, "not supported by panel");
|
||||
break;
|
||||
case PSR_MODULE_PARAM:
|
||||
seq_puts(m, "disabled by flag");
|
||||
break;
|
||||
case PSR_CRTC_NOT_ACTIVE:
|
||||
seq_puts(m, "crtc not active");
|
||||
break;
|
||||
case PSR_PWR_WELL_ENABLED:
|
||||
seq_puts(m, "power well enabled");
|
||||
break;
|
||||
case PSR_NOT_TILED:
|
||||
seq_puts(m, "not tiled");
|
||||
break;
|
||||
case PSR_SPRITE_ENABLED:
|
||||
seq_puts(m, "sprite enabled");
|
||||
break;
|
||||
case PSR_S3D_ENABLED:
|
||||
seq_puts(m, "stereo 3d enabled");
|
||||
break;
|
||||
case PSR_INTERLACED_ENABLED:
|
||||
seq_puts(m, "interlaced enabled");
|
||||
break;
|
||||
case PSR_HSW_NOT_DDIA:
|
||||
seq_puts(m, "HSW ties PSR to DDI A (eDP)");
|
||||
break;
|
||||
default:
|
||||
seq_puts(m, "unknown reason");
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
psrstat = I915_READ(EDP_PSR_STATUS_CTL);
|
||||
|
||||
seq_puts(m, "PSR Current State: ");
|
||||
switch (psrstat & EDP_PSR_STATUS_STATE_MASK) {
|
||||
case EDP_PSR_STATUS_STATE_IDLE:
|
||||
seq_puts(m, "Reset state\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_SRDONACK:
|
||||
seq_puts(m, "Wait for TG/Stream to send on frame of data after SRD conditions are met\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_SRDENT:
|
||||
seq_puts(m, "SRD entry\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_BUFOFF:
|
||||
seq_puts(m, "Wait for buffer turn off\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_BUFON:
|
||||
seq_puts(m, "Wait for buffer turn on\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_AUXACK:
|
||||
seq_puts(m, "Wait for AUX to acknowledge on SRD exit\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_STATE_SRDOFFACK:
|
||||
seq_puts(m, "Wait for TG/Stream to acknowledge the SRD VDM exit\n");
|
||||
break;
|
||||
default:
|
||||
seq_puts(m, "Unknown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_puts(m, "Link Status: ");
|
||||
switch (psrstat & EDP_PSR_STATUS_LINK_MASK) {
|
||||
case EDP_PSR_STATUS_LINK_FULL_OFF:
|
||||
seq_puts(m, "Link is fully off\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_LINK_FULL_ON:
|
||||
seq_puts(m, "Link is fully on\n");
|
||||
break;
|
||||
case EDP_PSR_STATUS_LINK_STANDBY:
|
||||
seq_puts(m, "Link is in standby\n");
|
||||
break;
|
||||
default:
|
||||
seq_puts(m, "Unknown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(m, "PSR Entry Count: %u\n",
|
||||
psrstat >> EDP_PSR_STATUS_COUNT_SHIFT &
|
||||
EDP_PSR_STATUS_COUNT_MASK);
|
||||
|
||||
seq_printf(m, "Max Sleep Timer Counter: %u\n",
|
||||
psrstat >> EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT &
|
||||
EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK);
|
||||
|
||||
seq_printf(m, "Had AUX error: %s\n",
|
||||
yesno(psrstat & EDP_PSR_STATUS_AUX_ERROR));
|
||||
|
||||
seq_printf(m, "Sending AUX: %s\n",
|
||||
yesno(psrstat & EDP_PSR_STATUS_AUX_SENDING));
|
||||
|
||||
seq_printf(m, "Sending Idle: %s\n",
|
||||
yesno(psrstat & EDP_PSR_STATUS_SENDING_IDLE));
|
||||
|
||||
seq_printf(m, "Sending TP2 TP3: %s\n",
|
||||
yesno(psrstat & EDP_PSR_STATUS_SENDING_TP2_TP3));
|
||||
|
||||
seq_printf(m, "Sending TP1: %s\n",
|
||||
yesno(psrstat & EDP_PSR_STATUS_SENDING_TP1));
|
||||
|
||||
seq_printf(m, "Idle Count: %u\n",
|
||||
psrstat & EDP_PSR_STATUS_IDLE_MASK);
|
||||
|
||||
psrperf = (I915_READ(EDP_PSR_PERF_CNT)) & EDP_PSR_PERF_CNT_MASK;
|
||||
seq_printf(m, "Performance Counter: %u\n", psrperf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i915_wedged_get(void *data, u64 *val)
|
||||
{
|
||||
@@ -1612,6 +1756,7 @@ i915_drop_caches_set(void *data, u64 val)
|
||||
struct drm_device *dev = data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_gem_object *obj, *next;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_DRIVER("Dropping caches: 0x%08llx\n", val);
|
||||
@@ -1632,7 +1777,8 @@ i915_drop_caches_set(void *data, u64 val)
|
||||
i915_gem_retire_requests(dev);
|
||||
|
||||
if (val & DROP_BOUND) {
|
||||
list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list)
|
||||
list_for_each_entry_safe(obj, next, &vm->inactive_list,
|
||||
mm_list)
|
||||
if (obj->pin_count == 0) {
|
||||
ret = i915_gem_object_unbind(obj);
|
||||
if (ret)
|
||||
@@ -1959,6 +2105,8 @@ static struct drm_info_list i915_debugfs_list[] = {
|
||||
{"i915_swizzle_info", i915_swizzle_info, 0},
|
||||
{"i915_ppgtt_info", i915_ppgtt_info, 0},
|
||||
{"i915_dpio", i915_dpio_info, 0},
|
||||
{"i915_llc", i915_llc, 0},
|
||||
{"i915_edp_psr_status", i915_edp_psr_status, 0},
|
||||
};
|
||||
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
|
||||
|
||||
|
||||
@@ -1358,7 +1358,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
|
||||
i915_gem_context_fini(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
i915_gem_cleanup_aliasing_ppgtt(dev);
|
||||
drm_mm_takedown(&dev_priv->mm.gtt_space);
|
||||
drm_mm_takedown(&dev_priv->gtt.base.mm);
|
||||
cleanup_irq:
|
||||
drm_irq_uninstall(dev);
|
||||
cleanup_gem_stolen:
|
||||
@@ -1435,22 +1435,6 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
|
||||
#undef SEP_COMMA
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_early_sanitize_regs - clean up BIOS state
|
||||
* @dev: DRM device
|
||||
*
|
||||
* This function must be called before we do any I915_READ or I915_WRITE. Its
|
||||
* purpose is to clean up any state left by the BIOS that may affect us when
|
||||
* reading and/or writing registers.
|
||||
*/
|
||||
static void intel_early_sanitize_regs(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev))
|
||||
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_driver_load - setup chip and create an initial config
|
||||
* @dev: DRM device
|
||||
@@ -1490,8 +1474,21 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
dev_priv->dev = dev;
|
||||
dev_priv->info = info;
|
||||
|
||||
spin_lock_init(&dev_priv->irq_lock);
|
||||
spin_lock_init(&dev_priv->gpu_error.lock);
|
||||
spin_lock_init(&dev_priv->backlight.lock);
|
||||
spin_lock_init(&dev_priv->uncore.lock);
|
||||
spin_lock_init(&dev_priv->mm.object_stat_lock);
|
||||
mutex_init(&dev_priv->dpio_lock);
|
||||
mutex_init(&dev_priv->rps.hw_lock);
|
||||
mutex_init(&dev_priv->modeset_restore_lock);
|
||||
|
||||
i915_dump_device_info(dev_priv);
|
||||
|
||||
INIT_LIST_HEAD(&dev_priv->vm_list);
|
||||
INIT_LIST_HEAD(&dev_priv->gtt.base.global_link);
|
||||
list_add(&dev_priv->gtt.base.global_link, &dev_priv->vm_list);
|
||||
|
||||
if (i915_get_bridge_dev(dev)) {
|
||||
ret = -EIO;
|
||||
goto free_priv;
|
||||
@@ -1517,7 +1514,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
goto put_bridge;
|
||||
}
|
||||
|
||||
intel_early_sanitize_regs(dev);
|
||||
intel_uncore_early_sanitize(dev);
|
||||
|
||||
if (IS_HASWELL(dev) && (I915_READ(HSW_EDRAM_PRESENT) == 1)) {
|
||||
/* The docs do not explain exactly how the calculation can be
|
||||
* made. It is somewhat guessable, but for now, it's always
|
||||
* 128MB.
|
||||
* NB: We can't write IDICR yet because we do not have gt funcs
|
||||
* set up */
|
||||
dev_priv->ellc_size = 128;
|
||||
DRM_INFO("Found %zuMB of eLLC\n", dev_priv->ellc_size);
|
||||
}
|
||||
|
||||
ret = i915_gem_gtt_init(dev);
|
||||
if (ret)
|
||||
@@ -1580,7 +1587,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
intel_detect_pch(dev);
|
||||
|
||||
intel_irq_init(dev);
|
||||
intel_gt_init(dev);
|
||||
intel_pm_init(dev);
|
||||
intel_uncore_sanitize(dev);
|
||||
intel_uncore_init(dev);
|
||||
|
||||
/* Try to make sure MCHBAR is enabled before poking at it */
|
||||
intel_setup_mchbar(dev);
|
||||
@@ -1605,14 +1614,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
if (!IS_I945G(dev) && !IS_I945GM(dev))
|
||||
pci_enable_msi(dev->pdev);
|
||||
|
||||
spin_lock_init(&dev_priv->irq_lock);
|
||||
spin_lock_init(&dev_priv->gpu_error.lock);
|
||||
spin_lock_init(&dev_priv->backlight.lock);
|
||||
mutex_init(&dev_priv->dpio_lock);
|
||||
|
||||
mutex_init(&dev_priv->rps.hw_lock);
|
||||
mutex_init(&dev_priv->modeset_restore_lock);
|
||||
|
||||
dev_priv->num_plane = 1;
|
||||
if (IS_VALLEYVIEW(dev))
|
||||
dev_priv->num_plane = 2;
|
||||
@@ -1642,7 +1643,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
if (INTEL_INFO(dev)->num_pipes) {
|
||||
/* Must be done after probing outputs */
|
||||
intel_opregion_init(dev);
|
||||
acpi_video_register();
|
||||
acpi_video_register_with_quirks();
|
||||
}
|
||||
|
||||
if (IS_GEN5(dev))
|
||||
@@ -1663,7 +1664,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
out_mtrrfree:
|
||||
arch_phys_wc_del(dev_priv->gtt.mtrr);
|
||||
io_mapping_free(dev_priv->gtt.mappable);
|
||||
dev_priv->gtt.gtt_remove(dev);
|
||||
dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
|
||||
out_rmmap:
|
||||
pci_iounmap(dev->pdev, dev_priv->regs);
|
||||
put_bridge:
|
||||
@@ -1748,7 +1749,9 @@ int i915_driver_unload(struct drm_device *dev)
|
||||
i915_free_hws(dev);
|
||||
}
|
||||
|
||||
drm_mm_takedown(&dev_priv->mm.gtt_space);
|
||||
list_del(&dev_priv->gtt.base.global_link);
|
||||
WARN_ON(!list_empty(&dev_priv->vm_list));
|
||||
drm_mm_takedown(&dev_priv->gtt.base.mm);
|
||||
if (dev_priv->regs != NULL)
|
||||
pci_iounmap(dev->pdev, dev_priv->regs);
|
||||
|
||||
@@ -1758,7 +1761,7 @@ int i915_driver_unload(struct drm_device *dev)
|
||||
destroy_workqueue(dev_priv->wq);
|
||||
pm_qos_remove_request(&dev_priv->pm_qos);
|
||||
|
||||
dev_priv->gtt.gtt_remove(dev);
|
||||
dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
|
||||
|
||||
if (dev_priv->slab)
|
||||
kmem_cache_destroy(dev_priv->slab);
|
||||
|
||||
@@ -118,6 +118,10 @@ module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, int, 0600);
|
||||
MODULE_PARM_DESC(i915_enable_ppgtt,
|
||||
"Enable PPGTT (default: true)");
|
||||
|
||||
int i915_enable_psr __read_mostly = 0;
|
||||
module_param_named(enable_psr, i915_enable_psr, int, 0600);
|
||||
MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)");
|
||||
|
||||
unsigned int i915_preliminary_hw_support __read_mostly = 0;
|
||||
module_param_named(preliminary_hw_support, i915_preliminary_hw_support, int, 0600);
|
||||
MODULE_PARM_DESC(preliminary_hw_support,
|
||||
@@ -137,6 +141,11 @@ module_param_named(fastboot, i915_fastboot, bool, 0600);
|
||||
MODULE_PARM_DESC(fastboot, "Try to skip unnecessary mode sets at boot time "
|
||||
"(default: false)");
|
||||
|
||||
bool i915_prefault_disable __read_mostly;
|
||||
module_param_named(prefault_disable, i915_prefault_disable, bool, 0600);
|
||||
MODULE_PARM_DESC(prefault_disable,
|
||||
"Disable page prefaulting for pread/pwrite/reloc (default:false). For developers only.");
|
||||
|
||||
static struct drm_driver driver;
|
||||
extern int intel_agp_enabled;
|
||||
|
||||
@@ -714,7 +723,7 @@ static int i915_drm_thaw(struct drm_device *dev)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
intel_gt_reset(dev);
|
||||
intel_uncore_sanitize(dev);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
@@ -740,7 +749,7 @@ int i915_resume(struct drm_device *dev)
|
||||
|
||||
pci_set_master(dev->pdev);
|
||||
|
||||
intel_gt_reset(dev);
|
||||
intel_uncore_sanitize(dev);
|
||||
|
||||
/*
|
||||
* Platforms with opregion should have sane BIOS, older ones (gen3 and
|
||||
@@ -761,140 +770,6 @@ int i915_resume(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i8xx_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (IS_I85X(dev))
|
||||
return -ENODEV;
|
||||
|
||||
I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830);
|
||||
POSTING_READ(D_STATE);
|
||||
|
||||
if (IS_I830(dev) || IS_845G(dev)) {
|
||||
I915_WRITE(DEBUG_RESET_I830,
|
||||
DEBUG_RESET_DISPLAY |
|
||||
DEBUG_RESET_RENDER |
|
||||
DEBUG_RESET_FULL);
|
||||
POSTING_READ(DEBUG_RESET_I830);
|
||||
msleep(1);
|
||||
|
||||
I915_WRITE(DEBUG_RESET_I830, 0);
|
||||
POSTING_READ(DEBUG_RESET_I830);
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
|
||||
I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830);
|
||||
POSTING_READ(D_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i965_reset_complete(struct drm_device *dev)
|
||||
{
|
||||
u8 gdrst;
|
||||
pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst);
|
||||
return (gdrst & GRDOM_RESET_ENABLE) == 0;
|
||||
}
|
||||
|
||||
static int i965_do_reset(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Set the domains we want to reset (GRDOM/bits 2 and 3) as
|
||||
* well as the reset bit (GR/bit 0). Setting the GR bit
|
||||
* triggers the reset; when done, the hardware will clear it.
|
||||
*/
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST,
|
||||
GRDOM_RENDER | GRDOM_RESET_ENABLE);
|
||||
ret = wait_for(i965_reset_complete(dev), 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We can't reset render&media without also resetting display ... */
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST,
|
||||
GRDOM_MEDIA | GRDOM_RESET_ENABLE);
|
||||
|
||||
ret = wait_for(i965_reset_complete(dev), 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ironlake_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 gdrst;
|
||||
int ret;
|
||||
|
||||
gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
|
||||
gdrst &= ~GRDOM_MASK;
|
||||
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
|
||||
gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE);
|
||||
ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We can't reset render&media without also resetting display ... */
|
||||
gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
|
||||
gdrst &= ~GRDOM_MASK;
|
||||
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
|
||||
gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE);
|
||||
return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
|
||||
}
|
||||
|
||||
static int gen6_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
unsigned long irqflags;
|
||||
|
||||
/* Hold gt_lock across reset to prevent any register access
|
||||
* with forcewake not set correctly
|
||||
*/
|
||||
spin_lock_irqsave(&dev_priv->gt_lock, irqflags);
|
||||
|
||||
/* Reset the chip */
|
||||
|
||||
/* GEN6_GDRST is not in the gt power well, no need to check
|
||||
* for fifo space for the write or forcewake the chip for
|
||||
* the read
|
||||
*/
|
||||
I915_WRITE_NOTRACE(GEN6_GDRST, GEN6_GRDOM_FULL);
|
||||
|
||||
/* Spin waiting for the device to ack the reset request */
|
||||
ret = wait_for((I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500);
|
||||
|
||||
/* If reset with a user forcewake, try to restore, otherwise turn it off */
|
||||
if (dev_priv->forcewake_count)
|
||||
dev_priv->gt.force_wake_get(dev_priv);
|
||||
else
|
||||
dev_priv->gt.force_wake_put(dev_priv);
|
||||
|
||||
/* Restore fifo count */
|
||||
dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int intel_gpu_reset(struct drm_device *dev)
|
||||
{
|
||||
switch (INTEL_INFO(dev)->gen) {
|
||||
case 7:
|
||||
case 6: return gen6_do_reset(dev);
|
||||
case 5: return ironlake_do_reset(dev);
|
||||
case 4: return i965_do_reset(dev);
|
||||
case 2: return i8xx_do_reset(dev);
|
||||
default: return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_reset - reset chip after a hang
|
||||
* @dev: drm device to reset
|
||||
@@ -1224,133 +1099,3 @@ module_exit(i915_exit);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
/* We give fast paths for the really cool registers */
|
||||
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
|
||||
((HAS_FORCE_WAKE((dev_priv)->dev)) && \
|
||||
((reg) < 0x40000) && \
|
||||
((reg) != FORCEWAKE))
|
||||
static void
|
||||
ilk_dummy_write(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
/* WaIssueDummyWriteToWakeupFromRC6:ilk Issue a dummy write to wake up
|
||||
* the chip from rc6 before touching it for real. MI_MODE is masked,
|
||||
* hence harmless to write 0 into. */
|
||||
I915_WRITE_NOTRACE(MI_MODE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
|
||||
{
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
|
||||
(I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unknown unclaimed register before writing to %x\n",
|
||||
reg);
|
||||
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
|
||||
{
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
|
||||
(I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unclaimed write to %x\n", reg);
|
||||
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
}
|
||||
|
||||
#define __i915_read(x, y) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
|
||||
u##x val = 0; \
|
||||
if (IS_GEN5(dev_priv->dev)) \
|
||||
ilk_dummy_write(dev_priv); \
|
||||
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
|
||||
unsigned long irqflags; \
|
||||
spin_lock_irqsave(&dev_priv->gt_lock, irqflags); \
|
||||
if (dev_priv->forcewake_count == 0) \
|
||||
dev_priv->gt.force_wake_get(dev_priv); \
|
||||
val = read##y(dev_priv->regs + reg); \
|
||||
if (dev_priv->forcewake_count == 0) \
|
||||
dev_priv->gt.force_wake_put(dev_priv); \
|
||||
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); \
|
||||
} else { \
|
||||
val = read##y(dev_priv->regs + reg); \
|
||||
} \
|
||||
trace_i915_reg_rw(false, reg, val, sizeof(val)); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
__i915_read(8, b)
|
||||
__i915_read(16, w)
|
||||
__i915_read(32, l)
|
||||
__i915_read(64, q)
|
||||
#undef __i915_read
|
||||
|
||||
#define __i915_write(x, y) \
|
||||
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
|
||||
u32 __fifo_ret = 0; \
|
||||
trace_i915_reg_rw(true, reg, val, sizeof(val)); \
|
||||
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
|
||||
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
|
||||
} \
|
||||
if (IS_GEN5(dev_priv->dev)) \
|
||||
ilk_dummy_write(dev_priv); \
|
||||
hsw_unclaimed_reg_clear(dev_priv, reg); \
|
||||
write##y(val, dev_priv->regs + reg); \
|
||||
if (unlikely(__fifo_ret)) { \
|
||||
gen6_gt_check_fifodbg(dev_priv); \
|
||||
} \
|
||||
hsw_unclaimed_reg_check(dev_priv, reg); \
|
||||
}
|
||||
__i915_write(8, b)
|
||||
__i915_write(16, w)
|
||||
__i915_write(32, l)
|
||||
__i915_write(64, q)
|
||||
#undef __i915_write
|
||||
|
||||
static const struct register_whitelist {
|
||||
uint64_t offset;
|
||||
uint32_t size;
|
||||
uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
|
||||
} whitelist[] = {
|
||||
{ RING_TIMESTAMP(RENDER_RING_BASE), 8, 0xF0 },
|
||||
};
|
||||
|
||||
int i915_reg_read_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_reg_read *reg = data;
|
||||
struct register_whitelist const *entry = whitelist;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) {
|
||||
if (entry->offset == reg->offset &&
|
||||
(1 << INTEL_INFO(dev)->gen & entry->gen_bitmask))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(whitelist))
|
||||
return -EINVAL;
|
||||
|
||||
switch (entry->size) {
|
||||
case 8:
|
||||
reg->val = I915_READ64(reg->offset);
|
||||
break;
|
||||
case 4:
|
||||
reg->val = I915_READ(reg->offset);
|
||||
break;
|
||||
case 2:
|
||||
reg->val = I915_READ16(reg->offset);
|
||||
break;
|
||||
case 1:
|
||||
reg->val = I915_READ8(reg->offset);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -391,11 +391,20 @@ struct drm_i915_display_funcs {
|
||||
/* pll clock increase/decrease */
|
||||
};
|
||||
|
||||
struct drm_i915_gt_funcs {
|
||||
struct intel_uncore_funcs {
|
||||
void (*force_wake_get)(struct drm_i915_private *dev_priv);
|
||||
void (*force_wake_put)(struct drm_i915_private *dev_priv);
|
||||
};
|
||||
|
||||
struct intel_uncore {
|
||||
spinlock_t lock; /** lock is also taken in irq contexts. */
|
||||
|
||||
struct intel_uncore_funcs funcs;
|
||||
|
||||
unsigned fifo_count;
|
||||
unsigned forcewake_count;
|
||||
};
|
||||
|
||||
#define DEV_INFO_FOR_EACH_FLAG(func, sep) \
|
||||
func(is_mobile) sep \
|
||||
func(is_i85x) sep \
|
||||
@@ -446,6 +455,54 @@ enum i915_cache_level {
|
||||
|
||||
typedef uint32_t gen6_gtt_pte_t;
|
||||
|
||||
struct i915_address_space {
|
||||
struct drm_mm mm;
|
||||
struct drm_device *dev;
|
||||
struct list_head global_link;
|
||||
unsigned long start; /* Start offset always 0 for dri2 */
|
||||
size_t total; /* size addr space maps (ex. 2GB for ggtt) */
|
||||
|
||||
struct {
|
||||
dma_addr_t addr;
|
||||
struct page *page;
|
||||
} scratch;
|
||||
|
||||
/**
|
||||
* List of objects currently involved in rendering.
|
||||
*
|
||||
* Includes buffers having the contents of their GPU caches
|
||||
* flushed, not necessarily primitives. last_rendering_seqno
|
||||
* represents when the rendering involved will be completed.
|
||||
*
|
||||
* A reference is held on the buffer while on this list.
|
||||
*/
|
||||
struct list_head active_list;
|
||||
|
||||
/**
|
||||
* LRU list of objects which are not in the ringbuffer and
|
||||
* are ready to unbind, but are still in the GTT.
|
||||
*
|
||||
* last_rendering_seqno is 0 while an object is in this list.
|
||||
*
|
||||
* A reference is not held on the buffer while on this list,
|
||||
* as merely being GTT-bound shouldn't prevent its being
|
||||
* freed, and we'll pull it off the list in the free path.
|
||||
*/
|
||||
struct list_head inactive_list;
|
||||
|
||||
/* FIXME: Need a more generic return type */
|
||||
gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
|
||||
enum i915_cache_level level);
|
||||
void (*clear_range)(struct i915_address_space *vm,
|
||||
unsigned int first_entry,
|
||||
unsigned int num_entries);
|
||||
void (*insert_entries)(struct i915_address_space *vm,
|
||||
struct sg_table *st,
|
||||
unsigned int first_entry,
|
||||
enum i915_cache_level cache_level);
|
||||
void (*cleanup)(struct i915_address_space *vm);
|
||||
};
|
||||
|
||||
/* The Graphics Translation Table is the way in which GEN hardware translates a
|
||||
* Graphics Virtual Address into a Physical Address. In addition to the normal
|
||||
* collateral associated with any va->pa translations GEN hardware also has a
|
||||
@@ -454,8 +511,7 @@ typedef uint32_t gen6_gtt_pte_t;
|
||||
* the spec.
|
||||
*/
|
||||
struct i915_gtt {
|
||||
unsigned long start; /* Start offset of used GTT */
|
||||
size_t total; /* Total size GTT can map */
|
||||
struct i915_address_space base;
|
||||
size_t stolen_size; /* Total size of stolen memory */
|
||||
|
||||
unsigned long mappable_end; /* End offset that we can CPU map */
|
||||
@@ -466,10 +522,6 @@ struct i915_gtt {
|
||||
void __iomem *gsm;
|
||||
|
||||
bool do_idle_maps;
|
||||
struct {
|
||||
dma_addr_t addr;
|
||||
struct page *page;
|
||||
} scratch;
|
||||
|
||||
int mtrr;
|
||||
|
||||
@@ -477,38 +529,28 @@ struct i915_gtt {
|
||||
int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
|
||||
size_t *stolen, phys_addr_t *mappable_base,
|
||||
unsigned long *mappable_end);
|
||||
void (*gtt_remove)(struct drm_device *dev);
|
||||
void (*gtt_clear_range)(struct drm_device *dev,
|
||||
unsigned int first_entry,
|
||||
unsigned int num_entries);
|
||||
void (*gtt_insert_entries)(struct drm_device *dev,
|
||||
struct sg_table *st,
|
||||
unsigned int pg_start,
|
||||
enum i915_cache_level cache_level);
|
||||
gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
|
||||
enum i915_cache_level level);
|
||||
};
|
||||
#define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT)
|
||||
#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
|
||||
|
||||
struct i915_hw_ppgtt {
|
||||
struct drm_device *dev;
|
||||
struct i915_address_space base;
|
||||
unsigned num_pd_entries;
|
||||
struct page **pt_pages;
|
||||
uint32_t pd_offset;
|
||||
dma_addr_t *pt_dma_addr;
|
||||
|
||||
/* pte functions, mirroring the interface of the global gtt. */
|
||||
void (*clear_range)(struct i915_hw_ppgtt *ppgtt,
|
||||
unsigned int first_entry,
|
||||
unsigned int num_entries);
|
||||
void (*insert_entries)(struct i915_hw_ppgtt *ppgtt,
|
||||
struct sg_table *st,
|
||||
unsigned int pg_start,
|
||||
enum i915_cache_level cache_level);
|
||||
gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
|
||||
enum i915_cache_level level);
|
||||
int (*enable)(struct drm_device *dev);
|
||||
void (*cleanup)(struct i915_hw_ppgtt *ppgtt);
|
||||
};
|
||||
|
||||
/* To make things as simple as possible (ie. no refcounting), a VMA's lifetime
|
||||
* will always be <= an objects lifetime. So object refcounting should cover us.
|
||||
*/
|
||||
struct i915_vma {
|
||||
struct drm_mm_node node;
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct i915_address_space *vm;
|
||||
|
||||
struct list_head vma_link; /* Link in the object's VMA list */
|
||||
};
|
||||
|
||||
struct i915_ctx_hang_stats {
|
||||
@@ -560,6 +602,18 @@ struct i915_fbc {
|
||||
} no_fbc_reason;
|
||||
};
|
||||
|
||||
enum no_psr_reason {
|
||||
PSR_NO_SOURCE, /* Not supported on platform */
|
||||
PSR_NO_SINK, /* Not supported by panel */
|
||||
PSR_MODULE_PARAM,
|
||||
PSR_CRTC_NOT_ACTIVE,
|
||||
PSR_PWR_WELL_ENABLED,
|
||||
PSR_NOT_TILED,
|
||||
PSR_SPRITE_ENABLED,
|
||||
PSR_S3D_ENABLED,
|
||||
PSR_INTERLACED_ENABLED,
|
||||
PSR_HSW_NOT_DDIA,
|
||||
};
|
||||
|
||||
enum intel_pch {
|
||||
PCH_NONE = 0, /* No PCH present */
|
||||
@@ -577,6 +631,7 @@ enum intel_sbi_destination {
|
||||
#define QUIRK_PIPEA_FORCE (1<<0)
|
||||
#define QUIRK_LVDS_SSC_DISABLE (1<<1)
|
||||
#define QUIRK_INVERT_BRIGHTNESS (1<<2)
|
||||
#define QUIRK_NO_PCH_PWM_ENABLE (1<<3)
|
||||
|
||||
struct intel_fbdev;
|
||||
struct intel_fbc_work;
|
||||
@@ -834,8 +889,6 @@ struct intel_l3_parity {
|
||||
struct i915_gem_mm {
|
||||
/** Memory allocator for GTT stolen memory */
|
||||
struct drm_mm stolen;
|
||||
/** Memory allocator for GTT */
|
||||
struct drm_mm gtt_space;
|
||||
/** List of all objects in gtt_space. Used to restore gtt
|
||||
* mappings on resume */
|
||||
struct list_head bound_list;
|
||||
@@ -855,29 +908,6 @@ struct i915_gem_mm {
|
||||
struct shrinker inactive_shrinker;
|
||||
bool shrinker_no_lock_stealing;
|
||||
|
||||
/**
|
||||
* List of objects currently involved in rendering.
|
||||
*
|
||||
* Includes buffers having the contents of their GPU caches
|
||||
* flushed, not necessarily primitives. last_rendering_seqno
|
||||
* represents when the rendering involved will be completed.
|
||||
*
|
||||
* A reference is held on the buffer while on this list.
|
||||
*/
|
||||
struct list_head active_list;
|
||||
|
||||
/**
|
||||
* LRU list of objects which are not in the ringbuffer and
|
||||
* are ready to unbind, but are still in the GTT.
|
||||
*
|
||||
* last_rendering_seqno is 0 while an object is in this list.
|
||||
*
|
||||
* A reference is not held on the buffer while on this list,
|
||||
* as merely being GTT-bound shouldn't prevent its being
|
||||
* freed, and we'll pull it off the list in the free path.
|
||||
*/
|
||||
struct list_head inactive_list;
|
||||
|
||||
/** LRU list of objects with fence regs on them. */
|
||||
struct list_head fence_list;
|
||||
|
||||
@@ -905,6 +935,7 @@ struct i915_gem_mm {
|
||||
struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
|
||||
|
||||
/* accounting, useful for userland debugging */
|
||||
spinlock_t object_stat_lock;
|
||||
size_t object_memory;
|
||||
u32 object_count;
|
||||
};
|
||||
@@ -1024,14 +1055,7 @@ typedef struct drm_i915_private {
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
struct drm_i915_gt_funcs gt;
|
||||
/** gt_fifo_count and the subsequent register write are synchronized
|
||||
* with dev->struct_mutex. */
|
||||
unsigned gt_fifo_count;
|
||||
/** forcewake_count is protected by gt_lock */
|
||||
unsigned forcewake_count;
|
||||
/** gt_lock is also taken in irq contexts. */
|
||||
spinlock_t gt_lock;
|
||||
struct intel_uncore uncore;
|
||||
|
||||
struct intel_gmbus gmbus[GMBUS_NUM_PORTS];
|
||||
|
||||
@@ -1124,7 +1148,8 @@ typedef struct drm_i915_private {
|
||||
enum modeset_restore modeset_restore;
|
||||
struct mutex modeset_restore_lock;
|
||||
|
||||
struct i915_gtt gtt;
|
||||
struct list_head vm_list; /* Global list of all address spaces */
|
||||
struct i915_gtt gtt; /* VMA representing the global address space */
|
||||
|
||||
struct i915_gem_mm mm;
|
||||
|
||||
@@ -1151,6 +1176,9 @@ typedef struct drm_i915_private {
|
||||
|
||||
struct intel_l3_parity l3_parity;
|
||||
|
||||
/* Cannot be determined by PCIID. You must always read a register. */
|
||||
size_t ellc_size;
|
||||
|
||||
/* gen6+ rps state */
|
||||
struct intel_gen6_power_mgmt rps;
|
||||
|
||||
@@ -1161,6 +1189,8 @@ typedef struct drm_i915_private {
|
||||
/* Haswell power well */
|
||||
struct i915_power_well power_well;
|
||||
|
||||
enum no_psr_reason no_psr_reason;
|
||||
|
||||
struct i915_gpu_error gpu_error;
|
||||
|
||||
struct drm_i915_gem_object *vlv_pctx;
|
||||
@@ -1228,8 +1258,9 @@ struct drm_i915_gem_object {
|
||||
|
||||
const struct drm_i915_gem_object_ops *ops;
|
||||
|
||||
/** Current space allocated to this object in the GTT, if any. */
|
||||
struct drm_mm_node gtt_space;
|
||||
/** List of VMAs backed by this object */
|
||||
struct list_head vma_list;
|
||||
|
||||
/** Stolen memory for this object, instead of being backed by shmem. */
|
||||
struct drm_mm_node *stolen;
|
||||
struct list_head global_list;
|
||||
@@ -1355,18 +1386,32 @@ struct drm_i915_gem_object {
|
||||
|
||||
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
|
||||
|
||||
/* Offset of the first PTE pointing to this object */
|
||||
static inline unsigned long
|
||||
i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o)
|
||||
/* This is a temporary define to help transition us to real VMAs. If you see
|
||||
* this, you're either reviewing code, or bisecting it. */
|
||||
static inline struct i915_vma *
|
||||
__i915_gem_obj_to_vma(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return o->gtt_space.start;
|
||||
if (list_empty(&obj->vma_list))
|
||||
return NULL;
|
||||
return list_first_entry(&obj->vma_list, struct i915_vma, vma_link);
|
||||
}
|
||||
|
||||
/* Whether or not this object is currently mapped by the translation tables */
|
||||
static inline bool
|
||||
i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *o)
|
||||
{
|
||||
return drm_mm_node_allocated(&o->gtt_space);
|
||||
struct i915_vma *vma = __i915_gem_obj_to_vma(o);
|
||||
if (vma == NULL)
|
||||
return false;
|
||||
return drm_mm_node_allocated(&vma->node);
|
||||
}
|
||||
|
||||
/* Offset of the first PTE pointing to this object */
|
||||
static inline unsigned long
|
||||
i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o)
|
||||
{
|
||||
BUG_ON(list_empty(&o->vma_list));
|
||||
return __i915_gem_obj_to_vma(o)->node.start;
|
||||
}
|
||||
|
||||
/* The size used in the translation tables may be larger than the actual size of
|
||||
@@ -1376,14 +1421,15 @@ i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *o)
|
||||
static inline unsigned long
|
||||
i915_gem_obj_ggtt_size(struct drm_i915_gem_object *o)
|
||||
{
|
||||
return o->gtt_space.size;
|
||||
BUG_ON(list_empty(&o->vma_list));
|
||||
return __i915_gem_obj_to_vma(o)->node.size;
|
||||
}
|
||||
|
||||
static inline void
|
||||
i915_gem_obj_ggtt_set_color(struct drm_i915_gem_object *o,
|
||||
enum i915_cache_level color)
|
||||
{
|
||||
o->gtt_space.color = color;
|
||||
__i915_gem_obj_to_vma(o)->node.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1580,10 +1626,12 @@ extern int i915_enable_rc6 __read_mostly;
|
||||
extern int i915_enable_fbc __read_mostly;
|
||||
extern bool i915_enable_hangcheck __read_mostly;
|
||||
extern int i915_enable_ppgtt __read_mostly;
|
||||
extern int i915_enable_psr __read_mostly;
|
||||
extern unsigned int i915_preliminary_hw_support __read_mostly;
|
||||
extern int i915_disable_power_well __read_mostly;
|
||||
extern int i915_enable_ips __read_mostly;
|
||||
extern bool i915_fastboot __read_mostly;
|
||||
extern bool i915_prefault_disable __read_mostly;
|
||||
|
||||
extern int i915_suspend(struct drm_device *dev, pm_message_t state);
|
||||
extern int i915_resume(struct drm_device *dev);
|
||||
@@ -1619,13 +1667,20 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
|
||||
extern void intel_console_resume(struct work_struct *work);
|
||||
|
||||
/* i915_irq.c */
|
||||
void i915_queue_hangcheck(struct drm_device *dev);
|
||||
void i915_hangcheck_elapsed(unsigned long data);
|
||||
void i915_handle_error(struct drm_device *dev, bool wedged);
|
||||
|
||||
extern void intel_irq_init(struct drm_device *dev);
|
||||
extern void intel_hpd_init(struct drm_device *dev);
|
||||
extern void intel_gt_init(struct drm_device *dev);
|
||||
extern void intel_gt_reset(struct drm_device *dev);
|
||||
extern void intel_pm_init(struct drm_device *dev);
|
||||
|
||||
extern void intel_uncore_sanitize(struct drm_device *dev);
|
||||
extern void intel_uncore_early_sanitize(struct drm_device *dev);
|
||||
extern void intel_uncore_init(struct drm_device *dev);
|
||||
extern void intel_uncore_reset(struct drm_device *dev);
|
||||
extern void intel_uncore_clear_errors(struct drm_device *dev);
|
||||
extern void intel_uncore_check_errors(struct drm_device *dev);
|
||||
|
||||
void
|
||||
i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
|
||||
@@ -1689,6 +1744,9 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
|
||||
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
||||
size_t size);
|
||||
void i915_gem_free_object(struct drm_gem_object *obj);
|
||||
struct i915_vma *i915_gem_vma_create(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm);
|
||||
void i915_gem_vma_destroy(struct i915_vma *vma);
|
||||
|
||||
int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
||||
uint32_t alignment,
|
||||
@@ -1857,7 +1915,7 @@ static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
|
||||
}
|
||||
|
||||
struct i915_ctx_hang_stats * __must_check
|
||||
i915_gem_context_get_hang_stats(struct intel_ring_buffer *ring,
|
||||
i915_gem_context_get_hang_stats(struct drm_device *dev,
|
||||
struct drm_file *file,
|
||||
u32 id);
|
||||
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
|
||||
@@ -2056,7 +2114,6 @@ extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
|
||||
*/
|
||||
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
|
||||
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
|
||||
int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
|
||||
|
||||
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val);
|
||||
int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val);
|
||||
@@ -2075,39 +2132,37 @@ void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
|
||||
int vlv_gpu_freq(int ddr_freq, int val);
|
||||
int vlv_freq_opcode(int ddr_freq, int val);
|
||||
|
||||
#define __i915_read(x, y) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg);
|
||||
|
||||
__i915_read(8, b)
|
||||
__i915_read(16, w)
|
||||
__i915_read(32, l)
|
||||
__i915_read(64, q)
|
||||
#define __i915_read(x) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg, bool trace);
|
||||
__i915_read(8)
|
||||
__i915_read(16)
|
||||
__i915_read(32)
|
||||
__i915_read(64)
|
||||
#undef __i915_read
|
||||
|
||||
#define __i915_write(x, y) \
|
||||
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val);
|
||||
|
||||
__i915_write(8, b)
|
||||
__i915_write(16, w)
|
||||
__i915_write(32, l)
|
||||
__i915_write(64, q)
|
||||
#define __i915_write(x) \
|
||||
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val, bool trace);
|
||||
__i915_write(8)
|
||||
__i915_write(16)
|
||||
__i915_write(32)
|
||||
__i915_write(64)
|
||||
#undef __i915_write
|
||||
|
||||
#define I915_READ8(reg) i915_read8(dev_priv, (reg))
|
||||
#define I915_WRITE8(reg, val) i915_write8(dev_priv, (reg), (val))
|
||||
#define I915_READ8(reg) i915_read8(dev_priv, (reg), true)
|
||||
#define I915_WRITE8(reg, val) i915_write8(dev_priv, (reg), (val), true)
|
||||
|
||||
#define I915_READ16(reg) i915_read16(dev_priv, (reg))
|
||||
#define I915_WRITE16(reg, val) i915_write16(dev_priv, (reg), (val))
|
||||
#define I915_READ16_NOTRACE(reg) readw(dev_priv->regs + (reg))
|
||||
#define I915_WRITE16_NOTRACE(reg, val) writew(val, dev_priv->regs + (reg))
|
||||
#define I915_READ16(reg) i915_read16(dev_priv, (reg), true)
|
||||
#define I915_WRITE16(reg, val) i915_write16(dev_priv, (reg), (val), true)
|
||||
#define I915_READ16_NOTRACE(reg) i915_read16(dev_priv, (reg), false)
|
||||
#define I915_WRITE16_NOTRACE(reg, val) i915_write16(dev_priv, (reg), (val), false)
|
||||
|
||||
#define I915_READ(reg) i915_read32(dev_priv, (reg))
|
||||
#define I915_WRITE(reg, val) i915_write32(dev_priv, (reg), (val))
|
||||
#define I915_READ_NOTRACE(reg) readl(dev_priv->regs + (reg))
|
||||
#define I915_WRITE_NOTRACE(reg, val) writel(val, dev_priv->regs + (reg))
|
||||
#define I915_READ(reg) i915_read32(dev_priv, (reg), true)
|
||||
#define I915_WRITE(reg, val) i915_write32(dev_priv, (reg), (val), true)
|
||||
#define I915_READ_NOTRACE(reg) i915_read32(dev_priv, (reg), false)
|
||||
#define I915_WRITE_NOTRACE(reg, val) i915_write32(dev_priv, (reg), (val), false)
|
||||
|
||||
#define I915_WRITE64(reg, val) i915_write64(dev_priv, (reg), (val))
|
||||
#define I915_READ64(reg) i915_read64(dev_priv, (reg))
|
||||
#define I915_WRITE64(reg, val) i915_write64(dev_priv, (reg), (val), true)
|
||||
#define I915_READ64(reg) i915_read64(dev_priv, (reg), true)
|
||||
|
||||
#define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg)
|
||||
#define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg)
|
||||
|
||||
@@ -76,15 +76,19 @@ static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj)
|
||||
static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv,
|
||||
size_t size)
|
||||
{
|
||||
spin_lock(&dev_priv->mm.object_stat_lock);
|
||||
dev_priv->mm.object_count++;
|
||||
dev_priv->mm.object_memory += size;
|
||||
spin_unlock(&dev_priv->mm.object_stat_lock);
|
||||
}
|
||||
|
||||
static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv,
|
||||
size_t size)
|
||||
{
|
||||
spin_lock(&dev_priv->mm.object_stat_lock);
|
||||
dev_priv->mm.object_count--;
|
||||
dev_priv->mm.object_memory -= size;
|
||||
spin_unlock(&dev_priv->mm.object_stat_lock);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -182,7 +186,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
|
||||
pinned += i915_gem_obj_ggtt_size(obj);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
args->aper_size = dev_priv->gtt.total;
|
||||
args->aper_size = dev_priv->gtt.base.total;
|
||||
args->aper_available_size = args->aper_size - pinned;
|
||||
|
||||
return 0;
|
||||
@@ -220,16 +224,10 @@ i915_gem_create(struct drm_file *file,
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_gem_handle_create(file, &obj->base, &handle);
|
||||
if (ret) {
|
||||
drm_gem_object_release(&obj->base);
|
||||
i915_gem_info_remove_obj(dev->dev_private, obj->base.size);
|
||||
i915_gem_object_free(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* drop reference from allocate - handle holds it now */
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
trace_i915_gem_object_create(obj);
|
||||
drm_gem_object_unreference_unlocked(&obj->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*handle_p = handle;
|
||||
return 0;
|
||||
@@ -459,7 +457,7 @@ i915_gem_shmem_pread(struct drm_device *dev,
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (!prefaulted) {
|
||||
if (likely(!i915_prefault_disable) && !prefaulted) {
|
||||
ret = fault_in_multipages_writeable(user_data, remain);
|
||||
/* Userspace is tricking us, but we've already clobbered
|
||||
* its pages with the prefault and promised to write the
|
||||
@@ -854,10 +852,12 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
|
||||
args->size))
|
||||
return -EFAULT;
|
||||
|
||||
ret = fault_in_multipages_readable(to_user_ptr(args->data_ptr),
|
||||
args->size);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
if (likely(!i915_prefault_disable)) {
|
||||
ret = fault_in_multipages_readable(to_user_ptr(args->data_ptr),
|
||||
args->size);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
@@ -1679,6 +1679,7 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
|
||||
bool purgeable_only)
|
||||
{
|
||||
struct drm_i915_gem_object *obj, *next;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
long count = 0;
|
||||
|
||||
list_for_each_entry_safe(obj, next,
|
||||
@@ -1692,9 +1693,7 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(obj, next,
|
||||
&dev_priv->mm.inactive_list,
|
||||
mm_list) {
|
||||
list_for_each_entry_safe(obj, next, &vm->inactive_list, mm_list) {
|
||||
if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) &&
|
||||
i915_gem_object_unbind(obj) == 0 &&
|
||||
i915_gem_object_put_pages(obj) == 0) {
|
||||
@@ -1865,6 +1864,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
u32 seqno = intel_ring_get_seqno(ring);
|
||||
|
||||
BUG_ON(ring == NULL);
|
||||
@@ -1881,7 +1881,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
|
||||
}
|
||||
|
||||
/* Move from whatever list we were on to the tail of execution. */
|
||||
list_move_tail(&obj->mm_list, &dev_priv->mm.active_list);
|
||||
list_move_tail(&obj->mm_list, &vm->active_list);
|
||||
list_move_tail(&obj->ring_list, &ring->active_list);
|
||||
|
||||
obj->last_read_seqno = seqno;
|
||||
@@ -1905,11 +1905,12 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
|
||||
BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS);
|
||||
BUG_ON(!obj->active);
|
||||
|
||||
list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
||||
list_move_tail(&obj->mm_list, &vm->inactive_list);
|
||||
|
||||
list_del_init(&obj->ring_list);
|
||||
obj->ring = NULL;
|
||||
@@ -2074,10 +2075,8 @@ int __i915_add_request(struct intel_ring_buffer *ring,
|
||||
ring->outstanding_lazy_request = 0;
|
||||
|
||||
if (!dev_priv->ums.mm_suspended) {
|
||||
if (i915_enable_hangcheck) {
|
||||
mod_timer(&dev_priv->gpu_error.hangcheck_timer,
|
||||
round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
|
||||
}
|
||||
i915_queue_hangcheck(ring->dev);
|
||||
|
||||
if (was_empty) {
|
||||
queue_delayed_work(dev_priv->wq,
|
||||
&dev_priv->mm.retire_work,
|
||||
@@ -2246,13 +2245,24 @@ void i915_gem_restore_fences(struct drm_device *dev)
|
||||
|
||||
for (i = 0; i < dev_priv->num_fence_regs; i++) {
|
||||
struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i];
|
||||
i915_gem_write_fence(dev, i, reg->obj);
|
||||
|
||||
/*
|
||||
* Commit delayed tiling changes if we have an object still
|
||||
* attached to the fence, otherwise just clear the fence.
|
||||
*/
|
||||
if (reg->obj) {
|
||||
i915_gem_object_update_fence(reg->obj, reg,
|
||||
reg->obj->tiling_mode);
|
||||
} else {
|
||||
i915_gem_write_fence(dev, i, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void i915_gem_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct intel_ring_buffer *ring;
|
||||
int i;
|
||||
@@ -2263,12 +2273,8 @@ void i915_gem_reset(struct drm_device *dev)
|
||||
/* Move everything out of the GPU domains to ensure we do any
|
||||
* necessary invalidation upon reuse.
|
||||
*/
|
||||
list_for_each_entry(obj,
|
||||
&dev_priv->mm.inactive_list,
|
||||
mm_list)
|
||||
{
|
||||
list_for_each_entry(obj, &vm->inactive_list, mm_list)
|
||||
obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
|
||||
}
|
||||
|
||||
i915_gem_restore_fences(dev);
|
||||
}
|
||||
@@ -2571,6 +2577,7 @@ int
|
||||
i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
|
||||
struct i915_vma *vma;
|
||||
int ret;
|
||||
|
||||
if (!i915_gem_obj_ggtt_bound(obj))
|
||||
@@ -2608,11 +2615,20 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
|
||||
list_del(&obj->mm_list);
|
||||
list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
|
||||
/* Avoid an unnecessary call to unbind on rebind. */
|
||||
obj->map_and_fenceable = true;
|
||||
|
||||
drm_mm_remove_node(&obj->gtt_space);
|
||||
vma = __i915_gem_obj_to_vma(obj);
|
||||
list_del(&vma->vma_link);
|
||||
drm_mm_remove_node(&vma->node);
|
||||
i915_gem_vma_destroy(vma);
|
||||
|
||||
/* Since the unbound list is global, only move to that list if
|
||||
* no more VMAs exist.
|
||||
* NB: Until we have real VMAs there will only ever be one */
|
||||
WARN_ON(!list_empty(&obj->vma_list));
|
||||
if (list_empty(&obj->vma_list))
|
||||
list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2781,6 +2797,10 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg,
|
||||
if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj))
|
||||
mb();
|
||||
|
||||
WARN(obj && (!obj->stride || !obj->tiling_mode),
|
||||
"bogus fence setup with stride: 0x%x, tiling mode: %i\n",
|
||||
obj->stride, obj->tiling_mode);
|
||||
|
||||
switch (INTEL_INFO(dev)->gen) {
|
||||
case 7:
|
||||
case 6:
|
||||
@@ -2822,6 +2842,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
|
||||
fence->obj = NULL;
|
||||
list_del_init(&fence->lru_list);
|
||||
}
|
||||
obj->fence_dirty = false;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -2951,7 +2972,6 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
|
||||
return 0;
|
||||
|
||||
i915_gem_object_update_fence(obj, reg, enable);
|
||||
obj->fence_dirty = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3037,12 +3057,17 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
u32 size, fence_size, fence_alignment, unfenced_alignment;
|
||||
bool mappable, fenceable;
|
||||
size_t gtt_max = map_and_fenceable ?
|
||||
dev_priv->gtt.mappable_end : dev_priv->gtt.total;
|
||||
dev_priv->gtt.mappable_end : dev_priv->gtt.base.total;
|
||||
struct i915_vma *vma;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!list_empty(&obj->vma_list)))
|
||||
return -EBUSY;
|
||||
|
||||
fence_size = i915_gem_get_gtt_size(dev,
|
||||
obj->base.size,
|
||||
obj->tiling_mode);
|
||||
@@ -3081,9 +3106,15 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
||||
|
||||
i915_gem_object_pin_pages(obj);
|
||||
|
||||
vma = i915_gem_vma_create(obj, &dev_priv->gtt.base);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
goto err_unpin;
|
||||
}
|
||||
|
||||
search_free:
|
||||
ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space,
|
||||
&obj->gtt_space,
|
||||
ret = drm_mm_insert_node_in_range_generic(&dev_priv->gtt.base.mm,
|
||||
&vma->node,
|
||||
size, alignment,
|
||||
obj->cache_level, 0, gtt_max,
|
||||
DRM_MM_SEARCH_DEFAULT);
|
||||
@@ -3095,25 +3126,21 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
||||
if (ret == 0)
|
||||
goto search_free;
|
||||
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
return ret;
|
||||
goto err_free_vma;
|
||||
}
|
||||
if (WARN_ON(!i915_gem_valid_gtt_space(dev, &obj->gtt_space,
|
||||
if (WARN_ON(!i915_gem_valid_gtt_space(dev, &vma->node,
|
||||
obj->cache_level))) {
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
drm_mm_remove_node(&obj->gtt_space);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_remove_node;
|
||||
}
|
||||
|
||||
ret = i915_gem_gtt_prepare_object(obj);
|
||||
if (ret) {
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
drm_mm_remove_node(&obj->gtt_space);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_remove_node;
|
||||
|
||||
list_move_tail(&obj->global_list, &dev_priv->mm.bound_list);
|
||||
list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
||||
list_add_tail(&obj->mm_list, &vm->inactive_list);
|
||||
list_add(&vma->vma_link, &obj->vma_list);
|
||||
|
||||
fenceable =
|
||||
i915_gem_obj_ggtt_size(obj) == fence_size &&
|
||||
@@ -3127,6 +3154,14 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
|
||||
trace_i915_gem_object_bind(obj, map_and_fenceable);
|
||||
i915_gem_verify_gtt(dev);
|
||||
return 0;
|
||||
|
||||
err_remove_node:
|
||||
drm_mm_remove_node(&vma->node);
|
||||
err_free_vma:
|
||||
i915_gem_vma_destroy(vma);
|
||||
err_unpin:
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3261,7 +3296,8 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
|
||||
|
||||
/* And bump the LRU for this access */
|
||||
if (i915_gem_object_is_inactive(obj))
|
||||
list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
||||
list_move_tail(&obj->mm_list,
|
||||
&dev_priv->gtt.base.inactive_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3271,6 +3307,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct i915_vma *vma = __i915_gem_obj_to_vma(obj);
|
||||
int ret;
|
||||
|
||||
if (obj->cache_level == cache_level)
|
||||
@@ -3281,7 +3318,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!i915_gem_valid_gtt_space(dev, &obj->gtt_space, cache_level)) {
|
||||
if (vma && !i915_gem_valid_gtt_space(dev, &vma->node, cache_level)) {
|
||||
ret = i915_gem_object_unbind(obj);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -3826,6 +3863,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
|
||||
INIT_LIST_HEAD(&obj->global_list);
|
||||
INIT_LIST_HEAD(&obj->ring_list);
|
||||
INIT_LIST_HEAD(&obj->exec_list);
|
||||
INIT_LIST_HEAD(&obj->vma_list);
|
||||
|
||||
obj->ops = ops;
|
||||
|
||||
@@ -3890,6 +3928,8 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
||||
} else
|
||||
obj->cache_level = I915_CACHE_NONE;
|
||||
|
||||
trace_i915_gem_object_create(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -3946,6 +3986,26 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
|
||||
i915_gem_object_free(obj);
|
||||
}
|
||||
|
||||
struct i915_vma *i915_gem_vma_create(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
|
||||
if (vma == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&vma->vma_link);
|
||||
vma->vm = vm;
|
||||
vma->obj = obj;
|
||||
|
||||
return vma;
|
||||
}
|
||||
|
||||
void i915_gem_vma_destroy(struct i915_vma *vma)
|
||||
{
|
||||
WARN_ON(vma->node.allocated);
|
||||
kfree(vma);
|
||||
}
|
||||
|
||||
int
|
||||
i915_gem_idle(struct drm_device *dev)
|
||||
{
|
||||
@@ -4105,8 +4165,8 @@ i915_gem_init_hw(struct drm_device *dev)
|
||||
if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
|
||||
return -EIO;
|
||||
|
||||
if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1))
|
||||
I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000);
|
||||
if (dev_priv->ellc_size)
|
||||
I915_WRITE(HSW_IDICR, I915_READ(HSW_IDICR) | IDIHASHMSK(0xf));
|
||||
|
||||
if (HAS_PCH_NOP(dev)) {
|
||||
u32 temp = I915_READ(GEN7_MSG_CTL);
|
||||
@@ -4202,7 +4262,7 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
BUG_ON(!list_empty(&dev_priv->mm.active_list));
|
||||
BUG_ON(!list_empty(&dev_priv->gtt.base.active_list));
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
ret = drm_irq_install(dev);
|
||||
@@ -4280,8 +4340,8 @@ i915_gem_load(struct drm_device *dev)
|
||||
SLAB_HWCACHE_ALIGN,
|
||||
NULL);
|
||||
|
||||
INIT_LIST_HEAD(&dev_priv->mm.active_list);
|
||||
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
|
||||
INIT_LIST_HEAD(&dev_priv->gtt.base.active_list);
|
||||
INIT_LIST_HEAD(&dev_priv->gtt.base.inactive_list);
|
||||
INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
|
||||
INIT_LIST_HEAD(&dev_priv->mm.bound_list);
|
||||
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
|
||||
@@ -4552,6 +4612,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
struct drm_i915_private,
|
||||
mm.inactive_shrinker);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int nr_to_scan = sc->nr_to_scan;
|
||||
bool unlock = true;
|
||||
@@ -4580,7 +4641,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
|
||||
if (obj->pages_pin_count == 0)
|
||||
cnt += obj->base.size >> PAGE_SHIFT;
|
||||
list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list)
|
||||
list_for_each_entry(obj, &vm->inactive_list, mm_list)
|
||||
if (obj->pin_count == 0 && obj->pages_pin_count == 0)
|
||||
cnt += obj->base.size >> PAGE_SHIFT;
|
||||
|
||||
|
||||
@@ -304,31 +304,24 @@ static int context_idr_cleanup(int id, void *p, void *data)
|
||||
}
|
||||
|
||||
struct i915_ctx_hang_stats *
|
||||
i915_gem_context_get_hang_stats(struct intel_ring_buffer *ring,
|
||||
i915_gem_context_get_hang_stats(struct drm_device *dev,
|
||||
struct drm_file *file,
|
||||
u32 id)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_file_private *file_priv = file->driver_priv;
|
||||
struct i915_hw_context *to;
|
||||
|
||||
if (dev_priv->hw_contexts_disabled)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (ring->id != RCS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (file == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
struct i915_hw_context *ctx;
|
||||
|
||||
if (id == DEFAULT_CONTEXT_ID)
|
||||
return &file_priv->hang_stats;
|
||||
|
||||
to = i915_gem_context_get(file->driver_priv, id);
|
||||
if (to == NULL)
|
||||
ctx = NULL;
|
||||
if (!dev_priv->hw_contexts_disabled)
|
||||
ctx = i915_gem_context_get(file->driver_priv, id);
|
||||
if (ctx == NULL)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return &to->hang_stats;
|
||||
return &ctx->hang_stats;
|
||||
}
|
||||
|
||||
void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
|
||||
|
||||
@@ -97,7 +97,7 @@ i915_verify_lists(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(obj, &dev_priv->mm.inactive_list, list) {
|
||||
list_for_each_entry(obj, &i915_gtt_vm->inactive_list, list) {
|
||||
if (obj->base.dev != dev ||
|
||||
!atomic_read(&obj->base.refcount.refcount)) {
|
||||
DRM_ERROR("freed inactive %p\n", obj);
|
||||
|
||||
@@ -34,11 +34,13 @@
|
||||
static bool
|
||||
mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
|
||||
{
|
||||
struct i915_vma *vma = __i915_gem_obj_to_vma(obj);
|
||||
|
||||
if (obj->pin_count)
|
||||
return false;
|
||||
|
||||
list_add(&obj->exec_list, unwind);
|
||||
return drm_mm_scan_add_block(&obj->gtt_space);
|
||||
return drm_mm_scan_add_block(&vma->node);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -47,7 +49,9 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
bool mappable, bool nonblocking)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct list_head eviction_list, unwind_list;
|
||||
struct i915_vma *vma;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
@@ -78,15 +82,14 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
|
||||
INIT_LIST_HEAD(&unwind_list);
|
||||
if (mappable)
|
||||
drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space,
|
||||
min_size, alignment, cache_level,
|
||||
0, dev_priv->gtt.mappable_end);
|
||||
drm_mm_init_scan_with_range(&vm->mm, min_size,
|
||||
alignment, cache_level, 0,
|
||||
dev_priv->gtt.mappable_end);
|
||||
else
|
||||
drm_mm_init_scan(&dev_priv->mm.gtt_space,
|
||||
min_size, alignment, cache_level);
|
||||
drm_mm_init_scan(&vm->mm, min_size, alignment, cache_level);
|
||||
|
||||
/* First see if there is a large enough contiguous idle region... */
|
||||
list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) {
|
||||
list_for_each_entry(obj, &vm->inactive_list, mm_list) {
|
||||
if (mark_free(obj, &unwind_list))
|
||||
goto found;
|
||||
}
|
||||
@@ -95,7 +98,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
goto none;
|
||||
|
||||
/* Now merge in the soon-to-be-expired objects... */
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
|
||||
list_for_each_entry(obj, &vm->active_list, mm_list) {
|
||||
if (mark_free(obj, &unwind_list))
|
||||
goto found;
|
||||
}
|
||||
@@ -106,8 +109,8 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
obj = list_first_entry(&unwind_list,
|
||||
struct drm_i915_gem_object,
|
||||
exec_list);
|
||||
|
||||
ret = drm_mm_scan_remove_block(&obj->gtt_space);
|
||||
vma = __i915_gem_obj_to_vma(obj);
|
||||
ret = drm_mm_scan_remove_block(&vma->node);
|
||||
BUG_ON(ret);
|
||||
|
||||
list_del_init(&obj->exec_list);
|
||||
@@ -127,7 +130,8 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
obj = list_first_entry(&unwind_list,
|
||||
struct drm_i915_gem_object,
|
||||
exec_list);
|
||||
if (drm_mm_scan_remove_block(&obj->gtt_space)) {
|
||||
vma = __i915_gem_obj_to_vma(obj);
|
||||
if (drm_mm_scan_remove_block(&vma->node)) {
|
||||
list_move(&obj->exec_list, &eviction_list);
|
||||
drm_gem_object_reference(&obj->base);
|
||||
continue;
|
||||
@@ -154,12 +158,13 @@ int
|
||||
i915_gem_evict_everything(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj, *next;
|
||||
bool lists_empty;
|
||||
int ret;
|
||||
|
||||
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
|
||||
list_empty(&dev_priv->mm.active_list));
|
||||
lists_empty = (list_empty(&vm->inactive_list) &&
|
||||
list_empty(&vm->active_list));
|
||||
if (lists_empty)
|
||||
return -ENOSPC;
|
||||
|
||||
@@ -176,8 +181,7 @@ i915_gem_evict_everything(struct drm_device *dev)
|
||||
i915_gem_retire_requests(dev);
|
||||
|
||||
/* Having flushed everything, unbind() should never raise an error */
|
||||
list_for_each_entry_safe(obj, next,
|
||||
&dev_priv->mm.inactive_list, mm_list)
|
||||
list_for_each_entry_safe(obj, next, &vm->inactive_list, mm_list)
|
||||
if (obj->pin_count == 0)
|
||||
WARN_ON(i915_gem_object_unbind(obj));
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
|
||||
|
||||
reloc->delta += target_offset;
|
||||
if (use_cpu_reloc(obj)) {
|
||||
uint32_t page_offset = reloc->offset & ~PAGE_MASK;
|
||||
uint32_t page_offset = offset_in_page(reloc->offset);
|
||||
char *vaddr;
|
||||
|
||||
ret = i915_gem_object_set_to_cpu_domain(obj, 1);
|
||||
@@ -284,7 +284,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
|
||||
reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
|
||||
reloc->offset & PAGE_MASK);
|
||||
reloc_entry = (uint32_t __iomem *)
|
||||
(reloc_page + (reloc->offset & ~PAGE_MASK));
|
||||
(reloc_page + offset_in_page(reloc->offset));
|
||||
iowrite32(reloc->delta, reloc_entry);
|
||||
io_mapping_unmap_atomic(reloc_page);
|
||||
}
|
||||
@@ -759,8 +759,10 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
|
||||
if (!access_ok(VERIFY_WRITE, ptr, length))
|
||||
return -EFAULT;
|
||||
|
||||
if (fault_in_multipages_readable(ptr, length))
|
||||
return -EFAULT;
|
||||
if (likely(!i915_prefault_disable)) {
|
||||
if (fault_in_multipages_readable(ptr, length))
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -873,7 +875,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
break;
|
||||
case I915_EXEC_BSD:
|
||||
ring = &dev_priv->ring[VCS];
|
||||
if (ctx_id != 0) {
|
||||
if (ctx_id != DEFAULT_CONTEXT_ID) {
|
||||
DRM_DEBUG("Ring %s doesn't support contexts\n",
|
||||
ring->name);
|
||||
return -EPERM;
|
||||
@@ -881,7 +883,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
break;
|
||||
case I915_EXEC_BLT:
|
||||
ring = &dev_priv->ring[BCS];
|
||||
if (ctx_id != 0) {
|
||||
if (ctx_id != DEFAULT_CONTEXT_ID) {
|
||||
DRM_DEBUG("Ring %s doesn't support contexts\n",
|
||||
ring->name);
|
||||
return -EPERM;
|
||||
@@ -889,7 +891,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
break;
|
||||
case I915_EXEC_VEBOX:
|
||||
ring = &dev_priv->ring[VECS];
|
||||
if (ctx_id != 0) {
|
||||
if (ctx_id != DEFAULT_CONTEXT_ID) {
|
||||
DRM_DEBUG("Ring %s doesn't support contexts\n",
|
||||
ring->name);
|
||||
return -EPERM;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
/* PPGTT stuff */
|
||||
#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
|
||||
#define HSW_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0x7f0))
|
||||
|
||||
#define GEN6_PDE_VALID (1 << 0)
|
||||
/* gen6+ has bit 11-4 for physical addr bit 39-32 */
|
||||
@@ -44,6 +45,15 @@
|
||||
#define GEN6_PTE_CACHE_LLC (2 << 1)
|
||||
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
|
||||
#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
|
||||
#define HSW_PTE_ADDR_ENCODE(addr) HSW_GTT_ADDR_ENCODE(addr)
|
||||
|
||||
/* Cacheability Control is a 4-bit value. The low three bits are stored in *
|
||||
* bits 3:1 of the PTE, while the fourth bit is stored in bit 11 of the PTE.
|
||||
*/
|
||||
#define HSW_CACHEABILITY_CONTROL(bits) ((((bits) & 0x7) << 1) | \
|
||||
(((bits) & 0x8) << (11 - 3)))
|
||||
#define HSW_WB_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x3)
|
||||
#define HSW_WB_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0xb)
|
||||
|
||||
static gen6_gtt_pte_t gen6_pte_encode(dma_addr_t addr,
|
||||
enum i915_cache_level level)
|
||||
@@ -92,17 +102,29 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
|
||||
enum i915_cache_level level)
|
||||
{
|
||||
gen6_gtt_pte_t pte = GEN6_PTE_VALID;
|
||||
pte |= GEN6_PTE_ADDR_ENCODE(addr);
|
||||
pte |= HSW_PTE_ADDR_ENCODE(addr);
|
||||
|
||||
if (level != I915_CACHE_NONE)
|
||||
pte |= GEN6_PTE_CACHE_LLC;
|
||||
pte |= HSW_WB_LLC_AGE0;
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
|
||||
enum i915_cache_level level)
|
||||
{
|
||||
gen6_gtt_pte_t pte = GEN6_PTE_VALID;
|
||||
pte |= HSW_PTE_ADDR_ENCODE(addr);
|
||||
|
||||
if (level != I915_CACHE_NONE)
|
||||
pte |= HSW_WB_ELLC_LLC_AGE0;
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ppgtt->dev->dev_private;
|
||||
struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
|
||||
gen6_gtt_pte_t __iomem *pd_addr;
|
||||
uint32_t pd_entry;
|
||||
int i;
|
||||
@@ -181,18 +203,18 @@ static int gen6_ppgtt_enable(struct drm_device *dev)
|
||||
}
|
||||
|
||||
/* PPGTT support for Sandybdrige/Gen6 and later */
|
||||
static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
|
||||
static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
|
||||
unsigned first_entry,
|
||||
unsigned num_entries)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ppgtt->dev->dev_private;
|
||||
struct i915_hw_ppgtt *ppgtt =
|
||||
container_of(vm, struct i915_hw_ppgtt, base);
|
||||
gen6_gtt_pte_t *pt_vaddr, scratch_pte;
|
||||
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
|
||||
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
|
||||
unsigned last_pte, i;
|
||||
|
||||
scratch_pte = ppgtt->pte_encode(dev_priv->gtt.scratch.addr,
|
||||
I915_CACHE_LLC);
|
||||
scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
|
||||
|
||||
while (num_entries) {
|
||||
last_pte = first_pte + num_entries;
|
||||
@@ -212,11 +234,13 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
|
||||
}
|
||||
}
|
||||
|
||||
static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
|
||||
static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
|
||||
struct sg_table *pages,
|
||||
unsigned first_entry,
|
||||
enum i915_cache_level cache_level)
|
||||
{
|
||||
struct i915_hw_ppgtt *ppgtt =
|
||||
container_of(vm, struct i915_hw_ppgtt, base);
|
||||
gen6_gtt_pte_t *pt_vaddr;
|
||||
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
|
||||
unsigned act_pte = first_entry % I915_PPGTT_PT_ENTRIES;
|
||||
@@ -227,7 +251,7 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
|
||||
dma_addr_t page_addr;
|
||||
|
||||
page_addr = sg_page_iter_dma_address(&sg_iter);
|
||||
pt_vaddr[act_pte] = ppgtt->pte_encode(page_addr, cache_level);
|
||||
pt_vaddr[act_pte] = vm->pte_encode(page_addr, cache_level);
|
||||
if (++act_pte == I915_PPGTT_PT_ENTRIES) {
|
||||
kunmap_atomic(pt_vaddr);
|
||||
act_pt++;
|
||||
@@ -239,13 +263,17 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
|
||||
kunmap_atomic(pt_vaddr);
|
||||
}
|
||||
|
||||
static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
|
||||
static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
|
||||
{
|
||||
struct i915_hw_ppgtt *ppgtt =
|
||||
container_of(vm, struct i915_hw_ppgtt, base);
|
||||
int i;
|
||||
|
||||
drm_mm_takedown(&ppgtt->base.mm);
|
||||
|
||||
if (ppgtt->pt_dma_addr) {
|
||||
for (i = 0; i < ppgtt->num_pd_entries; i++)
|
||||
pci_unmap_page(ppgtt->dev->pdev,
|
||||
pci_unmap_page(ppgtt->base.dev->pdev,
|
||||
ppgtt->pt_dma_addr[i],
|
||||
4096, PCI_DMA_BIDIRECTIONAL);
|
||||
}
|
||||
@@ -259,7 +287,7 @@ static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
|
||||
|
||||
static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
|
||||
{
|
||||
struct drm_device *dev = ppgtt->dev;
|
||||
struct drm_device *dev = ppgtt->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
unsigned first_pd_entry_in_global_pt;
|
||||
int i;
|
||||
@@ -271,17 +299,18 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
|
||||
first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt);
|
||||
|
||||
if (IS_HASWELL(dev)) {
|
||||
ppgtt->pte_encode = hsw_pte_encode;
|
||||
ppgtt->base.pte_encode = hsw_pte_encode;
|
||||
} else if (IS_VALLEYVIEW(dev)) {
|
||||
ppgtt->pte_encode = byt_pte_encode;
|
||||
ppgtt->base.pte_encode = byt_pte_encode;
|
||||
} else {
|
||||
ppgtt->pte_encode = gen6_pte_encode;
|
||||
ppgtt->base.pte_encode = gen6_pte_encode;
|
||||
}
|
||||
ppgtt->num_pd_entries = GEN6_PPGTT_PD_ENTRIES;
|
||||
ppgtt->enable = gen6_ppgtt_enable;
|
||||
ppgtt->clear_range = gen6_ppgtt_clear_range;
|
||||
ppgtt->insert_entries = gen6_ppgtt_insert_entries;
|
||||
ppgtt->cleanup = gen6_ppgtt_cleanup;
|
||||
ppgtt->base.clear_range = gen6_ppgtt_clear_range;
|
||||
ppgtt->base.insert_entries = gen6_ppgtt_insert_entries;
|
||||
ppgtt->base.cleanup = gen6_ppgtt_cleanup;
|
||||
ppgtt->base.scratch = dev_priv->gtt.base.scratch;
|
||||
ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries,
|
||||
GFP_KERNEL);
|
||||
if (!ppgtt->pt_pages)
|
||||
@@ -312,8 +341,8 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
|
||||
ppgtt->pt_dma_addr[i] = pt_addr;
|
||||
}
|
||||
|
||||
ppgtt->clear_range(ppgtt, 0,
|
||||
ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
|
||||
ppgtt->base.clear_range(&ppgtt->base, 0,
|
||||
ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES);
|
||||
|
||||
ppgtt->pd_offset = first_pd_entry_in_global_pt * sizeof(gen6_gtt_pte_t);
|
||||
|
||||
@@ -346,7 +375,7 @@ static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
|
||||
if (!ppgtt)
|
||||
return -ENOMEM;
|
||||
|
||||
ppgtt->dev = dev;
|
||||
ppgtt->base.dev = dev;
|
||||
|
||||
if (INTEL_INFO(dev)->gen < 8)
|
||||
ret = gen6_ppgtt_init(ppgtt);
|
||||
@@ -355,8 +384,11 @@ static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
|
||||
|
||||
if (ret)
|
||||
kfree(ppgtt);
|
||||
else
|
||||
else {
|
||||
dev_priv->mm.aliasing_ppgtt = ppgtt;
|
||||
drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
|
||||
ppgtt->base.total);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -369,7 +401,7 @@ void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
|
||||
if (!ppgtt)
|
||||
return;
|
||||
|
||||
ppgtt->cleanup(ppgtt);
|
||||
ppgtt->base.cleanup(&ppgtt->base);
|
||||
dev_priv->mm.aliasing_ppgtt = NULL;
|
||||
}
|
||||
|
||||
@@ -377,17 +409,17 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
|
||||
struct drm_i915_gem_object *obj,
|
||||
enum i915_cache_level cache_level)
|
||||
{
|
||||
ppgtt->insert_entries(ppgtt, obj->pages,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
cache_level);
|
||||
ppgtt->base.insert_entries(&ppgtt->base, obj->pages,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
cache_level);
|
||||
}
|
||||
|
||||
void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
|
||||
struct drm_i915_gem_object *obj)
|
||||
{
|
||||
ppgtt->clear_range(ppgtt,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
obj->base.size >> PAGE_SHIFT);
|
||||
ppgtt->base.clear_range(&ppgtt->base,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
obj->base.size >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
extern int intel_iommu_gfx_mapped;
|
||||
@@ -434,8 +466,9 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
|
||||
struct drm_i915_gem_object *obj;
|
||||
|
||||
/* First fill our portion of the GTT with scratch pages */
|
||||
dev_priv->gtt.gtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE,
|
||||
dev_priv->gtt.total / PAGE_SIZE);
|
||||
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
|
||||
dev_priv->gtt.base.start / PAGE_SIZE,
|
||||
dev_priv->gtt.base.total / PAGE_SIZE);
|
||||
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
||||
i915_gem_clflush_object(obj);
|
||||
@@ -464,12 +497,12 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
|
||||
* within the global GTT as well as accessible by the GPU through the GMADR
|
||||
* mapped BAR (dev_priv->mm.gtt->gtt).
|
||||
*/
|
||||
static void gen6_ggtt_insert_entries(struct drm_device *dev,
|
||||
static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
|
||||
struct sg_table *st,
|
||||
unsigned int first_entry,
|
||||
enum i915_cache_level level)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_private *dev_priv = vm->dev->dev_private;
|
||||
gen6_gtt_pte_t __iomem *gtt_entries =
|
||||
(gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
|
||||
int i = 0;
|
||||
@@ -478,8 +511,7 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev,
|
||||
|
||||
for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
|
||||
addr = sg_page_iter_dma_address(&sg_iter);
|
||||
iowrite32(dev_priv->gtt.pte_encode(addr, level),
|
||||
>t_entries[i]);
|
||||
iowrite32(vm->pte_encode(addr, level), >t_entries[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -490,8 +522,8 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev,
|
||||
* hardware should work, we must keep this posting read for paranoia.
|
||||
*/
|
||||
if (i != 0)
|
||||
WARN_ON(readl(>t_entries[i-1])
|
||||
!= dev_priv->gtt.pte_encode(addr, level));
|
||||
WARN_ON(readl(>t_entries[i-1]) !=
|
||||
vm->pte_encode(addr, level));
|
||||
|
||||
/* This next bit makes the above posting read even more important. We
|
||||
* want to flush the TLBs only after we're certain all the PTE updates
|
||||
@@ -501,11 +533,11 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev,
|
||||
POSTING_READ(GFX_FLSH_CNTL_GEN6);
|
||||
}
|
||||
|
||||
static void gen6_ggtt_clear_range(struct drm_device *dev,
|
||||
static void gen6_ggtt_clear_range(struct i915_address_space *vm,
|
||||
unsigned int first_entry,
|
||||
unsigned int num_entries)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_private *dev_priv = vm->dev->dev_private;
|
||||
gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
|
||||
(gen6_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
|
||||
const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
|
||||
@@ -516,15 +548,14 @@ static void gen6_ggtt_clear_range(struct drm_device *dev,
|
||||
first_entry, num_entries, max_entries))
|
||||
num_entries = max_entries;
|
||||
|
||||
scratch_pte = dev_priv->gtt.pte_encode(dev_priv->gtt.scratch.addr,
|
||||
I915_CACHE_LLC);
|
||||
scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
|
||||
for (i = 0; i < num_entries; i++)
|
||||
iowrite32(scratch_pte, >t_base[i]);
|
||||
readl(gtt_base);
|
||||
}
|
||||
|
||||
|
||||
static void i915_ggtt_insert_entries(struct drm_device *dev,
|
||||
static void i915_ggtt_insert_entries(struct i915_address_space *vm,
|
||||
struct sg_table *st,
|
||||
unsigned int pg_start,
|
||||
enum i915_cache_level cache_level)
|
||||
@@ -536,7 +567,7 @@ static void i915_ggtt_insert_entries(struct drm_device *dev,
|
||||
|
||||
}
|
||||
|
||||
static void i915_ggtt_clear_range(struct drm_device *dev,
|
||||
static void i915_ggtt_clear_range(struct i915_address_space *vm,
|
||||
unsigned int first_entry,
|
||||
unsigned int num_entries)
|
||||
{
|
||||
@@ -549,10 +580,11 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
const unsigned long entry = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT;
|
||||
|
||||
dev_priv->gtt.gtt_insert_entries(dev, obj->pages,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
cache_level);
|
||||
dev_priv->gtt.base.insert_entries(&dev_priv->gtt.base, obj->pages,
|
||||
entry,
|
||||
cache_level);
|
||||
|
||||
obj->has_global_gtt_mapping = 1;
|
||||
}
|
||||
@@ -561,10 +593,11 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
const unsigned long entry = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT;
|
||||
|
||||
dev_priv->gtt.gtt_clear_range(obj->base.dev,
|
||||
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
||||
obj->base.size >> PAGE_SHIFT);
|
||||
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
|
||||
entry,
|
||||
obj->base.size >> PAGE_SHIFT);
|
||||
|
||||
obj->has_global_gtt_mapping = 0;
|
||||
}
|
||||
@@ -623,38 +656,42 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
|
||||
BUG_ON(mappable_end > end);
|
||||
|
||||
/* Subtract the guard page ... */
|
||||
drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE);
|
||||
drm_mm_init(&dev_priv->gtt.base.mm, start, end - start - PAGE_SIZE);
|
||||
if (!HAS_LLC(dev))
|
||||
dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
|
||||
dev_priv->gtt.base.mm.color_adjust = i915_gtt_color_adjust;
|
||||
|
||||
/* Mark any preallocated objects as occupied */
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
||||
struct i915_vma *vma = __i915_gem_obj_to_vma(obj);
|
||||
int ret;
|
||||
DRM_DEBUG_KMS("reserving preallocated space: %lx + %zx\n",
|
||||
i915_gem_obj_ggtt_offset(obj), obj->base.size);
|
||||
|
||||
WARN_ON(i915_gem_obj_ggtt_bound(obj));
|
||||
ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space,
|
||||
&obj->gtt_space);
|
||||
ret = drm_mm_reserve_node(&dev_priv->gtt.base.mm, &vma->node);
|
||||
if (ret)
|
||||
DRM_DEBUG_KMS("Reservation failed\n");
|
||||
obj->has_global_gtt_mapping = 1;
|
||||
list_add(&vma->vma_link, &obj->vma_list);
|
||||
}
|
||||
|
||||
dev_priv->gtt.start = start;
|
||||
dev_priv->gtt.total = end - start;
|
||||
dev_priv->gtt.base.start = start;
|
||||
dev_priv->gtt.base.total = end - start;
|
||||
|
||||
/* Clear any non-preallocated blocks */
|
||||
drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space,
|
||||
drm_mm_for_each_hole(entry, &dev_priv->gtt.base.mm,
|
||||
hole_start, hole_end) {
|
||||
const unsigned long count = (hole_end - hole_start) / PAGE_SIZE;
|
||||
DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
|
||||
hole_start, hole_end);
|
||||
dev_priv->gtt.gtt_clear_range(dev, hole_start / PAGE_SIZE,
|
||||
(hole_end-hole_start) / PAGE_SIZE);
|
||||
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
|
||||
hole_start / PAGE_SIZE,
|
||||
count);
|
||||
}
|
||||
|
||||
/* And finally clear the reserved guard page */
|
||||
dev_priv->gtt.gtt_clear_range(dev, end / PAGE_SIZE - 1, 1);
|
||||
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
|
||||
end / PAGE_SIZE - 1, 1);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -677,7 +714,7 @@ void i915_gem_init_global_gtt(struct drm_device *dev)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
unsigned long gtt_size, mappable_size;
|
||||
|
||||
gtt_size = dev_priv->gtt.total;
|
||||
gtt_size = dev_priv->gtt.base.total;
|
||||
mappable_size = dev_priv->gtt.mappable_end;
|
||||
|
||||
if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) {
|
||||
@@ -696,7 +733,7 @@ void i915_gem_init_global_gtt(struct drm_device *dev)
|
||||
return;
|
||||
|
||||
DRM_ERROR("Aliased PPGTT setup failed %d\n", ret);
|
||||
drm_mm_takedown(&dev_priv->mm.gtt_space);
|
||||
drm_mm_takedown(&dev_priv->gtt.base.mm);
|
||||
gtt_size += GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE;
|
||||
}
|
||||
i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
|
||||
@@ -722,8 +759,8 @@ static int setup_scratch_page(struct drm_device *dev)
|
||||
#else
|
||||
dma_addr = page_to_phys(page);
|
||||
#endif
|
||||
dev_priv->gtt.scratch.page = page;
|
||||
dev_priv->gtt.scratch.addr = dma_addr;
|
||||
dev_priv->gtt.base.scratch.page = page;
|
||||
dev_priv->gtt.base.scratch.addr = dma_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -731,11 +768,13 @@ static int setup_scratch_page(struct drm_device *dev)
|
||||
static void teardown_scratch_page(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
set_pages_wb(dev_priv->gtt.scratch.page, 1);
|
||||
pci_unmap_page(dev->pdev, dev_priv->gtt.scratch.addr,
|
||||
struct page *page = dev_priv->gtt.base.scratch.page;
|
||||
|
||||
set_pages_wb(page, 1);
|
||||
pci_unmap_page(dev->pdev, dev_priv->gtt.base.scratch.addr,
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
put_page(dev_priv->gtt.scratch.page);
|
||||
__free_page(dev_priv->gtt.scratch.page);
|
||||
put_page(page);
|
||||
__free_page(page);
|
||||
}
|
||||
|
||||
static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
|
||||
@@ -798,17 +837,18 @@ static int gen6_gmch_probe(struct drm_device *dev,
|
||||
if (ret)
|
||||
DRM_ERROR("Scratch setup failed\n");
|
||||
|
||||
dev_priv->gtt.gtt_clear_range = gen6_ggtt_clear_range;
|
||||
dev_priv->gtt.gtt_insert_entries = gen6_ggtt_insert_entries;
|
||||
dev_priv->gtt.base.clear_range = gen6_ggtt_clear_range;
|
||||
dev_priv->gtt.base.insert_entries = gen6_ggtt_insert_entries;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gen6_gmch_remove(struct drm_device *dev)
|
||||
static void gen6_gmch_remove(struct i915_address_space *vm)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
iounmap(dev_priv->gtt.gsm);
|
||||
teardown_scratch_page(dev_priv->dev);
|
||||
|
||||
struct i915_gtt *gtt = container_of(vm, struct i915_gtt, base);
|
||||
iounmap(gtt->gsm);
|
||||
teardown_scratch_page(vm->dev);
|
||||
}
|
||||
|
||||
static int i915_gmch_probe(struct drm_device *dev,
|
||||
@@ -829,13 +869,13 @@ static int i915_gmch_probe(struct drm_device *dev,
|
||||
intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end);
|
||||
|
||||
dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev);
|
||||
dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range;
|
||||
dev_priv->gtt.gtt_insert_entries = i915_ggtt_insert_entries;
|
||||
dev_priv->gtt.base.clear_range = i915_ggtt_clear_range;
|
||||
dev_priv->gtt.base.insert_entries = i915_ggtt_insert_entries;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i915_gmch_remove(struct drm_device *dev)
|
||||
static void i915_gmch_remove(struct i915_address_space *vm)
|
||||
{
|
||||
intel_gmch_remove();
|
||||
}
|
||||
@@ -848,25 +888,30 @@ int i915_gem_gtt_init(struct drm_device *dev)
|
||||
|
||||
if (INTEL_INFO(dev)->gen <= 5) {
|
||||
gtt->gtt_probe = i915_gmch_probe;
|
||||
gtt->gtt_remove = i915_gmch_remove;
|
||||
gtt->base.cleanup = i915_gmch_remove;
|
||||
} else {
|
||||
gtt->gtt_probe = gen6_gmch_probe;
|
||||
gtt->gtt_remove = gen6_gmch_remove;
|
||||
if (IS_HASWELL(dev))
|
||||
gtt->pte_encode = hsw_pte_encode;
|
||||
gtt->base.cleanup = gen6_gmch_remove;
|
||||
if (IS_HASWELL(dev) && dev_priv->ellc_size)
|
||||
gtt->base.pte_encode = iris_pte_encode;
|
||||
else if (IS_HASWELL(dev))
|
||||
gtt->base.pte_encode = hsw_pte_encode;
|
||||
else if (IS_VALLEYVIEW(dev))
|
||||
gtt->pte_encode = byt_pte_encode;
|
||||
gtt->base.pte_encode = byt_pte_encode;
|
||||
else
|
||||
gtt->pte_encode = gen6_pte_encode;
|
||||
gtt->base.pte_encode = gen6_pte_encode;
|
||||
}
|
||||
|
||||
ret = gtt->gtt_probe(dev, >t->total, >t->stolen_size,
|
||||
ret = gtt->gtt_probe(dev, >t->base.total, >t->stolen_size,
|
||||
>t->mappable_base, >t->mappable_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gtt->base.dev = dev;
|
||||
|
||||
/* GMADR is the PCI mmio aperture into the global GTT. */
|
||||
DRM_INFO("Memory usable by graphics device = %zdM\n", gtt->total >> 20);
|
||||
DRM_INFO("Memory usable by graphics device = %zdM\n",
|
||||
gtt->base.total >> 20);
|
||||
DRM_DEBUG_DRIVER("GMADR size = %ldM\n", gtt->mappable_end >> 20);
|
||||
DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", gtt->stolen_size >> 20);
|
||||
|
||||
|
||||
@@ -45,45 +45,27 @@
|
||||
static unsigned long i915_stolen_to_physical(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct pci_dev *pdev = dev_priv->bridge_dev;
|
||||
struct resource *r;
|
||||
u32 base;
|
||||
|
||||
/* On the machines I have tested the Graphics Base of Stolen Memory
|
||||
* is unreliable, so on those compute the base by subtracting the
|
||||
* stolen memory from the Top of Low Usable DRAM which is where the
|
||||
* BIOS places the graphics stolen memory.
|
||||
/* Almost universally we can find the Graphics Base of Stolen Memory
|
||||
* at offset 0x5c in the igfx configuration space. On a few (desktop)
|
||||
* machines this is also mirrored in the bridge device at different
|
||||
* locations, or in the MCHBAR. On gen2, the layout is again slightly
|
||||
* different with the Graphics Segment immediately following Top of
|
||||
* Memory (or Top of Usable DRAM). Note it appears that TOUD is only
|
||||
* reported by 865g, so we just use the top of memory as determined
|
||||
* by the e820 probe.
|
||||
*
|
||||
* On gen2, the layout is slightly different with the Graphics Segment
|
||||
* immediately following Top of Memory (or Top of Usable DRAM). Note
|
||||
* it appears that TOUD is only reported by 865g, so we just use the
|
||||
* top of memory as determined by the e820 probe.
|
||||
*
|
||||
* XXX gen2 requires an unavailable symbol and 945gm fails with
|
||||
* its value of TOLUD.
|
||||
* XXX However gen2 requires an unavailable symbol.
|
||||
*/
|
||||
base = 0;
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
if (INTEL_INFO(dev)->gen >= 3) {
|
||||
/* Read Graphics Base of Stolen Memory directly */
|
||||
pci_read_config_dword(dev->pdev, 0x5c, &base);
|
||||
base &= ~((1<<20) - 1);
|
||||
} else if (INTEL_INFO(dev)->gen >= 6) {
|
||||
/* Read Base Data of Stolen Memory Register (BDSM) directly.
|
||||
* Note that there is also a MCHBAR miror at 0x1080c0 or
|
||||
* we could use device 2:0x5c instead.
|
||||
*/
|
||||
pci_read_config_dword(pdev, 0xB0, &base);
|
||||
base &= ~4095; /* lower bits used for locking register */
|
||||
} else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
|
||||
/* Read Graphics Base of Stolen Memory directly */
|
||||
pci_read_config_dword(pdev, 0xA4, &base);
|
||||
} else { /* GEN2 */
|
||||
#if 0
|
||||
} else if (IS_GEN3(dev)) {
|
||||
u8 val;
|
||||
/* Stolen is immediately below Top of Low Usable DRAM */
|
||||
pci_read_config_byte(pdev, 0x9c, &val);
|
||||
base = val >> 3 << 27;
|
||||
base -= dev_priv->mm.gtt->stolen_size;
|
||||
} else {
|
||||
/* Stolen is immediately above Top of Memory */
|
||||
base = max_low_pfn_mapped << PAGE_SHIFT;
|
||||
#endif
|
||||
@@ -367,8 +349,10 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
|
||||
u32 size)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct drm_mm_node *stolen;
|
||||
struct i915_vma *vma;
|
||||
int ret;
|
||||
|
||||
if (!drm_mm_initialized(&dev_priv->mm.stolen))
|
||||
@@ -409,30 +393,38 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
|
||||
if (gtt_offset == I915_GTT_OFFSET_NONE)
|
||||
return obj;
|
||||
|
||||
vma = i915_gem_vma_create(obj, &dev_priv->gtt.base);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* To simplify the initialisation sequence between KMS and GTT,
|
||||
* we allow construction of the stolen object prior to
|
||||
* setting up the GTT space. The actual reservation will occur
|
||||
* later.
|
||||
*/
|
||||
obj->gtt_space.start = gtt_offset;
|
||||
obj->gtt_space.size = size;
|
||||
if (drm_mm_initialized(&dev_priv->mm.gtt_space)) {
|
||||
ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space,
|
||||
&obj->gtt_space);
|
||||
vma->node.start = gtt_offset;
|
||||
vma->node.size = size;
|
||||
if (drm_mm_initialized(&dev_priv->gtt.base.mm)) {
|
||||
ret = drm_mm_reserve_node(&dev_priv->gtt.base.mm, &vma->node);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
|
||||
goto unref_out;
|
||||
i915_gem_vma_destroy(vma);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
obj->has_global_gtt_mapping = 1;
|
||||
|
||||
list_add_tail(&obj->global_list, &dev_priv->mm.bound_list);
|
||||
list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
|
||||
list_add_tail(&obj->mm_list, &vm->inactive_list);
|
||||
|
||||
return obj;
|
||||
|
||||
unref_out:
|
||||
err_out:
|
||||
drm_mm_remove_node(stolen);
|
||||
kfree(stolen);
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -622,6 +622,7 @@ static struct drm_i915_error_object *
|
||||
i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
|
||||
struct intel_ring_buffer *ring)
|
||||
{
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
u32 seqno;
|
||||
|
||||
@@ -641,7 +642,7 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
|
||||
}
|
||||
|
||||
seqno = ring->get_seqno(ring, false);
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
|
||||
list_for_each_entry(obj, &vm->active_list, mm_list) {
|
||||
if (obj->ring != ring)
|
||||
continue;
|
||||
|
||||
@@ -773,11 +774,12 @@ static void i915_gem_record_rings(struct drm_device *dev,
|
||||
static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
|
||||
struct drm_i915_error_state *error)
|
||||
{
|
||||
struct i915_address_space *vm = &dev_priv->gtt.base;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list)
|
||||
list_for_each_entry(obj, &vm->active_list, mm_list)
|
||||
i++;
|
||||
error->active_bo_count = i;
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
|
||||
@@ -797,7 +799,7 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
|
||||
error->active_bo_count =
|
||||
capture_active_bo(error->active_bo,
|
||||
error->active_bo_count,
|
||||
&dev_priv->mm.active_list);
|
||||
&vm->active_list);
|
||||
|
||||
if (error->pinned_bo)
|
||||
error->pinned_bo_count =
|
||||
|
||||
@@ -698,18 +698,13 @@ static void ironlake_rps_change_irq_handler(struct drm_device *dev)
|
||||
static void notify_ring(struct drm_device *dev,
|
||||
struct intel_ring_buffer *ring)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (ring->obj == NULL)
|
||||
return;
|
||||
|
||||
trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false));
|
||||
|
||||
wake_up_all(&ring->irq_queue);
|
||||
if (i915_enable_hangcheck) {
|
||||
mod_timer(&dev_priv->gpu_error.hangcheck_timer,
|
||||
round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
|
||||
}
|
||||
i915_queue_hangcheck(dev);
|
||||
}
|
||||
|
||||
static void gen6_pm_rps_work(struct work_struct *work)
|
||||
@@ -817,7 +812,7 @@ static void ivybridge_parity_work(struct work_struct *work)
|
||||
|
||||
mutex_unlock(&dev_priv->dev->struct_mutex);
|
||||
|
||||
parity_event[0] = "L3_PARITY_ERROR=1";
|
||||
parity_event[0] = I915_L3_PARITY_UEVENT "=1";
|
||||
parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
|
||||
parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
|
||||
parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
|
||||
@@ -849,6 +844,17 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev)
|
||||
queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
|
||||
}
|
||||
|
||||
static void ilk_gt_irq_handler(struct drm_device *dev,
|
||||
struct drm_i915_private *dev_priv,
|
||||
u32 gt_iir)
|
||||
{
|
||||
if (gt_iir &
|
||||
(GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT))
|
||||
notify_ring(dev, &dev_priv->ring[RCS]);
|
||||
if (gt_iir & ILK_BSD_USER_INTERRUPT)
|
||||
notify_ring(dev, &dev_priv->ring[VCS]);
|
||||
}
|
||||
|
||||
static void snb_gt_irq_handler(struct drm_device *dev,
|
||||
struct drm_i915_private *dev_priv,
|
||||
u32 gt_iir)
|
||||
@@ -913,6 +919,10 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
|
||||
spin_lock(&dev_priv->irq_lock);
|
||||
for (i = 1; i < HPD_NUM_PINS; i++) {
|
||||
|
||||
WARN(((hpd[i] & hotplug_trigger) &&
|
||||
dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED),
|
||||
"Received HPD interrupt although disabled\n");
|
||||
|
||||
if (!(hpd[i] & hotplug_trigger) ||
|
||||
dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED)
|
||||
continue;
|
||||
@@ -923,6 +933,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
|
||||
+ msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
|
||||
dev_priv->hpd_stats[i].hpd_last_jiffies = jiffies;
|
||||
dev_priv->hpd_stats[i].hpd_cnt = 0;
|
||||
DRM_DEBUG_KMS("Received HPD interrupt on PIN %d - cnt: 0\n", i);
|
||||
} else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) {
|
||||
dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED;
|
||||
dev_priv->hpd_event_bits &= ~(1 << i);
|
||||
@@ -930,6 +941,8 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
|
||||
storm_detected = true;
|
||||
} else {
|
||||
dev_priv->hpd_stats[i].hpd_cnt++;
|
||||
DRM_DEBUG_KMS("Received HPD interrupt on PIN %d - cnt: %d\n", i,
|
||||
dev_priv->hpd_stats[i].hpd_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1202,163 +1215,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
|
||||
cpt_serr_int_handler(dev);
|
||||
}
|
||||
|
||||
static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
|
||||
static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier = 0;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int i;
|
||||
|
||||
atomic_inc(&dev_priv->irq_received);
|
||||
|
||||
/* We get interrupts on unclaimed registers, so check for this before we
|
||||
* do any I915_{READ,WRITE}. */
|
||||
if (IS_HASWELL(dev) &&
|
||||
(I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unclaimed register before interrupt\n");
|
||||
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
|
||||
/* disable master interrupt before clearing iir */
|
||||
de_ier = I915_READ(DEIER);
|
||||
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
|
||||
|
||||
/* Disable south interrupts. We'll only write to SDEIIR once, so further
|
||||
* interrupts will will be stored on its back queue, and then we'll be
|
||||
* able to process them after we restore SDEIER (as soon as we restore
|
||||
* it, we'll get an interrupt if SDEIIR still has something to process
|
||||
* due to its back queue). */
|
||||
if (!HAS_PCH_NOP(dev)) {
|
||||
sde_ier = I915_READ(SDEIER);
|
||||
I915_WRITE(SDEIER, 0);
|
||||
POSTING_READ(SDEIER);
|
||||
}
|
||||
|
||||
/* On Haswell, also mask ERR_INT because we don't want to risk
|
||||
* generating "unclaimed register" interrupts from inside the interrupt
|
||||
* handler. */
|
||||
if (IS_HASWELL(dev)) {
|
||||
spin_lock(&dev_priv->irq_lock);
|
||||
ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
||||
spin_unlock(&dev_priv->irq_lock);
|
||||
}
|
||||
|
||||
gt_iir = I915_READ(GTIIR);
|
||||
if (gt_iir) {
|
||||
snb_gt_irq_handler(dev, dev_priv, gt_iir);
|
||||
I915_WRITE(GTIIR, gt_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
de_iir = I915_READ(DEIIR);
|
||||
if (de_iir) {
|
||||
if (de_iir & DE_ERR_INT_IVB)
|
||||
ivb_err_int_handler(dev);
|
||||
|
||||
if (de_iir & DE_AUX_CHANNEL_A_IVB)
|
||||
dp_aux_irq_handler(dev);
|
||||
|
||||
if (de_iir & DE_GSE_IVB)
|
||||
intel_opregion_asle_intr(dev);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
|
||||
drm_handle_vblank(dev, i);
|
||||
if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
|
||||
intel_prepare_page_flip(dev, i);
|
||||
intel_finish_page_flip_plane(dev, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* check event from PCH */
|
||||
if (!HAS_PCH_NOP(dev) && (de_iir & DE_PCH_EVENT_IVB)) {
|
||||
u32 pch_iir = I915_READ(SDEIIR);
|
||||
|
||||
cpt_irq_handler(dev, pch_iir);
|
||||
|
||||
/* clear PCH hotplug event before clear CPU irq */
|
||||
I915_WRITE(SDEIIR, pch_iir);
|
||||
}
|
||||
|
||||
I915_WRITE(DEIIR, de_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
pm_iir = I915_READ(GEN6_PMIIR);
|
||||
if (pm_iir) {
|
||||
if (IS_HASWELL(dev))
|
||||
hsw_pm_irq_handler(dev_priv, pm_iir);
|
||||
else if (pm_iir & GEN6_PM_RPS_EVENTS)
|
||||
gen6_rps_irq_handler(dev_priv, pm_iir);
|
||||
I915_WRITE(GEN6_PMIIR, pm_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (IS_HASWELL(dev)) {
|
||||
spin_lock(&dev_priv->irq_lock);
|
||||
if (ivb_can_enable_err_int(dev))
|
||||
ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
||||
spin_unlock(&dev_priv->irq_lock);
|
||||
}
|
||||
|
||||
I915_WRITE(DEIER, de_ier);
|
||||
POSTING_READ(DEIER);
|
||||
if (!HAS_PCH_NOP(dev)) {
|
||||
I915_WRITE(SDEIER, sde_ier);
|
||||
POSTING_READ(SDEIER);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ilk_gt_irq_handler(struct drm_device *dev,
|
||||
struct drm_i915_private *dev_priv,
|
||||
u32 gt_iir)
|
||||
{
|
||||
if (gt_iir &
|
||||
(GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT))
|
||||
notify_ring(dev, &dev_priv->ring[RCS]);
|
||||
if (gt_iir & ILK_BSD_USER_INTERRUPT)
|
||||
notify_ring(dev, &dev_priv->ring[VCS]);
|
||||
}
|
||||
|
||||
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
int ret = IRQ_NONE;
|
||||
u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier;
|
||||
|
||||
atomic_inc(&dev_priv->irq_received);
|
||||
|
||||
/* disable master interrupt before clearing iir */
|
||||
de_ier = I915_READ(DEIER);
|
||||
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
/* Disable south interrupts. We'll only write to SDEIIR once, so further
|
||||
* interrupts will will be stored on its back queue, and then we'll be
|
||||
* able to process them after we restore SDEIER (as soon as we restore
|
||||
* it, we'll get an interrupt if SDEIIR still has something to process
|
||||
* due to its back queue). */
|
||||
sde_ier = I915_READ(SDEIER);
|
||||
I915_WRITE(SDEIER, 0);
|
||||
POSTING_READ(SDEIER);
|
||||
|
||||
de_iir = I915_READ(DEIIR);
|
||||
gt_iir = I915_READ(GTIIR);
|
||||
pm_iir = I915_READ(GEN6_PMIIR);
|
||||
|
||||
if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0))
|
||||
goto done;
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
if (IS_GEN5(dev))
|
||||
ilk_gt_irq_handler(dev, dev_priv, gt_iir);
|
||||
else
|
||||
snb_gt_irq_handler(dev, dev_priv, gt_iir);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (de_iir & DE_AUX_CHANNEL_A)
|
||||
dp_aux_irq_handler(dev);
|
||||
@@ -1406,21 +1265,127 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
||||
I915_WRITE(SDEIIR, pch_iir);
|
||||
}
|
||||
|
||||
if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT)
|
||||
if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT)
|
||||
ironlake_rps_change_irq_handler(dev);
|
||||
}
|
||||
|
||||
if (IS_GEN6(dev) && pm_iir & GEN6_PM_RPS_EVENTS)
|
||||
gen6_rps_irq_handler(dev_priv, pm_iir);
|
||||
static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
I915_WRITE(GTIIR, gt_iir);
|
||||
I915_WRITE(DEIIR, de_iir);
|
||||
I915_WRITE(GEN6_PMIIR, pm_iir);
|
||||
if (de_iir & DE_ERR_INT_IVB)
|
||||
ivb_err_int_handler(dev);
|
||||
|
||||
if (de_iir & DE_AUX_CHANNEL_A_IVB)
|
||||
dp_aux_irq_handler(dev);
|
||||
|
||||
if (de_iir & DE_GSE_IVB)
|
||||
intel_opregion_asle_intr(dev);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
|
||||
drm_handle_vblank(dev, i);
|
||||
if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
|
||||
intel_prepare_page_flip(dev, i);
|
||||
intel_finish_page_flip_plane(dev, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* check event from PCH */
|
||||
if (!HAS_PCH_NOP(dev) && (de_iir & DE_PCH_EVENT_IVB)) {
|
||||
u32 pch_iir = I915_READ(SDEIIR);
|
||||
|
||||
cpt_irq_handler(dev, pch_iir);
|
||||
|
||||
/* clear PCH hotplug event before clear CPU irq */
|
||||
I915_WRITE(SDEIIR, pch_iir);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
atomic_inc(&dev_priv->irq_received);
|
||||
|
||||
/* We get interrupts on unclaimed registers, so check for this before we
|
||||
* do any I915_{READ,WRITE}. */
|
||||
intel_uncore_check_errors(dev);
|
||||
|
||||
/* disable master interrupt before clearing iir */
|
||||
de_ier = I915_READ(DEIER);
|
||||
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
/* Disable south interrupts. We'll only write to SDEIIR once, so further
|
||||
* interrupts will will be stored on its back queue, and then we'll be
|
||||
* able to process them after we restore SDEIER (as soon as we restore
|
||||
* it, we'll get an interrupt if SDEIIR still has something to process
|
||||
* due to its back queue). */
|
||||
if (!HAS_PCH_NOP(dev)) {
|
||||
sde_ier = I915_READ(SDEIER);
|
||||
I915_WRITE(SDEIER, 0);
|
||||
POSTING_READ(SDEIER);
|
||||
}
|
||||
|
||||
/* On Haswell, also mask ERR_INT because we don't want to risk
|
||||
* generating "unclaimed register" interrupts from inside the interrupt
|
||||
* handler. */
|
||||
if (IS_HASWELL(dev)) {
|
||||
spin_lock(&dev_priv->irq_lock);
|
||||
ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
||||
spin_unlock(&dev_priv->irq_lock);
|
||||
}
|
||||
|
||||
gt_iir = I915_READ(GTIIR);
|
||||
if (gt_iir) {
|
||||
if (INTEL_INFO(dev)->gen >= 6)
|
||||
snb_gt_irq_handler(dev, dev_priv, gt_iir);
|
||||
else
|
||||
ilk_gt_irq_handler(dev, dev_priv, gt_iir);
|
||||
I915_WRITE(GTIIR, gt_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
de_iir = I915_READ(DEIIR);
|
||||
if (de_iir) {
|
||||
if (INTEL_INFO(dev)->gen >= 7)
|
||||
ivb_display_irq_handler(dev, de_iir);
|
||||
else
|
||||
ilk_display_irq_handler(dev, de_iir);
|
||||
I915_WRITE(DEIIR, de_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
u32 pm_iir = I915_READ(GEN6_PMIIR);
|
||||
if (pm_iir) {
|
||||
if (IS_HASWELL(dev))
|
||||
hsw_pm_irq_handler(dev_priv, pm_iir);
|
||||
else if (pm_iir & GEN6_PM_RPS_EVENTS)
|
||||
gen6_rps_irq_handler(dev_priv, pm_iir);
|
||||
I915_WRITE(GEN6_PMIIR, pm_iir);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_HASWELL(dev)) {
|
||||
spin_lock(&dev_priv->irq_lock);
|
||||
if (ivb_can_enable_err_int(dev))
|
||||
ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
||||
spin_unlock(&dev_priv->irq_lock);
|
||||
}
|
||||
|
||||
done:
|
||||
I915_WRITE(DEIER, de_ier);
|
||||
POSTING_READ(DEIER);
|
||||
I915_WRITE(SDEIER, sde_ier);
|
||||
POSTING_READ(SDEIER);
|
||||
if (!HAS_PCH_NOP(dev)) {
|
||||
I915_WRITE(SDEIER, sde_ier);
|
||||
POSTING_READ(SDEIER);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1440,9 +1405,9 @@ static void i915_error_work_func(struct work_struct *work)
|
||||
gpu_error);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct intel_ring_buffer *ring;
|
||||
char *error_event[] = { "ERROR=1", NULL };
|
||||
char *reset_event[] = { "RESET=1", NULL };
|
||||
char *reset_done_event[] = { "ERROR=0", NULL };
|
||||
char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
|
||||
char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
|
||||
char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
|
||||
int i, ret;
|
||||
|
||||
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
|
||||
@@ -1696,29 +1661,14 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
|
||||
DE_PIPE_VBLANK_ILK(pipe);
|
||||
|
||||
if (!i915_pipe_enabled(dev, pipe))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
|
||||
DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ivybridge_enable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
if (!i915_pipe_enabled(dev, pipe))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_enable_display_irq(dev_priv,
|
||||
DE_PIPEA_VBLANK_IVB << (5 * pipe));
|
||||
ironlake_enable_display_irq(dev_priv, bit);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
@@ -1769,21 +1719,11 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
|
||||
DE_PIPE_VBLANK_ILK(pipe);
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
|
||||
DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
static void ivybridge_disable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_disable_display_irq(dev_priv,
|
||||
DE_PIPEA_VBLANK_IVB << (pipe * 5));
|
||||
ironlake_disable_display_irq(dev_priv, bit);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
@@ -2030,9 +1970,17 @@ void i915_hangcheck_elapsed(unsigned long data)
|
||||
if (busy_count)
|
||||
/* Reset timer case chip hangs without another request
|
||||
* being added */
|
||||
mod_timer(&dev_priv->gpu_error.hangcheck_timer,
|
||||
round_jiffies_up(jiffies +
|
||||
DRM_I915_HANGCHECK_JIFFIES));
|
||||
i915_queue_hangcheck(dev);
|
||||
}
|
||||
|
||||
void i915_queue_hangcheck(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
if (!i915_enable_hangcheck)
|
||||
return;
|
||||
|
||||
mod_timer(&dev_priv->gpu_error.hangcheck_timer,
|
||||
round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
|
||||
}
|
||||
|
||||
static void ibx_irq_preinstall(struct drm_device *dev)
|
||||
@@ -2054,6 +2002,23 @@ static void ibx_irq_preinstall(struct drm_device *dev)
|
||||
POSTING_READ(SDEIER);
|
||||
}
|
||||
|
||||
static void gen5_gt_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* and GT */
|
||||
I915_WRITE(GTIMR, 0xffffffff);
|
||||
I915_WRITE(GTIER, 0x0);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
/* and PM */
|
||||
I915_WRITE(GEN6_PMIMR, 0xffffffff);
|
||||
I915_WRITE(GEN6_PMIER, 0x0);
|
||||
POSTING_READ(GEN6_PMIER);
|
||||
}
|
||||
}
|
||||
|
||||
/* drm_dma.h hooks
|
||||
*/
|
||||
static void ironlake_irq_preinstall(struct drm_device *dev)
|
||||
@@ -2064,43 +2029,11 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
|
||||
|
||||
I915_WRITE(HWSTAM, 0xeffe);
|
||||
|
||||
/* XXX hotplug from PCH */
|
||||
|
||||
I915_WRITE(DEIMR, 0xffffffff);
|
||||
I915_WRITE(DEIER, 0x0);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
/* and GT */
|
||||
I915_WRITE(GTIMR, 0xffffffff);
|
||||
I915_WRITE(GTIER, 0x0);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
ibx_irq_preinstall(dev);
|
||||
}
|
||||
|
||||
static void ivybridge_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
|
||||
atomic_set(&dev_priv->irq_received, 0);
|
||||
|
||||
I915_WRITE(HWSTAM, 0xeffe);
|
||||
|
||||
/* XXX hotplug from PCH */
|
||||
|
||||
I915_WRITE(DEIMR, 0xffffffff);
|
||||
I915_WRITE(DEIER, 0x0);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
/* and GT */
|
||||
I915_WRITE(GTIMR, 0xffffffff);
|
||||
I915_WRITE(GTIER, 0x0);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
/* Power management */
|
||||
I915_WRITE(GEN6_PMIMR, 0xffffffff);
|
||||
I915_WRITE(GEN6_PMIER, 0x0);
|
||||
POSTING_READ(GEN6_PMIER);
|
||||
gen5_gt_irq_preinstall(dev);
|
||||
|
||||
ibx_irq_preinstall(dev);
|
||||
}
|
||||
@@ -2121,9 +2054,8 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
|
||||
/* and GT */
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, 0xffffffff);
|
||||
I915_WRITE(GTIER, 0x0);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
gen5_gt_irq_preinstall(dev);
|
||||
|
||||
I915_WRITE(DPINVGTT, 0xff);
|
||||
|
||||
@@ -2193,42 +2125,79 @@ static void ibx_irq_postinstall(struct drm_device *dev)
|
||||
I915_WRITE(SDEIMR, ~mask);
|
||||
}
|
||||
|
||||
static void gen5_gt_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 pm_irqs, gt_irqs;
|
||||
|
||||
pm_irqs = gt_irqs = 0;
|
||||
|
||||
dev_priv->gt_irq_mask = ~0;
|
||||
if (HAS_L3_GPU_CACHE(dev)) {
|
||||
/* L3 parity interrupt is always unmasked. */
|
||||
dev_priv->gt_irq_mask = ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
||||
gt_irqs |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
||||
}
|
||||
|
||||
gt_irqs |= GT_RENDER_USER_INTERRUPT;
|
||||
if (IS_GEN5(dev)) {
|
||||
gt_irqs |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT |
|
||||
ILK_BSD_USER_INTERRUPT;
|
||||
} else {
|
||||
gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
|
||||
}
|
||||
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
||||
I915_WRITE(GTIER, gt_irqs);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
pm_irqs |= GEN6_PM_RPS_EVENTS;
|
||||
|
||||
if (HAS_VEBOX(dev))
|
||||
pm_irqs |= PM_VEBOX_USER_INTERRUPT;
|
||||
|
||||
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
|
||||
I915_WRITE(GEN6_PMIMR, 0xffffffff);
|
||||
I915_WRITE(GEN6_PMIER, pm_irqs);
|
||||
POSTING_READ(GEN6_PMIER);
|
||||
}
|
||||
}
|
||||
|
||||
static int ironlake_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
/* enable kind of interrupts always enabled */
|
||||
u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
|
||||
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
|
||||
DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
|
||||
DE_PIPEA_FIFO_UNDERRUN | DE_POISON;
|
||||
u32 gt_irqs;
|
||||
u32 display_mask, extra_mask;
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 7) {
|
||||
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
|
||||
DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB |
|
||||
DE_PLANEB_FLIP_DONE_IVB |
|
||||
DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB |
|
||||
DE_ERR_INT_IVB);
|
||||
extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
|
||||
DE_PIPEA_VBLANK_IVB);
|
||||
|
||||
I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
|
||||
} else {
|
||||
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
|
||||
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
|
||||
DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
|
||||
DE_PIPEA_FIFO_UNDERRUN | DE_POISON);
|
||||
extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT;
|
||||
}
|
||||
|
||||
dev_priv->irq_mask = ~display_mask;
|
||||
|
||||
/* should always can generate irq */
|
||||
I915_WRITE(DEIIR, I915_READ(DEIIR));
|
||||
I915_WRITE(DEIMR, dev_priv->irq_mask);
|
||||
I915_WRITE(DEIER, display_mask |
|
||||
DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT);
|
||||
I915_WRITE(DEIER, display_mask | extra_mask);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
dev_priv->gt_irq_mask = ~0;
|
||||
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
||||
|
||||
gt_irqs = GT_RENDER_USER_INTERRUPT;
|
||||
|
||||
if (IS_GEN6(dev))
|
||||
gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
|
||||
else
|
||||
gt_irqs |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT |
|
||||
ILK_BSD_USER_INTERRUPT;
|
||||
|
||||
I915_WRITE(GTIER, gt_irqs);
|
||||
POSTING_READ(GTIER);
|
||||
gen5_gt_irq_postinstall(dev);
|
||||
|
||||
ibx_irq_postinstall(dev);
|
||||
|
||||
@@ -2246,67 +2215,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ivybridge_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
/* enable kind of interrupts always enabled */
|
||||
u32 display_mask =
|
||||
DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB |
|
||||
DE_PLANEC_FLIP_DONE_IVB |
|
||||
DE_PLANEB_FLIP_DONE_IVB |
|
||||
DE_PLANEA_FLIP_DONE_IVB |
|
||||
DE_AUX_CHANNEL_A_IVB |
|
||||
DE_ERR_INT_IVB;
|
||||
u32 pm_irqs = GEN6_PM_RPS_EVENTS;
|
||||
u32 gt_irqs;
|
||||
|
||||
dev_priv->irq_mask = ~display_mask;
|
||||
|
||||
/* should always can generate irq */
|
||||
I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
|
||||
I915_WRITE(DEIIR, I915_READ(DEIIR));
|
||||
I915_WRITE(DEIMR, dev_priv->irq_mask);
|
||||
I915_WRITE(DEIER,
|
||||
display_mask |
|
||||
DE_PIPEC_VBLANK_IVB |
|
||||
DE_PIPEB_VBLANK_IVB |
|
||||
DE_PIPEA_VBLANK_IVB);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
dev_priv->gt_irq_mask = ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
||||
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
||||
|
||||
gt_irqs = GT_RENDER_USER_INTERRUPT | GT_BSD_USER_INTERRUPT |
|
||||
GT_BLT_USER_INTERRUPT | GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
||||
I915_WRITE(GTIER, gt_irqs);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
|
||||
if (HAS_VEBOX(dev))
|
||||
pm_irqs |= PM_VEBOX_USER_INTERRUPT;
|
||||
|
||||
/* Our enable/disable rps functions may touch these registers so
|
||||
* make sure to set a known state for only the non-RPS bits.
|
||||
* The RMW is extra paranoia since this should be called after being set
|
||||
* to a known state in preinstall.
|
||||
* */
|
||||
I915_WRITE(GEN6_PMIMR,
|
||||
(I915_READ(GEN6_PMIMR) | ~GEN6_PM_RPS_EVENTS) & ~pm_irqs);
|
||||
I915_WRITE(GEN6_PMIER,
|
||||
(I915_READ(GEN6_PMIER) & GEN6_PM_RPS_EVENTS) | pm_irqs);
|
||||
POSTING_READ(GEN6_PMIER);
|
||||
|
||||
ibx_irq_postinstall(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int valleyview_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
u32 gt_irqs;
|
||||
u32 enable_mask;
|
||||
u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
|
||||
unsigned long irqflags;
|
||||
@@ -2346,13 +2257,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
|
||||
I915_WRITE(VLV_IIR, 0xffffffff);
|
||||
I915_WRITE(VLV_IIR, 0xffffffff);
|
||||
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
||||
|
||||
gt_irqs = GT_RENDER_USER_INTERRUPT | GT_BSD_USER_INTERRUPT |
|
||||
GT_BLT_USER_INTERRUPT;
|
||||
I915_WRITE(GTIER, gt_irqs);
|
||||
POSTING_READ(GTIER);
|
||||
gen5_gt_irq_postinstall(dev);
|
||||
|
||||
/* ack & enable invalid PTE error interrupts */
|
||||
#if 0 /* FIXME: add support to irq handler for checking these bits */
|
||||
@@ -3118,15 +3023,6 @@ void intel_irq_init(struct drm_device *dev)
|
||||
dev->driver->enable_vblank = valleyview_enable_vblank;
|
||||
dev->driver->disable_vblank = valleyview_disable_vblank;
|
||||
dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
|
||||
} else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
|
||||
/* Share uninstall handlers with ILK/SNB */
|
||||
dev->driver->irq_handler = ivybridge_irq_handler;
|
||||
dev->driver->irq_preinstall = ivybridge_irq_preinstall;
|
||||
dev->driver->irq_postinstall = ivybridge_irq_postinstall;
|
||||
dev->driver->irq_uninstall = ironlake_irq_uninstall;
|
||||
dev->driver->enable_vblank = ivybridge_enable_vblank;
|
||||
dev->driver->disable_vblank = ivybridge_disable_vblank;
|
||||
dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
|
||||
} else if (HAS_PCH_SPLIT(dev)) {
|
||||
dev->driver->irq_handler = ironlake_irq_handler;
|
||||
dev->driver->irq_preinstall = ironlake_irq_preinstall;
|
||||
|
||||
@@ -61,6 +61,12 @@
|
||||
#define GC_LOW_FREQUENCY_ENABLE (1 << 7)
|
||||
#define GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4)
|
||||
#define GC_DISPLAY_CLOCK_333_MHZ (4 << 4)
|
||||
#define GC_DISPLAY_CLOCK_267_MHZ_PNV (0 << 4)
|
||||
#define GC_DISPLAY_CLOCK_333_MHZ_PNV (1 << 4)
|
||||
#define GC_DISPLAY_CLOCK_444_MHZ_PNV (2 << 4)
|
||||
#define GC_DISPLAY_CLOCK_200_MHZ_PNV (5 << 4)
|
||||
#define GC_DISPLAY_CLOCK_133_MHZ_PNV (6 << 4)
|
||||
#define GC_DISPLAY_CLOCK_167_MHZ_PNV (7 << 4)
|
||||
#define GC_DISPLAY_CLOCK_MASK (7 << 4)
|
||||
#define GM45_GC_RENDER_CLOCK_MASK (0xf << 0)
|
||||
#define GM45_GC_RENDER_CLOCK_266_MHZ (8 << 0)
|
||||
@@ -1779,6 +1785,71 @@
|
||||
#define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
|
||||
#define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
|
||||
|
||||
/* HSW eDP PSR registers */
|
||||
#define EDP_PSR_CTL 0x64800
|
||||
#define EDP_PSR_ENABLE (1<<31)
|
||||
#define EDP_PSR_LINK_DISABLE (0<<27)
|
||||
#define EDP_PSR_LINK_STANDBY (1<<27)
|
||||
#define EDP_PSR_MIN_LINK_ENTRY_TIME_MASK (3<<25)
|
||||
#define EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES (0<<25)
|
||||
#define EDP_PSR_MIN_LINK_ENTRY_TIME_4_LINES (1<<25)
|
||||
#define EDP_PSR_MIN_LINK_ENTRY_TIME_2_LINES (2<<25)
|
||||
#define EDP_PSR_MIN_LINK_ENTRY_TIME_0_LINES (3<<25)
|
||||
#define EDP_PSR_MAX_SLEEP_TIME_SHIFT 20
|
||||
#define EDP_PSR_SKIP_AUX_EXIT (1<<12)
|
||||
#define EDP_PSR_TP1_TP2_SEL (0<<11)
|
||||
#define EDP_PSR_TP1_TP3_SEL (1<<11)
|
||||
#define EDP_PSR_TP2_TP3_TIME_500us (0<<8)
|
||||
#define EDP_PSR_TP2_TP3_TIME_100us (1<<8)
|
||||
#define EDP_PSR_TP2_TP3_TIME_2500us (2<<8)
|
||||
#define EDP_PSR_TP2_TP3_TIME_0us (3<<8)
|
||||
#define EDP_PSR_TP1_TIME_500us (0<<4)
|
||||
#define EDP_PSR_TP1_TIME_100us (1<<4)
|
||||
#define EDP_PSR_TP1_TIME_2500us (2<<4)
|
||||
#define EDP_PSR_TP1_TIME_0us (3<<4)
|
||||
#define EDP_PSR_IDLE_FRAME_SHIFT 0
|
||||
|
||||
#define EDP_PSR_AUX_CTL 0x64810
|
||||
#define EDP_PSR_AUX_DATA1 0x64814
|
||||
#define EDP_PSR_DPCD_COMMAND 0x80060000
|
||||
#define EDP_PSR_AUX_DATA2 0x64818
|
||||
#define EDP_PSR_DPCD_NORMAL_OPERATION (1<<24)
|
||||
#define EDP_PSR_AUX_DATA3 0x6481c
|
||||
#define EDP_PSR_AUX_DATA4 0x64820
|
||||
#define EDP_PSR_AUX_DATA5 0x64824
|
||||
|
||||
#define EDP_PSR_STATUS_CTL 0x64840
|
||||
#define EDP_PSR_STATUS_STATE_MASK (7<<29)
|
||||
#define EDP_PSR_STATUS_STATE_IDLE (0<<29)
|
||||
#define EDP_PSR_STATUS_STATE_SRDONACK (1<<29)
|
||||
#define EDP_PSR_STATUS_STATE_SRDENT (2<<29)
|
||||
#define EDP_PSR_STATUS_STATE_BUFOFF (3<<29)
|
||||
#define EDP_PSR_STATUS_STATE_BUFON (4<<29)
|
||||
#define EDP_PSR_STATUS_STATE_AUXACK (5<<29)
|
||||
#define EDP_PSR_STATUS_STATE_SRDOFFACK (6<<29)
|
||||
#define EDP_PSR_STATUS_LINK_MASK (3<<26)
|
||||
#define EDP_PSR_STATUS_LINK_FULL_OFF (0<<26)
|
||||
#define EDP_PSR_STATUS_LINK_FULL_ON (1<<26)
|
||||
#define EDP_PSR_STATUS_LINK_STANDBY (2<<26)
|
||||
#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT 20
|
||||
#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK 0x1f
|
||||
#define EDP_PSR_STATUS_COUNT_SHIFT 16
|
||||
#define EDP_PSR_STATUS_COUNT_MASK 0xf
|
||||
#define EDP_PSR_STATUS_AUX_ERROR (1<<15)
|
||||
#define EDP_PSR_STATUS_AUX_SENDING (1<<12)
|
||||
#define EDP_PSR_STATUS_SENDING_IDLE (1<<9)
|
||||
#define EDP_PSR_STATUS_SENDING_TP2_TP3 (1<<8)
|
||||
#define EDP_PSR_STATUS_SENDING_TP1 (1<<4)
|
||||
#define EDP_PSR_STATUS_IDLE_MASK 0xf
|
||||
|
||||
#define EDP_PSR_PERF_CNT 0x64844
|
||||
#define EDP_PSR_PERF_CNT_MASK 0xffffff
|
||||
|
||||
#define EDP_PSR_DEBUG_CTL 0x64860
|
||||
#define EDP_PSR_DEBUG_MASK_LPSP (1<<27)
|
||||
#define EDP_PSR_DEBUG_MASK_MEMUP (1<<26)
|
||||
#define EDP_PSR_DEBUG_MASK_HPD (1<<25)
|
||||
|
||||
/* VGA port control */
|
||||
#define ADPA 0x61100
|
||||
#define PCH_ADPA 0xe1100
|
||||
@@ -2048,6 +2119,7 @@
|
||||
* (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte
|
||||
* of the infoframe structure specified by CEA-861. */
|
||||
#define VIDEO_DIP_DATA_SIZE 32
|
||||
#define VIDEO_DIP_VSC_DATA_SIZE 36
|
||||
#define VIDEO_DIP_CTL 0x61170
|
||||
/* Pre HSW: */
|
||||
#define VIDEO_DIP_ENABLE (1 << 31)
|
||||
@@ -2195,6 +2267,8 @@
|
||||
#define BLC_PWM_CPU_CTL2 0x48250
|
||||
#define BLC_PWM_CPU_CTL 0x48254
|
||||
|
||||
#define HSW_BLC_PWM2_CTL 0x48350
|
||||
|
||||
/* PCH CTL1 is totally different, all but the below bits are reserved. CTL2 is
|
||||
* like the normal CTL from gen4 and earlier. Hooray for confusing naming. */
|
||||
#define BLC_PWM_PCH_CTL1 0xc8250
|
||||
@@ -2203,6 +2277,12 @@
|
||||
#define BLM_PCH_POLARITY (1 << 29)
|
||||
#define BLC_PWM_PCH_CTL2 0xc8254
|
||||
|
||||
#define UTIL_PIN_CTL 0x48400
|
||||
#define UTIL_PIN_ENABLE (1 << 31)
|
||||
|
||||
#define PCH_GTC_CTL 0xe7000
|
||||
#define PCH_GTC_ENABLE (1 << 31)
|
||||
|
||||
/* TV port control */
|
||||
#define TV_CTL 0x68000
|
||||
/** Enables the TV encoder */
|
||||
@@ -3721,6 +3801,9 @@
|
||||
#define DE_PLANEA_FLIP_DONE_IVB (1<<3)
|
||||
#define DE_PIPEA_VBLANK_IVB (1<<0)
|
||||
|
||||
#define DE_PIPE_VBLANK_ILK(pipe) (1 << ((pipe * 8) + 7))
|
||||
#define DE_PIPE_VBLANK_IVB(pipe) (1 << (pipe * 5))
|
||||
|
||||
#define VLV_MASTER_IER 0x4400c /* Gunit master IER */
|
||||
#define MASTER_INTERRUPT_ENABLE (1<<31)
|
||||
|
||||
@@ -4084,6 +4167,13 @@
|
||||
#define HSW_TVIDEO_DIP_VSC_DATA(trans) \
|
||||
_TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B)
|
||||
|
||||
#define HSW_STEREO_3D_CTL_A 0x70020
|
||||
#define S3D_ENABLE (1<<31)
|
||||
#define HSW_STEREO_3D_CTL_B 0x71020
|
||||
|
||||
#define HSW_STEREO_3D_CTL(trans) \
|
||||
_TRANSCODER(trans, HSW_STEREO_3D_CTL_A, HSW_STEREO_3D_CTL_A)
|
||||
|
||||
#define _PCH_TRANS_HTOTAL_B 0xe1000
|
||||
#define _PCH_TRANS_HBLANK_B 0xe1004
|
||||
#define _PCH_TRANS_HSYNC_B 0xe1008
|
||||
@@ -4472,6 +4562,10 @@
|
||||
#define GT_FIFO_FREE_ENTRIES 0x120008
|
||||
#define GT_FIFO_NUM_RESERVED_ENTRIES 20
|
||||
|
||||
#define HSW_IDICR 0x9008
|
||||
#define IDIHASHMSK(x) (((x) & 0x3f) << 16)
|
||||
#define HSW_EDRAM_PRESENT 0x120010
|
||||
|
||||
#define GEN6_UCGCTL1 0x9400
|
||||
# define GEN6_BLBUNIT_CLOCK_GATE_DISABLE (1 << 5)
|
||||
# define GEN6_CSUNIT_CLOCK_GATE_DISABLE (1 << 7)
|
||||
@@ -4862,7 +4956,8 @@
|
||||
#define SBI_SSCAUXDIV6 0x0610
|
||||
#define SBI_SSCAUXDIV_FINALDIV2SEL(x) ((x)<<4)
|
||||
#define SBI_DBUFF0 0x2a00
|
||||
#define SBI_DBUFF0_ENABLE (1<<0)
|
||||
#define SBI_GEN0 0x1f00
|
||||
#define SBI_GEN0_CFG_BUFFENABLE_DISABLE (1<<0)
|
||||
|
||||
/* LPT PIXCLK_GATE */
|
||||
#define PIXCLK_GATE 0xC6020
|
||||
@@ -4928,7 +5023,14 @@
|
||||
#define LCPLL_CLK_FREQ_450 (0<<26)
|
||||
#define LCPLL_CD_CLOCK_DISABLE (1<<25)
|
||||
#define LCPLL_CD2X_CLOCK_DISABLE (1<<23)
|
||||
#define LCPLL_POWER_DOWN_ALLOW (1<<22)
|
||||
#define LCPLL_CD_SOURCE_FCLK (1<<21)
|
||||
#define LCPLL_CD_SOURCE_FCLK_DONE (1<<19)
|
||||
|
||||
#define D_COMP (MCHBAR_MIRROR_BASE_SNB + 0x5F0C)
|
||||
#define D_COMP_RCOMP_IN_PROGRESS (1<<9)
|
||||
#define D_COMP_COMP_FORCE (1<<8)
|
||||
#define D_COMP_COMP_DISABLE (1<<0)
|
||||
|
||||
/* Pipe WM_LINETIME - watermark line time */
|
||||
#define PIPE_WM_LINETIME_A 0x45270
|
||||
|
||||
@@ -406,10 +406,12 @@ TRACE_EVENT(i915_flip_complete,
|
||||
TP_printk("plane=%d, obj=%p", __entry->plane, __entry->obj)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i915_reg_rw,
|
||||
TP_PROTO(bool write, u32 reg, u64 val, int len),
|
||||
TRACE_EVENT_CONDITION(i915_reg_rw,
|
||||
TP_PROTO(bool write, u32 reg, u64 val, int len, bool trace),
|
||||
|
||||
TP_ARGS(write, reg, val, len),
|
||||
TP_ARGS(write, reg, val, len, trace),
|
||||
|
||||
TP_CONDITION(trace),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, val)
|
||||
|
||||
@@ -613,6 +613,10 @@ intel_crt_detect(struct drm_connector *connector, bool force)
|
||||
enum drm_connector_status status;
|
||||
struct intel_load_detect_pipe tmp;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
|
||||
connector->base.id, drm_get_connector_name(connector),
|
||||
force);
|
||||
|
||||
if (I915_HAS_HOTPLUG(dev)) {
|
||||
/* We can not rely on the HPD pin always being correctly wired
|
||||
* up, for example many KVM do not pass it through, and so
|
||||
|
||||
@@ -301,7 +301,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder,
|
||||
struct intel_digital_port *intel_dig_port =
|
||||
enc_to_dig_port(encoder);
|
||||
|
||||
intel_dp->DP = intel_dig_port->port_reversal |
|
||||
intel_dp->DP = intel_dig_port->saved_port_bits |
|
||||
DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
|
||||
intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
|
||||
|
||||
@@ -1109,7 +1109,8 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
|
||||
* enabling the port.
|
||||
*/
|
||||
I915_WRITE(DDI_BUF_CTL(port),
|
||||
intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE);
|
||||
intel_dig_port->saved_port_bits |
|
||||
DDI_BUF_CTL_ENABLE);
|
||||
} else if (type == INTEL_OUTPUT_EDP) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
|
||||
@@ -1117,6 +1118,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
|
||||
intel_dp_stop_link_train(intel_dp);
|
||||
|
||||
ironlake_edp_backlight_on(intel_dp);
|
||||
intel_edp_psr_enable(intel_dp);
|
||||
}
|
||||
|
||||
if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
|
||||
@@ -1147,6 +1149,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
|
||||
if (type == INTEL_OUTPUT_EDP) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
|
||||
intel_edp_psr_disable(intel_dp);
|
||||
ironlake_edp_backlight_off(intel_dp);
|
||||
}
|
||||
}
|
||||
@@ -1347,8 +1350,9 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
|
||||
intel_encoder->get_config = intel_ddi_get_config;
|
||||
|
||||
intel_dig_port->port = port;
|
||||
intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) &
|
||||
DDI_BUF_PORT_REVERSAL;
|
||||
intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
|
||||
(DDI_BUF_PORT_REVERSAL |
|
||||
DDI_A_4_LANES);
|
||||
intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
|
||||
|
||||
intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
|
||||
|
||||
@@ -2274,6 +2274,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
}
|
||||
|
||||
intel_update_fbc(dev);
|
||||
intel_edp_psr_update(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
intel_crtc_update_sarea_pos(crtc, x, y);
|
||||
@@ -4162,6 +4163,30 @@ static int i9xx_misc_get_display_clock_speed(struct drm_device *dev)
|
||||
return 200000;
|
||||
}
|
||||
|
||||
static int pnv_get_display_clock_speed(struct drm_device *dev)
|
||||
{
|
||||
u16 gcfgc = 0;
|
||||
|
||||
pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
|
||||
|
||||
switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
|
||||
case GC_DISPLAY_CLOCK_267_MHZ_PNV:
|
||||
return 267000;
|
||||
case GC_DISPLAY_CLOCK_333_MHZ_PNV:
|
||||
return 333000;
|
||||
case GC_DISPLAY_CLOCK_444_MHZ_PNV:
|
||||
return 444000;
|
||||
case GC_DISPLAY_CLOCK_200_MHZ_PNV:
|
||||
return 200000;
|
||||
default:
|
||||
DRM_ERROR("Unknown pnv display core clock 0x%04x\n", gcfgc);
|
||||
case GC_DISPLAY_CLOCK_133_MHZ_PNV:
|
||||
return 133000;
|
||||
case GC_DISPLAY_CLOCK_167_MHZ_PNV:
|
||||
return 167000;
|
||||
}
|
||||
}
|
||||
|
||||
static int i915gm_get_display_clock_speed(struct drm_device *dev)
|
||||
{
|
||||
u16 gcfgc = 0;
|
||||
@@ -4946,22 +4971,19 @@ static void i9xx_get_pfit_config(struct intel_crtc *crtc,
|
||||
uint32_t tmp;
|
||||
|
||||
tmp = I915_READ(PFIT_CONTROL);
|
||||
if (!(tmp & PFIT_ENABLE))
|
||||
return;
|
||||
|
||||
/* Check whether the pfit is attached to our pipe. */
|
||||
if (INTEL_INFO(dev)->gen < 4) {
|
||||
if (crtc->pipe != PIPE_B)
|
||||
return;
|
||||
|
||||
/* gen2/3 store dither state in pfit control, needs to match */
|
||||
pipe_config->gmch_pfit.control = tmp & PANEL_8TO6_DITHER_ENABLE;
|
||||
} else {
|
||||
if ((tmp & PFIT_PIPE_MASK) != (crtc->pipe << PFIT_PIPE_SHIFT))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(tmp & PFIT_ENABLE))
|
||||
return;
|
||||
|
||||
pipe_config->gmch_pfit.control = I915_READ(PFIT_CONTROL);
|
||||
pipe_config->gmch_pfit.control = tmp;
|
||||
pipe_config->gmch_pfit.pgm_ratios = I915_READ(PFIT_PGM_RATIOS);
|
||||
if (INTEL_INFO(dev)->gen < 5)
|
||||
pipe_config->gmch_pfit.lvds_border_bits =
|
||||
@@ -5166,74 +5188,37 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
|
||||
BUG_ON(val != final);
|
||||
}
|
||||
|
||||
/* Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O. */
|
||||
static void lpt_init_pch_refclk(struct drm_device *dev)
|
||||
static void lpt_reset_fdi_mphy(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct intel_encoder *encoder;
|
||||
bool has_vga = false;
|
||||
bool is_sdv = false;
|
||||
u32 tmp;
|
||||
uint32_t tmp;
|
||||
|
||||
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
|
||||
switch (encoder->type) {
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
has_vga = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp = I915_READ(SOUTH_CHICKEN2);
|
||||
tmp |= FDI_MPHY_IOSFSB_RESET_CTL;
|
||||
I915_WRITE(SOUTH_CHICKEN2, tmp);
|
||||
|
||||
if (!has_vga)
|
||||
return;
|
||||
if (wait_for_atomic_us(I915_READ(SOUTH_CHICKEN2) &
|
||||
FDI_MPHY_IOSFSB_RESET_STATUS, 100))
|
||||
DRM_ERROR("FDI mPHY reset assert timeout\n");
|
||||
|
||||
mutex_lock(&dev_priv->dpio_lock);
|
||||
tmp = I915_READ(SOUTH_CHICKEN2);
|
||||
tmp &= ~FDI_MPHY_IOSFSB_RESET_CTL;
|
||||
I915_WRITE(SOUTH_CHICKEN2, tmp);
|
||||
|
||||
/* XXX: Rip out SDV support once Haswell ships for real. */
|
||||
if (IS_HASWELL(dev) && (dev->pci_device & 0xFF00) == 0x0C00)
|
||||
is_sdv = true;
|
||||
if (wait_for_atomic_us((I915_READ(SOUTH_CHICKEN2) &
|
||||
FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100))
|
||||
DRM_ERROR("FDI mPHY reset de-assert timeout\n");
|
||||
}
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
|
||||
tmp &= ~SBI_SSCCTL_DISABLE;
|
||||
tmp |= SBI_SSCCTL_PATHALT;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
|
||||
udelay(24);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
|
||||
tmp &= ~SBI_SSCCTL_PATHALT;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
|
||||
if (!is_sdv) {
|
||||
tmp = I915_READ(SOUTH_CHICKEN2);
|
||||
tmp |= FDI_MPHY_IOSFSB_RESET_CTL;
|
||||
I915_WRITE(SOUTH_CHICKEN2, tmp);
|
||||
|
||||
if (wait_for_atomic_us(I915_READ(SOUTH_CHICKEN2) &
|
||||
FDI_MPHY_IOSFSB_RESET_STATUS, 100))
|
||||
DRM_ERROR("FDI mPHY reset assert timeout\n");
|
||||
|
||||
tmp = I915_READ(SOUTH_CHICKEN2);
|
||||
tmp &= ~FDI_MPHY_IOSFSB_RESET_CTL;
|
||||
I915_WRITE(SOUTH_CHICKEN2, tmp);
|
||||
|
||||
if (wait_for_atomic_us((I915_READ(SOUTH_CHICKEN2) &
|
||||
FDI_MPHY_IOSFSB_RESET_STATUS) == 0,
|
||||
100))
|
||||
DRM_ERROR("FDI mPHY reset de-assert timeout\n");
|
||||
}
|
||||
/* WaMPhyProgramming:hsw */
|
||||
static void lpt_program_fdi_mphy(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x8008, SBI_MPHY);
|
||||
tmp &= ~(0xFF << 24);
|
||||
tmp |= (0x12 << 24);
|
||||
intel_sbi_write(dev_priv, 0x8008, tmp, SBI_MPHY);
|
||||
|
||||
if (is_sdv) {
|
||||
tmp = intel_sbi_read(dev_priv, 0x800C, SBI_MPHY);
|
||||
tmp |= 0x7FFF;
|
||||
intel_sbi_write(dev_priv, 0x800C, tmp, SBI_MPHY);
|
||||
}
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x2008, SBI_MPHY);
|
||||
tmp |= (1 << 11);
|
||||
intel_sbi_write(dev_priv, 0x2008, tmp, SBI_MPHY);
|
||||
@@ -5242,24 +5227,6 @@ static void lpt_init_pch_refclk(struct drm_device *dev)
|
||||
tmp |= (1 << 11);
|
||||
intel_sbi_write(dev_priv, 0x2108, tmp, SBI_MPHY);
|
||||
|
||||
if (is_sdv) {
|
||||
tmp = intel_sbi_read(dev_priv, 0x2038, SBI_MPHY);
|
||||
tmp |= (0x3F << 24) | (0xF << 20) | (0xF << 16);
|
||||
intel_sbi_write(dev_priv, 0x2038, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x2138, SBI_MPHY);
|
||||
tmp |= (0x3F << 24) | (0xF << 20) | (0xF << 16);
|
||||
intel_sbi_write(dev_priv, 0x2138, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x203C, SBI_MPHY);
|
||||
tmp |= (0x3F << 8);
|
||||
intel_sbi_write(dev_priv, 0x203C, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x213C, SBI_MPHY);
|
||||
tmp |= (0x3F << 8);
|
||||
intel_sbi_write(dev_priv, 0x213C, tmp, SBI_MPHY);
|
||||
}
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x206C, SBI_MPHY);
|
||||
tmp |= (1 << 24) | (1 << 21) | (1 << 18);
|
||||
intel_sbi_write(dev_priv, 0x206C, tmp, SBI_MPHY);
|
||||
@@ -5268,17 +5235,15 @@ static void lpt_init_pch_refclk(struct drm_device *dev)
|
||||
tmp |= (1 << 24) | (1 << 21) | (1 << 18);
|
||||
intel_sbi_write(dev_priv, 0x216C, tmp, SBI_MPHY);
|
||||
|
||||
if (!is_sdv) {
|
||||
tmp = intel_sbi_read(dev_priv, 0x2080, SBI_MPHY);
|
||||
tmp &= ~(7 << 13);
|
||||
tmp |= (5 << 13);
|
||||
intel_sbi_write(dev_priv, 0x2080, tmp, SBI_MPHY);
|
||||
tmp = intel_sbi_read(dev_priv, 0x2080, SBI_MPHY);
|
||||
tmp &= ~(7 << 13);
|
||||
tmp |= (5 << 13);
|
||||
intel_sbi_write(dev_priv, 0x2080, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x2180, SBI_MPHY);
|
||||
tmp &= ~(7 << 13);
|
||||
tmp |= (5 << 13);
|
||||
intel_sbi_write(dev_priv, 0x2180, tmp, SBI_MPHY);
|
||||
}
|
||||
tmp = intel_sbi_read(dev_priv, 0x2180, SBI_MPHY);
|
||||
tmp &= ~(7 << 13);
|
||||
tmp |= (5 << 13);
|
||||
intel_sbi_write(dev_priv, 0x2180, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x208C, SBI_MPHY);
|
||||
tmp &= ~0xFF;
|
||||
@@ -5300,34 +5265,120 @@ static void lpt_init_pch_refclk(struct drm_device *dev)
|
||||
tmp |= (0x1C << 16);
|
||||
intel_sbi_write(dev_priv, 0x2198, tmp, SBI_MPHY);
|
||||
|
||||
if (!is_sdv) {
|
||||
tmp = intel_sbi_read(dev_priv, 0x20C4, SBI_MPHY);
|
||||
tmp |= (1 << 27);
|
||||
intel_sbi_write(dev_priv, 0x20C4, tmp, SBI_MPHY);
|
||||
tmp = intel_sbi_read(dev_priv, 0x20C4, SBI_MPHY);
|
||||
tmp |= (1 << 27);
|
||||
intel_sbi_write(dev_priv, 0x20C4, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x21C4, SBI_MPHY);
|
||||
tmp |= (1 << 27);
|
||||
intel_sbi_write(dev_priv, 0x21C4, tmp, SBI_MPHY);
|
||||
tmp = intel_sbi_read(dev_priv, 0x21C4, SBI_MPHY);
|
||||
tmp |= (1 << 27);
|
||||
intel_sbi_write(dev_priv, 0x21C4, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x20EC, SBI_MPHY);
|
||||
tmp &= ~(0xF << 28);
|
||||
tmp |= (4 << 28);
|
||||
intel_sbi_write(dev_priv, 0x20EC, tmp, SBI_MPHY);
|
||||
tmp = intel_sbi_read(dev_priv, 0x20EC, SBI_MPHY);
|
||||
tmp &= ~(0xF << 28);
|
||||
tmp |= (4 << 28);
|
||||
intel_sbi_write(dev_priv, 0x20EC, tmp, SBI_MPHY);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, 0x21EC, SBI_MPHY);
|
||||
tmp &= ~(0xF << 28);
|
||||
tmp |= (4 << 28);
|
||||
intel_sbi_write(dev_priv, 0x21EC, tmp, SBI_MPHY);
|
||||
tmp = intel_sbi_read(dev_priv, 0x21EC, SBI_MPHY);
|
||||
tmp &= ~(0xF << 28);
|
||||
tmp |= (4 << 28);
|
||||
intel_sbi_write(dev_priv, 0x21EC, tmp, SBI_MPHY);
|
||||
}
|
||||
|
||||
/* Implements 3 different sequences from BSpec chapter "Display iCLK
|
||||
* Programming" based on the parameters passed:
|
||||
* - Sequence to enable CLKOUT_DP
|
||||
* - Sequence to enable CLKOUT_DP without spread
|
||||
* - Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O
|
||||
*/
|
||||
static void lpt_enable_clkout_dp(struct drm_device *dev, bool with_spread,
|
||||
bool with_fdi)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t reg, tmp;
|
||||
|
||||
if (WARN(with_fdi && !with_spread, "FDI requires downspread\n"))
|
||||
with_spread = true;
|
||||
if (WARN(dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE &&
|
||||
with_fdi, "LP PCH doesn't have FDI\n"))
|
||||
with_fdi = false;
|
||||
|
||||
mutex_lock(&dev_priv->dpio_lock);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
|
||||
tmp &= ~SBI_SSCCTL_DISABLE;
|
||||
tmp |= SBI_SSCCTL_PATHALT;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
|
||||
udelay(24);
|
||||
|
||||
if (with_spread) {
|
||||
tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
|
||||
tmp &= ~SBI_SSCCTL_PATHALT;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
|
||||
if (with_fdi) {
|
||||
lpt_reset_fdi_mphy(dev_priv);
|
||||
lpt_program_fdi_mphy(dev_priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* ULT uses SBI_GEN0, but ULT doesn't have VGA, so we don't care. */
|
||||
tmp = intel_sbi_read(dev_priv, SBI_DBUFF0, SBI_ICLK);
|
||||
tmp |= SBI_DBUFF0_ENABLE;
|
||||
intel_sbi_write(dev_priv, SBI_DBUFF0, tmp, SBI_ICLK);
|
||||
reg = (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) ?
|
||||
SBI_GEN0 : SBI_DBUFF0;
|
||||
tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
|
||||
tmp |= SBI_GEN0_CFG_BUFFENABLE_DISABLE;
|
||||
intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
|
||||
|
||||
mutex_unlock(&dev_priv->dpio_lock);
|
||||
}
|
||||
|
||||
/* Sequence to disable CLKOUT_DP */
|
||||
static void lpt_disable_clkout_dp(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t reg, tmp;
|
||||
|
||||
mutex_lock(&dev_priv->dpio_lock);
|
||||
|
||||
reg = (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) ?
|
||||
SBI_GEN0 : SBI_DBUFF0;
|
||||
tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
|
||||
tmp &= ~SBI_GEN0_CFG_BUFFENABLE_DISABLE;
|
||||
intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
|
||||
|
||||
tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
|
||||
if (!(tmp & SBI_SSCCTL_DISABLE)) {
|
||||
if (!(tmp & SBI_SSCCTL_PATHALT)) {
|
||||
tmp |= SBI_SSCCTL_PATHALT;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
udelay(32);
|
||||
}
|
||||
tmp |= SBI_SSCCTL_DISABLE;
|
||||
intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev_priv->dpio_lock);
|
||||
}
|
||||
|
||||
static void lpt_init_pch_refclk(struct drm_device *dev)
|
||||
{
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct intel_encoder *encoder;
|
||||
bool has_vga = false;
|
||||
|
||||
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
|
||||
switch (encoder->type) {
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
has_vga = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_vga)
|
||||
lpt_enable_clkout_dp(dev, true, true);
|
||||
else
|
||||
lpt_disable_clkout_dp(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize reference clocks when the driver loads
|
||||
*/
|
||||
@@ -5895,6 +5946,142 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
|
||||
struct intel_crtc *crtc;
|
||||
unsigned long irqflags;
|
||||
uint32_t val, pch_hpd_mask;
|
||||
|
||||
pch_hpd_mask = SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT;
|
||||
if (!(dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE))
|
||||
pch_hpd_mask |= SDE_PORTD_HOTPLUG_CPT | SDE_CRT_HOTPLUG_CPT;
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
|
||||
WARN(crtc->base.enabled, "CRTC for pipe %c enabled\n",
|
||||
pipe_name(crtc->pipe));
|
||||
|
||||
WARN(I915_READ(HSW_PWR_WELL_DRIVER), "Power well on\n");
|
||||
WARN(plls->spll_refcount, "SPLL enabled\n");
|
||||
WARN(plls->wrpll1_refcount, "WRPLL1 enabled\n");
|
||||
WARN(plls->wrpll2_refcount, "WRPLL2 enabled\n");
|
||||
WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n");
|
||||
WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE,
|
||||
"CPU PWM1 enabled\n");
|
||||
WARN(I915_READ(HSW_BLC_PWM2_CTL) & BLM_PWM_ENABLE,
|
||||
"CPU PWM2 enabled\n");
|
||||
WARN(I915_READ(BLC_PWM_PCH_CTL1) & BLM_PCH_PWM_ENABLE,
|
||||
"PCH PWM1 enabled\n");
|
||||
WARN(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE,
|
||||
"Utility pin enabled\n");
|
||||
WARN(I915_READ(PCH_GTC_CTL) & PCH_GTC_ENABLE, "PCH GTC enabled\n");
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
val = I915_READ(DEIMR);
|
||||
WARN((val & ~DE_PCH_EVENT_IVB) != val,
|
||||
"Unexpected DEIMR bits enabled: 0x%x\n", val);
|
||||
val = I915_READ(SDEIMR);
|
||||
WARN((val & ~pch_hpd_mask) != val,
|
||||
"Unexpected SDEIMR bits enabled: 0x%x\n", val);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements pieces of two sequences from BSpec:
|
||||
* - Sequence for display software to disable LCPLL
|
||||
* - Sequence for display software to allow package C8+
|
||||
* The steps implemented here are just the steps that actually touch the LCPLL
|
||||
* register. Callers should take care of disabling all the display engine
|
||||
* functions, doing the mode unset, fixing interrupts, etc.
|
||||
*/
|
||||
void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
|
||||
bool switch_to_fclk, bool allow_power_down)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
assert_can_disable_lcpll(dev_priv);
|
||||
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
|
||||
if (switch_to_fclk) {
|
||||
val |= LCPLL_CD_SOURCE_FCLK;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
|
||||
if (wait_for_atomic_us(I915_READ(LCPLL_CTL) &
|
||||
LCPLL_CD_SOURCE_FCLK_DONE, 1))
|
||||
DRM_ERROR("Switching to FCLK failed\n");
|
||||
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
}
|
||||
|
||||
val |= LCPLL_PLL_DISABLE;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
POSTING_READ(LCPLL_CTL);
|
||||
|
||||
if (wait_for((I915_READ(LCPLL_CTL) & LCPLL_PLL_LOCK) == 0, 1))
|
||||
DRM_ERROR("LCPLL still locked\n");
|
||||
|
||||
val = I915_READ(D_COMP);
|
||||
val |= D_COMP_COMP_DISABLE;
|
||||
I915_WRITE(D_COMP, val);
|
||||
POSTING_READ(D_COMP);
|
||||
ndelay(100);
|
||||
|
||||
if (wait_for((I915_READ(D_COMP) & D_COMP_RCOMP_IN_PROGRESS) == 0, 1))
|
||||
DRM_ERROR("D_COMP RCOMP still in progress\n");
|
||||
|
||||
if (allow_power_down) {
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
val |= LCPLL_POWER_DOWN_ALLOW;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
POSTING_READ(LCPLL_CTL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fully restores LCPLL, disallowing power down and switching back to LCPLL
|
||||
* source.
|
||||
*/
|
||||
void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
|
||||
if ((val & (LCPLL_PLL_LOCK | LCPLL_PLL_DISABLE | LCPLL_CD_SOURCE_FCLK |
|
||||
LCPLL_POWER_DOWN_ALLOW)) == LCPLL_PLL_LOCK)
|
||||
return;
|
||||
|
||||
if (val & LCPLL_POWER_DOWN_ALLOW) {
|
||||
val &= ~LCPLL_POWER_DOWN_ALLOW;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
}
|
||||
|
||||
val = I915_READ(D_COMP);
|
||||
val |= D_COMP_COMP_FORCE;
|
||||
val &= ~D_COMP_COMP_DISABLE;
|
||||
I915_WRITE(D_COMP, val);
|
||||
I915_READ(D_COMP);
|
||||
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
val &= ~LCPLL_PLL_DISABLE;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
|
||||
if (wait_for(I915_READ(LCPLL_CTL) & LCPLL_PLL_LOCK, 5))
|
||||
DRM_ERROR("LCPLL not locked yet\n");
|
||||
|
||||
if (val & LCPLL_CD_SOURCE_FCLK) {
|
||||
val = I915_READ(LCPLL_CTL);
|
||||
val &= ~LCPLL_CD_SOURCE_FCLK;
|
||||
I915_WRITE(LCPLL_CTL, val);
|
||||
|
||||
if (wait_for_atomic_us((I915_READ(LCPLL_CTL) &
|
||||
LCPLL_CD_SOURCE_FCLK_DONE) == 0, 1))
|
||||
DRM_ERROR("Switching back to LCPLL failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void haswell_modeset_global_resources(struct drm_device *dev)
|
||||
{
|
||||
bool enable = false;
|
||||
@@ -8434,6 +8621,8 @@ check_shared_dpll_state(struct drm_device *dev)
|
||||
pll->active, pll->refcount);
|
||||
WARN(pll->active && !pll->on,
|
||||
"pll in active use but not on in sw tracking\n");
|
||||
WARN(pll->on && !pll->active,
|
||||
"pll in on but not on in use in sw tracking\n");
|
||||
WARN(pll->on != active,
|
||||
"pll on state mismatch (expected %i, found %i)\n",
|
||||
pll->on, active);
|
||||
@@ -8658,15 +8847,20 @@ static void intel_set_config_restore_state(struct drm_device *dev,
|
||||
}
|
||||
|
||||
static bool
|
||||
is_crtc_connector_off(struct drm_crtc *crtc, struct drm_connector *connectors,
|
||||
int num_connectors)
|
||||
is_crtc_connector_off(struct drm_mode_set *set)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_connectors; i++)
|
||||
if (connectors[i].encoder &&
|
||||
connectors[i].encoder->crtc == crtc &&
|
||||
connectors[i].dpms != DRM_MODE_DPMS_ON)
|
||||
if (set->num_connectors == 0)
|
||||
return false;
|
||||
|
||||
if (WARN_ON(set->connectors == NULL))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < set->num_connectors; i++)
|
||||
if (set->connectors[i]->encoder &&
|
||||
set->connectors[i]->encoder->crtc == set->crtc &&
|
||||
set->connectors[i]->dpms != DRM_MODE_DPMS_ON)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -8679,10 +8873,8 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set,
|
||||
|
||||
/* We should be able to check here if the fb has the same properties
|
||||
* and then just flip_or_move it */
|
||||
if (set->connectors != NULL &&
|
||||
is_crtc_connector_off(set->crtc, *set->connectors,
|
||||
set->num_connectors)) {
|
||||
config->mode_changed = true;
|
||||
if (is_crtc_connector_off(set)) {
|
||||
config->mode_changed = true;
|
||||
} else if (set->crtc->fb != set->fb) {
|
||||
/* If we have no fb then treat it as a full mode set */
|
||||
if (set->crtc->fb == NULL) {
|
||||
@@ -9437,9 +9629,12 @@ static void intel_init_display(struct drm_device *dev)
|
||||
else if (IS_I915G(dev))
|
||||
dev_priv->display.get_display_clock_speed =
|
||||
i915_get_display_clock_speed;
|
||||
else if (IS_I945GM(dev) || IS_845G(dev) || IS_PINEVIEW_M(dev))
|
||||
else if (IS_I945GM(dev) || IS_845G(dev))
|
||||
dev_priv->display.get_display_clock_speed =
|
||||
i9xx_misc_get_display_clock_speed;
|
||||
else if (IS_PINEVIEW(dev))
|
||||
dev_priv->display.get_display_clock_speed =
|
||||
pnv_get_display_clock_speed;
|
||||
else if (IS_I915GM(dev))
|
||||
dev_priv->display.get_display_clock_speed =
|
||||
i915gm_get_display_clock_speed;
|
||||
@@ -9536,6 +9731,17 @@ static void quirk_invert_brightness(struct drm_device *dev)
|
||||
DRM_INFO("applying inverted panel brightness quirk\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Some machines (Dell XPS13) suffer broken backlight controls if
|
||||
* BLM_PCH_PWM_ENABLE is set.
|
||||
*/
|
||||
static void quirk_no_pcm_pwm_enable(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
dev_priv->quirks |= QUIRK_NO_PCH_PWM_ENABLE;
|
||||
DRM_INFO("applying no-PCH_PWM_ENABLE quirk\n");
|
||||
}
|
||||
|
||||
struct intel_quirk {
|
||||
int device;
|
||||
int subsystem_vendor;
|
||||
@@ -9605,6 +9811,11 @@ static struct intel_quirk intel_quirks[] = {
|
||||
|
||||
/* Acer Aspire 4736Z */
|
||||
{ 0x2a42, 0x1025, 0x0260, quirk_invert_brightness },
|
||||
|
||||
/* Dell XPS13 HD Sandy Bridge */
|
||||
{ 0x0116, 0x1028, 0x052e, quirk_no_pcm_pwm_enable },
|
||||
/* Dell XPS13 HD and XPS13 FHD Ivy Bridge */
|
||||
{ 0x0166, 0x1028, 0x058b, quirk_no_pcm_pwm_enable },
|
||||
};
|
||||
|
||||
static void intel_init_quirks(struct drm_device *dev)
|
||||
@@ -9955,8 +10166,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
|
||||
}
|
||||
pll->refcount = pll->active;
|
||||
|
||||
DRM_DEBUG_KMS("%s hw state readout: refcount %i\n",
|
||||
pll->name, pll->refcount);
|
||||
DRM_DEBUG_KMS("%s hw state readout: refcount %i, on %i\n",
|
||||
pll->name, pll->refcount, pll->on);
|
||||
}
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
|
||||
@@ -10016,6 +10227,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
|
||||
struct drm_plane *plane;
|
||||
struct intel_crtc *crtc;
|
||||
struct intel_encoder *encoder;
|
||||
int i;
|
||||
|
||||
intel_modeset_readout_hw_state(dev);
|
||||
|
||||
@@ -10047,6 +10259,18 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
|
||||
intel_dump_pipe_config(crtc, &crtc->config, "[setup_hw_state]");
|
||||
}
|
||||
|
||||
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
|
||||
struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
|
||||
|
||||
if (!pll->on || pll->active)
|
||||
continue;
|
||||
|
||||
DRM_DEBUG_KMS("%s enabled but not in use, disabling\n", pll->name);
|
||||
|
||||
pll->disable(dev_priv, pll);
|
||||
pll->on = false;
|
||||
}
|
||||
|
||||
if (force_restore) {
|
||||
/*
|
||||
* We need to use raw interfaces for restoring state to avoid
|
||||
@@ -10256,8 +10480,7 @@ intel_display_capture_error_state(struct drm_device *dev)
|
||||
* well was on, so here we have to clear the FPGA_DBG_RM_NOCLAIM bit to
|
||||
* prevent the next I915_WRITE from detecting it and printing an error
|
||||
* message. */
|
||||
if (HAS_POWER_WELL(dev))
|
||||
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
intel_uncore_clear_errors(dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -276,6 +276,45 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t get_aux_clock_divider(struct intel_dp *intel_dp,
|
||||
int index)
|
||||
{
|
||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* The clock divider is based off the hrawclk,
|
||||
* and would like to run at 2MHz. So, take the
|
||||
* hrawclk value and divide by 2 and use that
|
||||
*
|
||||
* Note that PCH attached eDP panels should use a 125MHz input
|
||||
* clock divider.
|
||||
*/
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
return index ? 0 : 100;
|
||||
} else if (intel_dig_port->port == PORT_A) {
|
||||
if (index)
|
||||
return 0;
|
||||
if (HAS_DDI(dev))
|
||||
return DIV_ROUND_CLOSEST(intel_ddi_get_cdclk_freq(dev_priv), 2000);
|
||||
else if (IS_GEN6(dev) || IS_GEN7(dev))
|
||||
return 200; /* SNB & IVB eDP input clock at 400Mhz */
|
||||
else
|
||||
return 225; /* eDP input clock at 450Mhz */
|
||||
} else if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
|
||||
/* Workaround for non-ULT HSW */
|
||||
switch (index) {
|
||||
case 0: return 63;
|
||||
case 1: return 72;
|
||||
default: return 0;
|
||||
}
|
||||
} else if (HAS_PCH_SPLIT(dev)) {
|
||||
return index ? 0 : DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
|
||||
} else {
|
||||
return index ? 0 :intel_hrawclk(dev) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_aux_ch(struct intel_dp *intel_dp,
|
||||
uint8_t *send, int send_bytes,
|
||||
@@ -286,10 +325,10 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t ch_ctl = intel_dp->aux_ch_ctl_reg;
|
||||
uint32_t ch_data = ch_ctl + 4;
|
||||
uint32_t aux_clock_divider;
|
||||
int i, ret, recv_bytes;
|
||||
uint32_t status;
|
||||
uint32_t aux_clock_divider;
|
||||
int try, precharge;
|
||||
int try, precharge, clock = 0;
|
||||
bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
|
||||
|
||||
/* dp aux is extremely sensitive to irq latency, hence request the
|
||||
@@ -299,31 +338,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
||||
pm_qos_update_request(&dev_priv->pm_qos, 0);
|
||||
|
||||
intel_dp_check_edp(intel_dp);
|
||||
/* The clock divider is based off the hrawclk,
|
||||
* and would like to run at 2MHz. So, take the
|
||||
* hrawclk value and divide by 2 and use that
|
||||
*
|
||||
* Note that PCH attached eDP panels should use a 125MHz input
|
||||
* clock divider.
|
||||
*/
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
aux_clock_divider = 100;
|
||||
} else if (intel_dig_port->port == PORT_A) {
|
||||
if (HAS_DDI(dev))
|
||||
aux_clock_divider = DIV_ROUND_CLOSEST(
|
||||
intel_ddi_get_cdclk_freq(dev_priv), 2000);
|
||||
else if (IS_GEN6(dev) || IS_GEN7(dev))
|
||||
aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */
|
||||
else
|
||||
aux_clock_divider = 225; /* eDP input clock at 450Mhz */
|
||||
} else if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
|
||||
/* Workaround for non-ULT HSW */
|
||||
aux_clock_divider = 74;
|
||||
} else if (HAS_PCH_SPLIT(dev)) {
|
||||
aux_clock_divider = DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
|
||||
} else {
|
||||
aux_clock_divider = intel_hrawclk(dev) / 2;
|
||||
}
|
||||
|
||||
if (IS_GEN6(dev))
|
||||
precharge = 3;
|
||||
@@ -345,37 +359,41 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Must try at least 3 times according to DP spec */
|
||||
for (try = 0; try < 5; try++) {
|
||||
/* Load the send data into the aux channel data registers */
|
||||
for (i = 0; i < send_bytes; i += 4)
|
||||
I915_WRITE(ch_data + i,
|
||||
pack_aux(send + i, send_bytes - i));
|
||||
while ((aux_clock_divider = get_aux_clock_divider(intel_dp, clock++))) {
|
||||
/* Must try at least 3 times according to DP spec */
|
||||
for (try = 0; try < 5; try++) {
|
||||
/* Load the send data into the aux channel data registers */
|
||||
for (i = 0; i < send_bytes; i += 4)
|
||||
I915_WRITE(ch_data + i,
|
||||
pack_aux(send + i, send_bytes - i));
|
||||
|
||||
/* Send the command and wait for it to complete */
|
||||
I915_WRITE(ch_ctl,
|
||||
DP_AUX_CH_CTL_SEND_BUSY |
|
||||
(has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
|
||||
DP_AUX_CH_CTL_TIME_OUT_400us |
|
||||
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
||||
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
|
||||
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
||||
/* Send the command and wait for it to complete */
|
||||
I915_WRITE(ch_ctl,
|
||||
DP_AUX_CH_CTL_SEND_BUSY |
|
||||
(has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
|
||||
DP_AUX_CH_CTL_TIME_OUT_400us |
|
||||
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
||||
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
|
||||
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
||||
|
||||
status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
|
||||
status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
|
||||
|
||||
/* Clear done status and any errors */
|
||||
I915_WRITE(ch_ctl,
|
||||
status |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
||||
/* Clear done status and any errors */
|
||||
I915_WRITE(ch_ctl,
|
||||
status |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
||||
|
||||
if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR))
|
||||
continue;
|
||||
if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR))
|
||||
continue;
|
||||
if (status & DP_AUX_CH_CTL_DONE)
|
||||
break;
|
||||
}
|
||||
if (status & DP_AUX_CH_CTL_DONE)
|
||||
break;
|
||||
}
|
||||
@@ -710,8 +728,11 @@ intel_dp_compute_config(struct intel_encoder *encoder,
|
||||
/* Walk through all bpp values. Luckily they're all nicely spaced with 2
|
||||
* bpc in between. */
|
||||
bpp = pipe_config->pipe_bpp;
|
||||
if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp)
|
||||
if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp) {
|
||||
DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n",
|
||||
dev_priv->vbt.edp_bpp);
|
||||
bpp = min_t(int, bpp, dev_priv->vbt.edp_bpp);
|
||||
}
|
||||
|
||||
for (; bpp >= 6*3; bpp -= 2*3) {
|
||||
mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp);
|
||||
@@ -1369,6 +1390,268 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_edp_psr(struct intel_dp *intel_dp)
|
||||
{
|
||||
return is_edp(intel_dp) &&
|
||||
intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
|
||||
}
|
||||
|
||||
static bool intel_edp_is_psr_enabled(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!IS_HASWELL(dev))
|
||||
return false;
|
||||
|
||||
return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
|
||||
}
|
||||
|
||||
static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
|
||||
struct edp_vsc_psr *vsc_psr)
|
||||
{
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_device *dev = dig_port->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
|
||||
u32 ctl_reg = HSW_TVIDEO_DIP_CTL(crtc->config.cpu_transcoder);
|
||||
u32 data_reg = HSW_TVIDEO_DIP_VSC_DATA(crtc->config.cpu_transcoder);
|
||||
uint32_t *data = (uint32_t *) vsc_psr;
|
||||
unsigned int i;
|
||||
|
||||
/* As per BSPec (Pipe Video Data Island Packet), we need to disable
|
||||
the video DIP being updated before program video DIP data buffer
|
||||
registers for DIP being updated. */
|
||||
I915_WRITE(ctl_reg, 0);
|
||||
POSTING_READ(ctl_reg);
|
||||
|
||||
for (i = 0; i < VIDEO_DIP_VSC_DATA_SIZE; i += 4) {
|
||||
if (i < sizeof(struct edp_vsc_psr))
|
||||
I915_WRITE(data_reg + i, *data++);
|
||||
else
|
||||
I915_WRITE(data_reg + i, 0);
|
||||
}
|
||||
|
||||
I915_WRITE(ctl_reg, VIDEO_DIP_ENABLE_VSC_HSW);
|
||||
POSTING_READ(ctl_reg);
|
||||
}
|
||||
|
||||
static void intel_edp_psr_setup(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct edp_vsc_psr psr_vsc;
|
||||
|
||||
if (intel_dp->psr_setup_done)
|
||||
return;
|
||||
|
||||
/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
|
||||
memset(&psr_vsc, 0, sizeof(psr_vsc));
|
||||
psr_vsc.sdp_header.HB0 = 0;
|
||||
psr_vsc.sdp_header.HB1 = 0x7;
|
||||
psr_vsc.sdp_header.HB2 = 0x2;
|
||||
psr_vsc.sdp_header.HB3 = 0x8;
|
||||
intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
|
||||
|
||||
/* Avoid continuous PSR exit by masking memup and hpd */
|
||||
I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
|
||||
EDP_PSR_DEBUG_MASK_HPD);
|
||||
|
||||
intel_dp->psr_setup_done = true;
|
||||
}
|
||||
|
||||
static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp, 0);
|
||||
int precharge = 0x3;
|
||||
int msg_size = 5; /* Header(4) + Message(1) */
|
||||
|
||||
/* Enable PSR in sink */
|
||||
if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT)
|
||||
intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
|
||||
DP_PSR_ENABLE &
|
||||
~DP_PSR_MAIN_LINK_ACTIVE);
|
||||
else
|
||||
intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
|
||||
DP_PSR_ENABLE |
|
||||
DP_PSR_MAIN_LINK_ACTIVE);
|
||||
|
||||
/* Setup AUX registers */
|
||||
I915_WRITE(EDP_PSR_AUX_DATA1, EDP_PSR_DPCD_COMMAND);
|
||||
I915_WRITE(EDP_PSR_AUX_DATA2, EDP_PSR_DPCD_NORMAL_OPERATION);
|
||||
I915_WRITE(EDP_PSR_AUX_CTL,
|
||||
DP_AUX_CH_CTL_TIME_OUT_400us |
|
||||
(msg_size << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
||||
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
|
||||
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
|
||||
}
|
||||
|
||||
static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t max_sleep_time = 0x1f;
|
||||
uint32_t idle_frames = 1;
|
||||
uint32_t val = 0x0;
|
||||
|
||||
if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) {
|
||||
val |= EDP_PSR_LINK_STANDBY;
|
||||
val |= EDP_PSR_TP2_TP3_TIME_0us;
|
||||
val |= EDP_PSR_TP1_TIME_0us;
|
||||
val |= EDP_PSR_SKIP_AUX_EXIT;
|
||||
} else
|
||||
val |= EDP_PSR_LINK_DISABLE;
|
||||
|
||||
I915_WRITE(EDP_PSR_CTL, val |
|
||||
EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES |
|
||||
max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
|
||||
idle_frames << EDP_PSR_IDLE_FRAME_SHIFT |
|
||||
EDP_PSR_ENABLE);
|
||||
}
|
||||
|
||||
static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_device *dev = dig_port->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_crtc *crtc = dig_port->base.base.crtc;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj;
|
||||
struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
|
||||
|
||||
if (!IS_HASWELL(dev)) {
|
||||
DRM_DEBUG_KMS("PSR not supported on this platform\n");
|
||||
dev_priv->no_psr_reason = PSR_NO_SOURCE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
|
||||
(dig_port->port != PORT_A)) {
|
||||
DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
|
||||
dev_priv->no_psr_reason = PSR_HSW_NOT_DDIA;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_edp_psr(intel_dp)) {
|
||||
DRM_DEBUG_KMS("PSR not supported by this panel\n");
|
||||
dev_priv->no_psr_reason = PSR_NO_SINK;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!i915_enable_psr) {
|
||||
DRM_DEBUG_KMS("PSR disable by flag\n");
|
||||
dev_priv->no_psr_reason = PSR_MODULE_PARAM;
|
||||
return false;
|
||||
}
|
||||
|
||||
crtc = dig_port->base.base.crtc;
|
||||
if (crtc == NULL) {
|
||||
DRM_DEBUG_KMS("crtc not active for PSR\n");
|
||||
dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
|
||||
return false;
|
||||
}
|
||||
|
||||
intel_crtc = to_intel_crtc(crtc);
|
||||
if (!intel_crtc->active || !crtc->fb || !crtc->mode.clock) {
|
||||
DRM_DEBUG_KMS("crtc not active for PSR\n");
|
||||
dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = to_intel_framebuffer(crtc->fb)->obj;
|
||||
if (obj->tiling_mode != I915_TILING_X ||
|
||||
obj->fence_reg == I915_FENCE_REG_NONE) {
|
||||
DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n");
|
||||
dev_priv->no_psr_reason = PSR_NOT_TILED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) {
|
||||
DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n");
|
||||
dev_priv->no_psr_reason = PSR_SPRITE_ENABLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config.cpu_transcoder)) &
|
||||
S3D_ENABLE) {
|
||||
DRM_DEBUG_KMS("PSR condition failed: Stereo 3D is Enabled\n");
|
||||
dev_priv->no_psr_reason = PSR_S3D_ENABLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n");
|
||||
dev_priv->no_psr_reason = PSR_INTERLACED_ENABLED;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
|
||||
if (!intel_edp_psr_match_conditions(intel_dp) ||
|
||||
intel_edp_is_psr_enabled(dev))
|
||||
return;
|
||||
|
||||
/* Setup PSR once */
|
||||
intel_edp_psr_setup(intel_dp);
|
||||
|
||||
/* Enable PSR on the panel */
|
||||
intel_edp_psr_enable_sink(intel_dp);
|
||||
|
||||
/* Enable PSR on the host */
|
||||
intel_edp_psr_enable_source(intel_dp);
|
||||
}
|
||||
|
||||
void intel_edp_psr_enable(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
|
||||
if (intel_edp_psr_match_conditions(intel_dp) &&
|
||||
!intel_edp_is_psr_enabled(dev))
|
||||
intel_edp_psr_do_enable(intel_dp);
|
||||
}
|
||||
|
||||
void intel_edp_psr_disable(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!intel_edp_is_psr_enabled(dev))
|
||||
return;
|
||||
|
||||
I915_WRITE(EDP_PSR_CTL, I915_READ(EDP_PSR_CTL) & ~EDP_PSR_ENABLE);
|
||||
|
||||
/* Wait till PSR is idle */
|
||||
if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL) &
|
||||
EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
|
||||
DRM_ERROR("Timed out waiting for PSR Idle State\n");
|
||||
}
|
||||
|
||||
void intel_edp_psr_update(struct drm_device *dev)
|
||||
{
|
||||
struct intel_encoder *encoder;
|
||||
struct intel_dp *intel_dp = NULL;
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
|
||||
if (encoder->type == INTEL_OUTPUT_EDP) {
|
||||
intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
|
||||
if (!is_edp_psr(intel_dp))
|
||||
return;
|
||||
|
||||
if (!intel_edp_psr_match_conditions(intel_dp))
|
||||
intel_edp_psr_disable(intel_dp);
|
||||
else
|
||||
if (!intel_edp_is_psr_enabled(dev))
|
||||
intel_edp_psr_do_enable(intel_dp);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_disable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
@@ -2282,6 +2565,13 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] == 0)
|
||||
return false; /* DPCD not present */
|
||||
|
||||
/* Check if the panel supports PSR */
|
||||
memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
|
||||
intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT,
|
||||
intel_dp->psr_dpcd,
|
||||
sizeof(intel_dp->psr_dpcd));
|
||||
if (is_edp_psr(intel_dp))
|
||||
DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
|
||||
if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
|
||||
DP_DWN_STRM_PORT_PRESENT))
|
||||
return true; /* native DP sink */
|
||||
@@ -2549,6 +2839,9 @@ intel_dp_detect(struct drm_connector *connector, bool force)
|
||||
enum drm_connector_status status;
|
||||
struct edid *edid = NULL;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
|
||||
intel_dp->has_audio = false;
|
||||
|
||||
if (HAS_PCH_SPLIT(dev))
|
||||
@@ -3173,6 +3466,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n",
|
||||
error, port_name(port));
|
||||
|
||||
intel_dp->psr_setup_done = false;
|
||||
|
||||
if (!intel_edp_init_connector(intel_dp, intel_connector)) {
|
||||
i2c_del_adapter(&intel_dp->adapter);
|
||||
if (is_edp(intel_dp)) {
|
||||
|
||||
@@ -487,6 +487,7 @@ struct intel_dp {
|
||||
uint8_t link_bw;
|
||||
uint8_t lane_count;
|
||||
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
|
||||
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_dp_aux_data algo;
|
||||
@@ -498,13 +499,14 @@ struct intel_dp {
|
||||
int backlight_off_delay;
|
||||
struct delayed_work panel_vdd_work;
|
||||
bool want_panel_vdd;
|
||||
bool psr_setup_done;
|
||||
struct intel_connector *attached_connector;
|
||||
};
|
||||
|
||||
struct intel_digital_port {
|
||||
struct intel_encoder base;
|
||||
enum port port;
|
||||
u32 port_reversal;
|
||||
u32 saved_port_bits;
|
||||
struct intel_dp dp;
|
||||
struct intel_hdmi hdmi;
|
||||
};
|
||||
@@ -804,7 +806,6 @@ extern void intel_init_power_well(struct drm_device *dev);
|
||||
extern void intel_set_power_well(struct drm_device *dev, bool enable);
|
||||
extern void intel_enable_gt_powersave(struct drm_device *dev);
|
||||
extern void intel_disable_gt_powersave(struct drm_device *dev);
|
||||
extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
|
||||
extern void ironlake_teardown_rc6(struct drm_device *dev);
|
||||
|
||||
extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
|
||||
@@ -833,4 +834,11 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
|
||||
enum transcoder pch_transcoder,
|
||||
bool enable);
|
||||
|
||||
extern void intel_edp_psr_enable(struct intel_dp *intel_dp);
|
||||
extern void intel_edp_psr_disable(struct intel_dp *intel_dp);
|
||||
extern void intel_edp_psr_update(struct drm_device *dev);
|
||||
extern void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
|
||||
bool switch_to_fclk, bool allow_power_down);
|
||||
extern void hsw_restore_lcpll(struct drm_i915_private *dev_priv);
|
||||
|
||||
#endif /* __INTEL_DRV_H__ */
|
||||
|
||||
@@ -332,6 +332,8 @@ static enum drm_connector_status
|
||||
intel_dvo_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -866,6 +866,9 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
|
||||
struct edid *edid;
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
|
||||
intel_hdmi->has_hdmi_sink = false;
|
||||
intel_hdmi->has_audio = false;
|
||||
intel_hdmi->rgb_quant_range_selectable = false;
|
||||
|
||||
@@ -109,6 +109,13 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
|
||||
flags |= DRM_MODE_FLAG_PVSYNC;
|
||||
|
||||
pipe_config->adjusted_mode.flags |= flags;
|
||||
|
||||
/* gen2/3 store dither state in pfit control, needs to match */
|
||||
if (INTEL_INFO(dev)->gen < 4) {
|
||||
tmp = I915_READ(PFIT_CONTROL);
|
||||
|
||||
pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
|
||||
@@ -297,14 +304,11 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
|
||||
|
||||
intel_pch_panel_fitting(intel_crtc, pipe_config,
|
||||
intel_connector->panel.fitting_mode);
|
||||
return true;
|
||||
} else {
|
||||
intel_gmch_panel_fitting(intel_crtc, pipe_config,
|
||||
intel_connector->panel.fitting_mode);
|
||||
}
|
||||
|
||||
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
||||
pipe_config->timings_set = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: It would be nice to support lower refresh rates on the
|
||||
@@ -339,6 +343,9 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
|
||||
struct drm_device *dev = connector->dev;
|
||||
enum drm_connector_status status;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
|
||||
status = intel_panel_detect(dev);
|
||||
if (status != connector_status_unknown)
|
||||
return status;
|
||||
|
||||
@@ -1333,7 +1333,9 @@ void intel_setup_overlay(struct drm_device *dev)
|
||||
|
||||
overlay->dev = dev;
|
||||
|
||||
reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE);
|
||||
reg_bo = NULL;
|
||||
if (!OVERLAY_NEEDS_PHYSICAL(dev))
|
||||
reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE);
|
||||
if (reg_bo == NULL)
|
||||
reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE);
|
||||
if (reg_bo == NULL)
|
||||
|
||||
@@ -194,6 +194,9 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
|
||||
adjusted_mode->vdisplay == mode->vdisplay)
|
||||
goto out;
|
||||
|
||||
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
||||
pipe_config->timings_set = true;
|
||||
|
||||
switch (fitting_mode) {
|
||||
case DRM_MODE_SCALE_CENTER:
|
||||
/*
|
||||
@@ -580,7 +583,8 @@ void intel_panel_enable_backlight(struct drm_device *dev,
|
||||
POSTING_READ(reg);
|
||||
I915_WRITE(reg, tmp | BLM_PWM_ENABLE);
|
||||
|
||||
if (HAS_PCH_SPLIT(dev)) {
|
||||
if (HAS_PCH_SPLIT(dev) &&
|
||||
!(dev_priv->quirks & QUIRK_NO_PCH_PWM_ENABLE)) {
|
||||
tmp = I915_READ(BLC_PWM_PCH_CTL1);
|
||||
tmp |= BLM_PCH_PWM_ENABLE;
|
||||
tmp &= ~BLM_PCH_OVERRIDE_ENABLE;
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <drm/i915_powerwell.h>
|
||||
|
||||
#define FORCEWAKE_ACK_TIMEOUT_MS 2
|
||||
|
||||
/* FBC, or Frame Buffer Compression, is a technique employed to compress the
|
||||
* framebuffer contents in-memory, aiming at reducing the required bandwidth
|
||||
* during in-memory transfers and, therefore, reduce the power packet.
|
||||
@@ -404,6 +402,8 @@ static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
|
||||
* following the termination of the page-flipping sequence
|
||||
* and indeed performing the enable as a co-routine and not
|
||||
* waiting synchronously upon the vblank.
|
||||
*
|
||||
* WaFbcWaitForVBlankBeforeEnable:ilk,snb
|
||||
*/
|
||||
schedule_delayed_work(&work->work, msecs_to_jiffies(50));
|
||||
}
|
||||
@@ -3121,13 +3121,10 @@ void valleyview_set_rps(struct drm_device *dev, u8 val)
|
||||
trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val));
|
||||
}
|
||||
|
||||
|
||||
static void gen6_disable_rps(struct drm_device *dev)
|
||||
static void gen6_disable_rps_interrupts(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
I915_WRITE(GEN6_RC_CONTROL, 0);
|
||||
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
|
||||
I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) & ~GEN6_PM_RPS_EVENTS);
|
||||
/* Complete PM interrupt masking here doesn't race with the rps work
|
||||
@@ -3142,23 +3139,23 @@ static void gen6_disable_rps(struct drm_device *dev)
|
||||
I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
|
||||
}
|
||||
|
||||
static void gen6_disable_rps(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
I915_WRITE(GEN6_RC_CONTROL, 0);
|
||||
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
|
||||
|
||||
gen6_disable_rps_interrupts(dev);
|
||||
}
|
||||
|
||||
static void valleyview_disable_rps(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
I915_WRITE(GEN6_RC_CONTROL, 0);
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
|
||||
I915_WRITE(GEN6_PMIER, 0);
|
||||
/* Complete PM interrupt masking here doesn't race with the rps work
|
||||
* item again unmasking PM interrupts because that is using a different
|
||||
* register (PMIMR) to mask PM interrupts. The only risk is in leaving
|
||||
* stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
|
||||
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
dev_priv->rps.pm_iir = 0;
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
|
||||
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
|
||||
gen6_disable_rps_interrupts(dev);
|
||||
|
||||
if (dev_priv->vlv_pctx) {
|
||||
drm_gem_object_unreference(&dev_priv->vlv_pctx->base);
|
||||
@@ -3168,6 +3165,10 @@ static void valleyview_disable_rps(struct drm_device *dev)
|
||||
|
||||
int intel_enable_rc6(const struct drm_device *dev)
|
||||
{
|
||||
/* No RC6 before Ironlake */
|
||||
if (INTEL_INFO(dev)->gen < 5)
|
||||
return 0;
|
||||
|
||||
/* Respect the kernel parameter if it is set */
|
||||
if (i915_enable_rc6 >= 0)
|
||||
return i915_enable_rc6;
|
||||
@@ -3191,6 +3192,19 @@ int intel_enable_rc6(const struct drm_device *dev)
|
||||
return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
|
||||
}
|
||||
|
||||
static void gen6_enable_rps_interrupts(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
WARN_ON(dev_priv->rps.pm_iir);
|
||||
I915_WRITE(GEN6_PMIMR, I915_READ(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
|
||||
I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
/* unmask all PM interrupts */
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0);
|
||||
}
|
||||
|
||||
static void gen6_enable_rps(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
@@ -3319,17 +3333,7 @@ static void gen6_enable_rps(struct drm_device *dev)
|
||||
|
||||
gen6_set_rps(dev_priv->dev, (gt_perf_status & 0xff00) >> 8);
|
||||
|
||||
/* requires MSI enabled */
|
||||
I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) | GEN6_PM_RPS_EVENTS);
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
/* FIXME: Our interrupt enabling sequence is bonghits.
|
||||
* dev_priv->rps.pm_iir really should be 0 here. */
|
||||
dev_priv->rps.pm_iir = 0;
|
||||
I915_WRITE(GEN6_PMIMR, I915_READ(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
|
||||
I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
/* unmask all PM interrupts */
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0);
|
||||
gen6_enable_rps_interrupts(dev);
|
||||
|
||||
rc6vids = 0;
|
||||
ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids);
|
||||
@@ -3599,14 +3603,7 @@ static void valleyview_enable_rps(struct drm_device *dev)
|
||||
|
||||
valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
|
||||
|
||||
/* requires MSI enabled */
|
||||
I915_WRITE(GEN6_PMIER, GEN6_PM_RPS_EVENTS);
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
WARN_ON(dev_priv->rps.pm_iir != 0);
|
||||
I915_WRITE(GEN6_PMIMR, 0);
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
/* enable all PM interrupts */
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0);
|
||||
gen6_enable_rps_interrupts(dev);
|
||||
|
||||
gen6_gt_force_wake_put(dev_priv);
|
||||
}
|
||||
@@ -4421,7 +4418,10 @@ static void ironlake_init_clock_gating(struct drm_device *dev)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE;
|
||||
|
||||
/* Required for FBC */
|
||||
/*
|
||||
* Required for FBC
|
||||
* WaFbcDisableDpfcClockGating:ilk
|
||||
*/
|
||||
dspclk_gate |= ILK_DPFCRUNIT_CLOCK_GATE_DISABLE |
|
||||
ILK_DPFCUNIT_CLOCK_GATE_DISABLE |
|
||||
ILK_DPFDUNIT_CLOCK_GATE_ENABLE;
|
||||
@@ -4458,6 +4458,7 @@ static void ironlake_init_clock_gating(struct drm_device *dev)
|
||||
* The bit 7,8,9 of 0x42020.
|
||||
*/
|
||||
if (IS_IRONLAKE_M(dev)) {
|
||||
/* WaFbcAsynchFlipDisableFbcQueue:ilk */
|
||||
I915_WRITE(ILK_DISPLAY_CHICKEN1,
|
||||
I915_READ(ILK_DISPLAY_CHICKEN1) |
|
||||
ILK_FBCQ_DIS);
|
||||
@@ -4594,6 +4595,8 @@ static void gen6_init_clock_gating(struct drm_device *dev)
|
||||
* The bit5 and bit7 of 0x42020
|
||||
* The bit14 of 0x70180
|
||||
* The bit14 of 0x71180
|
||||
*
|
||||
* WaFbcAsynchFlipDisableFbcQueue:snb
|
||||
*/
|
||||
I915_WRITE(ILK_DISPLAY_CHICKEN1,
|
||||
I915_READ(ILK_DISPLAY_CHICKEN1) |
|
||||
@@ -5284,254 +5287,6 @@ void intel_init_pm(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void __gen6_gt_wait_for_thread_c0(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 gt_thread_status_mask;
|
||||
|
||||
if (IS_HASWELL(dev_priv->dev))
|
||||
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK_HSW;
|
||||
else
|
||||
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK;
|
||||
|
||||
/* w/a for a sporadic read returning 0 by waiting for the GT
|
||||
* thread to wake up.
|
||||
*/
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(GEN6_GT_THREAD_STATUS_REG) & gt_thread_status_mask) == 0, 500))
|
||||
DRM_ERROR("GT thread status wait timed out\n");
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE, 0);
|
||||
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE, 1);
|
||||
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
|
||||
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK) & 1),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:snb */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(0xffff));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
POSTING_READ(ECOBUS);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 forcewake_ack;
|
||||
|
||||
if (IS_HASWELL(dev_priv->dev))
|
||||
forcewake_ack = FORCEWAKE_ACK_HSW;
|
||||
else
|
||||
forcewake_ack = FORCEWAKE_MT_ACK;
|
||||
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
POSTING_READ(ECOBUS);
|
||||
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:ivb,hsw */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generally this is called implicitly by the register read function. However,
|
||||
* if some sequence requires the GT to not power down then this function should
|
||||
* be called at the beginning of the sequence followed by a call to
|
||||
* gen6_gt_force_wake_put() at the end of the sequence.
|
||||
*/
|
||||
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->gt_lock, irqflags);
|
||||
if (dev_priv->forcewake_count++ == 0)
|
||||
dev_priv->gt.force_wake_get(dev_priv);
|
||||
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags);
|
||||
}
|
||||
|
||||
void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 gtfifodbg;
|
||||
gtfifodbg = I915_READ_NOTRACE(GTFIFODBG);
|
||||
if (WARN(gtfifodbg & GT_FIFO_CPU_ERROR_MASK,
|
||||
"MMIO read or write has been dropped %x\n", gtfifodbg))
|
||||
I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE, 0);
|
||||
/* something from same cacheline, but !FORCEWAKE */
|
||||
POSTING_READ(ECOBUS);
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
POSTING_READ(ECOBUS);
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* see gen6_gt_force_wake_get()
|
||||
*/
|
||||
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->gt_lock, irqflags);
|
||||
if (--dev_priv->forcewake_count == 0)
|
||||
dev_priv->gt.force_wake_put(dev_priv);
|
||||
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags);
|
||||
}
|
||||
|
||||
int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
|
||||
int loop = 500;
|
||||
u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
|
||||
while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
|
||||
udelay(10);
|
||||
fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
|
||||
}
|
||||
if (WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES))
|
||||
++ret;
|
||||
dev_priv->gt_fifo_count = fifo;
|
||||
}
|
||||
dev_priv->gt_fifo_count--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vlv_force_wake_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(0xffff));
|
||||
/* something from same cacheline, but !FORCEWAKE_VLV */
|
||||
POSTING_READ(FORCEWAKE_ACK_VLV);
|
||||
}
|
||||
|
||||
static void vlv_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV,
|
||||
_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for GT to ack forcewake request.\n");
|
||||
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_MEDIA_VLV) &
|
||||
FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for media to ack forcewake request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:vlv */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void vlv_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MEDIA_VLV,
|
||||
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
/* The below doubles as a POSTING_READ */
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
void intel_gt_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
vlv_force_wake_reset(dev_priv);
|
||||
} else if (INTEL_INFO(dev)->gen >= 6) {
|
||||
__gen6_gt_force_wake_reset(dev_priv);
|
||||
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
|
||||
__gen6_gt_force_wake_mt_reset(dev_priv);
|
||||
}
|
||||
}
|
||||
|
||||
void intel_gt_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
spin_lock_init(&dev_priv->gt_lock);
|
||||
|
||||
intel_gt_reset(dev);
|
||||
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
dev_priv->gt.force_wake_get = vlv_force_wake_get;
|
||||
dev_priv->gt.force_wake_put = vlv_force_wake_put;
|
||||
} else if (IS_HASWELL(dev)) {
|
||||
dev_priv->gt.force_wake_get = __gen6_gt_force_wake_mt_get;
|
||||
dev_priv->gt.force_wake_put = __gen6_gt_force_wake_mt_put;
|
||||
} else if (IS_IVYBRIDGE(dev)) {
|
||||
u32 ecobus;
|
||||
|
||||
/* IVB configs may use multi-threaded forcewake */
|
||||
|
||||
/* A small trick here - if the bios hasn't configured
|
||||
* MT forcewake, and if the device is in RC6, then
|
||||
* force_wake_mt_get will not wake the device and the
|
||||
* ECOBUS read will return zero. Which will be
|
||||
* (correctly) interpreted by the test below as MT
|
||||
* forcewake being disabled.
|
||||
*/
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
__gen6_gt_force_wake_mt_get(dev_priv);
|
||||
ecobus = I915_READ_NOTRACE(ECOBUS);
|
||||
__gen6_gt_force_wake_mt_put(dev_priv);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (ecobus & FORCEWAKE_MT_ENABLE) {
|
||||
dev_priv->gt.force_wake_get =
|
||||
__gen6_gt_force_wake_mt_get;
|
||||
dev_priv->gt.force_wake_put =
|
||||
__gen6_gt_force_wake_mt_put;
|
||||
} else {
|
||||
DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
|
||||
DRM_INFO("when using vblank-synced partial screen updates.\n");
|
||||
dev_priv->gt.force_wake_get = __gen6_gt_force_wake_get;
|
||||
dev_priv->gt.force_wake_put = __gen6_gt_force_wake_put;
|
||||
}
|
||||
} else if (IS_GEN6(dev)) {
|
||||
dev_priv->gt.force_wake_get = __gen6_gt_force_wake_get;
|
||||
dev_priv->gt.force_wake_put = __gen6_gt_force_wake_put;
|
||||
}
|
||||
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
|
||||
intel_gen6_powersave_work);
|
||||
}
|
||||
|
||||
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val)
|
||||
{
|
||||
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
|
||||
@@ -5634,3 +5389,11 @@ int vlv_freq_opcode(int ddr_freq, int val)
|
||||
return val;
|
||||
}
|
||||
|
||||
void intel_pm_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
|
||||
intel_gen6_powersave_work);
|
||||
}
|
||||
|
||||
|
||||
@@ -1696,6 +1696,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
|
||||
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
|
||||
enum drm_connector_status ret;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
|
||||
if (!intel_sdvo_get_value(intel_sdvo,
|
||||
SDVO_CMD_GET_ATTACHED_DISPLAYS,
|
||||
&response, 2))
|
||||
|
||||
@@ -1305,6 +1305,10 @@ intel_tv_detect(struct drm_connector *connector, bool force)
|
||||
struct intel_tv *intel_tv = intel_attached_tv(connector);
|
||||
int type;
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
|
||||
connector->base.id, drm_get_connector_name(connector),
|
||||
force);
|
||||
|
||||
mode = reported_modes[0];
|
||||
|
||||
if (force) {
|
||||
|
||||
595
drivers/gpu/drm/i915/intel_uncore.c
Normal file
595
drivers/gpu/drm/i915/intel_uncore.c
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "intel_drv.h"
|
||||
|
||||
#define FORCEWAKE_ACK_TIMEOUT_MS 2
|
||||
|
||||
#define __raw_i915_read8(dev_priv__, reg__) readb((dev_priv__)->regs + (reg__))
|
||||
#define __raw_i915_write8(dev_priv__, reg__, val__) writeb(val__, (dev_priv__)->regs + (reg__))
|
||||
|
||||
#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__))
|
||||
#define __raw_i915_write16(dev_priv__, reg__, val__) writew(val__, (dev_priv__)->regs + (reg__))
|
||||
|
||||
#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
|
||||
#define __raw_i915_write32(dev_priv__, reg__, val__) writel(val__, (dev_priv__)->regs + (reg__))
|
||||
|
||||
#define __raw_i915_read64(dev_priv__, reg__) readq((dev_priv__)->regs + (reg__))
|
||||
#define __raw_i915_write64(dev_priv__, reg__, val__) writeq(val__, (dev_priv__)->regs + (reg__))
|
||||
|
||||
#define __raw_posting_read(dev_priv__, reg__) (void)__raw_i915_read32(dev_priv__, reg__)
|
||||
|
||||
|
||||
static void __gen6_gt_wait_for_thread_c0(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 gt_thread_status_mask;
|
||||
|
||||
if (IS_HASWELL(dev_priv->dev))
|
||||
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK_HSW;
|
||||
else
|
||||
gt_thread_status_mask = GEN6_GT_THREAD_STATUS_CORE_MASK;
|
||||
|
||||
/* w/a for a sporadic read returning 0 by waiting for the GT
|
||||
* thread to wake up.
|
||||
*/
|
||||
if (wait_for_atomic_us((__raw_i915_read32(dev_priv, GEN6_GT_THREAD_STATUS_REG) & gt_thread_status_mask) == 0, 500))
|
||||
DRM_ERROR("GT thread status wait timed out\n");
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE, 0);
|
||||
/* something from same cacheline, but !FORCEWAKE */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, FORCEWAKE_ACK) & 1) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE, 1);
|
||||
/* something from same cacheline, but !FORCEWAKE */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, FORCEWAKE_ACK) & 1),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:snb */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_MT, _MASKED_BIT_DISABLE(0xffff));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 forcewake_ack;
|
||||
|
||||
if (IS_HASWELL(dev_priv->dev))
|
||||
forcewake_ack = FORCEWAKE_ACK_HSW;
|
||||
else
|
||||
forcewake_ack = FORCEWAKE_MT_ACK;
|
||||
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, forcewake_ack) & FORCEWAKE_KERNEL) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_MT,
|
||||
_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, forcewake_ack) & FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:ivb,hsw */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u32 gtfifodbg;
|
||||
|
||||
gtfifodbg = __raw_i915_read32(dev_priv, GTFIFODBG);
|
||||
if (WARN(gtfifodbg & GT_FIFO_CPU_ERROR_MASK,
|
||||
"MMIO read or write has been dropped %x\n", gtfifodbg))
|
||||
__raw_i915_write32(dev_priv, GTFIFODBG, GT_FIFO_CPU_ERROR_MASK);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE, 0);
|
||||
/* something from same cacheline, but !FORCEWAKE */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_MT,
|
||||
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
/* something from same cacheline, but !FORCEWAKE_MT */
|
||||
__raw_posting_read(dev_priv, ECOBUS);
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
static int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (dev_priv->uncore.fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
|
||||
int loop = 500;
|
||||
u32 fifo = __raw_i915_read32(dev_priv, GT_FIFO_FREE_ENTRIES);
|
||||
while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
|
||||
udelay(10);
|
||||
fifo = __raw_i915_read32(dev_priv, GT_FIFO_FREE_ENTRIES);
|
||||
}
|
||||
if (WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES))
|
||||
++ret;
|
||||
dev_priv->uncore.fifo_count = fifo;
|
||||
}
|
||||
dev_priv->uncore.fifo_count--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vlv_force_wake_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_VLV,
|
||||
_MASKED_BIT_DISABLE(0xffff));
|
||||
/* something from same cacheline, but !FORCEWAKE_VLV */
|
||||
__raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV);
|
||||
}
|
||||
|
||||
static void vlv_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_VLV,
|
||||
_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV,
|
||||
_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL));
|
||||
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, FORCEWAKE_ACK_VLV) & FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for GT to ack forcewake request.\n");
|
||||
|
||||
if (wait_for_atomic((__raw_i915_read32(dev_priv, FORCEWAKE_ACK_MEDIA_VLV) &
|
||||
FORCEWAKE_KERNEL),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for media to ack forcewake request.\n");
|
||||
|
||||
/* WaRsForcewakeWaitTC0:vlv */
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void vlv_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_VLV,
|
||||
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
__raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV,
|
||||
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
|
||||
/* The below doubles as a POSTING_READ */
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
void intel_uncore_early_sanitize(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev))
|
||||
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
|
||||
void intel_uncore_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
dev_priv->uncore.funcs.force_wake_get = vlv_force_wake_get;
|
||||
dev_priv->uncore.funcs.force_wake_put = vlv_force_wake_put;
|
||||
} else if (IS_HASWELL(dev)) {
|
||||
dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
|
||||
dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
|
||||
} else if (IS_IVYBRIDGE(dev)) {
|
||||
u32 ecobus;
|
||||
|
||||
/* IVB configs may use multi-threaded forcewake */
|
||||
|
||||
/* A small trick here - if the bios hasn't configured
|
||||
* MT forcewake, and if the device is in RC6, then
|
||||
* force_wake_mt_get will not wake the device and the
|
||||
* ECOBUS read will return zero. Which will be
|
||||
* (correctly) interpreted by the test below as MT
|
||||
* forcewake being disabled.
|
||||
*/
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
__gen6_gt_force_wake_mt_get(dev_priv);
|
||||
ecobus = __raw_i915_read32(dev_priv, ECOBUS);
|
||||
__gen6_gt_force_wake_mt_put(dev_priv);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (ecobus & FORCEWAKE_MT_ENABLE) {
|
||||
dev_priv->uncore.funcs.force_wake_get =
|
||||
__gen6_gt_force_wake_mt_get;
|
||||
dev_priv->uncore.funcs.force_wake_put =
|
||||
__gen6_gt_force_wake_mt_put;
|
||||
} else {
|
||||
DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
|
||||
DRM_INFO("when using vblank-synced partial screen updates.\n");
|
||||
dev_priv->uncore.funcs.force_wake_get =
|
||||
__gen6_gt_force_wake_get;
|
||||
dev_priv->uncore.funcs.force_wake_put =
|
||||
__gen6_gt_force_wake_put;
|
||||
}
|
||||
} else if (IS_GEN6(dev)) {
|
||||
dev_priv->uncore.funcs.force_wake_get =
|
||||
__gen6_gt_force_wake_get;
|
||||
dev_priv->uncore.funcs.force_wake_put =
|
||||
__gen6_gt_force_wake_put;
|
||||
}
|
||||
}
|
||||
|
||||
void intel_uncore_sanitize(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
vlv_force_wake_reset(dev_priv);
|
||||
} else if (INTEL_INFO(dev)->gen >= 6) {
|
||||
__gen6_gt_force_wake_reset(dev_priv);
|
||||
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
|
||||
__gen6_gt_force_wake_mt_reset(dev_priv);
|
||||
}
|
||||
|
||||
/* BIOS often leaves RC6 enabled, but disable it for hw init */
|
||||
intel_disable_gt_powersave(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generally this is called implicitly by the register read function. However,
|
||||
* if some sequence requires the GT to not power down then this function should
|
||||
* be called at the beginning of the sequence followed by a call to
|
||||
* gen6_gt_force_wake_put() at the end of the sequence.
|
||||
*/
|
||||
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
||||
if (dev_priv->uncore.forcewake_count++ == 0)
|
||||
dev_priv->uncore.funcs.force_wake_get(dev_priv);
|
||||
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
||||
}
|
||||
|
||||
/*
|
||||
* see gen6_gt_force_wake_get()
|
||||
*/
|
||||
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
||||
if (--dev_priv->uncore.forcewake_count == 0)
|
||||
dev_priv->uncore.funcs.force_wake_put(dev_priv);
|
||||
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
||||
}
|
||||
|
||||
/* We give fast paths for the really cool registers */
|
||||
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
|
||||
((HAS_FORCE_WAKE((dev_priv)->dev)) && \
|
||||
((reg) < 0x40000) && \
|
||||
((reg) != FORCEWAKE))
|
||||
|
||||
static void
|
||||
ilk_dummy_write(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
/* WaIssueDummyWriteToWakeupFromRC6:ilk Issue a dummy write to wake up
|
||||
* the chip from rc6 before touching it for real. MI_MODE is masked,
|
||||
* hence harmless to write 0 into. */
|
||||
__raw_i915_write32(dev_priv, MI_MODE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
|
||||
{
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
|
||||
(__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unknown unclaimed register before writing to %x\n",
|
||||
reg);
|
||||
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
|
||||
{
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
|
||||
(__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unclaimed write to %x\n", reg);
|
||||
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
}
|
||||
|
||||
#define __i915_read(x) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg, bool trace) { \
|
||||
unsigned long irqflags; \
|
||||
u##x val = 0; \
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); \
|
||||
if (dev_priv->info->gen == 5) \
|
||||
ilk_dummy_write(dev_priv); \
|
||||
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
|
||||
if (dev_priv->uncore.forcewake_count == 0) \
|
||||
dev_priv->uncore.funcs.force_wake_get(dev_priv); \
|
||||
val = __raw_i915_read##x(dev_priv, reg); \
|
||||
if (dev_priv->uncore.forcewake_count == 0) \
|
||||
dev_priv->uncore.funcs.force_wake_put(dev_priv); \
|
||||
} else { \
|
||||
val = __raw_i915_read##x(dev_priv, reg); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
|
||||
trace_i915_reg_rw(false, reg, val, sizeof(val), trace); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
__i915_read(8)
|
||||
__i915_read(16)
|
||||
__i915_read(32)
|
||||
__i915_read(64)
|
||||
#undef __i915_read
|
||||
|
||||
#define __i915_write(x) \
|
||||
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val, bool trace) { \
|
||||
unsigned long irqflags; \
|
||||
u32 __fifo_ret = 0; \
|
||||
trace_i915_reg_rw(true, reg, val, sizeof(val), trace); \
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); \
|
||||
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
|
||||
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
|
||||
} \
|
||||
if (dev_priv->info->gen == 5) \
|
||||
ilk_dummy_write(dev_priv); \
|
||||
hsw_unclaimed_reg_clear(dev_priv, reg); \
|
||||
__raw_i915_write##x(dev_priv, reg, val); \
|
||||
if (unlikely(__fifo_ret)) { \
|
||||
gen6_gt_check_fifodbg(dev_priv); \
|
||||
} \
|
||||
hsw_unclaimed_reg_check(dev_priv, reg); \
|
||||
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); \
|
||||
}
|
||||
__i915_write(8)
|
||||
__i915_write(16)
|
||||
__i915_write(32)
|
||||
__i915_write(64)
|
||||
#undef __i915_write
|
||||
|
||||
static const struct register_whitelist {
|
||||
uint64_t offset;
|
||||
uint32_t size;
|
||||
uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
|
||||
} whitelist[] = {
|
||||
{ RING_TIMESTAMP(RENDER_RING_BASE), 8, 0xF0 },
|
||||
};
|
||||
|
||||
int i915_reg_read_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_reg_read *reg = data;
|
||||
struct register_whitelist const *entry = whitelist;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) {
|
||||
if (entry->offset == reg->offset &&
|
||||
(1 << INTEL_INFO(dev)->gen & entry->gen_bitmask))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(whitelist))
|
||||
return -EINVAL;
|
||||
|
||||
switch (entry->size) {
|
||||
case 8:
|
||||
reg->val = I915_READ64(reg->offset);
|
||||
break;
|
||||
case 4:
|
||||
reg->val = I915_READ(reg->offset);
|
||||
break;
|
||||
case 2:
|
||||
reg->val = I915_READ16(reg->offset);
|
||||
break;
|
||||
case 1:
|
||||
reg->val = I915_READ8(reg->offset);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i8xx_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (IS_I85X(dev))
|
||||
return -ENODEV;
|
||||
|
||||
I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830);
|
||||
POSTING_READ(D_STATE);
|
||||
|
||||
if (IS_I830(dev) || IS_845G(dev)) {
|
||||
I915_WRITE(DEBUG_RESET_I830,
|
||||
DEBUG_RESET_DISPLAY |
|
||||
DEBUG_RESET_RENDER |
|
||||
DEBUG_RESET_FULL);
|
||||
POSTING_READ(DEBUG_RESET_I830);
|
||||
msleep(1);
|
||||
|
||||
I915_WRITE(DEBUG_RESET_I830, 0);
|
||||
POSTING_READ(DEBUG_RESET_I830);
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
|
||||
I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830);
|
||||
POSTING_READ(D_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i965_reset_complete(struct drm_device *dev)
|
||||
{
|
||||
u8 gdrst;
|
||||
pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst);
|
||||
return (gdrst & GRDOM_RESET_ENABLE) == 0;
|
||||
}
|
||||
|
||||
static int i965_do_reset(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Set the domains we want to reset (GRDOM/bits 2 and 3) as
|
||||
* well as the reset bit (GR/bit 0). Setting the GR bit
|
||||
* triggers the reset; when done, the hardware will clear it.
|
||||
*/
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST,
|
||||
GRDOM_RENDER | GRDOM_RESET_ENABLE);
|
||||
ret = wait_for(i965_reset_complete(dev), 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We can't reset render&media without also resetting display ... */
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST,
|
||||
GRDOM_MEDIA | GRDOM_RESET_ENABLE);
|
||||
|
||||
ret = wait_for(i965_reset_complete(dev), 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_write_config_byte(dev->pdev, I965_GDRST, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ironlake_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 gdrst;
|
||||
int ret;
|
||||
|
||||
gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
|
||||
gdrst &= ~GRDOM_MASK;
|
||||
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
|
||||
gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE);
|
||||
ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We can't reset render&media without also resetting display ... */
|
||||
gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
|
||||
gdrst &= ~GRDOM_MASK;
|
||||
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
|
||||
gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE);
|
||||
return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
|
||||
}
|
||||
|
||||
static int gen6_do_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
unsigned long irqflags;
|
||||
|
||||
/* Hold uncore.lock across reset to prevent any register access
|
||||
* with forcewake not set correctly
|
||||
*/
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
||||
|
||||
/* Reset the chip */
|
||||
|
||||
/* GEN6_GDRST is not in the gt power well, no need to check
|
||||
* for fifo space for the write or forcewake the chip for
|
||||
* the read
|
||||
*/
|
||||
__raw_i915_write32(dev_priv, GEN6_GDRST, GEN6_GRDOM_FULL);
|
||||
|
||||
/* Spin waiting for the device to ack the reset request */
|
||||
ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500);
|
||||
|
||||
/* If reset with a user forcewake, try to restore, otherwise turn it off */
|
||||
if (dev_priv->uncore.forcewake_count)
|
||||
dev_priv->uncore.funcs.force_wake_get(dev_priv);
|
||||
else
|
||||
dev_priv->uncore.funcs.force_wake_put(dev_priv);
|
||||
|
||||
/* Restore fifo count */
|
||||
dev_priv->uncore.fifo_count = __raw_i915_read32(dev_priv, GT_FIFO_FREE_ENTRIES);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int intel_gpu_reset(struct drm_device *dev)
|
||||
{
|
||||
switch (INTEL_INFO(dev)->gen) {
|
||||
case 7:
|
||||
case 6: return gen6_do_reset(dev);
|
||||
case 5: return ironlake_do_reset(dev);
|
||||
case 4: return i965_do_reset(dev);
|
||||
case 2: return i8xx_do_reset(dev);
|
||||
default: return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
void intel_uncore_clear_errors(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* XXX needs spinlock around caller's grouping */
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev))
|
||||
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
|
||||
void intel_uncore_check_errors(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (HAS_FPGA_DBG_UNCLAIMED(dev) &&
|
||||
(__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
|
||||
DRM_ERROR("Unclaimed register before interrupt\n");
|
||||
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ nvc0_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
return ret;
|
||||
|
||||
nv_subdev(priv)->unit = 0x00008000;
|
||||
nv_subdev(priv)->intr = nouveau_falcon_intr;
|
||||
nv_engine(priv)->cclass = &nvc0_bsp_cclass;
|
||||
nv_engine(priv)->sclass = nvc0_bsp_sclass;
|
||||
return 0;
|
||||
|
||||
@@ -90,6 +90,7 @@ nve0_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
return ret;
|
||||
|
||||
nv_subdev(priv)->unit = 0x00008000;
|
||||
nv_subdev(priv)->intr = nouveau_falcon_intr;
|
||||
nv_engine(priv)->cclass = &nve0_bsp_cclass;
|
||||
nv_engine(priv)->sclass = nve0_bsp_sclass;
|
||||
return 0;
|
||||
|
||||
@@ -23,6 +23,25 @@
|
||||
#include <engine/falcon.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
nouveau_falcon_intr(struct nouveau_subdev *subdev)
|
||||
{
|
||||
struct nouveau_falcon *falcon = (void *)subdev;
|
||||
u32 dispatch = nv_ro32(falcon, 0x01c);
|
||||
u32 intr = nv_ro32(falcon, 0x008) & dispatch & ~(dispatch >> 16);
|
||||
|
||||
if (intr & 0x00000010) {
|
||||
nv_debug(falcon, "ucode halted\n");
|
||||
nv_wo32(falcon, 0x004, 0x00000010);
|
||||
intr &= ~0x00000010;
|
||||
}
|
||||
|
||||
if (intr) {
|
||||
nv_error(falcon, "unhandled intr 0x%08x\n", intr);
|
||||
nv_wo32(falcon, 0x004, intr);
|
||||
}
|
||||
}
|
||||
|
||||
u32
|
||||
_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,7 @@ nvc0_ppp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
return ret;
|
||||
|
||||
nv_subdev(priv)->unit = 0x00000002;
|
||||
nv_subdev(priv)->intr = nouveau_falcon_intr;
|
||||
nv_engine(priv)->cclass = &nvc0_ppp_cclass;
|
||||
nv_engine(priv)->sclass = nvc0_ppp_sclass;
|
||||
return 0;
|
||||
|
||||
@@ -90,6 +90,7 @@ nvc0_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
return ret;
|
||||
|
||||
nv_subdev(priv)->unit = 0x00020000;
|
||||
nv_subdev(priv)->intr = nouveau_falcon_intr;
|
||||
nv_engine(priv)->cclass = &nvc0_vp_cclass;
|
||||
nv_engine(priv)->sclass = nvc0_vp_sclass;
|
||||
return 0;
|
||||
|
||||
@@ -90,6 +90,7 @@ nve0_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
return ret;
|
||||
|
||||
nv_subdev(priv)->unit = 0x00020000;
|
||||
nv_subdev(priv)->intr = nouveau_falcon_intr;
|
||||
nv_engine(priv)->cclass = &nve0_vp_cclass;
|
||||
nv_engine(priv)->sclass = nve0_vp_sclass;
|
||||
return 0;
|
||||
|
||||
@@ -72,6 +72,8 @@ int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *,
|
||||
struct nouveau_oclass *, u32, bool, const char *,
|
||||
const char *, int, void **);
|
||||
|
||||
void nouveau_falcon_intr(struct nouveau_subdev *subdev);
|
||||
|
||||
#define _nouveau_falcon_dtor _nouveau_engine_dtor
|
||||
int _nouveau_falcon_init(struct nouveau_object *);
|
||||
int _nouveau_falcon_fini(struct nouveau_object *, bool);
|
||||
|
||||
@@ -148,6 +148,7 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
|
||||
|
||||
if (unlikely(nvbo->gem))
|
||||
DRM_ERROR("bo %p still attached to GEM object\n", bo);
|
||||
WARN_ON(nvbo->pin_refcnt > 0);
|
||||
nv10_bo_put_tile_region(dev, nvbo->tile, NULL);
|
||||
kfree(nvbo);
|
||||
}
|
||||
@@ -197,6 +198,12 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
|
||||
size_t acc_size;
|
||||
int ret;
|
||||
int type = ttm_bo_type_device;
|
||||
int max_size = INT_MAX & ~((1 << drm->client.base.vm->vmm->lpg_shift) - 1);
|
||||
|
||||
if (size <= 0 || size > max_size) {
|
||||
nv_warn(drm, "skipped size %x\n", (u32)size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sg)
|
||||
type = ttm_bo_type_sg;
|
||||
@@ -340,13 +347,15 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
|
||||
struct ttm_buffer_object *bo = &nvbo->bo;
|
||||
int ret;
|
||||
int ret, ref;
|
||||
|
||||
ret = ttm_bo_reserve(bo, false, false, false, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (--nvbo->pin_refcnt)
|
||||
ref = --nvbo->pin_refcnt;
|
||||
WARN_ON_ONCE(ref < 0);
|
||||
if (ref)
|
||||
goto out;
|
||||
|
||||
nouveau_bo_placement_set(nvbo, bo->mem.placement, 0);
|
||||
@@ -578,7 +587,7 @@ nve0_bo_move_init(struct nouveau_channel *chan, u32 handle)
|
||||
int ret = RING_SPACE(chan, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NVC0(chan, NvSubCopy, 0x0000, 1);
|
||||
OUT_RING (chan, handle);
|
||||
OUT_RING (chan, handle & 0x0000ffff);
|
||||
FIRE_RING (chan);
|
||||
}
|
||||
return ret;
|
||||
@@ -973,7 +982,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chan->cli->mutex);
|
||||
mutex_lock_nested(&chan->cli->mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
/* create temporary vmas for the transfer and attach them to the
|
||||
* old nouveau_mem node, these will get cleaned up after ttm has
|
||||
@@ -1014,7 +1023,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm)
|
||||
struct ttm_mem_reg *, struct ttm_mem_reg *);
|
||||
int (*init)(struct nouveau_channel *, u32 handle);
|
||||
} _methods[] = {
|
||||
{ "COPY", 0, 0xa0b5, nve0_bo_move_copy, nve0_bo_move_init },
|
||||
{ "COPY", 4, 0xa0b5, nve0_bo_move_copy, nve0_bo_move_init },
|
||||
{ "GRCE", 0, 0xa0b5, nve0_bo_move_copy, nvc0_bo_move_init },
|
||||
{ "COPY1", 5, 0x90b8, nvc0_bo_move_copy, nvc0_bo_move_init },
|
||||
{ "COPY0", 4, 0x90b5, nvc0_bo_move_copy, nvc0_bo_move_init },
|
||||
@@ -1034,7 +1043,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm)
|
||||
struct nouveau_channel *chan;
|
||||
u32 handle = (mthd->engine << 16) | mthd->oclass;
|
||||
|
||||
if (mthd->init == nve0_bo_move_init)
|
||||
if (mthd->engine)
|
||||
chan = drm->cechan;
|
||||
else
|
||||
chan = drm->channel;
|
||||
|
||||
@@ -138,7 +138,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
|
||||
{
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
struct drm_gem_object *gem;
|
||||
int ret;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
|
||||
if (!gem)
|
||||
@@ -146,15 +146,19 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
|
||||
|
||||
nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
|
||||
if (!nouveau_fb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
goto err_unref;
|
||||
|
||||
ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem));
|
||||
if (ret) {
|
||||
drm_gem_object_unreference(gem);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return &nouveau_fb->base;
|
||||
|
||||
err:
|
||||
kfree(nouveau_fb);
|
||||
err_unref:
|
||||
drm_gem_object_unreference(gem);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
|
||||
@@ -524,9 +528,12 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct nouveau_page_flip_state *s;
|
||||
struct nouveau_channel *chan = NULL;
|
||||
struct nouveau_fence *fence;
|
||||
struct list_head res;
|
||||
struct ttm_validate_buffer res_val[2];
|
||||
struct ttm_validate_buffer resv[2] = {
|
||||
{ .bo = &old_bo->bo },
|
||||
{ .bo = &new_bo->bo },
|
||||
};
|
||||
struct ww_acquire_ctx ticket;
|
||||
LIST_HEAD(res);
|
||||
int ret;
|
||||
|
||||
if (!drm->channel)
|
||||
@@ -545,27 +552,19 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
chan = drm->channel;
|
||||
spin_unlock(&old_bo->bo.bdev->fence_lock);
|
||||
|
||||
mutex_lock(&chan->cli->mutex);
|
||||
|
||||
if (new_bo != old_bo) {
|
||||
ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);
|
||||
if (likely(!ret)) {
|
||||
res_val[0].bo = &old_bo->bo;
|
||||
res_val[1].bo = &new_bo->bo;
|
||||
INIT_LIST_HEAD(&res);
|
||||
list_add_tail(&res_val[0].head, &res);
|
||||
list_add_tail(&res_val[1].head, &res);
|
||||
ret = ttm_eu_reserve_buffers(&ticket, &res);
|
||||
if (ret)
|
||||
nouveau_bo_unpin(new_bo);
|
||||
}
|
||||
} else
|
||||
ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0);
|
||||
if (ret)
|
||||
goto fail_free;
|
||||
|
||||
if (ret) {
|
||||
mutex_unlock(&chan->cli->mutex);
|
||||
goto fail_free;
|
||||
list_add(&resv[1].head, &res);
|
||||
}
|
||||
list_add(&resv[0].head, &res);
|
||||
|
||||
mutex_lock(&chan->cli->mutex);
|
||||
ret = ttm_eu_reserve_buffers(&ticket, &res);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
|
||||
/* Initialize a page flip struct */
|
||||
*s = (struct nouveau_page_flip_state)
|
||||
@@ -576,10 +575,8 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
/* Emit a page flip */
|
||||
if (nv_device(drm->device)->card_type >= NV_50) {
|
||||
ret = nv50_display_flip_next(crtc, fb, chan, 0);
|
||||
if (ret) {
|
||||
mutex_unlock(&chan->cli->mutex);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
|
||||
@@ -590,22 +587,18 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
/* Update the crtc struct and cleanup */
|
||||
crtc->fb = fb;
|
||||
|
||||
if (old_bo != new_bo) {
|
||||
ttm_eu_fence_buffer_objects(&ticket, &res, fence);
|
||||
ttm_eu_fence_buffer_objects(&ticket, &res, fence);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(old_bo);
|
||||
} else {
|
||||
nouveau_bo_fence(new_bo, fence);
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
}
|
||||
nouveau_fence_unref(&fence);
|
||||
return 0;
|
||||
|
||||
fail_unreserve:
|
||||
if (old_bo != new_bo) {
|
||||
ttm_eu_backoff_reservation(&ticket, &res);
|
||||
ttm_eu_backoff_reservation(&ticket, &res);
|
||||
fail_unpin:
|
||||
mutex_unlock(&chan->cli->mutex);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(new_bo);
|
||||
} else
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
fail_free:
|
||||
kfree(s);
|
||||
return ret;
|
||||
|
||||
@@ -192,6 +192,18 @@ nouveau_accel_init(struct nouveau_drm *drm)
|
||||
|
||||
arg0 = NVE0_CHANNEL_IND_ENGINE_GR;
|
||||
arg1 = 1;
|
||||
} else
|
||||
if (device->chipset >= 0xa3 &&
|
||||
device->chipset != 0xaa &&
|
||||
device->chipset != 0xac) {
|
||||
ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE,
|
||||
NVDRM_CHAN + 1, NvDmaFB, NvDmaTT,
|
||||
&drm->cechan);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
|
||||
|
||||
arg0 = NvDmaFB;
|
||||
arg1 = NvDmaTT;
|
||||
} else {
|
||||
arg0 = NvDmaFB;
|
||||
arg1 = NvDmaTT;
|
||||
@@ -284,8 +296,6 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lock_class_key drm_client_lock_class_key;
|
||||
|
||||
static int
|
||||
nouveau_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
@@ -297,7 +307,6 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
lockdep_set_class(&drm->client.mutex, &drm_client_lock_class_key);
|
||||
|
||||
dev->dev_private = drm;
|
||||
drm->dev = dev;
|
||||
|
||||
@@ -385,6 +385,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (chan)
|
||||
nouveau_bo_vma_del(nvbo, &fbcon->nouveau_fb.vma);
|
||||
nouveau_bo_unmap(nvbo);
|
||||
out_unpin:
|
||||
nouveau_bo_unpin(nvbo);
|
||||
out_unref:
|
||||
|
||||
@@ -143,7 +143,7 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
|
||||
int ret;
|
||||
|
||||
fence->channel = chan;
|
||||
fence->timeout = jiffies + (3 * DRM_HZ);
|
||||
fence->timeout = jiffies + (15 * DRM_HZ);
|
||||
fence->sequence = ++fctx->sequence;
|
||||
|
||||
ret = fctx->emit(fence);
|
||||
|
||||
@@ -50,12 +50,6 @@ nouveau_gem_object_del(struct drm_gem_object *gem)
|
||||
return;
|
||||
nvbo->gem = NULL;
|
||||
|
||||
/* Lockdep hates you for doing reserve with gem object lock held */
|
||||
if (WARN_ON_ONCE(nvbo->pin_refcnt)) {
|
||||
nvbo->pin_refcnt = 1;
|
||||
nouveau_bo_unpin(nvbo);
|
||||
}
|
||||
|
||||
if (gem->import_attach)
|
||||
drm_prime_gem_destroy(gem, nvbo->bo.sg);
|
||||
|
||||
|
||||
@@ -355,6 +355,7 @@ struct nv50_oimm {
|
||||
|
||||
struct nv50_head {
|
||||
struct nouveau_crtc base;
|
||||
struct nouveau_bo *image;
|
||||
struct nv50_curs curs;
|
||||
struct nv50_sync sync;
|
||||
struct nv50_ovly ovly;
|
||||
@@ -517,9 +518,10 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
{
|
||||
struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct nv50_head *head = nv50_head(crtc);
|
||||
struct nv50_sync *sync = nv50_sync(crtc);
|
||||
int head = nv_crtc->index, ret;
|
||||
u32 *push;
|
||||
int ret;
|
||||
|
||||
swap_interval <<= 4;
|
||||
if (swap_interval == 0)
|
||||
@@ -537,7 +539,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
return ret;
|
||||
|
||||
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
|
||||
OUT_RING (chan, NvEvoSema0 + head);
|
||||
OUT_RING (chan, NvEvoSema0 + nv_crtc->index);
|
||||
OUT_RING (chan, sync->addr ^ 0x10);
|
||||
BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
|
||||
OUT_RING (chan, sync->data + 1);
|
||||
@@ -546,7 +548,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
OUT_RING (chan, sync->data);
|
||||
} else
|
||||
if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
|
||||
u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
|
||||
u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr;
|
||||
ret = RING_SPACE(chan, 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -565,7 +567,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL);
|
||||
} else
|
||||
if (chan) {
|
||||
u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
|
||||
u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr;
|
||||
ret = RING_SPACE(chan, 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -630,6 +632,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
evo_mthd(push, 0x0080, 1);
|
||||
evo_data(push, 0x00000000);
|
||||
evo_kick(push, sync);
|
||||
|
||||
nouveau_bo_ref(nv_fb->nvbo, &head->image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1038,18 +1042,17 @@ static int
|
||||
nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
|
||||
struct nv50_head *head = nv50_head(crtc);
|
||||
int ret;
|
||||
|
||||
ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (old_fb) {
|
||||
nvfb = nouveau_framebuffer(old_fb);
|
||||
nouveau_bo_unpin(nvfb->nvbo);
|
||||
if (ret == 0) {
|
||||
if (head->image)
|
||||
nouveau_bo_unpin(head->image);
|
||||
nouveau_bo_ref(nvfb->nvbo, &head->image);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1198,6 +1201,15 @@ nv50_crtc_lut_load(struct drm_crtc *crtc)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nv50_head *head = nv50_head(crtc);
|
||||
if (head->image)
|
||||
nouveau_bo_unpin(head->image);
|
||||
nouveau_bo_ref(NULL, &head->image);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
||||
uint32_t handle, uint32_t width, uint32_t height)
|
||||
@@ -1271,18 +1283,29 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct nv50_disp *disp = nv50_disp(crtc->dev);
|
||||
struct nv50_head *head = nv50_head(crtc);
|
||||
|
||||
nv50_dmac_destroy(disp->core, &head->ovly.base);
|
||||
nv50_pioc_destroy(disp->core, &head->oimm.base);
|
||||
nv50_dmac_destroy(disp->core, &head->sync.base);
|
||||
nv50_pioc_destroy(disp->core, &head->curs.base);
|
||||
|
||||
/*XXX: this shouldn't be necessary, but the core doesn't call
|
||||
* disconnect() during the cleanup paths
|
||||
*/
|
||||
if (head->image)
|
||||
nouveau_bo_unpin(head->image);
|
||||
nouveau_bo_ref(NULL, &head->image);
|
||||
|
||||
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
|
||||
if (nv_crtc->cursor.nvbo)
|
||||
nouveau_bo_unpin(nv_crtc->cursor.nvbo);
|
||||
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
||||
|
||||
nouveau_bo_unmap(nv_crtc->lut.nvbo);
|
||||
if (nv_crtc->lut.nvbo)
|
||||
nouveau_bo_unpin(nv_crtc->lut.nvbo);
|
||||
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
||||
|
||||
drm_crtc_cleanup(crtc);
|
||||
kfree(crtc);
|
||||
}
|
||||
@@ -1296,6 +1319,7 @@ static const struct drm_crtc_helper_funcs nv50_crtc_hfunc = {
|
||||
.mode_set_base = nv50_crtc_mode_set_base,
|
||||
.mode_set_base_atomic = nv50_crtc_mode_set_base_atomic,
|
||||
.load_lut = nv50_crtc_lut_load,
|
||||
.disable = nv50_crtc_disable,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs nv50_crtc_func = {
|
||||
|
||||
Reference in New Issue
Block a user