/* BEGIN CSTYLED */
/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
*/
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* Copyright (c) 2009, 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,
* 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 NON-INFRINGEMENT.
* 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.
*
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
/**
* Interrupts that are always left unmasked.
*
* Since pipe events are edge-triggered from the PIPESTAT register to IIR,
* we leave them always unmasked in IMR and then control enabling them through
* PIPESTAT alone.
*/
/** Interrupts that we mask and unmask at runtime. */
/** These are all of the interrupts used by the driver */
void
{
}
}
static inline void
{
}
}
/* For display hotplug interrupt */
void
{
}
}
#if 0
static inline void
{
}
}
#endif
static inline void
{
}
}
static inline void
{
}
}
static inline uint32_t
{
if (pipe == 0)
return PIPEASTAT;
if (pipe == 1)
return PIPEBSTAT;
return 0;
}
void
{
/* Enable the interrupt, clear any pending status */
}
}
void
{
}
}
/**
* i915_pipe_enabled - check if a pipe is enabled
* @dev: DRM device
* @pipe: pipe to check
*
* Reading certain registers when the pipe is disabled can hang the chip.
* Use this routine to make sure the PLL is running and the pipe is active
* before reading such registers if unsure.
*/
static int
{
return 1;
return 0;
}
{
unsigned long high_frame;
unsigned long low_frame;
return 0;
}
/*
* High & low register fields aren't synchronized, so make sure
* we get a low value that's stable across two reads of the high
* register.
*/
do {
return count;
}
/**
* i915_capture_error_state - capture an error record for later analysis
* @dev: drm device
*
* Should be called when an error is detected (either a hang or an error
* interrupt) to capture error state from the time of the error. Fills
* out a structure which becomes available in debugfs for user level tools
* to pick up.
*/
{
#if 0
if (dev_priv->first_error)
goto out;
#endif
if (!error) {
DRM_DEBUG("out ot memory, not capturing error state\n");
goto out;
}
} else {
}
}
out:
}
/**
* i915_handle_error - handle an error interrupt
* @dev: drm device
*
* Do some basic checking of regsiter state at error interrupt time and
* dump it to the syslog. Also call i915_capture_error_state() to make
* sure we get a record and make it available in debugfs. Fire a uevent
* so userspace knows something bad happened (should trigger collection
* of a ring dump etc.).
*/
{
DRM_DEBUG("render error detected, EIR: 0x%08x\n",
eir);
DRM_DEBUG(" IPEIR: 0x%08x\n",
DRM_DEBUG(" IPEHR: 0x%08x\n",
DRM_DEBUG(" INSTDONE: 0x%08x\n",
DRM_DEBUG(" INSTPS: 0x%08x\n",
DRM_DEBUG(" INSTDONE1: 0x%08x\n",
DRM_DEBUG(" ACTHD: 0x%08x\n",
(void)I915_READ(IPEIR_I965);
}
if (eir & GM45_ERROR_PAGE_TABLE) {
DRM_DEBUG("page table error\n");
DRM_DEBUG(" PGTBL_ER: 0x%08x\n",
}
}
if (eir & I915_ERROR_PAGE_TABLE) {
DRM_DEBUG("page table error\n");
DRM_DEBUG("PGTBL_ER: 0x%08x\n",
}
}
if (eir & I915_ERROR_MEMORY_REFRESH) {
DRM_DEBUG("memory refresh error\n");
DRM_DEBUG("PIPEASTAT: 0x%08x\n",
DRM_DEBUG("PIPEBSTAT: 0x%08x\n",
/* pipestat has already been acked */
}
if (eir & I915_ERROR_INSTRUCTION) {
DRM_DEBUG("instruction error\n");
DRM_DEBUG(" INSTPM: 0x%08x\n",
DRM_DEBUG(" IPEIR: 0x%08x\n",
DRM_DEBUG(" IPEHR: 0x%08x\n",
DRM_DEBUG(" INSTDONE: 0x%08x\n",
DRM_DEBUG(" ACTHD: 0x%08x\n",
} else {
DRM_DEBUG(" IPEIR: 0x%08x\n",
DRM_DEBUG(" IPEHR: 0x%08x\n",
DRM_DEBUG(" INSTDONE: 0x%08x\n",
DRM_DEBUG(" INSTPS: 0x%08x\n",
DRM_DEBUG(" INSTDONE1: 0x%08x\n",
DRM_DEBUG(" ACTHD: 0x%08x\n",
(void)I915_READ(IPEIR_I965);
}
}
if (eir) {
/*
* some errors might have become stuck,
* mask them.
*/
}
}
{
return 0;
}
}
{
int vblank = 0;
/* disable master interrupt before clearing iir */
for (;;) {
break;
ret = IRQ_HANDLED;
if (dev_priv->sarea_priv) {
}
if (gt_iir & GT_USER_INTERRUPT) {
}
if (de_iir & DE_PIPEA_VBLANK) {
vblank++;
drm_handle_vblank(dev, 0);
}
if (de_iir & DE_PIPEB_VBLANK) {
vblank++;
}
de_iir = new_de_iir;
gt_iir = new_gt_iir;
}
return ret;
}
{
int vblank = 0;
return igdng_irq_handler(dev);
if (iir == 0) {
return IRQ_NONE;
}
if (dev_priv->sarea_priv) {
if (dev_priv->hw_status_page)
}
if (iir & I915_USER_INTERRUPT) {
}
if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
/* The vblank interrupt gets enabled even if we didn't ask for
it, so make sure it's shut down again */
else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
{
vblank++;
drm_handle_vblank(dev, 0);
}
}
if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
/* The vblank interrupt gets enabled even if we didn't ask for
it, so make sure it's shut down again */
else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
{
vblank++;
}
}
return IRQ_HANDLED;
}
{
if (dev_priv->sarea_priv)
#if defined(__i386)
BEGIN_LP_RING(3);
(void) READ_BREADCRUMB(dev_priv);
BEGIN_LP_RING(2);
OUT_RING(0);
} else {
#endif /* __i386 */
BEGIN_LP_RING(4);
#if defined(__i386)
}
#endif /* __i386 */
#if defined(__i386)
#else
#endif /* __i386 */
{
(void) READ_BREADCRUMB(dev_priv);
BEGIN_LP_RING(2);
OUT_RING(0);
OUT_RING(0);
(void) READ_BREADCRUMB(dev_priv);
}
}
{
else
}
}
{
else
}
}
{
int ret = 0;
int wait_time = 0;
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
wait_time++;
if (dev_priv->sarea_priv) {
}
return 0;
}
if (wait_time > 5) {
DRM_DEBUG("%d: EBUSY -- rec: %d emitted: %d\n",
ret,
return ret;
}
goto waitmore;
}
if (dev_priv->sarea_priv)
if (wait_time > 5) {
return ret;
}
goto waitmore;
}
return ret;
}
/* Needs the lock as it touches the ring.
*/
/*ARGSUSED*/
{
int result;
if (!dev_priv) {
return (EINVAL);
}
sizeof (drm_i915_irq_emit32_t));
} else
DRM_ERROR("copy_to_user\n");
return (EFAULT);
}
return 0;
}
/* Doesn't need the hardware lock.
*/
/*ARGSUSED*/
{
if (!dev_priv) {
return (EINVAL);
}
}
{
if (pipe == 0)
else
}
}
{
if (pipe == 0)
else
}
}
{
if (!(pipeconf & PIPEACONF_ENABLE))
return -EINVAL;
else
return 0;
}
{
else
}
/* Set the vblank monitor pipe
*/
/*ARGSUSED*/
{
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return (-EINVAL);
}
return (0);
}
/*ARGSUSED*/
{
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
return 0;
}
/**
* Schedule buffer swap at given vertical blank.
*/
/*ARGSUSED*/
{
/* The delayed swap mechanism was fundamentally racy, and has been
* from the kernel, then waited for vblank before continuing to perform
* rendering. The problem was that the kernel might wake the client
* up before it dispatched the vblank swap (since the lock has to be
* held while touching the ringbuffer), in which case the client would
* clear and start the next frame before the swap occurred, and
* flicker would occur in addition to likely missing the vblank.
*
* In the absence of this ioctl, userland falls back to a correct path
* of waiting for a vblank, then dispatching the swap on its own.
* Context switching to userland and back is plenty fast enough for
* meeting the requirements of vblank swapping.
*/
return -EINVAL;
}
/* drm_dma.h hooks
*/
{
/* XXX hotplug from PCH */
/* and GT */
}
{
/* enable kind of interrupts always enabled */
/* should always can generate irq */
/* user interrupt should be enabled, but masked initial */
return 0;
}
{
}
{
return -EINVAL;
return 0;
}
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
return 0;
}
{
int error_mask;
(void) igdng_irq_postinstall(dev);
return;
}
/* Unmask the interrupts that we always want on. */
/*
* Enable some error detection, note the instruction error mask
* bit is reserved, so we leave it masked.
*/
} else {
}
/* Disable pipe interrupt enables, clear pending pipe status */
/* Clear pending interrupt status */
return;
}
{
return;
dev_priv->vblank_pipe = 0;
return;
}
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
}