/* 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 #include #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 * @drm_id: ID number of owning DRM Object * Returns: Pointer to timeline name string */ #define SDE_FENCE_TIMELINE_NAME(fence) \ (((struct sw_sync_timeline *)fence->timeline)->obj.name) int sde_fence_init(struct sde_fence *fence, const char *name, uint32_t drm_id) { 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->commit_count = 0; fence->done_count = 0; fence->drm_id = drm_id; 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; SDE_EVT32(fence->drm_id, 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; SDE_EVT32(fence->drm_id, 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); } SDE_EVT32(fence->drm_id, fence->done_count, ((struct sw_sync_timeline *) fence->timeline)->value); mutex_unlock(&fence->fence_lock); } #endif