/**************************************************************************
*
* Copyright © 2011 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. */
};
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 list_head fpriv_head;
struct drm_pending_event *event;
struct vmw_fence_obj *fence;
struct drm_device *dev;
uint32_t *tv_sec;
uint32_t *tv_usec;
};
/**
* 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_locked(struct kref *kref)
{
struct vmw_fence_obj *fence =
container_of(kref, struct vmw_fence_obj, kref);
struct vmw_fence_manager *fman = fence->fman;
unsigned int num_fences;
list_del_init(&fence->head);
num_fences = --fman->num_fence_objects;
spin_unlock_irq(&fman->lock);
if (fence->destroy)
fence->destroy(fence);
else
kfree(fence);
spin_lock_irq(&fman->lock);
}
/**
* Execute signal actions on fences recently signaled.
* This is done from a workqueue so we don't have to execute
* signal actions from atomic context.
*/
static void vmw_fence_work_func(struct work_struct *work)
{
struct vmw_fence_manager *fman =
container_of(work, struct vmw_fence_manager, work);
struct list_head list;
struct vmw_fence_action *action, *next_action;
bool seqno_valid;
do {
INIT_LIST_HEAD(&list);
mutex_lock(&fman->goal_irq_mutex);
spin_lock_irq(&fman->lock);
list_splice_init(&fman->cleanup_list, &list);
seqno_valid = fman->seqno_valid;
spin_unlock_irq(&fman->lock);
if (!seqno_valid && fman->goal_irq_on) {
fman->goal_irq_on = false;
vmw_goal_waiter_remove(fman->dev_priv);
}
mutex_unlock(&fman->goal_irq_mutex);
if (list_empty(&list))
return;
/*
* At this point, only we should be able to manipulate the
* list heads of the actions we have on the private list.