summaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c')
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c
new file mode 100644
index 000000000000..95f3e460e648
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_sync.c
@@ -0,0 +1,373 @@
+/****************************************************************************
+*
+* The MIT License (MIT)
+*
+* Copyright (c) 2014 - 2018 Vivante Corporation
+*
+* 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, sublicense,
+* 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS 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.
+*
+*****************************************************************************
+*
+* The GPL License (GPL)
+*
+* Copyright (C) 2014 - 2018 Vivante Corporation
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* 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.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*****************************************************************************
+*
+* Note: This software is released under dual MIT and GPL licenses. A
+* recipient may use this file under the terms of either the MIT license or
+* GPL License. If you wish to use only one license not the other, you can
+* indicate your decision by deleting one of the above license notices in your
+* version of this file.
+*
+*****************************************************************************/
+
+
+#include <gc_hal.h>
+#include <gc_hal_base.h>
+
+#if gcdANDROID_NATIVE_FENCE_SYNC
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include "gc_hal_kernel_sync.h"
+#include "gc_hal_kernel_linux.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0)
+
+static struct sync_pt * viv_sync_pt_dup(struct sync_pt *sync_pt)
+{
+ gceSTATUS status;
+ struct viv_sync_pt *pt;
+ struct viv_sync_pt *src;
+ struct viv_sync_timeline *obj;
+
+ src = (struct viv_sync_pt *)sync_pt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+ obj = (struct viv_sync_timeline *)sync_pt_parent(sync_pt);
+#else
+ obj = (struct viv_sync_timeline *)sync_pt->parent;
+#endif
+
+ /* Create the new sync_pt. */
+ pt = (struct viv_sync_pt *)
+ sync_pt_create(&obj->obj, sizeof(struct viv_sync_pt));
+
+ pt->stamp = src->stamp;
+
+ /* Reference signal. */
+ status = gckOS_MapSignal(obj->os,
+ src->signal,
+ gcvNULL /* (gctHANDLE) _GetProcessID() */,
+ &pt->signal);
+
+ if (gcmIS_ERROR(status)) {
+ sync_pt_free((struct sync_pt *)pt);
+ return NULL;
+ }
+
+ return (struct sync_pt *)pt;
+}
+
+static int viv_sync_pt_has_signaled(struct sync_pt *sync_pt)
+{
+ gceSTATUS status;
+ struct viv_sync_pt *pt;
+ struct viv_sync_timeline *obj;
+
+ pt = (struct viv_sync_pt *)sync_pt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+ obj = (struct viv_sync_timeline *)sync_pt_parent(sync_pt);
+#else
+ obj = (struct viv_sync_timeline *)sync_pt->parent;
+#endif
+
+ status = _QuerySignal(obj->os, pt->signal);
+
+ if (gcmIS_ERROR(status)) {
+ /* Error. */
+ return -1;
+ }
+
+ return (int) status;
+}
+
+static int viv_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
+{
+ int ret;
+ struct viv_sync_pt *pt1 = (struct viv_sync_pt *)a;
+ struct viv_sync_pt *pt2 = (struct viv_sync_pt *)b;
+
+ ret = (pt1->stamp < pt2->stamp) ? -1
+ : (pt1->stamp == pt2->stamp) ? 0
+ : 1;
+
+ return ret;
+}
+
+static void viv_sync_pt_free(struct sync_pt *sync_pt)
+{
+ struct viv_sync_pt *pt;
+ struct viv_sync_timeline *obj;
+
+ pt = (struct viv_sync_pt *)sync_pt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+ obj = (struct viv_sync_timeline *)sync_pt_parent(sync_pt);
+#else
+ obj = (struct viv_sync_timeline *)sync_pt->parent;
+#endif
+
+ gckOS_DestroySignal(obj->os, pt->signal);
+}
+
+static void viv_timeline_value_str(struct sync_timeline *timeline,
+ char *str, int size)
+{
+ struct viv_sync_timeline *obj;
+
+ obj = (struct viv_sync_timeline *)timeline;
+ snprintf(str, size, "stamp_%llu", obj->stamp);
+}
+
+static void viv_pt_value_str(struct sync_pt *sync_pt, char *str, int size)
+{
+ struct viv_sync_pt *pt;
+
+ pt = (struct viv_sync_pt *)sync_pt;
+ snprintf(str, size, "signal_%lu@stamp_%llu",
+ (unsigned long)pt->signal, pt->stamp);
+}
+
+static struct sync_timeline_ops viv_timeline_ops =
+{
+ .driver_name = "viv_gpu_sync",
+ .dup = viv_sync_pt_dup,
+ .has_signaled = viv_sync_pt_has_signaled,
+ .compare = viv_sync_pt_compare,
+ .free_pt = viv_sync_pt_free,
+ .timeline_value_str = viv_timeline_value_str,
+ .pt_value_str = viv_pt_value_str,
+};
+
+struct viv_sync_timeline * viv_sync_timeline_create(const char *name, gckOS os)
+{
+ struct viv_sync_timeline * obj;
+
+ obj = (struct viv_sync_timeline *)
+ sync_timeline_create(&viv_timeline_ops, sizeof(struct viv_sync_timeline), name);
+
+ obj->os = os;
+ obj->stamp = 0;
+
+ return obj;
+}
+
+struct sync_pt * viv_sync_pt_create(struct viv_sync_timeline *obj,
+ gctSIGNAL Signal)
+{
+ gceSTATUS status;
+ struct viv_sync_pt *pt;
+
+ pt = (struct viv_sync_pt *)
+ sync_pt_create(&obj->obj, sizeof(struct viv_sync_pt));
+
+ pt->stamp = obj->stamp++;
+
+ /* Dup signal. */
+ status = gckOS_MapSignal(obj->os,
+ Signal,
+ gcvNULL /* (gctHANDLE) _GetProcessID() */,
+ &pt->signal);
+
+ if (gcmIS_ERROR(status)) {
+ sync_pt_free((struct sync_pt *)pt);
+ return NULL;
+ }
+
+ return (struct sync_pt *)pt;
+}
+
+#else /* v4.9.0 */
+
+struct viv_sync_timeline * viv_sync_timeline_create(const char *name, gckOS Os)
+{
+ struct viv_sync_timeline *timeline;
+
+ timeline = kmalloc(sizeof(struct viv_sync_timeline),
+ gcdNOWARN | GFP_KERNEL);
+
+ if (!timeline)
+ return NULL;
+
+ strncpy(timeline->name, name, sizeof(timeline->name));
+ timeline->context = fence_context_alloc(1);
+ atomic64_set(&timeline->seqno, 0);
+ timeline->os = Os;
+
+ return timeline;
+}
+
+void viv_sync_timeline_destroy(struct viv_sync_timeline *timeline)
+{
+ kfree(timeline);
+}
+
+static const char * viv_fence_get_driver_name(struct fence *fence)
+{
+ return "viv_gpu_sync";
+}
+
+static const char * viv_fence_get_timeline_name(struct fence *fence)
+{
+ struct viv_fence *f = (struct viv_fence *)fence;
+ return f->parent->name;
+}
+
+/* Same as fence_signaled. */
+static inline bool __viv_fence_signaled(struct fence *fence)
+{
+ struct viv_fence *f = (struct viv_fence *)fence;
+ struct viv_sync_timeline *timeline = f->parent;
+ gceSTATUS status;
+
+ status = _QuerySignal(timeline->os, f->signal);
+
+ return (status == gcvSTATUS_TRUE) ? true : false;
+}
+
+static bool viv_fence_enable_signaling(struct fence *fence)
+{
+ /* fence is locked already. */
+ return !__viv_fence_signaled(fence);
+}
+
+static bool viv_fence_signaled(struct fence *fence)
+{
+ /* fence could be locked, could be not. */
+ return __viv_fence_signaled(fence);
+}
+
+static void viv_fence_release(struct fence *fence)
+{
+ struct viv_fence *f = (struct viv_fence *)fence;
+ struct viv_sync_timeline *timeline = f->parent;
+
+ if (f->signal)
+ gckOS_DestroySignal(timeline->os, f->signal);
+
+ kfree(fence);
+}
+
+static struct fence_ops viv_fence_ops =
+{
+ .get_driver_name = viv_fence_get_driver_name,
+ .get_timeline_name = viv_fence_get_timeline_name,
+ .enable_signaling = viv_fence_enable_signaling,
+ .signaled = viv_fence_signaled,
+ .wait = fence_default_wait,
+ .release = viv_fence_release,
+};
+
+struct fence * viv_fence_create(struct viv_sync_timeline *timeline,
+ gcsSIGNAL *signal)
+{
+ gceSTATUS status;
+ struct viv_fence *fence;
+ struct fence *old_fence = NULL;
+ unsigned seqno;
+
+ fence = kzalloc(sizeof(struct viv_fence), gcdNOWARN | GFP_KERNEL);
+
+ if (!fence)
+ return NULL;
+
+ /* Reference signal in fence. */
+ status = gckOS_MapSignal(timeline->os, (gctSIGNAL)(uintptr_t)signal->id,
+ NULL, &fence->signal);
+
+ if (gcmIS_ERROR(status)) {
+ kfree(fence);
+ return NULL;
+ }
+
+ spin_lock_init(&fence->lock);
+
+ fence->parent = timeline;
+
+ seqno = (unsigned)atomic64_inc_return(&timeline->seqno);
+
+ fence_init((struct fence *)fence, &viv_fence_ops,
+ &fence->lock, timeline->context, seqno);
+
+ /*
+ * Reference fence in signal.
+ * Be aware of recursive reference!!
+ */
+ spin_lock(&signal->lock);
+
+ if (signal->fence) {
+ old_fence = signal->fence;
+ signal->fence = NULL;
+ }
+
+ if (!signal->done) {
+ signal->fence = (struct fence*)fence;
+ fence_get((struct fence*)fence);
+ }
+
+ spin_unlock(&signal->lock);
+
+ if (old_fence)
+ fence_put(old_fence);
+
+ if (!signal->fence) {
+ /* Fence already signaled. */
+ gckOS_DestroySignal(timeline->os, fence->signal);
+ fence->signal = NULL;
+
+ fence_signal_locked((struct fence*)fence);
+ }
+
+ return (struct fence*)fence;
+}
+
+#endif /* v4.9.0 */
+
+#endif