/* * Copyright 2021 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include "igt.h" #include "igt_list.h" #include "igt_nouveau.h" #include "nouveau/nvif/push.h" #include "nouveau/nvhw/class/cla0b5.h" #include "nouveau/priv.h" #define PASCAL_DMA_COPY_A (0x0000C0B5) #define PASCAL_DMA_COPY_B (0x0000C1B5) #define VOLTA_DMA_COPY_A (0x0000C3B5) #define TURING_DMA_COPY_A (0x0000C5B5) #define AMPERE_DMA_COPY_A (0x0000C6B5) struct igt_nouveau_fb_priv { struct igt_nouveau_dev *dev; struct nouveau_bo *bo; }; static struct igt_nouveau_dev *get_nouveau_dev(int drm_fd) { struct igt_nouveau_dev *dev; struct nouveau_drm *drm; static IGT_LIST_HEAD(devices); igt_list_for_each_entry(dev, &devices, node) { if (dev->drm->fd == drm_fd) return dev; } igt_assert(dev = malloc(sizeof(*dev))); memset(dev, 0, sizeof(*dev)); IGT_INIT_LIST_HEAD(&dev->node); do_or_die(nouveau_drm_new(drm_fd, &dev->drm)); drm = dev->drm; igt_skip_on_f(!drm->nvif, "Only the NVIF interface for nouveau is supported\n"); do_or_die(nouveau_device_new(&drm->client, NV_DEVICE, &(struct nv_device_v0) { .device = ~0ULL, }, sizeof(struct nv_device_v0), &dev->dev)); do_or_die(nouveau_client_new(dev->dev, &dev->client)); igt_list_add(&dev->node, &devices); return dev; } uint32_t igt_nouveau_get_chipset(int fd) { return get_nouveau_dev(fd)->dev->chipset; } uint64_t igt_nouveau_get_block_height(uint64_t modifier) { uint8_t gob_height; uint8_t log_block_height_in_gobs = (modifier & 0xF); switch ((modifier >> 20) & 0x3) { case 0: case 2: gob_height = 8; break; case 1: gob_height = 4; break; default: igt_fail_on_f(true, "Unknown GOB height/page kind generation 3 in modifier %lx\n", modifier); break; } return gob_height * (1 << log_block_height_in_gobs); } static void decode_mod(uint16_t chipset, uint64_t modifier, uint32_t *tile_mode, uint32_t *kind) { *tile_mode = modifier & 0xF; *kind = (modifier >> 12) & 0xFF; if (chipset >= 0xc0) *tile_mode <<= 4; } int igt_nouveau_create_bo(int drm_fd, bool sysmem, igt_fb_t *fb) { struct igt_nouveau_dev *dev = get_nouveau_dev(drm_fd); struct nouveau_device *nvdev = dev->dev; union nouveau_bo_config config = { }; struct igt_nouveau_fb_priv *priv; uint32_t flags = sysmem ? NOUVEAU_BO_GART : NOUVEAU_BO_VRAM; if (fb->modifier) decode_mod(nvdev->chipset, fb->modifier, &config.nvc0.tile_mode, &config.nvc0.memtype); igt_assert(priv = malloc(sizeof(*priv))); do_or_die(nouveau_bo_new(nvdev, flags | NOUVEAU_BO_RDWR, nvdev->chipset < 0x140 ? 256 : 64, fb->size, &config, &priv->bo)); priv->dev = dev; fb->driver_priv = priv; if (!sysmem) igt_nouveau_fb_clear(fb); return priv->bo->handle; } void *igt_nouveau_mmap_bo(igt_fb_t *fb, int prot) { struct igt_nouveau_fb_priv *priv = fb->driver_priv; struct igt_nouveau_dev *dev = priv->dev; struct nouveau_client *client = dev->client; do_or_die(nouveau_bo_map(priv->bo, prot, client)); return priv->bo->map; } void igt_nouveau_munmap_bo(igt_fb_t *fb) { struct igt_nouveau_fb_priv *priv = fb->driver_priv; munmap(priv->bo->map, priv->bo->size); priv->bo->map = NULL; } void igt_nouveau_delete_bo(igt_fb_t *fb) { struct igt_nouveau_fb_priv *priv = fb->driver_priv; nouveau_bo_ref(NULL, &priv->bo); free(priv); } bool igt_nouveau_is_tiled(uint64_t modifier) { switch (modifier) { case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 0): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 1): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 2): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 3): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 4): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 5): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 0): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 1): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 2): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 3): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 4): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 5): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 0): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 1): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 2): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 3): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 4): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 5): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 0): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 1): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 2): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 3): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 4): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 5): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 0): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 1): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 2): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 3): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 4): case DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 5): return true; default: return false; } } /* TODO: Implement CE on Fermi */ static void init_ce(struct igt_nouveau_dev *dev) { struct nouveau_device *nv_dev = dev->dev; struct nouveau_client *client = dev->client; struct nouveau_mclass mclass[] = { { AMPERE_DMA_COPY_A, -1, NULL }, { TURING_DMA_COPY_A, -1, NULL }, { VOLTA_DMA_COPY_A, -1, NULL }, { PASCAL_DMA_COPY_B, -1, NULL }, { PASCAL_DMA_COPY_A, -1, NULL }, { MAXWELL_DMA_COPY_A, -1, NULL }, { KEPLER_DMA_COPY_A, -1, NULL }, { 0 } }; int oclass_idx; uint32_t oclass; if (dev->ce) return; do_or_die(nouveau_object_new(&nv_dev->object, 0, NOUVEAU_FIFO_CHANNEL_CLASS, &(struct nve0_fifo) { .engine = NVE0_FIFO_ENGINE_CE0 | NVE0_FIFO_ENGINE_CE1, }, sizeof(struct nve0_fifo), &dev->ce_channel)); oclass_idx = nouveau_object_mclass(dev->ce_channel, mclass); igt_assert_f(oclass_idx >= 0, "No supported dma-copy classes found\n"); oclass = mclass[oclass_idx].oclass; igt_debug("Found dma-copy class %04x\n", oclass); do_or_die(nouveau_pushbuf_new(client, dev->ce_channel, 4, 32 * 1024, 1, &dev->pushbuf)); do_or_die(nouveau_object_new(dev->ce_channel, oclass, oclass, NULL, 0, &dev->ce)); } void igt_nouveau_fb_clear(struct igt_fb *fb) { struct igt_nouveau_fb_priv *priv = fb->driver_priv; struct igt_nouveau_dev *dev = priv->dev; init_ce(dev); igt_set_timeout(30, "Timed out while clearing bo with dma-copy"); for (unsigned int plane = 0; plane < fb->num_planes; plane++) igt_nouveau_ce_zfilla0b5(dev, fb, priv->bo, plane); do_or_die(nouveau_bo_wait(priv->bo, NOUVEAU_BO_RD, dev->client)); igt_reset_timeout(); } void igt_nouveau_fb_blit(struct igt_fb *dst, struct igt_fb *src) { struct igt_nouveau_fb_priv *dst_priv = dst->driver_priv, *src_priv = src->driver_priv; struct igt_nouveau_dev *dev = dst_priv->dev; struct nouveau_bo *dst_nvbo = dst_priv->bo, *src_nvbo = src_priv->bo; init_ce(dev); igt_set_timeout(30, "Timed out while blitting bo with dma-copy"); for (unsigned int plane = 0; plane < src->num_planes; plane++) igt_nouveau_ce_copya0b5(dev, dst, dst_nvbo, src, src_nvbo, plane); do_or_die(nouveau_bo_wait(dst_priv->bo, NOUVEAU_BO_RD, dev->client)); igt_reset_timeout(); }