summaryrefslogtreecommitdiff
path: root/shared-core/via_drv.h
blob: 86cd049d3907604e57285a2d787f124a41a056c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
 * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
 * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
 * VIA, S3 GRAPHICS, 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.
 */
#ifndef _VIA_DRV_H_
#define _VIA_DRV_H_

#include "drm_sman.h"
#define DRIVER_AUTHOR	"Various"

#define DRIVER_NAME		"via"
#define DRIVER_DESC		"VIA Unichrome / Pro"

#include "via_verifier.h"

#if defined(__linux__)
#include "via_dmablit.h"

/*
 * This define and all its references can be removed when
 * the DMA blit code has been implemented for FreeBSD.
 */
#define VIA_HAVE_DMABLIT 1
#define VIA_HAVE_CORE_MM 1
#endif

#define VIA_PCI_BUF_SIZE 60000
#define VIA_FIRE_BUF_SIZE  1024
#define VIA_NUM_IRQS 4

typedef struct drm_via_ring_buffer {
	drm_local_map_t map;
	char *virtual_start;
} drm_via_ring_buffer_t;

typedef uint32_t maskarray_t[5];

typedef struct drm_via_irq {
	atomic_t irq_received;
	uint32_t pending_mask;
	uint32_t enable_mask;
	wait_queue_head_t irq_queue;
} drm_via_irq_t;
	
typedef struct drm_via_private {
	drm_via_sarea_t *sarea_priv;
	drm_local_map_t *sarea;
	drm_local_map_t *fb;
	drm_local_map_t *mmio;
	unsigned long agpAddr;
	wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS];
	char *dma_ptr;
	unsigned int dma_low;
	unsigned int dma_high;
	unsigned int dma_offset;
	uint32_t dma_wrap;
	volatile uint32_t *last_pause_ptr;
	volatile uint32_t *hw_addr_ptr;
	drm_via_ring_buffer_t ring;
        struct timeval last_vblank;
        int last_vblank_valid;
        unsigned usec_per_vblank;
	drm_via_state_t hc_state;
	char pci_buf[VIA_PCI_BUF_SIZE];
	const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE];
	uint32_t num_fire_offsets;
	int pro_group_a;
	drm_via_irq_t via_irqs[VIA_NUM_IRQS];
	unsigned num_irqs;
	maskarray_t *irq_masks;
	uint32_t irq_enable_mask; 
	uint32_t irq_pending_mask;	
	int *irq_map;
        /* Memory manager stuff */
#ifdef VIA_HAVE_CORE_MM
	unsigned idle_fault;
	drm_sman_t sman;
	int vram_initialized;
	int agp_initialized;
        unsigned long vram_offset;
        unsigned long agp_offset;
#endif
#ifdef VIA_HAVE_DMABLIT
	drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES];
#endif
} drm_via_private_t;

enum via_family {
	VIA_OTHER = 0,
	VIA_PRO_GROUP_A,
};

/* VIA MMIO register access */
#define VIA_BASE ((dev_priv->mmio))

#define VIA_READ(reg)		DRM_READ32(VIA_BASE, reg)
#define VIA_WRITE(reg,val)	DRM_WRITE32(VIA_BASE, reg, val)
#define VIA_READ8(reg)		DRM_READ8(VIA_BASE, reg)
#define VIA_WRITE8(reg,val)	DRM_WRITE8(VIA_BASE, reg, val)

extern drm_ioctl_desc_t via_ioctls[];
extern int via_max_ioctl;

extern int via_fb_init(DRM_IOCTL_ARGS);
extern int via_mem_alloc(DRM_IOCTL_ARGS);
extern int via_mem_free(DRM_IOCTL_ARGS);
extern int via_agp_init(DRM_IOCTL_ARGS);
extern int via_map_init(DRM_IOCTL_ARGS);
extern int via_decoder_futex(DRM_IOCTL_ARGS);
extern int via_wait_irq(DRM_IOCTL_ARGS);
extern int via_dma_blit_sync( DRM_IOCTL_ARGS );
extern int via_dma_blit( DRM_IOCTL_ARGS );

extern int via_driver_load(drm_device_t *dev, unsigned long chipset);
extern int via_driver_unload(drm_device_t *dev);
extern int via_final_context(drm_device_t * dev, int context);

extern int via_do_cleanup_map(drm_device_t * dev);
extern int via_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence);

extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS);
extern void via_driver_irq_preinstall(drm_device_t * dev);
extern void via_driver_irq_postinstall(drm_device_t * dev);
extern void via_driver_irq_uninstall(drm_device_t * dev);

extern int via_dma_cleanup(drm_device_t * dev);
extern void via_init_command_verifier(void);
extern int via_driver_dma_quiescent(drm_device_t * dev);
extern void via_init_futex(drm_via_private_t *dev_priv);
extern void via_cleanup_futex(drm_via_private_t *dev_priv);
extern void via_release_futex(drm_via_private_t *dev_priv, int context);
extern int via_driver_irq_wait(drm_device_t * dev, unsigned int irq,
			       int force_sequence, unsigned int *sequence);

#ifdef VIA_HAVE_CORE_MM
extern void via_reclaim_buffers_locked(drm_device_t *dev, struct file *filp);
extern void via_lastclose(drm_device_t *dev);
#else
extern int via_init_context(drm_device_t * dev, int context);
#endif

#ifdef VIA_HAVE_DMABLIT
extern void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq);
extern void via_init_dmablit(drm_device_t *dev);
#endif

#endif
f='#n658'>658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
 */
/**************************************************************************
 * 
 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
 * All Rights Reserved.
 * 
 **************************************************************************/

#define __NO_VERSION__
#include "i915.h"
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"


static inline void i915_print_status_page(drm_device_t *dev)
{
      	drm_i915_private_t *dev_priv = dev->dev_private;
	u32 *temp = dev_priv->hw_status_page;

	if (!temp) {
		DRM_DEBUG("no status page\n");
		return;
	}

   	DRM_DEBUG(  "hw_status: Interrupt Status : %x\n", temp[0]);
   	DRM_DEBUG(  "hw_status: LpRing Head ptr : %x\n", temp[1]);
   	DRM_DEBUG(  "hw_status: IRing Head ptr : %x\n", temp[2]);
      	DRM_DEBUG(  "hw_status: Reserved : %x\n", temp[3]);
   	DRM_DEBUG(  "hw_status: Driver Counter : %d\n", temp[5]);

}




/* Really want an OS-independent resettable timer.  Would like to have
 * this loop run for (eg) 3 sec, but have the timer reset every time
 * the head pointer changes, so that EBUSY only happens if the ring
 * actually stalls for (eg) 3 seconds.
 */
int i915_wait_ring( drm_device_t *dev, int n, const char *caller )
{
   	drm_i915_private_t *dev_priv = dev->dev_private;
   	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
	u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
	int i;

	for ( i = 0 ; i < 10000 ; i++ ) {
		ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
	   	ring->space = ring->head - (ring->tail+8);
		if (ring->space < 0) ring->space += ring->Size;
		if ( ring->space >= n )
			return 0;
		
		dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;

		if (ring->head != last_head) 
			i = 0;

		last_head = ring->head;
	}

	return DRM_ERR(EBUSY);
}

void i915_kernel_lost_context(drm_device_t *dev)
{
      	drm_i915_private_t *dev_priv = dev->dev_private;
   	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
      
   	ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
     	ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR;
     	ring->space = ring->head - (ring->tail+8);
     	if (ring->space < 0) ring->space += ring->Size;

	if (ring->head == ring->tail)
		dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}


int i915_dma_cleanup(drm_device_t *dev)
{
	/* Make sure interrupts are disabled here because the uninstall ioctl
	 * may not have been called from userspace and after dev_private
	 * is freed, it's too late.
	 */
	if (dev->irq) DRM(irq_uninstall)(dev);

	if (dev->dev_private) {
	   	drm_i915_private_t *dev_priv = 
	     		(drm_i915_private_t *) dev->dev_private;
	   
	   	if (dev_priv->ring.virtual_start) {
		   	drm_core_ioremapfree( &dev_priv->ring.map, dev);
		}

	   	if (dev_priv->hw_status_page) {
#ifdef __FreeBSD__
#if __FreeBSD_version > 500000	
			contigfree(dev_priv->hw_status_page, PAGE_SIZE, DRM(M_DRM));
#endif
#else
			pci_free_consistent(dev->pdev, PAGE_SIZE,
				      dev_priv->hw_status_page,
				      dev_priv->dma_status_page);
#endif
		   	/* Need to rewrite hardware status page */
		   	I915_WRITE(0x02080, 0x1ffff000);
		}

	   	DRM(free)(dev->dev_private, sizeof(drm_i915_private_t), 
			 DRM_MEM_DRIVER);

	   	dev->dev_private = NULL;
	}

   	return 0;
}



static int i915_initialize(drm_device_t *dev, 
			       drm_i915_private_t *dev_priv,
			       drm_i915_init_t *init)
{
   	memset(dev_priv, 0, sizeof(drm_i915_private_t));

	DRM_GETSAREA();
	if(!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
		dev->dev_private = (void *)dev_priv;
		i915_dma_cleanup(dev);
		return DRM_ERR(EINVAL);
	}
	
	dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);
	if(!dev_priv->mmio_map) {
		dev->dev_private = (void *)dev_priv;
		i915_dma_cleanup(dev);
		DRM_ERROR("can not find mmio map!\n");
		return DRM_ERR(EINVAL);
	}

	dev_priv->sarea_priv = (drm_i915_sarea_t *)
		((u8 *)dev_priv->sarea->handle +
		 init->sarea_priv_offset);

   	dev_priv->ring.Start = init->ring_start;
   	dev_priv->ring.End = init->ring_end;
   	dev_priv->ring.Size = init->ring_size;
   	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
		
	dev_priv->ring.map.offset = init->ring_start;
	dev_priv->ring.map.size = init->ring_size;
	dev_priv->ring.map.type = 0;
	dev_priv->ring.map.flags = 0;
	dev_priv->ring.map.mtrr = 0;

	drm_core_ioremap( &dev_priv->ring.map, dev );

   	if (dev_priv->ring.map.handle == NULL) {
		dev->dev_private = (void *) dev_priv;
	   	i915_dma_cleanup(dev);
	   	DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
	   	return DRM_ERR(ENOMEM);
	}

   	dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
   
	dev_priv->back_offset = init->back_offset;
	dev_priv->front_offset = init->front_offset;
	dev_priv->current_page = 0;
	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;

	/* We are using separate values as placeholders for mechanisms for
	 * private backbuffer/depthbuffer usage.
	 */
	dev_priv->use_mi_batchbuffer_start = 0;

	/* Allow hardware batchbuffers unless told otherwise.
	 */
	dev_priv->allow_batchbuffer = 1;

   	/* Program Hardware Status Page */
#ifdef __FreeBSD__
   	dev_priv->hw_status_page = contigmalloc(PAGE_SIZE, DRM(M_DRM), M_NOWAIT, 0ul, 0, 0, 0);
	dev_priv->dma_status_page = vtophys(dev_priv->hw_status_page);	
#else
   	dev_priv->hw_status_page =
		pci_alloc_consistent( dev->pdev, PAGE_SIZE, 
						&dev_priv->dma_status_page );
#endif

   	if (!dev_priv->hw_status_page) {
		dev->dev_private = (void *)dev_priv;
		i915_dma_cleanup(dev);
		DRM_ERROR("Can not allocate hardware status page\n");
		return DRM_ERR(ENOMEM);
	}
   	memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
	DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
   
   	I915_WRITE(0x02080, dev_priv->dma_status_page);
	DRM_DEBUG("Enabled hardware status page\n");
   
	dev->dev_private = (void *)dev_priv;

   	return 0;
}


static int i915_resume(drm_device_t *dev)
{
	drm_i915_private_t *dev_priv = 
		(drm_i915_private_t *) dev->dev_private;

	DRM_DEBUG( "%s\n", __FUNCTION__);
		   
	if(!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
		return DRM_ERR(EINVAL);
	}

	if(!dev_priv->mmio_map) {
		DRM_ERROR("can not find mmio map!\n");
		return DRM_ERR(EINVAL);
	}

   	if (dev_priv->ring.map.handle == NULL) {
	   	DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
	   	return DRM_ERR(ENOMEM);
	}

   	/* Program Hardware Status Page */
   	if (!dev_priv->hw_status_page) {
		DRM_ERROR("Can not find hardware status page\n");
		return DRM_ERR(EINVAL);
	}
	DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
   
   	I915_WRITE(0x02080, dev_priv->dma_status_page);
	DRM_DEBUG("Enabled hardware status page\n");

   	return 0;
}


int i915_dma_init( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
   	drm_i915_private_t *dev_priv;
   	drm_i915_init_t init;
   	int retcode = 0;
	
  	DRM_COPY_FROM_USER_IOCTL( init, (drm_i915_init_t __user *)data, sizeof(init));
	
   	switch(init.func) {
	 	case I915_INIT_DMA:
			dev_priv = DRM(alloc)(sizeof(drm_i915_private_t), 
					      DRM_MEM_DRIVER);
	   		if(dev_priv == NULL) 
				return DRM_ERR(ENOMEM);
	   		retcode = i915_initialize(dev, dev_priv, &init);
	   	break;
	 	case I915_CLEANUP_DMA:
	   		retcode = i915_dma_cleanup(dev);
	   	break;
		case I915_RESUME_DMA:
			retcode = i915_resume(dev);
	   	break;
	 	default:
	   		retcode = -EINVAL;
	   	break;
	}
   
   	return retcode;
}



/* Implement basically the same security restrictions as hardware does
 * for MI_BATCH_NON_SECURE.  These can be made stricter at any time.
 *
 * Most of the calculations below involve calculating the size of a
 * particular instruction.  It's important to get the size right as
 * that tells us where the next instruction to check is.  Any illegal
 * instruction detected will be given a size of zero, which is a
 * signal to abort the rest of the buffer.
 */
static int do_validate_cmd( int cmd )
{
	switch (((cmd>>29) & 0x7)) {
	case 0x0:
		switch ((cmd>>23) & 0x3f) {
		case 0x0: 
			return 1; /* MI_NOOP */
		case 0x4: 
			return 1; /* MI_FLUSH */
		default: 
			return 0; /* disallow everything else */
		}
		break;
	case 0x1: 
		return 0;	/* reserved */
	case 0x2: 
		return (cmd & 0xff) + 2; /* 2d commands */
	case 0x3:
		if (((cmd>>24) & 0x1f) <= 0x18)
			return 1;

		switch ((cmd>>24) & 0x1f) {
		case 0x1c: 
			return 1;
		case 0x1d:
			switch ((cmd>>16)&0xff) {
			case 0x3: 
				return (cmd & 0x1f) + 2;
			case 0x4: 
				return (cmd & 0xf) + 2;
			default:  
				return (cmd & 0xffff) + 2;
			}
		case 0x1e: 
			if (cmd & (1<<23)) 
				return (cmd & 0xffff) + 1;
			else
				return 1;
		case 0x1f:
			if ((cmd & (1<<23)) == 0) /* inline vertices */
				return (cmd & 0x1ffff) + 2;
			else if (cmd & (1<<17)) /* indirect random */
				if ((cmd & 0xffff) == 0)
					return 0; /* unknown length, too hard */
				else
					return (((cmd & 0xffff) + 1) / 2) + 1;
			else
				return 2; /* indirect sequential */
		default: 
			return 0;
		}
	default:
		return 0;
	}

	return 0;
}

static int validate_cmd( int cmd )
{
	int ret = do_validate_cmd( cmd );
	
/* 	printk("validate_cmd( %x ): %d\n", cmd, ret); */

	return ret;
}
	
	

static int i915_emit_cmds( drm_device_t *dev,
			   int __user *buffer,
			   int dwords )
{
   	drm_i915_private_t *dev_priv = dev->dev_private;
	int i;
	RING_LOCALS;

	for (i = 0 ; i < dwords ; ) {
		int cmd, sz;

		if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))
			return DRM_ERR( EINVAL );

/* 		printk("%d/%d ", i, dwords); */

		if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) 
			return DRM_ERR( EINVAL );

		BEGIN_LP_RING( sz );
		OUT_RING(cmd);

		while (++i, --sz) {
			if (DRM_COPY_FROM_USER_UNCHECKED( &cmd, &buffer[i], 
							  sizeof(cmd))) {
				return DRM_ERR( EINVAL );
			}
			OUT_RING(cmd);
		}
		ADVANCE_LP_RING();
	}

	return 0;
}

static int i915_emit_box( drm_device_t *dev,
			  drm_clip_rect_t __user *boxes,
			  int i,
			  int DR1,
			  int DR4)
{
   	drm_i915_private_t *dev_priv = dev->dev_private;
   	drm_clip_rect_t box;
   	RING_LOCALS;

	if (DRM_COPY_FROM_USER_UNCHECKED( &box, &boxes[i], sizeof(box) )) {
		return EFAULT;
	}

	if (box.y2 <= box.y1 ||
	    box.x2 <= box.x1 ||
	    box.y2 <= 0 ||
	    box.x2 <= 0) {
		DRM_ERROR("Bad box %d,%d..%d,%d\n",
			  box.x1, box.y1, box.x2, box.y2);
		return DRM_ERR(EINVAL);
	}
				

	BEGIN_LP_RING(6);
	OUT_RING( GFX_OP_DRAWRECT_INFO );
	OUT_RING( DR1 );
	OUT_RING( (box.x1 & 0xffff) | (box.y1<<16) );
	OUT_RING( ((box.x2-1) & 0xffff) | ((box.y2-1)<<16) );
	OUT_RING( DR4 );
	OUT_RING( 0 );
	ADVANCE_LP_RING();
	
	return 0;
}


static int i915_dispatch_cmdbuffer(drm_device_t *dev, 
				   drm_i915_cmdbuffer_t *cmd )
{
   	int nbox = cmd->num_cliprects;
	int i = 0, count, ret;

	if (cmd->sz & 0x3) {
		DRM_ERROR("alignment");
		return DRM_ERR(EINVAL);
	}
		
   	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;

	for (i = 0 ; i < count ; i++) {
		if (i < nbox) {
			ret = i915_emit_box( dev, cmd->cliprects, i, 
					     cmd->DR1, cmd->DR4);
			if (ret) 
				return ret;
		}

		ret = i915_emit_cmds( dev, (int __user *)cmd->buf, cmd->sz / 4 );
		if (ret) 
			return ret;
	}

	return 0;
}




static int i915_dispatch_batchbuffer(drm_device_t *dev, 
				    drm_i915_batchbuffer_t *batch )
{
   	drm_i915_private_t *dev_priv = dev->dev_private;
   	drm_clip_rect_t __user *boxes = batch->cliprects;
   	int nbox = batch->num_cliprects;
	int i = 0, count;
   	RING_LOCALS;

	if ((batch->start | batch->used) & 0x7) {
		DRM_ERROR("alignment");
		return DRM_ERR(EINVAL);
	}
		
   	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;

	for (i = 0 ; i < count ; i++) {
		if (i < nbox) {
			int ret = i915_emit_box( dev, boxes, i,
						 batch->DR1, batch->DR4);
			if (ret) 
				return ret;
		}

		if (dev_priv->use_mi_batchbuffer_start) {
			BEGIN_LP_RING(2);
			OUT_RING( MI_BATCH_BUFFER_START | (2<<6) );
			OUT_RING( batch->start | MI_BATCH_NON_SECURE );
			ADVANCE_LP_RING();
		} 
		else {
			BEGIN_LP_RING(4);
			OUT_RING( MI_BATCH_BUFFER );
			OUT_RING( batch->start | MI_BATCH_NON_SECURE );
			OUT_RING( batch->start + batch->used - 4 );
			OUT_RING( 0 );
			ADVANCE_LP_RING();
		}
	}

	
	dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;

	BEGIN_LP_RING(4);
	OUT_RING( CMD_STORE_DWORD_IDX );
	OUT_RING( 20 );
	OUT_RING( dev_priv->counter );
	OUT_RING( 0 );
	ADVANCE_LP_RING();

	return 0;
}

static int i915_dispatch_flip( drm_device_t *dev )
{
   	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

	DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n", 
		   __FUNCTION__, 
		   dev_priv->current_page,
		   dev_priv->sarea_priv->pf_current_page);

  	i915_kernel_lost_context(dev);


	BEGIN_LP_RING( 2 );
    	OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); 
	OUT_RING( 0 );
	ADVANCE_LP_RING();

	BEGIN_LP_RING( 6 );
	OUT_RING( CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP );	
	OUT_RING( 0 );
	if ( dev_priv->current_page == 0 ) {
		OUT_RING( dev_priv->back_offset );
		dev_priv->current_page = 1;
	} else {
		OUT_RING( dev_priv->front_offset );
		dev_priv->current_page = 0;
	}
	OUT_RING(0);
	ADVANCE_LP_RING();


	BEGIN_LP_RING( 2 );
	OUT_RING( MI_WAIT_FOR_EVENT |
		  MI_WAIT_FOR_PLANE_A_FLIP );
	OUT_RING( 0 );
	ADVANCE_LP_RING();
	

	dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;

	BEGIN_LP_RING(4);
	OUT_RING( CMD_STORE_DWORD_IDX );
	OUT_RING( 20 );
	OUT_RING( dev_priv->counter );
	OUT_RING( 0 );
	ADVANCE_LP_RING();

	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
	return 0;
}


static int i915_quiescent(drm_device_t *dev)
{
   	drm_i915_private_t *dev_priv = dev->dev_private;

  	i915_kernel_lost_context(dev);
	return i915_wait_ring( dev, dev_priv->ring.Size - 8, __FUNCTION__ );
}


int i915_flush_ioctl( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;

   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
		DRM_ERROR("i915_flush_ioctl called without lock held\n");
		return DRM_ERR(EINVAL);
	}

    	return i915_quiescent(dev); 
}

int i915_batchbuffer( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
   	drm_i915_private_t *dev_priv = (drm_i915_private_t *)dev->dev_private;
      	u32 *hw_status = dev_priv->hw_status_page;
   	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) 
     					dev_priv->sarea_priv; 
	drm_i915_batchbuffer_t batch;
	int ret;

	if (!dev_priv->allow_batchbuffer) {
		DRM_ERROR("Batchbuffer ioctl disabled\n");
		return DRM_ERR(EINVAL);
	}

	DRM_COPY_FROM_USER_IOCTL( batch, (drm_i915_batchbuffer_t __user *)data, 
				  sizeof(batch) );

	DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
		  batch.start, batch.used, batch.num_cliprects);


   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
		DRM_ERROR("i915_batchbuffer called without lock held\n");
		return DRM_ERR(EINVAL);
	}

	if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects, 
						       batch.num_cliprects *
						       sizeof(drm_clip_rect_t)))
		return DRM_ERR(EFAULT);

	ret = i915_dispatch_batchbuffer( dev, &batch );

   	sarea_priv->last_dispatch = (int) hw_status[5];   
	return ret;
}

int i915_cmdbuffer( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
   	drm_i915_private_t *dev_priv = (drm_i915_private_t *)dev->dev_private;
      	u32 *hw_status = dev_priv->hw_status_page;
   	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) 
		dev_priv->sarea_priv; 
	drm_i915_cmdbuffer_t cmdbuf;
	int ret;

	DRM_COPY_FROM_USER_IOCTL( cmdbuf, (drm_i915_cmdbuffer_t __user *)data, 
				  sizeof(cmdbuf) );

	DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
		  cmdbuf.buf, cmdbuf.sz, cmdbuf.num_cliprects);


   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
		DRM_ERROR("i915_cmdbuffer called without lock held\n");
		return DRM_ERR(EINVAL);
	}

	if (cmdbuf.num_cliprects && 
	    DRM_VERIFYAREA_READ(cmdbuf.cliprects, 
				cmdbuf.num_cliprects *
				sizeof(drm_clip_rect_t))) {
		DRM_ERROR("Fault accessing cliprects\n");
		return DRM_ERR(EFAULT);
	}

	ret = i915_dispatch_cmdbuffer( dev, &cmdbuf );
	if (ret) {
		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
		return ret;
	}

   	sarea_priv->last_dispatch = (int) hw_status[5];   
	return 0;
}



int i915_do_cleanup_pageflip( drm_device_t *dev )
{
	drm_i915_private_t *dev_priv = dev->dev_private;

	DRM_DEBUG("%s\n", __FUNCTION__);
	if (dev_priv->current_page != 0)
		i915_dispatch_flip( dev );

	return 0;
}

int i915_flip_bufs( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;

	DRM_DEBUG("%s\n", __FUNCTION__);
   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
		DRM_ERROR("i915_flip_buf called without lock held\n");
		return DRM_ERR(EINVAL);
	}

	return i915_dispatch_flip( dev );
}



int i915_getparam( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_getparam_t param;
	int value;

	if ( !dev_priv ) {
		DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
		return DRM_ERR(EINVAL);
	}

	DRM_COPY_FROM_USER_IOCTL(param, (drm_i915_getparam_t __user *)data, 
				 sizeof(param));

	switch( param.param ) {
	case I915_PARAM_IRQ_ACTIVE:
		value = dev->irq ? 1 : 0;
		break;
	case I915_PARAM_ALLOW_BATCHBUFFER:
		value = dev_priv->allow_batchbuffer ? 1 : 0;
		break;
	default:
		DRM_ERROR("Unkown parameter %d\n", param.param);
		return DRM_ERR(EINVAL);
	}

	if ( DRM_COPY_TO_USER( param.value, &value, sizeof(int) ) ) {
		DRM_ERROR("DRM_COPY_TO_USER failed\n");
		return DRM_ERR(EFAULT);
	}
	
	return 0;
}


int i915_setparam( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_setparam_t param;

	if ( !dev_priv ) {
		DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
		return DRM_ERR(EINVAL);
	}

	DRM_COPY_FROM_USER_IOCTL( param, (drm_i915_setparam_t __user *)data, 
				  sizeof(param) );

	switch( param.param ) {
	case I915_SETPARAM_USE_MI_BATCHBUFFER_START:
		dev_priv->use_mi_batchbuffer_start = param.value;
		break;
	case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY:
		dev_priv->tex_lru_log_granularity = param.value;
		break;
	case I915_SETPARAM_ALLOW_BATCHBUFFER:
		dev_priv->allow_batchbuffer = param.value;
		break;
	default:
		DRM_ERROR("unknown parameter %d\n", param.param);
		return DRM_ERR(EINVAL);
	}


	return 0;
}

static void i915_driver_pretakedown(drm_device_t *dev)
{
	if ( dev->dev_private ) {
		drm_i915_private_t *dev_priv = dev->dev_private;
	        i915_mem_takedown( &(dev_priv->agp_heap) );
 	}
	i915_dma_cleanup( dev );
}

static void i915_driver_prerelease(drm_device_t *dev, DRMFILE filp)
{
	if ( dev->dev_private ) {
		drm_i915_private_t *dev_priv = dev->dev_private;
                i915_mem_release( dev, filp, dev_priv->agp_heap );
	}
}

void i915_driver_register_fns(drm_device_t *dev)
{
	dev->driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED;
	dev->fn_tbl.pretakedown = i915_driver_pretakedown;
	dev->fn_tbl.prerelease = i915_driver_prerelease;
	dev->fn_tbl.irq_preinstall = i915_driver_irq_preinstall;
	dev->fn_tbl.irq_postinstall = i915_driver_irq_postinstall;
	dev->fn_tbl.irq_uninstall = i915_driver_irq_uninstall;
	dev->fn_tbl.irq_handler = i915_driver_irq_handler;
}