xref: /linux/drivers/gpu/drm/msm/msm_syncobj.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
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 **
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
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 *
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
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