1 /*
2 * Allwinner SoCs display driver.
3 *
4 * Copyright (C) 2018 Allwinner.
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11 /*
12 * This driver is modified from sw_sync, because we cannot
13 * access sw_sync through debugfs after Android-11.
14 */
15
16 #include <linux/dma-fence.h>
17 #include <linux/file.h>
18 #include <linux/fs.h>
19 #include <linux/uaccess.h>
20 #include <linux/sched.h>
21 #include <linux/slab.h>
22 #include <linux/sync_file.h>
23 #include <linux/miscdevice.h>
24
25 /*
26 * struct syncfence_create_data
27 * @value: the seqno to initialise the fence with
28 * @name: the name of the new sync point
29 * @fence: return the fd of the new sync_file with the created fence
30 */
31 struct syncfence_create_data {
32 __u32 value;
33 char name[32];
34 __s32 fence;
35 };
36
37 #define SYNCFENCE_IOC_MAGIC 'Z'
38 #define SYNCFENCE_IOC_CREATE_FENCE \
39 _IOWR(SYNCFENCE_IOC_MAGIC, 0, struct syncfence_create_data)
40 #define SYNCFENCE_IOC_INC _IOW(SYNCFENCE_IOC_MAGIC, 1, __u32)
41
42 struct fence_timeline {
43 struct kref ref;
44 char name[32];
45
46 u64 context;
47 int value;
48 struct list_head pt_list;
49 spinlock_t lock;
50
51 struct list_head timeline_list;
52 };
53
54 struct syncfence {
55 struct dma_fence base;
56 struct list_head link;
57 };
58
59 static LIST_HEAD(timeline_list_head);
60 static DEFINE_SPINLOCK(timeline_list_lock);
61
62 static const struct dma_fence_ops timeline_fence_ops;
63
dma_fence_to_syncfence(struct dma_fence * fence)64 static inline struct syncfence *dma_fence_to_syncfence(struct dma_fence *fence)
65 {
66 if (fence->ops != &timeline_fence_ops)
67 return NULL;
68 return container_of(fence, struct syncfence, base);
69 }
70
dma_fence_parent(struct dma_fence * fence)71 static inline struct fence_timeline *dma_fence_parent(struct dma_fence *fence)
72 {
73 return container_of(fence->lock, struct fence_timeline, lock);
74 }
75
fence_timeline_create(const char * name)76 static struct fence_timeline *fence_timeline_create(const char *name)
77 {
78 unsigned long flags;
79 struct fence_timeline *timeline;
80
81 timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
82 if (!timeline)
83 return NULL;
84
85 kref_init(&timeline->ref);
86 timeline->context = dma_fence_context_alloc(1);
87 strlcpy(timeline->name, name, sizeof(timeline->name));
88 INIT_LIST_HEAD(&timeline->pt_list);
89
90 /* add the new timeline into timeline_list_head */
91 spin_lock_irqsave(&timeline_list_lock, flags);
92 list_add_tail(&timeline->timeline_list, &timeline_list_head);
93 spin_unlock_irqrestore(&timeline_list_lock, flags);
94
95 return timeline;
96 }
97
fence_timeline_free(struct kref * ref)98 static void fence_timeline_free(struct kref *ref)
99 {
100 unsigned long flags;
101 struct fence_timeline *timeline =
102 container_of(ref, struct fence_timeline, ref);
103
104 /* remove it from timeline_list_head */
105 spin_lock_irqsave(&timeline_list_lock, flags);
106 list_del(&timeline->timeline_list);
107 spin_unlock_irqrestore(&timeline_list_lock, flags);
108
109 kfree(timeline);
110 }
111
fence_timeline_get(struct fence_timeline * timeline)112 static void fence_timeline_get(struct fence_timeline *timeline)
113 {
114 kref_get(&timeline->ref);
115 }
116
fence_timeline_put(struct fence_timeline * timeline)117 static void fence_timeline_put(struct fence_timeline *timeline)
118 {
119 kref_put(&timeline->ref, fence_timeline_free);
120 }
121
timeline_fence_get_driver_name(struct dma_fence * fence)122 static const char *timeline_fence_get_driver_name(struct dma_fence *fence)
123 {
124 return "syncfence";
125 }
126
timeline_fence_get_timeline_name(struct dma_fence * fence)127 static const char *timeline_fence_get_timeline_name(struct dma_fence *fence)
128 {
129 struct fence_timeline *parent = dma_fence_parent(fence);
130 return parent->name;
131 }
132
timeline_fence_release(struct dma_fence * fence)133 static void timeline_fence_release(struct dma_fence *fence)
134 {
135 unsigned long flags;
136 struct syncfence *sf = dma_fence_to_syncfence(fence);
137 struct fence_timeline *parent = dma_fence_parent(fence);
138
139 spin_lock_irqsave(fence->lock, flags);
140 if (!list_empty(&sf->link))
141 list_del(&sf->link);
142 spin_unlock_irqrestore(fence->lock, flags);
143
144 fence_timeline_put(parent);
145 dma_fence_free(fence);
146 }
147
timeline_fence_signaled(struct dma_fence * fence)148 static bool timeline_fence_signaled(struct dma_fence *fence)
149 {
150 struct fence_timeline *parent = dma_fence_parent(fence);
151 return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops);
152 }
153
timeline_fence_enable_signaling(struct dma_fence * fence)154 static bool timeline_fence_enable_signaling(struct dma_fence *fence)
155 {
156 return true;
157 }
158
timeline_fence_value_str(struct dma_fence * fence,char * str,int size)159 static void timeline_fence_value_str(struct dma_fence *fence,
160 char *str, int size)
161 {
162 snprintf(str, size, "%lld", fence->seqno);
163 }
164
timeline_fence_timeline_value_str(struct dma_fence * fence,char * str,int size)165 static void timeline_fence_timeline_value_str(struct dma_fence *fence,
166 char *str, int size)
167 {
168 struct fence_timeline *parent = dma_fence_parent(fence);
169 snprintf(str, size, "%d", parent->value);
170 }
171
172 static const struct dma_fence_ops timeline_fence_ops = {
173 .get_driver_name = timeline_fence_get_driver_name,
174 .get_timeline_name = timeline_fence_get_timeline_name,
175 .enable_signaling = timeline_fence_enable_signaling,
176 .signaled = timeline_fence_signaled,
177 .release = timeline_fence_release,
178 .fence_value_str = timeline_fence_value_str,
179 .timeline_value_str = timeline_fence_timeline_value_str,
180 };
181
fence_timeline_signal(struct fence_timeline * timeline,unsigned int inc)182 static void fence_timeline_signal(struct fence_timeline *timeline, unsigned int inc)
183 {
184 struct syncfence *pt, *next;
185
186 spin_lock_irq(&timeline->lock);
187
188 timeline->value += inc;
189
190 list_for_each_entry_safe(pt, next, &timeline->pt_list, link) {
191 if (!timeline_fence_signaled(&pt->base))
192 continue;
193
194 list_del_init(&pt->link);
195 dma_fence_signal_locked(&pt->base);
196 }
197
198 spin_unlock_irq(&timeline->lock);
199 }
200
syncfence_create(struct fence_timeline * timeline,unsigned int value)201 static struct syncfence *syncfence_create(struct fence_timeline *timeline,
202 unsigned int value)
203 {
204 struct syncfence *fence;
205
206 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
207 if (!fence)
208 return NULL;
209
210 fence_timeline_get(timeline);
211 dma_fence_init(&fence->base, &timeline_fence_ops, &timeline->lock,
212 timeline->context, value);
213 INIT_LIST_HEAD(&fence->link);
214
215 spin_lock_irq(&timeline->lock);
216 if (!dma_fence_is_signaled_locked(&fence->base)) {
217 list_add_tail(&fence->link, &timeline->pt_list);
218 }
219
220 spin_unlock_irq(&timeline->lock);
221
222 return fence;
223 }
224
syncfence_open(struct inode * inode,struct file * file)225 static int syncfence_open(struct inode *inode, struct file *file)
226 {
227 struct fence_timeline *timeline;
228 char name[64];
229
230 sprintf(name, "syncfence-%d", task_pid_nr(current));
231 timeline = fence_timeline_create(name);
232 if (!timeline)
233 return -ENOMEM;
234
235 file->private_data = timeline;
236 return 0;
237 }
238
syncfence_release(struct inode * inode,struct file * file)239 static int syncfence_release(struct inode *inode, struct file *file)
240 {
241 struct fence_timeline *timeline = file->private_data;
242 struct syncfence *fence, *next;
243
244 spin_lock_irq(&timeline->lock);
245
246 list_for_each_entry_safe(fence, next, &timeline->pt_list, link) {
247 dma_fence_set_error(&fence->base, -ENOENT);
248 dma_fence_signal_locked(&fence->base);
249 }
250
251 spin_unlock_irq(&timeline->lock);
252
253 fence_timeline_put(timeline);
254 return 0;
255 }
256
syncfence_ioctl_create_fence(struct fence_timeline * timeline,unsigned long arg)257 static long syncfence_ioctl_create_fence(struct fence_timeline *timeline,
258 unsigned long arg)
259 {
260 int fd = get_unused_fd_flags(O_CLOEXEC);
261 int err;
262 struct syncfence *fence;
263 struct sync_file *sync_file;
264 struct syncfence_create_data data;
265
266 if (fd < 0)
267 return fd;
268
269 if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
270 err = -EFAULT;
271 goto err;
272 }
273
274 fence = syncfence_create(timeline, data.value);
275 if (!fence) {
276 err = -ENOMEM;
277 goto err;
278 }
279
280 sync_file = sync_file_create(&fence->base);
281 dma_fence_put(&fence->base);
282 if (!sync_file) {
283 err = -ENOMEM;
284 goto err;
285 }
286
287 data.fence = fd;
288 if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
289 fput(sync_file->file);
290 err = -EFAULT;
291 goto err;
292 }
293
294 fd_install(fd, sync_file->file);
295
296 return 0;
297
298 err:
299 put_unused_fd(fd);
300 return err;
301 }
302
syncfence_ioctl_inc(struct fence_timeline * timeline,unsigned long arg)303 static long syncfence_ioctl_inc(struct fence_timeline *timeline, unsigned long arg)
304 {
305 u32 value;
306
307 if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
308 return -EFAULT;
309
310 while (value > INT_MAX) {
311 fence_timeline_signal(timeline, INT_MAX);
312 value -= INT_MAX;
313 }
314
315 fence_timeline_signal(timeline, value);
316
317 return 0;
318 }
319
syncfence_ioctl(struct file * file,unsigned int cmd,unsigned long arg)320 static long syncfence_ioctl(struct file *file, unsigned int cmd,
321 unsigned long arg)
322 {
323 struct fence_timeline *timeline = file->private_data;
324
325 switch (cmd) {
326 case SYNCFENCE_IOC_CREATE_FENCE:
327 return syncfence_ioctl_create_fence(timeline, arg);
328
329 case SYNCFENCE_IOC_INC:
330 return syncfence_ioctl_inc(timeline, arg);
331
332 default:
333 return -ENOTTY;
334 }
335 }
336
syncfence_debug_show(struct device * dev,struct device_attribute * attr,char * buf)337 static ssize_t syncfence_debug_show(struct device *dev,
338 struct device_attribute *attr, char *buf)
339 {
340 unsigned long flags;
341 ssize_t printed_count = 0;
342 struct fence_timeline *timeline, *next_timeline;
343 struct syncfence *pt, *next;
344
345 spin_lock_irqsave(&timeline_list_lock, flags);
346
347 list_for_each_entry_safe(timeline, next_timeline, &timeline_list_head, timeline_list) {
348 printed_count += snprintf(buf+printed_count, PAGE_SIZE-printed_count,
349 "\n+ timeline: %s [%d]:\n", timeline->name, timeline->value);
350
351 spin_lock_irq(&timeline->lock);
352 list_for_each_entry_safe(pt, next, &timeline->pt_list, link) {
353 bool signaled = timeline_fence_signaled(&pt->base);
354
355 printed_count += snprintf(buf+printed_count, PAGE_SIZE-printed_count,
356 "\t%lld [%d]\n", pt->base.seqno, signaled);
357 }
358 spin_unlock_irq(&timeline->lock);
359 }
360
361 spin_unlock_irqrestore(&timeline_list_lock, flags);
362 return printed_count;
363 }
364
365 static DEVICE_ATTR(debug, 0444, syncfence_debug_show, NULL);
366
367 static struct attribute *syncfence_attrs[] = {
368 &dev_attr_debug.attr,
369 NULL
370 };
371
372 static const struct attribute_group syncfence_attr_group = {
373 .attrs = syncfence_attrs
374 };
375
376 static const struct attribute_group *syncfence_attr_groups[] = {
377 &syncfence_attr_group,
378 NULL
379 };
380
381 static struct file_operations syncfence_ops = {
382 .owner = THIS_MODULE,
383 .open = syncfence_open,
384 .release = syncfence_release,
385 .unlocked_ioctl = syncfence_ioctl,
386 .compat_ioctl = syncfence_ioctl,
387 };
388
389 struct miscdevice syncfence_device = {
390 .minor = MISC_DYNAMIC_MINOR,
391 .name = "syncfence",
392 .fops = &syncfence_ops,
393 .groups = syncfence_attr_groups,
394 };
395
syncfence_init(void)396 int syncfence_init(void)
397 {
398 int result = 0;
399 result = misc_register(&syncfence_device);
400 if (result)
401 pr_err("Error %d adding syncfence", result);
402 return 0;
403 }
404
syncfence_exit(void)405 void syncfence_exit(void)
406 {
407 misc_deregister(&syncfence_device);
408 }
409
410