1d722e3fbSopenharmony_ci/* 2d722e3fbSopenharmony_ci * Copyright 2012 Red Hat Inc. 3d722e3fbSopenharmony_ci * 4d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation 7d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10d722e3fbSopenharmony_ci * 11d722e3fbSopenharmony_ci * The above copyright notice and this permission notice shall be included in 12d722e3fbSopenharmony_ci * all copies or substantial portions of the Software. 13d722e3fbSopenharmony_ci * 14d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17d722e3fbSopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18d722e3fbSopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19d722e3fbSopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20d722e3fbSopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 21d722e3fbSopenharmony_ci * 22d722e3fbSopenharmony_ci * Authors: Ben Skeggs 23d722e3fbSopenharmony_ci */ 24d722e3fbSopenharmony_ci 25d722e3fbSopenharmony_ci#include <stdio.h> 26d722e3fbSopenharmony_ci#include <stdlib.h> 27d722e3fbSopenharmony_ci#include <stdint.h> 28d722e3fbSopenharmony_ci#include <stdbool.h> 29d722e3fbSopenharmony_ci#include <string.h> 30d722e3fbSopenharmony_ci#include <assert.h> 31d722e3fbSopenharmony_ci#include <errno.h> 32d722e3fbSopenharmony_ci#include <inttypes.h> 33d722e3fbSopenharmony_ci 34d722e3fbSopenharmony_ci#include <xf86drm.h> 35d722e3fbSopenharmony_ci#include <xf86atomic.h> 36d722e3fbSopenharmony_ci#include "libdrm_lists.h" 37d722e3fbSopenharmony_ci#include "nouveau_drm.h" 38d722e3fbSopenharmony_ci 39d722e3fbSopenharmony_ci#include "nouveau.h" 40d722e3fbSopenharmony_ci#include "private.h" 41d722e3fbSopenharmony_ci 42d722e3fbSopenharmony_cistruct nouveau_pushbuf_krec { 43d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *next; 44d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo buffer[NOUVEAU_GEM_MAX_BUFFERS]; 45d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_reloc reloc[NOUVEAU_GEM_MAX_RELOCS]; 46d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_push push[NOUVEAU_GEM_MAX_PUSH]; 47d722e3fbSopenharmony_ci int nr_buffer; 48d722e3fbSopenharmony_ci int nr_reloc; 49d722e3fbSopenharmony_ci int nr_push; 50d722e3fbSopenharmony_ci uint64_t vram_used; 51d722e3fbSopenharmony_ci uint64_t gart_used; 52d722e3fbSopenharmony_ci}; 53d722e3fbSopenharmony_ci 54d722e3fbSopenharmony_cistruct nouveau_pushbuf_priv { 55d722e3fbSopenharmony_ci struct nouveau_pushbuf base; 56d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *list; 57d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec; 58d722e3fbSopenharmony_ci struct nouveau_list bctx_list; 59d722e3fbSopenharmony_ci struct nouveau_bo *bo; 60d722e3fbSopenharmony_ci uint32_t type; 61d722e3fbSopenharmony_ci uint32_t suffix0; 62d722e3fbSopenharmony_ci uint32_t suffix1; 63d722e3fbSopenharmony_ci uint32_t *ptr; 64d722e3fbSopenharmony_ci uint32_t *bgn; 65d722e3fbSopenharmony_ci int bo_next; 66d722e3fbSopenharmony_ci int bo_nr; 67d722e3fbSopenharmony_ci struct nouveau_bo *bos[]; 68d722e3fbSopenharmony_ci}; 69d722e3fbSopenharmony_ci 70d722e3fbSopenharmony_cistatic inline struct nouveau_pushbuf_priv * 71d722e3fbSopenharmony_cinouveau_pushbuf(struct nouveau_pushbuf *push) 72d722e3fbSopenharmony_ci{ 73d722e3fbSopenharmony_ci return (struct nouveau_pushbuf_priv *)push; 74d722e3fbSopenharmony_ci} 75d722e3fbSopenharmony_ci 76d722e3fbSopenharmony_cistatic int pushbuf_validate(struct nouveau_pushbuf *, bool); 77d722e3fbSopenharmony_cistatic int pushbuf_flush(struct nouveau_pushbuf *); 78d722e3fbSopenharmony_ci 79d722e3fbSopenharmony_cistatic bool 80d722e3fbSopenharmony_cipushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 81d722e3fbSopenharmony_ci uint32_t *domains) 82d722e3fbSopenharmony_ci{ 83d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 84d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 85d722e3fbSopenharmony_ci struct nouveau_device *dev = push->client->device; 86d722e3fbSopenharmony_ci struct nouveau_bo *kbo; 87d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 88d722e3fbSopenharmony_ci int i; 89d722e3fbSopenharmony_ci 90d722e3fbSopenharmony_ci /* VRAM is the only valid domain. GART and VRAM|GART buffers 91d722e3fbSopenharmony_ci * are all accounted to GART, so if this doesn't fit in VRAM 92d722e3fbSopenharmony_ci * straight up, a flush is needed. 93d722e3fbSopenharmony_ci */ 94d722e3fbSopenharmony_ci if (*domains == NOUVEAU_GEM_DOMAIN_VRAM) { 95d722e3fbSopenharmony_ci if (krec->vram_used + bo->size > dev->vram_limit) 96d722e3fbSopenharmony_ci return false; 97d722e3fbSopenharmony_ci krec->vram_used += bo->size; 98d722e3fbSopenharmony_ci return true; 99d722e3fbSopenharmony_ci } 100d722e3fbSopenharmony_ci 101d722e3fbSopenharmony_ci /* GART or VRAM|GART buffer. Account both of these buffer types 102d722e3fbSopenharmony_ci * to GART only for the moment, which simplifies things. If the 103d722e3fbSopenharmony_ci * buffer can fit already, we're done here. 104d722e3fbSopenharmony_ci */ 105d722e3fbSopenharmony_ci if (krec->gart_used + bo->size <= dev->gart_limit) { 106d722e3fbSopenharmony_ci krec->gart_used += bo->size; 107d722e3fbSopenharmony_ci return true; 108d722e3fbSopenharmony_ci } 109d722e3fbSopenharmony_ci 110d722e3fbSopenharmony_ci /* Ran out of GART space, if it's a VRAM|GART buffer and it'll 111d722e3fbSopenharmony_ci * fit into available VRAM, turn it into a VRAM buffer 112d722e3fbSopenharmony_ci */ 113d722e3fbSopenharmony_ci if ((*domains & NOUVEAU_GEM_DOMAIN_VRAM) && 114d722e3fbSopenharmony_ci krec->vram_used + bo->size <= dev->vram_limit) { 115d722e3fbSopenharmony_ci *domains &= NOUVEAU_GEM_DOMAIN_VRAM; 116d722e3fbSopenharmony_ci krec->vram_used += bo->size; 117d722e3fbSopenharmony_ci return true; 118d722e3fbSopenharmony_ci } 119d722e3fbSopenharmony_ci 120d722e3fbSopenharmony_ci /* Still couldn't fit the buffer in anywhere, so as a last resort; 121d722e3fbSopenharmony_ci * scan the buffer list for VRAM|GART buffers and turn them into 122d722e3fbSopenharmony_ci * VRAM buffers until we have enough space in GART for this one 123d722e3fbSopenharmony_ci */ 124d722e3fbSopenharmony_ci kref = krec->buffer; 125d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_buffer; i++, kref++) { 126d722e3fbSopenharmony_ci if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) 127d722e3fbSopenharmony_ci continue; 128d722e3fbSopenharmony_ci 129d722e3fbSopenharmony_ci kbo = (void *)(unsigned long)kref->user_priv; 130d722e3fbSopenharmony_ci if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) || 131d722e3fbSopenharmony_ci krec->vram_used + kbo->size > dev->vram_limit) 132d722e3fbSopenharmony_ci continue; 133d722e3fbSopenharmony_ci 134d722e3fbSopenharmony_ci kref->valid_domains &= NOUVEAU_GEM_DOMAIN_VRAM; 135d722e3fbSopenharmony_ci krec->gart_used -= kbo->size; 136d722e3fbSopenharmony_ci krec->vram_used += kbo->size; 137d722e3fbSopenharmony_ci if (krec->gart_used + bo->size <= dev->gart_limit) { 138d722e3fbSopenharmony_ci krec->gart_used += bo->size; 139d722e3fbSopenharmony_ci return true; 140d722e3fbSopenharmony_ci } 141d722e3fbSopenharmony_ci } 142d722e3fbSopenharmony_ci 143d722e3fbSopenharmony_ci /* Couldn't resolve a placement, need to force a flush */ 144d722e3fbSopenharmony_ci return false; 145d722e3fbSopenharmony_ci} 146d722e3fbSopenharmony_ci 147d722e3fbSopenharmony_cistatic struct drm_nouveau_gem_pushbuf_bo * 148d722e3fbSopenharmony_cipushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 149d722e3fbSopenharmony_ci uint32_t flags) 150d722e3fbSopenharmony_ci{ 151d722e3fbSopenharmony_ci struct nouveau_device *dev = push->client->device; 152d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 153d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 154d722e3fbSopenharmony_ci struct nouveau_pushbuf *fpush; 155d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 156d722e3fbSopenharmony_ci uint32_t domains, domains_wr, domains_rd; 157d722e3fbSopenharmony_ci 158d722e3fbSopenharmony_ci domains = 0; 159d722e3fbSopenharmony_ci if (flags & NOUVEAU_BO_VRAM) 160d722e3fbSopenharmony_ci domains |= NOUVEAU_GEM_DOMAIN_VRAM; 161d722e3fbSopenharmony_ci if (flags & NOUVEAU_BO_GART) 162d722e3fbSopenharmony_ci domains |= NOUVEAU_GEM_DOMAIN_GART; 163d722e3fbSopenharmony_ci domains_wr = domains * !!(flags & NOUVEAU_BO_WR); 164d722e3fbSopenharmony_ci domains_rd = domains * !!(flags & NOUVEAU_BO_RD); 165d722e3fbSopenharmony_ci 166d722e3fbSopenharmony_ci /* if buffer is referenced on another pushbuf that is owned by the 167d722e3fbSopenharmony_ci * same client, we need to flush the other pushbuf first to ensure 168d722e3fbSopenharmony_ci * the correct ordering of commands 169d722e3fbSopenharmony_ci */ 170d722e3fbSopenharmony_ci fpush = cli_push_get(push->client, bo); 171d722e3fbSopenharmony_ci if (fpush && fpush != push) 172d722e3fbSopenharmony_ci pushbuf_flush(fpush); 173d722e3fbSopenharmony_ci 174d722e3fbSopenharmony_ci kref = cli_kref_get(push->client, bo); 175d722e3fbSopenharmony_ci if (kref) { 176d722e3fbSopenharmony_ci /* possible conflict in memory types - flush and retry */ 177d722e3fbSopenharmony_ci if (!(kref->valid_domains & domains)) 178d722e3fbSopenharmony_ci return NULL; 179d722e3fbSopenharmony_ci 180d722e3fbSopenharmony_ci /* VRAM|GART buffer turning into a VRAM buffer. Make sure 181d722e3fbSopenharmony_ci * it'll fit in VRAM and force a flush if not. 182d722e3fbSopenharmony_ci */ 183d722e3fbSopenharmony_ci if ((kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART) && 184d722e3fbSopenharmony_ci ( domains == NOUVEAU_GEM_DOMAIN_VRAM)) { 185d722e3fbSopenharmony_ci if (krec->vram_used + bo->size > dev->vram_limit) 186d722e3fbSopenharmony_ci return NULL; 187d722e3fbSopenharmony_ci krec->vram_used += bo->size; 188d722e3fbSopenharmony_ci krec->gart_used -= bo->size; 189d722e3fbSopenharmony_ci } 190d722e3fbSopenharmony_ci 191d722e3fbSopenharmony_ci kref->valid_domains &= domains; 192d722e3fbSopenharmony_ci kref->write_domains |= domains_wr; 193d722e3fbSopenharmony_ci kref->read_domains |= domains_rd; 194d722e3fbSopenharmony_ci } else { 195d722e3fbSopenharmony_ci if (krec->nr_buffer == NOUVEAU_GEM_MAX_BUFFERS || 196d722e3fbSopenharmony_ci !pushbuf_kref_fits(push, bo, &domains)) 197d722e3fbSopenharmony_ci return NULL; 198d722e3fbSopenharmony_ci 199d722e3fbSopenharmony_ci kref = &krec->buffer[krec->nr_buffer++]; 200d722e3fbSopenharmony_ci kref->user_priv = (unsigned long)bo; 201d722e3fbSopenharmony_ci kref->handle = bo->handle; 202d722e3fbSopenharmony_ci kref->valid_domains = domains; 203d722e3fbSopenharmony_ci kref->write_domains = domains_wr; 204d722e3fbSopenharmony_ci kref->read_domains = domains_rd; 205d722e3fbSopenharmony_ci kref->presumed.valid = 1; 206d722e3fbSopenharmony_ci kref->presumed.offset = bo->offset; 207d722e3fbSopenharmony_ci if (bo->flags & NOUVEAU_BO_VRAM) 208d722e3fbSopenharmony_ci kref->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; 209d722e3fbSopenharmony_ci else 210d722e3fbSopenharmony_ci kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; 211d722e3fbSopenharmony_ci 212d722e3fbSopenharmony_ci cli_kref_set(push->client, bo, kref, push); 213d722e3fbSopenharmony_ci atomic_inc(&nouveau_bo(bo)->refcnt); 214d722e3fbSopenharmony_ci } 215d722e3fbSopenharmony_ci 216d722e3fbSopenharmony_ci return kref; 217d722e3fbSopenharmony_ci} 218d722e3fbSopenharmony_ci 219d722e3fbSopenharmony_cistatic uint32_t 220d722e3fbSopenharmony_cipushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 221d722e3fbSopenharmony_ci uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) 222d722e3fbSopenharmony_ci{ 223d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 224d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 225d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_reloc *krel; 226d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *pkref; 227d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *bkref; 228d722e3fbSopenharmony_ci uint32_t reloc = data; 229d722e3fbSopenharmony_ci 230d722e3fbSopenharmony_ci pkref = cli_kref_get(push->client, nvpb->bo); 231d722e3fbSopenharmony_ci bkref = cli_kref_get(push->client, bo); 232d722e3fbSopenharmony_ci krel = &krec->reloc[krec->nr_reloc++]; 233d722e3fbSopenharmony_ci 234d722e3fbSopenharmony_ci assert(pkref); 235d722e3fbSopenharmony_ci assert(bkref); 236d722e3fbSopenharmony_ci krel->reloc_bo_index = pkref - krec->buffer; 237d722e3fbSopenharmony_ci krel->reloc_bo_offset = (push->cur - nvpb->ptr) * 4; 238d722e3fbSopenharmony_ci krel->bo_index = bkref - krec->buffer; 239d722e3fbSopenharmony_ci krel->flags = 0; 240d722e3fbSopenharmony_ci krel->data = data; 241d722e3fbSopenharmony_ci krel->vor = vor; 242d722e3fbSopenharmony_ci krel->tor = tor; 243d722e3fbSopenharmony_ci 244d722e3fbSopenharmony_ci if (flags & NOUVEAU_BO_LOW) { 245d722e3fbSopenharmony_ci reloc = (bkref->presumed.offset + data); 246d722e3fbSopenharmony_ci krel->flags |= NOUVEAU_GEM_RELOC_LOW; 247d722e3fbSopenharmony_ci } else 248d722e3fbSopenharmony_ci if (flags & NOUVEAU_BO_HIGH) { 249d722e3fbSopenharmony_ci reloc = (bkref->presumed.offset + data) >> 32; 250d722e3fbSopenharmony_ci krel->flags |= NOUVEAU_GEM_RELOC_HIGH; 251d722e3fbSopenharmony_ci } 252d722e3fbSopenharmony_ci if (flags & NOUVEAU_BO_OR) { 253d722e3fbSopenharmony_ci if (bkref->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) 254d722e3fbSopenharmony_ci reloc |= vor; 255d722e3fbSopenharmony_ci else 256d722e3fbSopenharmony_ci reloc |= tor; 257d722e3fbSopenharmony_ci krel->flags |= NOUVEAU_GEM_RELOC_OR; 258d722e3fbSopenharmony_ci } 259d722e3fbSopenharmony_ci 260d722e3fbSopenharmony_ci return reloc; 261d722e3fbSopenharmony_ci} 262d722e3fbSopenharmony_ci 263d722e3fbSopenharmony_cistatic void 264d722e3fbSopenharmony_cipushbuf_dump(struct nouveau_pushbuf_krec *krec, int krec_id, int chid) 265d722e3fbSopenharmony_ci{ 266d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_reloc *krel; 267d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_push *kpsh; 268d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 269d722e3fbSopenharmony_ci struct nouveau_bo *bo; 270d722e3fbSopenharmony_ci uint32_t *bgn, *end; 271d722e3fbSopenharmony_ci int i; 272d722e3fbSopenharmony_ci 273d722e3fbSopenharmony_ci err("ch%d: krec %d pushes %d bufs %d relocs %d\n", chid, 274d722e3fbSopenharmony_ci krec_id, krec->nr_push, krec->nr_buffer, krec->nr_reloc); 275d722e3fbSopenharmony_ci 276d722e3fbSopenharmony_ci kref = krec->buffer; 277d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_buffer; i++, kref++) { 278d722e3fbSopenharmony_ci bo = (void *)(uintptr_t)kref->user_priv; 279d722e3fbSopenharmony_ci err("ch%d: buf %08x %08x %08x %08x %08x %p 0x%"PRIx64" 0x%"PRIx64"\n", chid, i, 280d722e3fbSopenharmony_ci kref->handle, kref->valid_domains, 281d722e3fbSopenharmony_ci kref->read_domains, kref->write_domains, bo->map, bo->offset, bo->size); 282d722e3fbSopenharmony_ci } 283d722e3fbSopenharmony_ci 284d722e3fbSopenharmony_ci krel = krec->reloc; 285d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_reloc; i++, krel++) { 286d722e3fbSopenharmony_ci err("ch%d: rel %08x %08x %08x %08x %08x %08x %08x\n", 287d722e3fbSopenharmony_ci chid, krel->reloc_bo_index, krel->reloc_bo_offset, 288d722e3fbSopenharmony_ci krel->bo_index, krel->flags, krel->data, 289d722e3fbSopenharmony_ci krel->vor, krel->tor); 290d722e3fbSopenharmony_ci } 291d722e3fbSopenharmony_ci 292d722e3fbSopenharmony_ci kpsh = krec->push; 293d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_push; i++, kpsh++) { 294d722e3fbSopenharmony_ci kref = krec->buffer + kpsh->bo_index; 295d722e3fbSopenharmony_ci bo = (void *)(unsigned long)kref->user_priv; 296d722e3fbSopenharmony_ci bgn = (uint32_t *)((char *)bo->map + kpsh->offset); 297d722e3fbSopenharmony_ci end = bgn + ((kpsh->length & 0x7fffff) /4); 298d722e3fbSopenharmony_ci 299d722e3fbSopenharmony_ci err("ch%d: psh %s%08x %010llx %010llx\n", chid, 300d722e3fbSopenharmony_ci bo->map ? "" : "(unmapped) ", kpsh->bo_index, 301d722e3fbSopenharmony_ci (unsigned long long)kpsh->offset, 302d722e3fbSopenharmony_ci (unsigned long long)(kpsh->offset + kpsh->length)); 303d722e3fbSopenharmony_ci if (!bo->map) 304d722e3fbSopenharmony_ci continue; 305d722e3fbSopenharmony_ci while (bgn < end) 306d722e3fbSopenharmony_ci err("\t0x%08x\n", *bgn++); 307d722e3fbSopenharmony_ci } 308d722e3fbSopenharmony_ci} 309d722e3fbSopenharmony_ci 310d722e3fbSopenharmony_cistatic int 311d722e3fbSopenharmony_cipushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan) 312d722e3fbSopenharmony_ci{ 313d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 314d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->list; 315d722e3fbSopenharmony_ci struct nouveau_device *dev = push->client->device; 316d722e3fbSopenharmony_ci struct nouveau_drm *drm = nouveau_drm(&dev->object); 317d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo_presumed *info; 318d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 319d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf req; 320d722e3fbSopenharmony_ci struct nouveau_fifo *fifo = chan->data; 321d722e3fbSopenharmony_ci struct nouveau_bo *bo; 322d722e3fbSopenharmony_ci int krec_id = 0; 323d722e3fbSopenharmony_ci int ret = 0, i; 324d722e3fbSopenharmony_ci 325d722e3fbSopenharmony_ci if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 326d722e3fbSopenharmony_ci return -EINVAL; 327d722e3fbSopenharmony_ci 328d722e3fbSopenharmony_ci if (push->kick_notify) 329d722e3fbSopenharmony_ci push->kick_notify(push); 330d722e3fbSopenharmony_ci 331d722e3fbSopenharmony_ci nouveau_pushbuf_data(push, NULL, 0, 0); 332d722e3fbSopenharmony_ci 333d722e3fbSopenharmony_ci while (krec && krec->nr_push) { 334d722e3fbSopenharmony_ci req.channel = fifo->channel; 335d722e3fbSopenharmony_ci req.nr_buffers = krec->nr_buffer; 336d722e3fbSopenharmony_ci req.buffers = (uint64_t)(unsigned long)krec->buffer; 337d722e3fbSopenharmony_ci req.nr_relocs = krec->nr_reloc; 338d722e3fbSopenharmony_ci req.nr_push = krec->nr_push; 339d722e3fbSopenharmony_ci req.relocs = (uint64_t)(unsigned long)krec->reloc; 340d722e3fbSopenharmony_ci req.push = (uint64_t)(unsigned long)krec->push; 341d722e3fbSopenharmony_ci req.suffix0 = nvpb->suffix0; 342d722e3fbSopenharmony_ci req.suffix1 = nvpb->suffix1; 343d722e3fbSopenharmony_ci req.vram_available = 0; /* for valgrind */ 344d722e3fbSopenharmony_ci if (dbg_on(1)) 345d722e3fbSopenharmony_ci req.vram_available |= NOUVEAU_GEM_PUSHBUF_SYNC; 346d722e3fbSopenharmony_ci req.gart_available = 0; 347d722e3fbSopenharmony_ci 348d722e3fbSopenharmony_ci if (dbg_on(0)) 349d722e3fbSopenharmony_ci pushbuf_dump(krec, krec_id++, fifo->channel); 350d722e3fbSopenharmony_ci 351d722e3fbSopenharmony_ci#ifndef SIMULATE 352d722e3fbSopenharmony_ci ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 353d722e3fbSopenharmony_ci &req, sizeof(req)); 354d722e3fbSopenharmony_ci nvpb->suffix0 = req.suffix0; 355d722e3fbSopenharmony_ci nvpb->suffix1 = req.suffix1; 356d722e3fbSopenharmony_ci dev->vram_limit = (req.vram_available * 357d722e3fbSopenharmony_ci nouveau_device(dev)->vram_limit_percent) / 100; 358d722e3fbSopenharmony_ci dev->gart_limit = (req.gart_available * 359d722e3fbSopenharmony_ci nouveau_device(dev)->gart_limit_percent) / 100; 360d722e3fbSopenharmony_ci#else 361d722e3fbSopenharmony_ci if (dbg_on(31)) 362d722e3fbSopenharmony_ci ret = -EINVAL; 363d722e3fbSopenharmony_ci#endif 364d722e3fbSopenharmony_ci 365d722e3fbSopenharmony_ci if (ret) { 366d722e3fbSopenharmony_ci err("kernel rejected pushbuf: %s\n", strerror(-ret)); 367d722e3fbSopenharmony_ci pushbuf_dump(krec, krec_id++, fifo->channel); 368d722e3fbSopenharmony_ci break; 369d722e3fbSopenharmony_ci } 370d722e3fbSopenharmony_ci 371d722e3fbSopenharmony_ci kref = krec->buffer; 372d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_buffer; i++, kref++) { 373d722e3fbSopenharmony_ci bo = (void *)(unsigned long)kref->user_priv; 374d722e3fbSopenharmony_ci 375d722e3fbSopenharmony_ci info = &kref->presumed; 376d722e3fbSopenharmony_ci if (!info->valid) { 377d722e3fbSopenharmony_ci bo->flags &= ~NOUVEAU_BO_APER; 378d722e3fbSopenharmony_ci if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM) 379d722e3fbSopenharmony_ci bo->flags |= NOUVEAU_BO_VRAM; 380d722e3fbSopenharmony_ci else 381d722e3fbSopenharmony_ci bo->flags |= NOUVEAU_BO_GART; 382d722e3fbSopenharmony_ci bo->offset = info->offset; 383d722e3fbSopenharmony_ci } 384d722e3fbSopenharmony_ci 385d722e3fbSopenharmony_ci if (kref->write_domains) 386d722e3fbSopenharmony_ci nouveau_bo(bo)->access |= NOUVEAU_BO_WR; 387d722e3fbSopenharmony_ci if (kref->read_domains) 388d722e3fbSopenharmony_ci nouveau_bo(bo)->access |= NOUVEAU_BO_RD; 389d722e3fbSopenharmony_ci } 390d722e3fbSopenharmony_ci 391d722e3fbSopenharmony_ci krec = krec->next; 392d722e3fbSopenharmony_ci } 393d722e3fbSopenharmony_ci 394d722e3fbSopenharmony_ci return ret; 395d722e3fbSopenharmony_ci} 396d722e3fbSopenharmony_ci 397d722e3fbSopenharmony_cistatic int 398d722e3fbSopenharmony_cipushbuf_flush(struct nouveau_pushbuf *push) 399d722e3fbSopenharmony_ci{ 400d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 401d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 402d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 403d722e3fbSopenharmony_ci struct nouveau_bufctx *bctx, *btmp; 404d722e3fbSopenharmony_ci struct nouveau_bo *bo; 405d722e3fbSopenharmony_ci int ret = 0, i; 406d722e3fbSopenharmony_ci 407d722e3fbSopenharmony_ci if (push->channel) { 408d722e3fbSopenharmony_ci ret = pushbuf_submit(push, push->channel); 409d722e3fbSopenharmony_ci } else { 410d722e3fbSopenharmony_ci nouveau_pushbuf_data(push, NULL, 0, 0); 411d722e3fbSopenharmony_ci krec->next = malloc(sizeof(*krec)); 412d722e3fbSopenharmony_ci nvpb->krec = krec->next; 413d722e3fbSopenharmony_ci } 414d722e3fbSopenharmony_ci 415d722e3fbSopenharmony_ci kref = krec->buffer; 416d722e3fbSopenharmony_ci for (i = 0; i < krec->nr_buffer; i++, kref++) { 417d722e3fbSopenharmony_ci bo = (void *)(unsigned long)kref->user_priv; 418d722e3fbSopenharmony_ci cli_kref_set(push->client, bo, NULL, NULL); 419d722e3fbSopenharmony_ci if (push->channel) 420d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &bo); 421d722e3fbSopenharmony_ci } 422d722e3fbSopenharmony_ci 423d722e3fbSopenharmony_ci krec = nvpb->krec; 424d722e3fbSopenharmony_ci krec->vram_used = 0; 425d722e3fbSopenharmony_ci krec->gart_used = 0; 426d722e3fbSopenharmony_ci krec->nr_buffer = 0; 427d722e3fbSopenharmony_ci krec->nr_reloc = 0; 428d722e3fbSopenharmony_ci krec->nr_push = 0; 429d722e3fbSopenharmony_ci 430d722e3fbSopenharmony_ci DRMLISTFOREACHENTRYSAFE(bctx, btmp, &nvpb->bctx_list, head) { 431d722e3fbSopenharmony_ci DRMLISTJOIN(&bctx->current, &bctx->pending); 432d722e3fbSopenharmony_ci DRMINITLISTHEAD(&bctx->current); 433d722e3fbSopenharmony_ci DRMLISTDELINIT(&bctx->head); 434d722e3fbSopenharmony_ci } 435d722e3fbSopenharmony_ci 436d722e3fbSopenharmony_ci return ret; 437d722e3fbSopenharmony_ci} 438d722e3fbSopenharmony_ci 439d722e3fbSopenharmony_cistatic void 440d722e3fbSopenharmony_cipushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel) 441d722e3fbSopenharmony_ci{ 442d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 443d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 444d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 445d722e3fbSopenharmony_ci 446d722e3fbSopenharmony_ci kref = krec->buffer + sref; 447d722e3fbSopenharmony_ci while (krec->nr_buffer-- > sref) { 448d722e3fbSopenharmony_ci struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv; 449d722e3fbSopenharmony_ci cli_kref_set(push->client, bo, NULL, NULL); 450d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &bo); 451d722e3fbSopenharmony_ci kref++; 452d722e3fbSopenharmony_ci } 453d722e3fbSopenharmony_ci krec->nr_buffer = sref; 454d722e3fbSopenharmony_ci krec->nr_reloc = srel; 455d722e3fbSopenharmony_ci} 456d722e3fbSopenharmony_ci 457d722e3fbSopenharmony_cistatic int 458d722e3fbSopenharmony_cipushbuf_refn(struct nouveau_pushbuf *push, bool retry, 459d722e3fbSopenharmony_ci struct nouveau_pushbuf_refn *refs, int nr) 460d722e3fbSopenharmony_ci{ 461d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 462d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 463d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 464d722e3fbSopenharmony_ci int sref = krec->nr_buffer; 465d722e3fbSopenharmony_ci int ret = 0, i; 466d722e3fbSopenharmony_ci 467d722e3fbSopenharmony_ci for (i = 0; i < nr; i++) { 468d722e3fbSopenharmony_ci kref = pushbuf_kref(push, refs[i].bo, refs[i].flags); 469d722e3fbSopenharmony_ci if (!kref) { 470d722e3fbSopenharmony_ci ret = -ENOSPC; 471d722e3fbSopenharmony_ci break; 472d722e3fbSopenharmony_ci } 473d722e3fbSopenharmony_ci } 474d722e3fbSopenharmony_ci 475d722e3fbSopenharmony_ci if (ret) { 476d722e3fbSopenharmony_ci pushbuf_refn_fail(push, sref, krec->nr_reloc); 477d722e3fbSopenharmony_ci if (retry) { 478d722e3fbSopenharmony_ci pushbuf_flush(push); 479d722e3fbSopenharmony_ci nouveau_pushbuf_space(push, 0, 0, 0); 480d722e3fbSopenharmony_ci return pushbuf_refn(push, false, refs, nr); 481d722e3fbSopenharmony_ci } 482d722e3fbSopenharmony_ci } 483d722e3fbSopenharmony_ci 484d722e3fbSopenharmony_ci return ret; 485d722e3fbSopenharmony_ci} 486d722e3fbSopenharmony_ci 487d722e3fbSopenharmony_cistatic int 488d722e3fbSopenharmony_cipushbuf_validate(struct nouveau_pushbuf *push, bool retry) 489d722e3fbSopenharmony_ci{ 490d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 491d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 492d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 493d722e3fbSopenharmony_ci struct nouveau_bufctx *bctx = push->bufctx; 494d722e3fbSopenharmony_ci struct nouveau_bufref *bref; 495d722e3fbSopenharmony_ci int relocs = bctx ? bctx->relocs * 2: 0; 496d722e3fbSopenharmony_ci int sref, srel, ret; 497d722e3fbSopenharmony_ci 498d722e3fbSopenharmony_ci ret = nouveau_pushbuf_space(push, relocs, relocs, 0); 499d722e3fbSopenharmony_ci if (ret || bctx == NULL) 500d722e3fbSopenharmony_ci return ret; 501d722e3fbSopenharmony_ci 502d722e3fbSopenharmony_ci sref = krec->nr_buffer; 503d722e3fbSopenharmony_ci srel = krec->nr_reloc; 504d722e3fbSopenharmony_ci 505d722e3fbSopenharmony_ci DRMLISTDEL(&bctx->head); 506d722e3fbSopenharmony_ci DRMLISTADD(&bctx->head, &nvpb->bctx_list); 507d722e3fbSopenharmony_ci 508d722e3fbSopenharmony_ci DRMLISTFOREACHENTRY(bref, &bctx->pending, thead) { 509d722e3fbSopenharmony_ci kref = pushbuf_kref(push, bref->bo, bref->flags); 510d722e3fbSopenharmony_ci if (!kref) { 511d722e3fbSopenharmony_ci ret = -ENOSPC; 512d722e3fbSopenharmony_ci break; 513d722e3fbSopenharmony_ci } 514d722e3fbSopenharmony_ci 515d722e3fbSopenharmony_ci if (bref->packet) { 516d722e3fbSopenharmony_ci pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0); 517d722e3fbSopenharmony_ci *push->cur++ = 0; 518d722e3fbSopenharmony_ci pushbuf_krel(push, bref->bo, bref->data, bref->flags, 519d722e3fbSopenharmony_ci bref->vor, bref->tor); 520d722e3fbSopenharmony_ci *push->cur++ = 0; 521d722e3fbSopenharmony_ci } 522d722e3fbSopenharmony_ci } 523d722e3fbSopenharmony_ci 524d722e3fbSopenharmony_ci DRMLISTJOIN(&bctx->pending, &bctx->current); 525d722e3fbSopenharmony_ci DRMINITLISTHEAD(&bctx->pending); 526d722e3fbSopenharmony_ci 527d722e3fbSopenharmony_ci if (ret) { 528d722e3fbSopenharmony_ci pushbuf_refn_fail(push, sref, srel); 529d722e3fbSopenharmony_ci if (retry) { 530d722e3fbSopenharmony_ci pushbuf_flush(push); 531d722e3fbSopenharmony_ci return pushbuf_validate(push, false); 532d722e3fbSopenharmony_ci } 533d722e3fbSopenharmony_ci } 534d722e3fbSopenharmony_ci 535d722e3fbSopenharmony_ci return ret; 536d722e3fbSopenharmony_ci} 537d722e3fbSopenharmony_ci 538d722e3fbSopenharmony_cidrm_public int 539d722e3fbSopenharmony_cinouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan, 540d722e3fbSopenharmony_ci int nr, uint32_t size, bool immediate, 541d722e3fbSopenharmony_ci struct nouveau_pushbuf **ppush) 542d722e3fbSopenharmony_ci{ 543d722e3fbSopenharmony_ci struct nouveau_drm *drm = nouveau_drm(&client->device->object); 544d722e3fbSopenharmony_ci struct nouveau_fifo *fifo = chan->data; 545d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb; 546d722e3fbSopenharmony_ci struct nouveau_pushbuf *push; 547d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf req = {}; 548d722e3fbSopenharmony_ci int ret; 549d722e3fbSopenharmony_ci 550d722e3fbSopenharmony_ci if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) 551d722e3fbSopenharmony_ci return -EINVAL; 552d722e3fbSopenharmony_ci 553d722e3fbSopenharmony_ci /* nop pushbuf call, to get the current "return to main" sequence 554d722e3fbSopenharmony_ci * we need to append to the pushbuf on early chipsets 555d722e3fbSopenharmony_ci */ 556d722e3fbSopenharmony_ci req.channel = fifo->channel; 557d722e3fbSopenharmony_ci req.nr_push = 0; 558d722e3fbSopenharmony_ci ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 559d722e3fbSopenharmony_ci &req, sizeof(req)); 560d722e3fbSopenharmony_ci if (ret) 561d722e3fbSopenharmony_ci return ret; 562d722e3fbSopenharmony_ci 563d722e3fbSopenharmony_ci nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos)); 564d722e3fbSopenharmony_ci if (!nvpb) 565d722e3fbSopenharmony_ci return -ENOMEM; 566d722e3fbSopenharmony_ci 567d722e3fbSopenharmony_ci#ifndef SIMULATE 568d722e3fbSopenharmony_ci nvpb->suffix0 = req.suffix0; 569d722e3fbSopenharmony_ci nvpb->suffix1 = req.suffix1; 570d722e3fbSopenharmony_ci#else 571d722e3fbSopenharmony_ci nvpb->suffix0 = 0xffffffff; 572d722e3fbSopenharmony_ci nvpb->suffix1 = 0xffffffff; 573d722e3fbSopenharmony_ci#endif 574d722e3fbSopenharmony_ci nvpb->krec = calloc(1, sizeof(*nvpb->krec)); 575d722e3fbSopenharmony_ci nvpb->list = nvpb->krec; 576d722e3fbSopenharmony_ci if (!nvpb->krec) { 577d722e3fbSopenharmony_ci free(nvpb); 578d722e3fbSopenharmony_ci return -ENOMEM; 579d722e3fbSopenharmony_ci } 580d722e3fbSopenharmony_ci 581d722e3fbSopenharmony_ci push = &nvpb->base; 582d722e3fbSopenharmony_ci push->client = client; 583d722e3fbSopenharmony_ci push->channel = immediate ? chan : NULL; 584d722e3fbSopenharmony_ci push->flags = NOUVEAU_BO_RD; 585d722e3fbSopenharmony_ci if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) { 586d722e3fbSopenharmony_ci push->flags |= NOUVEAU_BO_GART; 587d722e3fbSopenharmony_ci nvpb->type = NOUVEAU_BO_GART; 588d722e3fbSopenharmony_ci } else 589d722e3fbSopenharmony_ci if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) { 590d722e3fbSopenharmony_ci push->flags |= NOUVEAU_BO_VRAM; 591d722e3fbSopenharmony_ci nvpb->type = NOUVEAU_BO_VRAM; 592d722e3fbSopenharmony_ci } 593d722e3fbSopenharmony_ci nvpb->type |= NOUVEAU_BO_MAP; 594d722e3fbSopenharmony_ci 595d722e3fbSopenharmony_ci for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) { 596d722e3fbSopenharmony_ci ret = nouveau_bo_new(client->device, nvpb->type, 0, size, 597d722e3fbSopenharmony_ci NULL, &nvpb->bos[nvpb->bo_nr]); 598d722e3fbSopenharmony_ci if (ret) { 599d722e3fbSopenharmony_ci nouveau_pushbuf_del(&push); 600d722e3fbSopenharmony_ci return ret; 601d722e3fbSopenharmony_ci } 602d722e3fbSopenharmony_ci } 603d722e3fbSopenharmony_ci 604d722e3fbSopenharmony_ci DRMINITLISTHEAD(&nvpb->bctx_list); 605d722e3fbSopenharmony_ci *ppush = push; 606d722e3fbSopenharmony_ci return 0; 607d722e3fbSopenharmony_ci} 608d722e3fbSopenharmony_ci 609d722e3fbSopenharmony_cidrm_public void 610d722e3fbSopenharmony_cinouveau_pushbuf_del(struct nouveau_pushbuf **ppush) 611d722e3fbSopenharmony_ci{ 612d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush); 613d722e3fbSopenharmony_ci if (nvpb) { 614d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 615d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec; 616d722e3fbSopenharmony_ci while ((krec = nvpb->list)) { 617d722e3fbSopenharmony_ci kref = krec->buffer; 618d722e3fbSopenharmony_ci while (krec->nr_buffer--) { 619d722e3fbSopenharmony_ci unsigned long priv = kref++->user_priv; 620d722e3fbSopenharmony_ci struct nouveau_bo *bo = (void *)priv; 621d722e3fbSopenharmony_ci cli_kref_set(nvpb->base.client, bo, NULL, NULL); 622d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &bo); 623d722e3fbSopenharmony_ci } 624d722e3fbSopenharmony_ci nvpb->list = krec->next; 625d722e3fbSopenharmony_ci free(krec); 626d722e3fbSopenharmony_ci } 627d722e3fbSopenharmony_ci while (nvpb->bo_nr--) 628d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]); 629d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &nvpb->bo); 630d722e3fbSopenharmony_ci free(nvpb); 631d722e3fbSopenharmony_ci } 632d722e3fbSopenharmony_ci *ppush = NULL; 633d722e3fbSopenharmony_ci} 634d722e3fbSopenharmony_ci 635d722e3fbSopenharmony_cidrm_public struct nouveau_bufctx * 636d722e3fbSopenharmony_cinouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx) 637d722e3fbSopenharmony_ci{ 638d722e3fbSopenharmony_ci struct nouveau_bufctx *prev = push->bufctx; 639d722e3fbSopenharmony_ci push->bufctx = ctx; 640d722e3fbSopenharmony_ci return prev; 641d722e3fbSopenharmony_ci} 642d722e3fbSopenharmony_ci 643d722e3fbSopenharmony_cidrm_public int 644d722e3fbSopenharmony_cinouveau_pushbuf_space(struct nouveau_pushbuf *push, 645d722e3fbSopenharmony_ci uint32_t dwords, uint32_t relocs, uint32_t pushes) 646d722e3fbSopenharmony_ci{ 647d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 648d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 649d722e3fbSopenharmony_ci struct nouveau_client *client = push->client; 650d722e3fbSopenharmony_ci struct nouveau_bo *bo = NULL; 651d722e3fbSopenharmony_ci bool flushed = false; 652d722e3fbSopenharmony_ci int ret = 0; 653d722e3fbSopenharmony_ci 654d722e3fbSopenharmony_ci /* switch to next buffer if insufficient space in the current one */ 655d722e3fbSopenharmony_ci if (push->cur + dwords >= push->end) { 656d722e3fbSopenharmony_ci if (nvpb->bo_next < nvpb->bo_nr) { 657d722e3fbSopenharmony_ci nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo); 658d722e3fbSopenharmony_ci if (nvpb->bo_next == nvpb->bo_nr && push->channel) 659d722e3fbSopenharmony_ci nvpb->bo_next = 0; 660d722e3fbSopenharmony_ci } else { 661d722e3fbSopenharmony_ci ret = nouveau_bo_new(client->device, nvpb->type, 0, 662d722e3fbSopenharmony_ci nvpb->bos[0]->size, NULL, &bo); 663d722e3fbSopenharmony_ci if (ret) 664d722e3fbSopenharmony_ci return ret; 665d722e3fbSopenharmony_ci } 666d722e3fbSopenharmony_ci } 667d722e3fbSopenharmony_ci 668d722e3fbSopenharmony_ci /* make sure there's always enough space to queue up the pending 669d722e3fbSopenharmony_ci * data in the pushbuf proper 670d722e3fbSopenharmony_ci */ 671d722e3fbSopenharmony_ci pushes++; 672d722e3fbSopenharmony_ci 673d722e3fbSopenharmony_ci /* need to flush if we've run out of space on an immediate pushbuf, 674d722e3fbSopenharmony_ci * if the new buffer won't fit, or if the kernel push/reloc limits 675d722e3fbSopenharmony_ci * have been hit 676d722e3fbSopenharmony_ci */ 677d722e3fbSopenharmony_ci if ((bo && ( push->channel || 678d722e3fbSopenharmony_ci !pushbuf_kref(push, bo, push->flags))) || 679d722e3fbSopenharmony_ci krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS || 680d722e3fbSopenharmony_ci krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) { 681d722e3fbSopenharmony_ci if (nvpb->bo && krec->nr_buffer) 682d722e3fbSopenharmony_ci pushbuf_flush(push); 683d722e3fbSopenharmony_ci flushed = true; 684d722e3fbSopenharmony_ci } 685d722e3fbSopenharmony_ci 686d722e3fbSopenharmony_ci /* if necessary, switch to new buffer */ 687d722e3fbSopenharmony_ci if (bo) { 688d722e3fbSopenharmony_ci ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client); 689d722e3fbSopenharmony_ci if (ret) 690d722e3fbSopenharmony_ci return ret; 691d722e3fbSopenharmony_ci 692d722e3fbSopenharmony_ci nouveau_pushbuf_data(push, NULL, 0, 0); 693d722e3fbSopenharmony_ci nouveau_bo_ref(bo, &nvpb->bo); 694d722e3fbSopenharmony_ci nouveau_bo_ref(NULL, &bo); 695d722e3fbSopenharmony_ci 696d722e3fbSopenharmony_ci nvpb->bgn = nvpb->bo->map; 697d722e3fbSopenharmony_ci nvpb->ptr = nvpb->bgn; 698d722e3fbSopenharmony_ci push->cur = nvpb->bgn; 699d722e3fbSopenharmony_ci push->end = push->cur + (nvpb->bo->size / 4); 700d722e3fbSopenharmony_ci push->end -= 2 + push->rsvd_kick; /* space for suffix */ 701d722e3fbSopenharmony_ci } 702d722e3fbSopenharmony_ci 703d722e3fbSopenharmony_ci pushbuf_kref(push, nvpb->bo, push->flags); 704d722e3fbSopenharmony_ci return flushed ? pushbuf_validate(push, false) : 0; 705d722e3fbSopenharmony_ci} 706d722e3fbSopenharmony_ci 707d722e3fbSopenharmony_cidrm_public void 708d722e3fbSopenharmony_cinouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 709d722e3fbSopenharmony_ci uint64_t offset, uint64_t length) 710d722e3fbSopenharmony_ci{ 711d722e3fbSopenharmony_ci struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); 712d722e3fbSopenharmony_ci struct nouveau_pushbuf_krec *krec = nvpb->krec; 713d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_push *kpsh; 714d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 715d722e3fbSopenharmony_ci 716d722e3fbSopenharmony_ci if (bo != nvpb->bo && nvpb->bgn != push->cur) { 717d722e3fbSopenharmony_ci if (nvpb->suffix0 || nvpb->suffix1) { 718d722e3fbSopenharmony_ci *push->cur++ = nvpb->suffix0; 719d722e3fbSopenharmony_ci *push->cur++ = nvpb->suffix1; 720d722e3fbSopenharmony_ci } 721d722e3fbSopenharmony_ci 722d722e3fbSopenharmony_ci nouveau_pushbuf_data(push, nvpb->bo, 723d722e3fbSopenharmony_ci (nvpb->bgn - nvpb->ptr) * 4, 724d722e3fbSopenharmony_ci (push->cur - nvpb->bgn) * 4); 725d722e3fbSopenharmony_ci nvpb->bgn = push->cur; 726d722e3fbSopenharmony_ci } 727d722e3fbSopenharmony_ci 728d722e3fbSopenharmony_ci if (bo) { 729d722e3fbSopenharmony_ci kref = cli_kref_get(push->client, bo); 730d722e3fbSopenharmony_ci assert(kref); 731d722e3fbSopenharmony_ci kpsh = &krec->push[krec->nr_push++]; 732d722e3fbSopenharmony_ci kpsh->bo_index = kref - krec->buffer; 733d722e3fbSopenharmony_ci kpsh->offset = offset; 734d722e3fbSopenharmony_ci kpsh->length = length; 735d722e3fbSopenharmony_ci } 736d722e3fbSopenharmony_ci} 737d722e3fbSopenharmony_ci 738d722e3fbSopenharmony_cidrm_public int 739d722e3fbSopenharmony_cinouveau_pushbuf_refn(struct nouveau_pushbuf *push, 740d722e3fbSopenharmony_ci struct nouveau_pushbuf_refn *refs, int nr) 741d722e3fbSopenharmony_ci{ 742d722e3fbSopenharmony_ci return pushbuf_refn(push, true, refs, nr); 743d722e3fbSopenharmony_ci} 744d722e3fbSopenharmony_ci 745d722e3fbSopenharmony_cidrm_public void 746d722e3fbSopenharmony_cinouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo, 747d722e3fbSopenharmony_ci uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) 748d722e3fbSopenharmony_ci{ 749d722e3fbSopenharmony_ci *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor); 750d722e3fbSopenharmony_ci push->cur++; 751d722e3fbSopenharmony_ci} 752d722e3fbSopenharmony_ci 753d722e3fbSopenharmony_cidrm_public int 754d722e3fbSopenharmony_cinouveau_pushbuf_validate(struct nouveau_pushbuf *push) 755d722e3fbSopenharmony_ci{ 756d722e3fbSopenharmony_ci return pushbuf_validate(push, true); 757d722e3fbSopenharmony_ci} 758d722e3fbSopenharmony_ci 759d722e3fbSopenharmony_cidrm_public uint32_t 760d722e3fbSopenharmony_cinouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo) 761d722e3fbSopenharmony_ci{ 762d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf_bo *kref; 763d722e3fbSopenharmony_ci uint32_t flags = 0; 764d722e3fbSopenharmony_ci 765d722e3fbSopenharmony_ci if (cli_push_get(push->client, bo) == push) { 766d722e3fbSopenharmony_ci kref = cli_kref_get(push->client, bo); 767d722e3fbSopenharmony_ci assert(kref); 768d722e3fbSopenharmony_ci if (kref->read_domains) 769d722e3fbSopenharmony_ci flags |= NOUVEAU_BO_RD; 770d722e3fbSopenharmony_ci if (kref->write_domains) 771d722e3fbSopenharmony_ci flags |= NOUVEAU_BO_WR; 772d722e3fbSopenharmony_ci } 773d722e3fbSopenharmony_ci 774d722e3fbSopenharmony_ci return flags; 775d722e3fbSopenharmony_ci} 776d722e3fbSopenharmony_ci 777d722e3fbSopenharmony_cidrm_public int 778d722e3fbSopenharmony_cinouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan) 779d722e3fbSopenharmony_ci{ 780d722e3fbSopenharmony_ci if (!push->channel) 781d722e3fbSopenharmony_ci return pushbuf_submit(push, chan); 782d722e3fbSopenharmony_ci pushbuf_flush(push); 783d722e3fbSopenharmony_ci return pushbuf_validate(push, false); 784d722e3fbSopenharmony_ci} 785d722e3fbSopenharmony_ci 786d722e3fbSopenharmony_cidrm_public bool 787d722e3fbSopenharmony_cinouveau_check_dead_channel(struct nouveau_drm *drm, struct nouveau_object *chan) 788d722e3fbSopenharmony_ci{ 789d722e3fbSopenharmony_ci struct drm_nouveau_gem_pushbuf req = {}; 790d722e3fbSopenharmony_ci struct nouveau_fifo *fifo = chan->data; 791d722e3fbSopenharmony_ci int ret; 792d722e3fbSopenharmony_ci 793d722e3fbSopenharmony_ci req.channel = fifo->channel; 794d722e3fbSopenharmony_ci req.nr_push = 0; 795d722e3fbSopenharmony_ci 796d722e3fbSopenharmony_ci ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, 797d722e3fbSopenharmony_ci &req, sizeof(req)); 798d722e3fbSopenharmony_ci /* nouveau returns ENODEV once the channel was killed */ 799d722e3fbSopenharmony_ci return ret == -ENODEV; 800d722e3fbSopenharmony_ci} 801