1 /*- 2 * Copyright (c) 2015 Michal Meloun 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/bus.h> 30 #include <sys/kernel.h> 31 #include <sys/malloc.h> 32 #include <sys/pctrie.h> 33 #include <sys/vmem.h> 34 35 #include <machine/bus.h> 36 37 #include <dev/clk/clk.h> 38 #include <dev/drm2/drmP.h> 39 #include <dev/drm2/drm_crtc_helper.h> 40 #include <dev/drm2/drm_fb_helper.h> 41 42 #include <arm/nvidia/drm2/tegra_drm.h> 43 44 #include <vm/vm.h> 45 #include <vm/vm_pageout.h> 46 47 static void 48 tegra_bo_destruct(struct tegra_bo *bo) 49 { 50 struct pctrie_iter pages; 51 vm_page_t m; 52 size_t size; 53 int i; 54 55 if (bo->cdev_pager == NULL) 56 return; 57 58 size = round_page(bo->gem_obj.size); 59 if (bo->vbase != 0) 60 pmap_qremove(bo->vbase, bo->npages); 61 62 vm_page_iter_init(&pages, bo->cdev_pager); 63 VM_OBJECT_WLOCK(bo->cdev_pager); 64 for (i = 0; i < bo->npages; i++) { 65 m = vm_page_iter_lookup(&pages, i); 66 vm_page_busy_acquire(m, 0); 67 cdev_mgtdev_pager_free_page(&pages, m); 68 m->flags &= ~PG_FICTITIOUS; 69 vm_page_unwire_noq(m); 70 vm_page_free(m); 71 } 72 VM_OBJECT_WUNLOCK(bo->cdev_pager); 73 74 vm_object_deallocate(bo->cdev_pager); 75 if (bo->vbase != 0) 76 vmem_free(kernel_arena, bo->vbase, size); 77 } 78 79 static void 80 tegra_bo_free_object(struct drm_gem_object *gem_obj) 81 { 82 struct tegra_bo *bo; 83 84 bo = container_of(gem_obj, struct tegra_bo, gem_obj); 85 drm_gem_free_mmap_offset(gem_obj); 86 drm_gem_object_release(gem_obj); 87 88 tegra_bo_destruct(bo); 89 90 free(bo->m, DRM_MEM_DRIVER); 91 free(bo, DRM_MEM_DRIVER); 92 } 93 94 static int 95 tegra_bo_alloc_contig(size_t npages, u_long alignment, vm_memattr_t memattr, 96 vm_page_t **ret_page) 97 { 98 vm_page_t m; 99 int err, i, tries; 100 vm_paddr_t low, high, boundary; 101 102 low = 0; 103 high = -1UL; 104 boundary = 0; 105 tries = 0; 106 retry: 107 m = vm_page_alloc_noobj_contig(VM_ALLOC_WIRED | VM_ALLOC_ZERO, npages, 108 low, high, alignment, boundary, memattr); 109 if (m == NULL) { 110 if (tries < 3) { 111 err = vm_page_reclaim_contig(0, npages, low, high, 112 alignment, boundary); 113 if (err == ENOMEM) 114 vm_wait(NULL); 115 else if (err != 0) 116 return (ENOMEM); 117 tries++; 118 goto retry; 119 } 120 return (ENOMEM); 121 } 122 123 for (i = 0; i < npages; i++, m++) { 124 m->valid = VM_PAGE_BITS_ALL; 125 (*ret_page)[i] = m; 126 } 127 128 return (0); 129 } 130 131 /* Initialize pager and insert all object pages to it*/ 132 static int 133 tegra_bo_init_pager(struct tegra_bo *bo) 134 { 135 vm_page_t m; 136 size_t size; 137 int i; 138 139 size = round_page(bo->gem_obj.size); 140 141 bo->pbase = VM_PAGE_TO_PHYS(bo->m[0]); 142 if (vmem_alloc(kernel_arena, size, M_WAITOK | M_BESTFIT, &bo->vbase)) 143 return (ENOMEM); 144 145 VM_OBJECT_WLOCK(bo->cdev_pager); 146 for (i = 0; i < bo->npages; i++) { 147 m = bo->m[i]; 148 /* 149 * XXX This is a temporary hack. 150 * We need pager suitable for paging (mmap) managed 151 * real (non-fictitious) pages. 152 * - managed pages are needed for clean module unload. 153 * - aliasing fictitious page to real one is bad, 154 * pmap cannot handle this situation without issues 155 * It expects that 156 * paddr = PHYS_TO_VM_PAGE(VM_PAGE_TO_PHYS(paddr)) 157 * for every single page passed to pmap. 158 */ 159 m->oflags &= ~VPO_UNMANAGED; 160 m->flags |= PG_FICTITIOUS; 161 if (vm_page_insert(m, bo->cdev_pager, i) != 0) 162 return (EINVAL); 163 } 164 VM_OBJECT_WUNLOCK(bo->cdev_pager); 165 166 pmap_qenter(bo->vbase, bo->m, bo->npages); 167 return (0); 168 } 169 170 /* Allocate memory for frame buffer */ 171 static int 172 tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) 173 { 174 size_t size; 175 int rv; 176 177 size = bo->gem_obj.size; 178 179 bo->npages = atop(size); 180 bo->m = malloc(sizeof(vm_page_t *) * bo->npages, DRM_MEM_DRIVER, 181 M_WAITOK | M_ZERO); 182 183 rv = tegra_bo_alloc_contig(bo->npages, PAGE_SIZE, 184 VM_MEMATTR_WRITE_COMBINING, &(bo->m)); 185 if (rv != 0) { 186 DRM_WARNING("Cannot allocate memory for gem object.\n"); 187 return (rv); 188 } 189 rv = tegra_bo_init_pager(bo); 190 if (rv != 0) { 191 DRM_WARNING("Cannot initialize gem object pager.\n"); 192 return (rv); 193 } 194 return (0); 195 } 196 197 int 198 tegra_bo_create(struct drm_device *drm, size_t size, struct tegra_bo **res_bo) 199 { 200 struct tegra_bo *bo; 201 int rv; 202 203 if (size <= 0) 204 return (-EINVAL); 205 206 bo = malloc(sizeof(*bo), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); 207 208 size = round_page(size); 209 rv = drm_gem_object_init(drm, &bo->gem_obj, size); 210 if (rv != 0) { 211 free(bo, DRM_MEM_DRIVER); 212 return (rv); 213 } 214 rv = drm_gem_create_mmap_offset(&bo->gem_obj); 215 if (rv != 0) { 216 drm_gem_object_release(&bo->gem_obj); 217 free(bo, DRM_MEM_DRIVER); 218 return (rv); 219 } 220 221 bo->cdev_pager = cdev_pager_allocate(&bo->gem_obj, OBJT_MGTDEVICE, 222 drm->driver->gem_pager_ops, size, 0, 0, NULL); 223 rv = tegra_bo_alloc(drm, bo); 224 if (rv != 0) { 225 tegra_bo_free_object(&bo->gem_obj); 226 return (rv); 227 } 228 229 *res_bo = bo; 230 return (0); 231 } 232 233 static int 234 tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm, 235 size_t size, uint32_t *handle, struct tegra_bo **res_bo) 236 { 237 int rv; 238 struct tegra_bo *bo; 239 240 rv = tegra_bo_create(drm, size, &bo); 241 if (rv != 0) 242 return (rv); 243 244 rv = drm_gem_handle_create(file, &bo->gem_obj, handle); 245 if (rv != 0) { 246 tegra_bo_free_object(&bo->gem_obj); 247 drm_gem_object_release(&bo->gem_obj); 248 return (rv); 249 } 250 251 drm_gem_object_unreference_unlocked(&bo->gem_obj); 252 253 *res_bo = bo; 254 return (0); 255 } 256 257 static int 258 tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm_dev, 259 struct drm_mode_create_dumb *args) 260 { 261 struct tegra_drm *drm; 262 struct tegra_bo *bo; 263 int rv; 264 265 drm = container_of(drm_dev, struct tegra_drm, drm_dev); 266 267 args->pitch= (args->width * args->bpp + 7) / 8; 268 args->pitch = roundup(args->pitch, drm->pitch_align); 269 args->size = args->pitch * args->height; 270 rv = tegra_bo_create_with_handle(file, drm_dev, args->size, 271 &args->handle, &bo); 272 273 return (rv); 274 } 275 276 static int 277 tegra_bo_dumb_map_offset(struct drm_file *file_priv, 278 struct drm_device *drm_dev, uint32_t handle, uint64_t *offset) 279 { 280 struct drm_gem_object *gem_obj; 281 int rv; 282 283 DRM_LOCK(drm_dev); 284 gem_obj = drm_gem_object_lookup(drm_dev, file_priv, handle); 285 if (gem_obj == NULL) { 286 device_printf(drm_dev->dev, "Object not found\n"); 287 DRM_UNLOCK(drm_dev); 288 return (-EINVAL); 289 } 290 rv = drm_gem_create_mmap_offset(gem_obj); 291 if (rv != 0) 292 goto fail; 293 294 *offset = DRM_GEM_MAPPING_OFF(gem_obj->map_list.key) | 295 DRM_GEM_MAPPING_KEY; 296 297 drm_gem_object_unreference(gem_obj); 298 DRM_UNLOCK(drm_dev); 299 return (0); 300 301 fail: 302 drm_gem_object_unreference(gem_obj); 303 DRM_UNLOCK(drm_dev); 304 return (rv); 305 } 306 307 static int 308 tegra_bo_dumb_destroy(struct drm_file *file_priv, struct drm_device *drm_dev, 309 unsigned int handle) 310 { 311 int rv; 312 313 rv = drm_gem_handle_delete(file_priv, handle); 314 return (rv); 315 } 316 317 /* 318 * mmap support 319 */ 320 static int 321 tegra_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot, 322 vm_page_t *mres) 323 { 324 325 #ifdef DRM_PAGER_DEBUG 326 DRM_DEBUG("object %p offset %jd prot %d mres %p\n", 327 vm_obj, (intmax_t)offset, prot, mres); 328 #endif 329 return (VM_PAGER_FAIL); 330 331 } 332 333 static int 334 tegra_gem_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, 335 vm_ooffset_t foff, struct ucred *cred, u_short *color) 336 { 337 338 if (color != NULL) 339 *color = 0; 340 return (0); 341 } 342 343 static void 344 tegra_gem_pager_dtor(void *handle) 345 { 346 347 } 348 349 static struct cdev_pager_ops tegra_gem_pager_ops = { 350 .cdev_pg_fault = tegra_gem_pager_fault, 351 .cdev_pg_ctor = tegra_gem_pager_ctor, 352 .cdev_pg_dtor = tegra_gem_pager_dtor 353 }; 354 355 /* Fill up relevant fields in drm_driver ops */ 356 void 357 tegra_bo_driver_register(struct drm_driver *drm_drv) 358 { 359 drm_drv->gem_free_object = tegra_bo_free_object; 360 drm_drv->gem_pager_ops = &tegra_gem_pager_ops; 361 drm_drv->dumb_create = tegra_bo_dumb_create; 362 drm_drv->dumb_map_offset = tegra_bo_dumb_map_offset; 363 drm_drv->dumb_destroy = tegra_bo_dumb_destroy; 364 } 365