summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2008-07-17i915: remove old broken vblank codeJesse Barnes
Remove the unused (and broken) "in vblank" code now that the core has been fixed to use a counter while interrupts are enabled. Also make the vblank pipe get/set ioctls into dumb stub functions, since with the new code we can no longer let userspace control whether vblank interrupts are enabled, or the core code will misbehave.
2008-07-17Avoid incorrect vblank wakeupsJesse Barnes
The current code uses the hw vblank counter exclusively, which can lead to wakeups during the active period rather than during the vblank period if the hw counter counts displayed frames rather than vblank periods. This change coverts the code over to using the counter while interrupts are enabled, fixing that issue. It also includes a couple of related changes: one to not enable the new enable/disable behavior until the modeset ioctl is called (to preserve old client behavior) and another to account for lost events due to mode setting with the new counter scheme. BSD will require similar changes to its drm_irq.c code, but they should be straightforward.
2008-07-16FreeBSD: Fix radeon buildRobert Noland
2008-07-16BSD: change drm_locked_task*() to use the same scheme as linux.Owain Gordon Ainsworth
The current code can sleep in an interrupt handler, that is bad. So instead if we can't grab the lock, flag it and run the tasklet on unlock. Signed-off-by: Robert Noland <rnoland@2hip.net>
2008-07-15[FreeBSD] We aren't allowed to hold locks over bus_dma_tag_create or ↵Robert Noland
bus_dmamem_alloc.
2008-07-15This is a modified version of Hong's patch from last month, with a fewHong Liu
modifications to make it work correctly on my test hardware (altered the backlight write function, made it enable the legacy backlight controller interrupts on mobile hardware, sorted the interrupt function so we don't get an excessive number of vblank interrupts). This lets the backlight keys on my T61 work properly, though there's a 750msec or so delay between the request and the brightness actually changing - this sounds awfully like the hardware spinning waiting for a status flag to become ready, but as far as I can tell they're all set correctly. If anyone can figure out what's wrong here, it'd be nice to know. Some of the functions are still stubs and just tell the hardware that the request was successful. These can be filled in as kernel modesetting gets integrated. I think it's worth getting this in anyway, since it's required for backlight control to work properly on some new platforms. Signed-off-by: Matthew Garrett <mjg@redhat.com>
2008-07-15drm: fix missing symbol exportDave Airlie
2008-07-15drm: add fix for PAT on radeon with 2.6.26Dave Airlie
2008-07-15drm/pcigart: fix warningDave Airlie
2008-07-14intel-gem: Disable tiling if we get junk from the MCHBAR read.Eric Anholt
One of our systems has been returning 0xffffffff from all MCHBAR reads, which means we'll need to figure out why, or add an alternate detection method.
2008-07-12modesetting/helper: fix array overrun - count should be reset hereDave Airlie
2008-07-11intel-gem: Add two new ioctls for managing tiling on objects.Eric Anholt
Various chips have exciting interactions between the CPU and the GPU's different ways of accessing interleaved memory, so we need some kernel assistance in determining how it works. Only fully tested on GM965 so far.
2008-07-09Merge remote branch 'origin/modesetting-101' into modesetting-gemDave Airlie
2008-07-08xgi: use true/false instead of TRUE/FALSEmartin capitanio
2008-07-08nouveau: interface changes for nv5x 3dBen Skeggs
2008-07-07Add back flink, open and close ioctls.Kristian Høgsberg
They fell through the cracks in 86accbcb.
2008-07-07[intel-gem] typo fix in DRM_ERROREric Anholt
2008-07-07Merge commit 'origin/drm-gem' into ms-gemKristian Høgsberg
Conflicts: linux-core/drmP.h linux-core/drm_drv.c linux-core/drm_stub.c linux-core/i915_drv.c linux-core/i915_gem.c shared-core/i915_drv.h shared-core/i915_irq.c
2008-07-07Use lowercase bool constants.Kristian Høgsberg
2008-07-07Synchronize the DDC EDID read to it's fb_ddc.c counterpartAlan Hourihane
2008-07-06NV50: init gamma storageMaarten Maathuis
2008-07-06NV50: fix minor bug in fbcon crtc selectionMaarten Maathuis
2008-07-06modesetting-101: Rename DPMS modes to avoid compatibility issues with xorg ↵Maarten Maathuis
definitions.
2008-07-06modesetting-101: rename modeflags, as to avoid conflicts with the xorg ↵Maarten Maathuis
definitions
2008-07-06NV50: minor fixMaarten Maathuis
2008-07-05NV50: remove edid when monitor is gone, improve fbcon, misc fixesMaarten Maathuis
- This should avoid switching crtc's when going to fbcon.
2008-07-05NV50: fix switch_fb and connector_is_digitalMaarten Maathuis
2008-07-05modesetting-101: Make the interface variable names a little more consistent ↵Maarten Maathuis
+ modeprint changes. - All things are now called _id when they are id's. - modeprint now accepts driver name as first argument.
2008-07-04modesetting-101: tv_left_margin_property shouldn't be immutable.Maarten Maathuis
2008-07-04Forgot the 0'th element for the tv property.Maarten Maathuis
2008-07-04modesetting-101: Move some defines used for enumeration into the public header.Maarten Maathuis
- Otherwise userspace has no idea of the meaning.
2008-07-04[modesetting-101] Add subconnector and select_subconnector properties.Maarten Maathuis
- These facilitate DVI-I and tv-out that can drive multiple types of signals.
2008-07-04modesetting: rip out all of the generation code.Dave Airlie
not needed, hotplug will work just as well hopefully.
2008-07-03libdrm: fix typo in commentMaarten Maathuis
2008-07-03nv50: s/FALSE/false && s/TRUE/trueMaarten Maathuis
2008-07-03[drm-sysfs] connected is ambigious in the context of a connector, replace ↵Maarten Maathuis
with enabled
2008-07-03NV50: replace active by enabledMaarten Maathuis
2008-07-03[modeseting-101] add connected field to sysfsMaarten Maathuis
2008-07-03Forgot to fix the modeprint test.Maarten Maathuis
2008-07-03[libdrm] count connectors and such has no place in a crtc objectMaarten Maathuis
2008-07-03modesetting: lookup blob using correct identifier.Dave Airlie
blob is a blob not a connector
2008-07-03tests: Fix faulty error messages in modeprintJakob Bornecrantz
2008-07-03drm: fix encoders get permissionsDave Airlie
2008-07-02tests: modeprint s/fb/fd/Jakob Bornecrantz
2008-07-02tests: Updated modeprintJakob Bornecrantz
2008-07-03i915: official name for GM45 chipsetZhenyu Wang
Signed-off-by: Zhenyu Wang <zhenyu.z.wang@intel.com>
2008-07-02NV50: basic fbcon + misc fixesMaarten Maathuis
- There is one fb, used for as many outputs as possible. - Eventually smaller screens will be scaled to see the full console, but for the moment this'll do.
2008-07-02tests: Improved and renamed the mode app to modeprintJakob Bornecrantz
2008-07-01i915: only use tiled blits on 965+Jesse Barnes
When scheduled swaps occur, we need to blit between front & back buffers. I the buffers are tiled, we need to set the appropriate XY_SRC_COPY tile bit, only on 965 chips, since it will cause corruption on pre-965 (e.g. 945). Bug reported by and fix tested by Tomas Janousek <tomi@nomi.cz>. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2008-07-01Revert "i915: only use tiled blits on 965+"Jesse Barnes
This reverts commit 727d4f1d1667e43b3558bd5f6ed6dc2cd9c29401, somehow git deleted the symlink and replaced it with the file.
href='#n644'>644 645 646 647 648 649 650 651 652 653 654 655 656 657 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 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
 */
/*
 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
 * 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 TUNGSTEN 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.
 * 
 */

#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"

#define IS_I965G(dev)  (dev->pdev->device == 0x2972 || \
			dev->pdev->device == 0x2982 || \
			dev->pdev->device == 0x2992 || \
			dev->pdev->device == 0x29A2)


/* 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;
}

static 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->status_page_dmah) {
			drm_pci_free(dev, dev_priv->status_page_dmah);
			/* 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->cpp = init->cpp;
	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 */
	dev_priv->status_page_dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 
	    0xffffffff);

	if (!dev_priv->status_page_dmah) {
		dev->dev_private = (void *)dev_priv;
		i915_dma_cleanup(dev);
		DRM_ERROR("Can not allocate hardware status page\n");
		return DRM_ERR(ENOMEM);
	}
	dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
	dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;
	
	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_dma_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;
}

static 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_dma_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;

	if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)
		return DRM_ERR(EINVAL);

	BEGIN_LP_RING((dwords+1)&~1);

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

	     if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) {

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

		OUT_RING(cmd);

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

	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);
	}

	if (IS_I965G(dev)) {
		BEGIN_LP_RING(4);
		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
		OUT_RING(DR4);
		ADVANCE_LP_RING();
	} else {
		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;
}

/* XXX: Emitting the counter should really be moved to part of the IRQ
 * emit.  For now, do it in both places:
 */

static void i915_emit_breadcrumb(drm_device_t *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

	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();
#ifdef I915_HAVE_FENCE
	drm_fence_flush_old(dev, dev_priv->counter);
#endif
}


int i915_emit_mi_flush(drm_device_t *dev, uint32_t flush)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	uint32_t flush_cmd = CMD_MI_FLUSH;
	RING_LOCALS;

	flush_cmd |= flush;

	i915_kernel_lost_context(dev);

	BEGIN_LP_RING(4);
	OUT_RING(flush_cmd);
	OUT_RING(0);
	OUT_RING(0);
	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;
	}

	i915_emit_breadcrumb( dev );
	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();
		}
	}

	i915_emit_breadcrumb( dev );
	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();
#ifdef I915_HAVE_FENCE
	drm_fence_flush_old(dev, dev_priv->counter);
#endif
	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__);
}

static int i915_flush_ioctl(DRM_IOCTL_ARGS)
{
	DRM_DEVICE;

	LOCK_TEST_WITH_RETURN(dev, filp);

	return i915_quiescent(dev);
}

static 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);

	LOCK_TEST_WITH_RETURN(dev, filp);

	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;
}

static 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);

	LOCK_TEST_WITH_RETURN(dev, filp);

	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);