aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c')
-rw-r--r--drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c b/drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c
new file mode 100644
index 0000000..6293610
--- /dev/null
+++ b/drivers/gpu/mali400/r3p2/mali/linux/mali_sync.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * @file mali_sync.c
+ *
+ */
+
+#include <linux/seq_file.h>
+#include <linux/sync.h>
+#include <linux/timer.h>
+
+#include "mali_osk.h"
+#include "mali_kernel_common.h"
+
+struct mali_sync_timeline
+{
+ struct sync_timeline timeline;
+ atomic_t counter;
+ atomic_t signalled;
+};
+
+struct mali_sync_pt
+{
+ struct sync_pt pt;
+ u32 order;
+ s32 error;
+ struct timer_list timer;
+};
+
+static void mali_sync_timed_pt_timeout(unsigned long data);
+
+static inline struct mali_sync_timeline *to_mali_sync_timeline(struct sync_timeline *timeline)
+{
+ return container_of(timeline, struct mali_sync_timeline, timeline);
+}
+
+static inline struct mali_sync_pt *to_mali_sync_pt(struct sync_pt *pt)
+{
+ return container_of(pt, struct mali_sync_pt, pt);
+}
+
+static struct sync_pt *timeline_dup(struct sync_pt *pt)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
+ struct mali_sync_pt *new_mpt;
+ struct sync_pt *new_pt = sync_pt_create(pt->parent, sizeof(struct mali_sync_pt));
+
+ if (!new_pt)
+ {
+ return NULL;
+ }
+
+ new_mpt = to_mali_sync_pt(new_pt);
+ new_mpt->order = mpt->order;
+
+ return new_pt;
+
+}
+
+static int timeline_has_signaled(struct sync_pt *pt)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
+ struct mali_sync_timeline *mtl = to_mali_sync_timeline(pt->parent);
+ long diff;
+
+ if (0 != mpt->error)
+ {
+ return mpt->error;
+ }
+
+ diff = atomic_read(&mtl->signalled) - mpt->order;
+
+ return diff >= 0;
+}
+
+static int timeline_compare(struct sync_pt *a, struct sync_pt *b)
+{
+ struct mali_sync_pt *ma = container_of(a, struct mali_sync_pt, pt);
+ struct mali_sync_pt *mb = container_of(b, struct mali_sync_pt, pt);
+
+ long diff = ma->order - mb->order;
+
+ if (diff < 0)
+ {
+ return -1;
+ }
+ else if (diff == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+static void timeline_free_pt(struct sync_pt *pt)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
+
+ if (mpt->timer.function == mali_sync_timed_pt_timeout)
+ {
+ del_timer_sync(&mpt->timer);
+ }
+}
+
+static void timeline_print_tl(struct seq_file *s, struct sync_timeline *sync_timeline)
+{
+ struct mali_sync_timeline *mtl = to_mali_sync_timeline(sync_timeline);
+
+ seq_printf(s, "%u, %u", atomic_read(&mtl->signalled), atomic_read(&mtl->counter));
+}
+
+static void timeline_print_pt(struct seq_file *s, struct sync_pt *sync_pt)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(sync_pt);
+
+ seq_printf(s, "%u", mpt->order);
+
+}
+
+static struct sync_timeline_ops mali_timeline_ops = {
+ .driver_name = "Mali",
+ .dup = timeline_dup,
+ .has_signaled = timeline_has_signaled,
+ .compare = timeline_compare,
+ .free_pt = timeline_free_pt,
+ .print_obj = timeline_print_tl,
+ .print_pt = timeline_print_pt
+};
+
+int mali_sync_timeline_is_ours(struct sync_timeline *timeline)
+{
+ return (timeline->ops == &mali_timeline_ops);
+}
+
+struct sync_timeline *mali_sync_timeline_alloc(const char * name)
+{
+ struct sync_timeline *tl;
+ struct mali_sync_timeline *mtl;
+
+ tl = sync_timeline_create(&mali_timeline_ops,
+ sizeof(struct mali_sync_timeline), name);
+ if (!tl)
+ {
+ return NULL;
+ }
+
+ /* Set the counter in our private struct */
+ mtl = to_mali_sync_timeline(tl);
+ atomic_set(&mtl->counter, 0);
+ atomic_set(&mtl->signalled, 0);
+
+ return tl;
+}
+
+struct sync_pt *mali_sync_pt_alloc(struct sync_timeline *parent)
+{
+ struct sync_pt *pt = sync_pt_create(parent, sizeof(struct mali_sync_pt));
+ struct mali_sync_timeline *mtl = to_mali_sync_timeline(parent);
+ struct mali_sync_pt *mpt;
+
+ if (!pt)
+ {
+ return NULL;
+ }
+
+ mpt = to_mali_sync_pt(pt);
+ mpt->order = atomic_inc_return(&mtl->counter);
+ mpt->error = 0;
+
+ return pt;
+}
+
+static void mali_sync_timed_pt_timeout(unsigned long data)
+{
+ struct sync_pt *pt = (struct sync_pt *)data;
+
+ MALI_DEBUG_ASSERT_POINTER(pt);
+
+ mali_sync_signal_pt(pt, -ETIME);
+}
+
+struct sync_pt *mali_sync_timed_pt_alloc(struct sync_timeline *parent)
+{
+ struct sync_pt *pt;
+ struct mali_sync_pt *mpt;
+ const u32 timeout = msecs_to_jiffies(MALI_SYNC_TIMED_FENCE_TIMEOUT);
+
+ pt = mali_sync_pt_alloc(parent);
+ if (NULL == pt) return NULL;
+ mpt = to_mali_sync_pt(pt);
+
+ init_timer(&mpt->timer);
+
+ mpt->timer.function = mali_sync_timed_pt_timeout;
+ mpt->timer.data = (unsigned long)pt;
+ mpt->timer.expires = jiffies + timeout;
+
+ add_timer(&mpt->timer);
+
+ return pt;
+}
+
+/*
+ * Returns 0 if sync_pt has been committed and are ready for use, -ETIME if
+ * timeout already happened and the fence has been signalled.
+ *
+ * If an error occurs the sync point can not be used.
+ */
+int mali_sync_timed_commit(struct sync_pt *pt)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
+ int ret;
+
+ if (!mali_sync_timeline_is_ours(pt->parent))
+ {
+ return -EINVAL;
+ }
+
+ /* Stop timer */
+ ret = del_timer_sync(&mpt->timer);
+
+ if (0 == ret)
+ {
+ return -ETIME;
+ }
+
+ MALI_DEBUG_ASSERT(0 == timeline_has_signaled(pt));
+
+ return 0;
+}
+
+void mali_sync_signal_pt(struct sync_pt *pt, int error)
+{
+ struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
+ struct mali_sync_timeline *mtl = to_mali_sync_timeline(pt->parent);
+ int signalled;
+ long diff;
+
+ if (0 != error)
+ {
+ MALI_DEBUG_ASSERT(0 > error);
+ mpt->error = error;
+ }
+
+ do {
+
+ signalled = atomic_read(&mtl->signalled);
+
+ diff = signalled - mpt->order;
+
+ if (diff > 0)
+ {
+ /* The timeline is already at or ahead of this point. This should not happen unless userspace
+ * has been signalling fences out of order, so warn but don't violate the sync_pt API.
+ * The warning is only in debug builds to prevent a malicious user being able to spam dmesg.
+ */
+ MALI_DEBUG_PRINT_ERROR(("Sync points were triggerd in a different order to allocation!\n"));
+ return;
+ }
+ } while (atomic_cmpxchg(&mtl->signalled, signalled, mpt->order) != signalled);
+
+ sync_timeline_signal(pt->parent);
+}