1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /* Copyright (C) 2020 Google, Inc */
3
4 #include "drm/drm_drv.h"
5
6 #include "msm_drv.h"
7 #include "msm_syncobj.h"
8
9 struct drm_syncobj **
msm_syncobj_parse_deps(struct drm_device * dev,struct drm_sched_job * job,struct drm_file * file,uint64_t in_syncobjs_addr,uint32_t nr_in_syncobjs,size_t syncobj_stride)10 msm_syncobj_parse_deps(struct drm_device *dev,
11 struct drm_sched_job *job,
12 struct drm_file *file,
13 uint64_t in_syncobjs_addr,
14 uint32_t nr_in_syncobjs,
15 size_t syncobj_stride)
16 {
17 struct drm_syncobj **syncobjs = NULL;
18 struct drm_msm_syncobj syncobj_desc = {0};
19 int ret = 0;
20 uint32_t i, j;
21
22 syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
23 GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
24 if (!syncobjs)
25 return ERR_PTR(-ENOMEM);
26
27 for (i = 0; i < nr_in_syncobjs; ++i) {
28 uint64_t address = in_syncobjs_addr + i * syncobj_stride;
29
30 if (copy_from_user(&syncobj_desc,
31 u64_to_user_ptr(address),
32 min(syncobj_stride, sizeof(syncobj_desc)))) {
33 ret = -EFAULT;
34 break;
35 }
36
37 if (syncobj_desc.point &&
38 !drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) {
39 ret = UERR(EOPNOTSUPP, dev, "syncobj timeline unsupported");
40 break;
41 }
42
43 if (syncobj_desc.flags & ~MSM_SYNCOBJ_FLAGS) {
44 ret = UERR(EINVAL, dev, "invalid syncobj flags: %x", syncobj_desc.flags);
45 break;
46 }
47
48 ret = drm_sched_job_add_syncobj_dependency(job, file,
49 syncobj_desc.handle,
50 syncobj_desc.point);
51 if (ret)
52 break;
53
54 if (syncobj_desc.flags & MSM_SYNCOBJ_RESET) {
55 syncobjs[i] = drm_syncobj_find(file, syncobj_desc.handle);
56 if (!syncobjs[i]) {
57 ret = UERR(EINVAL, dev, "invalid syncobj handle: %u", i);
58 break;
59 }
60 }
61 }
62
63 if (ret) {
64 for (j = 0; j <= i; ++j) {
65 if (syncobjs[j])
66 drm_syncobj_put(syncobjs[j]);
67 }
68 kfree(syncobjs);
69 return ERR_PTR(ret);
70 }
71 return syncobjs;
72 }
73
74 void
msm_syncobj_reset(struct drm_syncobj ** syncobjs,uint32_t nr_syncobjs)75 msm_syncobj_reset(struct drm_syncobj **syncobjs, uint32_t nr_syncobjs)
76 {
77 uint32_t i;
78
79 for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
80 if (syncobjs[i])
81 drm_syncobj_replace_fence(syncobjs[i], NULL);
82 }
83 }
84
85 struct msm_syncobj_post_dep *
msm_syncobj_parse_post_deps(struct drm_device * dev,struct drm_file * file,uint64_t syncobjs_addr,uint32_t nr_syncobjs,size_t syncobj_stride)86 msm_syncobj_parse_post_deps(struct drm_device *dev,
87 struct drm_file *file,
88 uint64_t syncobjs_addr,
89 uint32_t nr_syncobjs,
90 size_t syncobj_stride)
91 {
92 struct msm_syncobj_post_dep *post_deps;
93 struct drm_msm_syncobj syncobj_desc = {0};
94 int ret = 0;
95 uint32_t i, j;
96
97 post_deps = kcalloc(nr_syncobjs, sizeof(*post_deps),
98 GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
99 if (!post_deps)
100 return ERR_PTR(-ENOMEM);
101
102 for (i = 0; i < nr_syncobjs; ++i) {
103 uint64_t address = syncobjs_addr + i * syncobj_stride;
104
105 if (copy_from_user(&syncobj_desc,
106 u64_to_user_ptr(address),
107 min(syncobj_stride, sizeof(syncobj_desc)))) {
108 ret = -EFAULT;
109 break;
110 }
111
112 post_deps[i].point = syncobj_desc.point;
113
114 if (syncobj_desc.flags) {
115 ret = UERR(EINVAL, dev, "invalid syncobj flags");
116 break;
117 }
118
119 if (syncobj_desc.point) {
120 if (!drm_core_check_feature(dev,
121 DRIVER_SYNCOBJ_TIMELINE)) {
122 ret = UERR(EOPNOTSUPP, dev, "syncobj timeline unsupported");
123 break;
124 }
125
126 post_deps[i].chain = dma_fence_chain_alloc();
127 if (!post_deps[i].chain) {
128 ret = -ENOMEM;
129 break;
130 }
131 }
132
133 post_deps[i].syncobj =
134 drm_syncobj_find(file, syncobj_desc.handle);
135 if (!post_deps[i].syncobj) {
136 ret = UERR(EINVAL, dev, "invalid syncobj handle");
137 break;
138 }
139 }
140
141 if (ret) {
142 for (j = 0; j <= i; ++j) {
143 dma_fence_chain_free(post_deps[j].chain);
144 if (post_deps[j].syncobj)
145 drm_syncobj_put(post_deps[j].syncobj);
146 }
147
148 kfree(post_deps);
149 return ERR_PTR(ret);
150 }
151
152 return post_deps;
153 }
154
155 void
msm_syncobj_process_post_deps(struct msm_syncobj_post_dep * post_deps,uint32_t count,struct dma_fence * fence)156 msm_syncobj_process_post_deps(struct msm_syncobj_post_dep *post_deps,
157 uint32_t count, struct dma_fence *fence)
158 {
159 uint32_t i;
160
161 for (i = 0; post_deps && i < count; ++i) {
162 if (post_deps[i].chain) {
163 drm_syncobj_add_point(post_deps[i].syncobj,
164 post_deps[i].chain,
165 fence, post_deps[i].point);
166 post_deps[i].chain = NULL;
167 } else {
168 drm_syncobj_replace_fence(post_deps[i].syncobj,
169 fence);
170 }
171 }
172 }
173