/*
*/
/*
* 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.
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* Copyright (c) 2009, 2013, Intel Corporation.
* 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,
* 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
* 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>
*
*/
/*
* I915 DRM Driver for Solaris
*
* This driver provides the hardware 3D acceleration support for Intel
* DRI (Direct Rendering Infrastructure). DRM (Direct Rendering Manager) here
* means the kernel device driver in DRI.
*
* I915 driver is a device dependent driver only, it depends on a misc module
* named drm for generic DRM operations.
*/
#include "drmP.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "drm_crtc_helper.h"
#include "intel_drv.h"
unsigned int i915_fbpercrtc = 0;
int i915_enable_rc6 = 0;
unsigned int i915_lvds_downclock = 0;
bool i915_try_reset = false;
bool i915_enable_hangcheck = true;
static void *i915_statep;
static int i915_quiesce(dev_info_t *);
extern struct cb_ops drm_cb_ops;
extern int intel_agp_enabled;
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
i915_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
i915_attach, /* devo_attach */
i915_detach, /* devo_detach */
nodev, /* devo_reset */
&drm_cb_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* power */
i915_quiesce, /* devo_quiesce */
};
&mod_driverops, /* drv_modops */
"I915 DRM driver", /* drv_linkinfo */
&i915_dev_ops, /* drv_dev_ops */
};
};
.vendor = 0x8086, \
.driver_data = (unsigned long) info }
};
};
.cursor_needs_physical = 1,
};
};
};
.cursor_needs_physical = 1,
.supports_tv = 1,
};
};
.supports_tv = 1,
};
.has_hotplug = 1,
.has_overlay = 1,
};
.has_overlay = 1,
.supports_tv = 1,
};
.has_overlay = 1,
};
.has_bsd_ring = 1,
};
.supports_tv = 1,
.has_bsd_ring = 1,
};
.has_overlay = 1,
};
.has_bsd_ring = 1,
};
.has_fbc = 1,
.has_bsd_ring = 1,
};
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
.has_force_wake = 1,
};
.has_fbc = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
.has_llc = 1,
.has_force_wake = 1,
};
#define GEN7_FEATURES \
.has_bsd_ring = 1, \
.has_blt_ring = 1, \
.has_llc = 1, \
.has_force_wake = 1
.is_ivybridge = 1,
};
.is_ivybridge = 1,
.is_mobile = 1,
.has_fbc = 1,
};
.is_ivybridge = 1,
.num_pipes = 0, /* legal, last one wins */
};
.is_mobile = 1,
.num_pipes = 2,
.is_valleyview = 1,
.has_llc = 0, /* legal, last one wins */
};
.num_pipes = 2,
.is_valleyview = 1,
.has_llc = 0, /* legal, last one wins */
};
.is_haswell = 1,
.has_ddi = 1,
.has_fpga_dbg = 1,
.has_vebox_ring = 1,
};
.is_haswell = 1,
.is_mobile = 1,
.has_ddi = 1,
.has_fpga_dbg = 1,
.has_fbc = 1,
.has_vebox_ring = 1,
};
{0, 0, 0}
};
{
/* LINTED */
int error;
/* In all current cases, num_pipes is equivalent to the PCH_NOP setting
* (which really amounts to a PCH but no South Display).
*/
return;
}
/*
* The reason to probe ISA bridge instead of Dev31:Fun0 is to
* make graphics device passthrough work easy for VMM, that only
* need to expose ISA bridge to let driver know the real hardware
* underneath. This is a requirement from virtualization team.
*/
if (isa_dip) {
"vendor-id", -1);
if (vendor_id == PCI_VENDOR_ID_INTEL) {
"device-id", -1);
if (device_id == INTEL_PCH_IBX_DEVICE_ID_TYPE) {
DRM_DEBUG_KMS("Found Ibex Peak PCH\n");
} else if (device_id == INTEL_PCH_CPT_DEVICE_ID_TYPE) {
DRM_DEBUG_KMS("Found CougarPoint PCH\n");
} else if (device_id == INTEL_PCH_PPT_DEVICE_ID_TYPE) {
/* PantherPoint is CPT compatible */
DRM_DEBUG_KMS("Found PatherPoint PCH\n");
} else if (device_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
DRM_DEBUG_KMS("Found LynxPoint PCH\n");
} else if (device_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
DRM_DEBUG_KMS("Found LynxPoint LP PCH\n");
}
}
}
}
{
return 0;
if (i915_semaphores >= 0)
return i915_semaphores;
#ifdef CONFIG_INTEL_IOMMU
/* Enable semaphores on SNB when IO remapping is off */
return false;
#endif
return 1;
}
{
/* ignore lid events during suspend */
intel_set_power_well(dev, true);
/* XXX FIXME: pci_save_state(dev->pdev); */
/* If KMS is active, we do the leavevt stuff here */
if (i915_gem_idle(dev, 0))
DRM_ERROR("GEM idle failed, resume may fail\n");
(void) drm_irq_uninstall(dev);
dev_priv->enable_hotplug_processing = false;
/*
* Disable CRTCs directly since we want to preserve sw state
* for _thaw.
*/
}
(void) i915_save_state(dev);
return 0;
}
int
{
int error;
/*
* First, try to restore the "console".
*/
(void) drm_fb_helper_force_kernel_mode();
DRM_ERROR("DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
if (error)
return error;
return 0;
}
static int
{
int error = 0;
(void) i915_restore_state(dev);
/* KMS EnterVT equivalent */
/* We need working interrupts for modeset enabling ... */
(void) drm_irq_install(dev);
intel_modeset_setup_hw_state(dev, true);
/*
* ... but also need to make sure that hotplug processing
* doesn't cause havoc. Like in the driver load code we don't
* bother with the tiny race here where we might loose hotplug
* notifications.
* */
dev_priv->enable_hotplug_processing = true;
}
return error;
}
static int
{
int error = 0;
}
return error;
}
int
{
int ret;
}
if (ret)
return ret;
return 0;
}
{
return -ENODEV;
msleep(1);
}
msleep(1);
return 0;
}
{
return (gdrst & GRDOM_RESET_ENABLE) == 0;
}
{
int ret;
/*
* triggers the reset; when done, the hardware will clear it.
*/
gdrst | GRDOM_RENDER |
if (ret)
return ret;
/* We can't reset render&media without also resetting display ... */
gdrst | GRDOM_MEDIA |
}
{
int ret;
gdrst &= ~GRDOM_MASK;
if (ret)
return ret;
/* We can't reset render&media without also resetting display ... */
gdrst &= ~GRDOM_MASK;
}
{
int ret;
unsigned long irqflags;
/* Hold gt_lock across reset to prevent any register access
* with forcewake not set correctly
*/
/* 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
*/
/* Spin waiting for the device to ack the reset request */
/* If reset with a user forcewake, try to restore, otherwise turn it off */
if (dev_priv->forcewake_count)
else
/* Restore fifo count */
return ret;
}
{
case 7:
default: return -ENODEV;
}
}
/**
* i915_reset - reset chip after a hang
* @dev: drm device to reset
*
* Reset the chip. Useful if a hang is detected. Returns zero on successful
* reset or otherwise an error code.
*
* Procedure is fairly simple:
* - reset the chip using the reset reg
* - re-init context state
* - re-init hardware status page
* - re-init ring buffer
* - re-init interrupt state
* - re-init display
*/
{
bool simulated;
int ret;
if (!i915_try_reset)
return 0;
DRM_ERROR("GPU hanging too fast, wait 5 secons for another reset");
return ret;
} else {
/* Also reset the gpu hangman. */
if (simulated) {
DRM_INFO("Simulated gpu hang, resetting stop_rings\n");
DRM_ERROR("Reset not implemented, but ignoring "
"error for simulated gpu hangs\n");
ret = 0;
}
} else {
}
}
if (ret) {
DRM_ERROR("Failed to reset chip.\n");
return ret;
}
/* Ok, now get things going again... */
/*
* Everything depends on having the GTT running, so we need to start
* there. Fortunately we don't need to do this unless we reset the
* chip at a PCI level.
*
* Next we need to restore the context, but we don't use those
* yet either...
*
* Ring buffer needs to be re-initialized in the KMS case, or if X
* was running at the time of the reset (i.e. we weren't VT
* switched away).
*/
int i;
if (ret)
}
/*
* It would make sense to re-init all the other hw state, at
* some unknown reason, this blows up my ilk, so don't.
*/
(void) drm_irq_uninstall(dev);
if (drm_irq_install(dev)) {
DRM_ERROR("Could not install irq for driver.\n");
return -EIO;
}
} else {
}
return 0;
}
/* don't use mtrr's here, the Xserver or user space app should
* deal with them for intel hardware.
*/
.load = i915_driver_load,
.open = i915_driver_open,
/* Used in place of i915_pm_ops for non-DRIVER_MODESET */
/* OSOL begin */
/* OSOL end */
#if defined(CONFIG_DEBUG_FS)
#endif
/*.gem_vm_ops = &i915_gem_vm_ops,*/
.ioctls = i915_ioctls,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
static int
{
switch (cmd) {
case DDI_ATTACH:
return (DDI_FAILURE);
}
if (!dev) {
DRM_ERROR("cannot get soft state");
return (DDI_FAILURE);
}
if (ret != DDI_SUCCESS)
return (ret);
case DDI_RESUME:
if (!dev) {
DRM_ERROR("cannot get soft state");
return (DDI_FAILURE);
}
return (i915_resume(dev));
}
DRM_ERROR("only supports attach or resume");
return (DDI_FAILURE);
}
static int
{
int item;
if (!dev) {
DRM_ERROR("cannot get soft state");
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (i915_suspend(dev));
}
DRM_ERROR("only supports detach or suspend");
return (DDI_FAILURE);
}
static int
/* LINTED */
{
if (!minor)
return (DDI_FAILURE);
return (DDI_FAILURE);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
int ret = 0;
if (!dev)
return (DDI_FAILURE);
(void) drm_fb_helper_force_kernel_mode();
if (ret)
/* Skip inactive CRTCs */
continue;
}
if (MDB_TRACK_ENABLE) {
}
}
}
}
return (DDI_SUCCESS);
}
{
return 0;
}
{
}
int
_init(void)
{
int ret;
sizeof (struct drm_device), DRM_MAX_INSTANCES);
if (ret)
return (ret);
if (ret) {
return (ret);
}
if (ret) {
i915_exit();
return (ret);
}
return (ret);
}
int
_fini(void)
{
int ret;
if (ret)
return (ret);
i915_exit();
return (ret);
}
int
{
}
/* We give fast paths for the really cool registers */
((reg) < 0x40000) && \
static void
{
/* WaIssueDummyWriteToWakeupFromRC6: 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. */
}
static void
{
DRM_INFO("Unknown unclaimed register before writing to %x\n",
reg);
}
}
static void
{
}
}
{
unsigned long irqflags;
if (dev_priv->forcewake_count == 0)
if (dev_priv->forcewake_count == 0)
} else
return val;
}
{
unsigned long irqflags;
if (dev_priv->forcewake_count == 0)
if (dev_priv->forcewake_count == 0)
} else
return val;
}
{
unsigned long irqflags;
if (dev_priv->forcewake_count == 0)
if (dev_priv->forcewake_count == 0)
} else
return val;
}
{
unsigned long irqflags;
if (dev_priv->forcewake_count == 0)
if (dev_priv->forcewake_count == 0)
} else
return val;
}
{
unsigned long irqflags;
if (__fifo_ret)
}
{
unsigned long irqflags;
if (__fifo_ret)
}
{
unsigned long irqflags;
if (__fifo_ret)
}
{
unsigned long irqflags;
if (__fifo_ret)
}
#define __i915_read(x) \
__i915_read(8)
__i915_read(16)
__i915_read(32)
__i915_read(64)
#define __i915_write(x) \
u ## x val);
__i915_write(8)
__i915_write(16)
__i915_write(32)
__i915_write(64)
static const struct register_whitelist {
} whitelist[] = {
};
{
int i;
break;
}
if (i == ARRAY_SIZE(whitelist))
return -EINVAL;
case 8:
break;
case 4:
break;
case 2:
break;
case 1:
break;
default:
WARN_ON(1);
return -EINVAL;
}
return 0;
}
{
}
{
/* Remove AGP support for GEN6+ platform */
}