⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mga_dma.c

📁 内核linux2.4.20,可跟rtlinux3.2打补丁 组成实时linux系统,编译内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 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 + -