1/* $NetBSD: ttm_bus_dma.c,v 1.10 2021/12/19 11:32:54 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: ttm_bus_dma.c,v 1.10 2021/12/19 11:32:54 riastradh Exp $"); 34 35#include <sys/bus.h> 36 37#include <uvm/uvm_extern.h> 38 39#include <drm/bus_dma_hacks.h> 40#include <ttm/ttm_bo_driver.h> 41#include <ttm/ttm_page_alloc.h> 42 43/* 44 * ttm_bus_dma_populate(ttm_dma) 45 * 46 * If ttm_dma is not already populated, wire its pages and load 47 * its DMA map. The wiring and loading are stable as long as the 48 * associated bo is reserved. 49 * 50 * Transitions from tt_unpopulated to tt_unbound. Marks as wired, 51 * a.k.a. !swapped. 52 */ 53int 54ttm_bus_dma_populate(struct ttm_dma_tt *ttm_dma) 55{ 56 int ret; 57 58 KASSERT(ttm_dma->ttm.state == tt_unpopulated); 59 60 /* If it's unpopulated, it can't be swapped. */ 61 KASSERT(!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED)); 62 /* Pretend it is now, for the sake of ttm_tt_wire. */ 63 ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED; 64 65 /* Wire the uvm pages and fill the ttm page array. */ 66 ret = ttm_tt_wire(&ttm_dma->ttm); 67 if (ret) 68 goto fail0; 69 70 /* Mark it populated but unbound. */ 71 ttm_dma->ttm.state = tt_unbound; 72 73 /* Mark it wired. */ 74 ttm_dma->ttm.page_flags &= ~TTM_PAGE_FLAG_SWAPPED; 75 76 /* Load the DMA map. */ 77 /* XXX errno NetBSD->Linux */ 78 ret = -bus_dmamap_load_pages(ttm_dma->ttm.bdev->dmat, 79 ttm_dma->dma_address, ttm_dma->ttm.pages, 80 (ttm_dma->ttm.num_pages << PAGE_SHIFT), BUS_DMA_NOWAIT); 81 if (ret) 82 goto fail1; 83 84 /* Success! */ 85 return 0; 86 87fail2: __unused 88 bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, ttm_dma->dma_address); 89fail1: KASSERT(ttm_dma->ttm.state == tt_unbound); 90 ttm_tt_unwire(&ttm_dma->ttm); 91 ttm_dma->ttm.state = tt_unpopulated; 92fail0: KASSERT(ret); 93 return ret; 94} 95 96static void 97ttm_bus_dma_put(struct ttm_dma_tt *ttm_dma, int flags) 98{ 99 struct uvm_object *const uobj = ttm_dma->ttm.swap_storage; 100 const size_t size = (ttm_dma->ttm.num_pages << PAGE_SHIFT); 101 102 /* 103 * Can't be tt_bound -- still in use and needs to be removed 104 * from GPU page tables. Can't be tt_unpopulated -- if it 105 * were, why are you hnadling this? Hence tt_unbound. 106 */ 107 KASSERTMSG((ttm_dma->ttm.state == tt_unbound), 108 "ttm_tt %p in invalid state for unpopulate/swapout: %d", 109 &ttm_dma->ttm, (int)ttm_dma->ttm.state); 110 111 /* If pages are wired and loaded, unload and unwire them. */ 112 if (!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED)) { 113 bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, 114 ttm_dma->dma_address); 115 ttm_tt_unwire(&ttm_dma->ttm); 116 ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED; 117 } 118 119 /* We are using uvm_aobj, which had better have a pgo_put. */ 120 KASSERT(uobj->pgops->pgo_put); 121 122 /* Release or deactivate the pages. */ 123 rw_enter(uobj->vmobjlock, RW_WRITER); 124 (void)(*uobj->pgops->pgo_put)(uobj, 0, size, flags); 125 /* pgo_put unlocks uobj->vmobjlock. */ 126 127 /* Mark it unpopulated. */ 128 ttm_dma->ttm.state = tt_unpopulated; 129} 130 131/* 132 * ttmm_bus_dma_unpopulate(ttm_dma) 133 * 134 * Unload any DMA map, unwire any pages, and release any pages 135 * associated with ttm_dma. 136 * 137 * Transitions from tt_unbound to tt_unpopulated. Marks as 138 * unwired, a.k.a. swapped. 139 */ 140void 141ttm_bus_dma_unpopulate(struct ttm_dma_tt *ttm_dma) 142{ 143 144 ttm_bus_dma_put(ttm_dma, PGO_CLEANIT|PGO_FREE); 145} 146 147/* 148 * ttm_bus_dma_swapout(ttm_dma) 149 * 150 * Unload any DMA map, unwire any pages, and deactivate any pages 151 * associated with ttm_dma so that they can be swapped out, but 152 * don't release them. 153 * 154 * Transitions from tt_unbound to tt_unpopulated. Marks as 155 * unwired, a.k.a. swapped. 156 */ 157void 158ttm_bus_dma_swapout(struct ttm_dma_tt *ttm_dma) 159{ 160 161 ttm_bus_dma_put(ttm_dma, PGO_DEACTIVATE); 162} 163