summaryrefslogtreecommitdiff
path: root/linux-core
AgeCommit message (Expand)Author
2007-02-16Simple fence object sample driver for via, based on idling the GPU.Thomas Hellstrom
2007-02-15Initial support for fence object classes.Thomas Hellstrom
2007-02-15Fix build against older kernels.Michel Dänzer
2007-02-14Merge branch 'ttm-vram-0-1-branch'Thomas Hellstrom
2007-02-14Fix multiple spinlock unlockingThomas Hellstrom
2007-02-14Rename drm_ttm.h to drm_objects.hThomas Hellstrom
2007-02-14Move fence- and buffer-object related header stuff to drm_ttm.hThomas Hellstrom
2007-02-14Remove an intel-specific hack and replace it with a fence driver callback.Thomas Hellstrom
2007-02-14Set the drm bus map type for each buffer object memory type.Thomas Hellstrom
2007-02-14Rework buffer object vm code to use nopfn() for kernels >= 2.6.19.Thomas Hellstrom
2007-02-13Remove debug printout.Thomas Hellstrom
2007-02-13Bugzilla Bug #9457Thomas Hellstrom
2007-02-13More bugfixes.Thomas Hellstrom
2007-02-12Fix some outdated URLs, remove others.Adam Jackson
2007-02-12Lindent.Thomas Hellstrom
2007-02-12More bugfixes.Thomas Hellstrom
2007-02-12Cleanup and fix support for pinned buffers.Thomas Hellstrom
2007-02-10Various bugfixes.Thomas Hellstrom
2007-02-09I915 accelerated blit copy functional.Thomas Hellstrom
2007-02-09Reinstate some LRU handling.Thomas Hellstrom
2007-02-09Remove some code that should have gone inThomas Hellstrom
2007-02-09Fix copyright statements.Thomas Hellstrom
2007-02-09Fix evict_mutex locking range.Thomas Hellstrom
2007-02-08Add an accelerated buffer copy cleanup helper.Thomas Hellstrom
2007-02-08Don't create a ttm just to copy from.Thomas Hellstrom
2007-02-08Fix mm_block leak.Thomas Hellstrom
2007-02-08A minor function interface change and some memcpy bugfixing.Thomas Hellstrom
2007-02-08Simplify external ttm page allocation.Thomas Hellstrom
2007-02-08Update memory compatibility tests.Thomas Hellstrom
2007-02-07Checkpoint commit.Thomas Hellstrom
2007-02-07Fix a stray unlock_kernel() in drm_vm.cThomas Hellstrom
2007-02-06Simplify pci map vs no pci map choice.Thomas Hellstrom
2007-02-06Implement a drm_mem_reg_t substructure in the buffer object type.Thomas Hellstrom
2007-02-06Implement a policy for selecting memory types.Thomas Hellstrom
2007-02-05i915: Add copy-blit operation.Thomas Hellstrom
2007-02-03nouveau: add missing nv04_graph.c symlink.Stephane Marchesin
2007-02-03nouveau: fix nv04 graph routines for new register names.Stephane Marchesin
2007-02-02Make also later kernels work with buffer object vmThomas Hellstrom
2007-02-02Make vm handle buffer objects instead of ttm objects.Thomas Hellstrom
2007-02-01Fix missing ttm_open_vma call from previous commit.Thomas Hellstrom
2007-02-01Prepare for removal of the ttm_object type.Thomas Hellstrom
2007-02-01Protect drm_mmap against disappearing maps.Thomas Hellstrom
2007-01-31memory manager: Make device driver aware of different memory types.Thomas Hellstrom
2007-01-31Fix an error-path oops.Thomas Hellstrom
2007-01-30Add a buffer object transfer function.Thomas Hellstrom
2007-01-30Clean up buffer object destruction somewhat.Thomas Hellstrom
2007-01-29Use pre-defined list_splice function.Thomas Hellstrom
2007-01-29s/buf/bo/ for consistency.Thomas Hellstrom
2007-01-29Some cleanup. A buffer object should only have one active memory type.Thomas Hellstrom
2007-01-25Remove a scary error printed when we were leaking memory caches.Thomas Hellstrom
> 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
/* $Id$
 * ffb_context.c: Creator/Creator3D DRI/DRM context switching.
 *
 * Copyright (C) 2000 David S. Miller (davem@redhat.com)
 *
 * Almost entirely stolen from tdfx_context.c, see there
 * for authors.
 */

#include <linux/sched.h>
#include <asm/upa.h>

#include "ffb.h"
#include "drmP.h"

#include "ffb_drv.h"

static int DRM(alloc_queue)(drm_device_t *dev, int is_2d_only)
{
	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private;
	int i;

	for (i = 0; i < FFB_MAX_CTXS; i++) {
		if (fpriv->hw_state[i] == NULL)
			break;
	}
	if (i == FFB_MAX_CTXS)
		return -1;

	fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL);
	if (fpriv->hw_state[i] == NULL)
		return -1;

	fpriv->hw_state[i]->is_2d_only = is_2d_only;

	/* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */
	return i + 1;
}

static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx)
{
	ffb_fbcPtr ffb = fpriv->regs;
	struct ffb_hw_context *ctx;
	int i;

	ctx = fpriv->hw_state[idx - 1];
	if (idx == 0 || ctx == NULL)
		return;

	if (ctx->is_2d_only) {
		/* 2D applications only care about certain pieces
		 * of state.
		 */
		ctx->drawop = upa_readl(&ffb->drawop);
		ctx->ppc = upa_readl(&ffb->ppc);
		ctx->wid = upa_readl(&ffb->wid);
		ctx->fg = upa_readl(&ffb->fg);
		ctx->bg = upa_readl(&ffb->bg);
		ctx->xclip = upa_readl(&ffb->xclip);
		ctx->fbc = upa_readl(&ffb->fbc);
		ctx->rop = upa_readl(&ffb->rop);
		ctx->cmp = upa_readl(&ffb->cmp);
		ctx->matchab = upa_readl(&ffb->matchab);
		ctx->magnab = upa_readl(&ffb->magnab);
		ctx->pmask = upa_readl(&ffb->pmask);
		ctx->xpmask = upa_readl(&ffb->xpmask);
		ctx->lpat = upa_readl(&ffb->lpat);
		ctx->fontxy = upa_readl(&ffb->fontxy);
		ctx->fontw = upa_readl(&ffb->fontw);
		ctx->fontinc = upa_readl(&ffb->fontinc);

		/* stencil/stencilctl only exists on FFB2+ and later
		 * due to the introduction of 3DRAM-III.
		 */
		if (fpriv->ffb_type == ffb2_vertical_plus ||
		    fpriv->ffb_type == ffb2_horizontal_plus) {
			ctx->stencil = upa_readl(&ffb->stencil);
			ctx->stencilctl = upa_readl(&ffb->stencilctl);
		}

		for (i = 0; i < 32; i++)
			ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
		ctx->ucsr = upa_readl(&ffb->ucsr);
		return;
	}

	/* Fetch drawop. */
	ctx->drawop = upa_readl(&ffb->drawop);

	/* If we were saving the vertex registers, this is where
	 * we would do it.  We would save 32 32-bit words starting
	 * at ffb->suvtx.
	 */

	/* Capture rendering attributes. */

	ctx->ppc = upa_readl(&ffb->ppc);		/* Pixel Processor Control */
	ctx->wid = upa_readl(&ffb->wid);		/* Current WID */
	ctx->fg = upa_readl(&ffb->fg);			/* Constant FG color */
	ctx->bg = upa_readl(&ffb->bg);			/* Constant BG color */
	ctx->consty = upa_readl(&ffb->consty);		/* Constant Y */
	ctx->constz = upa_readl(&ffb->constz);		/* Constant Z */
	ctx->xclip = upa_readl(&ffb->xclip);		/* X plane clip */
	ctx->dcss = upa_readl(&ffb->dcss);		/* Depth Cue Scale Slope */
	ctx->vclipmin = upa_readl(&ffb->vclipmin);	/* Primary XY clip, minimum */
	ctx->vclipmax = upa_readl(&ffb->vclipmax);	/* Primary XY clip, maximum */
	ctx->vclipzmin = upa_readl(&ffb->vclipzmin);	/* Primary Z clip, minimum */
	ctx->vclipzmax = upa_readl(&ffb->vclipzmax);	/* Primary Z clip, maximum */
	ctx->dcsf = upa_readl(&ffb->dcsf);		/* Depth Cue Scale Front Bound */
	ctx->dcsb = upa_readl(&ffb->dcsb);		/* Depth Cue Scale Back Bound */
	ctx->dczf = upa_readl(&ffb->dczf);		/* Depth Cue Scale Z Front */
	ctx->dczb = upa_readl(&ffb->dczb);		/* Depth Cue Scale Z Back */
	ctx->blendc = upa_readl(&ffb->blendc);		/* Alpha Blend Control */
	ctx->blendc1 = upa_readl(&ffb->blendc1);	/* Alpha Blend Color 1 */
	ctx->blendc2 = upa_readl(&ffb->blendc2);	/* Alpha Blend Color 2 */
	ctx->fbc = upa_readl(&ffb->fbc);		/* Frame Buffer Control */
	ctx->rop = upa_readl(&ffb->rop);		/* Raster Operation */
	ctx->cmp = upa_readl(&ffb->cmp);		/* Compare Controls */
	ctx->matchab = upa_readl(&ffb->matchab);	/* Buffer A/B Match Ops */
	ctx->matchc = upa_readl(&ffb->matchc);		/* Buffer C Match Ops */
	ctx->magnab = upa_readl(&ffb->magnab);		/* Buffer A/B Magnitude Ops */
	ctx->magnc = upa_readl(&ffb->magnc);		/* Buffer C Magnitude Ops */
	ctx->pmask = upa_readl(&ffb->pmask);		/* RGB Plane Mask */
	ctx->xpmask = upa_readl(&ffb->xpmask);		/* X Plane Mask */
	ctx->ypmask = upa_readl(&ffb->ypmask);		/* Y Plane Mask */
	ctx->zpmask = upa_readl(&ffb->zpmask);		/* Z Plane Mask */

	/* Auxiliary Clips. */
	ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min);
	ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max);
	ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min);
	ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max);
	ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min);
	ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max);
	ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min);
	ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max);

	ctx->lpat = upa_readl(&ffb->lpat);		/* Line Pattern */
	ctx->fontxy = upa_readl(&ffb->fontxy);		/* XY Font Coordinate */
	ctx->fontw = upa_readl(&ffb->fontw);		/* Font Width */
	ctx->fontinc = upa_readl(&ffb->fontinc);	/* Font X/Y Increment */

	/* These registers/features only exist on FFB2 and later chips. */
	if (fpriv->ffb_type >= ffb2_prototype) {
		ctx->dcss1 = upa_readl(&ffb->dcss1);	/* Depth Cue Scale Slope 1 */
		ctx->dcss2 = upa_readl(&ffb->dcss2);	/* Depth Cue Scale Slope 2 */
		ctx->dcss2 = upa_readl(&ffb->dcss3);	/* Depth Cue Scale Slope 3 */
		ctx->dcs2  = upa_readl(&ffb->dcs2);	/* Depth Cue Scale 2 */
		ctx->dcs3  = upa_readl(&ffb->dcs3);	/* Depth Cue Scale 3 */
		ctx->dcs4  = upa_readl(&ffb->dcs4);	/* Depth Cue Scale 4 */
		ctx->dcd2  = upa_readl(&ffb->dcd2);	/* Depth Cue Depth 2 */
		ctx->dcd3  = upa_readl(&ffb->dcd3);	/* Depth Cue Depth 3 */
		ctx->dcd4  = upa_readl(&ffb->dcd4);	/* Depth Cue Depth 4 */

		/* And stencil/stencilctl only exists on FFB2+ and later
		 * due to the introduction of 3DRAM-III.
		 */
		if (fpriv->ffb_type == ffb2_vertical_plus ||
		    fpriv->ffb_type == ffb2_horizontal_plus) {
			ctx->stencil = upa_readl(&ffb->stencil);
			ctx->stencilctl = upa_readl(&ffb->stencilctl);
		}
	}

	/* Save the 32x32 area pattern. */
	for (i = 0; i < 32; i++)
		ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);

	/* Finally, stash away the User Constol/Status Register. */
	ctx->ucsr = upa_readl(&ffb->ucsr);
}

static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx)
{
	ffb_fbcPtr ffb = fpriv->regs;
	struct ffb_hw_context *ctx;
	int i;

	ctx = fpriv->hw_state[idx - 1];
	if (idx == 0 || ctx == NULL)
		return;

	if (ctx->is_2d_only) {
		/* 2D applications only care about certain pieces
		 * of state.
		 */
		upa_writel(ctx->drawop, &ffb->drawop);

		/* If we were restoring the vertex registers, this is where
		 * we would do it.  We would restore 32 32-bit words starting
		 * at ffb->suvtx.
		 */

		upa_writel(ctx->ppc, &ffb->ppc);
		upa_writel(ctx->wid, &ffb->wid);
		upa_writel(ctx->fg,  &ffb->fg);
		upa_writel(ctx->bg, &ffb->bg);
		upa_writel(ctx->xclip, &ffb->xclip);
		upa_writel(ctx->fbc, &ffb->fbc);
		upa_writel(ctx->rop, &ffb->rop);
		upa_writel(ctx->cmp, &ffb->cmp);
		upa_writel(ctx->matchab, &ffb->matchab);
		upa_writel(ctx->magnab, &ffb->magnab);
		upa_writel(ctx->pmask, &ffb->pmask);
		upa_writel(ctx->xpmask, &ffb->xpmask);
		upa_writel(ctx->lpat, &ffb->lpat);
		upa_writel(ctx->fontxy, &ffb->fontxy);
		upa_writel(ctx->fontw, &ffb->fontw);
		upa_writel(ctx->fontinc, &ffb->fontinc);

		/* stencil/stencilctl only exists on FFB2+ and later
		 * due to the introduction of 3DRAM-III.
		 */
		if (fpriv->ffb_type == ffb2_vertical_plus ||
		    fpriv->ffb_type == ffb2_horizontal_plus) {
			upa_writel(ctx->stencil, &ffb->stencil);
			upa_writel(ctx->stencilctl, &ffb->stencilctl);
			upa_writel(0x80000000, &ffb->fbc);
			upa_writel((ctx->stencilctl | 0x80000),
				   &ffb->rawstencilctl);
			upa_writel(ctx->fbc, &ffb->fbc);
		}

		for (i = 0; i < 32; i++)
			upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
		upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
		return;
	}

	/* Restore drawop. */
	upa_writel(ctx->drawop, &ffb->drawop);

	/* If we were restoring the vertex registers, this is where
	 * we would do it.  We would restore 32 32-bit words starting
	 * at ffb->suvtx.
	 */

	/* Restore rendering attributes. */

	upa_writel(ctx->ppc, &ffb->ppc);		/* Pixel Processor Control */
	upa_writel(ctx->wid, &ffb->wid);		/* Current WID */
	upa_writel(ctx->fg, &ffb->fg);			/* Constant FG color */
	upa_writel(ctx->bg, &ffb->bg);			/* Constant BG color */
	upa_writel(ctx->consty, &ffb->consty);		/* Constant Y */
	upa_writel(ctx->constz, &ffb->constz);		/* Constant Z */
	upa_writel(ctx->xclip, &ffb->xclip);		/* X plane clip */
	upa_writel(ctx->dcss, &ffb->dcss);		/* Depth Cue Scale Slope */
	upa_writel(ctx->vclipmin, &ffb->vclipmin);	/* Primary XY clip, minimum */
	upa_writel(ctx->vclipmax, &ffb->vclipmax);	/* Primary XY clip, maximum */
	upa_writel(ctx->vclipzmin, &ffb->vclipzmin);	/* Primary Z clip, minimum */
	upa_writel(ctx->vclipzmax, &ffb->vclipzmax);	/* Primary Z clip, maximum */
	upa_writel(ctx->dcsf, &ffb->dcsf);		/* Depth Cue Scale Front Bound */
	upa_writel(ctx->dcsb, &ffb->dcsb);		/* Depth Cue Scale Back Bound */
	upa_writel(ctx->dczf, &ffb->dczf);		/* Depth Cue Scale Z Front */
	upa_writel(ctx->dczb, &ffb->dczb);		/* Depth Cue Scale Z Back */
	upa_writel(ctx->blendc, &ffb->blendc);		/* Alpha Blend Control */
	upa_writel(ctx->blendc1, &ffb->blendc1);	/* Alpha Blend Color 1 */
	upa_writel(ctx->blendc2, &ffb->blendc2);	/* Alpha Blend Color 2 */
	upa_writel(ctx->fbc, &ffb->fbc);		/* Frame Buffer Control */
	upa_writel(ctx->rop, &ffb->rop);		/* Raster Operation */
	upa_writel(ctx->cmp, &ffb->cmp);		/* Compare Controls */
	upa_writel(ctx->matchab, &ffb->matchab);	/* Buffer A/B Match Ops */
	upa_writel(ctx->matchc, &ffb->matchc);		/* Buffer C Match Ops */
	upa_writel(ctx->magnab, &ffb->magnab);		/* Buffer A/B Magnitude Ops */
	upa_writel(ctx->magnc, &ffb->magnc);		/* Buffer C Magnitude Ops */
	upa_writel(ctx->pmask, &ffb->pmask);		/* RGB Plane Mask */
	upa_writel(ctx->xpmask, &ffb->xpmask);		/* X Plane Mask */
	upa_writel(ctx->ypmask, &ffb->ypmask);		/* Y Plane Mask */
	upa_writel(ctx->zpmask, &ffb->zpmask);		/* Z Plane Mask */

	/* Auxiliary Clips. */
	upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min);
	upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max);
	upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min);
	upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max);
	upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min);
	upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max);
	upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min);
	upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max);

	upa_writel(ctx->lpat, &ffb->lpat);		/* Line Pattern */
	upa_writel(ctx->fontxy, &ffb->fontxy);		/* XY Font Coordinate */
	upa_writel(ctx->fontw, &ffb->fontw);		/* Font Width */
	upa_writel(ctx->fontinc, &ffb->fontinc);	/* Font X/Y Increment */

	/* These registers/features only exist on FFB2 and later chips. */
	if (fpriv->ffb_type >= ffb2_prototype) {
		upa_writel(ctx->dcss1, &ffb->dcss1);	/* Depth Cue Scale Slope 1 */
		upa_writel(ctx->dcss2, &ffb->dcss2);	/* Depth Cue Scale Slope 2 */
		upa_writel(ctx->dcss3, &ffb->dcss2);	/* Depth Cue Scale Slope 3 */
		upa_writel(ctx->dcs2, &ffb->dcs2);	/* Depth Cue Scale 2 */
		upa_writel(ctx->dcs3, &ffb->dcs3);	/* Depth Cue Scale 3 */
		upa_writel(ctx->dcs4, &ffb->dcs4);	/* Depth Cue Scale 4 */
		upa_writel(ctx->dcd2, &ffb->dcd2);	/* Depth Cue Depth 2 */
		upa_writel(ctx->dcd3, &ffb->dcd3);	/* Depth Cue Depth 3 */
		upa_writel(ctx->dcd4, &ffb->dcd4);	/* Depth Cue Depth 4 */

		/* And stencil/stencilctl only exists on FFB2+ and later
		 * due to the introduction of 3DRAM-III.
		 */
		if (fpriv->ffb_type == ffb2_vertical_plus ||
		    fpriv->ffb_type == ffb2_horizontal_plus) {
			/* Unfortunately, there is a hardware bug on
			 * the FFB2+ chips which prevents a normal write
			 * to the stencil control register from working
			 * as it should.
			 *
			 * The state controlled by the FFB stencilctl register
			 * really gets transferred to the per-buffer instances
			 * of the stencilctl register in the 3DRAM chips.
			 *
			 * The bug is that FFB does not update buffer C correctly,
			 * so we have to do it by hand for them.
			 */

			/* This will update buffers A and B. */
			upa_writel(ctx->stencil, &ffb->stencil);
			upa_writel(ctx->stencilctl, &ffb->stencilctl);

			/* Force FFB to use buffer C 3dram regs. */
			upa_writel(0x80000000, &ffb->fbc);
			upa_writel((ctx->stencilctl | 0x80000),
				   &ffb->rawstencilctl);

			/* Now restore the correct FBC controls. */
			upa_writel(ctx->fbc, &ffb->fbc);
		}
	}

	/* Restore the 32x32 area pattern. */
	for (i = 0; i < 32; i++)
		upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);

	/* Finally, stash away the User Constol/Status Register.
	 * The only state we really preserve here is the picking
	 * control.
	 */
	upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
}

#define FFB_UCSR_FB_BUSY       0x01000000
#define FFB_UCSR_RP_BUSY       0x02000000
#define FFB_UCSR_ALL_BUSY      (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY)

static void FFBWait(ffb_fbcPtr ffb)
{
	int limit = 100000;

	do {
		u32 regval = upa_readl(&ffb->ucsr);

		if ((regval & FFB_UCSR_ALL_BUSY) == 0)
			break;
	} while (--limit);
}

int DRM(context_switch)(drm_device_t *dev, int old, int new)
{
	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private;

#if DRM_DMA_HISTOGRAM
        dev->ctx_start = get_cycles();
#endif
        
        DRM_DEBUG("Context switch from %d to %d\n", old, new);

        if (new == dev->last_context ||
	    dev->last_context == 0) {
		dev->last_context = new;
                return 0;
	}
        
	FFBWait(fpriv->regs);
	ffb_save_context(fpriv, old);
	ffb_restore_context(fpriv, old, new);
	FFBWait(fpriv->regs);
        
	dev->last_context = new;

        return 0;
}

int DRM(resctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	drm_ctx_res_t	res;
	drm_ctx_t	ctx;
	int		i;

	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
	if (copy_from_user(&res, (drm_ctx_res_t __user *)arg, sizeof(res)))
		return -EFAULT;
	if (res.count >= DRM_RESERVED_CONTEXTS) {
		memset(&ctx, 0, sizeof(ctx));
		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
			ctx.handle = i;
			if (copy_to_user(&res.contexts[i],
					 &i,
					 sizeof(i)))
				return -EFAULT;
		}
	}
	res.count = DRM_RESERVED_CONTEXTS;
	if (copy_to_user((drm_ctx_res_t __user *)arg, &res, sizeof(res)))
		return -EFAULT;
	return 0;
}


int DRM(addctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	drm_ctx_t	ctx;
	int idx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;
	idx = DRM(alloc_queue)(dev, (ctx.flags & _DRM_CONTEXT_2DONLY));
	if (idx < 0)
		return -ENFILE;

	DRM_DEBUG("%d\n", ctx.handle);
	ctx.handle = idx;
	if (copy_to_user((drm_ctx_t __user *)arg, &ctx, sizeof(ctx)))
		return -EFAULT;
	return 0;
}

int DRM(modctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) dev->dev_private;
	struct ffb_hw_context *hwctx;
	drm_ctx_t ctx;
	int idx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;

	idx = ctx.handle;
	if (idx <= 0 || idx >= FFB_MAX_CTXS)
		return -EINVAL;

	hwctx = fpriv->hw_state[idx - 1];
	if (hwctx == NULL)
		return -EINVAL;

	if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0)
		hwctx->is_2d_only = 0;
	else
		hwctx->is_2d_only = 1;

	return 0;
}

int DRM(getctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) dev->dev_private;
	struct ffb_hw_context *hwctx;
	drm_ctx_t ctx;
	int idx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;

	idx = ctx.handle;
	if (idx <= 0 || idx >= FFB_MAX_CTXS)
		return -EINVAL;

	hwctx = fpriv->hw_state[idx - 1];
	if (hwctx == NULL)
		return -EINVAL;

	if (hwctx->is_2d_only != 0)
		ctx.flags = _DRM_CONTEXT_2DONLY;
	else
		ctx.flags = 0;

	if (copy_to_user((drm_ctx_t __user *)arg, &ctx, sizeof(ctx)))
		return -EFAULT;

	return 0;
}

int DRM(switchctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		   unsigned long arg)
{
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	drm_ctx_t	ctx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;
	DRM_DEBUG("%d\n", ctx.handle);
	return DRM(context_switch)(dev, dev->last_context, ctx.handle);
}

int DRM(newctx)(struct inode *inode, struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	drm_ctx_t	ctx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;
	DRM_DEBUG("%d\n", ctx.handle);

	return 0;
}

int DRM(rmctx)(struct inode *inode, struct file *filp, unsigned int cmd,
	       unsigned long arg)
{
	drm_ctx_t	ctx;
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) dev->dev_private;
	int idx;

	if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
		return -EFAULT;
	DRM_DEBUG("%d\n", ctx.handle);

	idx = ctx.handle - 1;
	if (idx < 0 || idx >= FFB_MAX_CTXS)
		return -EINVAL;

	if (fpriv->hw_state[idx] != NULL) {
		kfree(fpriv->hw_state[idx]);
		fpriv->hw_state[idx] = NULL;
	}
	return 0;
}

static void ffb_driver_release(drm_device_t *dev)
{
	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private;
	int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock);
	int idx;

	idx = context - 1;
	if (fpriv &&
	    context != DRM_KERNEL_CONTEXT &&
	    fpriv->hw_state[idx] != NULL) {
		kfree(fpriv->hw_state[idx]);
		fpriv->hw_state[idx] = NULL;
	}	
}

static int ffb_driver_presetup(drm_device_t *dev)
{
	int ret;
	ret = ffb_presetup(dev);
	if (_ret != 0) return ret;
}

static void ffb_driver_pretakedown(drm_device_t *dev)
{
	if (dev->dev_private) kfree(dev->dev_private);
}

static void ffb_driver_postcleanup(drm_device_t *dev)
{
	if (ffb_position != NULL) kfree(ffb_position);
}

static int ffb_driver_kernel_context_switch_unlock(struct drm_device *dev)
{
	dev->lock.filp = 0;
	{
		__volatile__ unsigned int *plock = &dev->lock.hw_lock->lock;
		unsigned int old, new, prev, ctx;
		
		ctx = lock.context;
		do {
			old  = *plock;
			new  = ctx;
			prev = cmpxchg(plock, old, new);
		} while (prev != old);
	}
	wake_up_interruptible(&dev->lock.lock_queue);
}

static unsigned long ffb_driver_get_map_ofs(drm_map_t *map)
{
	return (map->offset & 0xffffffff);
}

static unsigned long ffb_driver_get_reg_ofs(drm_device_t *dev)
{
	ffb_dev_priv_t *ffb_priv = (ffb_dev_priv_t *)dev->dev_private;

	if (ffb_priv)
		return ffb_priv->card_phys_base;

	return 0;
}

static void ffb_driver_register_fns(drm_device_t *dev)
{
	DRM(fops).get_unmapped_area = ffb_get_unmapped_area;
	dev->fn_tbl.release = ffb_driver_release;
	dev->fn_tbl.presetup = ffb_driver_presetup;
	dev->fn_tbl.pretakedown = ffb_driver_pretakedown;
	dev->fn_tbl.postcleanup = ffb_driver_postcleanup;
	dev->fn_tbl.kernel_context_switch = ffb_context_switch;
	dev->fn_tbl.kernel_context_switch_unlock = ffb_driver_kernel_context_switch_unlock;
	dev->fn_tbl.get_map_ofs = ffb_driver_get_map_ofs;
	dev->fn_tbl.get_reg_ofs = ffb_driver_get_reg_ofs;
}