| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) 2007 Ben Skeggs. | 
					
						
							|  |  |  |  * 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, sublicense, 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 NONINFRINGEMENT. | 
					
						
							|  |  |  |  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-02 18:01:07 +01:00
										 |  |  | #include <drm/drmP.h>
 | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-09 14:22:19 +01:00
										 |  |  | #include <linux/ktime.h>
 | 
					
						
							|  |  |  | #include <linux/hrtimer.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 08:17:34 +10:00
										 |  |  | #include "nouveau_drm.h"
 | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | #include "nouveau_dma.h"
 | 
					
						
							| 
									
										
										
										
											2012-07-20 08:17:34 +10:00
										 |  |  | #include "nouveau_fence.h"
 | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | #include <engine/fifo.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 09:48:30 +10:00
										 |  |  | struct fence_work { | 
					
						
							|  |  |  | 	struct work_struct base; | 
					
						
							|  |  |  | 	struct list_head head; | 
					
						
							|  |  |  | 	void (*func)(void *); | 
					
						
							|  |  |  | 	void *data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | nouveau_fence_signal(struct nouveau_fence *fence) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct fence_work *work, *temp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry_safe(work, temp, &fence->work, head) { | 
					
						
							|  |  |  | 		schedule_work(&work->base); | 
					
						
							|  |  |  | 		list_del(&work->head); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fence->channel = NULL; | 
					
						
							|  |  |  | 	list_del(&fence->head); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | void | 
					
						
							|  |  |  | nouveau_fence_context_del(struct nouveau_fence_chan *fctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct nouveau_fence *fence, *fnext; | 
					
						
							|  |  |  | 	spin_lock(&fctx->lock); | 
					
						
							|  |  |  | 	list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { | 
					
						
							| 
									
										
										
										
											2013-05-07 09:48:30 +10:00
										 |  |  | 		nouveau_fence_signal(fence); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	spin_unlock(&fctx->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | nouveau_fence_context_new(struct nouveau_fence_chan *fctx) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-22 11:55:54 +10:00
										 |  |  | 	INIT_LIST_HEAD(&fctx->flip); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	INIT_LIST_HEAD(&fctx->pending); | 
					
						
							|  |  |  | 	spin_lock_init(&fctx->lock); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 09:48:30 +10:00
										 |  |  | static void | 
					
						
							|  |  |  | nouveau_fence_work_handler(struct work_struct *kwork) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct fence_work *work = container_of(kwork, typeof(*work), base); | 
					
						
							|  |  |  | 	work->func(work->data); | 
					
						
							|  |  |  | 	kfree(work); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | nouveau_fence_work(struct nouveau_fence *fence, | 
					
						
							|  |  |  | 		   void (*func)(void *), void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct nouveau_channel *chan = fence->channel; | 
					
						
							|  |  |  | 	struct nouveau_fence_chan *fctx; | 
					
						
							|  |  |  | 	struct fence_work *work = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nouveau_fence_done(fence)) { | 
					
						
							|  |  |  | 		func(data); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fctx = chan->fence; | 
					
						
							|  |  |  | 	work = kmalloc(sizeof(*work), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!work) { | 
					
						
							|  |  |  | 		WARN_ON(nouveau_fence_wait(fence, false, false)); | 
					
						
							|  |  |  | 		func(data); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&fctx->lock); | 
					
						
							|  |  |  | 	if (!fence->channel) { | 
					
						
							|  |  |  | 		spin_unlock(&fctx->lock); | 
					
						
							|  |  |  | 		kfree(work); | 
					
						
							|  |  |  | 		func(data); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	INIT_WORK(&work->base, nouveau_fence_work_handler); | 
					
						
							|  |  |  | 	work->func = func; | 
					
						
							|  |  |  | 	work->data = data; | 
					
						
							|  |  |  | 	list_add(&work->head, &fence->work); | 
					
						
							|  |  |  | 	spin_unlock(&fctx->lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 08:17:34 +10:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | nouveau_fence_update(struct nouveau_channel *chan) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-19 10:51:42 +10:00
										 |  |  | 	struct nouveau_fence_chan *fctx = chan->fence; | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	struct nouveau_fence *fence, *fnext; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	spin_lock(&fctx->lock); | 
					
						
							|  |  |  | 	list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { | 
					
						
							| 
									
										
										
										
											2013-02-14 13:20:17 +10:00
										 |  |  | 		if (fctx->read(chan) < fence->sequence) | 
					
						
							| 
									
										
										
										
											2012-03-21 13:51:03 +10:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 09:48:30 +10:00
										 |  |  | 		nouveau_fence_signal(fence); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 		nouveau_fence_unref(&fence); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	spin_unlock(&fctx->lock); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-19 10:51:42 +10:00
										 |  |  | 	struct nouveau_fence_chan *fctx = chan->fence; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	fence->channel  = chan; | 
					
						
							| 
									
										
										
										
											2013-12-11 11:34:41 +01:00
										 |  |  | 	fence->timeout  = jiffies + (15 * HZ); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	fence->sequence = ++fctx->sequence; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-14 13:20:17 +10:00
										 |  |  | 	ret = fctx->emit(fence); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	if (!ret) { | 
					
						
							|  |  |  | 		kref_get(&fence->kref); | 
					
						
							|  |  |  | 		spin_lock(&fctx->lock); | 
					
						
							|  |  |  | 		list_add_tail(&fence->head, &fctx->pending); | 
					
						
							|  |  |  | 		spin_unlock(&fctx->lock); | 
					
						
							| 
									
										
										
										
											2010-11-24 10:30:22 +10:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | nouveau_fence_done(struct nouveau_fence *fence) | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	if (fence->channel) | 
					
						
							|  |  |  | 		nouveau_fence_update(fence->channel); | 
					
						
							|  |  |  | 	return !fence->channel; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2013-10-03 07:02:29 +10:00
										 |  |  | nouveau_fence_wait_uevent_handler(void *data, int index) | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-03 07:02:29 +10:00
										 |  |  | 	struct nouveau_fence_priv *priv = data; | 
					
						
							| 
									
										
										
										
											2013-08-27 16:12:54 -04:00
										 |  |  | 	wake_up_all(&priv->waiting); | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 	return NVKM_EVENT_KEEP; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct nouveau_channel *chan = fence->channel; | 
					
						
							|  |  |  | 	struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device); | 
					
						
							|  |  |  | 	struct nouveau_fence_priv *priv = chan->drm->fence; | 
					
						
							| 
									
										
										
										
											2013-10-03 07:02:29 +10:00
										 |  |  | 	struct nouveau_eventh *handler; | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-03 07:02:29 +10:00
										 |  |  | 	ret = nouveau_event_new(pfifo->uevent, 0, | 
					
						
							|  |  |  | 				nouveau_fence_wait_uevent_handler, | 
					
						
							|  |  |  | 				priv, &handler); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nouveau_event_get(handler); | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (fence->timeout) { | 
					
						
							|  |  |  | 		unsigned long timeout = fence->timeout - jiffies; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (time_before(jiffies, fence->timeout)) { | 
					
						
							|  |  |  | 			if (intr) { | 
					
						
							|  |  |  | 				ret = wait_event_interruptible_timeout( | 
					
						
							|  |  |  | 						priv->waiting, | 
					
						
							|  |  |  | 						nouveau_fence_done(fence), | 
					
						
							|  |  |  | 						timeout); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				ret = wait_event_timeout(priv->waiting, | 
					
						
							|  |  |  | 						nouveau_fence_done(fence), | 
					
						
							|  |  |  | 						timeout); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ret >= 0) { | 
					
						
							|  |  |  | 			fence->timeout = jiffies + ret; | 
					
						
							|  |  |  | 			if (time_after_eq(jiffies, fence->timeout)) | 
					
						
							|  |  |  | 				ret = -EBUSY; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (intr) { | 
					
						
							|  |  |  | 			ret = wait_event_interruptible(priv->waiting, | 
					
						
							|  |  |  | 					nouveau_fence_done(fence)); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			wait_event(priv->waiting, nouveau_fence_done(fence)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-03 07:02:29 +10:00
										 |  |  | 	nouveau_event_ref(NULL, &handler); | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 	if (unlikely(ret < 0)) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | int | 
					
						
							| 
									
										
										
										
											2012-04-30 12:51:48 +10:00
										 |  |  | nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 	struct nouveau_channel *chan = fence->channel; | 
					
						
							|  |  |  | 	struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL; | 
					
						
							| 
									
										
										
										
											2011-03-09 14:22:19 +01:00
										 |  |  | 	unsigned long sleep_time = NSEC_PER_MSEC / 1000; | 
					
						
							|  |  |  | 	ktime_t t; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-31 14:57:33 +10:00
										 |  |  | 	while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) { | 
					
						
							|  |  |  | 		ret = nouveau_fence_wait_uevent(fence, intr); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	while (!nouveau_fence_done(fence)) { | 
					
						
							|  |  |  | 		if (fence->timeout && time_after_eq(jiffies, fence->timeout)) { | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 			ret = -EBUSY; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 12:51:48 +10:00
										 |  |  | 		__set_current_state(intr ? TASK_INTERRUPTIBLE : | 
					
						
							|  |  |  | 					   TASK_UNINTERRUPTIBLE); | 
					
						
							| 
									
										
										
										
											2011-03-09 14:22:19 +01:00
										 |  |  | 		if (lazy) { | 
					
						
							|  |  |  | 			t = ktime_set(0, sleep_time); | 
					
						
							|  |  |  | 			schedule_hrtimeout(&t, HRTIMER_MODE_REL); | 
					
						
							|  |  |  | 			sleep_time *= 2; | 
					
						
							|  |  |  | 			if (sleep_time > NSEC_PER_MSEC) | 
					
						
							|  |  |  | 				sleep_time = NSEC_PER_MSEC; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (intr && signal_pending(current)) { | 
					
						
							| 
									
										
										
										
											2009-12-15 11:04:25 +10:00
										 |  |  | 			ret = -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__set_current_state(TASK_RUNNING); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | int | 
					
						
							|  |  |  | nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-14 13:20:17 +10:00
										 |  |  | 	struct nouveau_fence_chan *fctx = chan->fence; | 
					
						
							| 
									
										
										
										
											2012-05-04 16:25:47 +10:00
										 |  |  | 	struct nouveau_channel *prev; | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 08:17:34 +10:00
										 |  |  | 	prev = fence ? fence->channel : NULL; | 
					
						
							| 
									
										
										
										
											2012-05-04 16:25:47 +10:00
										 |  |  | 	if (prev) { | 
					
						
							|  |  |  | 		if (unlikely(prev != chan && !nouveau_fence_done(fence))) { | 
					
						
							| 
									
										
										
										
											2013-02-14 13:20:17 +10:00
										 |  |  | 			ret = fctx->sync(fence, prev, chan); | 
					
						
							| 
									
										
										
										
											2012-05-04 16:25:47 +10:00
										 |  |  | 			if (unlikely(ret)) | 
					
						
							|  |  |  | 				ret = nouveau_fence_wait(fence, true, false); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | static void | 
					
						
							|  |  |  | nouveau_fence_del(struct kref *kref) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref); | 
					
						
							|  |  |  | 	kfree(fence); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | nouveau_fence_unref(struct nouveau_fence **pfence) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (*pfence) | 
					
						
							|  |  |  | 		kref_put(&(*pfence)->kref, nouveau_fence_del); | 
					
						
							|  |  |  | 	*pfence = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct nouveau_fence * | 
					
						
							|  |  |  | nouveau_fence_ref(struct nouveau_fence *fence) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-13 10:23:46 +10:00
										 |  |  | 	if (fence) | 
					
						
							|  |  |  | 		kref_get(&fence->kref); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	return fence; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2013-02-14 13:43:21 +10:00
										 |  |  | nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, | 
					
						
							|  |  |  | 		  struct nouveau_fence **pfence) | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct nouveau_fence *fence; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-19 10:51:42 +10:00
										 |  |  | 	if (unlikely(!chan->fence)) | 
					
						
							| 
									
										
										
										
											2012-04-30 13:55:29 +10:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	fence = kzalloc(sizeof(*fence), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!fence) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-02-14 13:43:21 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 09:48:30 +10:00
										 |  |  | 	INIT_LIST_HEAD(&fence->work); | 
					
						
							| 
									
										
										
										
											2013-02-14 13:43:21 +10:00
										 |  |  | 	fence->sysmem = sysmem; | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 	kref_init(&fence->kref); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 18:19:49 +01:00
										 |  |  | 	ret = nouveau_fence_emit(fence, chan); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		nouveau_fence_unref(&fence); | 
					
						
							| 
									
										
										
										
											2012-04-30 13:30:00 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*pfence = fence; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } |