📄 mga_dma.c
字号:
/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*- * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * 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, sublicense, * 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 NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT 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. * * Authors: Rickard E. (Rik) Faith <faith@valinux.com> * Jeff Hartmann <jhartmann@valinux.com> * Keith Whitwell <keithw@valinux.com> * */#define __NO_VERSION__#include "drmP.h"#include "mga_drv.h"#include <linux/interrupt.h> /* For task queue support */#define MGA_REG(reg) 2#define MGA_BASE(reg) ((unsigned long) \ ((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle)#define MGA_ADDR(reg) (MGA_BASE(reg) + reg)#define MGA_DEREF(reg) *(__volatile__ int *)MGA_ADDR(reg)#define MGA_READ(reg) MGA_DEREF(reg)#define MGA_WRITE(reg,val) do { MGA_DEREF(reg) = val; } while (0)#define PDEA_pagpxfer_enable 0x2static int mga_flush_queue(drm_device_t *dev);static unsigned long mga_alloc_page(drm_device_t *dev){ unsigned long address; address = __get_free_page(GFP_KERNEL); if(address == 0UL) { return 0; } atomic_inc(&virt_to_page(address)->count); set_bit(PG_reserved, &virt_to_page(address)->flags); return address;}static void mga_free_page(drm_device_t *dev, unsigned long page){ if(!page) return; atomic_dec(&virt_to_page(page)->count); clear_bit(PG_reserved, &virt_to_page(page)->flags); free_page(page); return;}static void mga_delay(void){ return;}/* These are two age tags that will never be sent to * the hardware */#define MGA_BUF_USED 0xffffffff#define MGA_BUF_FREE 0static int mga_freelist_init(drm_device_t *dev){ drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; drm_mga_freelist_t *item; int i; dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); if(dev_priv->head == NULL) return -ENOMEM; memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t)); dev_priv->head->age = MGA_BUF_USED; for (i = 0; i < dma->buf_count; i++) { buf = dma->buflist[ i ]; buf_priv = buf->dev_private; item = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); if(item == NULL) return -ENOMEM; memset(item, 0, sizeof(drm_mga_freelist_t)); item->age = MGA_BUF_FREE; item->prev = dev_priv->head; item->next = dev_priv->head->next; if(dev_priv->head->next != NULL) dev_priv->head->next->prev = item; if(item->next == NULL) dev_priv->tail = item; item->buf = buf; buf_priv->my_freelist = item; buf_priv->discard = 0; buf_priv->dispatched = 0; dev_priv->head->next = item; } return 0;}static void mga_freelist_cleanup(drm_device_t *dev){ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; drm_mga_freelist_t *item; drm_mga_freelist_t *prev; item = dev_priv->head; while(item) { prev = item; item = item->next; drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); } dev_priv->head = dev_priv->tail = NULL;}/* Frees dispatch lock */static inline void mga_dma_quiescent(drm_device_t *dev){ drm_device_dma_t *dma = dev->dma; drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned long end; int i; DRM_DEBUG("dispatch_status = 0x%02lx\n", dev_priv->dispatch_status); end = jiffies + (HZ*3); while(1) { if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) { break; } if((signed)(end - jiffies) <= 0) { DRM_ERROR("irqs: %d wanted %d\n", atomic_read(&dev->total_irq), atomic_read(&dma->total_lost)); DRM_ERROR("lockup: dispatch_status = 0x%02lx," " jiffies = %lu, end = %lu\n", dev_priv->dispatch_status, jiffies, end); return; } for (i = 0 ; i < 2000 ; i++) mga_delay(); } end = jiffies + (HZ*3); DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS)); while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { if((signed)(end - jiffies) <= 0) { DRM_ERROR("irqs: %d wanted %d\n", atomic_read(&dev->total_irq), atomic_read(&dma->total_lost)); DRM_ERROR("lockup\n"); clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); return; } for (i = 0 ; i < 2000 ; i++) mga_delay(); } sarea_priv->dirty |= MGA_DMA_FLUSH; clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); DRM_DEBUG("exit, dispatch_status = 0x%02lx\n", dev_priv->dispatch_status);}static void mga_reset_freelist(drm_device_t *dev){ drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; int i; for (i = 0; i < dma->buf_count; i++) { buf = dma->buflist[ i ]; buf_priv = buf->dev_private; buf_priv->my_freelist->age = MGA_BUF_FREE; }}/* Least recently used : * These operations are not atomic b/c they are protected by the * hardware lock */drm_buf_t *mga_freelist_get(drm_device_t *dev){ DECLARE_WAITQUEUE(entry, current); drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; drm_mga_freelist_t *prev; drm_mga_freelist_t *next; static int failed = 0; int return_null = 0; if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) { DRM_DEBUG("Waiting on freelist," " tail->age = %d, last_prim_age= %d\n", dev_priv->tail->age, dev_priv->last_prim_age); add_wait_queue(&dev_priv->buf_queue, &entry); set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); for (;;) { current->state = TASK_INTERRUPTIBLE; mga_dma_schedule(dev, 0); if(dev_priv->tail->age < dev_priv->last_prim_age) break; atomic_inc(&dev->total_sleeps); schedule(); if (signal_pending(current)) { ++return_null; break; } } clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); current->state = TASK_RUNNING; remove_wait_queue(&dev_priv->buf_queue, &entry); if (return_null) return NULL; } if(dev_priv->tail->age < dev_priv->last_prim_age) { prev = dev_priv->tail->prev; next = dev_priv->tail; prev->next = NULL; next->prev = next->next = NULL; dev_priv->tail = prev; next->age = MGA_BUF_USED; failed = 0; return next->buf; } failed++; return NULL;}int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf){ drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_freelist_t *prev; drm_mga_freelist_t *head; drm_mga_freelist_t *next; if(buf_priv->my_freelist->age == MGA_BUF_USED) { /* Discarded buffer, put it on the tail */ next = buf_priv->my_freelist; next->age = MGA_BUF_FREE; prev = dev_priv->tail; prev->next = next; next->prev = prev; next->next = NULL; dev_priv->tail = next; } else { /* Normally aged buffer, put it on the head + 1, * as the real head is a sentinal element */ next = buf_priv->my_freelist; head = dev_priv->head; prev = head->next; head->next = next; prev->prev = next; next->prev = head; next->next = prev; } return 0;}static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init){ drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_prim_buf_t *prim_buffer; int i, temp, size_of_buf; int offset = init->reserved_map_agpstart; dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS; dev_priv->warp_ucode_size = init->warp_ucode_size; dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1), DRM_MEM_DRIVER); if(dev_priv->prim_bufs == NULL) { DRM_ERROR("Unable to allocate memory for prim_buf\n"); return -ENOMEM; } memset(dev_priv->prim_bufs, 0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1)); temp = init->warp_ucode_size + dev_priv->primary_size; temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; dev_priv->ioremap = drm_ioremap(dev->agp->base + offset, temp); if(dev_priv->ioremap == NULL) { DRM_ERROR("Ioremap failed\n"); return -ENOMEM; } init_waitqueue_head(&dev_priv->wait_queue); for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t), DRM_MEM_DRIVER); if(prim_buffer == NULL) return -ENOMEM; memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t)); prim_buffer->phys_head = offset + dev->agp->base; prim_buffer->current_dma_ptr = prim_buffer->head = (u32 *) (dev_priv->ioremap + offset - init->reserved_map_agpstart); prim_buffer->num_dwords = 0; prim_buffer->max_dwords = size_of_buf / sizeof(u32); prim_buffer->max_dwords -= 5; /* Leave room for the softrap */ prim_buffer->sec_used = 0; prim_buffer->idx = i; prim_buffer->prim_age = i + 1; offset = offset + size_of_buf; dev_priv->prim_bufs[i] = prim_buffer; } dev_priv->current_prim_idx = 0; dev_priv->next_prim = dev_priv->last_prim = dev_priv->current_prim = dev_priv->prim_bufs[0]; dev_priv->next_prim_age = 2; dev_priv->last_prim_age = 1; set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status); return 0;}void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim){ drm_mga_private_t *dev_priv = dev->dev_private; drm_device_dma_t *dma = dev->dma; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; int use_agp = PDEA_pagpxfer_enable; unsigned long end; int i; int next_idx; PRIMLOCALS; dev_priv->last_prim = prim; /* We never check for overflow, b/c there is always room */ PRIMPTR(prim); if(num_dwords <= 0) { DRM_ERROR("num_dwords == 0 when dispatched\n"); goto out_prim_wait; } PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_SOFTRAP, 0); PRIMFINISH(prim); end = jiffies + (HZ*3); if(sarea_priv->dirty & MGA_DMA_FLUSH) { while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { if((signed)(end - jiffies) <= 0) { DRM_ERROR("irqs: %d wanted %d\n", atomic_read(&dev->total_irq), atomic_read(&dma->total_lost)); DRM_ERROR("lockup (flush)\n"); goto out_prim_wait; } for (i = 0 ; i < 4096 ; i++) mga_delay(); } sarea_priv->dirty &= ~(MGA_DMA_FLUSH); } else { while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) { if((signed)(end - jiffies) <= 0) { DRM_ERROR("irqs: %d wanted %d\n", atomic_read(&dev->total_irq), atomic_read(&dma->total_lost)); DRM_ERROR("lockup (wait)\n"); goto out_prim_wait; } for (i = 0 ; i < 4096 ; i++) mga_delay(); } } mga_flush_write_combine(); atomic_inc(&dev_priv->pending_bufs); MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); prim->num_dwords = 0; sarea_priv->last_enqueue = prim->prim_age; next_idx = prim->idx + 1; if(next_idx >= MGA_NUM_PRIM_BUFS) next_idx = 0; dev_priv->next_prim = dev_priv->prim_bufs[next_idx]; return; out_prim_wait: prim->num_dwords = 0; prim->sec_used = 0; clear_bit(MGA_BUF_IN_USE, &prim->buffer_status); wake_up_interruptible(&dev_priv->wait_queue); clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status); clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);}int mga_advance_primary(drm_device_t *dev){ DECLARE_WAITQUEUE(entry, current); drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_prim_buf_t *prim_buffer; drm_device_dma_t *dma = dev->dma; int next_prim_idx; int ret = 0; /* This needs to reset the primary buffer if available, * we should collect stats on how many times it bites * it's tail */ next_prim_idx = dev_priv->current_prim_idx + 1; if(next_prim_idx >= MGA_NUM_PRIM_BUFS) next_prim_idx = 0; prim_buffer = dev_priv->prim_bufs[next_prim_idx]; set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); /* In use is cleared in interrupt handler */ if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) { add_wait_queue(&dev_priv->wait_queue, &entry); for (;;) { current->state = TASK_INTERRUPTIBLE; mga_dma_schedule(dev, 0); if(!test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) break; atomic_inc(&dev->total_sleeps); atomic_inc(&dma->total_missed_sched); schedule(); if (signal_pending(current)) { ret = -ERESTARTSYS; break; } } current->state = TASK_RUNNING; remove_wait_queue(&dev_priv->wait_queue, &entry); if(ret) return ret; } clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); /* This primary buffer is now free to use */ prim_buffer->current_dma_ptr = prim_buffer->head; prim_buffer->num_dwords = 0; prim_buffer->sec_used = 0; prim_buffer->prim_age = dev_priv->next_prim_age++; if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) { mga_flush_queue(dev); mga_dma_quiescent(dev); mga_reset_freelist(dev); prim_buffer->prim_age = (dev_priv->next_prim_age += 2); } /* Reset all buffer status stuff */ clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status); clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status); clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status); dev_priv->current_prim = prim_buffer; dev_priv->current_prim_idx = next_prim_idx; return 0;}/* More dynamic performance decisions */static inline int mga_decide_to_fire(drm_device_t *dev){ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) { return 1; } if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && dev_priv->next_prim->num_dwords) { return 1; } if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && dev_priv->next_prim->num_dwords) { return 1; } if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) { if(test_bit(MGA_BUF_SWAP_PENDING, &dev_priv->next_prim->buffer_status)) { return 1; } } if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) { if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) { return 1; } } if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) { if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) { return 1; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -