mesa/src/nouveau/winsys/nouveau_device.c

468 lines
12 KiB
C

#include "nouveau_device.h"
#include "nouveau_context.h"
#include "nvidia/g_nv_name_released.h"
#include "drm-uapi/nouveau_drm.h"
#include "util/hash_table.h"
#include "util/u_debug.h"
#include "util/os_file.h"
#include "util/os_misc.h"
#include <fcntl.h>
#include "nvif/cl0080.h"
#include "nvif/class.h"
#include "nvif/ioctl.h"
#include <unistd.h>
#include <xf86drm.h>
static const char *
name_for_chip(uint32_t dev_id,
uint16_t subsystem_id,
uint16_t subsystem_vendor_id)
{
const char *name = NULL;
for (uint32_t i = 0; i < ARRAY_SIZE(sChipsReleased); i++) {
const CHIPS_RELEASED *chip = &sChipsReleased[i];
if (dev_id != chip->devID)
continue;
if (chip->subSystemID == 0 && chip->subSystemVendorID == 0) {
/* When subSystemID and subSystemVendorID are both 0, this is the
* default name for the given chip. A more specific name may exist
* elsewhere in the list.
*/
assert(name == NULL);
name = chip->name;
continue;
}
/* If we find a specific name, return it */
if (chip->subSystemID == subsystem_id &&
chip->subSystemVendorID == subsystem_vendor_id)
return chip->name;
}
return name;
}
static uint8_t
sm_for_chipset(uint16_t chipset)
{
if (chipset >= 0x190)
return 89;
// GH100 is older than AD10X, but is SM90
else if (chipset >= 0x180)
return 90;
else if (chipset == 0x17b)
return 87;
else if (chipset >= 0x172)
return 86;
else if (chipset >= 0x170)
return 80;
else if (chipset >= 0x160)
return 75;
else if (chipset >= 0x14b)
return 72;
else if (chipset >= 0x140)
return 70;
else if (chipset >= 0x13b)
return 62;
else if (chipset >= 0x132)
return 61;
else if (chipset >= 0x130)
return 60;
else if (chipset >= 0x12b)
return 53;
else if (chipset >= 0x120)
return 52;
else if (chipset >= 0x110)
return 50;
// TODO: 37
else if (chipset >= 0x0f0)
return 35;
else if (chipset >= 0x0ea)
return 32;
else if (chipset >= 0x0e0)
return 30;
// GF110 is SM20
else if (chipset == 0x0c8)
return 20;
else if (chipset >= 0x0c1)
return 21;
else if (chipset >= 0x0c0)
return 20;
else if (chipset >= 0x0a3)
return 12;
// GT200 is SM13
else if (chipset >= 0x0a0)
return 13;
else if (chipset >= 0x080)
return 11;
// this has to be == because 0x63 is older than 0x50 and has no compute
else if (chipset == 0x050)
return 10;
// no compute
return 0x00;
}
static uint8_t
max_warps_per_mp_for_sm(uint8_t sm)
{
switch (sm) {
case 10:
case 11:
return 24;
case 12:
case 13:
case 75:
return 32;
case 20:
case 21:
case 86:
case 87:
case 89:
return 48;
case 30:
case 32:
case 35:
case 37:
case 50:
case 52:
case 53:
case 60:
case 61:
case 62:
case 70:
case 72:
case 80:
case 90:
return 64;
default:
assert(!"unkown SM version");
// return the biggest known value
return 64;
}
}
static uint8_t
mp_per_tpc_for_chipset(uint16_t chipset)
{
// GP100 is special and has two, otherwise it's a Volta and newer thing to have two
if (chipset == 0x130 || chipset >= 0x140)
return 2;
return 1;
}
static void
nouveau_ws_device_set_dbg_flags(struct nouveau_ws_device *dev)
{
const struct debug_control flags[] = {
{ "push_dump", NVK_DEBUG_PUSH_DUMP },
{ "push", NVK_DEBUG_PUSH_DUMP },
{ "push_sync", NVK_DEBUG_PUSH_SYNC },
{ "zero_memory", NVK_DEBUG_ZERO_MEMORY },
{ "vm", NVK_DEBUG_VM },
{ "no_cbuf", NVK_DEBUG_NO_CBUF },
{ NULL, 0 },
};
dev->debug_flags = parse_debug_string(getenv("NVK_DEBUG"), flags);
}
static int
nouveau_ws_param(int fd, uint64_t param, uint64_t *value)
{
struct drm_nouveau_getparam data = { .param = param };
int ret = drmCommandWriteRead(fd, DRM_NOUVEAU_GETPARAM, &data, sizeof(data));
if (ret)
return ret;
*value = data.value;
return 0;
}
static int
nouveau_ws_device_alloc(int fd, struct nouveau_ws_device *dev)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_new_v0 new;
struct nv_device_v0 dev;
} args = {
.ioctl = {
.object = 0,
.owner = NVIF_IOCTL_V0_OWNER_ANY,
.route = 0x00,
.type = NVIF_IOCTL_V0_NEW,
.version = 0,
},
.new = {
.handle = 0,
.object = (uintptr_t)dev,
.oclass = NV_DEVICE,
.route = NVIF_IOCTL_V0_ROUTE_NVIF,
.token = (uintptr_t)dev,
.version = 0,
},
.dev = {
.device = ~0ULL,
},
};
return drmCommandWrite(fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
}
static int
nouveau_ws_device_info(int fd, struct nouveau_ws_device *dev)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_mthd_v0 mthd;
struct nv_device_info_v0 info;
} args = {
.ioctl = {
.object = (uintptr_t)dev,
.owner = NVIF_IOCTL_V0_OWNER_ANY,
.route = 0x00,
.type = NVIF_IOCTL_V0_MTHD,
.version = 0,
},
.mthd = {
.method = NV_DEVICE_V0_INFO,
.version = 0,
},
.info = {
.version = 0,
},
};
int ret = drmCommandWriteRead(fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
if (ret)
return ret;
dev->info.chipset = args.info.chipset;
dev->info.vram_size_B = args.info.ram_user;
switch (args.info.platform) {
case NV_DEVICE_INFO_V0_IGP:
dev->info.type = NV_DEVICE_TYPE_IGP;
break;
case NV_DEVICE_INFO_V0_SOC:
dev->info.type = NV_DEVICE_TYPE_SOC;
break;
case NV_DEVICE_INFO_V0_PCI:
case NV_DEVICE_INFO_V0_AGP:
case NV_DEVICE_INFO_V0_PCIE:
default:
dev->info.type = NV_DEVICE_TYPE_DIS;
break;
}
STATIC_ASSERT(sizeof(dev->info.device_name) >= sizeof(args.info.name));
memcpy(dev->info.device_name, args.info.name, sizeof(args.info.name));
STATIC_ASSERT(sizeof(dev->info.chipset_name) >= sizeof(args.info.chip));
memcpy(dev->info.chipset_name, args.info.chip, sizeof(args.info.chip));
return 0;
}
struct nouveau_ws_device *
nouveau_ws_device_new(drmDevicePtr drm_device)
{
const char *path = drm_device->nodes[DRM_NODE_RENDER];
struct nouveau_ws_device *device = CALLOC_STRUCT(nouveau_ws_device);
uint64_t value = 0;
drmVersionPtr ver = NULL;
int fd = open(path, O_RDWR | O_CLOEXEC);
if (fd < 0)
goto out_open;
ver = drmGetVersion(fd);
if (!ver)
goto out_err;
if (strncmp("nouveau", ver->name, ver->name_len) != 0) {
fprintf(stderr,
"DRM kernel driver '%.*s' in use. NVK requires nouveau.\n",
ver->name_len, ver->name);
goto out_err;
}
uint32_t version =
ver->version_major << 24 |
ver->version_minor << 8 |
ver->version_patchlevel;
drmFreeVersion(ver);
ver = NULL;
if (version < 0x01000301)
goto out_err;
const uint64_t BDA = 1ull << 38;
const uint64_t KERN = 1ull << 39;
const uint64_t TOP = 1ull << 40;
struct drm_nouveau_vm_init vminit = { KERN, TOP-KERN };
int ret = drmCommandWrite(fd, DRM_NOUVEAU_VM_INIT, &vminit, sizeof(vminit));
if (ret == 0) {
device->has_vm_bind = true;
util_vma_heap_init(&device->vma_heap, 4096, BDA - 4096);
util_vma_heap_init(&device->bda_heap, BDA, KERN - BDA);
simple_mtx_init(&device->vma_mutex, mtx_plain);
}
if (nouveau_ws_device_alloc(fd, device))
goto out_err;
if (nouveau_ws_param(fd, NOUVEAU_GETPARAM_PCI_DEVICE, &value))
goto out_err;
device->info.device_id = value;
if (nouveau_ws_device_info(fd, device))
goto out_err;
const char *name;
if (drm_device->bustype == DRM_BUS_PCI) {
assert(device->info.type == NV_DEVICE_TYPE_DIS);
assert(device->info.device_id == drm_device->deviceinfo.pci->device_id);
device->info.pci.domain = drm_device->businfo.pci->domain;
device->info.pci.bus = drm_device->businfo.pci->bus;
device->info.pci.dev = drm_device->businfo.pci->dev;
device->info.pci.func = drm_device->businfo.pci->func;
device->info.pci.revision_id = drm_device->deviceinfo.pci->revision_id;
name = name_for_chip(drm_device->deviceinfo.pci->device_id,
drm_device->deviceinfo.pci->subdevice_id,
drm_device->deviceinfo.pci->subvendor_id);
} else {
name = name_for_chip(device->info.device_id, 0, 0);
}
if (name != NULL) {
size_t end = sizeof(device->info.device_name) - 1;
strncpy(device->info.device_name, name, end);
device->info.device_name[end] = 0;
}
device->fd = fd;
if (nouveau_ws_param(fd, NOUVEAU_GETPARAM_EXEC_PUSH_MAX, &value))
device->max_push = NOUVEAU_GEM_MAX_PUSH;
else
device->max_push = value;
if (device->info.vram_size_B == 0)
device->local_mem_domain = NOUVEAU_GEM_DOMAIN_GART;
else
device->local_mem_domain = NOUVEAU_GEM_DOMAIN_VRAM;
if (drm_device->bustype == DRM_BUS_PCI &&
!nouveau_ws_param(fd, NOUVEAU_GETPARAM_VRAM_BAR_SIZE, &value))
device->info.bar_size_B = value;
if (nouveau_ws_param(fd, NOUVEAU_GETPARAM_GRAPH_UNITS, &value))
goto out_err;
device->info.gpc_count = (value >> 0) & 0x000000ff;
device->info.tpc_count = (value >> 8) & 0x0000ffff;
nouveau_ws_device_set_dbg_flags(device);
struct nouveau_ws_context *tmp_ctx;
if (nouveau_ws_context_create(device, ~0, &tmp_ctx))
goto out_err;
device->info.sm = sm_for_chipset(device->info.chipset);
device->info.cls_copy = tmp_ctx->copy.cls;
device->info.cls_eng2d = tmp_ctx->eng2d.cls;
device->info.cls_eng3d = tmp_ctx->eng3d.cls;
device->info.cls_m2mf = tmp_ctx->m2mf.cls;
device->info.cls_compute = tmp_ctx->compute.cls;
// for now we hardcode those values, but in the future Nouveau could provide that information to
// us instead.
device->info.max_warps_per_mp = max_warps_per_mp_for_sm(device->info.sm);
device->info.mp_per_tpc = mp_per_tpc_for_chipset(device->info.chipset);
nouveau_ws_context_destroy(tmp_ctx);
simple_mtx_init(&device->bos_lock, mtx_plain);
device->bos = _mesa_pointer_hash_table_create(NULL);
return device;
out_err:
if (device->has_vm_bind) {
util_vma_heap_finish(&device->vma_heap);
util_vma_heap_finish(&device->bda_heap);
simple_mtx_destroy(&device->vma_mutex);
}
if (ver)
drmFreeVersion(ver);
out_open:
FREE(device);
close(fd);
return NULL;
}
void
nouveau_ws_device_destroy(struct nouveau_ws_device *device)
{
if (!device)
return;
_mesa_hash_table_destroy(device->bos, NULL);
simple_mtx_destroy(&device->bos_lock);
if (device->has_vm_bind) {
util_vma_heap_finish(&device->vma_heap);
util_vma_heap_finish(&device->bda_heap);
simple_mtx_destroy(&device->vma_mutex);
}
close(device->fd);
FREE(device);
}
uint64_t
nouveau_ws_device_vram_used(struct nouveau_ws_device *device)
{
uint64_t used = 0;
if (nouveau_ws_param(device->fd, NOUVEAU_GETPARAM_VRAM_USED, &used))
return 0;
/* Zero memory used would be very strange given that it includes kernel
* internal allocations.
*/
assert(used > 0);
return used;
}
uint64_t
nouveau_ws_device_timestamp(struct nouveau_ws_device *device)
{
uint64_t timestamp = 0;
if (nouveau_ws_param(device->fd, NOUVEAU_GETPARAM_PTIMER_TIME, &timestamp))
return 0;
return timestamp;
}
bool
nouveau_ws_device_has_tiled_bo(struct nouveau_ws_device *device)
{
uint64_t has = 0;
if (nouveau_ws_param(device->fd, NOUVEAU_GETPARAM_HAS_VMA_TILEMODE, &has))
return false;
return has != 0;
}