blob: 23e5614ac1d67f8aaae70893530b538cc8f9d782 [file] [log] [blame]
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <sync.h>
#include <sw_sync.h>
#include "msm_drv.h"
#include "sde_kms.h"
#include "sde_fence.h"
void *sde_sync_get(uint64_t fd)
{
/* force signed compare, fdget accepts an int argument */
return (signed int)fd >= 0 ? sync_fence_fdget(fd) : NULL;
}
void sde_sync_put(void *fence)
{
if (fence)
sync_fence_put(fence);
}
int sde_sync_wait(void *fence, long timeout_ms)
{
if (!fence)
return -EINVAL;
return sync_fence_wait(fence, timeout_ms);
}
uint32_t sde_sync_get_name_prefix(void *fence)
{
char *name;
uint32_t i, prefix;
if (!fence)
return 0x0;
name = ((struct sync_fence *)fence)->name;
prefix = 0x0;
for (i = 0; i < sizeof(uint32_t) && name[i]; ++i)
prefix = (prefix << CHAR_BIT) | name[i];
return prefix;
}
#if IS_ENABLED(CONFIG_SW_SYNC)
/**
* _sde_fence_create_fd - create fence object and return an fd for it
* This function is NOT thread-safe.
* @timeline: Timeline to associate with fence
* @name: Name for fence
* @val: Timeline value at which to signal the fence
* Return: File descriptor on success, or error code on error
*/
static int _sde_fence_create_fd(void *timeline, const char *name, uint32_t val)
{
struct sync_pt *sync_pt;
struct sync_fence *fence;
signed int fd = -EINVAL;
if (!timeline) {
SDE_ERROR("invalid timeline\n");
goto exit;
}
if (!name)
name = "sde_fence";
/* create sync point */
sync_pt = sw_sync_pt_create(timeline, val);
if (sync_pt == NULL) {
SDE_ERROR("failed to create sync point, %s\n", name);
goto exit;
}
/* create fence */
fence = sync_fence_create(name, sync_pt);
if (fence == NULL) {
sync_pt_free(sync_pt);
SDE_ERROR("couldn't create fence, %s\n", name);
goto exit;
}
/* create fd */
fd = get_unused_fd_flags(0);
if (fd < 0) {
SDE_ERROR("failed to get_unused_fd_flags(), %s\n", name);
sync_fence_put(fence);
goto exit;
}
sync_fence_install(fence, fd);
exit:
return fd;
}
/**
* SDE_FENCE_TIMELINE_NAME - macro for accessing s/w timeline's name
* @fence: Pointer to sde fence structure
* Returns: Pointer to timeline name string
*/
#define SDE_FENCE_TIMELINE_NAME(fence) \
(((struct sw_sync_timeline *)fence->timeline)->obj.name)
int sde_fence_init(void *dev,
struct sde_fence *fence,
const char *name)
{
if (!fence) {
SDE_ERROR("invalid argument(s)\n");
return -EINVAL;
}
fence->timeline = sw_sync_timeline_create(name ? name : "sde");
if (!fence->timeline) {
SDE_ERROR("failed to create timeline\n");
return -ENOMEM;
}
fence->dev = dev;
fence->commit_count = 0;
fence->done_count = 0;
mutex_init(&fence->fence_lock);
return 0;
}
void sde_fence_deinit(struct sde_fence *fence)
{
if (!fence) {
SDE_ERROR("invalid fence\n");
return;
}
mutex_destroy(&fence->fence_lock);
if (fence->timeline)
sync_timeline_destroy(fence->timeline);
}
int sde_fence_prepare(struct sde_fence *fence)
{
if (!fence) {
SDE_ERROR("invalid fence\n");
return -EINVAL;
}
mutex_lock(&fence->fence_lock);
++fence->commit_count;
MSM_EVTMSG(fence->dev,
SDE_FENCE_TIMELINE_NAME(fence),
fence->commit_count,
fence->done_count);
mutex_unlock(&fence->fence_lock);
return 0;
}
int sde_fence_create(struct sde_fence *fence, uint64_t *val, int offset)
{
uint32_t trigger_value;
int fd, rc = -EINVAL;
if (!fence || !fence->timeline || !val) {
SDE_ERROR("invalid argument(s), fence %pK, pval %pK\n",
fence, val);
} else {
/*
* Allow created fences to have a constant offset with respect
* to the timeline. This allows us to delay the fence signalling
* w.r.t. the commit completion (e.g., an offset of +1 would
* cause fences returned during a particular commit to signal
* after an additional delay of one commit, rather than at the
* end of the current one.
*/
mutex_lock(&fence->fence_lock);
trigger_value = fence->commit_count + (int32_t)offset;
fd = _sde_fence_create_fd(fence->timeline,
SDE_FENCE_TIMELINE_NAME(fence),
trigger_value);
*val = fd;
MSM_EVTMSG(fence->dev,
SDE_FENCE_TIMELINE_NAME(fence),
trigger_value,
fd);
mutex_unlock(&fence->fence_lock);
if (fd >= 0)
rc = 0;
}
return rc;
}
void sde_fence_signal(struct sde_fence *fence, bool is_error)
{
if (!fence || !fence->timeline) {
SDE_ERROR("invalid fence, %pK\n", fence);
return;
}
mutex_lock(&fence->fence_lock);
if ((fence->done_count - fence->commit_count) < 0)
++fence->done_count;
else
SDE_ERROR("detected extra signal attempt!\n");
/*
* Always advance 'done' counter,
* but only advance timeline if !error
*/
if (!is_error) {
int32_t val;
val = fence->done_count;
val -= ((struct sw_sync_timeline *)
fence->timeline)->value;
if (val < 0)
SDE_ERROR("invalid value\n");
else
sw_sync_timeline_inc(fence->timeline, (int)val);
}
MSM_EVTMSG(fence->dev,
SDE_FENCE_TIMELINE_NAME(fence),
fence->done_count,
((struct sw_sync_timeline *)
fence->timeline)->value);
mutex_unlock(&fence->fence_lock);
}
#endif