freebsd-dev/sys/dev/drm2/i915/i915_drv.c
Konstantin Belousov ce95d2f922 Reset function on SandyBridge holds the gt_lock for the whole duration
already.  Also, according to the specs, GDRST register is not in the
power well, so the forcewake for reset status read is excessive for
this reason.

Use plain register read for waiting of the reset completion
notification, to avoid gt_lock recursion.  Linux upstream did the
similar change, but their code was already restructured.

Reported by:	ray
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
2013-10-21 16:22:51 +00:00

822 lines
21 KiB
C

/* i915_drv.c -- Intel i915 driver -*- linux-c -*-
* Created: Wed Feb 14 17:10:04 2001 by gareth@valinux.com
*/
/*-
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* 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
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/drm.h>
#include <dev/drm2/drm_mm.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/drm2/drm_pciids.h>
#include <dev/drm2/i915/intel_drv.h>
/* drv_PCI_IDs comes from drm_pciids.h, generated from drm_pciids.txt. */
static drm_pci_id_list_t i915_pciidlist[] = {
i915_PCI_IDS
};
static const struct intel_device_info intel_i830_info = {
.gen = 2, .is_mobile = 1, .cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_845g_info = {
.gen = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i85x_info = {
.gen = 2, .is_i85x = 1, .is_mobile = 1,
.cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i865g_info = {
.gen = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i915g_info = {
.gen = 3, .is_i915g = 1, .cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i915gm_info = {
.gen = 3, .is_mobile = 1,
.cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
.supports_tv = 1,
};
static const struct intel_device_info intel_i945g_info = {
.gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i945gm_info = {
.gen = 3, .is_i945gm = 1, .is_mobile = 1,
.has_hotplug = 1, .cursor_needs_physical = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
.supports_tv = 1,
};
static const struct intel_device_info intel_i965g_info = {
.gen = 4, .is_broadwater = 1,
.has_hotplug = 1,
.has_overlay = 1,
};
static const struct intel_device_info intel_i965gm_info = {
.gen = 4, .is_crestline = 1,
.is_mobile = 1, .has_fbc = 1, .has_hotplug = 1,
.has_overlay = 1,
.supports_tv = 1,
};
static const struct intel_device_info intel_g33_info = {
.gen = 3, .is_g33 = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_overlay = 1,
};
static const struct intel_device_info intel_g45_info = {
.gen = 4, .is_g4x = 1, .need_gfx_hws = 1,
.has_pipe_cxsr = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
};
static const struct intel_device_info intel_gm45_info = {
.gen = 4, .is_g4x = 1,
.is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1,
.has_pipe_cxsr = 1, .has_hotplug = 1,
.supports_tv = 1,
.has_bsd_ring = 1,
};
static const struct intel_device_info intel_pineview_info = {
.gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_overlay = 1,
};
static const struct intel_device_info intel_ironlake_d_info = {
.gen = 5,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
};
static const struct intel_device_info intel_ironlake_m_info = {
.gen = 5, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_fbc = 0, /* disabled due to buggy hardware */
.has_bsd_ring = 1,
};
static const struct intel_device_info intel_sandybridge_d_info = {
.gen = 6,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
};
static const struct intel_device_info intel_sandybridge_m_info = {
.gen = 6, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_fbc = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
};
static const struct intel_device_info intel_ivybridge_d_info = {
.is_ivybridge = 1, .gen = 7,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
};
static const struct intel_device_info intel_ivybridge_m_info = {
.is_ivybridge = 1, .gen = 7, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
};
#define INTEL_VGA_DEVICE(id, info_) { \
.device = id, \
.info = info_, \
}
static const struct intel_gfx_device_id {
int device;
const struct intel_device_info *info;
} pciidlist[] = { /* aka */
INTEL_VGA_DEVICE(0x3577, &intel_i830_info),
INTEL_VGA_DEVICE(0x2562, &intel_845g_info),
INTEL_VGA_DEVICE(0x3582, &intel_i85x_info),
INTEL_VGA_DEVICE(0x358e, &intel_i85x_info),
INTEL_VGA_DEVICE(0x2572, &intel_i865g_info),
INTEL_VGA_DEVICE(0x2582, &intel_i915g_info),
INTEL_VGA_DEVICE(0x258a, &intel_i915g_info),
INTEL_VGA_DEVICE(0x2592, &intel_i915gm_info),
INTEL_VGA_DEVICE(0x2772, &intel_i945g_info),
INTEL_VGA_DEVICE(0x27a2, &intel_i945gm_info),
INTEL_VGA_DEVICE(0x27ae, &intel_i945gm_info),
INTEL_VGA_DEVICE(0x2972, &intel_i965g_info),
INTEL_VGA_DEVICE(0x2982, &intel_i965g_info),
INTEL_VGA_DEVICE(0x2992, &intel_i965g_info),
INTEL_VGA_DEVICE(0x29a2, &intel_i965g_info),
INTEL_VGA_DEVICE(0x29b2, &intel_g33_info),
INTEL_VGA_DEVICE(0x29c2, &intel_g33_info),
INTEL_VGA_DEVICE(0x29d2, &intel_g33_info),
INTEL_VGA_DEVICE(0x2a02, &intel_i965gm_info),
INTEL_VGA_DEVICE(0x2a12, &intel_i965gm_info),
INTEL_VGA_DEVICE(0x2a42, &intel_gm45_info),
INTEL_VGA_DEVICE(0x2e02, &intel_g45_info),
INTEL_VGA_DEVICE(0x2e12, &intel_g45_info),
INTEL_VGA_DEVICE(0x2e22, &intel_g45_info),
INTEL_VGA_DEVICE(0x2e32, &intel_g45_info),
INTEL_VGA_DEVICE(0x2e42, &intel_g45_info),
INTEL_VGA_DEVICE(0x2e92, &intel_g45_info),
INTEL_VGA_DEVICE(0xa001, &intel_pineview_info),
INTEL_VGA_DEVICE(0xa011, &intel_pineview_info),
INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info),
INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info),
INTEL_VGA_DEVICE(0x0102, &intel_sandybridge_d_info),
INTEL_VGA_DEVICE(0x0112, &intel_sandybridge_d_info),
INTEL_VGA_DEVICE(0x0122, &intel_sandybridge_d_info),
INTEL_VGA_DEVICE(0x0106, &intel_sandybridge_m_info),
INTEL_VGA_DEVICE(0x0116, &intel_sandybridge_m_info),
INTEL_VGA_DEVICE(0x0126, &intel_sandybridge_m_info),
INTEL_VGA_DEVICE(0x010A, &intel_sandybridge_d_info),
INTEL_VGA_DEVICE(0x0156, &intel_ivybridge_m_info), /* GT1 mobile */
INTEL_VGA_DEVICE(0x0166, &intel_ivybridge_m_info), /* GT2 mobile */
INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */
INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */
INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */
INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */
{0, 0}
};
static int i915_drm_freeze(struct drm_device *dev)
{
struct drm_i915_private *dev_priv;
int error;
dev_priv = dev->dev_private;
drm_kms_helper_poll_disable(dev);
#if 0
pci_save_state(dev->pdev);
#endif
DRM_LOCK(dev);
/* If KMS is active, we do the leavevt stuff here */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
error = -i915_gem_idle(dev);
if (error) {
DRM_UNLOCK(dev);
device_printf(dev->device,
"GEM idle failed, resume might fail\n");
return (error);
}
drm_irq_uninstall(dev);
}
i915_save_state(dev);
intel_opregion_fini(dev);
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
DRM_UNLOCK(dev);
return 0;
}
static int
i915_suspend(device_t kdev)
{
struct drm_device *dev;
int error;
dev = device_get_softc(kdev);
if (dev == NULL || dev->dev_private == NULL) {
DRM_ERROR("DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
DRM_DEBUG_KMS("starting suspend\n");
error = i915_drm_freeze(dev);
if (error)
return (error);
error = bus_generic_suspend(kdev);
DRM_DEBUG_KMS("finished suspend %d\n", error);
return (error);
}
static int i915_drm_thaw(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int error = 0;
DRM_LOCK(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
i915_gem_restore_gtt_mappings(dev);
}
i915_restore_state(dev);
intel_opregion_setup(dev);
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
dev_priv->mm.suspended = 0;
error = i915_gem_init_hw(dev);
if (HAS_PCH_SPLIT(dev))
ironlake_init_pch_refclk(dev);
DRM_UNLOCK(dev);
sx_xlock(&dev->mode_config.mutex);
drm_mode_config_reset(dev);
sx_xunlock(&dev->mode_config.mutex);
drm_irq_install(dev);
sx_xlock(&dev->mode_config.mutex);
/* Resume the modeset for every activated CRTC */
drm_helper_resume_force_mode(dev);
sx_xunlock(&dev->mode_config.mutex);
if (IS_IRONLAKE_M(dev))
ironlake_enable_rc6(dev);
DRM_LOCK(dev);
}
intel_opregion_init(dev);
dev_priv->modeset_on_lid = 0;
DRM_UNLOCK(dev);
return error;
}
static int
i915_resume(device_t kdev)
{
struct drm_device *dev;
int ret;
dev = device_get_softc(kdev);
DRM_DEBUG_KMS("starting resume\n");
#if 0
if (pci_enable_device(dev->pdev))
return -EIO;
pci_set_master(dev->pdev);
#endif
ret = -i915_drm_thaw(dev);
if (ret != 0)
return (ret);
drm_kms_helper_poll_enable(dev);
ret = bus_generic_resume(kdev);
DRM_DEBUG_KMS("finished resume %d\n", ret);
return (ret);
}
static int
i915_probe(device_t kdev)
{
return drm_probe(kdev, i915_pciidlist);
}
int i915_modeset;
static int
i915_attach(device_t kdev)
{
struct drm_device *dev;
dev = device_get_softc(kdev);
if (i915_modeset == 1)
i915_driver_info.driver_features |= DRIVER_MODESET;
dev->driver = &i915_driver_info;
return (drm_attach(kdev, i915_pciidlist));
}
const struct intel_device_info *
i915_get_device_id(int device)
{
const struct intel_gfx_device_id *did;
for (did = &pciidlist[0]; did->device != 0; did++) {
if (did->device != device)
continue;
return (did->info);
}
return (NULL);
}
static device_method_t i915_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, i915_probe),
DEVMETHOD(device_attach, i915_attach),
DEVMETHOD(device_suspend, i915_suspend),
DEVMETHOD(device_resume, i915_resume),
DEVMETHOD(device_detach, drm_detach),
DEVMETHOD_END
};
static driver_t i915_driver = {
"drmn",
i915_methods,
sizeof(struct drm_device)
};
extern devclass_t drm_devclass;
DRIVER_MODULE_ORDERED(i915kms, vgapci, i915_driver, drm_devclass, 0, 0,
SI_ORDER_ANY);
MODULE_DEPEND(i915kms, drmn, 1, 1, 1);
MODULE_DEPEND(i915kms, agp, 1, 1, 1);
MODULE_DEPEND(i915kms, iicbus, 1, 1, 1);
MODULE_DEPEND(i915kms, iic, 1, 1, 1);
MODULE_DEPEND(i915kms, iicbb, 1, 1, 1);
int intel_iommu_enabled = 0;
TUNABLE_INT("drm.i915.intel_iommu_enabled", &intel_iommu_enabled);
int i915_semaphores = -1;
TUNABLE_INT("drm.i915.semaphores", &i915_semaphores);
static int i915_try_reset = 1;
TUNABLE_INT("drm.i915.try_reset", &i915_try_reset);
unsigned int i915_lvds_downclock = 0;
TUNABLE_INT("drm.i915.lvds_downclock", &i915_lvds_downclock);
int i915_vbt_sdvo_panel_type = -1;
TUNABLE_INT("drm.i915.vbt_sdvo_panel_type", &i915_vbt_sdvo_panel_type);
unsigned int i915_powersave = 1;
TUNABLE_INT("drm.i915.powersave", &i915_powersave);
int i915_enable_fbc = 0;
TUNABLE_INT("drm.i915.enable_fbc", &i915_enable_fbc);
int i915_enable_rc6 = 0;
TUNABLE_INT("drm.i915.enable_rc6", &i915_enable_rc6);
int i915_panel_use_ssc = -1;
TUNABLE_INT("drm.i915.panel_use_ssc", &i915_panel_use_ssc);
int i915_panel_ignore_lid = 0;
TUNABLE_INT("drm.i915.panel_ignore_lid", &i915_panel_ignore_lid);
int i915_modeset = 1;
TUNABLE_INT("drm.i915.modeset", &i915_modeset);
int i915_enable_ppgtt = -1;
TUNABLE_INT("drm.i915.enable_ppgtt", &i915_enable_ppgtt);
int i915_enable_hangcheck = 1;
TUNABLE_INT("drm.i915.enable_hangcheck", &i915_enable_hangcheck);
#define PCI_VENDOR_INTEL 0x8086
#define INTEL_PCH_DEVICE_ID_MASK 0xff00
#define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00
#define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
#define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00
void
intel_detect_pch(struct drm_device *dev)
{
struct drm_i915_private *dev_priv;
device_t pch;
uint32_t id;
dev_priv = dev->dev_private;
pch = pci_find_class(PCIC_BRIDGE, PCIS_BRIDGE_ISA);
if (pch != NULL && pci_get_vendor(pch) == PCI_VENDOR_INTEL) {
id = pci_get_device(pch) & INTEL_PCH_DEVICE_ID_MASK;
if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) {
dev_priv->pch_type = PCH_IBX;
DRM_DEBUG_KMS("Found Ibex Peak PCH\n");
} else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) {
dev_priv->pch_type = PCH_CPT;
DRM_DEBUG_KMS("Found CougarPoint PCH\n");
} else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) {
/* PantherPoint is CPT compatible */
dev_priv->pch_type = PCH_CPT;
DRM_DEBUG_KMS("Found PatherPoint PCH\n");
} else
DRM_DEBUG_KMS("No PCH detected\n");
} else
DRM_DEBUG_KMS("No Intel PCI-ISA bridge found\n");
}
void
__gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
{
int count;
count = 0;
while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1))
DELAY(10);
I915_WRITE_NOTRACE(FORCEWAKE, 1);
POSTING_READ(FORCEWAKE);
count = 0;
while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0)
DELAY(10);
}
void
__gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
{
int count;
count = 0;
while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1))
DELAY(10);
I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 1);
POSTING_READ(FORCEWAKE_MT);
count = 0;
while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1) == 0)
DELAY(10);
}
void
gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
{
mtx_lock(&dev_priv->gt_lock);
if (dev_priv->forcewake_count++ == 0)
dev_priv->display.force_wake_get(dev_priv);
mtx_unlock(&dev_priv->gt_lock);
}
static void
gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
{
u32 gtfifodbg;
gtfifodbg = I915_READ_NOTRACE(GTFIFODBG);
if ((gtfifodbg & GT_FIFO_CPU_ERROR_MASK) != 0) {
printf("MMIO read or write has been dropped %x\n", gtfifodbg);
I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK);
}
}
void
__gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE, 0);
/* The below doubles as a POSTING_READ */
gen6_gt_check_fifodbg(dev_priv);
}
void
__gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 0);
/* The below doubles as a POSTING_READ */
gen6_gt_check_fifodbg(dev_priv);
}
void
gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
mtx_lock(&dev_priv->gt_lock);
if (--dev_priv->forcewake_count == 0)
dev_priv->display.force_wake_put(dev_priv);
mtx_unlock(&dev_priv->gt_lock);
}
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--) {
DELAY(10);
fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
}
if (loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES) {
printf("%s loop\n", __func__);
++ret;
}
dev_priv->gt_fifo_count = fifo;
}
dev_priv->gt_fifo_count--;
return (ret);
}
static int
i8xx_do_reset(struct drm_device *dev, u8 flags)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int onems;
if (IS_I85X(dev))
return -ENODEV;
onems = hz / 1000;
if (onems == 0)
onems = 1;
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);
pause("i8xxrst1", onems);
I915_WRITE(DEBUG_RESET_I830, 0);
POSTING_READ(DEBUG_RESET_I830);
}
pause("i8xxrst2", onems);
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;
gdrst = pci_read_config(dev->device, I965_GDRST, 1);
return (gdrst & 0x1);
}
static int
i965_do_reset(struct drm_device *dev, u8 flags)
{
u8 gdrst;
/*
* 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.
*/
gdrst = pci_read_config(dev->device, I965_GDRST, 1);
pci_write_config(dev->device, I965_GDRST, gdrst | flags | 0x1, 1);
return (_intel_wait_for(dev, i965_reset_complete(dev), 500, 1,
"915rst"));
}
static int
ironlake_do_reset(struct drm_device *dev, u8 flags)
{
struct drm_i915_private *dev_priv;
u32 gdrst;
dev_priv = dev->dev_private;
gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | flags | 0x1);
return (_intel_wait_for(dev,
(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1) != 0,
500, 1, "915rst"));
}
static int
gen6_do_reset(struct drm_device *dev, u8 flags)
{
struct drm_i915_private *dev_priv;
int ret;
dev_priv = dev->dev_private;
/* Hold gt_lock across reset to prevent any register access
* with forcewake not set correctly
*/
mtx_lock(&dev_priv->gt_lock);
/* 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 = _intel_wait_for(dev,
(I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0,
500, 1, "915rst");
/* If reset with a user forcewake, try to restore, otherwise turn it off */
if (dev_priv->forcewake_count)
dev_priv->display.force_wake_get(dev_priv);
else
dev_priv->display.force_wake_put(dev_priv);
/* Restore fifo count */
dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
mtx_unlock(&dev_priv->gt_lock);
return (ret);
}
int
i915_reset(struct drm_device *dev, u8 flags)
{
drm_i915_private_t *dev_priv = dev->dev_private;
/*
* We really should only reset the display subsystem if we actually
* need to
*/
bool need_display = true;
int ret;
if (!i915_try_reset)
return (0);
if (!sx_try_xlock(&dev->dev_struct_lock))
return (-EBUSY);
i915_gem_reset(dev);
ret = -ENODEV;
if (time_second - dev_priv->last_gpu_reset < 5) {
DRM_ERROR("GPU hanging too fast, declaring wedged!\n");
} else {
switch (INTEL_INFO(dev)->gen) {
case 7:
case 6:
ret = gen6_do_reset(dev, flags);
break;
case 5:
ret = ironlake_do_reset(dev, flags);
break;
case 4:
ret = i965_do_reset(dev, flags);
break;
case 2:
ret = i8xx_do_reset(dev, flags);
break;
}
}
dev_priv->last_gpu_reset = time_second;
if (ret) {
DRM_ERROR("Failed to reset chip.\n");
DRM_UNLOCK(dev);
return (ret);
}
if (drm_core_check_feature(dev, DRIVER_MODESET) ||
!dev_priv->mm.suspended) {
dev_priv->mm.suspended = 0;
i915_gem_init_swizzling(dev);
dev_priv->rings[RCS].init(&dev_priv->rings[RCS]);
if (HAS_BSD(dev))
dev_priv->rings[VCS].init(&dev_priv->rings[VCS]);
if (HAS_BLT(dev))
dev_priv->rings[BCS].init(&dev_priv->rings[BCS]);
i915_gem_init_ppgtt(dev);
drm_irq_uninstall(dev);
drm_mode_config_reset(dev);
DRM_UNLOCK(dev);
drm_irq_install(dev);
DRM_LOCK(dev);
}
DRM_UNLOCK(dev);
if (need_display) {
sx_xlock(&dev->mode_config.mutex);
drm_helper_resume_force_mode(dev);
sx_xunlock(&dev->mode_config.mutex);
}
return (0);
}
#define __i915_read(x, y) \
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
u##x val = 0; \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
mtx_lock(&dev_priv->gt_lock); \
if (dev_priv->forcewake_count == 0) \
dev_priv->display.force_wake_get(dev_priv); \
val = DRM_READ##y(dev_priv->mmio_map, reg); \
if (dev_priv->forcewake_count == 0) \
dev_priv->display.force_wake_put(dev_priv); \
mtx_unlock(&dev_priv->gt_lock); \
} else { \
val = DRM_READ##y(dev_priv->mmio_map, reg); \
} \
trace_i915_reg_rw(false, reg, val, sizeof(val)); \
return val; \
}
__i915_read(8, 8)
__i915_read(16, 16)
__i915_read(32, 32)
__i915_read(64, 64)
#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); \
} \
DRM_WRITE##y(dev_priv->mmio_map, reg, val); \
if (__predict_false(__fifo_ret)) { \
gen6_gt_check_fifodbg(dev_priv); \
} \
}
__i915_write(8, 8)
__i915_write(16, 16)
__i915_write(32, 32)
__i915_write(64, 64)
#undef __i915_write