/* 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, * distribute, sub license, 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 NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. * */ /* * 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" #define MAX_NOPID ((u32)~0) /** * 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. */ #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) /** Interrupts that we mask and unmask at runtime. */ #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) /** These are all of the interrupts used by the driver */ #define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ I915_INTERRUPT_ENABLE_VAR) void igdng_enable_irq(drm_i915_private_t *dev_priv, u32 mask, int gfx_irq) { if (gfx_irq && ((dev_priv->gt_irq_mask_reg & mask) != 0)) { dev_priv->gt_irq_mask_reg &= ~mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); (void) I915_READ(GTIMR); } else if ((dev_priv->irq_mask_reg & mask) != 0) { dev_priv->irq_mask_reg &= ~mask; I915_WRITE(DEIMR, dev_priv->irq_mask_reg); (void) I915_READ(DEIMR); } } static inline void igdng_disable_irq(drm_i915_private_t *dev_priv, u32 mask, int gfx_irq) { if (gfx_irq && ((dev_priv->gt_irq_mask_reg & mask) != mask)) { dev_priv->gt_irq_mask_reg |= mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); (void) I915_READ(GTIMR); } else if ((dev_priv->irq_mask_reg & mask) != mask) { dev_priv->irq_mask_reg |= mask; I915_WRITE(DEIMR, dev_priv->irq_mask_reg); (void) I915_READ(DEIMR); } } /* For display hotplug interrupt */ void igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { if ((dev_priv->irq_mask_reg & mask) != 0) { dev_priv->irq_mask_reg &= ~mask; I915_WRITE(DEIMR, dev_priv->irq_mask_reg); (void) I915_READ(DEIMR); } } #if 0 static inline void igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { if ((dev_priv->irq_mask_reg & mask) != mask) { dev_priv->irq_mask_reg |= mask; I915_WRITE(DEIMR, dev_priv->irq_mask_reg); (void) I915_READ(DEIMR); } } #endif static inline void i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask) { if ((dev_priv->irq_mask_reg & mask) != 0) { dev_priv->irq_mask_reg &= ~mask; I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IMR); } } static inline void i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) { if ((dev_priv->irq_mask_reg & mask) != mask) { dev_priv->irq_mask_reg |= mask; I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IMR); } } static inline uint32_t i915_pipestat(int pipe) { if (pipe == 0) return PIPEASTAT; if (pipe == 1) return PIPEBSTAT; return 0; } void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, uint32_t mask) { if ((dev_priv->pipestat[pipe] & mask) != mask) { u32 reg = i915_pipestat(pipe); dev_priv->pipestat[pipe] |= mask; /* Enable the interrupt, clear any pending status */ I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); (void) I915_READ(reg); } } void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) { if ((dev_priv->pipestat[pipe] & mask) != 0) { u32 reg = i915_pipestat(pipe); dev_priv->pipestat[pipe] &= ~mask; I915_WRITE(reg, dev_priv->pipestat[pipe]); (void) I915_READ(reg); } } /** * 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 i915_pipe_enabled(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; if (I915_READ(pipeconf) & PIPEACONF_ENABLE) return 1; return 0; } u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long high_frame; unsigned long low_frame; u32 high1, high2, low, count; high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; if (!i915_pipe_enabled(dev, pipe)) { DRM_ERROR("trying to get vblank count for disabled pipe %d\n", 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 { high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> PIPE_FRAME_HIGH_SHIFT); low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> PIPE_FRAME_LOW_SHIFT); high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> PIPE_FRAME_HIGH_SHIFT); } while (high1 != high2); count = (high1 << 8) | low; 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. */ static void i915_capture_error_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_error_state *error; spin_lock_irqsave(&dev_priv->error_lock, flags); #if 0 if (dev_priv->first_error) goto out; #endif error = drm_alloc(sizeof(*error), DRM_MEM_DRIVER); if (!error) { DRM_DEBUG("out ot memory, not capturing error state\n"); goto out; } error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); error->pipeastat = I915_READ(PIPEASTAT); error->pipebstat = I915_READ(PIPEBSTAT); error->instpm = I915_READ(INSTPM); if (!IS_I965G(dev)) { error->ipeir = I915_READ(IPEIR); error->ipehr = I915_READ(IPEHR); error->instdone = I915_READ(INSTDONE); error->acthd = I915_READ(ACTHD); } else { error->ipeir = I915_READ(IPEIR_I965); error->ipehr = I915_READ(IPEHR_I965); error->instdone = I915_READ(INSTDONE_I965); error->instps = I915_READ(INSTPS); error->instdone1 = I915_READ(INSTDONE1); error->acthd = I915_READ(ACTHD_I965); } (void) uniqtime(&error->time); dev_priv->first_error = error; DRM_DEBUG("Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); DRM_DEBUG("EIR: 0x%08x\n", error->eir); DRM_DEBUG(" PGTBL_ER: 0x%08x\n", error->pgtbl_er); DRM_DEBUG(" INSTPM: 0x%08x\n", error->instpm); DRM_DEBUG(" IPEIR: 0x%08x\n", error->ipeir); DRM_DEBUG(" IPEHR: 0x%08x\n", error->ipehr); DRM_DEBUG(" INSTDONE: 0x%08x\n", error->instdone); DRM_DEBUG(" ACTHD: 0x%08x\n", error->acthd); DRM_DEBUG(" DMA_FADD_P: 0x%08x\n", I915_READ(0x2078)); if (IS_I965G(dev)) { DRM_DEBUG(" INSTPS: 0x%08x\n", error->instps); DRM_DEBUG(" INSTDONE1: 0x%08x\n", error->instdone1); } drm_free(error, sizeof(*error), DRM_MEM_DRIVER); out: spin_unlock_irqrestore(&dev_priv->error_lock, flags); } /** * 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.). */ void i915_handle_error(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 eir = I915_READ(EIR); u32 pipea_stats = I915_READ(PIPEASTAT); u32 pipeb_stats = I915_READ(PIPEBSTAT); i915_capture_error_state(dev); DRM_DEBUG("render error detected, EIR: 0x%08x\n", eir); if (IS_G4X(dev)) { if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { u32 ipeir = I915_READ(IPEIR_I965); DRM_DEBUG(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); DRM_DEBUG(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); DRM_DEBUG(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE_I965)); DRM_DEBUG(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); DRM_DEBUG(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); DRM_DEBUG(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); (void)I915_READ(IPEIR_I965); } if (eir & GM45_ERROR_PAGE_TABLE) { u32 pgtbl_err = I915_READ(PGTBL_ER); DRM_DEBUG("page table error\n"); DRM_DEBUG(" PGTBL_ER: 0x%08x\n", pgtbl_err); I915_WRITE(PGTBL_ER, pgtbl_err); (void)I915_READ(PGTBL_ER); } } if (IS_I9XX(dev)) { if (eir & I915_ERROR_PAGE_TABLE) { u32 pgtbl_err = I915_READ(PGTBL_ER); DRM_DEBUG("page table error\n"); DRM_DEBUG("PGTBL_ER: 0x%08x\n", pgtbl_err); I915_WRITE(PGTBL_ER, pgtbl_err); (void)I915_READ(PGTBL_ER); } } if (eir & I915_ERROR_MEMORY_REFRESH) { DRM_DEBUG("memory refresh error\n"); DRM_DEBUG("PIPEASTAT: 0x%08x\n", pipea_stats); DRM_DEBUG("PIPEBSTAT: 0x%08x\n", pipeb_stats); /* pipestat has already been acked */ } if (eir & I915_ERROR_INSTRUCTION) { DRM_DEBUG("instruction error\n"); DRM_DEBUG(" INSTPM: 0x%08x\n", I915_READ(INSTPM)); if (!IS_I965G(dev)) { u32 ipeir = I915_READ(IPEIR); DRM_DEBUG(" IPEIR: 0x%08x\n", I915_READ(IPEIR)); DRM_DEBUG(" IPEHR: 0x%08x\n", I915_READ(IPEHR)); DRM_DEBUG(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE)); DRM_DEBUG(" ACTHD: 0x%08x\n", I915_READ(ACTHD)); I915_WRITE(IPEIR, ipeir); (void)I915_READ(IPEIR); } else { u32 ipeir = I915_READ(IPEIR_I965); DRM_DEBUG(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); DRM_DEBUG(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); DRM_DEBUG(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE_I965)); DRM_DEBUG(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); DRM_DEBUG(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); DRM_DEBUG(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); (void)I915_READ(IPEIR_I965); } } I915_WRITE(EIR, eir); (void)I915_READ(EIR); eir = I915_READ(EIR); if (eir) { /* * some errors might have become stuck, * mask them. */ DRM_DEBUG("EIR stuck: 0x%08x, masking\n", eir); I915_WRITE(EMR, I915_READ(EMR) | eir); I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); } } u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45; if (!i915_pipe_enabled(dev, pipe)) { DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); return 0; } return I915_READ(reg); } irqreturn_t igdng_irq_handler(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; u32 de_iir, gt_iir, de_ier; u32 new_de_iir, new_gt_iir; int vblank = 0; /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); (void)I915_READ(DEIER); de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); for (;;) { if (de_iir == 0 && gt_iir == 0) break; ret = IRQ_HANDLED; I915_WRITE(DEIIR, de_iir); new_de_iir = I915_READ(DEIIR); I915_WRITE(GTIIR, gt_iir); new_gt_iir = I915_READ(GTIIR); if (dev_priv->sarea_priv) { dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } if (gt_iir & GT_USER_INTERRUPT) { dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); DRM_WAKEUP(&dev_priv->irq_queue); } if (de_iir & DE_PIPEA_VBLANK) { vblank++; drm_handle_vblank(dev, 0); } if (de_iir & DE_PIPEB_VBLANK) { vblank++; drm_handle_vblank(dev, 1); } de_iir = new_de_iir; gt_iir = new_gt_iir; } I915_WRITE(DEIER, de_ier); (void)I915_READ(DEIER); return ret; } irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { drm_device_t *dev = (drm_device_t *) (void *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 iir; u32 pipea_stats = 0, pipeb_stats = 0; int vblank = 0; if (IS_IGDNG(dev)) return igdng_irq_handler(dev); iir = I915_READ(IIR); if (iir == 0) { return IRQ_NONE; } start: if (dev_priv->sarea_priv) { if (dev_priv->hw_status_page) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } I915_WRITE(IIR, iir); (void) I915_READ(IIR); /* Flush posted writes */ if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) i915_handle_error(dev); if (iir & I915_USER_INTERRUPT) { dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); DRM_WAKEUP(&dev_priv->irq_queue); } if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { pipea_stats = I915_READ(PIPEASTAT); /* The vblank interrupt gets enabled even if we didn't ask for it, so make sure it's shut down again */ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | PIPE_VBLANK_INTERRUPT_ENABLE); else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| PIPE_VBLANK_INTERRUPT_STATUS)) { vblank++; drm_handle_vblank(dev, 0); } I915_WRITE(PIPEASTAT, pipea_stats); } if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { pipeb_stats = I915_READ(PIPEBSTAT); /* The vblank interrupt gets enabled even if we didn't ask for it, so make sure it's shut down again */ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | PIPE_VBLANK_INTERRUPT_ENABLE); else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| PIPE_VBLANK_INTERRUPT_STATUS)) { vblank++; drm_handle_vblank(dev, 1); } I915_WRITE(PIPEBSTAT, pipeb_stats); } return IRQ_HANDLED; } int i915_emit_irq(drm_device_t * dev) { drm_i915_private_t *dev_priv = dev->dev_private; RING_LOCALS; i915_kernel_lost_context(dev); dev_priv->counter++; if (dev_priv->counter > 0x7FFFFFFFUL) dev_priv->counter = 1; if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_enqueue = dev_priv->counter; #if defined(__i386) if (IS_GM45(dev)) { BEGIN_LP_RING(3); OUT_RING(MI_STORE_DWORD_INDEX); OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); ADVANCE_LP_RING(); (void) READ_BREADCRUMB(dev_priv); BEGIN_LP_RING(2); OUT_RING(0); OUT_RING(MI_USER_INTERRUPT); ADVANCE_LP_RING(); } else { #endif /* __i386 */ BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); OUT_RING(MI_USER_INTERRUPT); ADVANCE_LP_RING(); #if defined(__i386) } #endif /* __i386 */ #if defined(__i386) if (IS_I965GM(dev) || IS_IGDNG(dev) || IS_GM45(dev)) #else if (IS_I965GM(dev) || IS_IGDNG(dev)) #endif /* __i386 */ { (void) READ_BREADCRUMB(dev_priv); BEGIN_LP_RING(2); OUT_RING(0); OUT_RING(0); ADVANCE_LP_RING(); (void) READ_BREADCRUMB(dev_priv); } return dev_priv->counter; } void i915_user_irq_on(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; spin_lock(&dev_priv->user_irq_lock); if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ if (IS_IGDNG(dev)) igdng_enable_irq(dev_priv, GT_USER_INTERRUPT, 1); else i915_enable_irq(dev_priv, I915_USER_INTERRUPT); } spin_unlock(&dev_priv->user_irq_lock); } void i915_user_irq_off(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; spin_lock(&dev_priv->user_irq_lock); if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { if (IS_IGDNG(dev)) igdng_disable_irq(dev_priv, GT_USER_INTERRUPT, 1); else i915_disable_irq(dev_priv, I915_USER_INTERRUPT); } spin_unlock(&dev_priv->user_irq_lock); } static int i915_wait_irq(drm_device_t * dev, int irq_nr) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = 0; int wait_time = 0; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } waitmore: wait_time++; if (READ_BREADCRUMB(dev_priv) >= irq_nr) { if (dev_priv->sarea_priv) { dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } return 0; } DRM_DEBUG("i915_wait_irq: irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv)); i915_user_irq_on(dev); DRM_WAIT_ON(ret, &dev_priv->irq_queue, 3 * DRM_HZ, READ_BREADCRUMB(dev_priv) >= irq_nr); i915_user_irq_off(dev); if (ret == EBUSY) { if (wait_time > 5) { DRM_DEBUG("%d: EBUSY -- rec: %d emitted: %d\n", ret, READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); return ret; } goto waitmore; } if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); if (ret == EINTR) { if (wait_time > 5) { DRM_DEBUG("EINTR wait %d now %d", dev_priv->counter, READ_BREADCRUMB(dev_priv)); return ret; } goto waitmore; } return ret; } /* Needs the lock as it touches the ring. */ /*ARGSUSED*/ int i915_irq_emit(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_irq_emit_t emit; int result; LOCK_TEST_WITH_RETURN(dev, fpriv); if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __FUNCTION__); return (EINVAL); } if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { drm_i915_irq_emit32_t irq_emit32; DRM_COPYFROM_WITH_RETURN(&irq_emit32, (drm_i915_irq_emit32_t __user *) data, sizeof (drm_i915_irq_emit32_t)); emit.irq_seq = (int __user *)(uintptr_t)irq_emit32.irq_seq; } else DRM_COPYFROM_WITH_RETURN(&emit, (drm_i915_irq_emit_t __user *) data, sizeof(emit)); spin_lock(&dev->struct_mutex); result = i915_emit_irq(dev); spin_unlock(&dev->struct_mutex); if (DRM_COPY_TO_USER(emit.irq_seq, &result, sizeof(int))) { DRM_ERROR("copy_to_user\n"); return (EFAULT); } return 0; } /* Doesn't need the hardware lock. */ /*ARGSUSED*/ int i915_irq_wait(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_irq_wait_t irqwait; if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __FUNCTION__); return (EINVAL); } DRM_COPYFROM_WITH_RETURN(&irqwait, (drm_i915_irq_wait_t __user *) data, sizeof(irqwait)); return i915_wait_irq(dev, irqwait.irq_seq); } static void igdng_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 vblank; if (pipe == 0) vblank = DE_PIPEA_VBLANK; else vblank = DE_PIPEB_VBLANK; if ((dev_priv->de_irq_enable_reg & vblank) == 0) { igdng_enable_irq(dev_priv, vblank, 0); dev_priv->de_irq_enable_reg |= vblank; I915_WRITE(DEIER, dev_priv->de_irq_enable_reg); (void) I915_READ(DEIER); } } static void igdng_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 vblank; if (pipe == 0) vblank = DE_PIPEA_VBLANK; else vblank = DE_PIPEB_VBLANK; if ((dev_priv->de_irq_enable_reg & vblank) != 0) { igdng_disable_irq(dev_priv, vblank, 0); dev_priv->de_irq_enable_reg &= ~vblank; I915_WRITE(DEIER, dev_priv->de_irq_enable_reg); (void) I915_READ(DEIER); } } int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 pipeconf; pipeconf = I915_READ(pipeconf_reg); if (!(pipeconf & PIPEACONF_ENABLE)) return -EINVAL; spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); if (IS_IGDNG(dev)) igdng_enable_vblank(dev, pipe); else if (IS_I965G(dev)) i915_enable_pipestat(dev_priv, pipe, PIPE_START_VBLANK_INTERRUPT_ENABLE); else i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); return 0; } void i915_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); if (IS_IGDNG(dev)) igdng_disable_vblank(dev, pipe); else i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE | PIPE_START_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } /* Set the vblank monitor pipe */ /*ARGSUSED*/ int i915_vblank_pipe_set(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_i915_private_t *dev_priv = dev->dev_private; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return (-EINVAL); } return (0); } /*ARGSUSED*/ int i915_vblank_pipe_get(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_pipe_t pipe; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } DRM_COPYFROM_WITH_RETURN(&pipe, (drm_i915_vblank_pipe_t __user *)data, sizeof (pipe)); pipe.pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; return 0; } /** * Schedule buffer swap at given vertical blank. */ /*ARGSUSED*/ int i915_vblank_swap(DRM_IOCTL_ARGS) { /* The delayed swap mechanism was fundamentally racy, and has been * removed. The model was that the client requested a delayed flip/swap * 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 */ static void igdng_irq_preinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; I915_WRITE(HWSTAM, 0xeffe); /* XXX hotplug from PCH */ I915_WRITE(DEIMR, 0xffffffff); I915_WRITE(DEIER, 0x0); (void) I915_READ(DEIER); /* and GT */ I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); (void) I915_READ(GTIER); } static int igdng_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_PCH_EVENT */; u32 render_mask = GT_USER_INTERRUPT; dev_priv->irq_mask_reg = ~display_mask; dev_priv->de_irq_enable_reg = display_mask; /* should always can generate irq */ I915_WRITE(DEIIR, I915_READ(DEIIR)); (void) I915_READ(DEIIR); I915_WRITE(DEIMR, dev_priv->irq_mask_reg); I915_WRITE(DEIER, dev_priv->de_irq_enable_reg); (void) I915_READ(DEIER); /* user interrupt should be enabled, but masked initial */ dev_priv->gt_irq_mask_reg = 0xffffffff; dev_priv->gt_irq_enable_reg = render_mask; I915_WRITE(GTIIR, I915_READ(GTIIR)); (void) I915_READ(GTIIR); I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg); (void) I915_READ(GTIER); return 0; } static void igdng_irq_uninstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(DEIMR, 0xffffffff); I915_WRITE(DEIER, 0x0); I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); I915_WRITE(GTIIR, I915_READ(GTIIR)); } int i915_driver_irq_preinstall(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; if (!dev_priv->mmio_map) return -EINVAL; if (IS_IGDNG(dev)) { igdng_irq_preinstall(dev); return 0; } I915_WRITE16(HWSTAM, 0xeffe); I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE16(IER, 0x0); (void) I915_READ(IER); return 0; } void i915_driver_irq_postinstall(drm_device_t * dev) { int error_mask; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; if (IS_IGDNG(dev)) { (void) igdng_irq_postinstall(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev)); return; } /* Unmask the interrupts that we always want on. */ dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; /* * Enable some error detection, note the instruction error mask * bit is reserved, so we leave it masked. */ if (IS_G4X(dev)) { error_mask = ~(GM45_ERROR_PAGE_TABLE | GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV | I915_ERROR_MEMORY_REFRESH); } else { error_mask = ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH); } I915_WRITE(EMR, error_mask); /* Disable pipe interrupt enables, clear pending pipe status */ I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); (void) I915_READ(PIPEASTAT); (void) I915_READ(PIPEBSTAT); /* Clear pending interrupt status */ I915_WRITE(IIR, I915_READ(IIR)); (void) I915_READ(IIR); I915_WRITE(IMR, dev_priv->irq_mask_reg); I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); (void) I915_READ(IER); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev)); return; } void i915_driver_irq_uninstall(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; if ((!dev_priv) || (dev->irq_enabled == 0)) return; dev_priv->vblank_pipe = 0; if (IS_IGDNG(dev)) { igdng_irq_uninstall(dev); DRM_FINI_WAITQUEUE(&dev_priv->irq_queue); return; } I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); I915_WRITE(IIR, I915_READ(IIR)); DRM_FINI_WAITQUEUE(&dev_priv->irq_queue); }