mga_dma.c revision 121447
1/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
2 * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
3 *
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Authors:
28 *    Rickard E. (Rik) Faith <faith@valinux.com>
29 *    Jeff Hartmann <jhartmann@valinux.com>
30 *    Keith Whitwell <keith@tungstengraphics.com>
31 *
32 * Rewritten by:
33 *    Gareth Hughes <gareth@valinux.com>
34 *
35 * $FreeBSD: head/sys/dev/drm/mga_dma.c 121447 2003-10-24 01:48:17Z anholt $
36 */
37
38#include "dev/drm/mga.h"
39#include "dev/drm/drmP.h"
40#include "dev/drm/drm.h"
41#include "dev/drm/mga_drm.h"
42#include "dev/drm/mga_drv.h"
43
44#define MGA_DEFAULT_USEC_TIMEOUT	10000
45#define MGA_FREELIST_DEBUG		0
46
47
48/* ================================================================
49 * Engine control
50 */
51
52int mga_do_wait_for_idle( drm_mga_private_t *dev_priv )
53{
54	u32 status = 0;
55	int i;
56	DRM_DEBUG( "\n" );
57
58	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
59		status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK;
60		if ( status == MGA_ENDPRDMASTS ) {
61			MGA_WRITE8( MGA_CRTC_INDEX, 0 );
62			return 0;
63		}
64		DRM_UDELAY( 1 );
65	}
66
67#if MGA_DMA_DEBUG
68	DRM_ERROR( "failed!\n" );
69	DRM_INFO( "   status=0x%08x\n", status );
70#endif
71	return DRM_ERR(EBUSY);
72}
73
74int mga_do_dma_idle( drm_mga_private_t *dev_priv )
75{
76	u32 status = 0;
77	int i;
78	DRM_DEBUG( "\n" );
79
80	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
81		status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK;
82		if ( status == MGA_ENDPRDMASTS ) return 0;
83		DRM_UDELAY( 1 );
84	}
85
86#if MGA_DMA_DEBUG
87	DRM_ERROR( "failed! status=0x%08x\n", status );
88#endif
89	return DRM_ERR(EBUSY);
90}
91
92int mga_do_dma_reset( drm_mga_private_t *dev_priv )
93{
94	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
95	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
96
97	DRM_DEBUG( "\n" );
98
99	/* The primary DMA stream should look like new right about now.
100	 */
101	primary->tail = 0;
102	primary->space = primary->size;
103	primary->last_flush = 0;
104
105	sarea_priv->last_wrap = 0;
106
107	/* FIXME: Reset counters, buffer ages etc...
108	 */
109
110	/* FIXME: What else do we need to reinitialize?  WARP stuff?
111	 */
112
113	return 0;
114}
115
116int mga_do_engine_reset( drm_mga_private_t *dev_priv )
117{
118	DRM_DEBUG( "\n" );
119
120	/* Okay, so we've completely screwed up and locked the engine.
121	 * How about we clean up after ourselves?
122	 */
123	MGA_WRITE( MGA_RST, MGA_SOFTRESET );
124	DRM_UDELAY( 15 );				/* Wait at least 10 usecs */
125	MGA_WRITE( MGA_RST, 0 );
126
127	/* Initialize the registers that get clobbered by the soft
128	 * reset.  Many of the core register values survive a reset,
129	 * but the drawing registers are basically all gone.
130	 *
131	 * 3D clients should probably die after calling this.  The X
132	 * server should reset the engine state to known values.
133	 */
134#if 0
135	MGA_WRITE( MGA_PRIMPTR,
136		   virt_to_bus((void *)dev_priv->prim.status_page) |
137		   MGA_PRIMPTREN0 |
138		   MGA_PRIMPTREN1 );
139#endif
140
141	MGA_WRITE( MGA_ICLEAR, MGA_SOFTRAPICLR );
142	MGA_WRITE( MGA_IEN,    MGA_SOFTRAPIEN );
143
144	/* The primary DMA stream should look like new right about now.
145	 */
146	mga_do_dma_reset( dev_priv );
147
148	/* This bad boy will never fail.
149	 */
150	return 0;
151}
152
153
154/* ================================================================
155 * Primary DMA stream
156 */
157
158void mga_do_dma_flush( drm_mga_private_t *dev_priv )
159{
160	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
161	u32 head, tail;
162	u32 status = 0;
163	int i;
164 	DMA_LOCALS;
165	DRM_DEBUG( "\n" );
166
167        /* We need to wait so that we can do an safe flush */
168	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
169		status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK;
170		if ( status == MGA_ENDPRDMASTS ) break;
171		DRM_UDELAY( 1 );
172	}
173
174	if ( primary->tail == primary->last_flush ) {
175		DRM_DEBUG( "   bailing out...\n" );
176		return;
177	}
178
179	tail = primary->tail + dev_priv->primary->offset;
180
181	/* We need to pad the stream between flushes, as the card
182	 * actually (partially?) reads the first of these commands.
183	 * See page 4-16 in the G400 manual, middle of the page or so.
184	 */
185	BEGIN_DMA( 1 );
186
187	DMA_BLOCK( MGA_DMAPAD,  0x00000000,
188		   MGA_DMAPAD,  0x00000000,
189		   MGA_DMAPAD,  0x00000000,
190		   MGA_DMAPAD,	0x00000000 );
191
192	ADVANCE_DMA();
193
194	primary->last_flush = primary->tail;
195
196	head = MGA_READ( MGA_PRIMADDRESS );
197
198	if ( head <= tail ) {
199		primary->space = primary->size - primary->tail;
200	} else {
201		primary->space = head - tail;
202	}
203
204	DRM_DEBUG( "   head = 0x%06lx\n", head - dev_priv->primary->offset );
205	DRM_DEBUG( "   tail = 0x%06lx\n", tail - dev_priv->primary->offset );
206	DRM_DEBUG( "  space = 0x%06x\n", primary->space );
207
208	mga_flush_write_combine();
209	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER );
210
211	DRM_DEBUG( "done.\n" );
212}
213
214void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv )
215{
216	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
217	u32 head, tail;
218	DMA_LOCALS;
219	DRM_DEBUG( "\n" );
220
221	BEGIN_DMA_WRAP();
222
223	DMA_BLOCK( MGA_DMAPAD,	0x00000000,
224		   MGA_DMAPAD,	0x00000000,
225		   MGA_DMAPAD,	0x00000000,
226		   MGA_DMAPAD,	0x00000000 );
227
228	ADVANCE_DMA();
229
230	tail = primary->tail + dev_priv->primary->offset;
231
232	primary->tail = 0;
233	primary->last_flush = 0;
234	primary->last_wrap++;
235
236	head = MGA_READ( MGA_PRIMADDRESS );
237
238	if ( head == dev_priv->primary->offset ) {
239		primary->space = primary->size;
240	} else {
241		primary->space = head - dev_priv->primary->offset;
242	}
243
244	DRM_DEBUG( "   head = 0x%06lx\n",
245		  head - dev_priv->primary->offset );
246	DRM_DEBUG( "   tail = 0x%06x\n", primary->tail );
247	DRM_DEBUG( "   wrap = %d\n", primary->last_wrap );
248	DRM_DEBUG( "  space = 0x%06x\n", primary->space );
249
250	mga_flush_write_combine();
251	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER );
252
253	set_bit( 0, &primary->wrapped );
254	DRM_DEBUG( "done.\n" );
255}
256
257void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv )
258{
259	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
260	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
261	u32 head = dev_priv->primary->offset;
262	DRM_DEBUG( "\n" );
263
264	sarea_priv->last_wrap++;
265	DRM_DEBUG( "   wrap = %d\n", sarea_priv->last_wrap );
266
267	mga_flush_write_combine();
268	MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL );
269
270	clear_bit( 0, &primary->wrapped );
271	DRM_DEBUG( "done.\n" );
272}
273
274
275/* ================================================================
276 * Freelist management
277 */
278
279#define MGA_BUFFER_USED		~0
280#define MGA_BUFFER_FREE		0
281
282#if MGA_FREELIST_DEBUG
283static void mga_freelist_print( drm_device_t *dev )
284{
285	drm_mga_private_t *dev_priv = dev->dev_private;
286	drm_mga_freelist_t *entry;
287
288	DRM_INFO( "\n" );
289	DRM_INFO( "current dispatch: last=0x%x done=0x%x\n",
290		  dev_priv->sarea_priv->last_dispatch,
291		  (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) -
292				 dev_priv->primary->offset) );
293	DRM_INFO( "current freelist:\n" );
294
295	for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) {
296		DRM_INFO( "   %p   idx=%2d  age=0x%x 0x%06lx\n",
297			  entry, entry->buf->idx, entry->age.head,
298			  entry->age.head - dev_priv->primary->offset );
299	}
300	DRM_INFO( "\n" );
301}
302#endif
303
304static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv )
305{
306	drm_device_dma_t *dma = dev->dma;
307	drm_buf_t *buf;
308	drm_mga_buf_priv_t *buf_priv;
309	drm_mga_freelist_t *entry;
310	int i;
311	DRM_DEBUG( "count=%d\n", dma->buf_count );
312
313	dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t),
314				     DRM_MEM_DRIVER );
315	if ( dev_priv->head == NULL )
316		return DRM_ERR(ENOMEM);
317
318	memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) );
319	SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 );
320
321	for ( i = 0 ; i < dma->buf_count ; i++ ) {
322		buf = dma->buflist[i];
323	        buf_priv = buf->dev_private;
324
325		entry = DRM(alloc)( sizeof(drm_mga_freelist_t),
326				    DRM_MEM_DRIVER );
327		if ( entry == NULL )
328			return DRM_ERR(ENOMEM);
329
330		memset( entry, 0, sizeof(drm_mga_freelist_t) );
331
332		entry->next = dev_priv->head->next;
333		entry->prev = dev_priv->head;
334		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 );
335		entry->buf = buf;
336
337		if ( dev_priv->head->next != NULL )
338			dev_priv->head->next->prev = entry;
339		if ( entry->next == NULL )
340			dev_priv->tail = entry;
341
342		buf_priv->list_entry = entry;
343		buf_priv->discard = 0;
344		buf_priv->dispatched = 0;
345
346		dev_priv->head->next = entry;
347	}
348
349	return 0;
350}
351
352static void mga_freelist_cleanup( drm_device_t *dev )
353{
354	drm_mga_private_t *dev_priv = dev->dev_private;
355	drm_mga_freelist_t *entry;
356	drm_mga_freelist_t *next;
357	DRM_DEBUG( "\n" );
358
359	entry = dev_priv->head;
360	while ( entry ) {
361		next = entry->next;
362		DRM(free)( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER );
363		entry = next;
364	}
365
366	dev_priv->head = dev_priv->tail = NULL;
367}
368
369#if 0
370/* FIXME: Still needed?
371 */
372static void mga_freelist_reset( drm_device_t *dev )
373{
374	drm_device_dma_t *dma = dev->dma;
375	drm_buf_t *buf;
376	drm_mga_buf_priv_t *buf_priv;
377	int i;
378
379	for ( i = 0 ; i < dma->buf_count ; i++ ) {
380		buf = dma->buflist[i];
381	        buf_priv = buf->dev_private;
382		SET_AGE( &buf_priv->list_entry->age,
383			 MGA_BUFFER_FREE, 0 );
384	}
385}
386#endif
387
388static drm_buf_t *mga_freelist_get( drm_device_t *dev )
389{
390	drm_mga_private_t *dev_priv = dev->dev_private;
391	drm_mga_freelist_t *next;
392	drm_mga_freelist_t *prev;
393	drm_mga_freelist_t *tail = dev_priv->tail;
394	u32 head, wrap;
395	DRM_DEBUG( "\n" );
396
397	head = MGA_READ( MGA_PRIMADDRESS );
398	wrap = dev_priv->sarea_priv->last_wrap;
399
400	DRM_DEBUG( "   tail=0x%06lx %d\n",
401		   tail->age.head ?
402		   tail->age.head - dev_priv->primary->offset : 0,
403		   tail->age.wrap );
404	DRM_DEBUG( "   head=0x%06lx %d\n",
405		   head - dev_priv->primary->offset, wrap );
406
407	if ( TEST_AGE( &tail->age, head, wrap ) ) {
408		prev = dev_priv->tail->prev;
409		next = dev_priv->tail;
410		prev->next = NULL;
411		next->prev = next->next = NULL;
412		dev_priv->tail = prev;
413		SET_AGE( &next->age, MGA_BUFFER_USED, 0 );
414		return next->buf;
415	}
416
417	DRM_DEBUG( "returning NULL!\n" );
418	return NULL;
419}
420
421int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf )
422{
423	drm_mga_private_t *dev_priv = dev->dev_private;
424	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
425	drm_mga_freelist_t *head, *entry, *prev;
426
427	DRM_DEBUG( "age=0x%06lx wrap=%d\n",
428		   buf_priv->list_entry->age.head -
429		   dev_priv->primary->offset,
430		   buf_priv->list_entry->age.wrap );
431
432	entry = buf_priv->list_entry;
433	head = dev_priv->head;
434
435	if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) {
436		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 );
437		prev = dev_priv->tail;
438		prev->next = entry;
439		entry->prev = prev;
440		entry->next = NULL;
441	} else {
442		prev = head->next;
443		head->next = entry;
444		prev->prev = entry;
445		entry->prev = head;
446		entry->next = prev;
447	}
448
449	return 0;
450}
451
452
453/* ================================================================
454 * DMA initialization, cleanup
455 */
456
457static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init )
458{
459	drm_mga_private_t *dev_priv;
460	int ret;
461	DRM_DEBUG( "\n" );
462
463	dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER );
464	if ( !dev_priv )
465		return DRM_ERR(ENOMEM);
466
467	memset( dev_priv, 0, sizeof(drm_mga_private_t) );
468
469	dev_priv->chipset = init->chipset;
470
471	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
472
473	if ( init->sgram ) {
474		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK;
475	} else {
476		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;
477	}
478	dev_priv->maccess	= init->maccess;
479
480	dev_priv->fb_cpp	= init->fb_cpp;
481	dev_priv->front_offset	= init->front_offset;
482	dev_priv->front_pitch	= init->front_pitch;
483	dev_priv->back_offset	= init->back_offset;
484	dev_priv->back_pitch	= init->back_pitch;
485
486	dev_priv->depth_cpp	= init->depth_cpp;
487	dev_priv->depth_offset	= init->depth_offset;
488	dev_priv->depth_pitch	= init->depth_pitch;
489
490	/* FIXME: Need to support AGP textures...
491	 */
492	dev_priv->texture_offset = init->texture_offset[0];
493	dev_priv->texture_size = init->texture_size[0];
494
495	DRM_GETSAREA();
496
497	if(!dev_priv->sarea) {
498		DRM_ERROR( "failed to find sarea!\n" );
499		/* Assign dev_private so we can do cleanup. */
500		dev->dev_private = (void *)dev_priv;
501		mga_do_cleanup_dma( dev );
502		return DRM_ERR(EINVAL);
503	}
504
505	DRM_FIND_MAP( dev_priv->fb, init->fb_offset );
506	if(!dev_priv->fb) {
507		DRM_ERROR( "failed to find framebuffer!\n" );
508		/* Assign dev_private so we can do cleanup. */
509		dev->dev_private = (void *)dev_priv;
510		mga_do_cleanup_dma( dev );
511		return DRM_ERR(EINVAL);
512	}
513	DRM_FIND_MAP( dev_priv->mmio, init->mmio_offset );
514	if(!dev_priv->mmio) {
515		DRM_ERROR( "failed to find mmio region!\n" );
516		/* Assign dev_private so we can do cleanup. */
517		dev->dev_private = (void *)dev_priv;
518		mga_do_cleanup_dma( dev );
519		return DRM_ERR(EINVAL);
520	}
521	DRM_FIND_MAP( dev_priv->status, init->status_offset );
522	if(!dev_priv->status) {
523		DRM_ERROR( "failed to find status page!\n" );
524		/* Assign dev_private so we can do cleanup. */
525		dev->dev_private = (void *)dev_priv;
526		mga_do_cleanup_dma( dev );
527		return DRM_ERR(EINVAL);
528	}
529
530	DRM_FIND_MAP( dev_priv->warp, init->warp_offset );
531	if(!dev_priv->warp) {
532		DRM_ERROR( "failed to find warp microcode region!\n" );
533		/* Assign dev_private so we can do cleanup. */
534		dev->dev_private = (void *)dev_priv;
535		mga_do_cleanup_dma( dev );
536		return DRM_ERR(EINVAL);
537	}
538	DRM_FIND_MAP( dev_priv->primary, init->primary_offset );
539	if(!dev_priv->primary) {
540		DRM_ERROR( "failed to find primary dma region!\n" );
541		/* Assign dev_private so we can do cleanup. */
542		dev->dev_private = (void *)dev_priv;
543		mga_do_cleanup_dma( dev );
544		return DRM_ERR(EINVAL);
545	}
546	DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset );
547	if(!dev_priv->buffers) {
548		DRM_ERROR( "failed to find dma buffer region!\n" );
549		/* Assign dev_private so we can do cleanup. */
550		dev->dev_private = (void *)dev_priv;
551		mga_do_cleanup_dma( dev );
552		return DRM_ERR(EINVAL);
553	}
554
555	dev_priv->sarea_priv =
556		(drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle +
557				    init->sarea_priv_offset);
558
559	DRM_IOREMAP( dev_priv->warp, dev );
560	DRM_IOREMAP( dev_priv->primary, dev );
561	DRM_IOREMAP( dev_priv->buffers, dev );
562
563	if(!dev_priv->warp->handle ||
564	   !dev_priv->primary->handle ||
565	   !dev_priv->buffers->handle ) {
566		DRM_ERROR( "failed to ioremap agp regions!\n" );
567		/* Assign dev_private so we can do cleanup. */
568		dev->dev_private = (void *)dev_priv;
569		mga_do_cleanup_dma( dev );
570		return DRM_ERR(ENOMEM);
571	}
572
573	ret = mga_warp_install_microcode( dev_priv );
574	if ( ret < 0 ) {
575		DRM_ERROR( "failed to install WARP ucode!\n" );
576		/* Assign dev_private so we can do cleanup. */
577		dev->dev_private = (void *)dev_priv;
578		mga_do_cleanup_dma( dev );
579		return ret;
580	}
581
582	ret = mga_warp_init( dev_priv );
583	if ( ret < 0 ) {
584		DRM_ERROR( "failed to init WARP engine!\n" );
585		/* Assign dev_private so we can do cleanup. */
586		dev->dev_private = (void *)dev_priv;
587		mga_do_cleanup_dma( dev );
588		return ret;
589	}
590
591	dev_priv->prim.status = (u32 *)dev_priv->status->handle;
592
593	mga_do_wait_for_idle( dev_priv );
594
595	/* Init the primary DMA registers.
596	 */
597	MGA_WRITE( MGA_PRIMADDRESS,
598		   dev_priv->primary->offset | MGA_DMA_GENERAL );
599#if 0
600	MGA_WRITE( MGA_PRIMPTR,
601		   virt_to_bus((void *)dev_priv->prim.status) |
602		   MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */
603		   MGA_PRIMPTREN1 );	/* DWGSYNC */
604#endif
605
606	dev_priv->prim.start = (u8 *)dev_priv->primary->handle;
607	dev_priv->prim.end = ((u8 *)dev_priv->primary->handle
608			      + dev_priv->primary->size);
609	dev_priv->prim.size = dev_priv->primary->size;
610
611	dev_priv->prim.tail = 0;
612	dev_priv->prim.space = dev_priv->prim.size;
613	dev_priv->prim.wrapped = 0;
614
615	dev_priv->prim.last_flush = 0;
616	dev_priv->prim.last_wrap = 0;
617
618	dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE;
619
620	dev_priv->prim.status[0] = dev_priv->primary->offset;
621	dev_priv->prim.status[1] = 0;
622
623	dev_priv->sarea_priv->last_wrap = 0;
624	dev_priv->sarea_priv->last_frame.head = 0;
625	dev_priv->sarea_priv->last_frame.wrap = 0;
626
627	if ( mga_freelist_init( dev, dev_priv ) < 0 ) {
628		DRM_ERROR( "could not initialize freelist\n" );
629		/* Assign dev_private so we can do cleanup. */
630		dev->dev_private = (void *)dev_priv;
631		mga_do_cleanup_dma( dev );
632		return DRM_ERR(ENOMEM);
633	}
634
635	/* Make dev_private visable to others. */
636	dev->dev_private = (void *)dev_priv;
637	return 0;
638}
639
640int mga_do_cleanup_dma( drm_device_t *dev )
641{
642	DRM_DEBUG( "\n" );
643
644#if __HAVE_IRQ
645	/* Make sure interrupts are disabled here because the uninstall ioctl
646	 * may not have been called from userspace and after dev_private
647	 * is freed, it's too late.
648	 */
649	if ( dev->irq ) DRM(irq_uninstall)(dev);
650#endif
651
652	if ( dev->dev_private ) {
653		drm_mga_private_t *dev_priv = dev->dev_private;
654
655		if ( dev_priv->warp != NULL )
656			DRM_IOREMAPFREE( dev_priv->warp, dev );
657		if ( dev_priv->primary != NULL )
658			DRM_IOREMAPFREE( dev_priv->primary, dev );
659		if ( dev_priv->buffers != NULL )
660			DRM_IOREMAPFREE( dev_priv->buffers, dev );
661
662		if ( dev_priv->head != NULL ) {
663			mga_freelist_cleanup( dev );
664		}
665
666		DRM(free)( dev->dev_private, sizeof(drm_mga_private_t),
667			   DRM_MEM_DRIVER );
668		dev->dev_private = NULL;
669	}
670
671	return 0;
672}
673
674int mga_dma_init( DRM_IOCTL_ARGS )
675{
676	DRM_DEVICE;
677	drm_mga_init_t init;
678
679	LOCK_TEST_WITH_RETURN( dev, filp );
680
681	DRM_COPY_FROM_USER_IOCTL( init, (drm_mga_init_t *)data, sizeof(init) );
682
683	switch ( init.func ) {
684	case MGA_INIT_DMA:
685		return mga_do_init_dma( dev, &init );
686	case MGA_CLEANUP_DMA:
687		return mga_do_cleanup_dma( dev );
688	}
689
690	return DRM_ERR(EINVAL);
691}
692
693
694/* ================================================================
695 * Primary DMA stream management
696 */
697
698int mga_dma_flush( DRM_IOCTL_ARGS )
699{
700	DRM_DEVICE;
701	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
702	drm_lock_t lock;
703
704	LOCK_TEST_WITH_RETURN( dev, filp );
705
706	DRM_COPY_FROM_USER_IOCTL( lock, (drm_lock_t *)data, sizeof(lock) );
707
708	DRM_DEBUG( "%s%s%s\n",
709		   (lock.flags & _DRM_LOCK_FLUSH) ?	"flush, " : "",
710		   (lock.flags & _DRM_LOCK_FLUSH_ALL) ?	"flush all, " : "",
711		   (lock.flags & _DRM_LOCK_QUIESCENT) ?	"idle, " : "" );
712
713	WRAP_WAIT_WITH_RETURN( dev_priv );
714
715	if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) {
716		mga_do_dma_flush( dev_priv );
717	}
718
719	if ( lock.flags & _DRM_LOCK_QUIESCENT ) {
720#if MGA_DMA_DEBUG
721		int ret = mga_do_wait_for_idle( dev_priv );
722		if ( ret < 0 )
723			DRM_INFO( "%s: -EBUSY\n", __FUNCTION__ );
724		return ret;
725#else
726		return mga_do_wait_for_idle( dev_priv );
727#endif
728	} else {
729		return 0;
730	}
731}
732
733int mga_dma_reset( DRM_IOCTL_ARGS )
734{
735	DRM_DEVICE;
736	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
737
738	LOCK_TEST_WITH_RETURN( dev, filp );
739
740	return mga_do_dma_reset( dev_priv );
741}
742
743
744/* ================================================================
745 * DMA buffer management
746 */
747
748static int mga_dma_get_buffers( DRMFILE filp,
749				drm_device_t *dev, drm_dma_t *d )
750{
751	drm_buf_t *buf;
752	int i;
753
754	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
755		buf = mga_freelist_get( dev );
756		if ( !buf ) return DRM_ERR(EAGAIN);
757
758		buf->filp = filp;
759
760		if ( DRM_COPY_TO_USER( &d->request_indices[i],
761				   &buf->idx, sizeof(buf->idx) ) )
762			return DRM_ERR(EFAULT);
763		if ( DRM_COPY_TO_USER( &d->request_sizes[i],
764				   &buf->total, sizeof(buf->total) ) )
765			return DRM_ERR(EFAULT);
766
767		d->granted_count++;
768	}
769	return 0;
770}
771
772int mga_dma_buffers( DRM_IOCTL_ARGS )
773{
774	DRM_DEVICE;
775	drm_device_dma_t *dma = dev->dma;
776	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
777	drm_dma_t d;
778	int ret = 0;
779
780	LOCK_TEST_WITH_RETURN( dev, filp );
781
782	DRM_COPY_FROM_USER_IOCTL( d, (drm_dma_t *)data, sizeof(d) );
783
784	/* Please don't send us buffers.
785	 */
786	if ( d.send_count != 0 ) {
787		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
788			   DRM_CURRENTPID, d.send_count );
789		return DRM_ERR(EINVAL);
790	}
791
792	/* We'll send you buffers.
793	 */
794	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
795		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
796			   DRM_CURRENTPID, d.request_count, dma->buf_count );
797		return DRM_ERR(EINVAL);
798	}
799
800	WRAP_TEST_WITH_RETURN( dev_priv );
801
802	d.granted_count = 0;
803
804	if ( d.request_count ) {
805		ret = mga_dma_get_buffers( filp, dev, &d );
806	}
807
808	DRM_COPY_TO_USER_IOCTL( (drm_dma_t *)data, d, sizeof(d) );
809
810	return ret;
811}
812