1 | // SPDX-License-Identifier: GPL-2.0 OR MIT |
2 | /************************************************************************** |
3 | * |
4 | * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including |
9 | * without limitation the rights to use, copy, modify, merge, publish, |
10 | * distribute, sub license, and/or sell copies of the Software, and to |
11 | * permit persons to whom the Software is furnished to do so, subject to |
12 | * the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice (including the |
15 | * next paragraph) shall be included in all copies or substantial portions |
16 | * of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
25 | * |
26 | **************************************************************************/ |
27 | |
28 | #include "vmwgfx_bo.h" |
29 | #include "vmwgfx_drv.h" |
30 | #include <drm/ttm/ttm_placement.h> |
31 | |
32 | static const struct ttm_place vram_placement_flags = { |
33 | .fpfn = 0, |
34 | .lpfn = 0, |
35 | .mem_type = TTM_PL_VRAM, |
36 | .flags = 0 |
37 | }; |
38 | |
39 | static const struct ttm_place sys_placement_flags = { |
40 | .fpfn = 0, |
41 | .lpfn = 0, |
42 | .mem_type = TTM_PL_SYSTEM, |
43 | .flags = 0 |
44 | }; |
45 | |
46 | struct ttm_placement vmw_vram_placement = { |
47 | .num_placement = 1, |
48 | .placement = &vram_placement_flags, |
49 | }; |
50 | |
51 | struct ttm_placement vmw_sys_placement = { |
52 | .num_placement = 1, |
53 | .placement = &sys_placement_flags, |
54 | }; |
55 | |
56 | const size_t vmw_tt_size = sizeof(struct vmw_ttm_tt); |
57 | |
58 | /** |
59 | * __vmw_piter_non_sg_next: Helper functions to advance |
60 | * a struct vmw_piter iterator. |
61 | * |
62 | * @viter: Pointer to the iterator. |
63 | * |
64 | * These functions return false if past the end of the list, |
65 | * true otherwise. Functions are selected depending on the current |
66 | * DMA mapping mode. |
67 | */ |
68 | static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) |
69 | { |
70 | return ++(viter->i) < viter->num_pages; |
71 | } |
72 | |
73 | static bool __vmw_piter_sg_next(struct vmw_piter *viter) |
74 | { |
75 | bool ret = __vmw_piter_non_sg_next(viter); |
76 | |
77 | return __sg_page_iter_dma_next(dma_iter: &viter->iter) && ret; |
78 | } |
79 | |
80 | |
81 | static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) |
82 | { |
83 | return viter->addrs[viter->i]; |
84 | } |
85 | |
86 | static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) |
87 | { |
88 | return sg_page_iter_dma_address(dma_iter: &viter->iter); |
89 | } |
90 | |
91 | |
92 | /** |
93 | * vmw_piter_start - Initialize a struct vmw_piter. |
94 | * |
95 | * @viter: Pointer to the iterator to initialize |
96 | * @vsgt: Pointer to a struct vmw_sg_table to initialize from |
97 | * @p_offset: Pointer offset used to update current array position |
98 | * |
99 | * Note that we're following the convention of __sg_page_iter_start, so that |
100 | * the iterator doesn't point to a valid page after initialization; it has |
101 | * to be advanced one step first. |
102 | */ |
103 | void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, |
104 | unsigned long p_offset) |
105 | { |
106 | viter->i = p_offset - 1; |
107 | viter->num_pages = vsgt->num_pages; |
108 | viter->pages = vsgt->pages; |
109 | switch (vsgt->mode) { |
110 | case vmw_dma_alloc_coherent: |
111 | viter->next = &__vmw_piter_non_sg_next; |
112 | viter->dma_address = &__vmw_piter_dma_addr; |
113 | viter->addrs = vsgt->addrs; |
114 | break; |
115 | case vmw_dma_map_populate: |
116 | case vmw_dma_map_bind: |
117 | viter->next = &__vmw_piter_sg_next; |
118 | viter->dma_address = &__vmw_piter_sg_addr; |
119 | __sg_page_iter_start(piter: &viter->iter.base, sglist: vsgt->sgt->sgl, |
120 | nents: vsgt->sgt->orig_nents, pgoffset: p_offset); |
121 | break; |
122 | default: |
123 | BUG(); |
124 | } |
125 | } |
126 | |
127 | /** |
128 | * vmw_ttm_unmap_from_dma - unmap device addresses previsouly mapped for |
129 | * TTM pages |
130 | * |
131 | * @vmw_tt: Pointer to a struct vmw_ttm_backend |
132 | * |
133 | * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. |
134 | */ |
135 | static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) |
136 | { |
137 | struct device *dev = vmw_tt->dev_priv->drm.dev; |
138 | |
139 | dma_unmap_sgtable(dev, sgt: &vmw_tt->sgt, dir: DMA_BIDIRECTIONAL, attrs: 0); |
140 | vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; |
141 | } |
142 | |
143 | /** |
144 | * vmw_ttm_map_for_dma - map TTM pages to get device addresses |
145 | * |
146 | * @vmw_tt: Pointer to a struct vmw_ttm_backend |
147 | * |
148 | * This function is used to get device addresses from the kernel DMA layer. |
149 | * However, it's violating the DMA API in that when this operation has been |
150 | * performed, it's illegal for the CPU to write to the pages without first |
151 | * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is |
152 | * therefore only legal to call this function if we know that the function |
153 | * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most |
154 | * a CPU write buffer flush. |
155 | */ |
156 | static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) |
157 | { |
158 | struct device *dev = vmw_tt->dev_priv->drm.dev; |
159 | |
160 | return dma_map_sgtable(dev, sgt: &vmw_tt->sgt, dir: DMA_BIDIRECTIONAL, attrs: 0); |
161 | } |
162 | |
163 | /** |
164 | * vmw_ttm_map_dma - Make sure TTM pages are visible to the device |
165 | * |
166 | * @vmw_tt: Pointer to a struct vmw_ttm_tt |
167 | * |
168 | * Select the correct function for and make sure the TTM pages are |
169 | * visible to the device. Allocate storage for the device mappings. |
170 | * If a mapping has already been performed, indicated by the storage |
171 | * pointer being non NULL, the function returns success. |
172 | */ |
173 | static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) |
174 | { |
175 | struct vmw_private *dev_priv = vmw_tt->dev_priv; |
176 | struct vmw_sg_table *vsgt = &vmw_tt->vsgt; |
177 | int ret = 0; |
178 | |
179 | if (vmw_tt->mapped) |
180 | return 0; |
181 | |
182 | vsgt->mode = dev_priv->map_mode; |
183 | vsgt->pages = vmw_tt->dma_ttm.pages; |
184 | vsgt->num_pages = vmw_tt->dma_ttm.num_pages; |
185 | vsgt->addrs = vmw_tt->dma_ttm.dma_address; |
186 | vsgt->sgt = NULL; |
187 | |
188 | switch (dev_priv->map_mode) { |
189 | case vmw_dma_map_bind: |
190 | case vmw_dma_map_populate: |
191 | if (vmw_tt->dma_ttm.page_flags & TTM_TT_FLAG_EXTERNAL) { |
192 | vsgt->sgt = vmw_tt->dma_ttm.sg; |
193 | } else { |
194 | vsgt->sgt = &vmw_tt->sgt; |
195 | ret = sg_alloc_table_from_pages_segment(sgt: &vmw_tt->sgt, |
196 | pages: vsgt->pages, n_pages: vsgt->num_pages, offset: 0, |
197 | size: (unsigned long)vsgt->num_pages << PAGE_SHIFT, |
198 | max_segment: dma_get_max_seg_size(dev: dev_priv->drm.dev), |
199 | GFP_KERNEL); |
200 | if (ret) |
201 | goto out_sg_alloc_fail; |
202 | } |
203 | |
204 | ret = vmw_ttm_map_for_dma(vmw_tt); |
205 | if (unlikely(ret != 0)) |
206 | goto out_map_fail; |
207 | |
208 | break; |
209 | default: |
210 | break; |
211 | } |
212 | |
213 | vmw_tt->mapped = true; |
214 | return 0; |
215 | |
216 | out_map_fail: |
217 | drm_warn(&dev_priv->drm, "VSG table map failed!" ); |
218 | sg_free_table(vsgt->sgt); |
219 | vsgt->sgt = NULL; |
220 | out_sg_alloc_fail: |
221 | return ret; |
222 | } |
223 | |
224 | /** |
225 | * vmw_ttm_unmap_dma - Tear down any TTM page device mappings |
226 | * |
227 | * @vmw_tt: Pointer to a struct vmw_ttm_tt |
228 | * |
229 | * Tear down any previously set up device DMA mappings and free |
230 | * any storage space allocated for them. If there are no mappings set up, |
231 | * this function is a NOP. |
232 | */ |
233 | static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) |
234 | { |
235 | struct vmw_private *dev_priv = vmw_tt->dev_priv; |
236 | |
237 | if (!vmw_tt->vsgt.sgt) |
238 | return; |
239 | |
240 | switch (dev_priv->map_mode) { |
241 | case vmw_dma_map_bind: |
242 | case vmw_dma_map_populate: |
243 | vmw_ttm_unmap_from_dma(vmw_tt); |
244 | sg_free_table(vmw_tt->vsgt.sgt); |
245 | vmw_tt->vsgt.sgt = NULL; |
246 | break; |
247 | default: |
248 | break; |
249 | } |
250 | vmw_tt->mapped = false; |
251 | } |
252 | |
253 | /** |
254 | * vmw_bo_sg_table - Return a struct vmw_sg_table object for a |
255 | * TTM buffer object |
256 | * |
257 | * @bo: Pointer to a struct ttm_buffer_object |
258 | * |
259 | * Returns a pointer to a struct vmw_sg_table object. The object should |
260 | * not be freed after use. |
261 | * Note that for the device addresses to be valid, the buffer object must |
262 | * either be reserved or pinned. |
263 | */ |
264 | const struct vmw_sg_table *vmw_bo_sg_table(struct ttm_buffer_object *bo) |
265 | { |
266 | struct vmw_ttm_tt *vmw_tt = |
267 | container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm); |
268 | |
269 | return &vmw_tt->vsgt; |
270 | } |
271 | |
272 | |
273 | static int vmw_ttm_bind(struct ttm_device *bdev, |
274 | struct ttm_tt *ttm, struct ttm_resource *bo_mem) |
275 | { |
276 | struct vmw_ttm_tt *vmw_be = |
277 | container_of(ttm, struct vmw_ttm_tt, dma_ttm); |
278 | int ret = 0; |
279 | |
280 | if (!bo_mem) |
281 | return -EINVAL; |
282 | |
283 | if (vmw_be->bound) |
284 | return 0; |
285 | |
286 | ret = vmw_ttm_map_dma(vmw_tt: vmw_be); |
287 | if (unlikely(ret != 0)) |
288 | return ret; |
289 | |
290 | vmw_be->gmr_id = bo_mem->start; |
291 | vmw_be->mem_type = bo_mem->mem_type; |
292 | |
293 | switch (bo_mem->mem_type) { |
294 | case VMW_PL_GMR: |
295 | ret = vmw_gmr_bind(dev_priv: vmw_be->dev_priv, vsgt: &vmw_be->vsgt, |
296 | num_pages: ttm->num_pages, gmr_id: vmw_be->gmr_id); |
297 | break; |
298 | case VMW_PL_MOB: |
299 | if (unlikely(vmw_be->mob == NULL)) { |
300 | vmw_be->mob = |
301 | vmw_mob_create(data_pages: ttm->num_pages); |
302 | if (unlikely(vmw_be->mob == NULL)) |
303 | return -ENOMEM; |
304 | } |
305 | |
306 | ret = vmw_mob_bind(dev_priv: vmw_be->dev_priv, mob: vmw_be->mob, |
307 | vsgt: &vmw_be->vsgt, num_data_pages: ttm->num_pages, |
308 | mob_id: vmw_be->gmr_id); |
309 | break; |
310 | case VMW_PL_SYSTEM: |
311 | /* Nothing to be done for a system bind */ |
312 | break; |
313 | default: |
314 | BUG(); |
315 | } |
316 | vmw_be->bound = true; |
317 | return ret; |
318 | } |
319 | |
320 | static void vmw_ttm_unbind(struct ttm_device *bdev, |
321 | struct ttm_tt *ttm) |
322 | { |
323 | struct vmw_ttm_tt *vmw_be = |
324 | container_of(ttm, struct vmw_ttm_tt, dma_ttm); |
325 | |
326 | if (!vmw_be->bound) |
327 | return; |
328 | |
329 | switch (vmw_be->mem_type) { |
330 | case VMW_PL_GMR: |
331 | vmw_gmr_unbind(dev_priv: vmw_be->dev_priv, gmr_id: vmw_be->gmr_id); |
332 | break; |
333 | case VMW_PL_MOB: |
334 | vmw_mob_unbind(dev_priv: vmw_be->dev_priv, mob: vmw_be->mob); |
335 | break; |
336 | case VMW_PL_SYSTEM: |
337 | break; |
338 | default: |
339 | BUG(); |
340 | } |
341 | |
342 | if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) |
343 | vmw_ttm_unmap_dma(vmw_tt: vmw_be); |
344 | vmw_be->bound = false; |
345 | } |
346 | |
347 | |
348 | static void vmw_ttm_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) |
349 | { |
350 | struct vmw_ttm_tt *vmw_be = |
351 | container_of(ttm, struct vmw_ttm_tt, dma_ttm); |
352 | |
353 | vmw_ttm_unmap_dma(vmw_tt: vmw_be); |
354 | ttm_tt_fini(ttm); |
355 | if (vmw_be->mob) |
356 | vmw_mob_destroy(mob: vmw_be->mob); |
357 | |
358 | kfree(objp: vmw_be); |
359 | } |
360 | |
361 | |
362 | static int vmw_ttm_populate(struct ttm_device *bdev, |
363 | struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) |
364 | { |
365 | bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0; |
366 | |
367 | if (ttm_tt_is_populated(tt: ttm)) |
368 | return 0; |
369 | |
370 | if (external && ttm->sg) |
371 | return drm_prime_sg_to_dma_addr_array(sgt: ttm->sg, |
372 | addrs: ttm->dma_address, |
373 | max_pages: ttm->num_pages); |
374 | |
375 | return ttm_pool_alloc(pool: &bdev->pool, tt: ttm, ctx); |
376 | } |
377 | |
378 | static void vmw_ttm_unpopulate(struct ttm_device *bdev, |
379 | struct ttm_tt *ttm) |
380 | { |
381 | struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, |
382 | dma_ttm); |
383 | bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0; |
384 | |
385 | if (external) |
386 | return; |
387 | |
388 | vmw_ttm_unbind(bdev, ttm); |
389 | |
390 | if (vmw_tt->mob) { |
391 | vmw_mob_destroy(mob: vmw_tt->mob); |
392 | vmw_tt->mob = NULL; |
393 | } |
394 | |
395 | vmw_ttm_unmap_dma(vmw_tt); |
396 | |
397 | ttm_pool_free(pool: &bdev->pool, tt: ttm); |
398 | } |
399 | |
400 | static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo, |
401 | uint32_t page_flags) |
402 | { |
403 | struct vmw_ttm_tt *vmw_be; |
404 | int ret; |
405 | bool external = bo->type == ttm_bo_type_sg; |
406 | |
407 | vmw_be = kzalloc(size: sizeof(*vmw_be), GFP_KERNEL); |
408 | if (!vmw_be) |
409 | return NULL; |
410 | |
411 | vmw_be->dev_priv = vmw_priv_from_ttm(bdev: bo->bdev); |
412 | vmw_be->mob = NULL; |
413 | |
414 | if (external) |
415 | page_flags |= TTM_TT_FLAG_EXTERNAL | TTM_TT_FLAG_EXTERNAL_MAPPABLE; |
416 | |
417 | if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent || external) |
418 | ret = ttm_sg_tt_init(ttm_dma: &vmw_be->dma_ttm, bo, page_flags, |
419 | caching: ttm_cached); |
420 | else |
421 | ret = ttm_tt_init(ttm: &vmw_be->dma_ttm, bo, page_flags, |
422 | caching: ttm_cached, extra_pages: 0); |
423 | if (unlikely(ret != 0)) |
424 | goto out_no_init; |
425 | |
426 | return &vmw_be->dma_ttm; |
427 | out_no_init: |
428 | kfree(objp: vmw_be); |
429 | return NULL; |
430 | } |
431 | |
432 | static void vmw_evict_flags(struct ttm_buffer_object *bo, |
433 | struct ttm_placement *placement) |
434 | { |
435 | *placement = vmw_sys_placement; |
436 | } |
437 | |
438 | static int vmw_ttm_io_mem_reserve(struct ttm_device *bdev, struct ttm_resource *mem) |
439 | { |
440 | struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev); |
441 | |
442 | switch (mem->mem_type) { |
443 | case TTM_PL_SYSTEM: |
444 | case VMW_PL_SYSTEM: |
445 | case VMW_PL_GMR: |
446 | case VMW_PL_MOB: |
447 | return 0; |
448 | case TTM_PL_VRAM: |
449 | mem->bus.offset = (mem->start << PAGE_SHIFT) + |
450 | dev_priv->vram_start; |
451 | mem->bus.is_iomem = true; |
452 | mem->bus.caching = ttm_cached; |
453 | break; |
454 | default: |
455 | return -EINVAL; |
456 | } |
457 | return 0; |
458 | } |
459 | |
460 | /** |
461 | * vmw_move_notify - TTM move_notify_callback |
462 | * |
463 | * @bo: The TTM buffer object about to move. |
464 | * @old_mem: The old memory where we move from |
465 | * @new_mem: The struct ttm_resource indicating to what memory |
466 | * region the move is taking place. |
467 | * |
468 | * Calls move_notify for all subsystems needing it. |
469 | * (currently only resources). |
470 | */ |
471 | static void vmw_move_notify(struct ttm_buffer_object *bo, |
472 | struct ttm_resource *old_mem, |
473 | struct ttm_resource *new_mem) |
474 | { |
475 | vmw_bo_move_notify(bo, mem: new_mem); |
476 | vmw_query_move_notify(bo, old_mem, new_mem); |
477 | } |
478 | |
479 | |
480 | /** |
481 | * vmw_swap_notify - TTM move_notify_callback |
482 | * |
483 | * @bo: The TTM buffer object about to be swapped out. |
484 | */ |
485 | static void vmw_swap_notify(struct ttm_buffer_object *bo) |
486 | { |
487 | vmw_bo_swap_notify(bo); |
488 | (void) ttm_bo_wait(bo, intr: false, no_wait: false); |
489 | } |
490 | |
491 | static bool vmw_memtype_is_system(uint32_t mem_type) |
492 | { |
493 | return mem_type == TTM_PL_SYSTEM || mem_type == VMW_PL_SYSTEM; |
494 | } |
495 | |
496 | static int vmw_move(struct ttm_buffer_object *bo, |
497 | bool evict, |
498 | struct ttm_operation_ctx *ctx, |
499 | struct ttm_resource *new_mem, |
500 | struct ttm_place *hop) |
501 | { |
502 | struct ttm_resource_manager *new_man; |
503 | struct ttm_resource_manager *old_man = NULL; |
504 | int ret = 0; |
505 | |
506 | new_man = ttm_manager_type(bdev: bo->bdev, mem_type: new_mem->mem_type); |
507 | if (bo->resource) |
508 | old_man = ttm_manager_type(bdev: bo->bdev, mem_type: bo->resource->mem_type); |
509 | |
510 | if (new_man->use_tt && !vmw_memtype_is_system(mem_type: new_mem->mem_type)) { |
511 | ret = vmw_ttm_bind(bdev: bo->bdev, ttm: bo->ttm, bo_mem: new_mem); |
512 | if (ret) |
513 | return ret; |
514 | } |
515 | |
516 | if (!bo->resource || (bo->resource->mem_type == TTM_PL_SYSTEM && |
517 | bo->ttm == NULL)) { |
518 | ttm_bo_move_null(bo, new_mem); |
519 | return 0; |
520 | } |
521 | |
522 | vmw_move_notify(bo, old_mem: bo->resource, new_mem); |
523 | |
524 | if (old_man && old_man->use_tt && new_man->use_tt) { |
525 | if (vmw_memtype_is_system(mem_type: bo->resource->mem_type)) { |
526 | ttm_bo_move_null(bo, new_mem); |
527 | return 0; |
528 | } |
529 | ret = ttm_bo_wait_ctx(bo, ctx); |
530 | if (ret) |
531 | goto fail; |
532 | |
533 | vmw_ttm_unbind(bdev: bo->bdev, ttm: bo->ttm); |
534 | ttm_resource_free(bo, res: &bo->resource); |
535 | ttm_bo_assign_mem(bo, new_mem); |
536 | return 0; |
537 | } else { |
538 | ret = ttm_bo_move_memcpy(bo, ctx, new_mem); |
539 | if (ret) |
540 | goto fail; |
541 | } |
542 | return 0; |
543 | fail: |
544 | vmw_move_notify(bo, old_mem: new_mem, new_mem: bo->resource); |
545 | return ret; |
546 | } |
547 | |
548 | struct ttm_device_funcs vmw_bo_driver = { |
549 | .ttm_tt_create = &vmw_ttm_tt_create, |
550 | .ttm_tt_populate = &vmw_ttm_populate, |
551 | .ttm_tt_unpopulate = &vmw_ttm_unpopulate, |
552 | .ttm_tt_destroy = &vmw_ttm_destroy, |
553 | .eviction_valuable = ttm_bo_eviction_valuable, |
554 | .evict_flags = vmw_evict_flags, |
555 | .move = vmw_move, |
556 | .swap_notify = vmw_swap_notify, |
557 | .io_mem_reserve = &vmw_ttm_io_mem_reserve, |
558 | }; |
559 | |
560 | int vmw_bo_create_and_populate(struct vmw_private *dev_priv, |
561 | size_t bo_size, u32 domain, |
562 | struct vmw_bo **bo_p) |
563 | { |
564 | struct ttm_operation_ctx ctx = { |
565 | .interruptible = false, |
566 | .no_wait_gpu = false |
567 | }; |
568 | struct vmw_bo *vbo; |
569 | int ret; |
570 | struct vmw_bo_params bo_params = { |
571 | .domain = domain, |
572 | .busy_domain = domain, |
573 | .bo_type = ttm_bo_type_kernel, |
574 | .size = bo_size, |
575 | .pin = true |
576 | }; |
577 | |
578 | ret = vmw_bo_create(dev_priv, params: &bo_params, p_bo: &vbo); |
579 | if (unlikely(ret != 0)) |
580 | return ret; |
581 | |
582 | ret = ttm_bo_reserve(bo: &vbo->tbo, interruptible: false, no_wait: true, NULL); |
583 | BUG_ON(ret != 0); |
584 | ret = vmw_ttm_populate(bdev: vbo->tbo.bdev, ttm: vbo->tbo.ttm, ctx: &ctx); |
585 | if (likely(ret == 0)) { |
586 | struct vmw_ttm_tt *vmw_tt = |
587 | container_of(vbo->tbo.ttm, struct vmw_ttm_tt, dma_ttm); |
588 | ret = vmw_ttm_map_dma(vmw_tt); |
589 | } |
590 | |
591 | ttm_bo_unreserve(bo: &vbo->tbo); |
592 | |
593 | if (likely(ret == 0)) |
594 | *bo_p = vbo; |
595 | return ret; |
596 | } |
597 | |