Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / gpu / host1x / fence.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Syncpoint dma_fence implementation
4  *
5  * Copyright (c) 2020, NVIDIA Corporation.
6  */
7
8 #include <linux/dma-fence.h>
9 #include <linux/file.h>
10 #include <linux/fs.h>
11 #include <linux/slab.h>
12 #include <linux/sync_file.h>
13
14 #include "fence.h"
15 #include "intr.h"
16 #include "syncpt.h"
17
18 static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
19 {
20         return "host1x";
21 }
22
23 static const char *host1x_syncpt_fence_get_timeline_name(struct dma_fence *f)
24 {
25         return "syncpoint";
26 }
27
28 static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
29 {
30         return container_of(f, struct host1x_syncpt_fence, base);
31 }
32
33 static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
34 {
35         struct host1x_syncpt_fence *sf = to_host1x_fence(f);
36
37         if (host1x_syncpt_is_expired(sf->sp, sf->threshold))
38                 return false;
39
40         /* Reference for interrupt path. */
41         dma_fence_get(f);
42
43         /*
44          * The dma_fence framework requires the fence driver to keep a
45          * reference to any fences for which 'enable_signaling' has been
46          * called (and that have not been signalled).
47          *
48          * We cannot currently always guarantee that all fences get signalled
49          * or cancelled. As such, for such situations, set up a timeout, so
50          * that long-lasting fences will get reaped eventually.
51          */
52         if (sf->timeout) {
53                 /* Reference for timeout path. */
54                 dma_fence_get(f);
55                 schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000));
56         }
57
58         host1x_intr_add_fence_locked(sf->sp->host, sf);
59
60         /*
61          * The fence may get signalled at any time after the above call,
62          * so we need to initialize all state used by signalling
63          * before it.
64          */
65
66         return true;
67 }
68
69 static const struct dma_fence_ops host1x_syncpt_fence_ops = {
70         .get_driver_name = host1x_syncpt_fence_get_driver_name,
71         .get_timeline_name = host1x_syncpt_fence_get_timeline_name,
72         .enable_signaling = host1x_syncpt_fence_enable_signaling,
73 };
74
75 void host1x_fence_signal(struct host1x_syncpt_fence *f)
76 {
77         if (atomic_xchg(&f->signaling, 1)) {
78                 /*
79                  * Already on timeout path, but we removed the fence before
80                  * timeout path could, so drop interrupt path reference.
81                  */
82                 dma_fence_put(&f->base);
83                 return;
84         }
85
86         if (f->timeout && cancel_delayed_work(&f->timeout_work)) {
87                 /*
88                  * We know that the timeout path will not be entered.
89                  * Safe to drop the timeout path's reference now.
90                  */
91                 dma_fence_put(&f->base);
92         }
93
94         dma_fence_signal_locked(&f->base);
95         dma_fence_put(&f->base);
96 }
97
98 static void do_fence_timeout(struct work_struct *work)
99 {
100         struct delayed_work *dwork = (struct delayed_work *)work;
101         struct host1x_syncpt_fence *f =
102                 container_of(dwork, struct host1x_syncpt_fence, timeout_work);
103
104         if (atomic_xchg(&f->signaling, 1)) {
105                 /* Already on interrupt path, drop timeout path reference if any. */
106                 if (f->timeout)
107                         dma_fence_put(&f->base);
108                 return;
109         }
110
111         if (host1x_intr_remove_fence(f->sp->host, f)) {
112                 /*
113                  * Managed to remove fence from queue, so it's safe to drop
114                  * the interrupt path's reference.
115                  */
116                 dma_fence_put(&f->base);
117         }
118
119         dma_fence_set_error(&f->base, -ETIMEDOUT);
120         dma_fence_signal(&f->base);
121         if (f->timeout)
122                 dma_fence_put(&f->base);
123 }
124
125 struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold,
126                                       bool timeout)
127 {
128         struct host1x_syncpt_fence *fence;
129
130         fence = kzalloc(sizeof(*fence), GFP_KERNEL);
131         if (!fence)
132                 return ERR_PTR(-ENOMEM);
133
134         fence->sp = sp;
135         fence->threshold = threshold;
136         fence->timeout = timeout;
137
138         dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock,
139                        dma_fence_context_alloc(1), 0);
140
141         INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
142
143         return &fence->base;
144 }
145 EXPORT_SYMBOL(host1x_fence_create);
146
147 void host1x_fence_cancel(struct dma_fence *f)
148 {
149         struct host1x_syncpt_fence *sf = to_host1x_fence(f);
150
151         schedule_delayed_work(&sf->timeout_work, 0);
152         flush_delayed_work(&sf->timeout_work);
153 }
154 EXPORT_SYMBOL(host1x_fence_cancel);