/**************************************************************************
*
* Copyright © 2011-2014 VMware, Inc., Palo Alto, CA., USA
* 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
* THE COPYRIGHT HOLDERS, AUTHORS 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 <drm/drmP.h>
#include "vmwgfx_drv.h"
#define VMW_FENCE_WRAP (1 << 31)
struct vmw_fence_manager {
int num_fence_objects;
struct vmw_private *dev_priv;
spinlock_t lock;
struct list_head fence_list;
struct work_struct work;
u32 user_fence_size;
u32 fence_size;
u32 event_fence_action_size;
bool fifo_down;
struct list_head cleanup_list;
uint32_t pending_actions[VMW_ACTION_MAX];
struct mutex goal_irq_mutex;
bool goal_irq_on; /* Protected by @goal_irq_mutex */
bool seqno_valid; /* Protected by @lock, and may not be set to true
without the @goal_irq_mutex held. */
u64 ctx;
};
struct vmw_user_fence {
struct ttm_base_object base;
struct vmw_fence_obj fence;
};
/**
* struct vmw_event_fence_action - fence action that delivers a drm event.
*
* @e: A struct drm_pending_event that controls the event delivery.
* @action: A struct vmw_fence_action to hook up to a fence.
* @fence: A referenced pointer to the fence to keep it alive while @action
* hangs on it.
* @dev: Pointer to a struct drm_device so we can access the event stuff.
* @kref: Both @e and @action has destructors, so we need to refcount.
* @size: Size accounted for this object.
* @tv_sec: If non-null, the variable pointed to will be assigned
* current time tv_sec val when the fence signals.
* @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will
* be assigned the current time tv_usec val when the fence signals.
*/
struct vmw_event_fence_action {
struct vmw_fence_action action;
struct drm_pending_event *event;
struct vmw_fence_obj *fence;
struct drm_device *dev;
uint32_t *tv_sec;
uint32_t *tv_usec;
};
static struct vmw_fence_manager *
fman_from_fence(struct vmw_fence_obj *fence)
{
return container_of(fence->base.lock, struct vmw_fence_manager, lock);
}
/**
* Note on fencing subsystem usage of irqs:
* Typically the vmw_fences_update function is called
*
* a) When a new fence seqno has been submitted by the fifo code.
* b) On-demand when we have waiters. Sleeping waiters will switch on the
* ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE
* irq is received. When the last fence waiter is gone, that IRQ is masked
* away.
*
* In situations where there are no waiters and we don't submit any new fences,
* fence objects may not be signaled. This is perfectly OK, since there are
* no consumers of the signaled data, but that is NOT ok when there are fence
* actions attached to a fence. The fencing subsystem then makes use of the
* FENCE_GOAL irq and sets the fence goal seqno to that of the next fence
* which has an action attached, and each time vmw_fences_update is called,
* the subsystem makes sure the fence goal seqno is updated.
*
* The fence goal seqno irq is on as long as there are unsignaled fence
* objects with actions attached to them.
*/
static void vmw_fence_obj_destroy(struct dma_fence *f)
{
struct vmw_fence_obj *fence =
container_of(f, struct vmw_fence_obj, base);
struct vmw_fence_manager *fman = fman_from_fence(fence);
spin_lock(&fman->lock);
list_del_init(&fence->head);
--fman->num_fence_objects;
spin_unlock(&fman->lock);
fence->destroy(fence);
}
static const char *vmw_fence_get_driver_name(struct dma_fence *f)
{
return "vmwgfx";
}
static const char *vmw_fence_get_timeline_name(struct dma_fence *f)
{
return "svga";
}
static bool vmw_fence_enable_signaling(struct dma_fence *f)
{
struct vmw_fence_obj *fence =
container_of(f, struct vmw_fence_obj, base);
struct vmw_fence_manager *fman = fman_from_fence(fence);
struct vmw_private *dev_priv = fman->dev_priv;
u32 *fifo_mem = dev_priv->mmio_virt;
u32 seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE);
if (seqno - fence->base.seqno < VMW_FENCE_WRAP)
return false;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
return true;
}
struct vmwgfx_wait_cb {
struct dma_fence_cb base;
struct task_struct *task;
};
static void
vmwgfx_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
{
struct vmwgfx_wait_cb *wait =
container_of(cb, struct vmwgfx_wait_cb, base);
wake_up_process(wait->task);
}
static void __vmw_fences_update(struct vmw_fence_manager *fman);
static long vmw_fence_wait(struct dma_fence *f, bool intr, signed long timeout)
{
struct vmw_fence_obj *