2000-02-22 08:43:59 -07:00
|
|
|
/* 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.
|
|
|
|
* 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@precisioninsight.com>
|
|
|
|
* Jeff Hartmann <jhartmann@precisioninsight.com>
|
2000-04-04 16:08:14 -06:00
|
|
|
* Keith Whitwell <keithw@precisioninsight.com>
|
2000-02-22 08:43:59 -07:00
|
|
|
*
|
2000-04-04 16:08:14 -06:00
|
|
|
* $XFree86$
|
2000-02-22 08:43:59 -07:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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 0x2
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static int mga_flush_queue(drm_device_t *dev);
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
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(&mem_map[MAP_NR((void *) address)].count);
|
|
|
|
set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags);
|
|
|
|
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mga_free_page(drm_device_t *dev, unsigned long page)
|
|
|
|
{
|
|
|
|
if(page == 0UL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
atomic_dec(&mem_map[MAP_NR((void *) page)].count);
|
|
|
|
clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags);
|
|
|
|
wake_up(&mem_map[MAP_NR((void *) page)].wait);
|
|
|
|
free_page(page);
|
|
|
|
return;
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
|
|
|
static void mga_delay(void)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static void mga_flush_write_combine(void)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
int xchangeDummy;
|
|
|
|
__asm__ volatile(" push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy));
|
|
|
|
__asm__ volatile(" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;"
|
|
|
|
" movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ;"
|
|
|
|
" pop %%eax" : /* no outputs */ : /* no inputs */ );
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* These are two age tags that will never be sent to
|
|
|
|
* the hardware */
|
|
|
|
#define MGA_BUF_USED 0xffffffff
|
|
|
|
#define MGA_BUF_FREE 0
|
|
|
|
|
|
|
|
static void mga_freelist_debug(drm_mga_freelist_t *item)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
if(item->buf != NULL) {
|
|
|
|
DRM_DEBUG("buf index : %d\n", item->buf->idx);
|
2000-02-22 08:43:59 -07:00
|
|
|
} else {
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("Freelist head\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("item->age : %x\n", item->age);
|
|
|
|
DRM_DEBUG("item->next : %p\n", item->next);
|
|
|
|
DRM_DEBUG("item->prev : %p\n", item->prev);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static 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;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
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;
|
|
|
|
dev_priv->head->next = item;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
item = dev_priv->head;
|
|
|
|
while(item) {
|
|
|
|
mga_freelist_debug(item);
|
|
|
|
item = item->next;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("Head\n");
|
|
|
|
mga_freelist_debug(dev_priv->head);
|
|
|
|
DRM_DEBUG("Tail\n");
|
|
|
|
mga_freelist_debug(dev_priv->tail);
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
return 0;
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
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);
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
dev_priv->head = dev_priv->tail = NULL;
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* 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;
|
|
|
|
__volatile__ unsigned int *status =
|
|
|
|
(__volatile__ unsigned int *)dev_priv->status_page;
|
|
|
|
unsigned long end;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
end = jiffies + (HZ*3);
|
|
|
|
while(1) {
|
|
|
|
if(!test_and_set_bit(0, &dev_priv->dispatch_lock)) {
|
|
|
|
break;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
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");
|
|
|
|
goto out_nolock;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
for (i = 0 ; i < 2000 ; i++) mga_delay();
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
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");
|
|
|
|
goto out_status;
|
|
|
|
}
|
|
|
|
for (i = 0 ; i < 2000 ; i++) mga_delay();
|
|
|
|
}
|
|
|
|
DRM_DEBUG("status[1] : %x last_sync_tag : %x\n", status[1],
|
|
|
|
dev_priv->last_sync_tag);
|
|
|
|
sarea_priv->dirty |= MGA_DMA_FLUSH;
|
|
|
|
|
|
|
|
out_status:
|
|
|
|
clear_bit(0, &dev_priv->dispatch_lock);
|
|
|
|
out_nolock:
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
#define FREELIST_INITIAL (MGA_DMA_BUF_NR * 2)
|
|
|
|
#define FREELIST_COMPARE(age) ((age >> 2))
|
|
|
|
|
|
|
|
unsigned int mga_create_sync_tag(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_private_t *dev_priv =
|
|
|
|
(drm_mga_private_t *) dev->dev_private;
|
|
|
|
unsigned int temp;
|
|
|
|
drm_buf_t *buf;
|
|
|
|
drm_mga_buf_priv_t *buf_priv;
|
|
|
|
drm_device_dma_t *dma = dev->dma;
|
|
|
|
int i;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
dev_priv->sync_tag++;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(dev_priv->sync_tag < FREELIST_INITIAL) {
|
|
|
|
dev_priv->sync_tag = FREELIST_INITIAL;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
if(dev_priv->sync_tag > 0x3fffffff) {
|
|
|
|
mga_flush_queue(dev);
|
|
|
|
mga_dma_quiescent(dev);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_priv->sync_tag = FREELIST_INITIAL;
|
|
|
|
}
|
|
|
|
temp = dev_priv->sync_tag << 2;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
dev_priv->sarea_priv->last_enqueue = temp;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("sync_tag : %x\n", temp);
|
|
|
|
return temp;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* 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)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_private_t *dev_priv =
|
|
|
|
(drm_mga_private_t *) dev->dev_private;
|
|
|
|
__volatile__ unsigned int *status =
|
|
|
|
(__volatile__ unsigned int *)dev_priv->status_page;
|
|
|
|
drm_mga_freelist_t *prev;
|
|
|
|
drm_mga_freelist_t *next;
|
|
|
|
|
|
|
|
if((dev_priv->tail->age >> 2) <= FREELIST_COMPARE(status[1])) {
|
|
|
|
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;
|
|
|
|
return next->buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_private_t *dev_priv =
|
|
|
|
(drm_mga_private_t *) dev->dev_private;
|
2000-02-22 08:43:59 -07:00
|
|
|
drm_mga_buf_priv_t *buf_priv = buf->dev_private;
|
2000-04-04 16:08:14 -06:00
|
|
|
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;
|
|
|
|
DRM_DEBUG("Discarded\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
} else {
|
2000-04-04 16:08:14 -06:00
|
|
|
/* 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;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static void mga_print_all_primary(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_private_t *dev_priv = dev->dev_private;
|
|
|
|
drm_mga_prim_buf_t *prim;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DRM_DEBUG("Full list of primarys\n");
|
|
|
|
for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
|
|
|
|
prim = dev_priv->prim_bufs[i];
|
|
|
|
DRM_DEBUG("index : %d num_dwords : %d "
|
|
|
|
"max_dwords : %d phy_head : %x\n",
|
|
|
|
prim->idx, prim->num_dwords,
|
|
|
|
prim->max_dwords, prim->phys_head);
|
|
|
|
DRM_DEBUG("sec_used : %d swap_pending : %x "
|
|
|
|
"in_use : %x force_fire : %d\n",
|
|
|
|
prim->sec_used, prim->swap_pending,
|
|
|
|
prim->in_use, atomic_read(&prim->force_fire));
|
|
|
|
DRM_DEBUG("needs_overflow : %d\n",
|
|
|
|
atomic_read(&prim->needs_overflow));
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
DRM_DEBUG("current_idx : %d, next_idx : %d, last_idx : %d\n",
|
|
|
|
dev_priv->next_prim->idx, dev_priv->last_prim->idx,
|
|
|
|
dev_priv->current_prim->idx);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
|
|
|
drm_mga_private_t *dev_priv = dev->dev_private;
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_prim_buf_t *prim_buffer;
|
|
|
|
int i, temp, size_of_buf;
|
|
|
|
int offset = init->reserved_map_agpstart;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("mga_init_primary_bufs\n");
|
|
|
|
dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) /
|
|
|
|
PAGE_SIZE) * PAGE_SIZE;
|
|
|
|
DRM_DEBUG("primary_size\n");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
DRM_DEBUG("memset\n");
|
|
|
|
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;
|
|
|
|
|
|
|
|
DRM_DEBUG("temp : %x\n", temp);
|
|
|
|
DRM_DEBUG("dev->agp->base: %lx\n", dev->agp->base);
|
|
|
|
DRM_DEBUG("init->reserved_map_agpstart: %x\n",
|
|
|
|
init->reserved_map_agpstart);
|
|
|
|
DRM_DEBUG("ioremap\n");
|
|
|
|
dev_priv->ioremap = drm_ioremap(dev->agp->base + offset,
|
|
|
|
temp);
|
|
|
|
if(dev_priv->ioremap == NULL) {
|
|
|
|
DRM_DEBUG("Ioremap failed\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
init_waitqueue_head(&dev_priv->wait_queue);
|
|
|
|
|
|
|
|
for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
|
|
|
|
DRM_DEBUG("For loop\n");
|
|
|
|
prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t),
|
|
|
|
DRM_MEM_DRIVER);
|
|
|
|
if(prim_buffer == NULL) return -ENOMEM;
|
|
|
|
DRM_DEBUG("memset\n");
|
|
|
|
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;
|
|
|
|
offset = offset + size_of_buf;
|
|
|
|
dev_priv->prim_bufs[i] = prim_buffer;
|
|
|
|
DRM_DEBUG("Looping\n");
|
|
|
|
}
|
|
|
|
dev_priv->current_prim_idx = 0;
|
|
|
|
dev_priv->next_prim =
|
|
|
|
dev_priv->last_prim =
|
|
|
|
dev_priv->current_prim =
|
|
|
|
dev_priv->prim_bufs[0];
|
|
|
|
set_bit(0, &dev_priv->current_prim->in_use);
|
|
|
|
DRM_DEBUG("init done\n");
|
|
|
|
return 0;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
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;
|
|
|
|
|
|
|
|
DRM_DEBUG("mga_fire_primary\n");
|
|
|
|
dev_priv->last_sync_tag = mga_create_sync_tag(dev);
|
|
|
|
dev_priv->last_prim = prim;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* We never check for overflow, b/c there is always room */
|
|
|
|
PRIMPTR(prim);
|
|
|
|
if(num_dwords <= 0) {
|
|
|
|
DRM_DEBUG("num_dwords == 0 when dispatched\n");
|
|
|
|
goto out_prim_wait;
|
|
|
|
}
|
|
|
|
PRIMOUTREG( MGAREG_DMAPAD, 0);
|
|
|
|
PRIMOUTREG( MGAREG_DMAPAD, 0);
|
|
|
|
PRIMOUTREG( MGAREG_DWGSYNC, dev_priv->last_sync_tag);
|
|
|
|
PRIMOUTREG( MGAREG_SOFTRAP, 0);
|
|
|
|
PRIMFINISH(prim);
|
|
|
|
|
|
|
|
end = jiffies + (HZ*3);
|
|
|
|
if(sarea_priv->dirty & MGA_DMA_FLUSH) {
|
|
|
|
DRM_DEBUG("Dma top flush\n");
|
|
|
|
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 in fire primary "
|
|
|
|
"(Dma Top Flush)\n");
|
|
|
|
goto out_prim_wait;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < 4096 ; i++) mga_delay();
|
|
|
|
}
|
|
|
|
sarea_priv->dirty &= ~(MGA_DMA_FLUSH);
|
2000-02-22 08:43:59 -07:00
|
|
|
} else {
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("Status wait\n");
|
|
|
|
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 in fire primary "
|
|
|
|
"(Status Wait)\n");
|
|
|
|
goto out_prim_wait;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < 4096 ; i++) mga_delay();
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
mga_flush_write_combine();
|
|
|
|
atomic_inc(&dev_priv->pending_bufs);
|
|
|
|
atomic_inc(&dma->total_lost);
|
|
|
|
MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
|
|
|
|
MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp);
|
|
|
|
prim->num_dwords = 0;
|
|
|
|
|
|
|
|
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(0, &prim->in_use);
|
|
|
|
wake_up_interruptible(&dev_priv->wait_queue);
|
|
|
|
clear_bit(0, &prim->swap_pending);
|
|
|
|
clear_bit(0, &dev_priv->dispatch_lock);
|
|
|
|
atomic_dec(&dev_priv->pending_bufs);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
int mga_advance_primary(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
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;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* 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];
|
|
|
|
atomic_set(&dev_priv->in_wait, 1);
|
|
|
|
|
|
|
|
/* In use is cleared in interrupt handler */
|
|
|
|
|
|
|
|
if(test_and_set_bit(0, &prim_buffer->in_use)) {
|
|
|
|
add_wait_queue(&dev_priv->wait_queue, &entry);
|
|
|
|
for (;;) {
|
|
|
|
current->state = TASK_INTERRUPTIBLE;
|
|
|
|
mga_dma_schedule(dev, 0);
|
|
|
|
if(!test_and_set_bit(0, &prim_buffer->in_use)) break;
|
|
|
|
atomic_inc(&dev->total_sleeps);
|
|
|
|
atomic_inc(&dma->total_missed_sched);
|
|
|
|
mga_print_all_primary(dev);
|
|
|
|
DRM_DEBUG("Schedule in advance\n");
|
|
|
|
/* Three second delay */
|
|
|
|
schedule_timeout(HZ*3);
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current->state = TASK_RUNNING;
|
|
|
|
remove_wait_queue(&dev_priv->wait_queue, &entry);
|
|
|
|
if(ret) return ret;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
atomic_set(&dev_priv->in_wait, 0);
|
|
|
|
/* 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;
|
|
|
|
atomic_set(&prim_buffer->needs_overflow, 0);
|
|
|
|
dev_priv->current_prim = prim_buffer;
|
|
|
|
dev_priv->current_prim_idx = next_prim_idx;
|
|
|
|
DRM_DEBUG("Primarys at advance\n");
|
|
|
|
mga_print_all_primary(dev);
|
|
|
|
return 0;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* More dynamic performance decisions */
|
|
|
|
static inline int mga_decide_to_fire(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
|
|
|
drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_device_dma_t *dma = dev->dma;
|
|
|
|
|
|
|
|
if(atomic_read(&dev_priv->next_prim->force_fire))
|
|
|
|
{
|
|
|
|
atomic_inc(&dma->total_prio);
|
|
|
|
return 1;
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if (atomic_read(&dev_priv->in_flush) && dev_priv->next_prim->num_dwords)
|
|
|
|
{
|
|
|
|
atomic_inc(&dma->total_prio);
|
|
|
|
return 1;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) {
|
|
|
|
if(test_bit(0, &dev_priv->next_prim->swap_pending)) {
|
|
|
|
atomic_inc(&dma->total_dmas);
|
|
|
|
return 1;
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) {
|
|
|
|
if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) {
|
|
|
|
atomic_inc(&dma->total_hit);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) {
|
|
|
|
if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) {
|
|
|
|
atomic_inc(&dma->total_missed_free);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_inc(&dma->total_tried);
|
|
|
|
return 0;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
int mga_dma_schedule(drm_device_t *dev, int locked)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
|
|
|
drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_device_dma_t *dma = dev->dma;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if (test_and_set_bit(0, &dev->dma_flag)) {
|
2000-02-22 08:43:59 -07:00
|
|
|
atomic_inc(&dma->total_missed_dma);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
DRM_DEBUG("mga_dma_schedule\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(atomic_read(&dev_priv->in_flush) ||
|
|
|
|
atomic_read(&dev_priv->in_wait)) {
|
|
|
|
locked = 1;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if (!locked &&
|
|
|
|
!drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
|
|
|
|
atomic_inc(&dma->total_missed_lock);
|
|
|
|
clear_bit(0, &dev->dma_flag);
|
|
|
|
return -EBUSY;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("I'm locked\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
if(!test_and_set_bit(0, &dev_priv->dispatch_lock)) {
|
|
|
|
/* Fire dma buffer */
|
|
|
|
if(mga_decide_to_fire(dev)) {
|
|
|
|
DRM_DEBUG("mga_fire_primary\n");
|
|
|
|
DRM_DEBUG("idx :%d\n", dev_priv->next_prim->idx);
|
|
|
|
atomic_set(&dev_priv->next_prim->force_fire, 0);
|
|
|
|
if(dev_priv->current_prim == dev_priv->next_prim &&
|
|
|
|
dev_priv->next_prim->num_dwords != 0) {
|
|
|
|
/* Schedule overflow for a later time */
|
|
|
|
atomic_set(
|
|
|
|
&dev_priv->current_prim->needs_overflow,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
mga_fire_primary(dev, dev_priv->next_prim);
|
|
|
|
} else {
|
|
|
|
clear_bit(0, &dev_priv->dispatch_lock);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DRM_DEBUG("I can't get the dispatch lock\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
2000-02-22 08:43:59 -07:00
|
|
|
if (!locked) {
|
|
|
|
if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
|
|
|
|
DRM_KERNEL_CONTEXT)) {
|
|
|
|
DRM_ERROR("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
clear_bit(0, &dev->dma_flag);
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(atomic_read(&dev_priv->in_flush) == 1 &&
|
|
|
|
dev_priv->next_prim->num_dwords == 0) {
|
|
|
|
/* Everything is on the hardware */
|
|
|
|
DRM_DEBUG("Primarys at Flush\n");
|
|
|
|
mga_print_all_primary(dev);
|
|
|
|
atomic_set(&dev_priv->in_flush, 0);
|
|
|
|
wake_up_interruptible(&dev_priv->flush_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static void mga_dma_service(int irq, void *device, struct pt_regs *regs)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_device_t *dev = (drm_device_t *)device;
|
|
|
|
drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
|
|
|
|
drm_mga_prim_buf_t *last_prim_buffer;
|
|
|
|
__volatile__ unsigned int *status =
|
|
|
|
(__volatile__ unsigned int *)dev_priv->status_page;
|
|
|
|
|
|
|
|
atomic_inc(&dev->total_irq);
|
|
|
|
MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
|
|
|
|
last_prim_buffer = dev_priv->last_prim;
|
|
|
|
last_prim_buffer->num_dwords = 0;
|
|
|
|
last_prim_buffer->sec_used = 0;
|
|
|
|
clear_bit(0, &last_prim_buffer->in_use);
|
|
|
|
wake_up_interruptible(&dev_priv->wait_queue);
|
|
|
|
clear_bit(0, &last_prim_buffer->swap_pending);
|
|
|
|
clear_bit(0, &dev_priv->dispatch_lock);
|
|
|
|
atomic_dec(&dev_priv->pending_bufs);
|
|
|
|
dev_priv->sarea_priv->last_dispatch = status[1];
|
|
|
|
queue_task(&dev->tq, &tq_immediate);
|
|
|
|
mark_bh(IMMEDIATE_BH);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static void mga_dma_task_queue(void *device)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_device_t *dev = (drm_device_t *) device;
|
|
|
|
|
2000-02-22 08:43:59 -07:00
|
|
|
mga_dma_schedule(dev, 0);
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
int mga_dma_cleanup(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
2000-04-04 16:08:14 -06:00
|
|
|
if(dev->dev_private) {
|
|
|
|
drm_mga_private_t *dev_priv =
|
|
|
|
(drm_mga_private_t *) dev->dev_private;
|
|
|
|
|
|
|
|
if(dev_priv->ioremap) {
|
|
|
|
int temp = (dev_priv->warp_ucode_size +
|
|
|
|
dev_priv->primary_size +
|
|
|
|
PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_ioremapfree((void *) dev_priv->ioremap, temp);
|
|
|
|
}
|
|
|
|
if(dev_priv->status_page != NULL) {
|
|
|
|
iounmap(dev_priv->status_page);
|
|
|
|
}
|
|
|
|
if(dev_priv->real_status_page != 0UL) {
|
|
|
|
mga_free_page(dev, dev_priv->real_status_page);
|
|
|
|
}
|
|
|
|
if(dev_priv->prim_bufs != NULL) {
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
|
|
|
|
if(dev_priv->prim_bufs[i] != NULL) {
|
|
|
|
drm_free(dev_priv->prim_bufs[i],
|
|
|
|
sizeof(drm_mga_prim_buf_t),
|
|
|
|
DRM_MEM_DRIVER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drm_free(dev_priv->prim_bufs, sizeof(void *) *
|
|
|
|
(MGA_NUM_PRIM_BUFS + 1),
|
|
|
|
DRM_MEM_DRIVER);
|
|
|
|
}
|
|
|
|
if(dev_priv->head != NULL) {
|
|
|
|
mga_freelist_cleanup(dev);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
|
|
|
|
drm_free(dev->dev_private, sizeof(drm_mga_private_t),
|
|
|
|
DRM_MEM_DRIVER);
|
|
|
|
dev->dev_private = NULL;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) {
|
|
|
|
drm_mga_private_t *dev_priv;
|
|
|
|
drm_map_t *sarea_map = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
|
|
|
|
if(dev_priv == NULL) return -ENOMEM;
|
|
|
|
dev->dev_private = (void *) dev_priv;
|
|
|
|
|
|
|
|
DRM_DEBUG("dev_private\n");
|
|
|
|
|
|
|
|
memset(dev_priv, 0, sizeof(drm_mga_private_t));
|
|
|
|
atomic_set(&dev_priv->in_flush, 0);
|
|
|
|
|
|
|
|
if((init->reserved_map_idx >= dev->map_count) ||
|
|
|
|
(init->buffer_map_idx >= dev->map_count)) {
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
DRM_DEBUG("reserved_map or buffer_map are invalid\n");
|
|
|
|
return -EINVAL;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
dev_priv->reserved_map_idx = init->reserved_map_idx;
|
|
|
|
dev_priv->buffer_map_idx = init->buffer_map_idx;
|
|
|
|
sarea_map = dev->maplist[0];
|
|
|
|
dev_priv->sarea_priv = (drm_mga_sarea_t *)
|
|
|
|
((u8 *)sarea_map->handle +
|
|
|
|
init->sarea_priv_offset);
|
|
|
|
DRM_DEBUG("sarea_priv\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* Scale primary size to the next page */
|
|
|
|
dev_priv->chipset = init->chipset;
|
|
|
|
dev_priv->frontOffset = init->frontOffset;
|
|
|
|
dev_priv->backOffset = init->backOffset;
|
|
|
|
dev_priv->depthOffset = init->depthOffset;
|
|
|
|
dev_priv->textureOffset = init->textureOffset;
|
|
|
|
dev_priv->textureSize = init->textureSize;
|
|
|
|
dev_priv->cpp = init->cpp;
|
|
|
|
dev_priv->sgram = init->sgram;
|
|
|
|
dev_priv->stride = init->stride;
|
2000-02-22 08:43:59 -07:00
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
dev_priv->mAccess = init->mAccess;
|
|
|
|
init_waitqueue_head(&dev_priv->flush_queue);
|
|
|
|
dev_priv->WarpPipe = -1;
|
|
|
|
|
|
|
|
DRM_DEBUG("chipset: %d ucode_size: %d backOffset: %x depthOffset: %x\n",
|
|
|
|
dev_priv->chipset, dev_priv->warp_ucode_size,
|
|
|
|
dev_priv->backOffset, dev_priv->depthOffset);
|
|
|
|
DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n",
|
|
|
|
dev_priv->cpp, dev_priv->sgram, dev_priv->stride,
|
|
|
|
dev_priv->mAccess);
|
|
|
|
|
|
|
|
memcpy(&dev_priv->WarpIndex, &init->WarpIndex,
|
|
|
|
sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES);
|
|
|
|
|
|
|
|
for (i = 0 ; i < MGA_MAX_WARP_PIPES ; i++)
|
|
|
|
DRM_DEBUG("warp pipe %d: installed: %d phys: %lx size: %x\n",
|
|
|
|
i,
|
|
|
|
dev_priv->WarpIndex[i].installed,
|
|
|
|
dev_priv->WarpIndex[i].phys_addr,
|
|
|
|
dev_priv->WarpIndex[i].size);
|
|
|
|
|
|
|
|
DRM_DEBUG("Doing init prim buffers\n");
|
|
|
|
if(mga_init_primary_bufs(dev, init) != 0) {
|
|
|
|
DRM_ERROR("Can not initialize primary buffers\n");
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
return -ENOMEM;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("Done with init prim buffers\n");
|
|
|
|
dev_priv->real_status_page = mga_alloc_page(dev);
|
|
|
|
if(dev_priv->real_status_page == 0UL) {
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
DRM_ERROR("Can not allocate status page\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
DRM_DEBUG("Status page at %lx\n", dev_priv->real_status_page);
|
|
|
|
|
|
|
|
dev_priv->status_page =
|
|
|
|
ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page),
|
|
|
|
PAGE_SIZE);
|
|
|
|
|
|
|
|
if(dev_priv->status_page == NULL) {
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
DRM_ERROR("Can not remap status page\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG("Status page remapped to %p\n", dev_priv->status_page);
|
|
|
|
/* Write status page when secend or softrap occurs */
|
|
|
|
MGA_WRITE(MGAREG_PRIMPTR,
|
|
|
|
virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003);
|
|
|
|
|
|
|
|
dev_priv->device = pci_find_device(0x102b, 0x0525, NULL);
|
|
|
|
if(dev_priv->device == NULL) {
|
|
|
|
DRM_ERROR("Could not find pci device for card\n");
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG("dma initialization\n");
|
|
|
|
|
|
|
|
/* Private is now filled in, initialize the hardware */
|
|
|
|
{
|
|
|
|
__volatile__ unsigned int *status =
|
|
|
|
(unsigned int *)dev_priv->status_page;
|
|
|
|
PRIMLOCALS;
|
|
|
|
PRIMGETPTR( dev_priv );
|
|
|
|
|
|
|
|
dev_priv->last_sync_tag = mga_create_sync_tag(dev);
|
|
|
|
|
|
|
|
PRIMOUTREG(MGAREG_DMAPAD, 0);
|
|
|
|
PRIMOUTREG(MGAREG_DMAPAD, 0);
|
|
|
|
PRIMOUTREG(MGAREG_DWGSYNC, dev_priv->last_sync_tag);
|
|
|
|
PRIMOUTREG(MGAREG_SOFTRAP, 0);
|
|
|
|
/* Poll for the first buffer to insure that
|
|
|
|
* the status register will be correct
|
|
|
|
*/
|
|
|
|
DRM_DEBUG("phys_head : %lx\n", (unsigned long)phys_head);
|
|
|
|
status[1] = 0;
|
|
|
|
|
|
|
|
mga_flush_write_combine();
|
|
|
|
MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
|
|
|
|
|
|
|
|
MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) |
|
|
|
|
PDEA_pagpxfer_enable));
|
|
|
|
|
|
|
|
while(MGA_READ(MGAREG_DWGSYNC) != dev_priv->last_sync_tag) ;
|
|
|
|
DRM_DEBUG("status[0] after initialization : %x\n", status[0]);
|
|
|
|
DRM_DEBUG("status[1] after initialization : %x\n", status[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mga_freelist_init(dev) != 0) {
|
|
|
|
DRM_ERROR("Could not initialize freelist\n");
|
|
|
|
mga_dma_cleanup(dev);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
DRM_DEBUG("dma init was successful\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mga_dma_init(struct inode *inode, struct file *filp,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
drm_file_t *priv = filp->private_data;
|
|
|
|
drm_device_t *dev = priv->dev;
|
|
|
|
drm_mga_init_t init;
|
|
|
|
|
|
|
|
copy_from_user_ret(&init, (drm_mga_init_t *)arg, sizeof(init), -EFAULT);
|
|
|
|
|
|
|
|
switch(init.func) {
|
|
|
|
case MGA_INIT_DMA:
|
|
|
|
return mga_dma_initialize(dev, &init);
|
|
|
|
case MGA_CLEANUP_DMA:
|
|
|
|
return mga_dma_cleanup(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int mga_irq_install(drm_device_t *dev, int irq)
|
|
|
|
{
|
|
|
|
int retcode;
|
|
|
|
|
|
|
|
if (!irq) return -EINVAL;
|
|
|
|
|
|
|
|
down(&dev->struct_sem);
|
|
|
|
if (dev->irq) {
|
|
|
|
up(&dev->struct_sem);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
dev->irq = irq;
|
|
|
|
up(&dev->struct_sem);
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("install irq handler %d\n", irq);
|
2000-02-22 08:43:59 -07:00
|
|
|
|
|
|
|
dev->context_flag = 0;
|
|
|
|
dev->interrupt_flag = 0;
|
|
|
|
dev->dma_flag = 0;
|
|
|
|
dev->dma->next_buffer = NULL;
|
|
|
|
dev->dma->next_queue = NULL;
|
|
|
|
dev->dma->this_buffer = NULL;
|
|
|
|
dev->tq.next = NULL;
|
|
|
|
dev->tq.sync = 0;
|
2000-04-04 16:08:14 -06:00
|
|
|
dev->tq.routine = mga_dma_task_queue;
|
2000-02-22 08:43:59 -07:00
|
|
|
dev->tq.data = dev;
|
|
|
|
|
|
|
|
/* Before installing handler */
|
|
|
|
MGA_WRITE(MGAREG_IEN, 0);
|
|
|
|
/* Install handler */
|
|
|
|
if ((retcode = request_irq(dev->irq,
|
|
|
|
mga_dma_service,
|
|
|
|
0,
|
|
|
|
dev->devname,
|
|
|
|
dev))) {
|
|
|
|
down(&dev->struct_sem);
|
|
|
|
dev->irq = 0;
|
|
|
|
up(&dev->struct_sem);
|
|
|
|
return retcode;
|
|
|
|
}
|
|
|
|
/* After installing handler */
|
2000-04-04 16:08:14 -06:00
|
|
|
MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
|
2000-02-22 08:43:59 -07:00
|
|
|
MGA_WRITE(MGAREG_IEN, 0x00000001);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mga_irq_uninstall(drm_device_t *dev)
|
|
|
|
{
|
|
|
|
int irq;
|
|
|
|
|
|
|
|
down(&dev->struct_sem);
|
|
|
|
irq = dev->irq;
|
|
|
|
dev->irq = 0;
|
|
|
|
up(&dev->struct_sem);
|
|
|
|
|
|
|
|
if (!irq) return -EINVAL;
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("remove irq handler %d\n", irq);
|
|
|
|
MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
|
2000-02-22 08:43:59 -07:00
|
|
|
MGA_WRITE(MGAREG_IEN, 0);
|
|
|
|
free_irq(irq, dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mga_control(struct inode *inode, struct file *filp, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
drm_file_t *priv = filp->private_data;
|
|
|
|
drm_device_t *dev = priv->dev;
|
|
|
|
drm_control_t ctl;
|
|
|
|
|
|
|
|
copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
|
|
|
|
|
|
|
|
switch (ctl.func) {
|
|
|
|
case DRM_INST_HANDLER:
|
|
|
|
return mga_irq_install(dev, ctl.irq);
|
|
|
|
case DRM_UNINST_HANDLER:
|
|
|
|
return mga_irq_uninstall(dev);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
static int mga_flush_queue(drm_device_t *dev)
|
2000-02-22 08:43:59 -07:00
|
|
|
{
|
|
|
|
DECLARE_WAITQUEUE(entry, current);
|
2000-04-04 16:08:14 -06:00
|
|
|
drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
|
2000-02-22 08:43:59 -07:00
|
|
|
int ret = 0;
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
if(dev_priv == NULL) {
|
|
|
|
return 0;
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
if(dev_priv->next_prim->num_dwords != 0) {
|
|
|
|
atomic_set(&dev_priv->in_flush, 1);
|
|
|
|
current->state = TASK_INTERRUPTIBLE;
|
|
|
|
add_wait_queue(&dev_priv->flush_queue, &entry);
|
|
|
|
for (;;) {
|
|
|
|
mga_dma_schedule(dev, 0);
|
|
|
|
if (atomic_read(&dev_priv->in_flush) == 0)
|
|
|
|
break;
|
|
|
|
atomic_inc(&dev->total_sleeps);
|
|
|
|
DRM_DEBUG("Schedule in flush_queue\n");
|
|
|
|
schedule_timeout(HZ*3);
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR; /* Can't restart */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current->state = TASK_RUNNING;
|
|
|
|
remove_wait_queue(&dev_priv->flush_queue, &entry);
|
|
|
|
}
|
|
|
|
atomic_set(&dev_priv->in_flush, 0);
|
2000-02-22 08:43:59 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
/* Must be called with the lock held */
|
|
|
|
void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
|
|
|
|
{
|
|
|
|
drm_device_dma_t *dma = dev->dma;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!dma) return;
|
|
|
|
if(dev->dev_private == NULL) return;
|
2000-04-05 12:48:23 -06:00
|
|
|
if(dma->buflist == NULL) return;
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
mga_flush_queue(dev);
|
|
|
|
|
|
|
|
for (i = 0; i < dma->buf_count; i++) {
|
|
|
|
drm_buf_t *buf = dma->buflist[ i ];
|
|
|
|
drm_mga_buf_priv_t *buf_priv = buf->dev_private;
|
|
|
|
|
2000-04-05 12:48:23 -06:00
|
|
|
/* Only buffers that need to get reclaimed ever
|
|
|
|
* get set to free
|
|
|
|
*/
|
|
|
|
if (buf->pid == pid && buf_priv) {
|
2000-04-04 16:08:14 -06:00
|
|
|
if(buf_priv->my_freelist->age == MGA_BUF_USED)
|
|
|
|
buf_priv->my_freelist->age = MGA_BUF_FREE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-02-22 08:43:59 -07:00
|
|
|
int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
drm_file_t *priv = filp->private_data;
|
|
|
|
drm_device_t *dev = priv->dev;
|
|
|
|
DECLARE_WAITQUEUE(entry, current);
|
|
|
|
int ret = 0;
|
|
|
|
drm_lock_t lock;
|
|
|
|
|
|
|
|
copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
|
|
|
|
|
|
|
|
if (lock.context == DRM_KERNEL_CONTEXT) {
|
|
|
|
DRM_ERROR("Process %d using kernel context %d\n",
|
|
|
|
current->pid, lock.context);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
|
|
|
|
lock.context, current->pid, dev->lock.hw_lock->lock,
|
|
|
|
lock.flags);
|
2000-02-22 08:43:59 -07:00
|
|
|
|
|
|
|
if (lock.context < 0) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
2000-02-22 08:43:59 -07:00
|
|
|
/* Only one queue:
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
add_wait_queue(&dev->lock.lock_queue, &entry);
|
|
|
|
for (;;) {
|
|
|
|
if (!dev->lock.hw_lock) {
|
|
|
|
/* Device has been unregistered */
|
|
|
|
ret = -EINTR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (drm_lock_take(&dev->lock.hw_lock->lock,
|
|
|
|
lock.context)) {
|
|
|
|
dev->lock.pid = current->pid;
|
|
|
|
dev->lock.lock_time = jiffies;
|
|
|
|
atomic_inc(&dev->total_locks);
|
|
|
|
break; /* Got lock */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Contention */
|
|
|
|
atomic_inc(&dev->total_sleeps);
|
|
|
|
current->state = TASK_INTERRUPTIBLE;
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("Calling lock schedule\n");
|
2000-02-22 08:43:59 -07:00
|
|
|
schedule();
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current->state = TASK_RUNNING;
|
|
|
|
remove_wait_queue(&dev->lock.lock_queue, &entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
if (lock.flags & _DRM_LOCK_QUIESCENT) {
|
2000-04-04 16:08:14 -06:00
|
|
|
DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
|
|
|
|
mga_flush_queue(dev);
|
|
|
|
mga_dma_quiescent(dev);
|
2000-02-22 08:43:59 -07:00
|
|
|
}
|
|
|
|
}
|
2000-04-04 16:08:14 -06:00
|
|
|
|
|
|
|
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
|
2000-02-22 08:43:59 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2000-04-04 16:08:14 -06:00
|
|
|
int mga_flush_ioctl(struct inode *inode, struct file *filp,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
drm_file_t *priv = filp->private_data;
|
|
|
|
drm_device_t *dev = priv->dev;
|
|
|
|
drm_lock_t lock;
|
|
|
|
drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
|
|
|
|
drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
|
|
|
|
__volatile__ unsigned int *status =
|
|
|
|
(__volatile__ unsigned int *)dev_priv->status_page;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
|
|
|
|
|
|
|
|
if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
|
|
|
|
DRM_ERROR("mga_flush_ioctl called without lock held\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) {
|
|
|
|
mga_flush_queue(dev);
|
|
|
|
|
|
|
|
if((MGA_READ(MGAREG_STATUS) & 0x00030001) == 0x00020000 &&
|
|
|
|
status[1] != dev_priv->last_sync_tag)
|
|
|
|
{
|
|
|
|
DRM_DEBUG("Reseting hardware status\n");
|
|
|
|
MGA_WRITE(MGAREG_DWGSYNC, dev_priv->last_sync_tag);
|
|
|
|
|
|
|
|
while(MGA_READ(MGAREG_DWGSYNC) !=
|
|
|
|
dev_priv->last_sync_tag)
|
|
|
|
{
|
|
|
|
for(i = 0; i < 4096; i++) mga_delay();
|
|
|
|
}
|
|
|
|
|
|
|
|
status[1] =
|
|
|
|
sarea_priv->last_dispatch =
|
|
|
|
dev_priv->last_sync_tag;
|
|
|
|
} else {
|
|
|
|
sarea_priv->last_dispatch = status[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(lock.flags & _DRM_LOCK_QUIESCENT) {
|
|
|
|
mga_dma_quiescent(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|