/*
 * drm_sync_helper.h: software fence and helper functions for fences and
 * reservations used for dma buffer access synchronization between drivers.
 *
 * Copyright 2014 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef _DRM_SYNC_HELPER_H_
#define _DRM_SYNC_HELPER_H_

#include <linux/dma-fence.h>
#include <linux/reservation.h>
#include <linux/atomic.h>
#include <linux/workqueue.h>

/**
 * Create software fence
 * @context: execution context
 * @seqno: the sequence number of this fence inside the execution context
 */
struct dma_fence *drm_sw_fence_new(u64 context,
			unsigned seqno);

/**
 * Signal and decrease reference count for a fence if it exists
 * @fence: fence to signal
 *
 * Utility function called when owner access to object associated with fence is
 * finished (e.g. GPU done with rendering).
 */
static inline void drm_fence_signal_and_put(struct dma_fence **fence)
{
	if (*fence) {
		dma_fence_signal(*fence);
		dma_fence_put(*fence);
		*fence = NULL;
	}
}

struct drm_reservation_cb;

struct drm_reservation_fence_cb {
	struct dma_fence_cb base;
	struct drm_reservation_cb *parent;
	struct dma_fence *fence;
};

/**
 * Callback executed when all fences in reservation callback are signaled
 * @rcb: reservation callback structure
 * @context: context provided by user at init time
 */
typedef void (*drm_reservation_cb_func_t)(struct drm_reservation_cb *rcb,
					  void *context);

/**
 * Reservation callback structure
 * @work: work context in which func is executed
 * @fence_cbs: fence callbacks array
 * @num_fence_cbs: number of fence callbacks
 * @count: count of signaled fences, when it drops to 0 func is called
 * @func: callback to execute when all fences are signaled
 * @context: context provided by user during initialization
 *
 * It is safe and expected that func will destroy this structure before
 * returning.
 */
struct drm_reservation_cb {
	struct work_struct work;
	struct drm_reservation_fence_cb **fence_cbs;
	unsigned num_fence_cbs;
	atomic_t count;
	void *context;
	drm_reservation_cb_func_t func;
};

/**
 * Initialize reservation callback
 * @rcb: reservation callback structure to initialize
 * @func: function to call when all fences are signaled
 * @context: parameter to call func with
 */
void drm_reservation_cb_init(struct drm_reservation_cb *rcb,
			     drm_reservation_cb_func_t func,
			     void *context);

/**
 * Add fences from reservation object to callback
 * @rcb: reservation callback structure
 * @resv: reservation object
 * @exclusive: (for exclusive wait) when true add all fences, otherwise only
 *    exclusive fence
 */
int drm_reservation_cb_add(struct drm_reservation_cb *rcb,
			   struct reservation_object *resv,
			   bool exclusive);

/**
 * Finish adding fences
 * @rcb: reservation callback structure
 *
 * It will trigger callback worker if all fences were signaled before.
 */
void drm_reservation_cb_done(struct drm_reservation_cb *rcb);

/**
 * Cleanup reservation callback structure
 * @rcb: reservation callback structure
 *
 * Can be called to cancel primed reservation callback.
 */
void drm_reservation_cb_fini(struct drm_reservation_cb *rcb);

/**
 * Add reservation to array of reservations
 * @resv: reservation to add
 * @resvs: array of reservations
 * @excl_resvs_bitmap: bitmap for exclusive reservations
 * @num_resvs: number of reservations in array
 * @exclusive: bool to store in excl_resvs_bitmap
 */
void
drm_add_reservation(struct reservation_object *resv,
			struct reservation_object **resvs,
			unsigned long *excl_resvs_bitmap,
			unsigned int *num_resvs, bool exclusive);

/**
 * Acquire ww_mutex lock on all reservations in the array
 * @resvs: array of reservations
 * @num_resvs: number of reservations in the array
 * @ctx: ww mutex context
 */
int drm_lock_reservations(struct reservation_object **resvs,
			unsigned int num_resvs, struct ww_acquire_ctx *ctx);

/**
 * Release ww_mutex lock on all reservations in the array
 * @resvs: array of reservations
 * @num_resvs: number of reservations in the array
 * @ctx: ww mutex context
 */
void drm_unlock_reservations(struct reservation_object **resvs,
				unsigned int num_resvs,
				struct ww_acquire_ctx *ctx);

#endif
