via_irq.c revision 302408
159769Sgrog/* via_irq.c 259769Sgrog * 324424Swosch * Copyright 2004 BEAM Ltd. 424424Swosch * Copyright 2002 Tungsten Graphics, Inc. 524424Swosch * Copyright 2005 Thomas Hellstrom. 624424Swosch * All Rights Reserved. 724424Swosch * 824424Swosch * Permission is hereby granted, free of charge, to any person obtaining a 924424Swosch * copy of this software and associated documentation files (the "Software"), 1024424Swosch * to deal in the Software without restriction, including without limitation 1124424Swosch * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1224424Swosch * and/or sell copies of the Software, and to permit persons to whom the 1324424Swosch * Software is furnished to do so, subject to the following conditions: 1424424Swosch * 1542704Swosch * The above copyright notice and this permission notice (including the next 1642704Swosch * paragraph) shall be included in all copies or substantial portions of the 1742704Swosch * Software. 1824424Swosch * 1942704Swosch * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2042704Swosch * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2142704Swosch * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2242704Swosch * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2342704Swosch * DAMAGES OR 2442704Swosch * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2542704Swosch * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2642704Swosch * DEALINGS IN THE SOFTWARE. 2742704Swosch * 2842704Swosch * Authors: 2942704Swosch * Terry Barnaby <terry1@beam.ltd.uk> 3059769Sgrog * Keith Whitwell <keith@tungstengraphics.com> 3159769Sgrog * Thomas Hellstrom <unichrome@shipmail.org> 3259769Sgrog * 3359769Sgrog * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank 3459769Sgrog * interrupt, as well as an infrastructure to handle other interrupts of the chip. 3559769Sgrog * The refresh rate is also calculated for video playback sync purposes. 3659769Sgrog */ 3759769Sgrog 3859769Sgrog#include <sys/cdefs.h> 3924424Swosch__FBSDID("$FreeBSD: stable/11/sys/dev/drm/via_irq.c 258780 2013-11-30 22:17:27Z eadler $"); 4042704Swosch 4124424Swosch#include "dev/drm/drmP.h" 4242704Swosch#include "dev/drm/drm.h" 4324424Swosch#include "dev/drm/via_drm.h" 4442704Swosch#include "dev/drm/via_drv.h" 4524424Swosch 4624424Swosch#define VIA_REG_INTERRUPT 0x200 4724424Swosch 4842704Swosch/* VIA_REG_INTERRUPT */ 4925031Swosch#define VIA_IRQ_GLOBAL (1U << 31) 5059156Swosch#define VIA_IRQ_VBLANK_ENABLE (1 << 19) 5125031Swosch#define VIA_IRQ_VBLANK_PENDING (1 << 3) 5225031Swosch#define VIA_IRQ_HQV0_ENABLE (1 << 11) 5324424Swosch#define VIA_IRQ_HQV1_ENABLE (1 << 25) 5424424Swosch#define VIA_IRQ_HQV0_PENDING (1 << 9) 5524424Swosch#define VIA_IRQ_HQV1_PENDING (1 << 10) 5624424Swosch#define VIA_IRQ_DMA0_DD_ENABLE (1 << 20) 5771231Sitojun#define VIA_IRQ_DMA0_TD_ENABLE (1 << 21) 5824424Swosch#define VIA_IRQ_DMA1_DD_ENABLE (1 << 22) 5971231Sitojun#define VIA_IRQ_DMA1_TD_ENABLE (1 << 23) 6025031Swosch#define VIA_IRQ_DMA0_DD_PENDING (1 << 4) 6171231Sitojun#define VIA_IRQ_DMA0_TD_PENDING (1 << 5) 6224424Swosch#define VIA_IRQ_DMA1_DD_PENDING (1 << 6) 6325031Swosch#define VIA_IRQ_DMA1_TD_PENDING (1 << 7) 6425031Swosch 6571231Sitojun 6625031Swosch/* 6771231Sitojun * Device-specific IRQs go here. This type might need to be extended with 6870110Swosch * the register if there are multiple IRQ control registers. 6970110Swosch * Currently we activate the HQV interrupts of Unichrome Pro group A. 7070110Swosch */ 7170110Swosch 7270110Swoschstatic maskarray_t via_pro_group_a_irqs[] = { 7370110Swosch {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, 7470110Swosch 0x00000000 }, 7570110Swosch {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, 7670110Swosch 0x00000000 }, 7770110Swosch {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, 7870110Swosch VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, 7980675Sasmodai {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, 8080675Sasmodai VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, 8180675Sasmodai}; 8280675Sasmodaistatic int via_num_pro_group_a = DRM_ARRAY_SIZE(via_pro_group_a_irqs); 8380675Sasmodaistatic int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; 8480675Sasmodai 8580675Sasmodaistatic maskarray_t via_unichrome_irqs[] = { 8680675Sasmodai {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, 8780675Sasmodai VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, 8880675Sasmodai {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, 8980675Sasmodai VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} 9080675Sasmodai}; 9180675Sasmodaistatic int via_num_unichrome = DRM_ARRAY_SIZE(via_unichrome_irqs); 9280675Sasmodaistatic int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; 9380675Sasmodai 9480675Sasmodai 9580675Sasmodaistatic unsigned time_diff(struct timeval *now, struct timeval *then) 9680675Sasmodai{ 9780675Sasmodai return (now->tv_usec >= then->tv_usec) ? 9880675Sasmodai now->tv_usec - then->tv_usec : 9980675Sasmodai 1000000 - (then->tv_usec - now->tv_usec); 10080675Sasmodai} 10180675Sasmodai 10280675Sasmodaiu32 via_get_vblank_counter(struct drm_device *dev, int crtc) 10380675Sasmodai{ 10480675Sasmodai drm_via_private_t *dev_priv = dev->dev_private; 10580675Sasmodai if (crtc != 0) 10680675Sasmodai return 0; 10780675Sasmodai 10880675Sasmodai return atomic_read(&dev_priv->vbl_received); 10980675Sasmodai} 11080675Sasmodai 11180675Sasmodaiirqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) 11280675Sasmodai{ 11380675Sasmodai struct drm_device *dev = (struct drm_device *) arg; 11480675Sasmodai drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 11580675Sasmodai u32 status; 11680675Sasmodai int handled = 0; 11780675Sasmodai struct timeval cur_vblank; 11880675Sasmodai drm_via_irq_t *cur_irq = dev_priv->via_irqs; 11980675Sasmodai int i; 12080675Sasmodai 12180675Sasmodai status = VIA_READ(VIA_REG_INTERRUPT); 12280675Sasmodai if (status & VIA_IRQ_VBLANK_PENDING) { 12380675Sasmodai atomic_inc(&dev_priv->vbl_received); 12480675Sasmodai if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { 12580675Sasmodai microtime(&cur_vblank); 12680675Sasmodai if (dev_priv->last_vblank_valid) { 12780675Sasmodai dev_priv->usec_per_vblank = 12880675Sasmodai time_diff(&cur_vblank, 12980675Sasmodai &dev_priv->last_vblank) >> 4; 13080675Sasmodai } 13180675Sasmodai dev_priv->last_vblank = cur_vblank; 13280675Sasmodai dev_priv->last_vblank_valid = 1; 13380675Sasmodai } 13480675Sasmodai if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { 13580675Sasmodai DRM_DEBUG("US per vblank is: %u\n", 13680675Sasmodai dev_priv->usec_per_vblank); 13780675Sasmodai } 138101401Swosch drm_handle_vblank(dev, 0); 13980675Sasmodai handled = 1; 14087200Swosch } 14187200Swosch 14287200Swosch for (i = 0; i < dev_priv->num_irqs; ++i) { 14380675Sasmodai if (status & cur_irq->pending_mask) { 144104772Smaxim atomic_inc(&cur_irq->irq_received); 145104772Smaxim DRM_WAKEUP(&cur_irq->irq_queue); 146104772Smaxim handled = 1; 147104772Smaxim if (dev_priv->irq_map[drm_via_irq_dma0_td] == i) { 148104781Sjhb via_dmablit_handler(dev, 0, 1); 149104781Sjhb } else if (dev_priv->irq_map[drm_via_irq_dma1_td] == i) { 150104781Sjhb via_dmablit_handler(dev, 1, 1); 151104781Sjhb } 152104781Sjhb } 153104781Sjhb cur_irq++; 154119217Smurray } 155119217Smurray 156119217Smurray /* Acknowlege interrupts */ 157119217Smurray VIA_WRITE(VIA_REG_INTERRUPT, status); 158119217Smurray 159119217Smurray 160119217Smurray if (handled) 161119217Smurray return IRQ_HANDLED; 162119217Smurray else 163119217Smurray return IRQ_NONE; 164119217Smurray} 165119217Smurray 166119217Smurraystatic __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) 167119217Smurray{ 168111949Swosch u32 status; 169104781Sjhb 17024424Swosch if (dev_priv) { 17124424Swosch /* Acknowlege interrupts */ 17224424Swosch status = VIA_READ(VIA_REG_INTERRUPT); 17324424Swosch VIA_WRITE(VIA_REG_INTERRUPT, status | 17469277Sasmodai dev_priv->irq_pending_mask); 17569277Sasmodai } 17624424Swosch} 17725031Swosch 17825031Swoschint via_enable_vblank(struct drm_device *dev, int crtc) 17925031Swosch{ 18080675Sasmodai drm_via_private_t *dev_priv = dev->dev_private; 181104782Sjhb u32 status; 18225031Swosch 183104782Sjhb if (crtc != 0) { 184104782Sjhb DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); 185104782Sjhb return -EINVAL; 186104797Sjhb } 187104797Sjhb 18825031Swosch status = VIA_READ(VIA_REG_INTERRUPT); 18925031Swosch VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); 19025031Swosch 19145349Swosch VIA_WRITE8(0x83d4, 0x11); 19245349Swosch VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); 193104782Sjhb 194104782Sjhb return 0; 195104782Sjhb} 196104782Sjhb 19742704Swoschvoid via_disable_vblank(struct drm_device *dev, int crtc) 19825031Swosch{ 19924424Swosch drm_via_private_t *dev_priv = dev->dev_private; 20059769Sgrog 20125031Swosch VIA_WRITE8(0x83d4, 0x11); 20225031Swosch VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); 20325031Swosch 20425031Swosch if (crtc != 0) 20559769Sgrog DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); 20625031Swosch} 20725031Swosch 20825031Swoschstatic int 20925031Swoschvia_driver_irq_wait(struct drm_device * dev, unsigned int irq, int force_sequence, 21024424Swosch unsigned int *sequence) 21125031Swosch{ 21225031Swosch drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 21325031Swosch unsigned int cur_irq_sequence; 21425031Swosch drm_via_irq_t *cur_irq; 21525031Swosch int ret = 0; 21659769Sgrog maskarray_t *masks; 21759769Sgrog int real_irq; 21842704Swosch 21942704Swosch DRM_DEBUG("\n"); 22042704Swosch 22170110Swosch if (!dev_priv) { 22242704Swosch DRM_ERROR("called with no initialization\n"); 22342704Swosch return -EINVAL; 22425031Swosch } 22525031Swosch 22624424Swosch if (irq >= drm_via_irq_num) { 22725031Swosch DRM_ERROR("Trying to wait on unknown irq %d\n", irq); 22825031Swosch return -EINVAL; 22925031Swosch } 23025031Swosch 23125031Swosch real_irq = dev_priv->irq_map[irq]; 23225031Swosch 23325031Swosch if (real_irq < 0) { 23425031Swosch DRM_ERROR("Video IRQ %d not available on this hardware.\n", 23525031Swosch irq); 23624424Swosch return -EINVAL; 23725031Swosch } 23825031Swosch 23925031Swosch masks = dev_priv->irq_masks; 24025031Swosch cur_irq = dev_priv->via_irqs + real_irq; 24125031Swosch 24225031Swosch if (masks[real_irq][2] && !force_sequence) { 24325031Swosch DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, 24425031Swosch ((VIA_READ(masks[irq][2]) & masks[irq][3]) == 24559156Swosch masks[irq][4])); 24625031Swosch cur_irq_sequence = atomic_read(&cur_irq->irq_received); 24725031Swosch } else { 24825031Swosch DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, 24925031Swosch (((cur_irq_sequence = 25025031Swosch atomic_read(&cur_irq->irq_received)) - 25125031Swosch *sequence) <= (1 << 23))); 25289981Sjoe } 25325031Swosch *sequence = cur_irq_sequence; 25425031Swosch return ret; 25525031Swosch} 25624424Swosch 25725031Swosch 25825031Swosch/* 25989981Sjoe * drm_dma.h hooks 26025031Swosch */ 26189981Sjoe 26289981Sjoevoid via_driver_irq_preinstall(struct drm_device * dev) 26325031Swosch{ 26489981Sjoe drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 26589981Sjoe u32 status; 26689981Sjoe drm_via_irq_t *cur_irq; 26770110Swosch int i; 26871231Sitojun 26970110Swosch DRM_DEBUG("dev_priv: %p\n", dev_priv); 27025031Swosch if (dev_priv) { 27171231Sitojun cur_irq = dev_priv->via_irqs; 27271231Sitojun 27369278Sasmodai dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; 27425031Swosch dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; 27571231Sitojun 27670110Swosch if (dev_priv->chipset == VIA_PRO_GROUP_A || 27771231Sitojun dev_priv->chipset == VIA_DX9_0) { 27870110Swosch dev_priv->irq_masks = via_pro_group_a_irqs; 27970110Swosch dev_priv->num_irqs = via_num_pro_group_a; 28071231Sitojun dev_priv->irq_map = via_irqmap_pro_group_a; 28170110Swosch } else { 28257000Swosch dev_priv->irq_masks = via_unichrome_irqs; 28325031Swosch dev_priv->num_irqs = via_num_unichrome; 28445349Swosch dev_priv->irq_map = via_irqmap_unichrome; 28578270Snik } 28671231Sitojun 28725031Swosch for (i = 0; i < dev_priv->num_irqs; ++i) { 28838440Sjkh atomic_set(&cur_irq->irq_received, 0); 28938440Sjkh cur_irq->enable_mask = dev_priv->irq_masks[i][0]; 29049392Swosch cur_irq->pending_mask = dev_priv->irq_masks[i][1]; 29157000Swosch DRM_INIT_WAITQUEUE(&cur_irq->irq_queue); 29238440Sjkh dev_priv->irq_enable_mask |= cur_irq->enable_mask; 29338440Sjkh dev_priv->irq_pending_mask |= cur_irq->pending_mask; 29470110Swosch cur_irq++; 29569278Sasmodai 29670110Swosch DRM_DEBUG("Initializing IRQ %d\n", i); 29725031Swosch } 29825031Swosch 29969278Sasmodai dev_priv->last_vblank_valid = 0; 30045349Swosch 30170110Swosch /* Clear VSync interrupt regs */ 30269278Sasmodai status = VIA_READ(VIA_REG_INTERRUPT); 30345349Swosch VIA_WRITE(VIA_REG_INTERRUPT, status & 30445349Swosch ~(dev_priv->irq_enable_mask)); 30569278Sasmodai 30669278Sasmodai /* Clear bits if they're already high */ 30780675Sasmodai viadrv_acknowledge_irqs(dev_priv); 30869278Sasmodai } 30970110Swosch} 31069278Sasmodai 31169278Sasmodaiint via_driver_irq_postinstall(struct drm_device *dev) 31269278Sasmodai{ 31357000Swosch drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 31445349Swosch u32 status; 31569277Sasmodai 31645349Swosch DRM_DEBUG("via_driver_irq_postinstall\n"); 31766542Sitojun if (!dev_priv) 31869277Sasmodai return -EINVAL; 31957000Swosch 32070110Swosch status = VIA_READ(VIA_REG_INTERRUPT); 32145349Swosch VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL 32257000Swosch | dev_priv->irq_enable_mask); 32369277Sasmodai 32470110Swosch /* Some magic, oh for some data sheets ! */ 32570110Swosch VIA_WRITE8(0x83d4, 0x11); 32642589Swosch VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); 32770110Swosch 32870110Swosch return 0; 32946321Swosch} 33045349Swosch 33145349Swoschvoid via_driver_irq_uninstall(struct drm_device * dev) 33257000Swosch{ 33346318Swosch drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 33470110Swosch u32 status; 33556406Swosch 33655389Sbillf DRM_DEBUG("\n"); 33755389Sbillf if (dev_priv) { 33857000Swosch 33955389Sbillf /* Some more magic, oh for some data sheets ! */ 34055389Sbillf 34155389Sbillf VIA_WRITE8(0x83d4, 0x11); 34270110Swosch VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); 34358448Swosch 34458448Swosch status = VIA_READ(VIA_REG_INTERRUPT); 34565412Swosch VIA_WRITE(VIA_REG_INTERRUPT, status & 34664612Salex ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); 34764612Salex } 34865411Swosch} 34965974Swosch 35069277Sasmodaiint via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) 35169277Sasmodai{ 35270119Swosch drm_via_irqwait_t *irqwait = data; 35369277Sasmodai struct timeval now; 35470111Swosch int ret = 0; 35580675Sasmodai drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 35675833Swosch drm_via_irq_t *cur_irq = dev_priv->via_irqs; 35778867Sitojun int force_sequence; 35879603Sitojun 35984087Swosch if (irqwait->request.irq >= dev_priv->num_irqs) { 36083686Sobrien DRM_ERROR("Trying to wait on unknown irq %d\n", 361104797Sjhb irqwait->request.irq); 36287201Swosch return -EINVAL; 36392013Swosch } 364104772Smaxim 365101331Swosch cur_irq += irqwait->request.irq; 366101331Swosch 367104797Sjhb switch (irqwait->request.type & ~VIA_IRQ_FLAGS_MASK) { 368104781Sjhb case VIA_IRQ_RELATIVE: 369104781Sjhb irqwait->request.sequence += 370104781Sjhb atomic_read(&cur_irq->irq_received); 371104659Smurray irqwait->request.type &= ~_DRM_VBLANK_RELATIVE; 372106406Smaxim case VIA_IRQ_ABSOLUTE: 373111949Swosch break; 374111949Swosch default: 375111949Swosch return -EINVAL; 376113054Smurray } 377114211Swosch 378114572Swosch if (irqwait->request.type & VIA_IRQ_SIGNAL) { 379119217Smurray DRM_ERROR("Signals on Via IRQs not implemented yet.\n"); 380119217Smurray return -EINVAL; 38124424Swosch } 382111949Swosch 38324424Swosch force_sequence = (irqwait->request.type & VIA_IRQ_FORCE_SEQUENCE); 38424424Swosch 38524424Swosch ret = via_driver_irq_wait(dev, irqwait->request.irq, force_sequence, 38624424Swosch &irqwait->request.sequence); 38724424Swosch microtime(&now); 38824424Swosch irqwait->reply.tval_sec = now.tv_sec; 38924424Swosch irqwait->reply.tval_usec = now.tv_usec; 39024424Swosch 39124424Swosch return ret; 39224424Swosch} 39324424Swosch