1 1.9 riastrad /* $NetBSD: drm_scatter.c,v 1.9 2021/12/19 12:30:05 riastradh Exp $ */ 2 1.2 riastrad 3 1.2 riastrad /*- 4 1.2 riastrad * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 1.2 riastrad * All rights reserved. 6 1.2 riastrad * 7 1.2 riastrad * This code is derived from software contributed to The NetBSD Foundation 8 1.2 riastrad * by Taylor R. Campbell. 9 1.2 riastrad * 10 1.2 riastrad * Redistribution and use in source and binary forms, with or without 11 1.2 riastrad * modification, are permitted provided that the following conditions 12 1.2 riastrad * are met: 13 1.2 riastrad * 1. Redistributions of source code must retain the above copyright 14 1.2 riastrad * notice, this list of conditions and the following disclaimer. 15 1.2 riastrad * 2. Redistributions in binary form must reproduce the above copyright 16 1.2 riastrad * notice, this list of conditions and the following disclaimer in the 17 1.2 riastrad * documentation and/or other materials provided with the distribution. 18 1.2 riastrad * 19 1.2 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.2 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.2 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.2 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.2 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.2 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.2 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.2 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.2 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.2 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.2 riastrad * POSSIBILITY OF SUCH DAMAGE. 30 1.2 riastrad */ 31 1.2 riastrad 32 1.2 riastrad #include <sys/cdefs.h> 33 1.9 riastrad __KERNEL_RCSID(0, "$NetBSD: drm_scatter.c,v 1.9 2021/12/19 12:30:05 riastradh Exp $"); 34 1.2 riastrad 35 1.2 riastrad #include <sys/types.h> 36 1.2 riastrad #include <sys/bus.h> 37 1.2 riastrad #include <sys/errno.h> 38 1.2 riastrad #include <sys/systm.h> 39 1.2 riastrad 40 1.9 riastrad #include <linux/mm.h> 41 1.2 riastrad #include <linux/slab.h> 42 1.2 riastrad 43 1.8 riastrad #include <drm/drm_device.h> 44 1.7 riastrad #include <drm/drm_drv.h> 45 1.6 riastrad 46 1.6 riastrad #include "../dist/drm/drm_internal.h" 47 1.5 riastrad #include "../dist/drm/drm_legacy.h" 48 1.2 riastrad 49 1.7 riastrad #if IS_ENABLED(CONFIG_DRM_LEGACY) 50 1.2 riastrad static int drm_sg_alloc_mem(struct drm_device *, size_t, 51 1.2 riastrad struct drm_sg_mem **); 52 1.3 riastrad static void drm_sg_free_mem(struct drm_device *, struct drm_sg_mem *); 53 1.2 riastrad 54 1.2 riastrad int 55 1.5 riastrad drm_legacy_sg_alloc(struct drm_device *dev, void *data, 56 1.2 riastrad struct drm_file *file __unused) 57 1.2 riastrad { 58 1.2 riastrad struct drm_scatter_gather *const request = data; 59 1.4 mrg struct drm_sg_mem *sg = NULL; 60 1.2 riastrad int error; 61 1.2 riastrad 62 1.2 riastrad /* 63 1.2 riastrad * XXX Should not hold this mutex during drm_sg_alloc. For 64 1.2 riastrad * now, we'll just use non-blocking allocations. 65 1.2 riastrad */ 66 1.2 riastrad KASSERT(mutex_is_locked(&drm_global_mutex)); 67 1.2 riastrad 68 1.2 riastrad /* 69 1.2 riastrad * Sanity-check the inputs. 70 1.2 riastrad */ 71 1.2 riastrad if (!drm_core_check_feature(dev, DRIVER_SG)) 72 1.2 riastrad return -ENODEV; 73 1.2 riastrad if (dev->sg != NULL) 74 1.2 riastrad return -EBUSY; 75 1.2 riastrad if (request->size > 0xffffffffUL) 76 1.2 riastrad return -ENOMEM; 77 1.2 riastrad 78 1.2 riastrad /* 79 1.2 riastrad * Do the allocation. 80 1.2 riastrad * 81 1.2 riastrad * XXX Would it be safe to drop drm_global_mutex here to avoid 82 1.2 riastrad * holding it during allocation? 83 1.2 riastrad */ 84 1.2 riastrad error = drm_sg_alloc_mem(dev, request->size, &sg); 85 1.2 riastrad if (error) 86 1.2 riastrad return error; 87 1.2 riastrad 88 1.2 riastrad /* Set it up to be the device's scatter-gather memory. */ 89 1.2 riastrad dev->sg = sg; 90 1.2 riastrad 91 1.2 riastrad /* Success! */ 92 1.2 riastrad request->handle = sg->handle; 93 1.2 riastrad return 0; 94 1.2 riastrad } 95 1.2 riastrad 96 1.2 riastrad int 97 1.5 riastrad drm_legacy_sg_free(struct drm_device *dev, void *data, struct drm_file *file) 98 1.2 riastrad { 99 1.2 riastrad struct drm_scatter_gather *const request = data; 100 1.2 riastrad struct drm_sg_mem *sg; 101 1.2 riastrad 102 1.2 riastrad KASSERT(mutex_is_locked(&drm_global_mutex)); 103 1.2 riastrad 104 1.2 riastrad /* 105 1.2 riastrad * Sanity-check the inputs. 106 1.2 riastrad */ 107 1.2 riastrad if (!drm_core_check_feature(dev, DRIVER_SG)) 108 1.2 riastrad return -ENODEV; 109 1.2 riastrad 110 1.2 riastrad sg = dev->sg; 111 1.2 riastrad if (sg == NULL) 112 1.2 riastrad return -ENXIO; 113 1.2 riastrad if (sg->handle != request->handle) 114 1.2 riastrad return -EINVAL; 115 1.2 riastrad 116 1.2 riastrad /* Remove dev->sg. */ 117 1.2 riastrad dev->sg = NULL; 118 1.2 riastrad 119 1.3 riastrad /* Free it. */ 120 1.3 riastrad drm_sg_free_mem(dev, sg); 121 1.3 riastrad 122 1.2 riastrad /* Success! */ 123 1.2 riastrad return 0; 124 1.2 riastrad } 125 1.2 riastrad 126 1.3 riastrad void 127 1.3 riastrad drm_legacy_sg_cleanup(struct drm_device *dev) 128 1.3 riastrad { 129 1.3 riastrad 130 1.3 riastrad if (!drm_core_check_feature(dev, DRIVER_SG)) 131 1.3 riastrad return; 132 1.3 riastrad if (dev->sg == NULL) 133 1.3 riastrad return; 134 1.3 riastrad if (drm_core_check_feature(dev, DRIVER_MODESET)) 135 1.3 riastrad return; 136 1.3 riastrad 137 1.3 riastrad drm_sg_free_mem(dev, dev->sg); 138 1.3 riastrad } 139 1.3 riastrad 140 1.2 riastrad static int 141 1.2 riastrad drm_sg_alloc_mem(struct drm_device *dev, size_t size, struct drm_sg_mem **sgp) 142 1.2 riastrad { 143 1.2 riastrad int nsegs; 144 1.2 riastrad int error; 145 1.2 riastrad 146 1.2 riastrad KASSERT(drm_core_check_feature(dev, DRIVER_SG)); 147 1.2 riastrad 148 1.2 riastrad KASSERT(size <= (size_t)0xffffffffUL); /* XXX 32-bit sizes only? */ 149 1.2 riastrad const size_t nbytes = PAGE_ALIGN(size); 150 1.2 riastrad const size_t npages = nbytes >> PAGE_SHIFT; 151 1.2 riastrad KASSERT(npages <= (size_t)INT_MAX); 152 1.2 riastrad 153 1.2 riastrad /* 154 1.2 riastrad * Allocate a drm_sg_mem record. 155 1.2 riastrad */ 156 1.2 riastrad struct drm_sg_mem *const sg = 157 1.2 riastrad kmem_zalloc(offsetof(struct drm_sg_mem, sg_segs[npages]), 158 1.2 riastrad KM_NOSLEEP); 159 1.2 riastrad if (sg == NULL) 160 1.2 riastrad return -ENOMEM; 161 1.2 riastrad sg->sg_tag = dev->dmat; 162 1.2 riastrad sg->sg_nsegs_max = (unsigned int)npages; 163 1.2 riastrad 164 1.2 riastrad /* 165 1.2 riastrad * Allocate the requested amount of DMA-safe memory. 166 1.2 riastrad */ 167 1.2 riastrad KASSERT(sg->sg_nsegs_max <= (unsigned int)INT_MAX); 168 1.2 riastrad /* XXX errno NetBSD->Linux */ 169 1.2 riastrad error = -bus_dmamem_alloc(sg->sg_tag, nbytes, PAGE_SIZE, 0, 170 1.2 riastrad sg->sg_segs, (int)sg->sg_nsegs_max, &nsegs, BUS_DMA_NOWAIT); 171 1.2 riastrad if (error) 172 1.2 riastrad goto fail0; 173 1.2 riastrad KASSERT(0 <= nsegs); 174 1.2 riastrad sg->sg_nsegs = (unsigned int)nsegs; 175 1.2 riastrad 176 1.2 riastrad /* 177 1.2 riastrad * XXX Old drm passed DRM_BUS_NOWAIT below but BUS_DMA_WAITOK 178 1.2 riastrad * above. WTF? 179 1.2 riastrad */ 180 1.2 riastrad 181 1.2 riastrad /* 182 1.2 riastrad * Map the DMA-safe memory into kernel virtual address space. 183 1.2 riastrad */ 184 1.2 riastrad /* XXX errno NetBSD->Linux */ 185 1.2 riastrad error = -bus_dmamem_map(sg->sg_tag, sg->sg_segs, nsegs, nbytes, 186 1.2 riastrad &sg->virtual, 187 1.2 riastrad (BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE)); 188 1.2 riastrad if (error) 189 1.2 riastrad goto fail1; 190 1.2 riastrad sg->sg_size = nbytes; 191 1.2 riastrad 192 1.2 riastrad /* 193 1.2 riastrad * Create a map for DMA transfers. 194 1.2 riastrad */ 195 1.2 riastrad /* XXX errno NetBSD->Linux */ 196 1.2 riastrad error = -bus_dmamap_create(sg->sg_tag, nbytes, nsegs, nbytes, 0, 197 1.2 riastrad BUS_DMA_NOWAIT, &sg->sg_map); 198 1.2 riastrad if (error) 199 1.2 riastrad goto fail2; 200 1.2 riastrad 201 1.2 riastrad /* 202 1.2 riastrad * Load the kva buffer into the map for DMA transfers. 203 1.2 riastrad */ 204 1.2 riastrad /* XXX errno NetBSD->Linux */ 205 1.2 riastrad error = -bus_dmamap_load(sg->sg_tag, sg->sg_map, sg->virtual, nbytes, 206 1.2 riastrad NULL, (BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)); 207 1.2 riastrad if (error) 208 1.2 riastrad goto fail3; 209 1.2 riastrad 210 1.2 riastrad /* 211 1.2 riastrad * Use the kernel virtual address as the userland handle. 212 1.2 riastrad * 213 1.2 riastrad * XXX This leaks some information about kernel virtual 214 1.2 riastrad * addresses to userland; should we use an idr instead? 215 1.2 riastrad */ 216 1.2 riastrad sg->handle = (unsigned long)(uintptr_t)sg->virtual; 217 1.2 riastrad KASSERT(sg->virtual == (void *)(uintptr_t)sg->handle); 218 1.2 riastrad 219 1.2 riastrad /* Success! */ 220 1.2 riastrad *sgp = sg; 221 1.2 riastrad return 0; 222 1.2 riastrad 223 1.2 riastrad fail3: bus_dmamap_destroy(sg->sg_tag, sg->sg_map); 224 1.2 riastrad fail2: bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); 225 1.2 riastrad fail1: KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); 226 1.2 riastrad bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); 227 1.2 riastrad fail0: sg->sg_tag = NULL; /* XXX paranoia */ 228 1.3 riastrad kmem_free(sg, offsetof(struct drm_sg_mem, sg_segs[sg->sg_nsegs_max])); 229 1.2 riastrad return error; 230 1.2 riastrad } 231 1.2 riastrad 232 1.3 riastrad static void 233 1.3 riastrad drm_sg_free_mem(struct drm_device *dev, struct drm_sg_mem *sg) 234 1.2 riastrad { 235 1.2 riastrad 236 1.2 riastrad bus_dmamap_unload(sg->sg_tag, sg->sg_map); 237 1.2 riastrad bus_dmamap_destroy(sg->sg_tag, sg->sg_map); 238 1.2 riastrad bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); 239 1.2 riastrad KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); 240 1.2 riastrad bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); 241 1.2 riastrad sg->sg_tag = NULL; /* XXX paranoia */ 242 1.2 riastrad kmem_free(sg, offsetof(struct drm_sg_mem, sg_segs[sg->sg_nsegs_max])); 243 1.2 riastrad } 244 1.7 riastrad #endif 245