blob: fdf6020a35af9d84c59c03fb3787992b76976ea2 [file] [log] [blame]
/*
* 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 <stdlib.h>
#include <inttypes.h>
#include <nouveau_drm.h>
#include <nouveau/nouveau.h>
#include <nouveau/nvif/class.h>
#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();
}