1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /**************************************************************************
3  *
4  * Copyright © 2018-2019 VMware, Inc., Palo Alto, CA., USA
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25  * USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  **************************************************************************/
28 
29 #include <drm/ttm/ttm_placement.h>
30 
31 #include "vmwgfx_drv.h"
32 #include "vmwgfx_resource_priv.h"
33 #include "vmwgfx_binding.h"
34 
35 /**
36  * struct vmw_dx_streamoutput - Streamoutput resource metadata.
37  * @res: Base resource struct.
38  * @ctx: Non-refcounted context to which @res belong.
39  * @cotable: Refcounted cotable holding this Streamoutput.
40  * @cotable_head: List head for cotable-so_res list.
41  * @id: User-space provided identifier.
42  * @size: User-space provided mob size.
43  * @committed: Whether streamoutput is actually created or pending creation.
44  */
45 struct vmw_dx_streamoutput {
46 	struct vmw_resource res;
47 	struct vmw_resource *ctx;
48 	struct vmw_resource *cotable;
49 	struct list_head cotable_head;
50 	u32 id;
51 	u32 size;
52 	bool committed;
53 };
54 
55 static int vmw_dx_streamoutput_create(struct vmw_resource *res);
56 static int vmw_dx_streamoutput_bind(struct vmw_resource *res,
57 				    struct ttm_validate_buffer *val_buf);
58 static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback,
59 				      struct ttm_validate_buffer *val_buf);
60 static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res,
61 					      enum vmw_cmdbuf_res_state state);
62 
63 static const struct vmw_res_func vmw_dx_streamoutput_func = {
64 	.res_type = vmw_res_streamoutput,
65 	.needs_backup = true,
66 	.may_evict = false,
67 	.type_name = "DX streamoutput",
68 	.backup_placement = &vmw_mob_placement,
69 	.create = vmw_dx_streamoutput_create,
70 	.destroy = NULL, /* Command buffer managed resource. */
71 	.bind = vmw_dx_streamoutput_bind,
72 	.unbind = vmw_dx_streamoutput_unbind,
73 	.commit_notify = vmw_dx_streamoutput_commit_notify,
74 };
75 
76 static inline struct vmw_dx_streamoutput *
77 vmw_res_to_dx_streamoutput(struct vmw_resource *res)
78 {
79 	return container_of(res, struct vmw_dx_streamoutput, res);
80 }
81 
82 /**
83  * vmw_dx_streamoutput_unscrub - Reattach the MOB to streamoutput.
84  * @res: The streamoutput resource.
85  *
86  * Return: 0 on success, negative error code on failure.
87  */
88 static int vmw_dx_streamoutput_unscrub(struct vmw_resource *res)
89 {
90 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
91 	struct vmw_private *dev_priv = res->dev_priv;
92 	struct {
93 		SVGA3dCmdHeader header;
94 		SVGA3dCmdDXBindStreamOutput body;
95 	} *cmd;
96 
97 	if (!list_empty(&so->cotable_head) || !so->committed )
98 		return 0;
99 
100 	cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id);
101 	if (!cmd)
102 		return -ENOMEM;
103 
104 	cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT;
105 	cmd->header.size = sizeof(cmd->body);
106 	cmd->body.soid = so->id;
107 	cmd->body.mobid = res->backup->base.resource->start;
108 	cmd->body.offsetInBytes = res->backup_offset;
109 	cmd->body.sizeInBytes = so->size;
110 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
111 
112 	vmw_cotable_add_resource(so->cotable, &so->cotable_head);
113 
114 	return 0;
115 }
116 
117 static int vmw_dx_streamoutput_create(struct vmw_resource *res)
118 {
119 	struct vmw_private *dev_priv = res->dev_priv;
120 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
121 	int ret = 0;
122 
123 	WARN_ON_ONCE(!so->committed);
124 
125 	if (vmw_resource_mob_attached(res)) {
126 		mutex_lock(&dev_priv->binding_mutex);
127 		ret = vmw_dx_streamoutput_unscrub(res);
128 		mutex_unlock(&dev_priv->binding_mutex);
129 	}
130 
131 	res->id = so->id;
132 
133 	return ret;
134 }
135 
136 static int vmw_dx_streamoutput_bind(struct vmw_resource *res,
137 				    struct ttm_validate_buffer *val_buf)
138 {
139 	struct vmw_private *dev_priv = res->dev_priv;
140 	struct ttm_buffer_object *bo = val_buf->bo;
141 	int ret;
142 
143 	if (WARN_ON(bo->resource->mem_type != VMW_PL_MOB))
144 		return -EINVAL;
145 
146 	mutex_lock(&dev_priv->binding_mutex);
147 	ret = vmw_dx_streamoutput_unscrub(res);
148 	mutex_unlock(&dev_priv->binding_mutex);
149 
150 	return ret;
151 }
152 
153 /**
154  * vmw_dx_streamoutput_scrub - Unbind the MOB from streamoutput.
155  * @res: The streamoutput resource.
156  *
157  * Return: 0 on success, negative error code on failure.
158  */
159 static int vmw_dx_streamoutput_scrub(struct vmw_resource *res)
160 {
161 	struct vmw_private *dev_priv = res->dev_priv;
162 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
163 	struct {
164 		SVGA3dCmdHeader header;
165 		SVGA3dCmdDXBindStreamOutput body;
166 	} *cmd;
167 
168 	if (list_empty(&so->cotable_head))
169 		return 0;
170 
171 	WARN_ON_ONCE(!so->committed);
172 
173 	cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id);
174 	if (!cmd)
175 		return -ENOMEM;
176 
177 	cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT;
178 	cmd->header.size = sizeof(cmd->body);
179 	cmd->body.soid = res->id;
180 	cmd->body.mobid = SVGA3D_INVALID_ID;
181 	cmd->body.offsetInBytes = 0;
182 	cmd->body.sizeInBytes = so->size;
183 	vmw_cmd_commit(dev_priv, sizeof(*cmd));
184 
185 	res->id = -1;
186 	list_del_init(&so->cotable_head);
187 
188 	return 0;
189 }
190 
191 static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback,
192 				      struct ttm_validate_buffer *val_buf)
193 {
194 	struct vmw_private *dev_priv = res->dev_priv;
195 	struct vmw_fence_obj *fence;
196 	int ret;
197 
198 	if (WARN_ON(res->backup->base.resource->mem_type != VMW_PL_MOB))
199 		return -EINVAL;
200 
201 	mutex_lock(&dev_priv->binding_mutex);
202 	ret = vmw_dx_streamoutput_scrub(res);
203 	mutex_unlock(&dev_priv->binding_mutex);
204 
205 	if (ret)
206 		return ret;
207 
208 	(void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
209 	vmw_bo_fence_single(val_buf->bo, fence);
210 
211 	if (fence != NULL)
212 		vmw_fence_obj_unreference(&fence);
213 
214 	return 0;
215 }
216 
217 static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res,
218 					   enum vmw_cmdbuf_res_state state)
219 {
220 	struct vmw_private *dev_priv = res->dev_priv;
221 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
222 
223 	if (state == VMW_CMDBUF_RES_ADD) {
224 		mutex_lock(&dev_priv->binding_mutex);
225 		vmw_cotable_add_resource(so->cotable, &so->cotable_head);
226 		so->committed = true;
227 		res->id = so->id;
228 		mutex_unlock(&dev_priv->binding_mutex);
229 	} else {
230 		mutex_lock(&dev_priv->binding_mutex);
231 		list_del_init(&so->cotable_head);
232 		so->committed = false;
233 		res->id = -1;
234 		mutex_unlock(&dev_priv->binding_mutex);
235 	}
236 }
237 
238 /**
239  * vmw_dx_streamoutput_lookup - Do a streamoutput resource lookup by user key.
240  * @man: Command buffer managed resource manager for current context.
241  * @user_key: User-space identifier for lookup.
242  *
243  * Return: Valid refcounted vmw_resource on success, error pointer on failure.
244  */
245 struct vmw_resource *
246 vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man,
247 			   u32 user_key)
248 {
249 	return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_streamoutput,
250 				     user_key);
251 }
252 
253 static void vmw_dx_streamoutput_res_free(struct vmw_resource *res)
254 {
255 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
256 
257 	vmw_resource_unreference(&so->cotable);
258 	kfree(so);
259 }
260 
261 static void vmw_dx_streamoutput_hw_destroy(struct vmw_resource *res)
262 {
263 	/* Destroyed by user-space cmd buf or as part of context takedown. */
264 	res->id = -1;
265 }
266 
267 /**
268  * vmw_dx_streamoutput_add - Add a streamoutput as a cmd buf managed resource.
269  * @man: Command buffer managed resource manager for current context.
270  * @ctx: Pointer to context resource.
271  * @user_key: The identifier for this streamoutput.
272  * @list: The list of staged command buffer managed resources.
273  *
274  * Return: 0 on success, negative error code on failure.
275  */
276 int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man,
277 			    struct vmw_resource *ctx, u32 user_key,
278 			    struct list_head *list)
279 {
280 	struct vmw_dx_streamoutput *so;
281 	struct vmw_resource *res;
282 	struct vmw_private *dev_priv = ctx->dev_priv;
283 	int ret;
284 
285 	so = kmalloc(sizeof(*so), GFP_KERNEL);
286 	if (!so) {
287 		return -ENOMEM;
288 	}
289 
290 	res = &so->res;
291 	so->ctx = ctx;
292 	so->cotable = vmw_resource_reference
293 		(vmw_context_cotable(ctx, SVGA_COTABLE_STREAMOUTPUT));
294 	so->id = user_key;
295 	so->committed = false;
296 	INIT_LIST_HEAD(&so->cotable_head);
297 	ret = vmw_resource_init(dev_priv, res, true,
298 				vmw_dx_streamoutput_res_free,
299 				&vmw_dx_streamoutput_func);
300 	if (ret)
301 		goto out_resource_init;
302 
303 	ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_streamoutput, user_key,
304 				 res, list);
305 	if (ret)
306 		goto out_resource_init;
307 
308 	res->id = so->id;
309 	res->hw_destroy = vmw_dx_streamoutput_hw_destroy;
310 
311 out_resource_init:
312 	vmw_resource_unreference(&res);
313 
314 	return ret;
315 }
316 
317 /**
318  * vmw_dx_streamoutput_set_size - Sets streamoutput mob size in res struct.
319  * @res: The streamoutput res for which need to set size.
320  * @size: The size provided by user-space to set.
321  */
322 void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size)
323 {
324 	struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res);
325 
326 	so->size = size;
327 }
328 
329 /**
330  * vmw_dx_streamoutput_remove - Stage streamoutput for removal.
331  * @man: Command buffer managed resource manager for current context.
332  * @user_key: The identifier for this streamoutput.
333  * @list: The list of staged command buffer managed resources.
334  *
335  * Return: 0 on success, negative error code on failure.
336  */
337 int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man,
338 			       u32 user_key,
339 			       struct list_head *list)
340 {
341 	struct vmw_resource *r;
342 
343 	return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_streamoutput,
344 				     (u32)user_key, list, &r);
345 }
346 
347 /**
348  * vmw_dx_streamoutput_cotable_list_scrub - cotable unbind_func callback.
349  * @dev_priv: Device private.
350  * @list: The list of cotable resources.
351  * @readback: Whether the call was part of a readback unbind.
352  */
353 void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv,
354 					    struct list_head *list,
355 					    bool readback)
356 {
357 	struct vmw_dx_streamoutput *entry, *next;
358 
359 	lockdep_assert_held_once(&dev_priv->binding_mutex);
360 
361 	list_for_each_entry_safe(entry, next, list, cotable_head) {
362 		WARN_ON(vmw_dx_streamoutput_scrub(&entry->res));
363 		if (!readback)
364 			entry->committed =false;
365 	}
366 }
367