diff options
Diffstat (limited to 'drivers/staging/psb/psb_irq.c')
-rw-r--r-- | drivers/staging/psb/psb_irq.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/drivers/staging/psb/psb_irq.c b/drivers/staging/psb/psb_irq.c new file mode 100644 index 000000000000..7d486e3fe089 --- /dev/null +++ b/drivers/staging/psb/psb_irq.c @@ -0,0 +1,420 @@ +/************************************************************************** + * Copyright (c) 2007, Intel Corporation. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + **************************************************************************/ +/* + */ + +#include <drm/drmP.h> +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_msvdx.h" +#include "lnc_topaz.h" + +/* + * Video display controller interrupt. + */ + +static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + int wake = 0; + + if (!drm_psb_disable_vsync && (vdc_stat & _PSB_VSYNC_PIPEA_FLAG)) { +#ifdef PSB_FIXME + atomic_inc(&dev->vbl_received); +#endif + wake = 1; + PSB_WVDC32(_PSB_VBLANK_INTERRUPT_ENABLE | + _PSB_VBLANK_CLEAR, PSB_PIPEASTAT); + } + + if (!drm_psb_disable_vsync && (vdc_stat & _PSB_VSYNC_PIPEB_FLAG)) { +#ifdef PSB_FIXME + atomic_inc(&dev->vbl_received2); +#endif + wake = 1; + PSB_WVDC32(_PSB_VBLANK_INTERRUPT_ENABLE | + _PSB_VBLANK_CLEAR, PSB_PIPEBSTAT); + } + + PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R); + (void) PSB_RVDC32(PSB_INT_IDENTITY_R); + DRM_READMEMORYBARRIER(); + +#ifdef PSB_FIXME + if (wake) { + DRM_WAKEUP(&dev->vbl_queue); + drm_vbl_send_signals(dev); + } +#endif +} + +/* + * SGX interrupt source 1. + */ + +static void psb_sgx_interrupt(struct drm_device *dev, uint32_t sgx_stat, + uint32_t sgx_stat2) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + + if (sgx_stat & _PSB_CE_TWOD_COMPLETE) { + DRM_WAKEUP(&dev_priv->event_2d_queue); + psb_fence_handler(dev, PSB_ENGINE_2D); + } + + if (unlikely(sgx_stat2 & _PSB_CE2_BIF_REQUESTER_FAULT)) + psb_print_pagefault(dev_priv); + + psb_scheduler_handler(dev_priv, sgx_stat); +} + +/* + * MSVDX interrupt. + */ +static void psb_msvdx_interrupt(struct drm_device *dev, + uint32_t msvdx_stat) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + + if (msvdx_stat & MSVDX_INTERRUPT_STATUS_CR_MMU_FAULT_IRQ_MASK) { + /*Ideally we should we should never get to this */ + PSB_DEBUG_IRQ("MSVDX:MMU Fault:0x%x fence2_irq_on=%d\n", + msvdx_stat, dev_priv->fence2_irq_on); + + /* Pause MMU */ + PSB_WMSVDX32(MSVDX_MMU_CONTROL0_CR_MMU_PAUSE_MASK, + MSVDX_MMU_CONTROL0); + DRM_WRITEMEMORYBARRIER(); + + /* Clear this interupt bit only */ + PSB_WMSVDX32(MSVDX_INTERRUPT_STATUS_CR_MMU_FAULT_IRQ_MASK, + MSVDX_INTERRUPT_CLEAR); + PSB_RMSVDX32(MSVDX_INTERRUPT_CLEAR); + DRM_READMEMORYBARRIER(); + + dev_priv->msvdx_needs_reset = 1; + } else if (msvdx_stat & MSVDX_INTERRUPT_STATUS_CR_MTX_IRQ_MASK) { + PSB_DEBUG_IRQ + ("MSVDX: msvdx_stat: 0x%x fence2_irq_on=%d(MTX)\n", + msvdx_stat, dev_priv->fence2_irq_on); + + /* Clear all interupt bits */ + PSB_WMSVDX32(0xffff, MSVDX_INTERRUPT_CLEAR); + PSB_RMSVDX32(MSVDX_INTERRUPT_CLEAR); + DRM_READMEMORYBARRIER(); + + psb_msvdx_mtx_interrupt(dev); + } +} + +irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + uint32_t vdc_stat,msvdx_int = 0, topaz_int = 0; + uint32_t sgx_stat = 0; + uint32_t sgx_stat2 = 0; + uint32_t sgx_int = 0; + int handled = 0; + + spin_lock(&dev_priv->irqmask_lock); + + vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R); + + if (vdc_stat & _PSB_IRQ_SGX_FLAG) { + PSB_DEBUG_IRQ("Got SGX interrupt\n"); + sgx_int = 1; + } + if (vdc_stat & _PSB_IRQ_MSVDX_FLAG) { + PSB_DEBUG_IRQ("Got MSVDX interrupt\n"); + msvdx_int = 1; + } + + if (vdc_stat & _LNC_IRQ_TOPAZ_FLAG) { + PSB_DEBUG_IRQ("Got TOPAX interrupt\n"); + topaz_int = 1; + } + if (sgx_int && (dev_priv->graphics_state == PSB_PWR_STATE_D0i0)) { + sgx_stat = PSB_RSGX32(PSB_CR_EVENT_STATUS); + sgx_stat2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); + + sgx_stat2 &= dev_priv->sgx2_irq_mask; + sgx_stat &= dev_priv->sgx_irq_mask; + PSB_WSGX32(sgx_stat2, PSB_CR_EVENT_HOST_CLEAR2); + PSB_WSGX32(sgx_stat, PSB_CR_EVENT_HOST_CLEAR); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR); + } else if (unlikely(PSB_D_PM & drm_psb_debug)) { + if (sgx_int) + PSB_DEBUG_PM("sgx int in down mode\n"); + } + vdc_stat &= dev_priv->vdc_irq_mask; + spin_unlock(&dev_priv->irqmask_lock); + + if (msvdx_int) { + uint32_t msvdx_stat = 0; + + msvdx_stat = PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS); + psb_msvdx_interrupt(dev, msvdx_stat); + handled = 1; + } + + if (IS_MRST(dev) && topaz_int) { + uint32_t topaz_stat = 0; + + TOPAZ_READ32(TOPAZ_CR_IMG_TOPAZ_INTSTAT,&topaz_stat); + lnc_topaz_interrupt (dev, topaz_stat); + handled = 1; + } + + if (vdc_stat) { + /* MSVDX IRQ status is part of vdc_irq_mask */ + psb_vdc_interrupt(dev, vdc_stat); + handled = 1; + } + + if (sgx_stat || sgx_stat2) { + + psb_sgx_interrupt(dev, sgx_stat, sgx_stat2); + handled = 1; + } + + if (!handled) + return IRQ_NONE; + + + return IRQ_HANDLED; +} + +void psb_msvdx_irq_preinstall(struct drm_psb_private *dev_priv) +{ + unsigned long mtx_int = 0; + dev_priv->vdc_irq_mask |= _PSB_IRQ_MSVDX_FLAG; + + /* Clear MTX interrupt */ + REGIO_WRITE_FIELD_LITE(mtx_int, MSVDX_INTERRUPT_STATUS, CR_MTX_IRQ, + 1); + PSB_WMSVDX32(mtx_int, MSVDX_INTERRUPT_CLEAR); +} + +void psb_irq_preinstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long mtx_int = 0; + unsigned long irqflags; + PSB_DEBUG_PM("psb_irq_preinstall\n"); + + down_read(&dev_priv->sgx_sem); + psb_check_power_state(dev, PSB_DEVICE_SGX); + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + PSB_WVDC32(0x00000000, PSB_INT_MASK_R); + PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); + PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); + + dev_priv->sgx_irq_mask = _PSB_CE_PIXELBE_END_RENDER | + _PSB_CE_DPM_3D_MEM_FREE | + _PSB_CE_TA_FINISHED | + _PSB_CE_DPM_REACHED_MEM_THRESH | + _PSB_CE_DPM_OUT_OF_MEMORY_GBL | + _PSB_CE_DPM_OUT_OF_MEMORY_MT | + _PSB_CE_TA_TERMINATE | _PSB_CE_SW_EVENT; + + dev_priv->sgx2_irq_mask = _PSB_CE2_BIF_REQUESTER_FAULT; + + dev_priv->vdc_irq_mask = _PSB_IRQ_SGX_FLAG | _PSB_IRQ_MSVDX_FLAG; + + if (!drm_psb_disable_vsync) + dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG | + _PSB_VSYNC_PIPEB_FLAG; + + /* Clear MTX interrupt */ + REGIO_WRITE_FIELD_LITE(mtx_int, MSVDX_INTERRUPT_STATUS, + CR_MTX_IRQ, 1); + PSB_WMSVDX32(mtx_int, MSVDX_INTERRUPT_CLEAR); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + up_read(&dev_priv->sgx_sem); +} + +void psb_msvdx_irq_postinstall(struct drm_psb_private *dev_priv) +{ + /* Enable Mtx Interupt to host */ + unsigned long enables = 0; + PSB_DEBUG_GENERAL("Setting up MSVDX IRQs.....\n"); + REGIO_WRITE_FIELD_LITE(enables, MSVDX_INTERRUPT_STATUS, CR_MTX_IRQ, + 1); + PSB_WMSVDX32(enables, MSVDX_HOST_INTERRUPT_ENABLE); +} + +int psb_irq_postinstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + unsigned long enables = 0; + + PSB_DEBUG_PM("psb_irq_postinstall\n"); + down_read(&dev_priv->sgx_sem); + psb_check_power_state(dev, PSB_DEVICE_SGX); + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + PSB_WSGX32(dev_priv->sgx2_irq_mask, PSB_CR_EVENT_HOST_ENABLE2); + PSB_WSGX32(dev_priv->sgx_irq_mask, PSB_CR_EVENT_HOST_ENABLE); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); + + /* MSVDX IRQ Setup, Enable Mtx Interupt to host */ + PSB_DEBUG_GENERAL("Setting up MSVDX IRQs.....\n"); + REGIO_WRITE_FIELD_LITE(enables, MSVDX_INTERRUPT_STATUS, + CR_MTX_IRQ, 1); + PSB_WMSVDX32(enables, MSVDX_HOST_INTERRUPT_ENABLE); + + dev_priv->irq_enabled = 1; + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + up_read(&dev_priv->sgx_sem); + return 0; +} + +void psb_irq_uninstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + PSB_DEBUG_PM("psb_irq_uninstall\n"); + down_read(&dev_priv->sgx_sem); + psb_check_power_state(dev, PSB_DEVICE_SGX); + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + dev_priv->sgx_irq_mask = 0x00000000; + dev_priv->sgx2_irq_mask = 0x00000000; + dev_priv->vdc_irq_mask = 0x00000000; + + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + PSB_WSGX32(dev_priv->sgx_irq_mask, PSB_CR_EVENT_HOST_ENABLE); + PSB_WSGX32(dev_priv->sgx2_irq_mask, PSB_CR_EVENT_HOST_ENABLE2); + wmb(); + PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R); + PSB_WSGX32(PSB_RSGX32(PSB_CR_EVENT_STATUS), + PSB_CR_EVENT_HOST_CLEAR); + PSB_WSGX32(PSB_RSGX32(PSB_CR_EVENT_STATUS2), + PSB_CR_EVENT_HOST_CLEAR2); + + /* MSVDX IRQ Setup */ + /* Clear interrupt enabled flag */ + PSB_WMSVDX32(0, MSVDX_HOST_INTERRUPT_ENABLE); + + if (IS_MRST(dev)) + TOPAZ_WRITE32(TOPAZ_CR_IMG_TOPAZ_INTENAB, 0); + + dev_priv->irq_enabled = 0; + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + up_read(&dev_priv->sgx_sem); +} + +void psb_2D_irq_off(struct drm_psb_private *dev_priv) +{ + unsigned long irqflags; + uint32_t old_mask; + uint32_t cleared_mask; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + --dev_priv->irqen_count_2d; + if (dev_priv->irq_enabled && dev_priv->irqen_count_2d == 0) { + + old_mask = dev_priv->sgx_irq_mask; + dev_priv->sgx_irq_mask &= ~_PSB_CE_TWOD_COMPLETE; + PSB_WSGX32(dev_priv->sgx_irq_mask, + PSB_CR_EVENT_HOST_ENABLE); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); + + cleared_mask = + (old_mask ^ dev_priv->sgx_irq_mask) & old_mask; + PSB_WSGX32(cleared_mask, PSB_CR_EVENT_HOST_CLEAR); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR); + } + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +void psb_2D_irq_on(struct drm_psb_private *dev_priv) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + if (dev_priv->irq_enabled && dev_priv->irqen_count_2d == 0) { + dev_priv->sgx_irq_mask |= _PSB_CE_TWOD_COMPLETE; + PSB_WSGX32(dev_priv->sgx_irq_mask, + PSB_CR_EVENT_HOST_ENABLE); + (void) PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); + } + ++dev_priv->irqen_count_2d; + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +#ifdef PSB_FIXME +static int psb_vblank_do_wait(struct drm_device *dev, + unsigned int *sequence, atomic_t *counter) +{ + unsigned int cur_vblank; + int ret = 0; + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + (((cur_vblank = atomic_read(counter)) + - *sequence) <= (1 << 23))); + *sequence = cur_vblank; + + return ret; +} +#endif + +void psb_msvdx_irq_off(struct drm_psb_private *dev_priv) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + if (dev_priv->irq_enabled) { + dev_priv->vdc_irq_mask &= ~_PSB_IRQ_MSVDX_FLAG; + PSB_WSGX32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + (void) PSB_RSGX32(PSB_INT_ENABLE_R); + } + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +void psb_msvdx_irq_on(struct drm_psb_private *dev_priv) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + if (dev_priv->irq_enabled) { + dev_priv->vdc_irq_mask |= _PSB_IRQ_MSVDX_FLAG; + PSB_WSGX32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + (void) PSB_RSGX32(PSB_INT_ENABLE_R); + } + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} |