i915_irq.c revision 8566479d2f5fd989f537085885d6fa1df55e36e9
/* BEGIN CSTYLED */
/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
*/
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* 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"
/*
* These are the interrupts used by the driver
*/
#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
static inline void
{
}
}
static inline void
{
}
}
/**
* i915_get_pipe - return the the pipe associated with a given plane
* @dev: DRM device
* @plane: plane to look for
*
* The Intel Mesa & 2D drivers call the vblank routines with a plane number
* rather than a pipe number, since they may not always be equal. This routine
* maps the given @plane back to a pipe number.
*/
static int
{
}
/**
* i915_get_plane - return the the plane associated with a given pipe
* @dev: DRM device
* @pipe: pipe to look for
*
* The Intel Mesa & 2D drivers call the vblank routines with a plane number
* rather than a plane number, since they may not always be equal. This routine
* maps the given @pipe back to a plane number.
*/
static int
{
return 0;
return 1;
}
/**
* 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;
}
/**
* Emit a synchronous flip.
*
* This function must be called with the drawable spinlock held.
*/
static void
int plane)
{
/* If the window is visible on the other plane, we have to flip on that
* plane as well.
*/
if (plane == 1) {
} else {
}
for (i = 0; i < num_rects; i++)
pf_planes = 0x3;
break;
}
}
}
/**
* Emit blits for scheduled buffer swaps.
*
* This function will be called with the HW lock held.
*/
{
unsigned counter[2];
struct drm_drawable_info *drw;
/* COPY rop (0xcc), map cpp to magic color depth constants */
dst_pitch >>= 2;
}
src_pitch >>= 2;
}
nhits = 0;
* interrupt context or normal context, but we don't have to worry
* about getting interrupted by something acquiring the lock, because
* we are the interrupt context thing that acquires the lock.
*/
/* Find buffer swaps scheduled for this vertical blank */
continue;
if (!drw) {
continue;
}
struct drm_drawable_info *drw_cmp =
if (drw_cmp &&
break;
}
}
/* List of hits was empty, or we reached the end of it */
nhits++;
}
if (nhits == 0) {
return;
}
/* Emit blits for buffer swaps, partitioning both outputs into as many
* slices as there are buffer swaps scheduled in order to avoid tearing
* (based on the assumption that a single buffer swap would always
* complete before scanout starts).
*/
for (i = 0; i++ < nhits;
int init_drawrect = 1;
if (i == nhits)
struct drm_clip_rect *rect;
if (!drw)
continue;
continue;
}
if (init_drawrect) {
BEGIN_LP_RING(4);
OUT_RING(0);
OUT_RING(0);
} else {
BEGIN_LP_RING(6);
OUT_RING(0);
OUT_RING(0);
OUT_RING(0);
OUT_RING(0);
}
init_drawrect = 0;
}
continue;
BEGIN_LP_RING(8);
}
}
}
}
}
{
unsigned long high_frame;
unsigned long low_frame;
int pipe;
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 {
/* count may be reset by other driver(e.g. 2D driver),
we have no way to know if it is wrapped or resetted
when count is zero. do a rough guess.
*/
return count;
}
{
int vblank = 0;
if (iir == 0) {
return IRQ_NONE;
}
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++;
}
}
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++;
}
}
if (dev_priv->sarea_priv)
if (iir & I915_USER_INTERRUPT) {
#ifdef I915_HAVE_FENCE
#endif
}
if (vblank) {
if (dev_priv->swaps_pending > 0)
}
return IRQ_HANDLED;
}
{
BEGIN_LP_RING(2);
OUT_RING(0);
}
{
}
}
{
}
}
{
int ret = 0;
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
if (dev_priv->sarea_priv)
return 0;
}
DRM_DEBUG("%d: EBUSY -- rec: %d emitted: %d\n",
ret,
}
if (dev_priv->sarea_priv)
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);
}
}
{
u32 pipestat_reg = 0;
switch (pipe) {
case 0:
break;
case 1:
break;
default:
DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
pipe);
break;
}
if (pipestat_reg)
{
/*
* Older chips didn't have the start vblank interrupt,
* but
*/
else
/*
* Clear any pending status
*/
}
return 0;
}
{
u32 pipestat_reg = 0;
switch (pipe) {
case 0:
break;
case 1:
break;
default:
DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
pipe);
break;
}
if (pipestat_reg)
{
/*
* Clear any pending status
*/
(void) I915_READ(pipestat_reg);
}
}
{
}
/* 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*/
{
int ret;
if (!dev_priv) {
return -EINVAL;
}
DRM_DEBUG("Rotation not supported\n");
return -EINVAL;
}
_DRM_VBLANK_FLIP)) {
return -EINVAL;
}
return -EINVAL;
}
/* It makes no sense to schedule a swap for a drawable that doesn't have
* valid information at this point. E.g. this could mean that the X
* server is too old to push drawable information to the DRM, in which
* case all such swaps would become ineffective.
*/
return -EINVAL;
}
/*
* We take the ref here and put it when the swap actually completes
* in the tasklet.
*/
if (ret)
return ret;
if (seqtype == _DRM_VBLANK_RELATIVE)
} else {
DRM_DEBUG("Missed target sequence\n");
return -EINVAL;
}
}
struct drm_drawable_info *drw;
if (!drw) {
irqflags);
DRM_DEBUG("Invalid drawable ID %d\n",
return -EINVAL;
}
return 0;
}
}
DRM_DEBUG("Already scheduled\n");
return 0;
}
}
DRM_DEBUG("Too many swaps queued\n");
return -EBUSY;
}
if (!vbl_swap) {
DRM_ERROR("Failed to allocate memory to queue swap\n");
return -ENOMEM;
}
DRM_DEBUG("vbl_swap\n");
return 0;
}
/* drm_dma.h hooks
*/
{
return -EINVAL;
return 0;
}
{
dev_priv->swaps_pending = 0;
dev_priv->user_irq_refcount = 0;
/*
* Initialize the hardware status page IRQ location.
*/
return;
}
{
if (!dev_priv)
return;
dev_priv->vblank_pipe = 0;
dev_priv->irq_enabled = 0;
}