| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
|  | 36 | #include "drmP.h" | 
|  | 37 | #include "drm.h" | 
|  | 38 | #include "mga_drm.h" | 
|  | 39 | #include "mga_drv.h" | 
|  | 40 |  | 
|  | 41 | #define MGA_DEFAULT_USEC_TIMEOUT	10000 | 
|  | 42 | #define MGA_FREELIST_DEBUG		0 | 
|  | 43 |  | 
|  | 44 | static int mga_do_cleanup_dma( drm_device_t *dev ); | 
|  | 45 |  | 
|  | 46 | /* ================================================================ | 
|  | 47 | * Engine control | 
|  | 48 | */ | 
|  | 49 |  | 
|  | 50 | int mga_do_wait_for_idle( drm_mga_private_t *dev_priv ) | 
|  | 51 | { | 
|  | 52 | u32 status = 0; | 
|  | 53 | int i; | 
|  | 54 | DRM_DEBUG( "\n" ); | 
|  | 55 |  | 
|  | 56 | for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { | 
|  | 57 | status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; | 
|  | 58 | if ( status == MGA_ENDPRDMASTS ) { | 
|  | 59 | MGA_WRITE8( MGA_CRTC_INDEX, 0 ); | 
|  | 60 | return 0; | 
|  | 61 | } | 
|  | 62 | DRM_UDELAY( 1 ); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | #if MGA_DMA_DEBUG | 
|  | 66 | DRM_ERROR( "failed!\n" ); | 
|  | 67 | DRM_INFO( "   status=0x%08x\n", status ); | 
|  | 68 | #endif | 
|  | 69 | return DRM_ERR(EBUSY); | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | static int mga_do_dma_reset( drm_mga_private_t *dev_priv ) | 
|  | 73 | { | 
|  | 74 | drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; | 
|  | 75 | drm_mga_primary_buffer_t *primary = &dev_priv->prim; | 
|  | 76 |  | 
|  | 77 | DRM_DEBUG( "\n" ); | 
|  | 78 |  | 
|  | 79 | /* The primary DMA stream should look like new right about now. | 
|  | 80 | */ | 
|  | 81 | primary->tail = 0; | 
|  | 82 | primary->space = primary->size; | 
|  | 83 | primary->last_flush = 0; | 
|  | 84 |  | 
|  | 85 | sarea_priv->last_wrap = 0; | 
|  | 86 |  | 
|  | 87 | /* FIXME: Reset counters, buffer ages etc... | 
|  | 88 | */ | 
|  | 89 |  | 
|  | 90 | /* FIXME: What else do we need to reinitialize?  WARP stuff? | 
|  | 91 | */ | 
|  | 92 |  | 
|  | 93 | return 0; | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | /* ================================================================ | 
|  | 97 | * Primary DMA stream | 
|  | 98 | */ | 
|  | 99 |  | 
|  | 100 | void mga_do_dma_flush( drm_mga_private_t *dev_priv ) | 
|  | 101 | { | 
|  | 102 | drm_mga_primary_buffer_t *primary = &dev_priv->prim; | 
|  | 103 | u32 head, tail; | 
|  | 104 | u32 status = 0; | 
|  | 105 | int i; | 
|  | 106 | DMA_LOCALS; | 
|  | 107 | DRM_DEBUG( "\n" ); | 
|  | 108 |  | 
|  | 109 | /* We need to wait so that we can do an safe flush */ | 
|  | 110 | for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { | 
|  | 111 | status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; | 
|  | 112 | if ( status == MGA_ENDPRDMASTS ) break; | 
|  | 113 | DRM_UDELAY( 1 ); | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | if ( primary->tail == primary->last_flush ) { | 
|  | 117 | DRM_DEBUG( "   bailing out...\n" ); | 
|  | 118 | return; | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | tail = primary->tail + dev_priv->primary->offset; | 
|  | 122 |  | 
|  | 123 | /* We need to pad the stream between flushes, as the card | 
|  | 124 | * actually (partially?) reads the first of these commands. | 
|  | 125 | * See page 4-16 in the G400 manual, middle of the page or so. | 
|  | 126 | */ | 
|  | 127 | BEGIN_DMA( 1 ); | 
|  | 128 |  | 
|  | 129 | DMA_BLOCK( MGA_DMAPAD,  0x00000000, | 
|  | 130 | MGA_DMAPAD,  0x00000000, | 
|  | 131 | MGA_DMAPAD,  0x00000000, | 
|  | 132 | MGA_DMAPAD,	0x00000000 ); | 
|  | 133 |  | 
|  | 134 | ADVANCE_DMA(); | 
|  | 135 |  | 
|  | 136 | primary->last_flush = primary->tail; | 
|  | 137 |  | 
|  | 138 | head = MGA_READ( MGA_PRIMADDRESS ); | 
|  | 139 |  | 
|  | 140 | if ( head <= tail ) { | 
|  | 141 | primary->space = primary->size - primary->tail; | 
|  | 142 | } else { | 
|  | 143 | primary->space = head - tail; | 
|  | 144 | } | 
|  | 145 |  | 
|  | 146 | DRM_DEBUG( "   head = 0x%06lx\n", head - dev_priv->primary->offset ); | 
|  | 147 | DRM_DEBUG( "   tail = 0x%06lx\n", tail - dev_priv->primary->offset ); | 
|  | 148 | DRM_DEBUG( "  space = 0x%06x\n", primary->space ); | 
|  | 149 |  | 
|  | 150 | mga_flush_write_combine(); | 
|  | 151 | MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); | 
|  | 152 |  | 
|  | 153 | DRM_DEBUG( "done.\n" ); | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) | 
|  | 157 | { | 
|  | 158 | drm_mga_primary_buffer_t *primary = &dev_priv->prim; | 
|  | 159 | u32 head, tail; | 
|  | 160 | DMA_LOCALS; | 
|  | 161 | DRM_DEBUG( "\n" ); | 
|  | 162 |  | 
|  | 163 | BEGIN_DMA_WRAP(); | 
|  | 164 |  | 
|  | 165 | DMA_BLOCK( MGA_DMAPAD,	0x00000000, | 
|  | 166 | MGA_DMAPAD,	0x00000000, | 
|  | 167 | MGA_DMAPAD,	0x00000000, | 
|  | 168 | MGA_DMAPAD,	0x00000000 ); | 
|  | 169 |  | 
|  | 170 | ADVANCE_DMA(); | 
|  | 171 |  | 
|  | 172 | tail = primary->tail + dev_priv->primary->offset; | 
|  | 173 |  | 
|  | 174 | primary->tail = 0; | 
|  | 175 | primary->last_flush = 0; | 
|  | 176 | primary->last_wrap++; | 
|  | 177 |  | 
|  | 178 | head = MGA_READ( MGA_PRIMADDRESS ); | 
|  | 179 |  | 
|  | 180 | if ( head == dev_priv->primary->offset ) { | 
|  | 181 | primary->space = primary->size; | 
|  | 182 | } else { | 
|  | 183 | primary->space = head - dev_priv->primary->offset; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | DRM_DEBUG( "   head = 0x%06lx\n", | 
|  | 187 | head - dev_priv->primary->offset ); | 
|  | 188 | DRM_DEBUG( "   tail = 0x%06x\n", primary->tail ); | 
|  | 189 | DRM_DEBUG( "   wrap = %d\n", primary->last_wrap ); | 
|  | 190 | DRM_DEBUG( "  space = 0x%06x\n", primary->space ); | 
|  | 191 |  | 
|  | 192 | mga_flush_write_combine(); | 
|  | 193 | MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); | 
|  | 194 |  | 
|  | 195 | set_bit( 0, &primary->wrapped ); | 
|  | 196 | DRM_DEBUG( "done.\n" ); | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) | 
|  | 200 | { | 
|  | 201 | drm_mga_primary_buffer_t *primary = &dev_priv->prim; | 
|  | 202 | drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; | 
|  | 203 | u32 head = dev_priv->primary->offset; | 
|  | 204 | DRM_DEBUG( "\n" ); | 
|  | 205 |  | 
|  | 206 | sarea_priv->last_wrap++; | 
|  | 207 | DRM_DEBUG( "   wrap = %d\n", sarea_priv->last_wrap ); | 
|  | 208 |  | 
|  | 209 | mga_flush_write_combine(); | 
|  | 210 | MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); | 
|  | 211 |  | 
|  | 212 | clear_bit( 0, &primary->wrapped ); | 
|  | 213 | DRM_DEBUG( "done.\n" ); | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 |  | 
|  | 217 | /* ================================================================ | 
|  | 218 | * Freelist management | 
|  | 219 | */ | 
|  | 220 |  | 
|  | 221 | #define MGA_BUFFER_USED		~0 | 
|  | 222 | #define MGA_BUFFER_FREE		0 | 
|  | 223 |  | 
|  | 224 | #if MGA_FREELIST_DEBUG | 
|  | 225 | static void mga_freelist_print( drm_device_t *dev ) | 
|  | 226 | { | 
|  | 227 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 228 | drm_mga_freelist_t *entry; | 
|  | 229 |  | 
|  | 230 | DRM_INFO( "\n" ); | 
|  | 231 | DRM_INFO( "current dispatch: last=0x%x done=0x%x\n", | 
|  | 232 | dev_priv->sarea_priv->last_dispatch, | 
|  | 233 | (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) - | 
|  | 234 | dev_priv->primary->offset) ); | 
|  | 235 | DRM_INFO( "current freelist:\n" ); | 
|  | 236 |  | 
|  | 237 | for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) { | 
|  | 238 | DRM_INFO( "   %p   idx=%2d  age=0x%x 0x%06lx\n", | 
|  | 239 | entry, entry->buf->idx, entry->age.head, | 
|  | 240 | entry->age.head - dev_priv->primary->offset ); | 
|  | 241 | } | 
|  | 242 | DRM_INFO( "\n" ); | 
|  | 243 | } | 
|  | 244 | #endif | 
|  | 245 |  | 
|  | 246 | static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv ) | 
|  | 247 | { | 
|  | 248 | drm_device_dma_t *dma = dev->dma; | 
|  | 249 | drm_buf_t *buf; | 
|  | 250 | drm_mga_buf_priv_t *buf_priv; | 
|  | 251 | drm_mga_freelist_t *entry; | 
|  | 252 | int i; | 
|  | 253 | DRM_DEBUG( "count=%d\n", dma->buf_count ); | 
|  | 254 |  | 
|  | 255 | dev_priv->head = drm_alloc( sizeof(drm_mga_freelist_t), | 
|  | 256 | DRM_MEM_DRIVER ); | 
|  | 257 | if ( dev_priv->head == NULL ) | 
|  | 258 | return DRM_ERR(ENOMEM); | 
|  | 259 |  | 
|  | 260 | memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) ); | 
|  | 261 | SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 ); | 
|  | 262 |  | 
|  | 263 | for ( i = 0 ; i < dma->buf_count ; i++ ) { | 
|  | 264 | buf = dma->buflist[i]; | 
|  | 265 | buf_priv = buf->dev_private; | 
|  | 266 |  | 
|  | 267 | entry = drm_alloc( sizeof(drm_mga_freelist_t), | 
|  | 268 | DRM_MEM_DRIVER ); | 
|  | 269 | if ( entry == NULL ) | 
|  | 270 | return DRM_ERR(ENOMEM); | 
|  | 271 |  | 
|  | 272 | memset( entry, 0, sizeof(drm_mga_freelist_t) ); | 
|  | 273 |  | 
|  | 274 | entry->next = dev_priv->head->next; | 
|  | 275 | entry->prev = dev_priv->head; | 
|  | 276 | SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); | 
|  | 277 | entry->buf = buf; | 
|  | 278 |  | 
|  | 279 | if ( dev_priv->head->next != NULL ) | 
|  | 280 | dev_priv->head->next->prev = entry; | 
|  | 281 | if ( entry->next == NULL ) | 
|  | 282 | dev_priv->tail = entry; | 
|  | 283 |  | 
|  | 284 | buf_priv->list_entry = entry; | 
|  | 285 | buf_priv->discard = 0; | 
|  | 286 | buf_priv->dispatched = 0; | 
|  | 287 |  | 
|  | 288 | dev_priv->head->next = entry; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | return 0; | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | static void mga_freelist_cleanup( drm_device_t *dev ) | 
|  | 295 | { | 
|  | 296 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 297 | drm_mga_freelist_t *entry; | 
|  | 298 | drm_mga_freelist_t *next; | 
|  | 299 | DRM_DEBUG( "\n" ); | 
|  | 300 |  | 
|  | 301 | entry = dev_priv->head; | 
|  | 302 | while ( entry ) { | 
|  | 303 | next = entry->next; | 
|  | 304 | drm_free( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); | 
|  | 305 | entry = next; | 
|  | 306 | } | 
|  | 307 |  | 
|  | 308 | dev_priv->head = dev_priv->tail = NULL; | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | #if 0 | 
|  | 312 | /* FIXME: Still needed? | 
|  | 313 | */ | 
|  | 314 | static void mga_freelist_reset( drm_device_t *dev ) | 
|  | 315 | { | 
|  | 316 | drm_device_dma_t *dma = dev->dma; | 
|  | 317 | drm_buf_t *buf; | 
|  | 318 | drm_mga_buf_priv_t *buf_priv; | 
|  | 319 | int i; | 
|  | 320 |  | 
|  | 321 | for ( i = 0 ; i < dma->buf_count ; i++ ) { | 
|  | 322 | buf = dma->buflist[i]; | 
|  | 323 | buf_priv = buf->dev_private; | 
|  | 324 | SET_AGE( &buf_priv->list_entry->age, | 
|  | 325 | MGA_BUFFER_FREE, 0 ); | 
|  | 326 | } | 
|  | 327 | } | 
|  | 328 | #endif | 
|  | 329 |  | 
|  | 330 | static drm_buf_t *mga_freelist_get( drm_device_t *dev ) | 
|  | 331 | { | 
|  | 332 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 333 | drm_mga_freelist_t *next; | 
|  | 334 | drm_mga_freelist_t *prev; | 
|  | 335 | drm_mga_freelist_t *tail = dev_priv->tail; | 
|  | 336 | u32 head, wrap; | 
|  | 337 | DRM_DEBUG( "\n" ); | 
|  | 338 |  | 
|  | 339 | head = MGA_READ( MGA_PRIMADDRESS ); | 
|  | 340 | wrap = dev_priv->sarea_priv->last_wrap; | 
|  | 341 |  | 
|  | 342 | DRM_DEBUG( "   tail=0x%06lx %d\n", | 
|  | 343 | tail->age.head ? | 
|  | 344 | tail->age.head - dev_priv->primary->offset : 0, | 
|  | 345 | tail->age.wrap ); | 
|  | 346 | DRM_DEBUG( "   head=0x%06lx %d\n", | 
|  | 347 | head - dev_priv->primary->offset, wrap ); | 
|  | 348 |  | 
|  | 349 | if ( TEST_AGE( &tail->age, head, wrap ) ) { | 
|  | 350 | prev = dev_priv->tail->prev; | 
|  | 351 | next = dev_priv->tail; | 
|  | 352 | prev->next = NULL; | 
|  | 353 | next->prev = next->next = NULL; | 
|  | 354 | dev_priv->tail = prev; | 
|  | 355 | SET_AGE( &next->age, MGA_BUFFER_USED, 0 ); | 
|  | 356 | return next->buf; | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | DRM_DEBUG( "returning NULL!\n" ); | 
|  | 360 | return NULL; | 
|  | 361 | } | 
|  | 362 |  | 
|  | 363 | int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf ) | 
|  | 364 | { | 
|  | 365 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 366 | drm_mga_buf_priv_t *buf_priv = buf->dev_private; | 
|  | 367 | drm_mga_freelist_t *head, *entry, *prev; | 
|  | 368 |  | 
|  | 369 | DRM_DEBUG( "age=0x%06lx wrap=%d\n", | 
|  | 370 | buf_priv->list_entry->age.head - | 
|  | 371 | dev_priv->primary->offset, | 
|  | 372 | buf_priv->list_entry->age.wrap ); | 
|  | 373 |  | 
|  | 374 | entry = buf_priv->list_entry; | 
|  | 375 | head = dev_priv->head; | 
|  | 376 |  | 
|  | 377 | if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) { | 
|  | 378 | SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); | 
|  | 379 | prev = dev_priv->tail; | 
|  | 380 | prev->next = entry; | 
|  | 381 | entry->prev = prev; | 
|  | 382 | entry->next = NULL; | 
|  | 383 | } else { | 
|  | 384 | prev = head->next; | 
|  | 385 | head->next = entry; | 
|  | 386 | prev->prev = entry; | 
|  | 387 | entry->prev = head; | 
|  | 388 | entry->next = prev; | 
|  | 389 | } | 
|  | 390 |  | 
|  | 391 | return 0; | 
|  | 392 | } | 
|  | 393 |  | 
|  | 394 |  | 
|  | 395 | /* ================================================================ | 
|  | 396 | * DMA initialization, cleanup | 
|  | 397 | */ | 
|  | 398 |  | 
|  | 399 | static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) | 
|  | 400 | { | 
|  | 401 | drm_mga_private_t *dev_priv; | 
|  | 402 | int ret; | 
|  | 403 | DRM_DEBUG( "\n" ); | 
|  | 404 |  | 
|  | 405 | dev_priv = drm_alloc( sizeof(drm_mga_private_t), DRM_MEM_DRIVER ); | 
|  | 406 | if ( !dev_priv ) | 
|  | 407 | return DRM_ERR(ENOMEM); | 
|  | 408 |  | 
|  | 409 | memset( dev_priv, 0, sizeof(drm_mga_private_t) ); | 
|  | 410 |  | 
|  | 411 | dev_priv->chipset = init->chipset; | 
|  | 412 |  | 
|  | 413 | dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT; | 
|  | 414 |  | 
|  | 415 | if ( init->sgram ) { | 
|  | 416 | dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK; | 
|  | 417 | } else { | 
|  | 418 | dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR; | 
|  | 419 | } | 
|  | 420 | dev_priv->maccess	= init->maccess; | 
|  | 421 |  | 
|  | 422 | dev_priv->fb_cpp	= init->fb_cpp; | 
|  | 423 | dev_priv->front_offset	= init->front_offset; | 
|  | 424 | dev_priv->front_pitch	= init->front_pitch; | 
|  | 425 | dev_priv->back_offset	= init->back_offset; | 
|  | 426 | dev_priv->back_pitch	= init->back_pitch; | 
|  | 427 |  | 
|  | 428 | dev_priv->depth_cpp	= init->depth_cpp; | 
|  | 429 | dev_priv->depth_offset	= init->depth_offset; | 
|  | 430 | dev_priv->depth_pitch	= init->depth_pitch; | 
|  | 431 |  | 
|  | 432 | /* FIXME: Need to support AGP textures... | 
|  | 433 | */ | 
|  | 434 | dev_priv->texture_offset = init->texture_offset[0]; | 
|  | 435 | dev_priv->texture_size = init->texture_size[0]; | 
|  | 436 |  | 
|  | 437 | DRM_GETSAREA(); | 
|  | 438 |  | 
|  | 439 | if(!dev_priv->sarea) { | 
|  | 440 | DRM_ERROR( "failed to find sarea!\n" ); | 
|  | 441 | /* Assign dev_private so we can do cleanup. */ | 
|  | 442 | dev->dev_private = (void *)dev_priv; | 
|  | 443 | mga_do_cleanup_dma( dev ); | 
|  | 444 | return DRM_ERR(EINVAL); | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); | 
|  | 448 | if(!dev_priv->mmio) { | 
|  | 449 | DRM_ERROR( "failed to find mmio region!\n" ); | 
|  | 450 | /* Assign dev_private so we can do cleanup. */ | 
|  | 451 | dev->dev_private = (void *)dev_priv; | 
|  | 452 | mga_do_cleanup_dma( dev ); | 
|  | 453 | return DRM_ERR(EINVAL); | 
|  | 454 | } | 
|  | 455 | dev_priv->status = drm_core_findmap(dev, init->status_offset); | 
|  | 456 | if(!dev_priv->status) { | 
|  | 457 | DRM_ERROR( "failed to find status page!\n" ); | 
|  | 458 | /* Assign dev_private so we can do cleanup. */ | 
|  | 459 | dev->dev_private = (void *)dev_priv; | 
|  | 460 | mga_do_cleanup_dma( dev ); | 
|  | 461 | return DRM_ERR(EINVAL); | 
|  | 462 | } | 
|  | 463 | dev_priv->warp = drm_core_findmap(dev, init->warp_offset); | 
|  | 464 | if(!dev_priv->warp) { | 
|  | 465 | DRM_ERROR( "failed to find warp microcode region!\n" ); | 
|  | 466 | /* Assign dev_private so we can do cleanup. */ | 
|  | 467 | dev->dev_private = (void *)dev_priv; | 
|  | 468 | mga_do_cleanup_dma( dev ); | 
|  | 469 | return DRM_ERR(EINVAL); | 
|  | 470 | } | 
|  | 471 | dev_priv->primary = drm_core_findmap(dev, init->primary_offset); | 
|  | 472 | if(!dev_priv->primary) { | 
|  | 473 | DRM_ERROR( "failed to find primary dma region!\n" ); | 
|  | 474 | /* Assign dev_private so we can do cleanup. */ | 
|  | 475 | dev->dev_private = (void *)dev_priv; | 
|  | 476 | mga_do_cleanup_dma( dev ); | 
|  | 477 | return DRM_ERR(EINVAL); | 
|  | 478 | } | 
|  | 479 | dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); | 
|  | 480 | if(!dev->agp_buffer_map) { | 
|  | 481 | DRM_ERROR( "failed to find dma buffer region!\n" ); | 
|  | 482 | /* Assign dev_private so we can do cleanup. */ | 
|  | 483 | dev->dev_private = (void *)dev_priv; | 
|  | 484 | mga_do_cleanup_dma( dev ); | 
|  | 485 | return DRM_ERR(EINVAL); | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | dev_priv->sarea_priv = | 
|  | 489 | (drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle + | 
|  | 490 | init->sarea_priv_offset); | 
|  | 491 |  | 
|  | 492 | drm_core_ioremap( dev_priv->warp, dev ); | 
|  | 493 | drm_core_ioremap( dev_priv->primary, dev ); | 
|  | 494 | drm_core_ioremap( dev->agp_buffer_map, dev ); | 
|  | 495 |  | 
|  | 496 | if(!dev_priv->warp->handle || | 
|  | 497 | !dev_priv->primary->handle || | 
|  | 498 | !dev->agp_buffer_map->handle ) { | 
|  | 499 | DRM_ERROR( "failed to ioremap agp regions!\n" ); | 
|  | 500 | /* Assign dev_private so we can do cleanup. */ | 
|  | 501 | dev->dev_private = (void *)dev_priv; | 
|  | 502 | mga_do_cleanup_dma( dev ); | 
|  | 503 | return DRM_ERR(ENOMEM); | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | ret = mga_warp_install_microcode( dev_priv ); | 
|  | 507 | if ( ret < 0 ) { | 
|  | 508 | DRM_ERROR( "failed to install WARP ucode!\n" ); | 
|  | 509 | /* Assign dev_private so we can do cleanup. */ | 
|  | 510 | dev->dev_private = (void *)dev_priv; | 
|  | 511 | mga_do_cleanup_dma( dev ); | 
|  | 512 | return ret; | 
|  | 513 | } | 
|  | 514 |  | 
|  | 515 | ret = mga_warp_init( dev_priv ); | 
|  | 516 | if ( ret < 0 ) { | 
|  | 517 | DRM_ERROR( "failed to init WARP engine!\n" ); | 
|  | 518 | /* Assign dev_private so we can do cleanup. */ | 
|  | 519 | dev->dev_private = (void *)dev_priv; | 
|  | 520 | mga_do_cleanup_dma( dev ); | 
|  | 521 | return ret; | 
|  | 522 | } | 
|  | 523 |  | 
|  | 524 | dev_priv->prim.status = (u32 *)dev_priv->status->handle; | 
|  | 525 |  | 
|  | 526 | mga_do_wait_for_idle( dev_priv ); | 
|  | 527 |  | 
|  | 528 | /* Init the primary DMA registers. | 
|  | 529 | */ | 
|  | 530 | MGA_WRITE( MGA_PRIMADDRESS, | 
|  | 531 | dev_priv->primary->offset | MGA_DMA_GENERAL ); | 
|  | 532 | #if 0 | 
|  | 533 | MGA_WRITE( MGA_PRIMPTR, | 
|  | 534 | virt_to_bus((void *)dev_priv->prim.status) | | 
|  | 535 | MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */ | 
|  | 536 | MGA_PRIMPTREN1 );	/* DWGSYNC */ | 
|  | 537 | #endif | 
|  | 538 |  | 
|  | 539 | dev_priv->prim.start = (u8 *)dev_priv->primary->handle; | 
|  | 540 | dev_priv->prim.end = ((u8 *)dev_priv->primary->handle | 
|  | 541 | + dev_priv->primary->size); | 
|  | 542 | dev_priv->prim.size = dev_priv->primary->size; | 
|  | 543 |  | 
|  | 544 | dev_priv->prim.tail = 0; | 
|  | 545 | dev_priv->prim.space = dev_priv->prim.size; | 
|  | 546 | dev_priv->prim.wrapped = 0; | 
|  | 547 |  | 
|  | 548 | dev_priv->prim.last_flush = 0; | 
|  | 549 | dev_priv->prim.last_wrap = 0; | 
|  | 550 |  | 
|  | 551 | dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE; | 
|  | 552 |  | 
|  | 553 | dev_priv->prim.status[0] = dev_priv->primary->offset; | 
|  | 554 | dev_priv->prim.status[1] = 0; | 
|  | 555 |  | 
|  | 556 | dev_priv->sarea_priv->last_wrap = 0; | 
|  | 557 | dev_priv->sarea_priv->last_frame.head = 0; | 
|  | 558 | dev_priv->sarea_priv->last_frame.wrap = 0; | 
|  | 559 |  | 
|  | 560 | if ( mga_freelist_init( dev, dev_priv ) < 0 ) { | 
|  | 561 | DRM_ERROR( "could not initialize freelist\n" ); | 
|  | 562 | /* Assign dev_private so we can do cleanup. */ | 
|  | 563 | dev->dev_private = (void *)dev_priv; | 
|  | 564 | mga_do_cleanup_dma( dev ); | 
|  | 565 | return DRM_ERR(ENOMEM); | 
|  | 566 | } | 
|  | 567 |  | 
|  | 568 | /* Make dev_private visable to others. */ | 
|  | 569 | dev->dev_private = (void *)dev_priv; | 
|  | 570 | return 0; | 
|  | 571 | } | 
|  | 572 |  | 
|  | 573 | static int mga_do_cleanup_dma( drm_device_t *dev ) | 
|  | 574 | { | 
|  | 575 | DRM_DEBUG( "\n" ); | 
|  | 576 |  | 
|  | 577 | /* Make sure interrupts are disabled here because the uninstall ioctl | 
|  | 578 | * may not have been called from userspace and after dev_private | 
|  | 579 | * is freed, it's too late. | 
|  | 580 | */ | 
|  | 581 | if ( dev->irq_enabled ) drm_irq_uninstall(dev); | 
|  | 582 |  | 
|  | 583 | if ( dev->dev_private ) { | 
|  | 584 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 585 |  | 
|  | 586 | if ( dev_priv->warp != NULL ) | 
|  | 587 | drm_core_ioremapfree( dev_priv->warp, dev ); | 
|  | 588 | if ( dev_priv->primary != NULL ) | 
|  | 589 | drm_core_ioremapfree( dev_priv->primary, dev ); | 
|  | 590 | if ( dev->agp_buffer_map != NULL ) | 
|  | 591 | drm_core_ioremapfree( dev->agp_buffer_map, dev ); | 
|  | 592 |  | 
|  | 593 | if ( dev_priv->head != NULL ) { | 
|  | 594 | mga_freelist_cleanup( dev ); | 
|  | 595 | } | 
|  | 596 |  | 
|  | 597 | drm_free( dev->dev_private, sizeof(drm_mga_private_t), | 
|  | 598 | DRM_MEM_DRIVER ); | 
|  | 599 | dev->dev_private = NULL; | 
|  | 600 | } | 
|  | 601 |  | 
|  | 602 | return 0; | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | int mga_dma_init( DRM_IOCTL_ARGS ) | 
|  | 606 | { | 
|  | 607 | DRM_DEVICE; | 
|  | 608 | drm_mga_init_t init; | 
|  | 609 |  | 
|  | 610 | LOCK_TEST_WITH_RETURN( dev, filp ); | 
|  | 611 |  | 
|  | 612 | DRM_COPY_FROM_USER_IOCTL( init, (drm_mga_init_t __user *)data, sizeof(init) ); | 
|  | 613 |  | 
|  | 614 | switch ( init.func ) { | 
|  | 615 | case MGA_INIT_DMA: | 
|  | 616 | return mga_do_init_dma( dev, &init ); | 
|  | 617 | case MGA_CLEANUP_DMA: | 
|  | 618 | return mga_do_cleanup_dma( dev ); | 
|  | 619 | } | 
|  | 620 |  | 
|  | 621 | return DRM_ERR(EINVAL); | 
|  | 622 | } | 
|  | 623 |  | 
|  | 624 |  | 
|  | 625 | /* ================================================================ | 
|  | 626 | * Primary DMA stream management | 
|  | 627 | */ | 
|  | 628 |  | 
|  | 629 | int mga_dma_flush( DRM_IOCTL_ARGS ) | 
|  | 630 | { | 
|  | 631 | DRM_DEVICE; | 
|  | 632 | drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; | 
|  | 633 | drm_lock_t lock; | 
|  | 634 |  | 
|  | 635 | LOCK_TEST_WITH_RETURN( dev, filp ); | 
|  | 636 |  | 
|  | 637 | DRM_COPY_FROM_USER_IOCTL( lock, (drm_lock_t __user *)data, sizeof(lock) ); | 
|  | 638 |  | 
|  | 639 | DRM_DEBUG( "%s%s%s\n", | 
|  | 640 | (lock.flags & _DRM_LOCK_FLUSH) ?	"flush, " : "", | 
|  | 641 | (lock.flags & _DRM_LOCK_FLUSH_ALL) ?	"flush all, " : "", | 
|  | 642 | (lock.flags & _DRM_LOCK_QUIESCENT) ?	"idle, " : "" ); | 
|  | 643 |  | 
|  | 644 | WRAP_WAIT_WITH_RETURN( dev_priv ); | 
|  | 645 |  | 
|  | 646 | if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) { | 
|  | 647 | mga_do_dma_flush( dev_priv ); | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | if ( lock.flags & _DRM_LOCK_QUIESCENT ) { | 
|  | 651 | #if MGA_DMA_DEBUG | 
|  | 652 | int ret = mga_do_wait_for_idle( dev_priv ); | 
|  | 653 | if ( ret < 0 ) | 
|  | 654 | DRM_INFO( "%s: -EBUSY\n", __FUNCTION__ ); | 
|  | 655 | return ret; | 
|  | 656 | #else | 
|  | 657 | return mga_do_wait_for_idle( dev_priv ); | 
|  | 658 | #endif | 
|  | 659 | } else { | 
|  | 660 | return 0; | 
|  | 661 | } | 
|  | 662 | } | 
|  | 663 |  | 
|  | 664 | int mga_dma_reset( DRM_IOCTL_ARGS ) | 
|  | 665 | { | 
|  | 666 | DRM_DEVICE; | 
|  | 667 | drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; | 
|  | 668 |  | 
|  | 669 | LOCK_TEST_WITH_RETURN( dev, filp ); | 
|  | 670 |  | 
|  | 671 | return mga_do_dma_reset( dev_priv ); | 
|  | 672 | } | 
|  | 673 |  | 
|  | 674 |  | 
|  | 675 | /* ================================================================ | 
|  | 676 | * DMA buffer management | 
|  | 677 | */ | 
|  | 678 |  | 
|  | 679 | static int mga_dma_get_buffers( DRMFILE filp, | 
|  | 680 | drm_device_t *dev, drm_dma_t *d ) | 
|  | 681 | { | 
|  | 682 | drm_buf_t *buf; | 
|  | 683 | int i; | 
|  | 684 |  | 
|  | 685 | for ( i = d->granted_count ; i < d->request_count ; i++ ) { | 
|  | 686 | buf = mga_freelist_get( dev ); | 
|  | 687 | if ( !buf ) return DRM_ERR(EAGAIN); | 
|  | 688 |  | 
|  | 689 | buf->filp = filp; | 
|  | 690 |  | 
|  | 691 | if ( DRM_COPY_TO_USER( &d->request_indices[i], | 
|  | 692 | &buf->idx, sizeof(buf->idx) ) ) | 
|  | 693 | return DRM_ERR(EFAULT); | 
|  | 694 | if ( DRM_COPY_TO_USER( &d->request_sizes[i], | 
|  | 695 | &buf->total, sizeof(buf->total) ) ) | 
|  | 696 | return DRM_ERR(EFAULT); | 
|  | 697 |  | 
|  | 698 | d->granted_count++; | 
|  | 699 | } | 
|  | 700 | return 0; | 
|  | 701 | } | 
|  | 702 |  | 
|  | 703 | int mga_dma_buffers( DRM_IOCTL_ARGS ) | 
|  | 704 | { | 
|  | 705 | DRM_DEVICE; | 
|  | 706 | drm_device_dma_t *dma = dev->dma; | 
|  | 707 | drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; | 
|  | 708 | drm_dma_t __user *argp = (void __user *)data; | 
|  | 709 | drm_dma_t d; | 
|  | 710 | int ret = 0; | 
|  | 711 |  | 
|  | 712 | LOCK_TEST_WITH_RETURN( dev, filp ); | 
|  | 713 |  | 
|  | 714 | DRM_COPY_FROM_USER_IOCTL( d, argp, sizeof(d) ); | 
|  | 715 |  | 
|  | 716 | /* Please don't send us buffers. | 
|  | 717 | */ | 
|  | 718 | if ( d.send_count != 0 ) { | 
|  | 719 | DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n", | 
|  | 720 | DRM_CURRENTPID, d.send_count ); | 
|  | 721 | return DRM_ERR(EINVAL); | 
|  | 722 | } | 
|  | 723 |  | 
|  | 724 | /* We'll send you buffers. | 
|  | 725 | */ | 
|  | 726 | if ( d.request_count < 0 || d.request_count > dma->buf_count ) { | 
|  | 727 | DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n", | 
|  | 728 | DRM_CURRENTPID, d.request_count, dma->buf_count ); | 
|  | 729 | return DRM_ERR(EINVAL); | 
|  | 730 | } | 
|  | 731 |  | 
|  | 732 | WRAP_TEST_WITH_RETURN( dev_priv ); | 
|  | 733 |  | 
|  | 734 | d.granted_count = 0; | 
|  | 735 |  | 
|  | 736 | if ( d.request_count ) { | 
|  | 737 | ret = mga_dma_get_buffers( filp, dev, &d ); | 
|  | 738 | } | 
|  | 739 |  | 
|  | 740 | DRM_COPY_TO_USER_IOCTL( argp, d, sizeof(d) ); | 
|  | 741 |  | 
|  | 742 | return ret; | 
|  | 743 | } | 
|  | 744 |  | 
|  | 745 | void mga_driver_pretakedown(drm_device_t *dev) | 
|  | 746 | { | 
|  | 747 | mga_do_cleanup_dma( dev ); | 
|  | 748 | } | 
|  | 749 |  | 
|  | 750 | int mga_driver_dma_quiescent(drm_device_t *dev) | 
|  | 751 | { | 
|  | 752 | drm_mga_private_t *dev_priv = dev->dev_private; | 
|  | 753 | return mga_do_wait_for_idle( dev_priv ); | 
|  | 754 | } |