Newer
Older
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
*new_tile = nv10_bo_set_tiling(dev, offset, new_reg->size,
nvbo->mode, nvbo->zeta);
return 0;
}
static void
nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
struct nouveau_drm_tile *new_tile,
struct nouveau_drm_tile **old_tile)
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct drm_device *dev = drm->dev;
struct dma_fence *fence = dma_resv_get_excl(bo->base.resv);
nv10_bo_put_tile_region(dev, *old_tile, fence);
*old_tile = new_tile;
}
static int
nouveau_bo_move(struct ttm_buffer_object *bo, bool evict,
struct ttm_operation_ctx *ctx,
struct ttm_resource *new_reg)
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct ttm_resource *old_reg = &bo->mem;
struct nouveau_drm_tile *new_tile = NULL;
if (new_reg->mem_type == TTM_PL_TT) {
ret = nouveau_ttm_tt_bind(bo->bdev, bo->ttm, new_reg);
if (ret)
return ret;
}
nouveau_bo_move_ntfy(bo, evict, new_reg);
ret = ttm_bo_wait_ctx(bo, ctx);
if (nvbo->bo.pin_count)
NV_WARN(drm, "Moving pinned object %p!\n", nvbo);
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
ret = nouveau_bo_vm_bind(bo, new_reg, &new_tile);
/* Fake bo copy. */
if (old_reg->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
if (old_reg->mem_type == TTM_PL_SYSTEM &&
new_reg->mem_type == TTM_PL_TT) {
ttm_bo_move_null(bo, new_reg);
goto out;
}
if (old_reg->mem_type == TTM_PL_TT &&
new_reg->mem_type == TTM_PL_SYSTEM) {
nouveau_ttm_tt_unbind(bo->bdev, bo->ttm);
ttm_resource_free(bo, &bo->mem);
ttm_bo_assign_mem(bo, new_reg);
/* Hardware assisted copy. */
if (drm->ttm.move) {
if (new_reg->mem_type == TTM_PL_SYSTEM)
ret = nouveau_bo_move_flipd(bo, evict, ctx,
new_reg);
else if (old_reg->mem_type == TTM_PL_SYSTEM)
ret = nouveau_bo_move_flips(bo, evict, ctx,
new_reg);
else
ret = nouveau_bo_move_m2mf(bo, evict, ctx,
new_reg);
if (!ret)
goto out;
}
/* Fallback to software copy. */
ret = ttm_bo_move_memcpy(bo, ctx, new_reg);
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
if (ret)
nouveau_bo_vm_cleanup(bo, NULL, &new_tile);
else
nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile);
}
out_ntfy:
if (ret) {
swap(*new_reg, bo->mem);
nouveau_bo_move_ntfy(bo, false, new_reg);
swap(*new_reg, bo->mem);
}
}
static int
nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
struct nouveau_bo *nvbo = nouveau_bo(bo);
return drm_vma_node_verify_access(&nvbo->bo.base.vma_node,
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
static void
nouveau_ttm_io_mem_free_locked(struct nouveau_drm *drm,
struct ttm_resource *reg)
{
struct nouveau_mem *mem = nouveau_mem(reg);
if (drm->client.mem->oclass >= NVIF_CLASS_MEM_NV50) {
switch (reg->mem_type) {
case TTM_PL_TT:
if (mem->kind)
nvif_object_unmap_handle(&mem->mem.object);
break;
case TTM_PL_VRAM:
nvif_object_unmap_handle(&mem->mem.object);
break;
default:
break;
}
}
}
nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *reg)
struct nouveau_drm *drm = nouveau_bdev(bdev);
struct nvkm_device *device = nvxx_device(&drm->client.device);
struct nouveau_mem *mem = nouveau_mem(reg);
struct nvif_mmu *mmu = &drm->client.mmu;
const u8 type = mmu->type[drm->ttm.type_vram].type;
int ret;
mutex_lock(&drm->ttm.io_reserve_mutex);
retry:
switch (reg->mem_type) {
case TTM_PL_SYSTEM:
/* System memory */
ret = 0;
goto out;
case TTM_PL_TT:
if (drm->agp.bridge) {
reg->bus.offset = (reg->start << PAGE_SHIFT) +
drm->agp.base;
reg->bus.is_iomem = !drm->agp.cma;
reg->bus.caching = ttm_write_combined;
}
#endif
if (drm->client.mem->oclass < NVIF_CLASS_MEM_NV50 ||
!mem->kind) {
/* untiled */
ret = 0;
fallthrough; /* tiled memory */
case TTM_PL_VRAM:
reg->bus.offset = (reg->start << PAGE_SHIFT) +
device->func->resource_addr(device, 1);
reg->bus.is_iomem = true;
/* Some BARs do not support being ioremapped WC */
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA &&
type & NVIF_MEM_UNCACHED)
reg->bus.caching = ttm_uncached;
else
reg->bus.caching = ttm_write_combined;
if (drm->client.mem->oclass >= NVIF_CLASS_MEM_NV50) {
union {
struct nv50_mem_map_v0 nv50;
struct gf100_mem_map_v0 gf100;
} args;
u64 handle, length;
u32 argc = 0;
switch (mem->mem.object.oclass) {
case NVIF_CLASS_MEM_NV50:
args.nv50.version = 0;
args.nv50.ro = 0;
args.nv50.kind = mem->kind;
args.nv50.comp = mem->comp;

Thierry Reding
committed
argc = sizeof(args.nv50);
break;
case NVIF_CLASS_MEM_GF100:
args.gf100.version = 0;
args.gf100.ro = 0;
args.gf100.kind = mem->kind;

Thierry Reding
committed
argc = sizeof(args.gf100);
break;
default:
WARN_ON(1);
break;
}
ret = nvif_object_map_handle(&mem->mem.object,

Thierry Reding
committed
&args, argc,
&handle, &length);
if (ret != 1) {
if (WARN_ON(ret == 0))
ret = -EINVAL;
goto out;
reg->bus.offset = handle;
ret = 0;
break;
default:
ret = -EINVAL;
out:
if (ret == -ENOSPC) {
struct nouveau_bo *nvbo;
nvbo = list_first_entry_or_null(&drm->ttm.io_reserve_lru,
typeof(*nvbo),
io_reserve_lru);
if (nvbo) {
list_del_init(&nvbo->io_reserve_lru);
drm_vma_node_unmap(&nvbo->bo.base.vma_node,
bdev->dev_mapping);
nouveau_ttm_io_mem_free_locked(drm, &nvbo->bo.mem);
goto retry;
}
}
mutex_unlock(&drm->ttm.io_reserve_mutex);
return ret;
}
static void
nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_resource *reg)
struct nouveau_drm *drm = nouveau_bdev(bdev);
mutex_lock(&drm->ttm.io_reserve_mutex);
nouveau_ttm_io_mem_free_locked(drm, reg);
mutex_unlock(&drm->ttm.io_reserve_mutex);
vm_fault_t nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct nvkm_device *device = nvxx_device(&drm->client.device);
u32 mappable = device->func->resource_size(device, 1) >> PAGE_SHIFT;
/* as long as the bo isn't in vram, and isn't tiled, we've got
* nothing to do here.
*/
if (bo->mem.mem_type != TTM_PL_VRAM) {
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA ||
!nvbo->kind)
if (bo->mem.mem_type != TTM_PL_SYSTEM)
return 0;
nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_GART, 0);
} else {
/* make sure bo is in mappable vram */
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA ||
bo->mem.start + bo->mem.num_pages < mappable)
return 0;
for (i = 0; i < nvbo->placement.num_placement; ++i) {
nvbo->placements[i].fpfn = 0;
nvbo->placements[i].lpfn = mappable;
for (i = 0; i < nvbo->placement.num_busy_placement; ++i) {
nvbo->busy_placements[i].fpfn = 0;
nvbo->busy_placements[i].lpfn = mappable;
}
nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, 0);
ret = nouveau_bo_validate(nvbo, false, false);
if (unlikely(ret == -EBUSY || ret == -ERESTARTSYS))
return VM_FAULT_NOPAGE;
else if (unlikely(ret))
return VM_FAULT_SIGBUS;
ttm_bo_move_to_lru_tail_unlocked(bo);
return 0;
static int
nouveau_ttm_tt_populate(struct ttm_bo_device *bdev,
struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
{
struct ttm_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
struct device *dev;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
return 0;
if (slave && ttm->sg) {
/* make userspace faulting work */
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
ttm_dma->dma_address, ttm->num_pages);
return 0;
}
dev = drm->dev->dev;
if (drm->agp.bridge) {
return ttm_pool_populate(ttm, ctx);
#if IS_ENABLED(CONFIG_SWIOTLB) && IS_ENABLED(CONFIG_X86)
if (swiotlb_nr_tbl()) {
return ttm_dma_populate((void *)ttm, dev, ctx);
}
#endif
return ttm_populate_and_map_pages(dev, ttm_dma, ctx);
}
static void
nouveau_ttm_tt_unpopulate(struct ttm_bo_device *bdev,
struct ttm_tt *ttm)
{
struct ttm_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
struct device *dev;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
if (slave)
return;
dev = drm->dev->dev;
if (drm->agp.bridge) {
ttm_pool_unpopulate(ttm);
#if IS_ENABLED(CONFIG_SWIOTLB) && IS_ENABLED(CONFIG_X86)
if (swiotlb_nr_tbl()) {
ttm_dma_unpopulate((void *)ttm, dev);
return;
}
#endif
ttm_unmap_and_unpopulate_pages(dev, ttm_dma);
}
static void
nouveau_ttm_tt_destroy(struct ttm_bo_device *bdev,
struct ttm_tt *ttm)
{
#if IS_ENABLED(CONFIG_AGP)
struct nouveau_drm *drm = nouveau_bdev(bdev);
if (drm->agp.bridge) {
return;
}
#endif
nouveau_sgdma_destroy(bdev, ttm);
}
void
nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive)
{
struct dma_resv *resv = nvbo->bo.base.resv;

Maarten Lankhorst
committed
dma_resv_add_excl_fence(resv, &fence->base);
else if (fence)
dma_resv_add_shared_fence(resv, &fence->base);
}
static void
nouveau_bo_delete_mem_notify(struct ttm_buffer_object *bo)
{
nouveau_bo_move_ntfy(bo, false, NULL);
}
struct ttm_bo_driver nouveau_bo_driver = {
.ttm_tt_create = &nouveau_ttm_tt_create,
.ttm_tt_populate = &nouveau_ttm_tt_populate,
.ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate,
.ttm_tt_destroy = &nouveau_ttm_tt_destroy,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = nouveau_bo_evict_flags,
.delete_mem_notify = nouveau_bo_delete_mem_notify,
.move = nouveau_bo_move,
.verify_access = nouveau_bo_verify_access,
.io_mem_reserve = &nouveau_ttm_io_mem_reserve,
.io_mem_free = &nouveau_ttm_io_mem_free,