blob: ed611e711d4ee56dc046ba90d904b4c88a22c621 [file] [log] [blame]
Sachin Bhayare2b6d0042018-01-13 19:38:21 +05301/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/fs.h>
17#include <linux/file.h>
18#include <linux/slab.h>
19#include <linux/spinlock.h>
20#include <linux/fence.h>
21#include <linux/sync_file.h>
22
23#include "mdss_sync.h"
24
25#define MDSS_SYNC_NAME_SIZE 64
26#define MDSS_SYNC_DRIVER_NAME "mdss"
27
28/**
29 * struct mdss_fence - sync fence context
30 * @base: base sync fence object
31 * @name: name of this sync fence
32 * @fence_list: linked list of outstanding sync fence
33 */
34struct mdss_fence {
35 struct fence base;
36 char name[MDSS_SYNC_NAME_SIZE];
37 struct list_head fence_list;
38};
39
40/**
41 * struct mdss_timeline - sync timeline context
42 * @kref: reference count of timeline
43 * @lock: serialization lock for timeline and fence update
44 * @name: name of timeline
45 * @fence_name: fence name prefix
46 * @next_value: next commit sequence number
47 * @value: current retired sequence number
48 * @context: fence context identifier
49 * @fence_list_head: linked list of outstanding sync fence
50 */
51struct mdss_timeline {
52 struct kref kref;
53 spinlock_t lock;
54 char name[MDSS_SYNC_NAME_SIZE];
55 u32 next_value;
56 u32 value;
57 u64 context;
58 struct list_head fence_list_head;
59};
60
61/*
62 * to_mdss_fence - get mdss fence from fence base object
63 * @fence: Pointer to fence base object
64 */
65static struct mdss_fence *to_mdss_fence(struct fence *fence)
66{
67 return container_of(fence, struct mdss_fence, base);
68}
69
70/*
71 * to_mdss_timeline - get mdss timeline from fence base object
72 * @fence: Pointer to fence base object
73 */
74static struct mdss_timeline *to_mdss_timeline(struct fence *fence)
75{
76 return container_of(fence->lock, struct mdss_timeline, lock);
77}
78
79/*
80 * mdss_free_timeline - Free the given timeline object
81 * @kref: Pointer to timeline kref object.
82 */
83static void mdss_free_timeline(struct kref *kref)
84{
85 struct mdss_timeline *tl =
86 container_of(kref, struct mdss_timeline, kref);
87
88 kfree(tl);
89}
90
91/*
92 * mdss_put_timeline - Put the given timeline object
93 * @tl: Pointer to timeline object.
94 */
95static void mdss_put_timeline(struct mdss_timeline *tl)
96{
97 if (!tl) {
98 pr_err("invalid parameters\n");
99 return;
100 }
101
102 kref_put(&tl->kref, mdss_free_timeline);
103}
104
105/*
106 * mdss_get_timeline - Get the given timeline object
107 * @tl: Pointer to timeline object.
108 */
109static void mdss_get_timeline(struct mdss_timeline *tl)
110{
111 if (!tl) {
112 pr_err("invalid parameters\n");
113 return;
114 }
115
116 kref_get(&tl->kref);
117}
118
119static const char *mdss_fence_get_driver_name(struct fence *fence)
120{
121 return MDSS_SYNC_DRIVER_NAME;
122}
123
124static const char *mdss_fence_get_timeline_name(struct fence *fence)
125{
126 struct mdss_timeline *tl = to_mdss_timeline(fence);
127
128 return tl->name;
129}
130
131static bool mdss_fence_enable_signaling(struct fence *fence)
132{
133 return true;
134}
135
136static bool mdss_fence_signaled(struct fence *fence)
137{
138 struct mdss_timeline *tl = to_mdss_timeline(fence);
139 bool status;
140
141 status = ((s32) (tl->value - fence->seqno)) >= 0;
142 pr_debug("status:%d fence seq:%d and timeline:%s:%d next %d\n",
143 status, fence->seqno, tl->name,
144 tl->value, tl->next_value);
145 return status;
146}
147
148static void mdss_fence_release(struct fence *fence)
149{
150 struct mdss_fence *f = to_mdss_fence(fence);
151 unsigned long flags;
152
153 spin_lock_irqsave(fence->lock, flags);
154 if (!list_empty(&f->fence_list))
155 list_del(&f->fence_list);
156 spin_unlock_irqrestore(fence->lock, flags);
157 mdss_put_timeline(to_mdss_timeline(fence));
158 kfree_rcu(f, base.rcu);
159}
160
161static void mdss_fence_value_str(struct fence *fence, char *str, int size)
162{
163 snprintf(str, size, "%u", fence->seqno);
164}
165
166static void mdss_fence_timeline_value_str(struct fence *fence, char *str,
167 int size)
168{
169 struct mdss_timeline *tl = to_mdss_timeline(fence);
170
171 snprintf(str, size, "%u", tl->value);
172}
173
174static struct fence_ops mdss_fence_ops = {
175 .get_driver_name = mdss_fence_get_driver_name,
176 .get_timeline_name = mdss_fence_get_timeline_name,
177 .enable_signaling = mdss_fence_enable_signaling,
178 .signaled = mdss_fence_signaled,
179 .wait = fence_default_wait,
180 .release = mdss_fence_release,
181 .fence_value_str = mdss_fence_value_str,
182 .timeline_value_str = mdss_fence_timeline_value_str,
183};
184
185/*
186 * mdss_create_timeline - Create timeline object with the given name
187 * @name: Pointer to name character string.
188 */
189struct mdss_timeline *mdss_create_timeline(const char *name)
190{
191 struct mdss_timeline *tl;
192
193 if (!name) {
194 pr_err("invalid parameters\n");
195 return NULL;
196 }
197
198 tl = kzalloc(sizeof(struct mdss_timeline), GFP_KERNEL);
199 if (!tl)
200 return NULL;
201
202 kref_init(&tl->kref);
203 snprintf(tl->name, sizeof(tl->name), "%s", name);
204 spin_lock_init(&tl->lock);
205 tl->context = fence_context_alloc(1);
206 INIT_LIST_HEAD(&tl->fence_list_head);
207
208 return tl;
209}
210
211/*
212 * mdss_destroy_timeline - Destroy the given timeline object
213 * @tl: Pointer to timeline object.
214 */
215void mdss_destroy_timeline(struct mdss_timeline *tl)
216{
217 mdss_put_timeline(tl);
218}
219
220/*
221 * mdss_inc_timeline_locked - Increment timeline by given amount
222 * @tl: Pointer to timeline object.
223 * @increment: the amount to increase the timeline by.
224 */
225static int mdss_inc_timeline_locked(struct mdss_timeline *tl,
226 int increment)
227{
228 struct mdss_fence *f, *next;
229
230 tl->value += increment;
231 list_for_each_entry_safe(f, next, &tl->fence_list_head, fence_list) {
232 if (fence_is_signaled_locked(&f->base)) {
233 pr_debug("%s signaled\n", f->name);
234 list_del_init(&f->fence_list);
235 }
236 }
237
238 return 0;
239}
240
241/*
242 * mdss_resync_timeline - Resync timeline to last committed value
243 * @tl: Pointer to timeline object.
244 */
245void mdss_resync_timeline(struct mdss_timeline *tl)
246{
247 unsigned long flags;
248 s32 val;
249
250 if (!tl) {
251 pr_err("invalid parameters\n");
252 return;
253 }
254
255 spin_lock_irqsave(&tl->lock, flags);
256 val = tl->next_value - tl->value;
257 if (val > 0) {
258 pr_warn("flush %s:%d\n", tl->name, val);
259 mdss_inc_timeline_locked(tl, val);
260 }
261 spin_unlock_irqrestore(&tl->lock, flags);
262}
263
264/*
265 * mdss_get_sync_fence - Create fence object from the given timeline
266 * @tl: Pointer to timeline object
267 * @timestamp: Pointer to timestamp of the returned fence. Null if not required.
268 * Return: pointer fence created on give time line.
269 */
270struct mdss_fence *mdss_get_sync_fence(
271 struct mdss_timeline *tl, const char *fence_name,
272 u32 *timestamp, int offset)
273{
274 struct mdss_fence *f;
275 unsigned long flags;
276 u32 val;
277
278 if (!tl) {
279 pr_err("invalid parameters\n");
280 return NULL;
281 }
282
283 f = kzalloc(sizeof(struct mdss_fence), GFP_KERNEL);
284 if (!f)
285 return NULL;
286
287 INIT_LIST_HEAD(&f->fence_list);
288 spin_lock_irqsave(&tl->lock, flags);
289 val = tl->next_value + offset;
290 tl->next_value += 1;
291 fence_init(&f->base, &mdss_fence_ops, &tl->lock, tl->context, val);
292 list_add_tail(&f->fence_list, &tl->fence_list_head);
293 mdss_get_timeline(tl);
294 spin_unlock_irqrestore(&tl->lock, flags);
295 snprintf(f->name, sizeof(f->name), "%s_%u", fence_name, val);
296
297 if (timestamp)
298 *timestamp = val;
299
300 pr_debug("fence created at val=%u tl->name %s next_value %d value %d offset %d\n",
301 val, tl->name, tl->next_value, tl->value, offset);
302
303 return (struct mdss_fence *) &f->base;
304}
305
306/*
307 * mdss_inc_timeline - Increment timeline by given amount
308 * @tl: Pointer to timeline object.
309 * @increment: the amount to increase the timeline by.
310 */
311int mdss_inc_timeline(struct mdss_timeline *tl, int increment)
312{
313 unsigned long flags;
314 int rc;
315
316 if (!tl) {
317 pr_err("invalid parameters\n");
318 return -EINVAL;
319 }
320
321 spin_lock_irqsave(&tl->lock, flags);
322 rc = mdss_inc_timeline_locked(tl, increment);
323 spin_unlock_irqrestore(&tl->lock, flags);
324
325 return rc;
326}
327
328/*
329 * mdss_get_timeline_commit_ts - Return commit tick of given timeline
330 * @tl: Pointer to timeline object.
331 */
332u32 mdss_get_timeline_commit_ts(struct mdss_timeline *tl)
333{
334 if (!tl) {
335 pr_err("invalid parameters\n");
336 return 0;
337 }
338
339 return tl->next_value;
340}
341
342/*
343 * mdss_get_timeline_retire_ts - Return retire tick of given timeline
344 * @tl: Pointer to timeline object.
345 */
346u32 mdss_get_timeline_retire_ts(struct mdss_timeline *tl)
347{
348 if (!tl) {
349 pr_err("invalid parameters\n");
350 return 0;
351 }
352
353 return tl->value;
354}
355
356/*
357 * mdss_put_sync_fence - Destroy given fence object
358 * @fence: Pointer to fence object.
359 */
360void mdss_put_sync_fence(struct mdss_fence *fence)
361{
362 if (!fence) {
363 pr_err("invalid parameters\n");
364 return;
365 }
366
367 fence_put((struct fence *) fence);
368}
369
370/*
371 * mdss_wait_sync_fence - Wait until fence signal or timeout
372 * @fence: Pointer to fence object.
373 * @timeout: maximum wait time, in msec, for fence to signal.
374 */
375int mdss_wait_sync_fence(struct mdss_fence *fence,
376 long timeout)
377{
378 int rc;
379
380 if (!fence) {
381 pr_err("invalid parameters\n");
382 return -EINVAL;
383 }
384
385 rc = fence_wait_timeout((struct fence *) fence, false,
386 msecs_to_jiffies(timeout));
387 if (rc > 0) {
388 pr_debug("fence signaled\n");
389 rc = 0;
390 } else if (rc == 0) {
391 pr_debug("fence timeout\n");
392 rc = -ETIMEDOUT;
393 }
394
395 return rc;
396}
397
398/*
399 * mdss_get_fd_sync_fence - Get fence object of given file descriptor
400 * @fd: File description of fence object.
401 */
402struct mdss_fence *mdss_get_fd_sync_fence(int fd)
403{
404 return (struct mdss_fence *) sync_file_get_fence(fd);
405}
406
407/*
408 * mdss_get_sync_fence_fd - Get file descriptor of given fence object
409 * @fence: Pointer to fence object.
410 * Return: File descriptor on success, or error code on error
411 */
412int mdss_get_sync_fence_fd(struct mdss_fence *fence)
413{
414 int fd;
415 struct sync_file *sync_file;
416
417 if (!fence) {
418 pr_err("invalid parameters\n");
419 return -EINVAL;
420 }
421
422 fd = get_unused_fd_flags(O_CLOEXEC);
423 if (fd < 0) {
424 pr_err("fail to get unused fd\n");
425 return fd;
426 }
427
428 sync_file = sync_file_create((struct fence *) fence);
429 if (!sync_file) {
430 put_unused_fd(fd);
431 pr_err("failed to create sync file\n");
432 return -ENOMEM;
433 }
434
435 fd_install(fd, sync_file->file);
436
437 return fd;
438}
439
440/*
441 * mdss_put_sync_fence - Destroy given fence object
442 * @fence: Pointer to fence object.
443 * Return: fence name
444 */
445const char *mdss_get_sync_fence_name(struct mdss_fence *fence)
446{
447 if (!fence) {
448 pr_err("invalid parameters\n");
449 return NULL;
450 }
451
452 return fence->name;
453}