summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2006-09-29Make handling of dev_priv->vblank_pipe more robust.Michel Dänzer
Initialize it to default value if it hasn't been set by the X server yet. In i915_vblank_pipe_set(), only update dev_priv->vblank_pipe and call i915_enable_interrupt() if the argument passed from userspace is valid to avoid corrupting dev_priv->vblank_pipe on invalid arguments. (cherry picked from 87c57cba1a70221fc570b253bf3b24682ef6b894 commit)
2006-09-29DRM_I915_VBLANK_SWAP ioctl: Take drm_vblank_seq_type_t instead of pipe number.Michel Dänzer
Handle relative as well as absolute target sequence numbers. Return error if target sequence has already passed, so userspace can deal with this situation as it sees fit. On success, return the sequence number of the vertical blank when the buffer swap is expected to take place. Also add DRM_IOCTL_I915_VBLANK_SWAP definition for userspace code that may want to use ioctl() instead of drmCommandWriteRead(). (cherry picked from d5a0f107511e128658e2d5e15bd7e6215c507f29 commit)
2006-09-29Change first valid DRM drawable ID to be 1 instead of 0.Michel Dänzer
This makes it easier for userspace to know when it needs to allocate an ID. Also free drawable information memory when it's no longer needed. (cherry picked from df7551ef7334d728ec0371423661bb403d3e270a commit)
2006-09-29Add copyright notice.Michel Dänzer
(cherry picked from d04751facea36cb888c7510b126658fdbc4277d5 commit)
2006-09-29i915: Add ioctl for scheduling buffer swaps at vertical blanks.Michel Dänzer
This uses the core facility to schedule a driver callback that will be called ASAP after the given vertical blank interrupt with the HW lock held. (cherry picked from 257771fa290b62d4d2ad896843cf3a207978d0bb commit)
2006-09-29Locking and memory management fixes.Michel Dänzer
(cherry picked from 23d2833aaa37a33b9ddcf06cc796f59befc0d360 commit)
2006-09-29Drop tasklet locked driver callback when uninstalling IRQ.Michel Dänzer
(cherry picked from b9f3009160d8bd1a26a77d6f1616f1679c7b969d commit)
2006-09-29Export drm_get_drawable_info symbol from core.Michel Dänzer
(cherry picked from 43f8675534c7e95efbc92eaf2c8cc43aef95f125 commit)
2006-09-29Hook up DRM_IOCTL_UPDATE_DRAW ioctl.Michel Dänzer
(cherry picked from 98a89504589427a76c3f5cfa2266962a1a212672 commit)
2006-09-29Only reallocate cliprect memory if the number of cliprects changes.Michel Dänzer
Also improve diagnostic output. (cherry picked from af48be1096221d551319c67a9e782b50ef58fefd commit)
2006-09-29Add support for tracking drawable information to coreMichel Dänzer
Actually make the existing ioctls for adding and removing drawables do something useful, and add another ioctl for the X server to update drawable information. The only kind of drawable information tracked so far is cliprects. (cherry picked from 29598e5253ff5c085ccf63580fd24b84db848424 commit)
2006-09-29Add support for interrupt triggered driver callback with lock held to DRM core.Michel Dänzer
(cherry picked from d817cc1f30060fcc4a85a05b2de8a2a1687421b5 commit)
2006-09-29Add support for secondary vertical blank interrupt to i915 driver.Michel Dänzer
When the vertical blank interrupt is enabled for both pipes, pipe A is considered primary and pipe B secondary. When it's only enabled for one pipe, it's always considered primary for backwards compatibility. (cherry picked from 0c7d7f43610f705e8536a949cf2407efaa5ec217 commit)
2006-09-29Add support for secondary vertical blank interrupt to DRM core.Michel Dänzer
(cherry picked from ab351505f36a6c66405ea7604378268848340a42 commit)
2006-09-29Add a new buffer flag.Thomas Hellstrom
Fix up some comments.
2006-09-29Fix buffer manager takedown error.Thomas Hellstrom
Prepare for the possibility to evict all buffers from vram / agp. This will be used by the X server when, for example, switching vts.
2006-09-29i915: Only schedule vblank tasklet if there are scheduled swaps pending.Michel Dänzer
This fixes issues on X server startup with versions of xf86-video-intel that enable the IRQ before they have a context ID.
2006-09-28i915: Avoid mis-counting vblank interrupts when they're only enabled for pipe A.Michel Dänzer
It looks like 'after a while', I915REG_INT_IDENTITY_R for some reason always has VSYNC_PIPEB_FLAG set in the interrupt handler, even though pipe B is disabled. So we only increase dev->vbl_received if the corresponding bit is also set in dev->vblank_pipe.
2006-09-28i915: Bump minor for swap scheduling ioctl and secondary vblank support.Michel Dänzer
2006-09-28i915_vblank_swap: Add support for DRM_VBLANK_NEXTONMISS.Michel Dänzer
2006-09-28Only return EBUSY after we've established we need to schedule a new swap.Michel Dänzer
2006-09-28Core vsync: Don't clobber target sequence number when scheduling signal.Michel Dänzer
It looks like this would have caused signals to always get sent on the next vertical blank, regardless of the sequence number.
2006-09-28Core vsync: Add flag DRM_VBLANK_NEXTONMISS.Michel Dänzer
When this flag is set and the target sequence is missed, wait for the next vertical blank instead of returning immediately.
2006-09-28Fix 'sequence has passed' condition in i915_vblank_swap().Michel Dänzer
2006-09-28Add SAREA fileds for determining which pipe to sync window buffer swaps to.Michel Dänzer
2006-09-28Add definition of DRM_VBLANK_SECONDARY.Michel Dänzer
2006-09-28Make handling of dev_priv->vblank_pipe more robust.Michel Dänzer
Initialize it to default value if it hasn't been set by the X server yet. In i915_vblank_pipe_set(), only update dev_priv->vblank_pipe and call i915_enable_interrupt() if the argument passed from userspace is valid to avoid corrupting dev_priv->vblank_pipe on invalid arguments.
2006-09-28DRM_I915_VBLANK_SWAP ioctl: Take drm_vblank_seq_type_t instead of pipe number.Michel Dänzer
Handle relative as well as absolute target sequence numbers. Return error if target sequence has already passed, so userspace can deal with this situation as it sees fit. On success, return the sequence number of the vertical blank when the buffer swap is expected to take place. Also add DRM_IOCTL_I915_VBLANK_SWAP definition for userspace code that may want to use ioctl() instead of drmCommandWriteRead().
2006-09-28Change first valid DRM drawable ID to be 1 instead of 0.Michel Dänzer
This makes it easier for userspace to know when it needs to allocate an ID. Also free drawable information memory when it's no longer needed.
2006-09-28Add copyright notice.Michel Dänzer
2006-09-28i915: Add ioctl for scheduling buffer swaps at vertical blanks.Michel Dänzer
This uses the core facility to schedule a driver callback that will be called ASAP after the given vertical blank interrupt with the HW lock held.
2006-09-28Locking and memory management fixes.Michel Dänzer
2006-09-28Drop tasklet locked driver callback when uninstalling IRQ.Michel Dänzer
2006-09-28Export drm_get_drawable_info symbol from core.Michel Dänzer
2006-09-28Hook up DRM_IOCTL_UPDATE_DRAW ioctl.Michel Dänzer
2006-09-28Only reallocate cliprect memory if the number of cliprects changes.Michel Dänzer
Also improve diagnostic output.
2006-09-28Add support for tracking drawable information to coreMichel Dänzer
Actually make the existing ioctls for adding and removing drawables do something useful, and add another ioctl for the X server to update drawable information. The only kind of drawable information tracked so far is cliprects.
2006-09-28Add support for interrupt triggered driver callback with lock held to DRM core.Michel Dänzer
2006-09-28Add support for secondary vertical blank interrupt to i915 driver.Michel Dänzer
When the vertical blank interrupt is enabled for both pipes, pipe A is considered primary and pipe B secondary. When it's only enabled for one pipe, it's always considered primary for backwards compatibility.
2006-09-28Add support for secondary vertical blank interrupt to DRM core.Michel Dänzer
2006-09-28Libdrm version bump and naming.Thomas Hellstrom
2006-09-28Don't enable fence / buffer objects on non-linux systems.Thomas Hellstrom
Bump driver minor and date.
2006-09-27Activate error message that was never hit since it was maskedThomas Hellstrom
by drm_lock_transfer. Ifdef out drm_lock_transfer. I see no use for it currently. Should be removed.
2006-09-27Fix racy buffer object destruction.Thomas Hellstrom
2006-09-27Fix tt fixed size that slipped through in previous commit.Thomas Hellstrom
2006-09-27Adapt to architecture-specific hooks for gatt pages.Thomas Hellstrom
2006-09-26Silence valgrind.Thomas Hellstrom
2006-09-26Remove the call to drm_lock_transfer, since it is not used anymore.Thomas Hellstrom
Fix up drm_lock_free to retain the last locking context information.
2006-09-26Allow for a driver to overload the ttm backend object methods.Thomas Hellstrom
2006-09-25Add /proc filesystem buffer / fence object accounting.Thomas Hellstrom
Check for NULL pointer in the i915 flush handler. Remove i915_sync_flush declaration.
ref='#n692'>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 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
/*
 * Copyright 2003-2008 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.
 *
 * Authors:
 *     Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
 *     Dave Airlie
 *     Keith Packard
 *     ... ?
 */

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

#if DRM_DEBUG_CODE
#define DRM_DEBUG_RELOCATION	(drm_debug != 0)
#else
#define DRM_DEBUG_RELOCATION	0
#endif

enum i915_buf_idle {
	I915_RELOC_UNCHECKED,
	I915_RELOC_IDLE,
	I915_RELOC_BUSY
};

struct i915_relocatee_info {
	struct drm_buffer_object *buf;
	unsigned long offset;
	uint32_t *data_page;
	unsigned page_offset;
	struct drm_bo_kmap_obj kmap;
	int is_iomem;
	int dst;
	int idle;
	int performed_ring_relocs;
#ifdef DRM_KMAP_ATOMIC_PROT_PFN
	unsigned long pfn;
	pgprot_t pg_prot;
#endif
};

struct drm_i915_validate_buffer {
	struct drm_buffer_object *buffer;
	int presumed_offset_correct;
	void __user *data;
	int ret;
	enum i915_buf_idle idle;
};

/*
 * I'd like to use MI_STORE_DATA_IMM here, but I can't make
 * it work. Seems like GART writes are broken with that
 * instruction. Also I'm not sure that MI_FLUSH will
 * act as a memory barrier for that instruction. It will
 * for this single dword 2D blit.
 */

static void i915_emit_ring_reloc(struct drm_device *dev, uint32_t offset,
				 uint32_t value)
{
	struct drm_i915_private *dev_priv =
	    (struct drm_i915_private *)dev->dev_private;

	RING_LOCALS;
	i915_kernel_lost_context(dev);
	BEGIN_LP_RING(6);
	OUT_RING((0x02 << 29) | (0x40 << 22) | (0x3 << 20) | (0x3));
	OUT_RING((0x3 << 24) | (0xF0 << 16) | (0x40));
	OUT_RING((0x1 << 16) | (0x4));
	OUT_RING(offset);
	OUT_RING(value);
	OUT_RING(0);
	ADVANCE_LP_RING();
}

static void i915_dereference_buffers_locked(struct drm_i915_validate_buffer
					    *buffers, unsigned num_buffers)
{
	while (num_buffers--)
		drm_bo_usage_deref_locked(&buffers[num_buffers].buffer);
}

int i915_apply_reloc(struct drm_file *file_priv, int num_buffers,
		     struct drm_i915_validate_buffer *buffers,
		     struct i915_relocatee_info *relocatee, uint32_t * reloc)
{
	unsigned index;
	unsigned long new_cmd_offset;
	u32 val;
	int ret, i;
	int buf_index = -1;

	/*
	 * FIXME: O(relocs * buffers) complexity.
	 */

	for (i = 0; i <= num_buffers; i++)
		if (buffers[i].buffer)
			if (reloc[2] == buffers[i].buffer->base.hash.key)
				buf_index = i;

	if (buf_index == -1) {
		DRM_ERROR("Illegal relocation buffer %08X\n", reloc[2]);
		return -EINVAL;
	}

	/*
	 * Short-circuit relocations that were correctly
	 * guessed by the client
	 */
	if (buffers[buf_index].presumed_offset_correct && !DRM_DEBUG_RELOCATION)
		return 0;

	new_cmd_offset = reloc[0];
	if (!relocatee->data_page ||
	    !drm_bo_same_page(relocatee->offset, new_cmd_offset)) {
		struct drm_bo_mem_reg *mem = &relocatee->buf->mem;

		drm_bo_kunmap(&relocatee->kmap);
		relocatee->data_page = NULL;
		relocatee->offset = new_cmd_offset;

		if (unlikely(relocatee->idle == I915_RELOC_UNCHECKED)) {
		  ret = drm_bo_wait(relocatee->buf, 0, 1, 0, 0);
			if (ret)
				return ret;
			relocatee->idle = I915_RELOC_IDLE;
		}

		if (unlikely((mem->mem_type != DRM_BO_MEM_LOCAL) &&
			     (mem->flags & DRM_BO_FLAG_CACHED_MAPPED)))
			drm_bo_evict_cached(relocatee->buf);

		ret = drm_bo_kmap(relocatee->buf, new_cmd_offset >> PAGE_SHIFT,
				  1, &relocatee->kmap);
		if (ret) {
			DRM_ERROR
			    ("Could not map command buffer to apply relocs\n %08lx",
			     new_cmd_offset);
			return ret;
		}
		relocatee->data_page = drm_bmo_virtual(&relocatee->kmap,
						       &relocatee->is_iomem);
		relocatee->page_offset = (relocatee->offset & PAGE_MASK);
	}

	val = buffers[buf_index].buffer->offset;
	index = (reloc[0] - relocatee->page_offset) >> 2;

	/* add in validate */
	val = val + reloc[1];

	if (DRM_DEBUG_RELOCATION) {
		if (buffers[buf_index].presumed_offset_correct &&
		    relocatee->data_page[index] != val) {
			DRM_DEBUG
			    ("Relocation mismatch source %d target %d buffer %d user %08x kernel %08x\n",
			     reloc[0], reloc[1], buf_index,
			     relocatee->data_page[index], val);
		}
	}

	if (relocatee->is_iomem)
		iowrite32(val, relocatee->data_page + index);
	else
		relocatee->data_page[index] = val;
	return 0;
}

int i915_process_relocs(struct drm_file *file_priv,
			uint32_t buf_handle,
			uint32_t __user ** reloc_user_ptr,
			struct i915_relocatee_info *relocatee,
			struct drm_i915_validate_buffer *buffers,
			uint32_t num_buffers)
{
	int ret, reloc_stride;
	uint32_t cur_offset;
	uint32_t reloc_count;
	uint32_t reloc_type;
	uint32_t reloc_buf_size;
	uint32_t *reloc_buf = NULL;
	int i;

	/* do a copy from user from the user ptr */
	ret = get_user(reloc_count, *reloc_user_ptr);
	if (ret) {
		DRM_ERROR("Could not map relocation buffer.\n");
		goto out;
	}

	ret = get_user(reloc_type, (*reloc_user_ptr) + 1);
	if (ret) {
		DRM_ERROR("Could not map relocation buffer.\n");
		goto out;
	}

	if (reloc_type != 0) {
		DRM_ERROR("Unsupported relocation type requested\n");
		ret = -EINVAL;
		goto out;
	}

	reloc_buf_size =
	    (I915_RELOC_HEADER +
	     (reloc_count * I915_RELOC0_STRIDE)) * sizeof(uint32_t);
	reloc_buf = kmalloc(reloc_buf_size, GFP_KERNEL);
	if (!reloc_buf) {
		DRM_ERROR("Out of memory for reloc buffer\n");
		ret = -ENOMEM;
		goto out;
	}

	if (copy_from_user(reloc_buf, *reloc_user_ptr, reloc_buf_size)) {
		ret = -EFAULT;
		goto out;
	}

	/* get next relocate buffer handle */
	*reloc_user_ptr = (uint32_t *) * (unsigned long *)&reloc_buf[2];

	reloc_stride = I915_RELOC0_STRIDE * sizeof(uint32_t);	/* may be different for other types of relocs */

	DRM_DEBUG("num relocs is %d, next is %p\n", reloc_count,
		  *reloc_user_ptr);

	for (i = 0; i < reloc_count; i++) {
		cur_offset = I915_RELOC_HEADER + (i * I915_RELOC0_STRIDE);

		ret = i915_apply_reloc(file_priv, num_buffers, buffers,
				       relocatee, reloc_buf + cur_offset);
		if (ret)
			goto out;
	}

      out:
	if (reloc_buf)
		kfree(reloc_buf);

	if (relocatee->data_page) {
		drm_bo_kunmap(&relocatee->kmap);
		relocatee->data_page = NULL;
	}

	return ret;
}

static int i915_exec_reloc(struct drm_file *file_priv, drm_handle_t buf_handle,
			   uint32_t __user * reloc_user_ptr,
			   struct drm_i915_validate_buffer *buffers,
			   uint32_t buf_count)
{
	struct drm_device *dev = file_priv->minor->dev;
	struct i915_relocatee_info relocatee;
	int ret = 0;
	int b;

	/*
	 * Short circuit relocations when all previous
	 * buffers offsets were correctly guessed by
	 * the client
	 */
	if (!DRM_DEBUG_RELOCATION) {
		for (b = 0; b < buf_count; b++)
			if (!buffers[b].presumed_offset_correct)
				break;

		if (b == buf_count)
			return 0;
	}

	memset(&relocatee, 0, sizeof(relocatee));
	relocatee.idle = I915_RELOC_UNCHECKED;

	mutex_lock(&dev->struct_mutex);
	relocatee.buf = drm_lookup_buffer_object(file_priv, buf_handle, 1);
	mutex_unlock(&dev->struct_mutex);
	if (!relocatee.buf) {
		DRM_DEBUG("relocatee buffer invalid %08x\n", buf_handle);
		ret = -EINVAL;
		goto out_err;
	}

	mutex_lock(&relocatee.buf->mutex);
	while (reloc_user_ptr) {
		ret =
		    i915_process_relocs(file_priv, buf_handle, &reloc_user_ptr,
					&relocatee, buffers, buf_count);
		if (ret) {
			DRM_ERROR("process relocs failed\n");
			goto out_err1;
		}
	}

      out_err1:
	mutex_unlock(&relocatee.buf->mutex);
	drm_bo_usage_deref_unlocked(&relocatee.buf);
      out_err:
	return ret;
}

static void i915_clear_relocatee(struct i915_relocatee_info *relocatee)
{
	if (relocatee->data_page) {
#ifndef DRM_KMAP_ATOMIC_PROT_PFN
		drm_bo_kunmap(&relocatee->kmap);
#else
		kunmap_atomic(relocatee->data_page, KM_USER0);
#endif
		relocatee->data_page = NULL;
	}
	relocatee->buf = NULL;
	relocatee->dst = ~0;
}

static int i915_update_relocatee(struct i915_relocatee_info *relocatee,
				 struct drm_i915_validate_buffer *buffers,
				 unsigned int dst, unsigned long dst_offset)
{
	int ret;

	if (unlikely(dst != relocatee->dst || NULL == relocatee->buf)) {
		i915_clear_relocatee(relocatee);
		relocatee->dst = dst;
		relocatee->buf = buffers[dst].buffer;
		relocatee->idle = buffers[dst].idle;

		/*
		 * Check for buffer idle. If the buffer is busy, revert to
		 * ring relocations.
		 */

		if (relocatee->idle == I915_RELOC_UNCHECKED) {
			preempt_enable();
			mutex_lock(&relocatee->buf->mutex);

			ret = drm_bo_wait(relocatee->buf, 0, 1, 1, 0);
			if (ret == 0)
				relocatee->idle = I915_RELOC_IDLE;
			else {
				relocatee->idle = I915_RELOC_BUSY;
				relocatee->performed_ring_relocs = 1;
			}
			mutex_unlock(&relocatee->buf->mutex);
			preempt_disable();
			buffers[dst].idle = relocatee->idle;
		}
	}

	if (relocatee->idle == I915_RELOC_BUSY)
		return 0;

	if (unlikely(dst_offset > relocatee->buf->num_pages * PAGE_SIZE)) {
		DRM_ERROR("Relocation destination out of bounds.\n");
		return -EINVAL;
	}
	if (unlikely(!drm_bo_same_page(relocatee->page_offset, dst_offset) ||
		     NULL == relocatee->data_page)) {
#ifdef DRM_KMAP_ATOMIC_PROT_PFN
		if (NULL != relocatee->data_page) {
			kunmap_atomic(relocatee->data_page, KM_USER0);
			relocatee->data_page = NULL;
		}
		ret = drm_bo_pfn_prot(relocatee->buf, dst_offset,
				      &relocatee->pfn, &relocatee->pg_prot);
		if (ret) {
			DRM_ERROR("Can't map relocation destination.\n");
			return -EINVAL;
		}
		relocatee->data_page =
		    kmap_atomic_prot_pfn(relocatee->pfn, KM_USER0,
					 relocatee->pg_prot);
#else
		if (NULL != relocatee->data_page) {
			drm_bo_kunmap(&relocatee->kmap);
			relocatee->data_page = NULL;
		}

		ret = drm_bo_kmap(relocatee->buf, dst_offset >> PAGE_SHIFT,
				  1, &relocatee->kmap);
		if (ret) {
			DRM_ERROR("Can't map relocation destination.\n");
			return ret;
		}

		relocatee->data_page = drm_bmo_virtual(&relocatee->kmap,
						       &relocatee->is_iomem);
#endif
		relocatee->page_offset = dst_offset & PAGE_MASK;
	}
	return 0;
}

static int i915_apply_post_reloc(uint32_t reloc[],
				 struct drm_i915_validate_buffer *buffers,
				 uint32_t num_buffers,
				 struct i915_relocatee_info *relocatee)
{
	uint32_t reloc_buffer = reloc[2];
	uint32_t dst_buffer = reloc[3];
	uint32_t val;
	uint32_t index;
	int ret;

	if (likely(buffers[reloc_buffer].presumed_offset_correct))
		return 0;
	if (unlikely(reloc_buffer >= num_buffers)) {
		DRM_ERROR("Invalid reloc buffer index.\n");
		return -EINVAL;
	}