1fb1d9738SJakob Bornecrantz /************************************************************************** 2fb1d9738SJakob Bornecrantz * 3fb1d9738SJakob Bornecrantz * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA 4fb1d9738SJakob Bornecrantz * All Rights Reserved. 5fb1d9738SJakob Bornecrantz * 6fb1d9738SJakob Bornecrantz * Permission is hereby granted, free of charge, to any person obtaining a 7fb1d9738SJakob Bornecrantz * copy of this software and associated documentation files (the 8fb1d9738SJakob Bornecrantz * "Software"), to deal in the Software without restriction, including 9fb1d9738SJakob Bornecrantz * without limitation the rights to use, copy, modify, merge, publish, 10fb1d9738SJakob Bornecrantz * distribute, sub license, and/or sell copies of the Software, and to 11fb1d9738SJakob Bornecrantz * permit persons to whom the Software is furnished to do so, subject to 12fb1d9738SJakob Bornecrantz * the following conditions: 13fb1d9738SJakob Bornecrantz * 14fb1d9738SJakob Bornecrantz * The above copyright notice and this permission notice (including the 15fb1d9738SJakob Bornecrantz * next paragraph) shall be included in all copies or substantial portions 16fb1d9738SJakob Bornecrantz * of the Software. 17fb1d9738SJakob Bornecrantz * 18fb1d9738SJakob Bornecrantz * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19fb1d9738SJakob Bornecrantz * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20fb1d9738SJakob Bornecrantz * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21fb1d9738SJakob Bornecrantz * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22fb1d9738SJakob Bornecrantz * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23fb1d9738SJakob Bornecrantz * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24fb1d9738SJakob Bornecrantz * USE OR OTHER DEALINGS IN THE SOFTWARE. 25fb1d9738SJakob Bornecrantz * 26fb1d9738SJakob Bornecrantz **************************************************************************/ 27fb1d9738SJakob Bornecrantz 28fb1d9738SJakob Bornecrantz #include "drmP.h" 29fb1d9738SJakob Bornecrantz #include "vmwgfx_drv.h" 30fb1d9738SJakob Bornecrantz 31fb1d9738SJakob Bornecrantz #define VMW_FENCE_WRAP (1 << 24) 32fb1d9738SJakob Bornecrantz 33fb1d9738SJakob Bornecrantz irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS) 34fb1d9738SJakob Bornecrantz { 35fb1d9738SJakob Bornecrantz struct drm_device *dev = (struct drm_device *)arg; 36fb1d9738SJakob Bornecrantz struct vmw_private *dev_priv = vmw_priv(dev); 37fb1d9738SJakob Bornecrantz uint32_t status; 38fb1d9738SJakob Bornecrantz 39fb1d9738SJakob Bornecrantz spin_lock(&dev_priv->irq_lock); 40fb1d9738SJakob Bornecrantz status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 41fb1d9738SJakob Bornecrantz spin_unlock(&dev_priv->irq_lock); 42fb1d9738SJakob Bornecrantz 43fb1d9738SJakob Bornecrantz if (status & SVGA_IRQFLAG_ANY_FENCE) 44fb1d9738SJakob Bornecrantz wake_up_all(&dev_priv->fence_queue); 45fb1d9738SJakob Bornecrantz if (status & SVGA_IRQFLAG_FIFO_PROGRESS) 46fb1d9738SJakob Bornecrantz wake_up_all(&dev_priv->fifo_queue); 47fb1d9738SJakob Bornecrantz 48fb1d9738SJakob Bornecrantz if (likely(status)) { 49fb1d9738SJakob Bornecrantz outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 50fb1d9738SJakob Bornecrantz return IRQ_HANDLED; 51fb1d9738SJakob Bornecrantz } 52fb1d9738SJakob Bornecrantz 53fb1d9738SJakob Bornecrantz return IRQ_NONE; 54fb1d9738SJakob Bornecrantz } 55fb1d9738SJakob Bornecrantz 56fb1d9738SJakob Bornecrantz static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) 57fb1d9738SJakob Bornecrantz { 58fb1d9738SJakob Bornecrantz uint32_t busy; 59fb1d9738SJakob Bornecrantz 60fb1d9738SJakob Bornecrantz mutex_lock(&dev_priv->hw_mutex); 61fb1d9738SJakob Bornecrantz busy = vmw_read(dev_priv, SVGA_REG_BUSY); 62fb1d9738SJakob Bornecrantz mutex_unlock(&dev_priv->hw_mutex); 63fb1d9738SJakob Bornecrantz 64fb1d9738SJakob Bornecrantz return (busy == 0); 65fb1d9738SJakob Bornecrantz } 66fb1d9738SJakob Bornecrantz 67fb1d9738SJakob Bornecrantz 68fb1d9738SJakob Bornecrantz bool vmw_fence_signaled(struct vmw_private *dev_priv, 69fb1d9738SJakob Bornecrantz uint32_t sequence) 70fb1d9738SJakob Bornecrantz { 71fb1d9738SJakob Bornecrantz __le32 __iomem *fifo_mem = dev_priv->mmio_virt; 72fb1d9738SJakob Bornecrantz struct vmw_fifo_state *fifo_state; 73fb1d9738SJakob Bornecrantz bool ret; 74fb1d9738SJakob Bornecrantz 75fb1d9738SJakob Bornecrantz if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) 76fb1d9738SJakob Bornecrantz return true; 77fb1d9738SJakob Bornecrantz 78fb1d9738SJakob Bornecrantz dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); 79fb1d9738SJakob Bornecrantz if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) 80fb1d9738SJakob Bornecrantz return true; 81fb1d9738SJakob Bornecrantz 82fb1d9738SJakob Bornecrantz fifo_state = &dev_priv->fifo; 83fb1d9738SJakob Bornecrantz if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && 84fb1d9738SJakob Bornecrantz vmw_fifo_idle(dev_priv, sequence)) 85fb1d9738SJakob Bornecrantz return true; 86fb1d9738SJakob Bornecrantz 87fb1d9738SJakob Bornecrantz /** 88fb1d9738SJakob Bornecrantz * Below is to signal stale fences that have wrapped. 89fb1d9738SJakob Bornecrantz * First, block fence submission. 90fb1d9738SJakob Bornecrantz */ 91fb1d9738SJakob Bornecrantz 92fb1d9738SJakob Bornecrantz down_read(&fifo_state->rwsem); 93fb1d9738SJakob Bornecrantz 94fb1d9738SJakob Bornecrantz /** 95fb1d9738SJakob Bornecrantz * Then check if the sequence is higher than what we've actually 96fb1d9738SJakob Bornecrantz * emitted. Then the fence is stale and signaled. 97fb1d9738SJakob Bornecrantz */ 98fb1d9738SJakob Bornecrantz 99fb1d9738SJakob Bornecrantz ret = ((dev_priv->fence_seq - sequence) > VMW_FENCE_WRAP); 100fb1d9738SJakob Bornecrantz up_read(&fifo_state->rwsem); 101fb1d9738SJakob Bornecrantz 102fb1d9738SJakob Bornecrantz return ret; 103fb1d9738SJakob Bornecrantz } 104fb1d9738SJakob Bornecrantz 105fb1d9738SJakob Bornecrantz int vmw_fallback_wait(struct vmw_private *dev_priv, 106fb1d9738SJakob Bornecrantz bool lazy, 107fb1d9738SJakob Bornecrantz bool fifo_idle, 108fb1d9738SJakob Bornecrantz uint32_t sequence, 109fb1d9738SJakob Bornecrantz bool interruptible, 110fb1d9738SJakob Bornecrantz unsigned long timeout) 111fb1d9738SJakob Bornecrantz { 112fb1d9738SJakob Bornecrantz struct vmw_fifo_state *fifo_state = &dev_priv->fifo; 113fb1d9738SJakob Bornecrantz 114fb1d9738SJakob Bornecrantz uint32_t count = 0; 115fb1d9738SJakob Bornecrantz uint32_t signal_seq; 116fb1d9738SJakob Bornecrantz int ret; 117fb1d9738SJakob Bornecrantz unsigned long end_jiffies = jiffies + timeout; 118fb1d9738SJakob Bornecrantz bool (*wait_condition)(struct vmw_private *, uint32_t); 119fb1d9738SJakob Bornecrantz DEFINE_WAIT(__wait); 120fb1d9738SJakob Bornecrantz 121fb1d9738SJakob Bornecrantz wait_condition = (fifo_idle) ? &vmw_fifo_idle : 122fb1d9738SJakob Bornecrantz &vmw_fence_signaled; 123fb1d9738SJakob Bornecrantz 124fb1d9738SJakob Bornecrantz /** 125fb1d9738SJakob Bornecrantz * Block command submission while waiting for idle. 126fb1d9738SJakob Bornecrantz */ 127fb1d9738SJakob Bornecrantz 128fb1d9738SJakob Bornecrantz if (fifo_idle) 129fb1d9738SJakob Bornecrantz down_read(&fifo_state->rwsem); 130fb1d9738SJakob Bornecrantz signal_seq = dev_priv->fence_seq; 131fb1d9738SJakob Bornecrantz ret = 0; 132fb1d9738SJakob Bornecrantz 133fb1d9738SJakob Bornecrantz for (;;) { 134fb1d9738SJakob Bornecrantz prepare_to_wait(&dev_priv->fence_queue, &__wait, 135fb1d9738SJakob Bornecrantz (interruptible) ? 136fb1d9738SJakob Bornecrantz TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); 137fb1d9738SJakob Bornecrantz if (wait_condition(dev_priv, sequence)) 138fb1d9738SJakob Bornecrantz break; 139fb1d9738SJakob Bornecrantz if (time_after_eq(jiffies, end_jiffies)) { 140fb1d9738SJakob Bornecrantz DRM_ERROR("SVGA device lockup.\n"); 141fb1d9738SJakob Bornecrantz break; 142fb1d9738SJakob Bornecrantz } 143fb1d9738SJakob Bornecrantz if (lazy) 144fb1d9738SJakob Bornecrantz schedule_timeout(1); 145fb1d9738SJakob Bornecrantz else if ((++count & 0x0F) == 0) { 146fb1d9738SJakob Bornecrantz /** 147fb1d9738SJakob Bornecrantz * FIXME: Use schedule_hr_timeout here for 148fb1d9738SJakob Bornecrantz * newer kernels and lower CPU utilization. 149fb1d9738SJakob Bornecrantz */ 150fb1d9738SJakob Bornecrantz 151fb1d9738SJakob Bornecrantz __set_current_state(TASK_RUNNING); 152fb1d9738SJakob Bornecrantz schedule(); 153fb1d9738SJakob Bornecrantz __set_current_state((interruptible) ? 154fb1d9738SJakob Bornecrantz TASK_INTERRUPTIBLE : 155fb1d9738SJakob Bornecrantz TASK_UNINTERRUPTIBLE); 156fb1d9738SJakob Bornecrantz } 157fb1d9738SJakob Bornecrantz if (interruptible && signal_pending(current)) { 1583d3a5b32SThomas Hellstrom ret = -ERESTARTSYS; 159fb1d9738SJakob Bornecrantz break; 160fb1d9738SJakob Bornecrantz } 161fb1d9738SJakob Bornecrantz } 162fb1d9738SJakob Bornecrantz finish_wait(&dev_priv->fence_queue, &__wait); 163fb1d9738SJakob Bornecrantz if (ret == 0 && fifo_idle) { 164fb1d9738SJakob Bornecrantz __le32 __iomem *fifo_mem = dev_priv->mmio_virt; 165fb1d9738SJakob Bornecrantz iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE); 166fb1d9738SJakob Bornecrantz } 167fb1d9738SJakob Bornecrantz wake_up_all(&dev_priv->fence_queue); 168fb1d9738SJakob Bornecrantz if (fifo_idle) 169fb1d9738SJakob Bornecrantz up_read(&fifo_state->rwsem); 170fb1d9738SJakob Bornecrantz 171fb1d9738SJakob Bornecrantz return ret; 172fb1d9738SJakob Bornecrantz } 173fb1d9738SJakob Bornecrantz 174fb1d9738SJakob Bornecrantz int vmw_wait_fence(struct vmw_private *dev_priv, 175fb1d9738SJakob Bornecrantz bool lazy, uint32_t sequence, 176fb1d9738SJakob Bornecrantz bool interruptible, unsigned long timeout) 177fb1d9738SJakob Bornecrantz { 178fb1d9738SJakob Bornecrantz long ret; 179fb1d9738SJakob Bornecrantz unsigned long irq_flags; 180fb1d9738SJakob Bornecrantz struct vmw_fifo_state *fifo = &dev_priv->fifo; 181fb1d9738SJakob Bornecrantz 182fb1d9738SJakob Bornecrantz if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) 183fb1d9738SJakob Bornecrantz return 0; 184fb1d9738SJakob Bornecrantz 185fb1d9738SJakob Bornecrantz if (likely(vmw_fence_signaled(dev_priv, sequence))) 186fb1d9738SJakob Bornecrantz return 0; 187fb1d9738SJakob Bornecrantz 188fb1d9738SJakob Bornecrantz vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); 189fb1d9738SJakob Bornecrantz 190fb1d9738SJakob Bornecrantz if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE)) 191fb1d9738SJakob Bornecrantz return vmw_fallback_wait(dev_priv, lazy, true, sequence, 192fb1d9738SJakob Bornecrantz interruptible, timeout); 193fb1d9738SJakob Bornecrantz 194fb1d9738SJakob Bornecrantz if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) 195fb1d9738SJakob Bornecrantz return vmw_fallback_wait(dev_priv, lazy, false, sequence, 196fb1d9738SJakob Bornecrantz interruptible, timeout); 197fb1d9738SJakob Bornecrantz 198fb1d9738SJakob Bornecrantz mutex_lock(&dev_priv->hw_mutex); 199fb1d9738SJakob Bornecrantz if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) { 200fb1d9738SJakob Bornecrantz spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); 201fb1d9738SJakob Bornecrantz outl(SVGA_IRQFLAG_ANY_FENCE, 202fb1d9738SJakob Bornecrantz dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 203fb1d9738SJakob Bornecrantz vmw_write(dev_priv, SVGA_REG_IRQMASK, 204fb1d9738SJakob Bornecrantz vmw_read(dev_priv, SVGA_REG_IRQMASK) | 205fb1d9738SJakob Bornecrantz SVGA_IRQFLAG_ANY_FENCE); 206fb1d9738SJakob Bornecrantz spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); 207fb1d9738SJakob Bornecrantz } 208fb1d9738SJakob Bornecrantz mutex_unlock(&dev_priv->hw_mutex); 209fb1d9738SJakob Bornecrantz 210fb1d9738SJakob Bornecrantz if (interruptible) 211fb1d9738SJakob Bornecrantz ret = wait_event_interruptible_timeout 212fb1d9738SJakob Bornecrantz (dev_priv->fence_queue, 213fb1d9738SJakob Bornecrantz vmw_fence_signaled(dev_priv, sequence), 214fb1d9738SJakob Bornecrantz timeout); 215fb1d9738SJakob Bornecrantz else 216fb1d9738SJakob Bornecrantz ret = wait_event_timeout 217fb1d9738SJakob Bornecrantz (dev_priv->fence_queue, 218fb1d9738SJakob Bornecrantz vmw_fence_signaled(dev_priv, sequence), 219fb1d9738SJakob Bornecrantz timeout); 220fb1d9738SJakob Bornecrantz 2213d3a5b32SThomas Hellstrom if (unlikely(ret == 0)) 222fb1d9738SJakob Bornecrantz ret = -EBUSY; 223fb1d9738SJakob Bornecrantz else if (likely(ret > 0)) 224fb1d9738SJakob Bornecrantz ret = 0; 225fb1d9738SJakob Bornecrantz 226fb1d9738SJakob Bornecrantz mutex_lock(&dev_priv->hw_mutex); 227fb1d9738SJakob Bornecrantz if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) { 228fb1d9738SJakob Bornecrantz spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); 229fb1d9738SJakob Bornecrantz vmw_write(dev_priv, SVGA_REG_IRQMASK, 230fb1d9738SJakob Bornecrantz vmw_read(dev_priv, SVGA_REG_IRQMASK) & 231fb1d9738SJakob Bornecrantz ~SVGA_IRQFLAG_ANY_FENCE); 232fb1d9738SJakob Bornecrantz spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); 233fb1d9738SJakob Bornecrantz } 234fb1d9738SJakob Bornecrantz mutex_unlock(&dev_priv->hw_mutex); 235fb1d9738SJakob Bornecrantz 236fb1d9738SJakob Bornecrantz return ret; 237fb1d9738SJakob Bornecrantz } 238fb1d9738SJakob Bornecrantz 239fb1d9738SJakob Bornecrantz void vmw_irq_preinstall(struct drm_device *dev) 240fb1d9738SJakob Bornecrantz { 241fb1d9738SJakob Bornecrantz struct vmw_private *dev_priv = vmw_priv(dev); 242fb1d9738SJakob Bornecrantz uint32_t status; 243fb1d9738SJakob Bornecrantz 244fb1d9738SJakob Bornecrantz if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) 245fb1d9738SJakob Bornecrantz return; 246fb1d9738SJakob Bornecrantz 247fb1d9738SJakob Bornecrantz spin_lock_init(&dev_priv->irq_lock); 248fb1d9738SJakob Bornecrantz status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 249fb1d9738SJakob Bornecrantz outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 250fb1d9738SJakob Bornecrantz } 251fb1d9738SJakob Bornecrantz 252fb1d9738SJakob Bornecrantz int vmw_irq_postinstall(struct drm_device *dev) 253fb1d9738SJakob Bornecrantz { 254fb1d9738SJakob Bornecrantz return 0; 255fb1d9738SJakob Bornecrantz } 256fb1d9738SJakob Bornecrantz 257fb1d9738SJakob Bornecrantz void vmw_irq_uninstall(struct drm_device *dev) 258fb1d9738SJakob Bornecrantz { 259fb1d9738SJakob Bornecrantz struct vmw_private *dev_priv = vmw_priv(dev); 260fb1d9738SJakob Bornecrantz uint32_t status; 261fb1d9738SJakob Bornecrantz 262fb1d9738SJakob Bornecrantz if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) 263fb1d9738SJakob Bornecrantz return; 264fb1d9738SJakob Bornecrantz 265fb1d9738SJakob Bornecrantz mutex_lock(&dev_priv->hw_mutex); 266fb1d9738SJakob Bornecrantz vmw_write(dev_priv, SVGA_REG_IRQMASK, 0); 267fb1d9738SJakob Bornecrantz mutex_unlock(&dev_priv->hw_mutex); 268fb1d9738SJakob Bornecrantz 269fb1d9738SJakob Bornecrantz status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 270fb1d9738SJakob Bornecrantz outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); 271fb1d9738SJakob Bornecrantz } 272fb1d9738SJakob Bornecrantz 273fb1d9738SJakob Bornecrantz #define VMW_FENCE_WAIT_TIMEOUT 3*HZ; 274fb1d9738SJakob Bornecrantz 275fb1d9738SJakob Bornecrantz int vmw_fence_wait_ioctl(struct drm_device *dev, void *data, 276fb1d9738SJakob Bornecrantz struct drm_file *file_priv) 277fb1d9738SJakob Bornecrantz { 278fb1d9738SJakob Bornecrantz struct drm_vmw_fence_wait_arg *arg = 279fb1d9738SJakob Bornecrantz (struct drm_vmw_fence_wait_arg *)data; 280fb1d9738SJakob Bornecrantz unsigned long timeout; 281fb1d9738SJakob Bornecrantz 282fb1d9738SJakob Bornecrantz if (!arg->cookie_valid) { 283fb1d9738SJakob Bornecrantz arg->cookie_valid = 1; 284fb1d9738SJakob Bornecrantz arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT; 285fb1d9738SJakob Bornecrantz } 286fb1d9738SJakob Bornecrantz 287fb1d9738SJakob Bornecrantz timeout = jiffies; 288fb1d9738SJakob Bornecrantz if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) 289fb1d9738SJakob Bornecrantz return -EBUSY; 290fb1d9738SJakob Bornecrantz 291fb1d9738SJakob Bornecrantz timeout = (unsigned long)arg->kernel_cookie - timeout; 292fb1d9738SJakob Bornecrantz return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout); 293fb1d9738SJakob Bornecrantz } 294