diff --git a/android/Android.mk b/android/Android.mk index a6d3b4ac3f5..ced12e30565 100644 --- a/android/Android.mk +++ b/android/Android.mk @@ -79,11 +79,6 @@ LOCAL_SHARED_LIBRARIES += libdrm_radeon MESON_GEN_PKGCONFIGS += libdrm_radeon:$(LIBDRM_VERSION) endif -ifneq ($(filter nouveau,$(BOARD_MESA3D_GALLIUM_DRIVERS)),) -LOCAL_SHARED_LIBRARIES += libdrm_nouveau -MESON_GEN_PKGCONFIGS += libdrm_nouveau:$(LIBDRM_VERSION) -endif - ifneq ($(filter d3d12,$(BOARD_MESA3D_GALLIUM_DRIVERS)),) LOCAL_HEADER_LIBRARIES += DirectX-Headers LOCAL_STATIC_LIBRARIES += DirectX-Guids diff --git a/meson.build b/meson.build index f7e02315128..d70046ed74c 100644 --- a/meson.build +++ b/meson.build @@ -1633,12 +1633,10 @@ endif # bar are both on use 2.4.3 for both of them dep_libdrm_amdgpu = null_dep dep_libdrm_radeon = null_dep -dep_libdrm_nouveau = null_dep dep_libdrm_intel = null_dep _drm_amdgpu_ver = '2.4.119' _drm_radeon_ver = '2.4.71' -_drm_nouveau_ver = '2.4.102' _drm_intel_ver = '2.4.75' _drm_ver = '2.4.109' @@ -1646,7 +1644,6 @@ _libdrm_checks = [ ['intel', with_gallium_i915], ['amdgpu', (with_amd_vk and not with_platform_windows) or with_gallium_radeonsi], ['radeon', (with_gallium_radeonsi or with_gallium_r300 or with_gallium_r600)], - ['nouveau', with_any_nouveau], ] # Loop over the enables versions and get the highest libdrm requirement for all diff --git a/src/gallium/drivers/nouveau/ci/gitlab-ci-inc.yml b/src/gallium/drivers/nouveau/ci/gitlab-ci-inc.yml index 7600611c9bb..aa60eca0e3a 100644 --- a/src/gallium/drivers/nouveau/ci/gitlab-ci-inc.yml +++ b/src/gallium/drivers/nouveau/ci/gitlab-ci-inc.yml @@ -7,6 +7,7 @@ - changes: &nouveau_file_list - src/nouveau/* - src/nouveau/codegen/**/* + - src/nouveau/drm/**/* - src/nouveau/drm-shim/**/* - src/nouveau/headers/**/* - src/nouveau/winsys/**/* diff --git a/src/gallium/drivers/nouveau/meson.build b/src/gallium/drivers/nouveau/meson.build index d05ff93b2af..ec728b364c4 100644 --- a/src/gallium/drivers/nouveau/meson.build +++ b/src/gallium/drivers/nouveau/meson.build @@ -185,11 +185,12 @@ libnouveau = static_library( inc_src, inc_include, inc_gallium, inc_gallium_aux, inc_mapi, inc_mesa, ], gnu_symbol_visibility : 'hidden', - dependencies : [dep_libdrm, idep_nouveau_codegen, dep_libdrm_nouveau, idep_mesautil, idep_nir_headers], + link_with : [libnouveauwinsys], + dependencies : [dep_libdrm, idep_libnouveauwinsys, idep_nouveau_codegen, idep_mesautil, idep_nir_headers], ) driver_nouveau = declare_dependency( compile_args : '-DGALLIUM_NOUVEAU', dependencies : [idep_nir, idep_nouveau_codegen], - link_with : [libnouveauwinsys, libnouveau_codegen, libnouveau], + link_with : [libnouveau_codegen, libnouveau], ) diff --git a/src/gallium/drivers/nouveau/nouveau_context.h b/src/gallium/drivers/nouveau/nouveau_context.h index b4c88c18e84..7e7851c397f 100644 --- a/src/gallium/drivers/nouveau/nouveau_context.h +++ b/src/gallium/drivers/nouveau/nouveau_context.h @@ -3,7 +3,7 @@ #include "pipe/p_context.h" #include "pipe/p_state.h" -#include +#include "nouveau_winsys.h" #define NOUVEAU_MAX_SCRATCH_BUFS 4 diff --git a/src/gallium/drivers/nouveau/nouveau_screen.c b/src/gallium/drivers/nouveau/nouveau_screen.c index 22924204514..906fed77411 100644 --- a/src/gallium/drivers/nouveau/nouveau_screen.c +++ b/src/gallium/drivers/nouveau/nouveau_screen.c @@ -16,10 +16,10 @@ #include #include -#include +#include "drm-uapi/nouveau_drm.h" #include -#include -#include +#include "nvif/class.h" +#include "nvif/cl0080.h" #include "nouveau_winsys.h" #include "nouveau_screen.h" @@ -261,13 +261,13 @@ nouveau_pushbuf_destroy(struct nouveau_pushbuf **push) } static bool -nouveau_check_for_uma(int chipset, struct nouveau_object *obj) +nouveau_check_for_uma(int chipset, struct nouveau_device *dev) { struct nv_device_info_v0 info = { .version = 0, }; - nouveau_object_mthd(obj, NV_DEVICE_V0_INFO, &info, sizeof(info)); + nouveau_device_info(dev, &info); return (info.platform == NV_DEVICE_INFO_V0_IGP) || (info.platform == NV_DEVICE_INFO_V0_SOC); } @@ -286,6 +286,7 @@ nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev) struct pipe_screen *pscreen = &screen->base; struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 }; struct nvc0_fifo nvc0_data = { }; + struct nve0_fifo nve0_data = { .engine = NOUVEAU_FIFO_ENGINE_GR }; uint64_t time; int size, ret; void *data; @@ -313,9 +314,12 @@ nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev) if (dev->chipset < 0xc0) { data = &nv04_data; size = sizeof(nv04_data); - } else { + } else if (dev->chipset < 0xe0) { data = &nvc0_data; size = sizeof(nvc0_data); + } else { + data = &nve0_data; + size = sizeof(nve0_data); } bool enable_svm = debug_get_bool_option("NOUVEAU_SVM", false); @@ -432,7 +436,7 @@ nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev) PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_STREAM_OUTPUT | PIPE_BIND_COMMAND_ARGS_BUFFER; - screen->is_uma = nouveau_check_for_uma(dev->chipset, &dev->object); + screen->is_uma = nouveau_check_for_uma(dev->chipset, dev); memset(&mm_config, 0, sizeof(mm_config)); nouveau_fence_list_init(&screen->fence); diff --git a/src/gallium/drivers/nouveau/nouveau_vp3_video.c b/src/gallium/drivers/nouveau/nouveau_vp3_video.c index e264f665437..19965c762df 100644 --- a/src/gallium/drivers/nouveau/nouveau_vp3_video.c +++ b/src/gallium/drivers/nouveau/nouveau_vp3_video.c @@ -394,7 +394,7 @@ firmware_present(struct pipe_screen *pscreen, enum pipe_video_profile profile) struct nouveau_object *channel = NULL, *bsp = NULL; struct nv04_fifo nv04_data = {.vram = 0xbeef0201, .gart = 0xbeef0202}; struct nvc0_fifo nvc0_args = {}; - struct nve0_fifo nve0_args = {.engine = NVE0_FIFO_ENGINE_BSP}; + struct nve0_fifo nve0_args = {.engine = NOUVEAU_FIFO_ENGINE_BSP}; void *data = NULL; int size; diff --git a/src/gallium/drivers/nouveau/nouveau_vp3_video.h b/src/gallium/drivers/nouveau/nouveau_vp3_video.h index 96e995f89d5..03d7cc3bb95 100644 --- a/src/gallium/drivers/nouveau/nouveau_vp3_video.h +++ b/src/gallium/drivers/nouveau/nouveau_vp3_video.h @@ -20,7 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include "nouveau_winsys.h" #include "pipe/p_defines.h" #include "vl/vl_video_buffer.h" diff --git a/src/gallium/drivers/nouveau/nouveau_winsys.h b/src/gallium/drivers/nouveau/nouveau_winsys.h index 9049b53812e..434127155f2 100644 --- a/src/gallium/drivers/nouveau/nouveau_winsys.h +++ b/src/gallium/drivers/nouveau/nouveau_winsys.h @@ -8,8 +8,7 @@ #include "util/os_misc.h" #include "drm-uapi/drm.h" -#include - +#include "nouveau.h" #include "nouveau_screen.h" #ifndef NV04_PFIFO_MAX_PACKET_LEN diff --git a/src/gallium/drivers/nouveau/nv30/nv30_screen.c b/src/gallium/drivers/nouveau/nv30/nv30_screen.c index 6c60faea722..0dddc5184c1 100644 --- a/src/gallium/drivers/nouveau/nv30/nv30_screen.c +++ b/src/gallium/drivers/nouveau/nv30/nv30_screen.c @@ -24,7 +24,7 @@ */ #include -#include +#include "drm-uapi/nouveau_drm.h" #include "util/format/u_format.h" #include "util/format/u_format_s3tc.h" #include "util/u_screen.h" @@ -719,7 +719,7 @@ nv30_screen_create(struct nouveau_device *dev) nouveau_heap_init(&screen->vp_data_heap, 6, 468 - 6); } - ret = nouveau_bo_wrap(screen->base.device, fifo->notify, &screen->notify); + ret = nouveau_bo_wrap(screen->base.device, fifo->base.notify, &screen->notify); if (ret == 0) ret = BO_MAP(&screen->base, screen->notify, 0, screen->base.client); if (ret) diff --git a/src/gallium/drivers/nouveau/nv50/nv50_context.c b/src/gallium/drivers/nouveau/nv50/nv50_context.c index f4e3046714b..a41df15715e 100644 --- a/src/gallium/drivers/nouveau/nv50/nv50_context.c +++ b/src/gallium/drivers/nouveau/nv50/nv50_context.c @@ -440,8 +440,8 @@ out_err: void nv50_bufctx_fence(struct nv50_context *nv50, struct nouveau_bufctx *bufctx, bool on_flush) { - struct nouveau_list *list = on_flush ? &bufctx->current : &bufctx->pending; - struct nouveau_list *it; + struct list_head *list = on_flush ? &bufctx->current : &bufctx->pending; + struct list_head *it; for (it = list->next; it != list; it = it->next) { struct nouveau_bufref *ref = (struct nouveau_bufref *)it; diff --git a/src/gallium/drivers/nouveau/nv50/nv50_screen.c b/src/gallium/drivers/nouveau/nv50/nv50_screen.c index 051c31d9785..dc1d36aa565 100644 --- a/src/gallium/drivers/nouveau/nv50/nv50_screen.c +++ b/src/gallium/drivers/nouveau/nv50/nv50_screen.c @@ -22,7 +22,7 @@ #include #include -#include +#include "drm-uapi/nouveau_drm.h" #include "util/format/u_format.h" #include "util/format/u_format_s3tc.h" #include "util/u_screen.h" diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_context.c b/src/gallium/drivers/nouveau/nvc0/nvc0_context.c index 9d107a27586..4900099c7f4 100644 --- a/src/gallium/drivers/nouveau/nvc0/nvc0_context.c +++ b/src/gallium/drivers/nouveau/nvc0/nvc0_context.c @@ -30,7 +30,7 @@ #include "xf86drm.h" -#include "nouveau_drm.h" +#include "drm-uapi/nouveau_drm.h" static void @@ -578,8 +578,8 @@ void nvc0_bufctx_fence(struct nvc0_context *nvc0, struct nouveau_bufctx *bufctx, bool on_flush) { - struct nouveau_list *list = on_flush ? &bufctx->current : &bufctx->pending; - struct nouveau_list *it; + struct list_head *list = on_flush ? &bufctx->current : &bufctx->pending; + struct list_head *it; NOUVEAU_DRV_STAT_IFD(unsigned count = 0); for (it = list->next; it != list; it = it->next) { diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c b/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c index 28a616a621f..68268478f6f 100644 --- a/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c +++ b/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c @@ -21,7 +21,7 @@ */ #include -#include +#include "drm-uapi/nouveau_drm.h" #include #include "util/format/u_format.h" #include "util/format/u_format_s3tc.h" diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_video.c b/src/gallium/drivers/nouveau/nvc0/nvc0_video.c index 1823225f387..f62d4b71022 100644 --- a/src/gallium/drivers/nouveau/nvc0/nvc0_video.c +++ b/src/gallium/drivers/nouveau/nvc0/nvc0_video.c @@ -143,9 +143,9 @@ nvc0_create_decoder(struct pipe_context *context, data = &nvc0_args; } else { unsigned engine[] = { - NVE0_FIFO_ENGINE_BSP, - NVE0_FIFO_ENGINE_VP, - NVE0_FIFO_ENGINE_PPP + NOUVEAU_FIFO_ENGINE_BSP, + NOUVEAU_FIFO_ENGINE_VP, + NOUVEAU_FIFO_ENGINE_PPP }; nve0_args.engine = engine[i]; diff --git a/src/gallium/winsys/nouveau/drm/meson.build b/src/gallium/winsys/nouveau/drm/meson.build index 60671eb37e0..5a7898f54c1 100644 --- a/src/gallium/winsys/nouveau/drm/meson.build +++ b/src/gallium/winsys/nouveau/drm/meson.build @@ -18,12 +18,35 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +files_libnouveauwinsys = files( + 'nouveau.h', + 'nouveau.c', + 'nouveau_drm_public.h', + 'nouveau_drm_winsys.c', +) + libnouveauwinsys = static_library( 'nouveauwinsys', - files('nouveau_drm_public.h', 'nouveau_drm_winsys.c'), + files_libnouveauwinsys, include_directories : [ - inc_src, inc_include, inc_gallium, inc_gallium_aux, inc_gallium_drivers, + inc_src, + inc_include, + inc_nouveau_drm, + inc_gallium, + inc_gallium_aux, + inc_gallium_drivers, + ], + c_args: [ + cc.get_supported_arguments('-Wno-gnu-variable-sized-type-not-at-end') ], gnu_symbol_visibility : 'hidden', - dependencies : [dep_libdrm_nouveau, idep_mesautil], + dependencies : [dep_libdrm, idep_mesautil], +) + +idep_libnouveauwinsys = declare_dependency( + include_directories : [ + include_directories('.'), + inc_nouveau_drm, + ], + link_with : [libnouveauwinsys], ) diff --git a/src/gallium/winsys/nouveau/drm/nouveau.c b/src/gallium/winsys/nouveau/drm/nouveau.c new file mode 100644 index 00000000000..0d50a32ab12 --- /dev/null +++ b/src/gallium/winsys/nouveau/drm/nouveau.c @@ -0,0 +1,1748 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "drm-uapi/nouveau_drm.h" +#include "nouveau.h" +#include "nvif/class.h" +#include "nvif/cl0080.h" + +#include "util/bitscan.h" +#include "util/list.h" +#include "util/os_mman.h" +#include "util/simple_mtx.h" +#include "util/u_atomic.h" +#include "util/u_memory.h" + +static FILE *nouveau_out = NULL; +static uint32_t nouveau_debug = 0; + +#define dbg_on(lvl) (nouveau_debug & (1 << lvl)) +#define dbg(lvl, fmt, args...) do { \ + if (dbg_on((lvl))) \ + fprintf(nouveau_out, "nouveau: "fmt, ##args); \ +} while(0) +#define err(fmt, args...) fprintf(nouveau_out, "nouveau: "fmt, ##args) + +static void +debug_init(void) +{ + static bool once = false; + char *debug, *out; + + if (once) + return; + once = true; + + debug = getenv("NOUVEAU_LIBDRM_DEBUG"); + if (debug) { + int n = strtol(debug, NULL, 0); + if (n >= 0) + nouveau_debug = n; + } + + nouveau_out = stderr; + out = getenv("NOUVEAU_LIBDRM_OUT"); + if (out) { + FILE *fout = fopen(out, "w"); + if (fout) + nouveau_out = fout; + } +} + +int +nouveau_drm_new(int fd, struct nouveau_drm **pdrm) +{ + debug_init(); + + struct nouveau_drm *drm = calloc(1, sizeof(*drm)); + if (!drm) + return -ENOMEM; + drm->fd = fd; + + drmVersionPtr ver = drmGetVersion(fd); + if (!ver) + goto out_err; + *pdrm = drm; + + drm->version = (ver->version_major << 24) | + (ver->version_minor << 8) | + ver->version_patchlevel; + if (drm->version < 0x01000301) + goto out_err; + + drmFreeVersion(ver); + return 0; + +out_err: + nouveau_drm_del(&drm); + return -EINVAL; +} + +void +nouveau_drm_del(struct nouveau_drm **pdrm) +{ + free(*pdrm); + *pdrm = NULL; +} + +static int +nouveau_object_channel_new(struct nouveau_object *parent, uint64_t handle, uint32_t oclass, + struct nouveau_object *obj) +{ + // TODO nv04? + struct nouveau_drm *drm = nouveau_drm(parent); + struct nouveau_device *dev = (void*)obj->parent; + struct nouveau_fifo *fifo = obj->data; + struct drm_nouveau_channel_alloc req = { }; + + /* nvc0 doesn't need any special handling here */ + if (dev->chipset < 0xc0) { + struct nv04_fifo *nv04 = obj->data; + req.fb_ctxdma_handle = nv04->vram; + req.tt_ctxdma_handle = nv04->gart; + } else if (dev->chipset >= 0xe0) { + struct nve0_fifo *nve0 = obj->data; + req.fb_ctxdma_handle = 0xffffffff; + req.tt_ctxdma_handle = nve0->engine; + } + + int ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_CHANNEL_ALLOC, &req, sizeof(req)); + if (ret) + return ret; + + fifo->pushbuf = req.pushbuf_domains; + fifo->notify = req.notifier_handle; + obj->handle = req.channel; + return 0; +} + +static int +nouveau_object_notifier_new(struct nouveau_object *parent, uint64_t handle, + struct nouveau_object *obj) +{ + struct nouveau_drm *drm = nouveau_drm(obj); + struct nv04_notify *ntfy = obj->data; + struct drm_nouveau_notifierobj_alloc req = { + .channel = parent->handle, + .handle = handle, + .size = ntfy->length, + }; + + int ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, &req, sizeof(req)); + if (ret) + return ret; + + ntfy->offset = req.offset; + return 0; +} + +static int +nouveau_object_subchan_new(struct nouveau_object *parent, uint64_t handle, uint32_t oclass, + struct nouveau_object *obj) +{ + struct nouveau_drm *drm = nouveau_drm(parent); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_new_v0 new; + } args = { + .ioctl = { + .route = 0xff, + .token = parent->handle, + .type = NVIF_IOCTL_V0_NEW, + .version = 0, + }, + .new = { + .handle = handle, + .object = (uintptr_t)obj, + .oclass = oclass, + .route = NVIF_IOCTL_V0_ROUTE_NVIF, + .token = (uintptr_t)obj, + .version = 0, + }, + }; + + return drmCommandWrite(drm->fd, DRM_NOUVEAU_NVIF, &args, sizeof(args)); +} + +/* TODO: split this interfaces up so we can verify the right parent object type gets passed in */ +int +nouveau_object_new(struct nouveau_object *parent, uint64_t handle, uint32_t oclass, void *data, + uint32_t length, struct nouveau_object **pobj) +{ + struct nouveau_object *obj = calloc(1, sizeof(*obj)); + if (!obj) + return -ENOMEM; + + obj->parent = parent; + obj->handle = handle; + obj->oclass = oclass; + if (length) { + obj->data = malloc(length); + memcpy(obj->data, data, length); + } + + int ret; + switch (oclass) { + case NOUVEAU_FIFO_CHANNEL_CLASS: + ret = nouveau_object_channel_new(parent, handle, oclass, obj); + break; + case NOUVEAU_NOTIFIER_CLASS: + ret = nouveau_object_notifier_new(parent, handle, obj); + break; + default: + ret = nouveau_object_subchan_new(parent, handle, oclass, obj); + break; + } + + if (ret) { + free(obj->data); + free(obj); + return ret; + } + + *pobj = obj; + return 0; +} + +static void +nouveau_object_channel_del(struct nouveau_object *obj) +{ + struct nouveau_drm *drm = nouveau_drm(obj->parent); + struct drm_nouveau_channel_free req = { + .channel = obj->handle, + }; + + int ret = drmCommandWrite(drm->fd, DRM_NOUVEAU_CHANNEL_FREE, &req, sizeof(req)); + assert(!ret); +} + +static void +nouveau_object_subchan_del(struct nouveau_object *obj) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_del del; + } args = { + .ioctl = { + .object = (uintptr_t)obj, + .owner = NVIF_IOCTL_V0_OWNER_ANY, + .route = 0x00, + .type = NVIF_IOCTL_V0_DEL, + .version = 0, + }, + }; + + drmCommandWrite(obj->parent->handle, DRM_NOUVEAU_NVIF, &args, sizeof(args)); +} + +static void +nouveau_object_gpuobj_del(struct nouveau_object *obj) +{ + struct nouveau_drm *drm = nouveau_drm(obj->parent); + struct drm_nouveau_gpuobj_free req = { + .channel = obj->parent->handle, + .handle = obj->handle, + }; + + drmCommandWrite(drm->fd, DRM_NOUVEAU_GPUOBJ_FREE, &req, sizeof(req)); +} + +void +nouveau_object_del(struct nouveau_object **pobj) +{ + if (!*pobj) + return; + + struct nouveau_object *obj = *pobj; + switch (obj->oclass) { + case NOUVEAU_FIFO_CHANNEL_CLASS: + nouveau_object_channel_del(obj); + break; + case NOUVEAU_NOTIFIER_CLASS: + nouveau_object_gpuobj_del(obj); + break; + default: + nouveau_object_subchan_del(obj); + break; + } + free(obj->data); + free(obj); + *pobj = NULL; +} + +#define NOUVEAU_OBJECT_MAX_CLASSES 16 +int +nouveau_object_mclass(struct nouveau_object *obj, const struct nouveau_mclass *mclass) +{ + struct nouveau_drm *drm = nouveau_drm(obj->parent); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_sclass_v0 sclass; + struct nvif_ioctl_sclass_oclass_v0 list[NOUVEAU_OBJECT_MAX_CLASSES]; + } args = { + .ioctl = { + .route = 0xff, + .token = obj->handle, + .type = NVIF_IOCTL_V0_SCLASS, + .version = 0, + }, + .sclass = { + .count = NOUVEAU_OBJECT_MAX_CLASSES, + .version = 0, + }, + }; + + int ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_NVIF, &args, sizeof(args)); + if (ret) + return ret; + + int i; + for (i = 0; mclass[i].oclass; i++) { + for (int j = 0; j < args.sclass.count; j++) { + if (args.list[j].oclass == mclass[i].oclass) + return i; + } + } + + return -ENODEV; +} + +struct nouveau_device_priv { + struct nouveau_device base; + simple_mtx_t lock; + struct list_head bo_list; + uint32_t *client; + uint32_t nr_client; + int gart_limit_percent; + int vram_limit_percent; +}; + +static inline struct nouveau_device_priv * +nouveau_device(struct nouveau_device *dev) +{ + return (struct nouveau_device_priv *)dev; +} + +int +nouveau_device_info(struct nouveau_device *dev, struct nv_device_info_v0 *info) +{ + struct nouveau_drm *drm = nouveau_drm(dev->object.parent); + 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(drm->fd, DRM_NOUVEAU_NVIF, &args, sizeof(args)); + if (ret) + return ret; + + *info = args.info; + return 0; +} + +int +nouveau_device_new(struct nouveau_object *parent, struct nouveau_device **pdev) +{ + struct nouveau_drm *drm = nouveau_drm(parent); + struct nouveau_device *dev; + uint64_t v; + char *tmp; + + struct nouveau_device_priv *nvdev = calloc(1, sizeof(*nvdev)); + if (!nvdev) + return -ENOMEM; + dev = *pdev = &nvdev->base; + dev->object.parent = parent; + + 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)&nvdev->base.object, + .oclass = NV_DEVICE, + .route = NVIF_IOCTL_V0_ROUTE_NVIF, + .token = (uintptr_t)&nvdev->base.object, + .version = 0, + }, + .dev = { + .device = ~0ULL, + }, + }; + + int ret = drmCommandWrite(drm->fd, DRM_NOUVEAU_NVIF, &args, sizeof(args)); + if (ret) + goto done; + + struct nv_device_info_v0 info; + ret = nouveau_device_info(dev, &info); + if (ret) + goto done; + + nvdev->base.chipset = info.chipset; + + ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_FB_SIZE, &v); + if (ret) + goto done; + nvdev->base.vram_size = v; + + ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_AGP_SIZE, &v); + if (ret) + goto done; + nvdev->base.gart_size = v; + + tmp = getenv("NOUVEAU_LIBDRM_VRAM_LIMIT_PERCENT"); + if (tmp) + nvdev->vram_limit_percent = atoi(tmp); + else + nvdev->vram_limit_percent = 80; + nvdev->base.vram_limit = (nvdev->base.vram_size * nvdev->vram_limit_percent) / 100; + + tmp = getenv("NOUVEAU_LIBDRM_GART_LIMIT_PERCENT"); + if (tmp) + nvdev->gart_limit_percent = atoi(tmp); + else + nvdev->gart_limit_percent = 80; + nvdev->base.gart_limit = (nvdev->base.gart_size * nvdev->gart_limit_percent) / 100; + + simple_mtx_init(&nvdev->lock, mtx_plain); + list_inithead(&nvdev->bo_list); +done: + if (ret) + nouveau_device_del(pdev); + return ret; +} + +void +nouveau_device_del(struct nouveau_device **pdev) +{ + struct nouveau_device_priv *nvdev = nouveau_device(*pdev); + if (!nvdev) + return; + + simple_mtx_destroy(&nvdev->lock); + free(nvdev->client); + free(nvdev); + *pdev = NULL; +} + +int +nouveau_getparam(struct nouveau_device *dev, uint64_t param, uint64_t *value) +{ + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct drm_nouveau_getparam r = { .param = param }; + int ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GETPARAM, &r, sizeof(r)); + *value = r.value; + return ret; +} + +struct nouveau_client_kref { + struct drm_nouveau_gem_pushbuf_bo *kref; + struct nouveau_pushbuf *push; +}; + +struct nouveau_client_priv { + struct nouveau_client base; + struct nouveau_client_kref *kref; + unsigned kref_nr; +}; + +static inline struct nouveau_client_priv * +nouveau_client(struct nouveau_client *client) +{ + return (struct nouveau_client_priv *)client; +} + +int +nouveau_client_new(struct nouveau_device *dev, struct nouveau_client **pclient) +{ + struct nouveau_device_priv *nvdev = nouveau_device(dev); + struct nouveau_client_priv *pcli; + int id = 0; + int ret = -ENOMEM; + uint32_t *clients; + + simple_mtx_lock(&nvdev->lock); + + int i; + for (i = 0; i < nvdev->nr_client; i++) { + id = ffs(nvdev->client[i]) - 1; + if (id >= 0) + goto out; + } + + clients = realloc(nvdev->client, sizeof(uint32_t) * (i + 1)); + if (!clients) + goto unlock; + nvdev->client = clients; + nvdev->client[i] = 0; + nvdev->nr_client++; + +out: + pcli = calloc(1, sizeof(*pcli)); + if (pcli) { + nvdev->client[i] |= (1 << id); + pcli->base.device = dev; + pcli->base.id = (i * 32) + id; + ret = 0; + } + + *pclient = &pcli->base; + +unlock: + simple_mtx_unlock(&nvdev->lock); + return ret; +} + +void +nouveau_client_del(struct nouveau_client **pclient) +{ + struct nouveau_client_priv *pcli = nouveau_client(*pclient); + struct nouveau_device_priv *nvdev; + if (pcli) { + int id = pcli->base.id; + nvdev = nouveau_device(pcli->base.device); + simple_mtx_lock(&nvdev->lock); + nvdev->client[id / 32] &= ~(1 << (id % 32)); + simple_mtx_unlock(&nvdev->lock); + free(pcli->kref); + free(pcli); + } +} + +struct nouveau_bo_priv { + struct nouveau_bo base; + struct list_head head; + uint32_t refcnt; + uint64_t map_handle; + uint32_t name; + uint32_t access; +}; + +static inline struct nouveau_bo_priv * +nouveau_bo(struct nouveau_bo *bo) +{ + return (struct nouveau_bo_priv *)bo; +} + +static void +bo_info(struct nouveau_bo *bo, struct drm_nouveau_gem_info *info) +{ + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + + nvbo->map_handle = info->map_handle; + bo->handle = info->handle; + bo->size = info->size; + bo->offset = info->offset; + + bo->flags = 0; + if (info->domain & NOUVEAU_GEM_DOMAIN_VRAM) + bo->flags |= NOUVEAU_BO_VRAM; + if (info->domain & NOUVEAU_GEM_DOMAIN_GART) + bo->flags |= NOUVEAU_BO_GART; + if (!(info->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG)) + bo->flags |= NOUVEAU_BO_CONTIG; + if (nvbo->map_handle) + bo->flags |= NOUVEAU_BO_MAP; + + if (bo->device->chipset >= 0xc0) { + bo->config.nvc0.memtype = (info->tile_flags & 0xff00) >> 8; + bo->config.nvc0.tile_mode = info->tile_mode; + } else + if (bo->device->chipset >= 0x80 || bo->device->chipset == 0x50) { + bo->config.nv50.memtype = (info->tile_flags & 0x07f00) >> 8 | + (info->tile_flags & 0x30000) >> 9; + bo->config.nv50.tile_mode = info->tile_mode << 4; + } else { + //bo->config.nv04.surf_flags = info->tile_flags & 7; + //bo->config.nv04.surf_pitch = info->tile_mode; + } +} + +static void +nouveau_bo_make_global(struct nouveau_bo_priv *nvbo) +{ + if (!nvbo->head.next) { + struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device); + simple_mtx_lock(&nvdev->lock); + if (!nvbo->head.next) + list_add(&nvbo->head, &nvdev->bo_list); + simple_mtx_unlock(&nvdev->lock); + } +} + +static int +nouveau_bo_wrap_locked(struct nouveau_device *dev, uint32_t handle, + struct nouveau_bo **pbo, int name) +{ + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct nouveau_device_priv *nvdev = nouveau_device(dev); + struct drm_nouveau_gem_info req = { .handle = handle }; + struct nouveau_bo_priv *nvbo; + int ret; + + list_for_each_entry(struct nouveau_bo_priv, nvbo, &nvdev->bo_list, head) { + if (nvbo->base.handle == handle) { + if (p_atomic_inc_return(&nvbo->refcnt) == 1) { + /* + * Uh oh, this bo is dead and someone else + * will free it, but because refcnt is + * now non-zero fortunately they won't + * call the ioctl to close the bo. + * + * Remove this bo from the list so other + * calls to nouveau_bo_wrap_locked will + * see our replacement nvbo. + */ + list_del(&nvbo->head); + if (!name) + name = nvbo->name; + break; + } + + *pbo = &nvbo->base; + return 0; + } + } + + ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_INFO, &req, sizeof(req)); + if (ret) + return ret; + + nvbo = calloc(1, sizeof(*nvbo)); + if (nvbo) { + p_atomic_set(&nvbo->refcnt, 1); + nvbo->base.device = dev; + bo_info(&nvbo->base, &req); + nvbo->name = name; + list_add(&nvbo->head, &nvdev->bo_list); + *pbo = &nvbo->base; + return 0; + } + + return -ENOMEM; +} + +int +nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, uint32_t align, uint64_t size, + union nouveau_bo_config *config, struct nouveau_bo **pbo) +{ + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct drm_nouveau_gem_new req = {}; + struct drm_nouveau_gem_info *info = &req.info; + int ret; + + struct nouveau_bo_priv *nvbo = calloc(1, sizeof(*nvbo)); + if (!nvbo) + return -ENOMEM; + + struct nouveau_bo *bo = &nvbo->base; + p_atomic_set(&nvbo->refcnt, 1); + bo->device = dev; + bo->flags = flags; + bo->size = size; + + if (bo->flags & NOUVEAU_BO_VRAM) + info->domain |= NOUVEAU_GEM_DOMAIN_VRAM; + if (bo->flags & NOUVEAU_BO_GART) + info->domain |= NOUVEAU_GEM_DOMAIN_GART; + if (!info->domain) + info->domain |= NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART; + + if (bo->flags & NOUVEAU_BO_MAP) + info->domain |= NOUVEAU_GEM_DOMAIN_MAPPABLE; + + if (bo->flags & NOUVEAU_BO_COHERENT) + info->domain |= NOUVEAU_GEM_DOMAIN_COHERENT; + + if (!(bo->flags & NOUVEAU_BO_CONTIG)) + info->tile_flags = NOUVEAU_GEM_TILE_NONCONTIG; + + info->size = bo->size; + req.align = align; + + if (config) { + if (dev->chipset >= 0xc0) { + info->tile_flags = (config->nvc0.memtype & 0xff) << 8; + info->tile_mode = config->nvc0.tile_mode; + } else + if (dev->chipset >= 0x80 || dev->chipset == 0x50) { + info->tile_flags = (config->nv50.memtype & 0x07f) << 8 | + (config->nv50.memtype & 0x180) << 9; + info->tile_mode = config->nv50.tile_mode >> 4; + } + } + + ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_NEW, &req, sizeof(req)); + if (ret) { + free(nvbo); + return ret; + } + + bo_info(bo, info); + + *pbo = bo; + return 0; +} + +static void +nouveau_bo_del(struct nouveau_bo *bo) +{ + struct nouveau_drm *drm = nouveau_drm(&bo->device->object); + struct nouveau_device_priv *nvdev = nouveau_device(bo->device); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + + if (nvbo->head.next) { + simple_mtx_lock(&nvdev->lock); + if (p_atomic_read(&nvbo->refcnt) == 0) { + list_del(&nvbo->head); + drmCloseBufferHandle(drm->fd, bo->handle); + } + simple_mtx_unlock(&nvdev->lock); + } else { + drmCloseBufferHandle(drm->fd, bo->handle); + } + if (bo->map) + os_munmap(bo->map, bo->size); + free(nvbo); +} + +int +nouveau_bo_map(struct nouveau_bo *bo, uint32_t access, struct nouveau_client *client) +{ + struct nouveau_drm *drm = nouveau_drm(&bo->device->object); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + if (bo->map == NULL) { + bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, drm->fd, nvbo->map_handle); + if (bo->map == MAP_FAILED) { + bo->map = NULL; + return -errno; + } + } + return nouveau_bo_wait(bo, access, client); +} + +int +nouveau_bo_name_get(struct nouveau_bo *bo, uint32_t *name) +{ + struct drm_gem_flink req = { .handle = bo->handle }; + struct nouveau_drm *drm = nouveau_drm(&bo->device->object); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + + *name = nvbo->name; + if (!*name) { + int ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_FLINK, &req); + + if (ret) { + *name = 0; + return ret; + } + nvbo->name = *name = req.name; + + nouveau_bo_make_global(nvbo); + } + return 0; +} + +int +nouveau_bo_name_ref(struct nouveau_device *dev, uint32_t name, struct nouveau_bo **pbo) +{ + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct nouveau_device_priv *nvdev = nouveau_device(dev); + struct drm_gem_open req = { .name = name }; + int ret; + + simple_mtx_lock(&nvdev->lock); + list_for_each_entry(struct nouveau_bo_priv, nvbo, &nvdev->bo_list, head) { + if (nvbo->name == name) { + ret = nouveau_bo_wrap_locked(dev, nvbo->base.handle, pbo, name); + simple_mtx_unlock(&nvdev->lock); + return ret; + } + } + + ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_OPEN, &req); + if (ret == 0) { + ret = nouveau_bo_wrap_locked(dev, req.handle, pbo, name); + } + + simple_mtx_unlock(&nvdev->lock); + return ret; +} + +int +nouveau_bo_prime_handle_ref(struct nouveau_device *dev, int prime_fd, struct nouveau_bo **bo) +{ + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct nouveau_device_priv *nvdev = nouveau_device(dev); + int ret; + unsigned int handle; + + nouveau_bo_ref(NULL, bo); + + simple_mtx_lock(&nvdev->lock); + ret = drmPrimeFDToHandle(drm->fd, prime_fd, &handle); + if (ret == 0) { + ret = nouveau_bo_wrap_locked(dev, handle, bo, 0); + } + simple_mtx_unlock(&nvdev->lock); + return ret; +} + +void +nouveau_bo_ref(struct nouveau_bo *bo, struct nouveau_bo **pref) +{ + struct nouveau_bo *ref = *pref; + if (bo) { + p_atomic_inc(&nouveau_bo(bo)->refcnt); + } + if (ref) { + if (p_atomic_dec_zero(&nouveau_bo(ref)->refcnt)) + nouveau_bo_del(ref); + } + *pref = bo; +} + +int +nouveau_bo_set_prime(struct nouveau_bo *bo, int *prime_fd) +{ + struct nouveau_drm *drm = nouveau_drm(&bo->device->object); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + int ret; + + ret = drmPrimeHandleToFD(drm->fd, nvbo->base.handle, O_CLOEXEC, prime_fd); + if (ret) + return ret; + + nouveau_bo_make_global(nvbo); + return 0; +} + +static inline struct nouveau_pushbuf * +cli_push_get(struct nouveau_client *client, struct nouveau_bo *bo) +{ + struct nouveau_client_priv *pcli = nouveau_client(client); + struct nouveau_pushbuf *push = NULL; + if (pcli->kref_nr > bo->handle) + push = pcli->kref[bo->handle].push; + return push; +} + +static inline struct drm_nouveau_gem_pushbuf_bo * +cli_kref_get(struct nouveau_client *client, struct nouveau_bo *bo) +{ + struct nouveau_client_priv *pcli = nouveau_client(client); + struct drm_nouveau_gem_pushbuf_bo *kref = NULL; + if (pcli->kref_nr > bo->handle) + kref = pcli->kref[bo->handle].kref; + return kref; +} + +static inline void +cli_kref_set(struct nouveau_client *client, struct nouveau_bo *bo, + struct drm_nouveau_gem_pushbuf_bo *kref, struct nouveau_pushbuf *push) +{ + struct nouveau_client_priv *pcli = nouveau_client(client); + if (pcli->kref_nr <= bo->handle) { + pcli->kref = realloc(pcli->kref, sizeof(*pcli->kref) * bo->handle * 2); + while (pcli->kref_nr < bo->handle * 2) { + pcli->kref[pcli->kref_nr].kref = NULL; + pcli->kref[pcli->kref_nr].push = NULL; + pcli->kref_nr++; + } + } + pcli->kref[bo->handle].kref = kref; + pcli->kref[bo->handle].push = push; +} + +int +nouveau_bo_wait(struct nouveau_bo *bo, uint32_t access, struct nouveau_client *client) +{ + struct nouveau_drm *drm = nouveau_drm(&bo->device->object); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + struct drm_nouveau_gem_cpu_prep req; + struct nouveau_pushbuf *push; + int ret = 0; + + if (!(access & NOUVEAU_BO_RDWR)) + return 0; + + push = cli_push_get(client, bo); + if (push && push->channel) + nouveau_pushbuf_kick(push, push->channel); + + if (!nvbo->head.next && !(nvbo->access & NOUVEAU_BO_WR) && !(access & NOUVEAU_BO_WR)) + return 0; + + req.handle = bo->handle; + req.flags = 0; + if (access & NOUVEAU_BO_WR) + req.flags |= NOUVEAU_GEM_CPU_PREP_WRITE; + if (access & NOUVEAU_BO_NOBLOCK) + req.flags |= NOUVEAU_GEM_CPU_PREP_NOWAIT; + + ret = drmCommandWrite(drm->fd, DRM_NOUVEAU_GEM_CPU_PREP, &req, sizeof(req)); + if (ret == 0) + nvbo->access = 0; + return ret; +} + +int +nouveau_bo_wrap(struct nouveau_device *dev, uint32_t handle, struct nouveau_bo **pbo) +{ + struct nouveau_device_priv *nvdev = nouveau_device(dev); + int ret; + simple_mtx_lock(&nvdev->lock); + ret = nouveau_bo_wrap_locked(dev, handle, pbo, 0); + simple_mtx_unlock(&nvdev->lock); + return ret; +} + +struct nouveau_bufref_priv { + struct nouveau_bufref base; + struct nouveau_bufref_priv *next; + struct nouveau_bufctx *bufctx; +}; + +struct nouveau_bufbin_priv { + struct nouveau_bufref_priv *list; + int relocs; +}; + +struct nouveau_bufctx_priv { + struct nouveau_bufctx base; + struct nouveau_bufref_priv *free; + int nr_bins; + struct nouveau_bufbin_priv bins[]; +}; + +static inline struct nouveau_bufctx_priv * +nouveau_bufctx(struct nouveau_bufctx *bctx) +{ + return (struct nouveau_bufctx_priv *)bctx; +} + +int +nouveau_bufctx_new(struct nouveau_client *client, int bins, struct nouveau_bufctx **pbctx) +{ + struct nouveau_bufctx_priv *priv; + + priv = CALLOC_VARIANT_LENGTH_STRUCT(nouveau_bufctx_priv, sizeof(priv->bins[0]) * bins); + if (priv) { + list_inithead(&priv->base.head); + list_inithead(&priv->base.pending); + list_inithead(&priv->base.current); + priv->base.client = client; + priv->nr_bins = bins; + *pbctx = &priv->base; + return 0; + } + + return -ENOMEM; +} + +void +nouveau_bufctx_del(struct nouveau_bufctx **pbctx) +{ + struct nouveau_bufctx_priv *pctx = nouveau_bufctx(*pbctx); + struct nouveau_bufref_priv *pref; + if (pctx) { + while (pctx->nr_bins--) + nouveau_bufctx_reset(&pctx->base, pctx->nr_bins); + while ((pref = pctx->free)) { + pctx->free = pref->next; + free(pref); + } + free(pctx); + *pbctx = NULL; + } +} + +struct nouveau_bufref * +nouveau_bufctx_mthd(struct nouveau_bufctx *bctx, int bin, uint32_t packet, struct nouveau_bo *bo, + uint64_t data, uint32_t flags, uint32_t vor, uint32_t tor) +{ + struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx); + struct nouveau_bufbin_priv *pbin = &pctx->bins[bin]; + struct nouveau_bufref *bref = nouveau_bufctx_refn(bctx, bin, bo, flags); + if (bref) { + bref->packet = packet; + bref->data = data; + bref->vor = vor; + bref->tor = tor; + pbin->relocs++; + bctx->relocs++; + } + return bref; +} + +struct nouveau_bufref * +nouveau_bufctx_refn(struct nouveau_bufctx *bctx, int bin, + struct nouveau_bo *bo, uint32_t flags) +{ + struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx); + struct nouveau_bufbin_priv *pbin = &pctx->bins[bin]; + struct nouveau_bufref_priv *pref = pctx->free; + + if (!pref) + pref = malloc(sizeof(*pref)); + else + pctx->free = pref->next; + + if (!pref) + return NULL; + + pref->base.bo = bo; + pref->base.flags = flags; + pref->base.packet = 0; + + list_addtail(&pref->base.thead, &bctx->pending); + pref->bufctx = bctx; + pref->next = pbin->list; + pbin->list = pref; + + return &pref->base; +} + +struct nouveau_pushbuf_krec { + struct nouveau_pushbuf_krec *next; + struct drm_nouveau_gem_pushbuf_bo buffer[NOUVEAU_GEM_MAX_BUFFERS]; + struct drm_nouveau_gem_pushbuf_reloc reloc[NOUVEAU_GEM_MAX_RELOCS]; + struct drm_nouveau_gem_pushbuf_push push[NOUVEAU_GEM_MAX_PUSH]; + int nr_buffer; + int nr_reloc; + int nr_push; + uint64_t vram_used; + uint64_t gart_used; +}; + +struct nouveau_pushbuf_priv { + struct nouveau_pushbuf base; + struct nouveau_pushbuf_krec *list; + struct nouveau_pushbuf_krec *krec; + struct list_head bctx_list; + struct nouveau_bo *bo; + uint32_t type; + uint32_t suffix0; + uint32_t suffix1; + uint32_t *ptr; + uint32_t *bgn; + int bo_next; + int bo_nr; + struct nouveau_bo *bos[]; +}; + +static inline struct nouveau_pushbuf_priv * +nouveau_pushbuf(struct nouveau_pushbuf *push) +{ + return (struct nouveau_pushbuf_priv *)push; +} + +static void +pushbuf_dump(struct nouveau_pushbuf_krec *krec, int krec_id, int chid) +{ + struct drm_nouveau_gem_pushbuf_reloc *krel; + struct drm_nouveau_gem_pushbuf_push *kpsh; + struct drm_nouveau_gem_pushbuf_bo *kref; + struct nouveau_bo *bo; + uint32_t *bgn, *end; + int i; + + err("ch%d: krec %d pushes %d bufs %d relocs %d\n", + chid, krec_id, krec->nr_push, krec->nr_buffer, krec->nr_reloc); + + kref = krec->buffer; + for (i = 0; i < krec->nr_buffer; i++, kref++) { + bo = (void *)(uintptr_t)kref->user_priv; + err("ch%d: buf %08x %08x %08x %08x %08x %p 0x%"PRIx64" 0x%"PRIx64"\n", + chid, i, kref->handle, kref->valid_domains, + kref->read_domains, kref->write_domains, bo->map, bo->offset, bo->size); + } + + krel = krec->reloc; + for (i = 0; i < krec->nr_reloc; i++, krel++) { + err("ch%d: rel %08x %08x %08x %08x %08x %08x %08x\n", + chid, krel->reloc_bo_index, krel->reloc_bo_offset, + krel->bo_index, krel->flags, krel->data, + krel->vor, krel->tor); + } + + kpsh = krec->push; + for (i = 0; i < krec->nr_push; i++, kpsh++) { + kref = krec->buffer + kpsh->bo_index; + bo = (void *)(unsigned long)kref->user_priv; + bgn = (uint32_t *)((char *)bo->map + kpsh->offset); + end = bgn + ((kpsh->length & 0x7fffff) /4); + + err("ch%d: psh %s%08x %010llx %010llx\n", chid, + bo->map ? "" : "(unmapped) ", kpsh->bo_index, + (unsigned long long)kpsh->offset, + (unsigned long long)(kpsh->offset + kpsh->length)); + if (!bo->map) + continue; + while (bgn < end) + err("\t0x%08x\n", *bgn++); + } +} + +static int +pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->list; + struct nouveau_device *dev = push->client->device; + struct nouveau_drm *drm = nouveau_drm(&dev->object); + struct drm_nouveau_gem_pushbuf_bo_presumed *info; + struct drm_nouveau_gem_pushbuf_bo *kref; + struct drm_nouveau_gem_pushbuf req; + int channel = chan->handle; + struct nouveau_bo *bo; + int krec_id = 0; + int ret = 0, i; + + if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) + return -EINVAL; + + if (push->kick_notify) + push->kick_notify(push); + + nouveau_pushbuf_data(push, NULL, 0, 0); + + while (krec && krec->nr_push) { + req.channel = channel; + req.nr_buffers = krec->nr_buffer; + req.buffers = (uint64_t)(unsigned long)krec->buffer; + req.nr_relocs = krec->nr_reloc; + req.nr_push = krec->nr_push; + req.relocs = (uint64_t)(unsigned long)krec->reloc; + req.push = (uint64_t)(unsigned long)krec->push; + req.suffix0 = nvpb->suffix0; + req.suffix1 = nvpb->suffix1; + req.vram_available = 0; + if (dbg_on(1)) + req.vram_available |= NOUVEAU_GEM_PUSHBUF_SYNC; + req.gart_available = 0; + + if (dbg_on(0)) + pushbuf_dump(krec, krec_id++, channel); + + ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, &req, sizeof(req)); + nvpb->suffix0 = req.suffix0; + nvpb->suffix1 = req.suffix1; + dev->vram_limit = (req.vram_available * nouveau_device(dev)->vram_limit_percent) / 100; + dev->gart_limit = (req.gart_available * nouveau_device(dev)->gart_limit_percent) / 100; + + if (ret) { + err("kernel rejected pushbuf: %s\n", strerror(-ret)); + pushbuf_dump(krec, krec_id++, channel); + break; + } + + kref = krec->buffer; + for (i = 0; i < krec->nr_buffer; i++, kref++) { + bo = (void *)(unsigned long)kref->user_priv; + + info = &kref->presumed; + if (!info->valid) { + bo->flags &= ~NOUVEAU_BO_APER; + if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM) + bo->flags |= NOUVEAU_BO_VRAM; + else + bo->flags |= NOUVEAU_BO_GART; + bo->offset = info->offset; + } + + if (kref->write_domains) + nouveau_bo(bo)->access |= NOUVEAU_BO_WR; + if (kref->read_domains) + nouveau_bo(bo)->access |= NOUVEAU_BO_RD; + } + + krec = krec->next; + } + + return ret; +} + +static int +pushbuf_flush(struct nouveau_pushbuf *push) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_bo *kref; + int ret = 0, i; + + if (push->channel) { + ret = pushbuf_submit(push, push->channel); + } else { + nouveau_pushbuf_data(push, NULL, 0, 0); + krec->next = malloc(sizeof(*krec)); + nvpb->krec = krec->next; + } + + kref = krec->buffer; + for (i = 0; i < krec->nr_buffer; i++, kref++) { + struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv; + cli_kref_set(push->client, bo, NULL, NULL); + if (push->channel) + nouveau_bo_ref(NULL, &bo); + } + + krec = nvpb->krec; + krec->vram_used = 0; + krec->gart_used = 0; + krec->nr_buffer = 0; + krec->nr_reloc = 0; + krec->nr_push = 0; + + list_for_each_entry_safe(struct nouveau_bufctx, bctx, &nvpb->bctx_list, head) { + list_splice(&bctx->current, &bctx->pending); + list_inithead(&bctx->current); + list_delinit(&bctx->head); + } + + return ret; +} + +static bool +pushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint32_t *domains) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct nouveau_device *dev = push->client->device; + struct nouveau_bo *kbo; + struct drm_nouveau_gem_pushbuf_bo *kref; + int i; + + /* VRAM is the only valid domain. GART and VRAM|GART buffers + * are all accounted to GART, so if this doesn't fit in VRAM + * straight up, a flush is needed. + */ + if (*domains == NOUVEAU_GEM_DOMAIN_VRAM) { + if (krec->vram_used + bo->size > dev->vram_limit) + return false; + krec->vram_used += bo->size; + return true; + } + + /* GART or VRAM|GART buffer. Account both of these buffer types + * to GART only for the moment, which simplifies things. If the + * buffer can fit already, we're done here. + */ + if (krec->gart_used + bo->size <= dev->gart_limit) { + krec->gart_used += bo->size; + return true; + } + + /* Ran out of GART space, if it's a VRAM|GART buffer and it'll + * fit into available VRAM, turn it into a VRAM buffer + */ + if ((*domains & NOUVEAU_GEM_DOMAIN_VRAM) && + krec->vram_used + bo->size <= dev->vram_limit) { + *domains &= NOUVEAU_GEM_DOMAIN_VRAM; + krec->vram_used += bo->size; + return true; + } + + /* Still couldn't fit the buffer in anywhere, so as a last resort; + * scan the buffer list for VRAM|GART buffers and turn them into + * VRAM buffers until we have enough space in GART for this one + */ + kref = krec->buffer; + for (i = 0; i < krec->nr_buffer; i++, kref++) { + if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) + continue; + + kbo = (void *)(unsigned long)kref->user_priv; + if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) || + krec->vram_used + kbo->size > dev->vram_limit) + continue; + + kref->valid_domains &= NOUVEAU_GEM_DOMAIN_VRAM; + krec->gart_used -= kbo->size; + krec->vram_used += kbo->size; + if (krec->gart_used + bo->size <= dev->gart_limit) { + krec->gart_used += bo->size; + return true; + } + } + + /* Couldn't resolve a placement, need to force a flush */ + return false; +} + +static struct drm_nouveau_gem_pushbuf_bo * +pushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint32_t flags) +{ + struct nouveau_device *dev = push->client->device; + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct nouveau_pushbuf *fpush; + struct drm_nouveau_gem_pushbuf_bo *kref; + uint32_t domains, domains_wr, domains_rd; + + domains = 0; + if (flags & NOUVEAU_BO_VRAM) + domains |= NOUVEAU_GEM_DOMAIN_VRAM; + if (flags & NOUVEAU_BO_GART) + domains |= NOUVEAU_GEM_DOMAIN_GART; + domains_wr = domains * !!(flags & NOUVEAU_BO_WR); + domains_rd = domains * !!(flags & NOUVEAU_BO_RD); + + /* if buffer is referenced on another pushbuf that is owned by the + * same client, we need to flush the other pushbuf first to ensure + * the correct ordering of commands + */ + fpush = cli_push_get(push->client, bo); + if (fpush && fpush != push) + pushbuf_flush(fpush); + + kref = cli_kref_get(push->client, bo); + if (kref) { + /* possible conflict in memory types - flush and retry */ + if (!(kref->valid_domains & domains)) + return NULL; + + /* VRAM|GART buffer turning into a VRAM buffer. Make sure + * it'll fit in VRAM and force a flush if not. + */ + if ((kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART) && + ( domains == NOUVEAU_GEM_DOMAIN_VRAM)) { + if (krec->vram_used + bo->size > dev->vram_limit) + return NULL; + krec->vram_used += bo->size; + krec->gart_used -= bo->size; + } + + kref->valid_domains &= domains; + kref->write_domains |= domains_wr; + kref->read_domains |= domains_rd; + } else { + if (krec->nr_buffer == NOUVEAU_GEM_MAX_BUFFERS || + !pushbuf_kref_fits(push, bo, &domains)) + return NULL; + + kref = &krec->buffer[krec->nr_buffer++]; + kref->user_priv = (unsigned long)bo; + kref->handle = bo->handle; + kref->valid_domains = domains; + kref->write_domains = domains_wr; + kref->read_domains = domains_rd; + kref->presumed.valid = 1; + kref->presumed.offset = bo->offset; + if (bo->flags & NOUVEAU_BO_VRAM) + kref->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; + else + kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; + + cli_kref_set(push->client, bo, kref, push); + p_atomic_inc(&nouveau_bo(bo)->refcnt); + } + + return kref; +} + +static uint32_t +pushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo, + uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_reloc *krel; + struct drm_nouveau_gem_pushbuf_bo *pkref; + struct drm_nouveau_gem_pushbuf_bo *bkref; + uint32_t reloc = data; + + pkref = cli_kref_get(push->client, nvpb->bo); + bkref = cli_kref_get(push->client, bo); + krel = &krec->reloc[krec->nr_reloc++]; + + assert(pkref); + assert(bkref); + krel->reloc_bo_index = pkref - krec->buffer; + krel->reloc_bo_offset = (push->cur - nvpb->ptr) * 4; + krel->bo_index = bkref - krec->buffer; + krel->flags = 0; + krel->data = data; + krel->vor = vor; + krel->tor = tor; + + if (flags & NOUVEAU_BO_LOW) { + reloc = (bkref->presumed.offset + data); + krel->flags |= NOUVEAU_GEM_RELOC_LOW; + } + if (flags & NOUVEAU_BO_OR) { + if (bkref->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) + reloc |= vor; + else + reloc |= tor; + krel->flags |= NOUVEAU_GEM_RELOC_OR; + } + + return reloc; +} + +static void +pushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_bo *kref; + + kref = krec->buffer + sref; + while (krec->nr_buffer-- > sref) { + struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv; + cli_kref_set(push->client, bo, NULL, NULL); + nouveau_bo_ref(NULL, &bo); + kref++; + } + krec->nr_buffer = sref; + krec->nr_reloc = srel; +} + +static int +pushbuf_refn(struct nouveau_pushbuf *push, bool retry, + struct nouveau_pushbuf_refn *refs, int nr) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_bo *kref; + int sref = krec->nr_buffer; + int ret = 0, i; + + for (i = 0; i < nr; i++) { + kref = pushbuf_kref(push, refs[i].bo, refs[i].flags); + if (!kref) { + ret = -ENOSPC; + break; + } + } + + if (ret) { + pushbuf_refn_fail(push, sref, krec->nr_reloc); + if (retry) { + pushbuf_flush(push); + nouveau_pushbuf_space(push, 0, 0, 0); + return pushbuf_refn(push, false, refs, nr); + } + } + + return ret; +} + +static int +pushbuf_validate(struct nouveau_pushbuf *push, bool retry) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_bo *kref; + struct nouveau_bufctx *bctx = push->bufctx; + int relocs = bctx ? bctx->relocs * 2: 0; + int sref, srel, ret; + + ret = nouveau_pushbuf_space(push, relocs, relocs, 0); + if (ret || bctx == NULL) + return ret; + + sref = krec->nr_buffer; + srel = krec->nr_reloc; + + list_del(&bctx->head); + list_add(&bctx->head, &nvpb->bctx_list); + + list_for_each_entry(struct nouveau_bufref, bref, &bctx->pending, thead) { + kref = pushbuf_kref(push, bref->bo, bref->flags); + if (!kref) { + ret = -ENOSPC; + break; + } + + if (bref->packet) { + pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0); + *push->cur++ = 0; + pushbuf_krel(push, bref->bo, bref->data, bref->flags, + bref->vor, bref->tor); + *push->cur++ = 0; + } + } + + list_splice(&bctx->pending, &bctx->current); + list_inithead(&bctx->pending); + + if (ret) { + pushbuf_refn_fail(push, sref, srel); + if (retry) { + pushbuf_flush(push); + return pushbuf_validate(push, false); + } + } + + return ret; +} + +int +nouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan, int nr, + uint32_t size, bool immediate, struct nouveau_pushbuf **ppush) +{ + struct nouveau_drm *drm = nouveau_drm(&client->device->object); + struct nouveau_fifo *fifo = chan->data; + struct nouveau_pushbuf_priv *nvpb; + struct nouveau_pushbuf *push; + struct drm_nouveau_gem_pushbuf req = {}; + int ret; + + if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS) + return -EINVAL; + + /* nop pushbuf call, to get the current "return to main" sequence + * we need to append to the pushbuf on early chipsets + */ + req.channel = chan->handle; + req.nr_push = 0; + ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF, &req, sizeof(req)); + if (ret) + return ret; + + nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos)); + if (!nvpb) + return -ENOMEM; + + nvpb->suffix0 = req.suffix0; + nvpb->suffix1 = req.suffix1; + nvpb->krec = calloc(1, sizeof(*nvpb->krec)); + nvpb->list = nvpb->krec; + if (!nvpb->krec) { + free(nvpb); + return -ENOMEM; + } + + push = &nvpb->base; + push->client = client; + push->channel = immediate ? chan : NULL; + push->flags = NOUVEAU_BO_RD; + if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) { + push->flags |= NOUVEAU_BO_GART; + nvpb->type = NOUVEAU_BO_GART; + } else + if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) { + push->flags |= NOUVEAU_BO_VRAM; + nvpb->type = NOUVEAU_BO_VRAM; + } + nvpb->type |= NOUVEAU_BO_MAP; + + for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) { + ret = nouveau_bo_new(client->device, nvpb->type, 0, size, NULL, &nvpb->bos[nvpb->bo_nr]); + if (ret) { + nouveau_pushbuf_del(&push); + return ret; + } + } + + list_inithead(&nvpb->bctx_list); + *ppush = push; + return 0; +} + +void +nouveau_pushbuf_del(struct nouveau_pushbuf **ppush) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush); + if (nvpb) { + struct drm_nouveau_gem_pushbuf_bo *kref; + struct nouveau_pushbuf_krec *krec; + while ((krec = nvpb->list)) { + kref = krec->buffer; + while (krec->nr_buffer--) { + unsigned long priv = kref++->user_priv; + struct nouveau_bo *bo = (void *)priv; + cli_kref_set(nvpb->base.client, bo, NULL, NULL); + nouveau_bo_ref(NULL, &bo); + } + nvpb->list = krec->next; + free(krec); + } + while (nvpb->bo_nr--) + nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]); + nouveau_bo_ref(NULL, &nvpb->bo); + free(nvpb); + } + *ppush = NULL; +} + +void +nouveau_bufctx_reset(struct nouveau_bufctx *bctx, int bin) +{ + struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx); + struct nouveau_bufbin_priv *pbin = &pctx->bins[bin]; + struct nouveau_bufref_priv *pref; + + while ((pref = pbin->list)) { + list_delinit(&pref->base.thead); + pbin->list = pref->next; + pref->next = pctx->free; + pctx->free = pref; + } + + bctx->relocs -= pbin->relocs; + pbin->relocs = 0; +} + +struct nouveau_bufctx * +nouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx) +{ + struct nouveau_bufctx *prev = push->bufctx; + push->bufctx = ctx; + return prev; +} + +void +nouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint64_t offset, + uint64_t length) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct drm_nouveau_gem_pushbuf_push *kpsh; + struct drm_nouveau_gem_pushbuf_bo *kref; + + if (bo != nvpb->bo && nvpb->bgn != push->cur) { + if (nvpb->suffix0 || nvpb->suffix1) { + *push->cur++ = nvpb->suffix0; + *push->cur++ = nvpb->suffix1; + } + + nouveau_pushbuf_data(push, nvpb->bo, (nvpb->bgn - nvpb->ptr) * 4, (push->cur - nvpb->bgn) * 4); + nvpb->bgn = push->cur; + } + + if (bo) { + kref = cli_kref_get(push->client, bo); + assert(kref); + kpsh = &krec->push[krec->nr_push++]; + kpsh->bo_index = kref - krec->buffer; + kpsh->offset = offset; + kpsh->length = length; + } +} + +int +nouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan) +{ + if (!push->channel) + return pushbuf_submit(push, chan); + pushbuf_flush(push); + return pushbuf_validate(push, false); +} + +int +nouveau_pushbuf_refn(struct nouveau_pushbuf *push, struct nouveau_pushbuf_refn *refs, int nr) +{ + return pushbuf_refn(push, true, refs, nr); +} + +void +nouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint32_t data, + uint32_t flags, uint32_t vor, uint32_t tor) +{ + *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor); + push->cur++; +} + +int +nouveau_pushbuf_space(struct nouveau_pushbuf *push, uint32_t dwords, uint32_t relocs, + uint32_t pushes) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push); + struct nouveau_pushbuf_krec *krec = nvpb->krec; + struct nouveau_client *client = push->client; + struct nouveau_bo *bo = NULL; + bool flushed = false; + int ret = 0; + + /* switch to next buffer if insufficient space in the current one */ + if (push->cur + dwords >= push->end) { + if (nvpb->bo_next < nvpb->bo_nr) { + nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo); + if (nvpb->bo_next == nvpb->bo_nr && push->channel) + nvpb->bo_next = 0; + } else { + ret = nouveau_bo_new(client->device, nvpb->type, 0, nvpb->bos[0]->size, NULL, &bo); + if (ret) + return ret; + } + } + + /* make sure there's always enough space to queue up the pending + * data in the pushbuf proper + */ + pushes++; + + /* need to flush if we've run out of space on an immediate pushbuf, + * if the new buffer won't fit, or if the kernel push/reloc limits + * have been hit + */ + if ((bo && (push->channel || !pushbuf_kref(push, bo, push->flags))) || + krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS || + krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) { + if (nvpb->bo && krec->nr_buffer) + pushbuf_flush(push); + flushed = true; + } + + /* if necessary, switch to new buffer */ + if (bo) { + ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client); + if (ret) + return ret; + + nouveau_pushbuf_data(push, NULL, 0, 0); + nouveau_bo_ref(bo, &nvpb->bo); + nouveau_bo_ref(NULL, &bo); + + nvpb->bgn = nvpb->bo->map; + nvpb->ptr = nvpb->bgn; + push->cur = nvpb->bgn; + push->end = push->cur + (nvpb->bo->size / 4); + push->end -= 2 + push->rsvd_kick; /* space for suffix */ + } + + pushbuf_kref(push, nvpb->bo, push->flags); + return flushed ? pushbuf_validate(push, false) : 0; +} + +int +nouveau_pushbuf_validate(struct nouveau_pushbuf *push) +{ + return pushbuf_validate(push, true); +} diff --git a/src/gallium/winsys/nouveau/drm/nouveau.h b/src/gallium/winsys/nouveau/drm/nouveau.h new file mode 100644 index 00000000000..7a7d3d354da --- /dev/null +++ b/src/gallium/winsys/nouveau/drm/nouveau.h @@ -0,0 +1,204 @@ +#ifndef NOUVEAU_H +#define NOUVEAU_H + +#include +#include + +#include "util/list.h" + +#include "drm-uapi/nouveau_drm.h" + +#define NOUVEAU_FIFO_CHANNEL_CLASS 0x80000001 +#define NOUVEAU_NOTIFIER_CLASS 0x80000002 + +#define NOUVEAU_BO_VRAM 0x00000001 +#define NOUVEAU_BO_GART 0x00000002 +#define NOUVEAU_BO_APER (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART) +#define NOUVEAU_BO_RD 0x00000100 +#define NOUVEAU_BO_WR 0x00000200 +#define NOUVEAU_BO_RDWR (NOUVEAU_BO_RD | NOUVEAU_BO_WR) +#define NOUVEAU_BO_NOBLOCK 0x00000400 +#define NOUVEAU_BO_LOW 0x00001000 +#define NOUVEAU_BO_OR 0x00004000 +#define NOUVEAU_BO_COHERENT 0x10000000 +#define NOUVEAU_BO_NOSNOOP 0x20000000 +#define NOUVEAU_BO_CONTIG 0x40000000 +#define NOUVEAU_BO_MAP 0x80000000 + +struct nouveau_mclass { + int32_t oclass; + int version; +}; + +struct nouveau_object { + struct nouveau_object *parent; + uint64_t handle; + uint32_t oclass; + void *data; +}; + +struct nouveau_drm { + struct nouveau_object client; + int fd; + uint32_t version; +}; + +struct nouveau_device { + struct nouveau_object object; + uint32_t chipset; + uint64_t vram_size; + uint64_t gart_size; + uint64_t vram_limit; + uint64_t gart_limit; +}; + +struct nouveau_client { + struct nouveau_device *device; + int id; +}; + +union nouveau_bo_config { + struct { + uint32_t memtype; + uint32_t tile_mode; + } nv50; + struct { + uint32_t memtype; + uint32_t tile_mode; + } nvc0; +}; + +struct nouveau_bo { + struct nouveau_device *device; + uint32_t handle; + uint64_t size; + uint32_t flags; + uint64_t offset; + void *map; + union nouveau_bo_config config; +}; + +struct nouveau_bufref { + struct list_head thead; + struct nouveau_bo *bo; + uint32_t packet; + uint32_t flags; + uint32_t data; + uint32_t vor; + uint32_t tor; + uint32_t priv_data; + void *priv; +}; + +struct nouveau_bufctx { + struct nouveau_client *client; + struct list_head head; + struct list_head pending; + struct list_head current; + int relocs; +}; + +struct nouveau_pushbuf { + struct nouveau_client *client; + struct nouveau_object *channel; + struct nouveau_bufctx *bufctx; + void (*kick_notify)(struct nouveau_pushbuf *); + void *user_priv; + uint32_t rsvd_kick; + uint32_t flags; + uint32_t *cur; + uint32_t *end; +}; + +struct nouveau_pushbuf_refn { + struct nouveau_bo *bo; + uint32_t flags; +}; + +struct nouveau_fifo { + uint32_t pushbuf; + uint32_t notify; +}; + +struct nv04_fifo { + struct nouveau_fifo base; + uint32_t vram; + uint32_t gart; +}; + +struct nv04_notify { + uint32_t offset; + uint32_t length; +}; + +struct nvc0_fifo { + struct nouveau_fifo base; +}; + +struct nve0_fifo { + struct nouveau_fifo base; + uint32_t engine; +}; + +int nouveau_drm_new(int fd, struct nouveau_drm **); +void nouveau_drm_del(struct nouveau_drm **); + +static inline struct nouveau_drm * +nouveau_drm(struct nouveau_object *obj) +{ + while (obj && obj->parent) + obj = obj->parent; + return (struct nouveau_drm *)obj; +} + +struct nv_device_info_v0; + +int nouveau_device_new(struct nouveau_object *parent, struct nouveau_device **); +void nouveau_device_del(struct nouveau_device **); +int nouveau_device_info(struct nouveau_device *, struct nv_device_info_v0 *); +int nouveau_getparam(struct nouveau_device *, uint64_t param, uint64_t *value); + +int nouveau_client_new(struct nouveau_device *, struct nouveau_client **); +void nouveau_client_del(struct nouveau_client **); + +int nouveau_object_new(struct nouveau_object *parent, uint64_t handle, uint32_t oclass, void *data, + uint32_t length, struct nouveau_object **); +void nouveau_object_del(struct nouveau_object **); +int nouveau_object_mclass(struct nouveau_object *, const struct nouveau_mclass *); + +int nouveau_bo_new(struct nouveau_device *, uint32_t flags, uint32_t align, uint64_t size, + union nouveau_bo_config *, struct nouveau_bo **); +int nouveau_bo_map(struct nouveau_bo *, uint32_t access, struct nouveau_client *); +int nouveau_bo_name_get(struct nouveau_bo *, uint32_t *name); +int nouveau_bo_name_ref(struct nouveau_device *, uint32_t name, struct nouveau_bo **); +int nouveau_bo_prime_handle_ref(struct nouveau_device *, int prime_fd, struct nouveau_bo **); +void nouveau_bo_ref(struct nouveau_bo *, struct nouveau_bo **); +int nouveau_bo_set_prime(struct nouveau_bo *, int *prime_fd); +int nouveau_bo_wait(struct nouveau_bo *, uint32_t access, struct nouveau_client *); +int nouveau_bo_wrap(struct nouveau_device *, uint32_t handle, struct nouveau_bo **); + +int nouveau_bufctx_new(struct nouveau_client *, int bins, struct nouveau_bufctx **); +void nouveau_bufctx_del(struct nouveau_bufctx **); +struct nouveau_bufref * +nouveau_bufctx_refn(struct nouveau_bufctx *, int bin, struct nouveau_bo *, uint32_t flags); +struct nouveau_bufref * +nouveau_bufctx_mthd(struct nouveau_bufctx *, int bin, uint32_t packet, struct nouveau_bo *, + uint64_t data, uint32_t flags, uint32_t vor, uint32_t tor); +void nouveau_bufctx_reset(struct nouveau_bufctx *, int bin); + +int nouveau_pushbuf_new(struct nouveau_client *, struct nouveau_object *chan, int nr, uint32_t size, + bool immediate, struct nouveau_pushbuf **); +void nouveau_pushbuf_del(struct nouveau_pushbuf **); +struct nouveau_bufctx * +nouveau_pushbuf_bufctx(struct nouveau_pushbuf *, struct nouveau_bufctx *); +void nouveau_pushbuf_data(struct nouveau_pushbuf *, struct nouveau_bo *, uint64_t offset, + uint64_t length); +int nouveau_pushbuf_kick(struct nouveau_pushbuf *, struct nouveau_object *chan); +int nouveau_pushbuf_refn(struct nouveau_pushbuf *, struct nouveau_pushbuf_refn *, int nr); +void nouveau_pushbuf_reloc(struct nouveau_pushbuf *, struct nouveau_bo *, uint32_t data, + uint32_t flags, uint32_t vor, uint32_t tor); +int nouveau_pushbuf_space(struct nouveau_pushbuf *, uint32_t dwords, uint32_t relocs, + uint32_t pushes); +int nouveau_pushbuf_validate(struct nouveau_pushbuf *); + +#endif diff --git a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c index 584acf75605..cf12df9eacd 100644 --- a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c +++ b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c @@ -17,8 +17,8 @@ #include "nouveau/nouveau_winsys.h" #include "nouveau/nouveau_screen.h" -#include -#include +#include "nvif/class.h" +#include "nvif/cl0080.h" static struct hash_table *fd_tab = NULL; @@ -79,10 +79,7 @@ nouveau_drm_screen_create(int fd) if (ret) goto err; - ret = nouveau_device_new(&drm->client, NV_DEVICE, - &(struct nv_device_v0) { - .device = ~0ULL, - }, sizeof(struct nv_device_v0), &dev); + ret = nouveau_device_new(&drm->client, &dev); if (ret) goto err; diff --git a/src/nouveau/drm/meson.build b/src/nouveau/drm/meson.build new file mode 100644 index 00000000000..cdb7894ba89 --- /dev/null +++ b/src/nouveau/drm/meson.build @@ -0,0 +1 @@ +inc_nouveau_drm = include_directories('.') \ No newline at end of file diff --git a/src/nouveau/drm/nvif/cl0080.h b/src/nouveau/drm/nvif/cl0080.h new file mode 100644 index 00000000000..331620a52af --- /dev/null +++ b/src/nouveau/drm/nvif/cl0080.h @@ -0,0 +1,45 @@ +#ifndef __NVIF_CL0080_H__ +#define __NVIF_CL0080_H__ + +struct nv_device_v0 { + __u8 version; + __u8 pad01[7]; + __u64 device; /* device identifier, ~0 for client default */ +}; + +#define NV_DEVICE_V0_INFO 0x00 +#define NV_DEVICE_V0_TIME 0x01 + +struct nv_device_info_v0 { + __u8 version; +#define NV_DEVICE_INFO_V0_IGP 0x00 +#define NV_DEVICE_INFO_V0_PCI 0x01 +#define NV_DEVICE_INFO_V0_AGP 0x02 +#define NV_DEVICE_INFO_V0_PCIE 0x03 +#define NV_DEVICE_INFO_V0_SOC 0x04 + __u8 platform; + __u16 chipset; /* from NV_PMC_BOOT_0 */ + __u8 revision; /* from NV_PMC_BOOT_0 */ +#define NV_DEVICE_INFO_V0_TNT 0x01 +#define NV_DEVICE_INFO_V0_CELSIUS 0x02 +#define NV_DEVICE_INFO_V0_KELVIN 0x03 +#define NV_DEVICE_INFO_V0_RANKINE 0x04 +#define NV_DEVICE_INFO_V0_CURIE 0x05 +#define NV_DEVICE_INFO_V0_TESLA 0x06 +#define NV_DEVICE_INFO_V0_FERMI 0x07 +#define NV_DEVICE_INFO_V0_KEPLER 0x08 +#define NV_DEVICE_INFO_V0_MAXWELL 0x09 + __u8 family; + __u8 pad06[2]; + __u64 ram_size; + __u64 ram_user; + char chip[16]; + char name[64]; +}; + +struct nv_device_time_v0 { + __u8 version; + __u8 pad01[7]; + __u64 time; +}; +#endif diff --git a/src/nouveau/drm/nvif/class.h b/src/nouveau/drm/nvif/class.h new file mode 100644 index 00000000000..4179cd65ac0 --- /dev/null +++ b/src/nouveau/drm/nvif/class.h @@ -0,0 +1,141 @@ +#ifndef __NVIF_CLASS_H__ +#define __NVIF_CLASS_H__ + +/* these class numbers are made up by us, and not nvidia-assigned */ +#define NVIF_CLASS_CONTROL /* if0001.h */ -1 +#define NVIF_CLASS_PERFMON /* if0002.h */ -2 +#define NVIF_CLASS_PERFDOM /* if0003.h */ -3 +#define NVIF_CLASS_SW_NV04 /* if0004.h */ -4 +#define NVIF_CLASS_SW_NV10 /* if0005.h */ -5 +#define NVIF_CLASS_SW_NV50 /* if0005.h */ -6 +#define NVIF_CLASS_SW_GF100 /* if0005.h */ -7 + +/* the below match nvidia-assigned (either in hw, or sw) class numbers */ +#define NV_DEVICE /* cl0080.h */ 0x00000080 + +#define NV_DMA_FROM_MEMORY /* cl0002.h */ 0x00000002 +#define NV_DMA_TO_MEMORY /* cl0002.h */ 0x00000003 +#define NV_DMA_IN_MEMORY /* cl0002.h */ 0x0000003d + +#define FERMI_TWOD_A 0x0000902d + +#define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x00009039 + +#define KEPLER_INLINE_TO_MEMORY_A 0x0000a040 +#define KEPLER_INLINE_TO_MEMORY_B 0x0000a140 + +#define NV04_DISP /* cl0046.h */ 0x00000046 + +#define NV03_CHANNEL_DMA /* cl506b.h */ 0x0000006b +#define NV10_CHANNEL_DMA /* cl506b.h */ 0x0000006e +#define NV17_CHANNEL_DMA /* cl506b.h */ 0x0000176e +#define NV40_CHANNEL_DMA /* cl506b.h */ 0x0000406e +#define NV50_CHANNEL_DMA /* cl506e.h */ 0x0000506e +#define G82_CHANNEL_DMA /* cl826e.h */ 0x0000826e + +#define NV50_CHANNEL_GPFIFO /* cl506f.h */ 0x0000506f +#define G82_CHANNEL_GPFIFO /* cl826f.h */ 0x0000826f +#define FERMI_CHANNEL_GPFIFO /* cl906f.h */ 0x0000906f +#define KEPLER_CHANNEL_GPFIFO_A /* cla06f.h */ 0x0000a06f +#define MAXWELL_CHANNEL_GPFIFO_A /* cla06f.h */ 0x0000b06f + +#define NV50_DISP /* cl5070.h */ 0x00005070 +#define G82_DISP /* cl5070.h */ 0x00008270 +#define GT200_DISP /* cl5070.h */ 0x00008370 +#define GT214_DISP /* cl5070.h */ 0x00008570 +#define GT206_DISP /* cl5070.h */ 0x00008870 +#define GF110_DISP /* cl5070.h */ 0x00009070 +#define GK104_DISP /* cl5070.h */ 0x00009170 +#define GK110_DISP /* cl5070.h */ 0x00009270 +#define GM107_DISP /* cl5070.h */ 0x00009470 +#define GM204_DISP /* cl5070.h */ 0x00009570 + +#define NV31_MPEG 0x00003174 +#define G82_MPEG 0x00008274 + +#define NV74_VP2 0x00007476 + +#define NV50_DISP_CURSOR /* cl507a.h */ 0x0000507a +#define G82_DISP_CURSOR /* cl507a.h */ 0x0000827a +#define GT214_DISP_CURSOR /* cl507a.h */ 0x0000857a +#define GF110_DISP_CURSOR /* cl507a.h */ 0x0000907a +#define GK104_DISP_CURSOR /* cl507a.h */ 0x0000917a + +#define NV50_DISP_OVERLAY /* cl507b.h */ 0x0000507b +#define G82_DISP_OVERLAY /* cl507b.h */ 0x0000827b +#define GT214_DISP_OVERLAY /* cl507b.h */ 0x0000857b +#define GF110_DISP_OVERLAY /* cl507b.h */ 0x0000907b +#define GK104_DISP_OVERLAY /* cl507b.h */ 0x0000917b + +#define NV50_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000507c +#define G82_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000827c +#define GT200_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000837c +#define GT214_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000857c +#define GF110_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000907c +#define GK104_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000917c +#define GK110_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000927c + +#define NV50_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000507d +#define G82_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000827d +#define GT200_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000837d +#define GT214_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000857d +#define GT206_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000887d +#define GF110_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000907d +#define GK104_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000917d +#define GK110_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000927d +#define GM107_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000947d +#define GM204_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000957d + +#define NV50_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000507e +#define G82_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000827e +#define GT200_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000837e +#define GT214_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000857e +#define GF110_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000907e +#define GK104_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000917e + +#define FERMI_A /* cl9097.h */ 0x00009097 +#define FERMI_B /* cl9097.h */ 0x00009197 +#define FERMI_C /* cl9097.h */ 0x00009297 + +#define KEPLER_A /* cl9097.h */ 0x0000a097 +#define KEPLER_B /* cl9097.h */ 0x0000a197 +#define KEPLER_C /* cl9097.h */ 0x0000a297 + +#define MAXWELL_A /* cl9097.h */ 0x0000b097 +#define MAXWELL_B /* cl9097.h */ 0x0000b197 + +#define NV74_BSP 0x000074b0 + +#define GT212_MSVLD 0x000085b1 +#define IGT21A_MSVLD 0x000086b1 +#define G98_MSVLD 0x000088b1 +#define GF100_MSVLD 0x000090b1 +#define GK104_MSVLD 0x000095b1 + +#define GT212_MSPDEC 0x000085b2 +#define G98_MSPDEC 0x000088b2 +#define GF100_MSPDEC 0x000090b2 +#define GK104_MSPDEC 0x000095b2 + +#define GT212_MSPPP 0x000085b3 +#define G98_MSPPP 0x000088b3 +#define GF100_MSPPP 0x000090b3 + +#define G98_SEC 0x000088b4 + +#define GT212_DMA 0x000085b5 +#define FERMI_DMA 0x000090b5 +#define KEPLER_DMA_COPY_A 0x0000a0b5 +#define MAXWELL_DMA_COPY_A 0x0000b0b5 + +#define FERMI_DECOMPRESS 0x000090b8 + +#define FERMI_COMPUTE_A 0x000090c0 +#define FERMI_COMPUTE_B 0x000091c0 +#define KEPLER_COMPUTE_A 0x0000a0c0 +#define KEPLER_COMPUTE_B 0x0000a1c0 +#define MAXWELL_COMPUTE_A 0x0000b0c0 +#define MAXWELL_COMPUTE_B 0x0000b1c0 + +#define NV74_CIPHER 0x000074c1 +#endif diff --git a/src/nouveau/meson.build b/src/nouveau/meson.build index 1339ffb744c..358c46356af 100644 --- a/src/nouveau/meson.build +++ b/src/nouveau/meson.build @@ -18,6 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +subdir('drm') subdir('headers') if with_nouveau_vk subdir('compiler') diff --git a/src/nouveau/winsys/meson.build b/src/nouveau/winsys/meson.build index 37590386d7e..5fcf28f3ca4 100644 --- a/src/nouveau/winsys/meson.build +++ b/src/nouveau/winsys/meson.build @@ -13,11 +13,11 @@ libnouveau_ws = static_library( include_directories : [ inc_include, inc_src, + inc_nouveau_drm, ], c_args: [ cc.get_supported_arguments('-Wno-gnu-variable-sized-type-not-at-end') ], dependencies : [ dep_libdrm, - dep_libdrm_nouveau, dep_valgrind, idep_nvidia_headers, ], diff --git a/src/nouveau/winsys/nouveau_device.c b/src/nouveau/winsys/nouveau_device.c index 03341fc0d85..c752b954281 100644 --- a/src/nouveau/winsys/nouveau_device.c +++ b/src/nouveau/winsys/nouveau_device.c @@ -12,8 +12,8 @@ #include #include -#include -#include +#include "nvif/cl0080.h" +#include "nvif/class.h" #include #include