tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / mali / linux / mali_sync.c
1 /*
2  * Copyright (C) 2011-2012 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 /**
12  * @file mali_sync.c
13  *
14  */
15
16 #include <linux/seq_file.h>
17 #include <linux/sync.h>
18
19 #include "mali_osk.h"
20 #include "mali_kernel_common.h"
21
22 struct mali_sync_timeline
23 {
24         struct sync_timeline timeline;
25         atomic_t counter;
26         atomic_t signalled;
27 };
28
29 struct mali_sync_pt
30 {
31         struct sync_pt pt;
32         u32 order;
33         s32 error;
34 };
35
36 static inline struct mali_sync_timeline *to_mali_sync_timeline(struct sync_timeline *timeline)
37 {
38         return container_of(timeline, struct mali_sync_timeline, timeline);
39 }
40
41 static inline struct mali_sync_pt *to_mali_sync_pt(struct sync_pt *pt)
42 {
43         return container_of(pt, struct mali_sync_pt, pt);
44 }
45
46 static struct sync_pt *timeline_dup(struct sync_pt *pt)
47 {
48         struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
49         struct mali_sync_pt *new_mpt;
50         struct sync_pt *new_pt = sync_pt_create(pt->parent, sizeof(struct mali_sync_pt));
51
52         if (!new_pt)
53         {
54                 return NULL;
55         }
56
57         new_mpt = to_mali_sync_pt(new_pt);
58         new_mpt->order = mpt->order;
59
60         return new_pt;
61
62 }
63
64 static int timeline_has_signaled(struct sync_pt *pt)
65 {
66         struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
67         struct mali_sync_timeline *mtl = to_mali_sync_timeline(pt->parent);
68         long diff;
69
70         if (0 != mpt->error)
71         {
72                 return mpt->error;
73         }
74
75         diff = atomic_read(&mtl->signalled) - mpt->order;
76
77         return diff >= 0;
78 }
79
80 static int timeline_compare(struct sync_pt *a, struct sync_pt *b)
81 {
82         struct mali_sync_pt *ma = container_of(a, struct mali_sync_pt, pt);
83         struct mali_sync_pt *mb = container_of(b, struct mali_sync_pt, pt);
84
85         long diff = ma->order - mb->order;
86
87         if (diff < 0)
88         {
89                 return -1;
90         }
91         else if (diff == 0)
92         {
93                 return 0;
94         }
95         else
96         {
97                 return 1;
98         }
99 }
100
101 static void timeline_print_tl(struct seq_file *s, struct sync_timeline *sync_timeline)
102 {
103         struct mali_sync_timeline *mtl = to_mali_sync_timeline(sync_timeline);
104
105         seq_printf(s, "%u, %u", atomic_read(&mtl->signalled), atomic_read(&mtl->counter));
106 }
107
108 static void timeline_print_pt(struct seq_file *s, struct sync_pt *sync_pt)
109 {
110         struct mali_sync_pt *mpt = to_mali_sync_pt(sync_pt);
111
112         seq_printf(s, "%u", mpt->order);
113
114 }
115
116 static struct sync_timeline_ops mali_timeline_ops = {
117         .driver_name    = "Mali",
118         .dup            = timeline_dup,
119         .has_signaled   = timeline_has_signaled,
120         .compare        = timeline_compare,
121         .print_obj      = timeline_print_tl,
122         .print_pt       = timeline_print_pt
123 };
124
125 int mali_sync_timeline_is_ours(struct sync_timeline *timeline)
126 {
127         return (timeline->ops == &mali_timeline_ops);
128 }
129
130 struct sync_timeline *mali_sync_timeline_alloc(const char * name)
131 {
132         struct sync_timeline *tl;
133         struct mali_sync_timeline *mtl;
134
135         tl = sync_timeline_create(&mali_timeline_ops,
136                                   sizeof(struct mali_sync_timeline), name);
137         if (!tl)
138         {
139                 return NULL;
140         }
141
142         /* Set the counter in our private struct */
143         mtl = to_mali_sync_timeline(tl);
144         atomic_set(&mtl->counter, 0);
145         atomic_set(&mtl->signalled, 0);
146
147         return tl;
148 }
149
150 struct sync_pt *mali_sync_pt_alloc(struct sync_timeline *parent)
151 {
152         struct sync_pt *pt = sync_pt_create(parent, sizeof(struct mali_sync_pt));
153         struct mali_sync_timeline *mtl = to_mali_sync_timeline(parent);
154         struct mali_sync_pt *mpt;
155
156         if (!pt)
157         {
158                 return NULL;
159         }
160
161         mpt = to_mali_sync_pt(pt);
162         mpt->order = atomic_inc_return(&mtl->counter);
163         mpt->error = 0;
164
165         return pt;
166 }
167
168 void mali_sync_signal_pt(struct sync_pt *pt, int error)
169 {
170         struct mali_sync_pt *mpt = to_mali_sync_pt(pt);
171         struct mali_sync_timeline *mtl = to_mali_sync_timeline(pt->parent);
172         int signalled;
173         long diff;
174
175         if (0 != error)
176         {
177                 MALI_DEBUG_ASSERT(0 > error);
178                 mpt->error = error;
179         }
180
181         do {
182
183                 signalled = atomic_read(&mtl->signalled);
184
185                 diff = signalled - mpt->order;
186
187                 if (diff > 0)
188                 {
189                         /* The timeline is already at or ahead of this point. This should not happen unless userspace
190                          * has been signalling fences out of order, so warn but don't violate the sync_pt API.
191                          * The warning is only in debug builds to prevent a malicious user being able to spam dmesg.
192                          */
193                         MALI_DEBUG_PRINT_ERROR(("Sync points were triggerd in a different order to allocation!\n"));
194                         return;
195                 }
196         } while (atomic_cmpxchg(&mtl->signalled, signalled, mpt->order) != signalled);
197
198         sync_timeline_signal(pt->parent);
199 }