mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-17 10:52:26 +07:00
Merge branch 'linux-5.1' of git://github.com/skeggsb/linux into drm-next
Various fixes/cleanups, along with initial support for SVM features utilising HMM address-space mirroring and device memory migration. There's a lot more work to do in these areas, both in terms of features and efficiency, but these can slowly trickle in later down the track. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Ben Skeggs <skeggsb@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/CACAvsv5bsB4rRY1Gqa_Bp_KAd-v_q1rGZ4nYmOAQhceL0Nr-Xg@mail.gmail.com
This commit is contained in:
commit
71f4e45a4e
@ -30,6 +30,8 @@ nouveau-y += nouveau_vga.o
|
||||
# DRM - memory management
|
||||
nouveau-y += nouveau_bo.o
|
||||
nouveau-y += nouveau_gem.o
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_SVM) += nouveau_svm.o
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_SVM) += nouveau_dmem.o
|
||||
nouveau-y += nouveau_mem.o
|
||||
nouveau-y += nouveau_prime.o
|
||||
nouveau-y += nouveau_sgdma.o
|
||||
|
@ -71,3 +71,15 @@ config DRM_NOUVEAU_BACKLIGHT
|
||||
help
|
||||
Say Y here if you want to control the backlight of your display
|
||||
(e.g. a laptop panel).
|
||||
|
||||
config DRM_NOUVEAU_SVM
|
||||
bool "(EXPERIMENTAL) Enable SVM (Shared Virtual Memory) support"
|
||||
depends on ARCH_HAS_HMM
|
||||
depends on DRM_NOUVEAU
|
||||
depends on STAGING
|
||||
select HMM_MIRROR
|
||||
select DEVICE_PRIVATE
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to enable experimental support for
|
||||
Shared Virtual Memory (SVM).
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "nvreg.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "disp.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
#include <subdev/bios/pll.h>
|
||||
#include <subdev/clk.h>
|
||||
@ -1077,12 +1078,223 @@ nouveau_crtc_set_config(struct drm_mode_set *set,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct nv04_page_flip_state {
|
||||
struct list_head head;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_crtc *crtc;
|
||||
int bpp, pitch;
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
static int
|
||||
nv04_finish_page_flip(struct nouveau_channel *chan,
|
||||
struct nv04_page_flip_state *ps)
|
||||
{
|
||||
struct nouveau_fence_chan *fctx = chan->fence;
|
||||
struct nouveau_drm *drm = chan->drm;
|
||||
struct drm_device *dev = drm->dev;
|
||||
struct nv04_page_flip_state *s;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (list_empty(&fctx->flip)) {
|
||||
NV_ERROR(drm, "unexpected pageflip\n");
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s = list_first_entry(&fctx->flip, struct nv04_page_flip_state, head);
|
||||
if (s->event) {
|
||||
drm_crtc_arm_vblank_event(s->crtc, s->event);
|
||||
} else {
|
||||
/* Give up ownership of vblank for page-flipped crtc */
|
||||
drm_crtc_vblank_put(s->crtc);
|
||||
}
|
||||
|
||||
list_del(&s->head);
|
||||
if (ps)
|
||||
*ps = *s;
|
||||
kfree(s);
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv04_flip_complete(struct nvif_notify *notify)
|
||||
{
|
||||
struct nouveau_cli *cli = (void *)notify->object->client;
|
||||
struct nouveau_drm *drm = cli->drm;
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nv04_page_flip_state state;
|
||||
|
||||
if (!nv04_finish_page_flip(chan, &state)) {
|
||||
nv_set_crtc_base(drm->dev, drm_crtc_index(state.crtc),
|
||||
state.offset + state.crtc->y *
|
||||
state.pitch + state.crtc->x *
|
||||
state.bpp / 8);
|
||||
}
|
||||
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
static int
|
||||
nv04_page_flip_emit(struct nouveau_channel *chan,
|
||||
struct nouveau_bo *old_bo,
|
||||
struct nouveau_bo *new_bo,
|
||||
struct nv04_page_flip_state *s,
|
||||
struct nouveau_fence **pfence)
|
||||
{
|
||||
struct nouveau_fence_chan *fctx = chan->fence;
|
||||
struct nouveau_drm *drm = chan->drm;
|
||||
struct drm_device *dev = drm->dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Queue it to the pending list */
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_add_tail(&s->head, &fctx->flip);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
/* Synchronize with the old framebuffer */
|
||||
ret = nouveau_fence_sync(old_bo, chan, false, false);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Emit the pageflip */
|
||||
ret = RING_SPACE(chan, 2);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
|
||||
OUT_RING (chan, 0x00000000);
|
||||
FIRE_RING (chan);
|
||||
|
||||
ret = nouveau_fence_new(chan, false, pfence);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_del(&s->head);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event, u32 flags,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->primary->fb)->nvbo;
|
||||
struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
|
||||
struct nv04_page_flip_state *s;
|
||||
struct nouveau_channel *chan;
|
||||
struct nouveau_cli *cli;
|
||||
struct nouveau_fence *fence;
|
||||
struct nv04_display *dispnv04 = nv04_display(dev);
|
||||
int head = nouveau_crtc(crtc)->index;
|
||||
int ret;
|
||||
|
||||
chan = drm->channel;
|
||||
if (!chan)
|
||||
return -ENODEV;
|
||||
cli = (void *)chan->user.client;
|
||||
|
||||
s = kzalloc(sizeof(*s), GFP_KERNEL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (new_bo != old_bo) {
|
||||
ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM, true);
|
||||
if (ret)
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
mutex_lock(&cli->mutex);
|
||||
ret = ttm_bo_reserve(&new_bo->bo, true, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
|
||||
/* synchronise rendering channel with the kernel's channel */
|
||||
ret = nouveau_fence_sync(new_bo, chan, false, true);
|
||||
if (ret) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
if (new_bo != old_bo) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
|
||||
ret = ttm_bo_reserve(&old_bo->bo, true, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
/* Initialize a page flip struct */
|
||||
*s = (struct nv04_page_flip_state)
|
||||
{ { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0],
|
||||
new_bo->bo.offset };
|
||||
|
||||
/* Keep vblanks on during flip, for the target crtc of this flip */
|
||||
drm_crtc_vblank_get(crtc);
|
||||
|
||||
/* Emit a page flip */
|
||||
if (swap_interval) {
|
||||
ret = RING_SPACE(chan, 8);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x012c, 1);
|
||||
OUT_RING (chan, 0);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0134, 1);
|
||||
OUT_RING (chan, head);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0100, 1);
|
||||
OUT_RING (chan, 0);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0130, 1);
|
||||
OUT_RING (chan, 0);
|
||||
}
|
||||
|
||||
nouveau_bo_ref(new_bo, &dispnv04->image[head]);
|
||||
|
||||
ret = nv04_page_flip_emit(chan, old_bo, new_bo, s, &fence);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
mutex_unlock(&cli->mutex);
|
||||
|
||||
/* Update the crtc struct and cleanup */
|
||||
crtc->primary->fb = fb;
|
||||
|
||||
nouveau_bo_fence(old_bo, fence, false);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(old_bo);
|
||||
nouveau_fence_unref(&fence);
|
||||
return 0;
|
||||
|
||||
fail_unreserve:
|
||||
drm_crtc_vblank_put(crtc);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
fail_unpin:
|
||||
mutex_unlock(&cli->mutex);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(new_bo);
|
||||
fail_free:
|
||||
kfree(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs nv04_crtc_funcs = {
|
||||
.cursor_set = nv04_crtc_cursor_set,
|
||||
.cursor_move = nv04_crtc_cursor_move,
|
||||
.gamma_set = nv_crtc_gamma_set,
|
||||
.set_config = nouveau_crtc_set_config,
|
||||
.page_flip = nouveau_crtc_page_flip,
|
||||
.page_flip = nv04_crtc_page_flip,
|
||||
.destroy = nv_crtc_destroy,
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,160 @@
|
||||
#include "hw.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_bo.h"
|
||||
|
||||
#include <nvif/if0004.h>
|
||||
|
||||
static void
|
||||
nv04_display_fini(struct drm_device *dev, bool suspend)
|
||||
{
|
||||
struct nv04_display *disp = nv04_display(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/* Disable flip completion events. */
|
||||
nvif_notify_put(&disp->flip);
|
||||
|
||||
/* Disable vblank interrupts. */
|
||||
NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0);
|
||||
if (nv_two_heads(dev))
|
||||
NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0);
|
||||
|
||||
if (!suspend)
|
||||
return;
|
||||
|
||||
/* Un-pin FB and cursors so they'll be evicted to system memory. */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
|
||||
nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
|
||||
if (!nouveau_fb || !nouveau_fb->nvbo)
|
||||
continue;
|
||||
|
||||
nouveau_bo_unpin(nouveau_fb->nvbo);
|
||||
}
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
if (nv_crtc->cursor.nvbo) {
|
||||
if (nv_crtc->cursor.set_offset)
|
||||
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
|
||||
nouveau_bo_unpin(nv_crtc->cursor.nvbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nv04_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
{
|
||||
struct nv04_display *disp = nv04_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_encoder *encoder;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
/* meh.. modeset apparently doesn't setup all the regs and depends
|
||||
* on pre-existing state, for now load the state of the card *before*
|
||||
* nouveau was loaded, and then do a modeset.
|
||||
*
|
||||
* best thing to do probably is to make save/restore routines not
|
||||
* save/restore "pre-load" state, but more general so we can save
|
||||
* on suspend too.
|
||||
*/
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
nv_crtc->save(&nv_crtc->base);
|
||||
}
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
|
||||
encoder->enc_save(&encoder->base.base);
|
||||
|
||||
/* Enable flip completion events. */
|
||||
nvif_notify_get(&disp->flip);
|
||||
|
||||
if (!resume)
|
||||
return 0;
|
||||
|
||||
/* Re-pin FB/cursors. */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
|
||||
nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
|
||||
if (!nouveau_fb || !nouveau_fb->nvbo)
|
||||
continue;
|
||||
|
||||
ret = nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM, true);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "Could not pin framebuffer\n");
|
||||
}
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
if (!nv_crtc->cursor.nvbo)
|
||||
continue;
|
||||
|
||||
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, true);
|
||||
if (!ret && nv_crtc->cursor.set_offset)
|
||||
ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "Could not pin/map cursor.\n");
|
||||
}
|
||||
|
||||
/* Force CLUT to get re-loaded during modeset. */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
nv_crtc->lut.depth = 0;
|
||||
}
|
||||
|
||||
/* This should ensure we don't hit a locking problem when someone
|
||||
* wakes us up via a connector. We should never go into suspend
|
||||
* while the display is on anyways.
|
||||
*/
|
||||
if (runtime)
|
||||
return 0;
|
||||
|
||||
/* Restore mode. */
|
||||
drm_helper_resume_force_mode(dev);
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
if (!nv_crtc->cursor.nvbo)
|
||||
continue;
|
||||
|
||||
if (nv_crtc->cursor.set_offset)
|
||||
nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
|
||||
nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
|
||||
nv_crtc->cursor_saved_y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_display_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nv04_display *disp = nv04_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_encoder *encoder;
|
||||
struct nouveau_crtc *nv_crtc;
|
||||
|
||||
/* Restore state */
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
|
||||
encoder->enc_restore(&encoder->base.base);
|
||||
|
||||
list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head)
|
||||
nv_crtc->restore(&nv_crtc->base);
|
||||
|
||||
nouveau_hw_save_vga_fonts(dev, 0);
|
||||
|
||||
nvif_notify_fini(&disp->flip);
|
||||
|
||||
nouveau_display(dev)->priv = NULL;
|
||||
kfree(disp);
|
||||
|
||||
nvif_object_unmap(&drm->client.device.object);
|
||||
}
|
||||
|
||||
int
|
||||
nv04_display_create(struct drm_device *dev)
|
||||
@ -58,6 +212,13 @@ nv04_display_create(struct drm_device *dev)
|
||||
/* Pre-nv50 doesn't support atomic, so don't expose the ioctls */
|
||||
dev->driver->driver_features &= ~DRIVER_ATOMIC;
|
||||
|
||||
/* Request page flip completion event. */
|
||||
if (drm->nvsw.client) {
|
||||
nvif_notify_init(&drm->nvsw, nv04_flip_complete,
|
||||
false, NV04_NVSW_NTFY_UEVENT,
|
||||
NULL, 0, 0, &disp->flip);
|
||||
}
|
||||
|
||||
nouveau_hw_save_vga_fonts(dev, 1);
|
||||
|
||||
nv04_crtc_create(dev, 0);
|
||||
@ -121,58 +282,3 @@ nv04_display_create(struct drm_device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nv04_display_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nv04_display *disp = nv04_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_encoder *encoder;
|
||||
struct nouveau_crtc *nv_crtc;
|
||||
|
||||
/* Restore state */
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
|
||||
encoder->enc_restore(&encoder->base.base);
|
||||
|
||||
list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head)
|
||||
nv_crtc->restore(&nv_crtc->base);
|
||||
|
||||
nouveau_hw_save_vga_fonts(dev, 0);
|
||||
|
||||
nouveau_display(dev)->priv = NULL;
|
||||
kfree(disp);
|
||||
|
||||
nvif_object_unmap(&drm->client.device.object);
|
||||
}
|
||||
|
||||
int
|
||||
nv04_display_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_encoder *encoder;
|
||||
struct nouveau_crtc *crtc;
|
||||
|
||||
/* meh.. modeset apparently doesn't setup all the regs and depends
|
||||
* on pre-existing state, for now load the state of the card *before*
|
||||
* nouveau was loaded, and then do a modeset.
|
||||
*
|
||||
* best thing to do probably is to make save/restore routines not
|
||||
* save/restore "pre-load" state, but more general so we can save
|
||||
* on suspend too.
|
||||
*/
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
|
||||
crtc->save(&crtc->base);
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
|
||||
encoder->enc_save(&encoder->base.base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nv04_display_fini(struct drm_device *dev)
|
||||
{
|
||||
/* disable vblank interrupts */
|
||||
NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0);
|
||||
if (nv_two_heads(dev))
|
||||
NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0);
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ struct nv04_display {
|
||||
uint32_t saved_vga_font[4][16384];
|
||||
uint32_t dac_users[4];
|
||||
struct nouveau_bo *image[2];
|
||||
struct nvif_notify flip;
|
||||
};
|
||||
|
||||
static inline struct nv04_display *
|
||||
@ -92,9 +93,6 @@ nv04_display(struct drm_device *dev)
|
||||
|
||||
/* nv04_display.c */
|
||||
int nv04_display_create(struct drm_device *);
|
||||
void nv04_display_destroy(struct drm_device *);
|
||||
int nv04_display_init(struct drm_device *);
|
||||
void nv04_display_fini(struct drm_device *);
|
||||
|
||||
/* nv04_crtc.c */
|
||||
int nv04_crtc_create(struct drm_device *, int index);
|
||||
@ -176,4 +174,5 @@ nouveau_bios_run_init_table(struct drm_device *dev, u16 table,
|
||||
);
|
||||
}
|
||||
|
||||
int nv04_flip_complete(struct nvif_notify *);
|
||||
#endif
|
||||
|
@ -42,7 +42,7 @@ nv50_core_new(struct nouveau_drm *drm, struct nv50_core **pcore)
|
||||
int version;
|
||||
int (*new)(struct nouveau_drm *, s32, struct nv50_core **);
|
||||
} cores[] = {
|
||||
{ TU104_DISP_CORE_CHANNEL_DMA, 0, corec57d_new },
|
||||
{ TU102_DISP_CORE_CHANNEL_DMA, 0, corec57d_new },
|
||||
{ GV100_DISP_CORE_CHANNEL_DMA, 0, corec37d_new },
|
||||
{ GP102_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
|
||||
{ GP100_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
|
||||
|
@ -31,7 +31,7 @@ nv50_curs_new(struct nouveau_drm *drm, int head, struct nv50_wndw **pwndw)
|
||||
int version;
|
||||
int (*new)(struct nouveau_drm *, int, s32, struct nv50_wndw **);
|
||||
} curses[] = {
|
||||
{ TU104_DISP_CURSOR, 0, cursc37a_new },
|
||||
{ TU102_DISP_CURSOR, 0, cursc37a_new },
|
||||
{ GV100_DISP_CURSOR, 0, cursc37a_new },
|
||||
{ GK104_DISP_CURSOR, 0, curs907a_new },
|
||||
{ GF110_DISP_CURSOR, 0, curs907a_new },
|
||||
|
@ -817,7 +817,8 @@ nv50_msto_enable(struct drm_encoder *encoder)
|
||||
|
||||
r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, armh->dp.pbn,
|
||||
armh->dp.tu);
|
||||
WARN_ON(!r);
|
||||
if (!r)
|
||||
DRM_DEBUG_KMS("Failed to allocate VCPI\n");
|
||||
|
||||
if (!mstm->links++)
|
||||
nv50_outp_acquire(mstm->outp);
|
||||
@ -2220,8 +2221,8 @@ nv50_disp_func = {
|
||||
* Init
|
||||
*****************************************************************************/
|
||||
|
||||
void
|
||||
nv50_display_fini(struct drm_device *dev)
|
||||
static void
|
||||
nv50_display_fini(struct drm_device *dev, bool suspend)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct drm_encoder *encoder;
|
||||
@ -2242,8 +2243,8 @@ nv50_display_fini(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_init(struct drm_device *dev)
|
||||
static int
|
||||
nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
{
|
||||
struct nv50_core *core = nv50_disp(dev)->core;
|
||||
struct drm_encoder *encoder;
|
||||
@ -2269,7 +2270,7 @@ nv50_display_init(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
nv50_display_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_disp *disp = nv50_disp(dev);
|
||||
|
@ -31,7 +31,7 @@ nv50_wimm_init(struct nouveau_drm *drm, struct nv50_wndw *wndw)
|
||||
int version;
|
||||
int (*init)(struct nouveau_drm *, s32, struct nv50_wndw *);
|
||||
} wimms[] = {
|
||||
{ TU104_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
|
||||
{ TU102_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
|
||||
{ GV100_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
|
||||
{}
|
||||
};
|
||||
|
@ -626,7 +626,7 @@ nv50_wndw_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
|
||||
int (*new)(struct nouveau_drm *, enum drm_plane_type,
|
||||
int, s32, struct nv50_wndw **);
|
||||
} wndws[] = {
|
||||
{ TU104_DISP_WINDOW_CHANNEL_DMA, 0, wndwc57e_new },
|
||||
{ TU102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc57e_new },
|
||||
{ GV100_DISP_WINDOW_CHANNEL_DMA, 0, wndwc37e_new },
|
||||
{}
|
||||
};
|
||||
|
@ -54,6 +54,9 @@
|
||||
|
||||
#define VOLTA_USERMODE_A 0x0000c361
|
||||
|
||||
#define MAXWELL_FAULT_BUFFER_A /* clb069.h */ 0x0000b069
|
||||
#define VOLTA_FAULT_BUFFER_A /* clb069.h */ 0x0000c369
|
||||
|
||||
#define NV03_CHANNEL_DMA /* cl506b.h */ 0x0000006b
|
||||
#define NV10_CHANNEL_DMA /* cl506b.h */ 0x0000006e
|
||||
#define NV17_CHANNEL_DMA /* cl506b.h */ 0x0000176e
|
||||
@ -84,7 +87,7 @@
|
||||
#define GP100_DISP /* cl5070.h */ 0x00009770
|
||||
#define GP102_DISP /* cl5070.h */ 0x00009870
|
||||
#define GV100_DISP /* cl5070.h */ 0x0000c370
|
||||
#define TU104_DISP /* cl5070.h */ 0x0000c570
|
||||
#define TU102_DISP /* cl5070.h */ 0x0000c570
|
||||
|
||||
#define NV31_MPEG 0x00003174
|
||||
#define G82_MPEG 0x00008274
|
||||
@ -97,7 +100,7 @@
|
||||
#define GF110_DISP_CURSOR /* cl507a.h */ 0x0000907a
|
||||
#define GK104_DISP_CURSOR /* cl507a.h */ 0x0000917a
|
||||
#define GV100_DISP_CURSOR /* cl507a.h */ 0x0000c37a
|
||||
#define TU104_DISP_CURSOR /* cl507a.h */ 0x0000c57a
|
||||
#define TU102_DISP_CURSOR /* cl507a.h */ 0x0000c57a
|
||||
|
||||
#define NV50_DISP_OVERLAY /* cl507b.h */ 0x0000507b
|
||||
#define G82_DISP_OVERLAY /* cl507b.h */ 0x0000827b
|
||||
@ -106,7 +109,7 @@
|
||||
#define GK104_DISP_OVERLAY /* cl507b.h */ 0x0000917b
|
||||
|
||||
#define GV100_DISP_WINDOW_IMM_CHANNEL_DMA /* clc37b.h */ 0x0000c37b
|
||||
#define TU104_DISP_WINDOW_IMM_CHANNEL_DMA /* clc37b.h */ 0x0000c57b
|
||||
#define TU102_DISP_WINDOW_IMM_CHANNEL_DMA /* clc37b.h */ 0x0000c57b
|
||||
|
||||
#define NV50_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000507c
|
||||
#define G82_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000827c
|
||||
@ -129,7 +132,7 @@
|
||||
#define GP100_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000977d
|
||||
#define GP102_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000987d
|
||||
#define GV100_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000c37d
|
||||
#define TU104_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000c57d
|
||||
#define TU102_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000c57d
|
||||
|
||||
#define NV50_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000507e
|
||||
#define G82_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000827e
|
||||
@ -139,7 +142,7 @@
|
||||
#define GK104_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000917e
|
||||
|
||||
#define GV100_DISP_WINDOW_CHANNEL_DMA /* clc37e.h */ 0x0000c37e
|
||||
#define TU104_DISP_WINDOW_CHANNEL_DMA /* clc37e.h */ 0x0000c57e
|
||||
#define TU102_DISP_WINDOW_CHANNEL_DMA /* clc37e.h */ 0x0000c57e
|
||||
|
||||
#define NV50_TESLA 0x00005097
|
||||
#define G82_TESLA 0x00008297
|
||||
|
12
drivers/gpu/drm/nouveau/include/nvif/clb069.h
Normal file
12
drivers/gpu/drm/nouveau/include/nvif/clb069.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __NVIF_CLB069_H__
|
||||
#define __NVIF_CLB069_H__
|
||||
struct nvif_clb069_v0 {
|
||||
__u8 version;
|
||||
__u8 pad01[3];
|
||||
__u32 entries;
|
||||
__u32 get;
|
||||
__u32 put;
|
||||
};
|
||||
|
||||
#define NVB069_V0_NTFY_FAULT 0x00
|
||||
#endif
|
@ -3,7 +3,8 @@
|
||||
struct nvif_vmm_v0 {
|
||||
__u8 version;
|
||||
__u8 page_nr;
|
||||
__u8 pad02[6];
|
||||
__u8 managed;
|
||||
__u8 pad03[5];
|
||||
__u64 addr;
|
||||
__u64 size;
|
||||
__u8 data[];
|
||||
@ -14,6 +15,9 @@ struct nvif_vmm_v0 {
|
||||
#define NVIF_VMM_V0_PUT 0x02
|
||||
#define NVIF_VMM_V0_MAP 0x03
|
||||
#define NVIF_VMM_V0_UNMAP 0x04
|
||||
#define NVIF_VMM_V0_PFNMAP 0x05
|
||||
#define NVIF_VMM_V0_PFNCLR 0x06
|
||||
#define NVIF_VMM_V0_MTHD(i) ((i) + 0x80)
|
||||
|
||||
struct nvif_vmm_page_v0 {
|
||||
__u8 version;
|
||||
@ -61,4 +65,28 @@ struct nvif_vmm_unmap_v0 {
|
||||
__u8 pad01[7];
|
||||
__u64 addr;
|
||||
};
|
||||
|
||||
struct nvif_vmm_pfnmap_v0 {
|
||||
__u8 version;
|
||||
__u8 page;
|
||||
__u8 pad02[6];
|
||||
__u64 addr;
|
||||
__u64 size;
|
||||
#define NVIF_VMM_PFNMAP_V0_ADDR 0xfffffffffffff000ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_ADDR_SHIFT 12
|
||||
#define NVIF_VMM_PFNMAP_V0_APER 0x00000000000000f0ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_HOST 0x0000000000000000ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_VRAM 0x0000000000000010ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_W 0x0000000000000002ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_V 0x0000000000000001ULL
|
||||
#define NVIF_VMM_PFNMAP_V0_NONE 0x0000000000000000ULL
|
||||
__u64 phys[];
|
||||
};
|
||||
|
||||
struct nvif_vmm_pfnclr_v0 {
|
||||
__u8 version;
|
||||
__u8 pad01[7];
|
||||
__u64 addr;
|
||||
__u64 size;
|
||||
};
|
||||
#endif
|
||||
|
@ -6,6 +6,12 @@ struct gp100_vmm_vn {
|
||||
/* nvif_vmm_vX ... */
|
||||
};
|
||||
|
||||
struct gp100_vmm_v0 {
|
||||
/* nvif_vmm_vX ... */
|
||||
__u8 version;
|
||||
__u8 fault_replay;
|
||||
};
|
||||
|
||||
struct gp100_vmm_map_vn {
|
||||
/* nvif_vmm_map_vX ... */
|
||||
};
|
||||
@ -18,4 +24,19 @@ struct gp100_vmm_map_v0 {
|
||||
__u8 priv;
|
||||
__u8 kind;
|
||||
};
|
||||
|
||||
#define GP100_VMM_VN_FAULT_REPLAY NVIF_VMM_V0_MTHD(0x00)
|
||||
#define GP100_VMM_VN_FAULT_CANCEL NVIF_VMM_V0_MTHD(0x01)
|
||||
|
||||
struct gp100_vmm_fault_replay_vn {
|
||||
};
|
||||
|
||||
struct gp100_vmm_fault_cancel_v0 {
|
||||
__u8 version;
|
||||
__u8 hub;
|
||||
__u8 gpc;
|
||||
__u8 client;
|
||||
__u8 pad04[4];
|
||||
__u64 inst;
|
||||
};
|
||||
#endif
|
||||
|
@ -30,8 +30,8 @@ struct nvif_vmm {
|
||||
int page_nr;
|
||||
};
|
||||
|
||||
int nvif_vmm_init(struct nvif_mmu *, s32 oclass, u64 addr, u64 size,
|
||||
void *argv, u32 argc, struct nvif_vmm *);
|
||||
int nvif_vmm_init(struct nvif_mmu *, s32 oclass, bool managed, u64 addr,
|
||||
u64 size, void *argv, u32 argc, struct nvif_vmm *);
|
||||
void nvif_vmm_fini(struct nvif_vmm *);
|
||||
int nvif_vmm_get(struct nvif_vmm *, enum nvif_vmm_get, bool sparse,
|
||||
u8 page, u8 align, u64 size, struct nvif_vma *);
|
||||
|
@ -28,6 +28,7 @@ enum nvkm_devidx {
|
||||
NVKM_SUBDEV_ICCSENSE,
|
||||
NVKM_SUBDEV_THERM,
|
||||
NVKM_SUBDEV_CLK,
|
||||
NVKM_SUBDEV_GSP,
|
||||
NVKM_SUBDEV_SECBOOT,
|
||||
|
||||
NVKM_ENGINE_BSP,
|
||||
@ -137,6 +138,7 @@ struct nvkm_device {
|
||||
struct nvkm_fb *fb;
|
||||
struct nvkm_fuse *fuse;
|
||||
struct nvkm_gpio *gpio;
|
||||
struct nvkm_gsp *gsp;
|
||||
struct nvkm_i2c *i2c;
|
||||
struct nvkm_subdev *ibus;
|
||||
struct nvkm_iccsense *iccsense;
|
||||
@ -209,6 +211,7 @@ struct nvkm_device_chip {
|
||||
int (*fb )(struct nvkm_device *, int idx, struct nvkm_fb **);
|
||||
int (*fuse )(struct nvkm_device *, int idx, struct nvkm_fuse **);
|
||||
int (*gpio )(struct nvkm_device *, int idx, struct nvkm_gpio **);
|
||||
int (*gsp )(struct nvkm_device *, int idx, struct nvkm_gsp **);
|
||||
int (*i2c )(struct nvkm_device *, int idx, struct nvkm_i2c **);
|
||||
int (*ibus )(struct nvkm_device *, int idx, struct nvkm_subdev **);
|
||||
int (*iccsense)(struct nvkm_device *, int idx, struct nvkm_iccsense **);
|
||||
|
@ -11,5 +11,5 @@ int gm200_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
int gp100_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
int gp102_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
int gv100_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
int tu104_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
int tu102_ce_new(struct nvkm_device *, int, struct nvkm_engine **);
|
||||
#endif
|
||||
|
@ -36,5 +36,5 @@ int gm200_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gp100_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gp102_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gv100_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int tu104_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int tu102_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
#endif
|
||||
|
@ -74,5 +74,5 @@ int gm20b_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
int gp100_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
int gp10b_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
int gv100_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
int tu104_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
int tu102_fifo_new(struct nvkm_device *, int, struct nvkm_fifo **);
|
||||
#endif
|
||||
|
@ -10,6 +10,9 @@ struct nvkm_gr {
|
||||
|
||||
u64 nvkm_gr_units(struct nvkm_gr *);
|
||||
int nvkm_gr_tlb_flush(struct nvkm_gr *);
|
||||
int nvkm_gr_ctxsw_pause(struct nvkm_device *);
|
||||
int nvkm_gr_ctxsw_resume(struct nvkm_device *);
|
||||
u32 nvkm_gr_ctxsw_inst(struct nvkm_device *);
|
||||
|
||||
int nv04_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
|
||||
int nv10_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
struct nvkm_nvdec {
|
||||
struct nvkm_engine engine;
|
||||
u32 addr;
|
||||
|
||||
struct nvkm_falcon *falcon;
|
||||
};
|
||||
|
||||
|
@ -5,10 +5,13 @@
|
||||
|
||||
struct nvkm_sec2 {
|
||||
struct nvkm_engine engine;
|
||||
u32 addr;
|
||||
|
||||
struct nvkm_falcon *falcon;
|
||||
struct nvkm_msgqueue *queue;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
int gp102_sec2_new(struct nvkm_device *, int, struct nvkm_sec2 **);
|
||||
int tu102_sec2_new(struct nvkm_device *, int, struct nvkm_sec2 **);
|
||||
#endif
|
||||
|
@ -29,5 +29,5 @@ int gf100_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
int gk20a_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
int gm107_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
int gm20b_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
int tu104_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
int tu102_bar_new(struct nvkm_device *, int, struct nvkm_bar **);
|
||||
#endif
|
||||
|
@ -31,5 +31,5 @@ int gf100_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
int gm107_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
int gm200_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
int gv100_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
int tu104_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
int tu102_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
|
||||
#endif
|
||||
|
@ -13,6 +13,8 @@ struct nvkm_fault {
|
||||
struct nvkm_event event;
|
||||
|
||||
struct nvkm_notify nrpfb;
|
||||
|
||||
struct nvkm_device_oclass user;
|
||||
};
|
||||
|
||||
struct nvkm_fault_data {
|
||||
@ -30,5 +32,5 @@ struct nvkm_fault_data {
|
||||
|
||||
int gp100_fault_new(struct nvkm_device *, int, struct nvkm_fault **);
|
||||
int gv100_fault_new(struct nvkm_device *, int, struct nvkm_fault **);
|
||||
int tu104_fault_new(struct nvkm_device *, int, struct nvkm_fault **);
|
||||
int tu102_fault_new(struct nvkm_device *, int, struct nvkm_fault **);
|
||||
#endif
|
||||
|
14
drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h
Normal file
14
drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __NVKM_GSP_H__
|
||||
#define __NVKM_GSP_H__
|
||||
#define nvkm_gsp(p) container_of((p), struct nvkm_gsp, subdev)
|
||||
#include <core/subdev.h>
|
||||
|
||||
struct nvkm_gsp {
|
||||
struct nvkm_subdev subdev;
|
||||
u32 addr;
|
||||
|
||||
struct nvkm_falcon *falcon;
|
||||
};
|
||||
|
||||
int gv100_gsp_new(struct nvkm_device *, int, struct nvkm_gsp **);
|
||||
#endif
|
@ -31,5 +31,5 @@ int gk104_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
int gk20a_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
int gp100_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
int gp10b_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
int tu104_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
int tu102_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@ struct nvkm_vma {
|
||||
bool part:1; /* Region was split from an allocated region by map(). */
|
||||
bool user:1; /* Region user-allocated. */
|
||||
bool busy:1; /* Region busy (for temporarily preventing user access). */
|
||||
bool mapped:1; /* Region contains valid pages. */
|
||||
struct nvkm_memory *memory; /* Memory currently mapped into VMA. */
|
||||
struct nvkm_tags *tags; /* Compression tag reference. */
|
||||
};
|
||||
@ -44,6 +45,8 @@ struct nvkm_vmm {
|
||||
|
||||
dma_addr_t null;
|
||||
void *nullp;
|
||||
|
||||
bool replay;
|
||||
};
|
||||
|
||||
int nvkm_vmm_new(struct nvkm_device *, u64 addr, u64 size, void *argv, u32 argc,
|
||||
@ -63,6 +66,7 @@ struct nvkm_vmm_map {
|
||||
struct nvkm_mm_node *mem;
|
||||
struct scatterlist *sgl;
|
||||
dma_addr_t *dma;
|
||||
u64 *pfn;
|
||||
u64 off;
|
||||
|
||||
const struct nvkm_vmm_page *page;
|
||||
@ -130,5 +134,5 @@ int gm20b_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
int gp100_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
int gp10b_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
int gv100_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
int tu104_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
int tu102_mmu_new(struct nvkm_device *, int, struct nvkm_mmu **);
|
||||
#endif
|
||||
|
@ -9,6 +9,7 @@ struct nvkm_top {
|
||||
struct list_head device;
|
||||
};
|
||||
|
||||
u32 nvkm_top_addr(struct nvkm_device *, enum nvkm_devidx);
|
||||
u32 nvkm_top_reset(struct nvkm_device *, enum nvkm_devidx);
|
||||
u32 nvkm_top_intr(struct nvkm_device *, u32 intr, u64 *subdevs);
|
||||
u32 nvkm_top_intr_mask(struct nvkm_device *, enum nvkm_devidx);
|
||||
|
@ -38,6 +38,7 @@ int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, u8 temp,
|
||||
|
||||
int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
int gf100_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
int gf117_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
int gm20b_volt_new(struct nvkm_device *, int, struct nvkm_volt **);
|
||||
|
@ -214,6 +214,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NOUVEAU_GETPARAM_FB_SIZE:
|
||||
getparam->value = drm->gem.vram_available;
|
||||
break;
|
||||
@ -338,7 +339,8 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
|
||||
goto done;
|
||||
|
||||
if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nouveau_vma_new(chan->ntfy, &cli->vmm, &chan->ntfy_vma);
|
||||
ret = nouveau_vma_new(chan->ntfy, chan->chan->vmm,
|
||||
&chan->ntfy_vma);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
|
||||
struct nouveau_drm *drm = cli->drm;
|
||||
struct nouveau_bo *nvbo;
|
||||
struct nvif_mmu *mmu = &cli->mmu;
|
||||
struct nvif_vmm *vmm = &cli->vmm.vmm;
|
||||
struct nvif_vmm *vmm = cli->svm.cli ? &cli->svm.vmm : &cli->vmm.vmm;
|
||||
size_t acc_size;
|
||||
int type = ttm_bo_type_device;
|
||||
int ret, i, pi = -1;
|
||||
@ -1434,7 +1434,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg)
|
||||
if (drm->client.mem->oclass < NVIF_CLASS_MEM_NV50 || !mem->kind)
|
||||
/* untiled */
|
||||
break;
|
||||
/* fallthrough, tiled memory */
|
||||
/* fall through - tiled memory */
|
||||
case TTM_PL_VRAM:
|
||||
reg->bus.offset = reg->start << PAGE_SHIFT;
|
||||
reg->bus.base = device->func->resource_addr(device, 1);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "nouveau_fence.h"
|
||||
#include "nouveau_abi16.h"
|
||||
#include "nouveau_vmm.h"
|
||||
#include "nouveau_svm.h"
|
||||
|
||||
MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM");
|
||||
int nouveau_vram_pushbuf;
|
||||
@ -95,6 +96,10 @@ nouveau_channel_del(struct nouveau_channel **pchan)
|
||||
|
||||
if (chan->fence)
|
||||
nouveau_fence(chan->drm)->context_del(chan);
|
||||
|
||||
if (cli)
|
||||
nouveau_svmm_part(chan->vmm->svmm, chan->inst);
|
||||
|
||||
nvif_object_fini(&chan->nvsw);
|
||||
nvif_object_fini(&chan->gart);
|
||||
nvif_object_fini(&chan->vram);
|
||||
@ -130,6 +135,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
|
||||
chan->device = device;
|
||||
chan->drm = drm;
|
||||
chan->vmm = cli->svm.cli ? &cli->svm : &cli->vmm;
|
||||
atomic_set(&chan->killed, 0);
|
||||
|
||||
/* allocate memory for dma push buffer */
|
||||
@ -157,7 +163,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
chan->push.addr = chan->push.buffer->bo.offset;
|
||||
|
||||
if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nouveau_vma_new(chan->push.buffer, &cli->vmm,
|
||||
ret = nouveau_vma_new(chan->push.buffer, chan->vmm,
|
||||
&chan->push.vma);
|
||||
if (ret) {
|
||||
nouveau_channel_del(pchan);
|
||||
@ -172,7 +178,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
args.target = NV_DMA_V0_TARGET_VM;
|
||||
args.access = NV_DMA_V0_ACCESS_VM;
|
||||
args.start = 0;
|
||||
args.limit = cli->vmm.vmm.limit - 1;
|
||||
args.limit = chan->vmm->vmm.limit - 1;
|
||||
} else
|
||||
if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) {
|
||||
if (device->info.family == NV_DEVICE_INFO_V0_TNT) {
|
||||
@ -202,7 +208,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
args.target = NV_DMA_V0_TARGET_VM;
|
||||
args.access = NV_DMA_V0_ACCESS_RDWR;
|
||||
args.start = 0;
|
||||
args.limit = cli->vmm.vmm.limit - 1;
|
||||
args.limit = chan->vmm->vmm.limit - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +226,6 @@ static int
|
||||
nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
u64 runlist, bool priv, struct nouveau_channel **pchan)
|
||||
{
|
||||
struct nouveau_cli *cli = (void *)device->object.client;
|
||||
static const u16 oclasses[] = { TURING_CHANNEL_GPFIFO_A,
|
||||
VOLTA_CHANNEL_GPFIFO_A,
|
||||
PASCAL_CHANNEL_GPFIFO_A,
|
||||
@ -255,7 +260,7 @@ nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
args.volta.ilength = 0x02000;
|
||||
args.volta.ioffset = 0x10000 + chan->push.addr;
|
||||
args.volta.runlist = runlist;
|
||||
args.volta.vmm = nvif_handle(&cli->vmm.vmm.object);
|
||||
args.volta.vmm = nvif_handle(&chan->vmm->vmm.object);
|
||||
args.volta.priv = priv;
|
||||
size = sizeof(args.volta);
|
||||
} else
|
||||
@ -264,7 +269,7 @@ nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
args.kepler.ilength = 0x02000;
|
||||
args.kepler.ioffset = 0x10000 + chan->push.addr;
|
||||
args.kepler.runlist = runlist;
|
||||
args.kepler.vmm = nvif_handle(&cli->vmm.vmm.object);
|
||||
args.kepler.vmm = nvif_handle(&chan->vmm->vmm.object);
|
||||
args.kepler.priv = priv;
|
||||
size = sizeof(args.kepler);
|
||||
} else
|
||||
@ -272,14 +277,14 @@ nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
args.fermi.version = 0;
|
||||
args.fermi.ilength = 0x02000;
|
||||
args.fermi.ioffset = 0x10000 + chan->push.addr;
|
||||
args.fermi.vmm = nvif_handle(&cli->vmm.vmm.object);
|
||||
args.fermi.vmm = nvif_handle(&chan->vmm->vmm.object);
|
||||
size = sizeof(args.fermi);
|
||||
} else {
|
||||
args.nv50.version = 0;
|
||||
args.nv50.ilength = 0x02000;
|
||||
args.nv50.ioffset = 0x10000 + chan->push.addr;
|
||||
args.nv50.pushbuf = nvif_handle(&chan->push.ctxdma);
|
||||
args.nv50.vmm = nvif_handle(&cli->vmm.vmm.object);
|
||||
args.nv50.vmm = nvif_handle(&chan->vmm->vmm.object);
|
||||
size = sizeof(args.nv50);
|
||||
}
|
||||
|
||||
@ -350,7 +355,6 @@ static int
|
||||
nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
|
||||
{
|
||||
struct nvif_device *device = chan->device;
|
||||
struct nouveau_cli *cli = (void *)chan->user.client;
|
||||
struct nouveau_drm *drm = chan->drm;
|
||||
struct nv_dma_v0 args = {};
|
||||
int ret, i;
|
||||
@ -376,7 +380,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
|
||||
args.target = NV_DMA_V0_TARGET_VM;
|
||||
args.access = NV_DMA_V0_ACCESS_VM;
|
||||
args.start = 0;
|
||||
args.limit = cli->vmm.vmm.limit - 1;
|
||||
args.limit = chan->vmm->vmm.limit - 1;
|
||||
} else {
|
||||
args.target = NV_DMA_V0_TARGET_VRAM;
|
||||
args.access = NV_DMA_V0_ACCESS_RDWR;
|
||||
@ -393,7 +397,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
|
||||
args.target = NV_DMA_V0_TARGET_VM;
|
||||
args.access = NV_DMA_V0_ACCESS_VM;
|
||||
args.start = 0;
|
||||
args.limit = cli->vmm.vmm.limit - 1;
|
||||
args.limit = chan->vmm->vmm.limit - 1;
|
||||
} else
|
||||
if (chan->drm->agp.bridge) {
|
||||
args.target = NV_DMA_V0_TARGET_AGP;
|
||||
@ -405,7 +409,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
|
||||
args.target = NV_DMA_V0_TARGET_VM;
|
||||
args.access = NV_DMA_V0_ACCESS_RDWR;
|
||||
args.start = 0;
|
||||
args.limit = cli->vmm.vmm.limit - 1;
|
||||
args.limit = chan->vmm->vmm.limit - 1;
|
||||
}
|
||||
|
||||
ret = nvif_object_init(&chan->user, gart, NV_DMA_IN_MEMORY,
|
||||
@ -495,6 +499,10 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device,
|
||||
nouveau_channel_del(pchan);
|
||||
}
|
||||
|
||||
ret = nouveau_svmm_join((*pchan)->vmm->svmm, (*pchan)->inst);
|
||||
if (ret)
|
||||
nouveau_channel_del(pchan);
|
||||
|
||||
done:
|
||||
cli->base.super = super;
|
||||
return ret;
|
||||
|
@ -8,6 +8,7 @@ struct nvif_device;
|
||||
struct nouveau_channel {
|
||||
struct nvif_device *device;
|
||||
struct nouveau_drm *drm;
|
||||
struct nouveau_vmm *vmm;
|
||||
|
||||
int chid;
|
||||
u64 inst;
|
||||
|
@ -32,18 +32,13 @@
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "dispnv04/hw.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_gem.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#include "nouveau_fence.h"
|
||||
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/cl0046.h>
|
||||
#include <nvif/event.h>
|
||||
|
||||
@ -412,15 +407,14 @@ nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
|
||||
#endif
|
||||
|
||||
int
|
||||
nouveau_display_init(struct drm_device *dev)
|
||||
nouveau_display_init(struct drm_device *dev, bool resume, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
int ret;
|
||||
|
||||
ret = disp->init(dev);
|
||||
ret = disp->init(dev, resume, runtime);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -437,8 +431,6 @@ nouveau_display_init(struct drm_device *dev)
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
/* enable flip completion events */
|
||||
nvif_notify_get(&drm->flip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -457,9 +449,6 @@ nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
|
||||
drm_helper_force_disable_all(dev);
|
||||
}
|
||||
|
||||
/* disable flip completion events */
|
||||
nvif_notify_put(&drm->flip);
|
||||
|
||||
/* disable hotplug interrupts */
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
|
||||
@ -472,7 +461,7 @@ nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
|
||||
cancel_work_sync(&drm->hpd_work);
|
||||
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
disp->fini(dev);
|
||||
disp->fini(dev, suspend);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -625,7 +614,6 @@ int
|
||||
nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev)) {
|
||||
if (!runtime) {
|
||||
@ -636,32 +624,9 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_display_fini(dev, true, runtime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nouveau_display_fini(dev, true, runtime);
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
|
||||
nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
|
||||
if (!nouveau_fb || !nouveau_fb->nvbo)
|
||||
continue;
|
||||
|
||||
nouveau_bo_unpin(nouveau_fb->nvbo);
|
||||
}
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
if (nv_crtc->cursor.nvbo) {
|
||||
if (nv_crtc->cursor.set_offset)
|
||||
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
|
||||
nouveau_bo_unpin(nv_crtc->cursor.nvbo);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -669,275 +634,16 @@ void
|
||||
nouveau_display_resume(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
nouveau_display_init(dev, true, runtime);
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev)) {
|
||||
nouveau_display_init(dev);
|
||||
if (disp->suspend) {
|
||||
drm_atomic_helper_resume(dev, disp->suspend);
|
||||
disp->suspend = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* re-pin fb/cursors */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_framebuffer *nouveau_fb;
|
||||
|
||||
nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
|
||||
if (!nouveau_fb || !nouveau_fb->nvbo)
|
||||
continue;
|
||||
|
||||
ret = nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM, true);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "Could not pin framebuffer\n");
|
||||
}
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
if (!nv_crtc->cursor.nvbo)
|
||||
continue;
|
||||
|
||||
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, true);
|
||||
if (!ret && nv_crtc->cursor.set_offset)
|
||||
ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "Could not pin/map cursor.\n");
|
||||
}
|
||||
|
||||
nouveau_display_init(dev);
|
||||
|
||||
/* Force CLUT to get re-loaded during modeset */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
nv_crtc->lut.depth = 0;
|
||||
}
|
||||
|
||||
/* This should ensure we don't hit a locking problem when someone
|
||||
* wakes us up via a connector. We should never go into suspend
|
||||
* while the display is on anyways.
|
||||
*/
|
||||
if (runtime)
|
||||
return;
|
||||
|
||||
drm_helper_resume_force_mode(dev);
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
if (!nv_crtc->cursor.nvbo)
|
||||
continue;
|
||||
|
||||
if (nv_crtc->cursor.set_offset)
|
||||
nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
|
||||
nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
|
||||
nv_crtc->cursor_saved_y);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_page_flip_emit(struct nouveau_channel *chan,
|
||||
struct nouveau_bo *old_bo,
|
||||
struct nouveau_bo *new_bo,
|
||||
struct nouveau_page_flip_state *s,
|
||||
struct nouveau_fence **pfence)
|
||||
{
|
||||
struct nouveau_fence_chan *fctx = chan->fence;
|
||||
struct nouveau_drm *drm = chan->drm;
|
||||
struct drm_device *dev = drm->dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Queue it to the pending list */
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_add_tail(&s->head, &fctx->flip);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
/* Synchronize with the old framebuffer */
|
||||
ret = nouveau_fence_sync(old_bo, chan, false, false);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Emit the pageflip */
|
||||
ret = RING_SPACE(chan, 2);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
|
||||
OUT_RING (chan, 0x00000000);
|
||||
FIRE_RING (chan);
|
||||
|
||||
ret = nouveau_fence_new(chan, false, pfence);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_del(&s->head);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event, u32 flags,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->primary->fb)->nvbo;
|
||||
struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
|
||||
struct nouveau_page_flip_state *s;
|
||||
struct nouveau_channel *chan;
|
||||
struct nouveau_cli *cli;
|
||||
struct nouveau_fence *fence;
|
||||
struct nv04_display *dispnv04 = nv04_display(dev);
|
||||
int head = nouveau_crtc(crtc)->index;
|
||||
int ret;
|
||||
|
||||
chan = drm->channel;
|
||||
if (!chan)
|
||||
return -ENODEV;
|
||||
cli = (void *)chan->user.client;
|
||||
|
||||
s = kzalloc(sizeof(*s), GFP_KERNEL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (new_bo != old_bo) {
|
||||
ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM, true);
|
||||
if (ret)
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
mutex_lock(&cli->mutex);
|
||||
ret = ttm_bo_reserve(&new_bo->bo, true, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
|
||||
/* synchronise rendering channel with the kernel's channel */
|
||||
ret = nouveau_fence_sync(new_bo, chan, false, true);
|
||||
if (ret) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
if (new_bo != old_bo) {
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
|
||||
ret = ttm_bo_reserve(&old_bo->bo, true, false, NULL);
|
||||
if (ret)
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
/* Initialize a page flip struct */
|
||||
*s = (struct nouveau_page_flip_state)
|
||||
{ { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0],
|
||||
new_bo->bo.offset };
|
||||
|
||||
/* Keep vblanks on during flip, for the target crtc of this flip */
|
||||
drm_crtc_vblank_get(crtc);
|
||||
|
||||
/* Emit a page flip */
|
||||
if (swap_interval) {
|
||||
ret = RING_SPACE(chan, 8);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x012c, 1);
|
||||
OUT_RING (chan, 0);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0134, 1);
|
||||
OUT_RING (chan, head);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0100, 1);
|
||||
OUT_RING (chan, 0);
|
||||
BEGIN_NV04(chan, NvSubImageBlit, 0x0130, 1);
|
||||
OUT_RING (chan, 0);
|
||||
}
|
||||
|
||||
nouveau_bo_ref(new_bo, &dispnv04->image[head]);
|
||||
|
||||
ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
mutex_unlock(&cli->mutex);
|
||||
|
||||
/* Update the crtc struct and cleanup */
|
||||
crtc->primary->fb = fb;
|
||||
|
||||
nouveau_bo_fence(old_bo, fence, false);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(old_bo);
|
||||
nouveau_fence_unref(&fence);
|
||||
return 0;
|
||||
|
||||
fail_unreserve:
|
||||
drm_crtc_vblank_put(crtc);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
fail_unpin:
|
||||
mutex_unlock(&cli->mutex);
|
||||
if (old_bo != new_bo)
|
||||
nouveau_bo_unpin(new_bo);
|
||||
fail_free:
|
||||
kfree(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_finish_page_flip(struct nouveau_channel *chan,
|
||||
struct nouveau_page_flip_state *ps)
|
||||
{
|
||||
struct nouveau_fence_chan *fctx = chan->fence;
|
||||
struct nouveau_drm *drm = chan->drm;
|
||||
struct drm_device *dev = drm->dev;
|
||||
struct nouveau_page_flip_state *s;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (list_empty(&fctx->flip)) {
|
||||
NV_ERROR(drm, "unexpected pageflip\n");
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
|
||||
if (s->event) {
|
||||
drm_crtc_arm_vblank_event(s->crtc, s->event);
|
||||
} else {
|
||||
/* Give up ownership of vblank for page-flipped crtc */
|
||||
drm_crtc_vblank_put(s->crtc);
|
||||
}
|
||||
|
||||
list_del(&s->head);
|
||||
if (ps)
|
||||
*ps = *s;
|
||||
kfree(s);
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_flip_complete(struct nvif_notify *notify)
|
||||
{
|
||||
struct nouveau_drm *drm = container_of(notify, typeof(*drm), flip);
|
||||
struct nouveau_channel *chan = drm->channel;
|
||||
struct nouveau_page_flip_state state;
|
||||
|
||||
if (!nouveau_finish_page_flip(chan, &state)) {
|
||||
nv_set_crtc_base(drm->dev, drm_crtc_index(state.crtc),
|
||||
state.offset + state.crtc->y *
|
||||
state.pitch + state.crtc->x *
|
||||
state.bpp / 8);
|
||||
}
|
||||
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -25,19 +25,11 @@ int nouveau_framebuffer_new(struct drm_device *,
|
||||
const struct drm_mode_fb_cmd2 *,
|
||||
struct nouveau_bo *, struct nouveau_framebuffer **);
|
||||
|
||||
struct nouveau_page_flip_state {
|
||||
struct list_head head;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct drm_crtc *crtc;
|
||||
int bpp, pitch;
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
struct nouveau_display {
|
||||
void *priv;
|
||||
void (*dtor)(struct drm_device *);
|
||||
int (*init)(struct drm_device *);
|
||||
void (*fini)(struct drm_device *);
|
||||
int (*init)(struct drm_device *, bool resume, bool runtime);
|
||||
void (*fini)(struct drm_device *, bool suspend);
|
||||
|
||||
struct nvif_disp disp;
|
||||
|
||||
@ -61,7 +53,7 @@ nouveau_display(struct drm_device *dev)
|
||||
|
||||
int nouveau_display_create(struct drm_device *dev);
|
||||
void nouveau_display_destroy(struct drm_device *dev);
|
||||
int nouveau_display_init(struct drm_device *dev);
|
||||
int nouveau_display_init(struct drm_device *dev, bool resume, bool runtime);
|
||||
void nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime);
|
||||
int nouveau_display_suspend(struct drm_device *dev, bool runtime);
|
||||
void nouveau_display_resume(struct drm_device *dev, bool runtime);
|
||||
@ -71,13 +63,6 @@ bool nouveau_display_scanoutpos(struct drm_device *, unsigned int,
|
||||
bool, int *, int *, ktime_t *,
|
||||
ktime_t *, const struct drm_display_mode *);
|
||||
|
||||
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event,
|
||||
uint32_t page_flip_flags,
|
||||
struct drm_modeset_acquire_ctx *ctx);
|
||||
int nouveau_finish_page_flip(struct nouveau_channel *,
|
||||
struct nouveau_page_flip_state *);
|
||||
|
||||
int nouveau_display_dumb_create(struct drm_file *, struct drm_device *,
|
||||
struct drm_mode_create_dumb *args);
|
||||
int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
|
||||
|
887
drivers/gpu/drm/nouveau/nouveau_dmem.c
Normal file
887
drivers/gpu/drm/nouveau/nouveau_dmem.c
Normal file
@ -0,0 +1,887 @@
|
||||
/*
|
||||
* Copyright 2018 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 "nouveau_dmem.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_chan.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_mem.h"
|
||||
#include "nouveau_bo.h"
|
||||
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/object.h>
|
||||
#include <nvif/if500b.h>
|
||||
#include <nvif/if900b.h>
|
||||
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/hmm.h>
|
||||
|
||||
/*
|
||||
* FIXME: this is ugly right now we are using TTM to allocate vram and we pin
|
||||
* it in vram while in use. We likely want to overhaul memory management for
|
||||
* nouveau to be more page like (not necessarily with system page size but a
|
||||
* bigger page size) at lowest level and have some shim layer on top that would
|
||||
* provide the same functionality as TTM.
|
||||
*/
|
||||
#define DMEM_CHUNK_SIZE (2UL << 20)
|
||||
#define DMEM_CHUNK_NPAGES (DMEM_CHUNK_SIZE >> PAGE_SHIFT)
|
||||
|
||||
struct nouveau_migrate;
|
||||
|
||||
enum nouveau_aper {
|
||||
NOUVEAU_APER_VIRT,
|
||||
NOUVEAU_APER_VRAM,
|
||||
NOUVEAU_APER_HOST,
|
||||
};
|
||||
|
||||
typedef int (*nouveau_migrate_copy_t)(struct nouveau_drm *drm, u64 npages,
|
||||
enum nouveau_aper, u64 dst_addr,
|
||||
enum nouveau_aper, u64 src_addr);
|
||||
|
||||
struct nouveau_dmem_chunk {
|
||||
struct list_head list;
|
||||
struct nouveau_bo *bo;
|
||||
struct nouveau_drm *drm;
|
||||
unsigned long pfn_first;
|
||||
unsigned long callocated;
|
||||
unsigned long bitmap[BITS_TO_LONGS(DMEM_CHUNK_NPAGES)];
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct nouveau_dmem_migrate {
|
||||
nouveau_migrate_copy_t copy_func;
|
||||
struct nouveau_channel *chan;
|
||||
};
|
||||
|
||||
struct nouveau_dmem {
|
||||
struct hmm_devmem *devmem;
|
||||
struct nouveau_dmem_migrate migrate;
|
||||
struct list_head chunk_free;
|
||||
struct list_head chunk_full;
|
||||
struct list_head chunk_empty;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
struct nouveau_dmem_fault {
|
||||
struct nouveau_drm *drm;
|
||||
struct nouveau_fence *fence;
|
||||
dma_addr_t *dma;
|
||||
unsigned long npages;
|
||||
};
|
||||
|
||||
struct nouveau_migrate {
|
||||
struct vm_area_struct *vma;
|
||||
struct nouveau_drm *drm;
|
||||
struct nouveau_fence *fence;
|
||||
unsigned long npages;
|
||||
dma_addr_t *dma;
|
||||
unsigned long dma_nr;
|
||||
};
|
||||
|
||||
static void
|
||||
nouveau_dmem_free(struct hmm_devmem *devmem, struct page *page)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
struct nouveau_drm *drm;
|
||||
unsigned long idx;
|
||||
|
||||
chunk = (void *)hmm_devmem_page_get_drvdata(page);
|
||||
idx = page_to_pfn(page) - chunk->pfn_first;
|
||||
drm = chunk->drm;
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
*
|
||||
* This is really a bad example, we need to overhaul nouveau memory
|
||||
* management to be more page focus and allow lighter locking scheme
|
||||
* to be use in the process.
|
||||
*/
|
||||
spin_lock(&chunk->lock);
|
||||
clear_bit(idx, chunk->bitmap);
|
||||
WARN_ON(!chunk->callocated);
|
||||
chunk->callocated--;
|
||||
/*
|
||||
* FIXME when chunk->callocated reach 0 we should add the chunk to
|
||||
* a reclaim list so that it can be freed in case of memory pressure.
|
||||
*/
|
||||
spin_unlock(&chunk->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_dmem_fault_alloc_and_copy(struct vm_area_struct *vma,
|
||||
const unsigned long *src_pfns,
|
||||
unsigned long *dst_pfns,
|
||||
unsigned long start,
|
||||
unsigned long end,
|
||||
void *private)
|
||||
{
|
||||
struct nouveau_dmem_fault *fault = private;
|
||||
struct nouveau_drm *drm = fault->drm;
|
||||
struct device *dev = drm->dev->dev;
|
||||
unsigned long addr, i, npages = 0;
|
||||
nouveau_migrate_copy_t copy;
|
||||
int ret;
|
||||
|
||||
|
||||
/* First allocate new memory */
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, i++) {
|
||||
struct page *dpage, *spage;
|
||||
|
||||
dst_pfns[i] = 0;
|
||||
spage = migrate_pfn_to_page(src_pfns[i]);
|
||||
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
dpage = hmm_vma_alloc_locked_page(vma, addr);
|
||||
if (!dpage) {
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)) |
|
||||
MIGRATE_PFN_LOCKED;
|
||||
npages++;
|
||||
}
|
||||
|
||||
/* Allocate storage for DMA addresses, so we can unmap later. */
|
||||
fault->dma = kmalloc(sizeof(*fault->dma) * npages, GFP_KERNEL);
|
||||
if (!fault->dma)
|
||||
goto error;
|
||||
|
||||
/* Copy things over */
|
||||
copy = drm->dmem->migrate.copy_func;
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, i++) {
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
struct page *spage, *dpage;
|
||||
u64 src_addr, dst_addr;
|
||||
|
||||
dpage = migrate_pfn_to_page(dst_pfns[i]);
|
||||
if (!dpage || dst_pfns[i] == MIGRATE_PFN_ERROR)
|
||||
continue;
|
||||
|
||||
spage = migrate_pfn_to_page(src_pfns[i]);
|
||||
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) {
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
__free_page(dpage);
|
||||
continue;
|
||||
}
|
||||
|
||||
fault->dma[fault->npages] =
|
||||
dma_map_page_attrs(dev, dpage, 0, PAGE_SIZE,
|
||||
PCI_DMA_BIDIRECTIONAL,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (dma_mapping_error(dev, fault->dma[fault->npages])) {
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
__free_page(dpage);
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_addr = fault->dma[fault->npages++];
|
||||
|
||||
chunk = (void *)hmm_devmem_page_get_drvdata(spage);
|
||||
src_addr = page_to_pfn(spage) - chunk->pfn_first;
|
||||
src_addr = (src_addr << PAGE_SHIFT) + chunk->bo->bo.offset;
|
||||
|
||||
ret = copy(drm, 1, NOUVEAU_APER_HOST, dst_addr,
|
||||
NOUVEAU_APER_VRAM, src_addr);
|
||||
if (ret) {
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
__free_page(dpage);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_fence_new(drm->dmem->migrate.chan, false, &fault->fence);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, ++i) {
|
||||
struct page *page;
|
||||
|
||||
if (!dst_pfns[i] || dst_pfns[i] == MIGRATE_PFN_ERROR)
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(dst_pfns[i]);
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
if (page == NULL)
|
||||
continue;
|
||||
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
void nouveau_dmem_fault_finalize_and_map(struct vm_area_struct *vma,
|
||||
const unsigned long *src_pfns,
|
||||
const unsigned long *dst_pfns,
|
||||
unsigned long start,
|
||||
unsigned long end,
|
||||
void *private)
|
||||
{
|
||||
struct nouveau_dmem_fault *fault = private;
|
||||
struct nouveau_drm *drm = fault->drm;
|
||||
|
||||
if (fault->fence) {
|
||||
nouveau_fence_wait(fault->fence, true, false);
|
||||
nouveau_fence_unref(&fault->fence);
|
||||
} else {
|
||||
/*
|
||||
* FIXME wait for channel to be IDLE before calling finalizing
|
||||
* the hmem object below (nouveau_migrate_hmem_fini()).
|
||||
*/
|
||||
}
|
||||
|
||||
while (fault->npages--) {
|
||||
dma_unmap_page(drm->dev->dev, fault->dma[fault->npages],
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
}
|
||||
kfree(fault->dma);
|
||||
}
|
||||
|
||||
static const struct migrate_vma_ops nouveau_dmem_fault_migrate_ops = {
|
||||
.alloc_and_copy = nouveau_dmem_fault_alloc_and_copy,
|
||||
.finalize_and_map = nouveau_dmem_fault_finalize_and_map,
|
||||
};
|
||||
|
||||
static int
|
||||
nouveau_dmem_fault(struct hmm_devmem *devmem,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long addr,
|
||||
const struct page *page,
|
||||
unsigned int flags,
|
||||
pmd_t *pmdp)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(devmem->device);
|
||||
unsigned long src[1] = {0}, dst[1] = {0};
|
||||
struct nouveau_dmem_fault fault = {0};
|
||||
int ret;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* FIXME what we really want is to find some heuristic to migrate more
|
||||
* than just one page on CPU fault. When such fault happens it is very
|
||||
* likely that more surrounding page will CPU fault too.
|
||||
*/
|
||||
fault.drm = nouveau_drm(drm_dev);
|
||||
ret = migrate_vma(&nouveau_dmem_fault_migrate_ops, vma, addr,
|
||||
addr + PAGE_SIZE, src, dst, &fault);
|
||||
if (ret)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
if (dst[0] == MIGRATE_PFN_ERROR)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hmm_devmem_ops
|
||||
nouveau_dmem_devmem_ops = {
|
||||
.free = nouveau_dmem_free,
|
||||
.fault = nouveau_dmem_fault,
|
||||
};
|
||||
|
||||
static int
|
||||
nouveau_dmem_chunk_alloc(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
int ret;
|
||||
|
||||
if (drm->dmem == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
chunk = list_first_entry_or_null(&drm->dmem->chunk_empty,
|
||||
struct nouveau_dmem_chunk,
|
||||
list);
|
||||
if (chunk == NULL) {
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_del(&chunk->list);
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
|
||||
ret = nouveau_bo_new(&drm->client, DMEM_CHUNK_SIZE, 0,
|
||||
TTM_PL_FLAG_VRAM, 0, 0, NULL, NULL,
|
||||
&chunk->bo);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
|
||||
if (ret) {
|
||||
nouveau_bo_ref(NULL, &chunk->bo);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bitmap_zero(chunk->bitmap, DMEM_CHUNK_NPAGES);
|
||||
spin_lock_init(&chunk->lock);
|
||||
|
||||
out:
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
if (chunk->bo)
|
||||
list_add(&chunk->list, &drm->dmem->chunk_empty);
|
||||
else
|
||||
list_add_tail(&chunk->list, &drm->dmem->chunk_empty);
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nouveau_dmem_chunk *
|
||||
nouveau_dmem_chunk_first_free_locked(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
|
||||
chunk = list_first_entry_or_null(&drm->dmem->chunk_free,
|
||||
struct nouveau_dmem_chunk,
|
||||
list);
|
||||
if (chunk)
|
||||
return chunk;
|
||||
|
||||
chunk = list_first_entry_or_null(&drm->dmem->chunk_empty,
|
||||
struct nouveau_dmem_chunk,
|
||||
list);
|
||||
if (chunk->bo)
|
||||
return chunk;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_dmem_pages_alloc(struct nouveau_drm *drm,
|
||||
unsigned long npages,
|
||||
unsigned long *pages)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
unsigned long c;
|
||||
int ret;
|
||||
|
||||
memset(pages, 0xff, npages * sizeof(*pages));
|
||||
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
for (c = 0; c < npages;) {
|
||||
unsigned long i;
|
||||
|
||||
chunk = nouveau_dmem_chunk_first_free_locked(drm);
|
||||
if (chunk == NULL) {
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
ret = nouveau_dmem_chunk_alloc(drm);
|
||||
if (ret) {
|
||||
if (c)
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
spin_lock(&chunk->lock);
|
||||
i = find_first_zero_bit(chunk->bitmap, DMEM_CHUNK_NPAGES);
|
||||
while (i < DMEM_CHUNK_NPAGES && c < npages) {
|
||||
pages[c] = chunk->pfn_first + i;
|
||||
set_bit(i, chunk->bitmap);
|
||||
chunk->callocated++;
|
||||
c++;
|
||||
|
||||
i = find_next_zero_bit(chunk->bitmap,
|
||||
DMEM_CHUNK_NPAGES, i);
|
||||
}
|
||||
spin_unlock(&chunk->lock);
|
||||
}
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *
|
||||
nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm)
|
||||
{
|
||||
unsigned long pfns[1];
|
||||
struct page *page;
|
||||
int ret;
|
||||
|
||||
/* FIXME stop all the miss-match API ... */
|
||||
ret = nouveau_dmem_pages_alloc(drm, 1, pfns);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
page = pfn_to_page(pfns[0]);
|
||||
get_page(page);
|
||||
lock_page(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_dmem_page_free_locked(struct nouveau_drm *drm, struct page *page)
|
||||
{
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_dmem_resume(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
int ret;
|
||||
|
||||
if (drm->dmem == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_free, list) {
|
||||
ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
|
||||
/* FIXME handle pin failure */
|
||||
WARN_ON(ret);
|
||||
}
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_full, list) {
|
||||
ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
|
||||
/* FIXME handle pin failure */
|
||||
WARN_ON(ret);
|
||||
}
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_empty, list) {
|
||||
ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
|
||||
/* FIXME handle pin failure */
|
||||
WARN_ON(ret);
|
||||
}
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_dmem_suspend(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
|
||||
if (drm->dmem == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_free, list) {
|
||||
nouveau_bo_unpin(chunk->bo);
|
||||
}
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_full, list) {
|
||||
nouveau_bo_unpin(chunk->bo);
|
||||
}
|
||||
list_for_each_entry (chunk, &drm->dmem->chunk_empty, list) {
|
||||
nouveau_bo_unpin(chunk->bo);
|
||||
}
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_dmem_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_dmem_chunk *chunk, *tmp;
|
||||
|
||||
if (drm->dmem == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&drm->dmem->mutex);
|
||||
|
||||
WARN_ON(!list_empty(&drm->dmem->chunk_free));
|
||||
WARN_ON(!list_empty(&drm->dmem->chunk_full));
|
||||
|
||||
list_for_each_entry_safe (chunk, tmp, &drm->dmem->chunk_empty, list) {
|
||||
if (chunk->bo) {
|
||||
nouveau_bo_unpin(chunk->bo);
|
||||
nouveau_bo_ref(NULL, &chunk->bo);
|
||||
}
|
||||
list_del(&chunk->list);
|
||||
kfree(chunk);
|
||||
}
|
||||
|
||||
mutex_unlock(&drm->dmem->mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
nvc0b5_migrate_copy(struct nouveau_drm *drm, u64 npages,
|
||||
enum nouveau_aper dst_aper, u64 dst_addr,
|
||||
enum nouveau_aper src_aper, u64 src_addr)
|
||||
{
|
||||
struct nouveau_channel *chan = drm->dmem->migrate.chan;
|
||||
u32 launch_dma = (1 << 9) /* MULTI_LINE_ENABLE. */ |
|
||||
(1 << 8) /* DST_MEMORY_LAYOUT_PITCH. */ |
|
||||
(1 << 7) /* SRC_MEMORY_LAYOUT_PITCH. */ |
|
||||
(1 << 2) /* FLUSH_ENABLE_TRUE. */ |
|
||||
(2 << 0) /* DATA_TRANSFER_TYPE_NON_PIPELINED. */;
|
||||
int ret;
|
||||
|
||||
ret = RING_SPACE(chan, 13);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (src_aper != NOUVEAU_APER_VIRT) {
|
||||
switch (src_aper) {
|
||||
case NOUVEAU_APER_VRAM:
|
||||
BEGIN_IMC0(chan, NvSubCopy, 0x0260, 0);
|
||||
break;
|
||||
case NOUVEAU_APER_HOST:
|
||||
BEGIN_IMC0(chan, NvSubCopy, 0x0260, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
launch_dma |= 0x00001000; /* SRC_TYPE_PHYSICAL. */
|
||||
}
|
||||
|
||||
if (dst_aper != NOUVEAU_APER_VIRT) {
|
||||
switch (dst_aper) {
|
||||
case NOUVEAU_APER_VRAM:
|
||||
BEGIN_IMC0(chan, NvSubCopy, 0x0264, 0);
|
||||
break;
|
||||
case NOUVEAU_APER_HOST:
|
||||
BEGIN_IMC0(chan, NvSubCopy, 0x0264, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
launch_dma |= 0x00002000; /* DST_TYPE_PHYSICAL. */
|
||||
}
|
||||
|
||||
BEGIN_NVC0(chan, NvSubCopy, 0x0400, 8);
|
||||
OUT_RING (chan, upper_32_bits(src_addr));
|
||||
OUT_RING (chan, lower_32_bits(src_addr));
|
||||
OUT_RING (chan, upper_32_bits(dst_addr));
|
||||
OUT_RING (chan, lower_32_bits(dst_addr));
|
||||
OUT_RING (chan, PAGE_SIZE);
|
||||
OUT_RING (chan, PAGE_SIZE);
|
||||
OUT_RING (chan, PAGE_SIZE);
|
||||
OUT_RING (chan, npages);
|
||||
BEGIN_NVC0(chan, NvSubCopy, 0x0300, 1);
|
||||
OUT_RING (chan, launch_dma);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_dmem_migrate_init(struct nouveau_drm *drm)
|
||||
{
|
||||
switch (drm->ttm.copy.oclass) {
|
||||
case PASCAL_DMA_COPY_A:
|
||||
case PASCAL_DMA_COPY_B:
|
||||
case VOLTA_DMA_COPY_A:
|
||||
case TURING_DMA_COPY_A:
|
||||
drm->dmem->migrate.copy_func = nvc0b5_migrate_copy;
|
||||
drm->dmem->migrate.chan = drm->ttm.chan;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_dmem_init(struct nouveau_drm *drm)
|
||||
{
|
||||
struct device *device = drm->dev->dev;
|
||||
unsigned long i, size;
|
||||
int ret;
|
||||
|
||||
/* This only make sense on PASCAL or newer */
|
||||
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_PASCAL)
|
||||
return;
|
||||
|
||||
if (!(drm->dmem = kzalloc(sizeof(*drm->dmem), GFP_KERNEL)))
|
||||
return;
|
||||
|
||||
mutex_init(&drm->dmem->mutex);
|
||||
INIT_LIST_HEAD(&drm->dmem->chunk_free);
|
||||
INIT_LIST_HEAD(&drm->dmem->chunk_full);
|
||||
INIT_LIST_HEAD(&drm->dmem->chunk_empty);
|
||||
|
||||
size = ALIGN(drm->client.device.info.ram_user, DMEM_CHUNK_SIZE);
|
||||
|
||||
/* Initialize migration dma helpers before registering memory */
|
||||
ret = nouveau_dmem_migrate_init(drm);
|
||||
if (ret) {
|
||||
kfree(drm->dmem);
|
||||
drm->dmem = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME we need some kind of policy to decide how much VRAM we
|
||||
* want to register with HMM. For now just register everything
|
||||
* and latter if we want to do thing like over commit then we
|
||||
* could revisit this.
|
||||
*/
|
||||
drm->dmem->devmem = hmm_devmem_add(&nouveau_dmem_devmem_ops,
|
||||
device, size);
|
||||
if (drm->dmem->devmem == NULL) {
|
||||
kfree(drm->dmem);
|
||||
drm->dmem = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < (size / DMEM_CHUNK_SIZE); ++i) {
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
struct page *page;
|
||||
unsigned long j;
|
||||
|
||||
chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
|
||||
if (chunk == NULL) {
|
||||
nouveau_dmem_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
chunk->drm = drm;
|
||||
chunk->pfn_first = drm->dmem->devmem->pfn_first;
|
||||
chunk->pfn_first += (i * DMEM_CHUNK_NPAGES);
|
||||
list_add_tail(&chunk->list, &drm->dmem->chunk_empty);
|
||||
|
||||
page = pfn_to_page(chunk->pfn_first);
|
||||
for (j = 0; j < DMEM_CHUNK_NPAGES; ++j, ++page) {
|
||||
hmm_devmem_page_set_drvdata(page, (long)chunk);
|
||||
}
|
||||
}
|
||||
|
||||
NV_INFO(drm, "DMEM: registered %ldMB of device memory\n", size >> 20);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_dmem_migrate_alloc_and_copy(struct vm_area_struct *vma,
|
||||
const unsigned long *src_pfns,
|
||||
unsigned long *dst_pfns,
|
||||
unsigned long start,
|
||||
unsigned long end,
|
||||
void *private)
|
||||
{
|
||||
struct nouveau_migrate *migrate = private;
|
||||
struct nouveau_drm *drm = migrate->drm;
|
||||
struct device *dev = drm->dev->dev;
|
||||
unsigned long addr, i, npages = 0;
|
||||
nouveau_migrate_copy_t copy;
|
||||
int ret;
|
||||
|
||||
/* First allocate new memory */
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, i++) {
|
||||
struct page *dpage, *spage;
|
||||
|
||||
dst_pfns[i] = 0;
|
||||
spage = migrate_pfn_to_page(src_pfns[i]);
|
||||
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
dpage = nouveau_dmem_page_alloc_locked(drm);
|
||||
if (!dpage)
|
||||
continue;
|
||||
|
||||
dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)) |
|
||||
MIGRATE_PFN_LOCKED |
|
||||
MIGRATE_PFN_DEVICE;
|
||||
npages++;
|
||||
}
|
||||
|
||||
if (!npages)
|
||||
return;
|
||||
|
||||
/* Allocate storage for DMA addresses, so we can unmap later. */
|
||||
migrate->dma = kmalloc(sizeof(*migrate->dma) * npages, GFP_KERNEL);
|
||||
if (!migrate->dma)
|
||||
goto error;
|
||||
|
||||
/* Copy things over */
|
||||
copy = drm->dmem->migrate.copy_func;
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, i++) {
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
struct page *spage, *dpage;
|
||||
u64 src_addr, dst_addr;
|
||||
|
||||
dpage = migrate_pfn_to_page(dst_pfns[i]);
|
||||
if (!dpage || dst_pfns[i] == MIGRATE_PFN_ERROR)
|
||||
continue;
|
||||
|
||||
chunk = (void *)hmm_devmem_page_get_drvdata(dpage);
|
||||
dst_addr = page_to_pfn(dpage) - chunk->pfn_first;
|
||||
dst_addr = (dst_addr << PAGE_SHIFT) + chunk->bo->bo.offset;
|
||||
|
||||
spage = migrate_pfn_to_page(src_pfns[i]);
|
||||
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) {
|
||||
nouveau_dmem_page_free_locked(drm, dpage);
|
||||
dst_pfns[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
migrate->dma[migrate->dma_nr] =
|
||||
dma_map_page_attrs(dev, spage, 0, PAGE_SIZE,
|
||||
PCI_DMA_BIDIRECTIONAL,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (dma_mapping_error(dev, migrate->dma[migrate->dma_nr])) {
|
||||
nouveau_dmem_page_free_locked(drm, dpage);
|
||||
dst_pfns[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
src_addr = migrate->dma[migrate->dma_nr++];
|
||||
|
||||
ret = copy(drm, 1, NOUVEAU_APER_VRAM, dst_addr,
|
||||
NOUVEAU_APER_HOST, src_addr);
|
||||
if (ret) {
|
||||
nouveau_dmem_page_free_locked(drm, dpage);
|
||||
dst_pfns[i] = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_fence_new(drm->dmem->migrate.chan, false, &migrate->fence);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
for (addr = start, i = 0; addr < end; addr += PAGE_SIZE, ++i) {
|
||||
struct page *page;
|
||||
|
||||
if (!dst_pfns[i] || dst_pfns[i] == MIGRATE_PFN_ERROR)
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(dst_pfns[i]);
|
||||
dst_pfns[i] = MIGRATE_PFN_ERROR;
|
||||
if (page == NULL)
|
||||
continue;
|
||||
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
void nouveau_dmem_migrate_finalize_and_map(struct vm_area_struct *vma,
|
||||
const unsigned long *src_pfns,
|
||||
const unsigned long *dst_pfns,
|
||||
unsigned long start,
|
||||
unsigned long end,
|
||||
void *private)
|
||||
{
|
||||
struct nouveau_migrate *migrate = private;
|
||||
struct nouveau_drm *drm = migrate->drm;
|
||||
|
||||
if (migrate->fence) {
|
||||
nouveau_fence_wait(migrate->fence, true, false);
|
||||
nouveau_fence_unref(&migrate->fence);
|
||||
} else {
|
||||
/*
|
||||
* FIXME wait for channel to be IDLE before finalizing
|
||||
* the hmem object below (nouveau_migrate_hmem_fini()) ?
|
||||
*/
|
||||
}
|
||||
|
||||
while (migrate->dma_nr--) {
|
||||
dma_unmap_page(drm->dev->dev, migrate->dma[migrate->dma_nr],
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
}
|
||||
kfree(migrate->dma);
|
||||
|
||||
/*
|
||||
* FIXME optimization: update GPU page table to point to newly
|
||||
* migrated memory.
|
||||
*/
|
||||
}
|
||||
|
||||
static const struct migrate_vma_ops nouveau_dmem_migrate_ops = {
|
||||
.alloc_and_copy = nouveau_dmem_migrate_alloc_and_copy,
|
||||
.finalize_and_map = nouveau_dmem_migrate_finalize_and_map,
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_dmem_migrate_vma(struct nouveau_drm *drm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
unsigned long *src_pfns, *dst_pfns, npages;
|
||||
struct nouveau_migrate migrate = {0};
|
||||
unsigned long i, c, max;
|
||||
int ret = 0;
|
||||
|
||||
npages = (end - start) >> PAGE_SHIFT;
|
||||
max = min(SG_MAX_SINGLE_ALLOC, npages);
|
||||
src_pfns = kzalloc(sizeof(long) * max, GFP_KERNEL);
|
||||
if (src_pfns == NULL)
|
||||
return -ENOMEM;
|
||||
dst_pfns = kzalloc(sizeof(long) * max, GFP_KERNEL);
|
||||
if (dst_pfns == NULL) {
|
||||
kfree(src_pfns);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
migrate.drm = drm;
|
||||
migrate.vma = vma;
|
||||
migrate.npages = npages;
|
||||
for (i = 0; i < npages; i += c) {
|
||||
unsigned long next;
|
||||
|
||||
c = min(SG_MAX_SINGLE_ALLOC, npages);
|
||||
next = start + (c << PAGE_SHIFT);
|
||||
ret = migrate_vma(&nouveau_dmem_migrate_ops, vma, start,
|
||||
next, src_pfns, dst_pfns, &migrate);
|
||||
if (ret)
|
||||
goto out;
|
||||
start = next;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(dst_pfns);
|
||||
kfree(src_pfns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
nouveau_dmem_page(struct nouveau_drm *drm, struct page *page)
|
||||
{
|
||||
if (!is_device_private_page(page))
|
||||
return false;
|
||||
|
||||
if (drm->dmem->devmem != page->pgmap->data)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_dmem_convert_pfn(struct nouveau_drm *drm,
|
||||
struct hmm_range *range)
|
||||
{
|
||||
unsigned long i, npages;
|
||||
|
||||
npages = (range->end - range->start) >> PAGE_SHIFT;
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct nouveau_dmem_chunk *chunk;
|
||||
struct page *page;
|
||||
uint64_t addr;
|
||||
|
||||
page = hmm_pfn_to_page(range, range->pfns[i]);
|
||||
if (page == NULL)
|
||||
continue;
|
||||
|
||||
if (!(range->pfns[i] & range->flags[HMM_PFN_DEVICE_PRIVATE])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!nouveau_dmem_page(drm, page)) {
|
||||
WARN(1, "Some unknown device memory !\n");
|
||||
range->pfns[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk = (void *)hmm_devmem_page_get_drvdata(page);
|
||||
addr = page_to_pfn(page) - chunk->pfn_first;
|
||||
addr = (addr + chunk->bo->bo.mem.start) << PAGE_SHIFT;
|
||||
|
||||
range->pfns[i] &= ((1UL << range->pfn_shift) - 1);
|
||||
range->pfns[i] |= (addr >> PAGE_SHIFT) << range->pfn_shift;
|
||||
}
|
||||
}
|
60
drivers/gpu/drm/nouveau/nouveau_dmem.h
Normal file
60
drivers/gpu/drm/nouveau/nouveau_dmem.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
#ifndef __NOUVEAU_DMEM_H__
|
||||
#define __NOUVEAU_DMEM_H__
|
||||
#include <nvif/os.h>
|
||||
struct drm_device;
|
||||
struct drm_file;
|
||||
struct nouveau_drm;
|
||||
struct hmm_range;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM)
|
||||
void nouveau_dmem_init(struct nouveau_drm *);
|
||||
void nouveau_dmem_fini(struct nouveau_drm *);
|
||||
void nouveau_dmem_suspend(struct nouveau_drm *);
|
||||
void nouveau_dmem_resume(struct nouveau_drm *);
|
||||
|
||||
int nouveau_dmem_migrate_vma(struct nouveau_drm *drm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end);
|
||||
|
||||
void nouveau_dmem_convert_pfn(struct nouveau_drm *drm,
|
||||
struct hmm_range *range);
|
||||
#else /* IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM) */
|
||||
static inline void nouveau_dmem_init(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_dmem_fini(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_dmem_suspend(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_dmem_resume(struct nouveau_drm *drm) {}
|
||||
|
||||
static inline int nouveau_dmem_migrate_vma(struct nouveau_drm *drm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void nouveau_dmem_convert_pfn(struct nouveau_drm *drm,
|
||||
struct hmm_range *range) {}
|
||||
#endif /* IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM) */
|
||||
#endif
|
@ -44,7 +44,6 @@
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/cl0002.h>
|
||||
#include <nvif/cla06f.h>
|
||||
#include <nvif/if0004.h>
|
||||
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_dma.h"
|
||||
@ -63,6 +62,8 @@
|
||||
#include "nouveau_usif.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_platform.h"
|
||||
#include "nouveau_svm.h"
|
||||
#include "nouveau_dmem.h"
|
||||
|
||||
MODULE_PARM_DESC(config, "option string to pass to driver core");
|
||||
static char *nouveau_config;
|
||||
@ -173,6 +174,7 @@ nouveau_cli_fini(struct nouveau_cli *cli)
|
||||
WARN_ON(!list_empty(&cli->worker));
|
||||
|
||||
usif_client_fini(cli);
|
||||
nouveau_vmm_fini(&cli->svm);
|
||||
nouveau_vmm_fini(&cli->vmm);
|
||||
nvif_mmu_fini(&cli->mmu);
|
||||
nvif_device_fini(&cli->device);
|
||||
@ -283,19 +285,134 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname,
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_accel_fini(struct nouveau_drm *drm)
|
||||
nouveau_accel_ce_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
nouveau_channel_idle(drm->cechan);
|
||||
nvif_object_fini(&drm->ttm.copy);
|
||||
nouveau_channel_del(&drm->cechan);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_accel_ce_init(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
int ret = 0;
|
||||
|
||||
/* Allocate channel that has access to a (preferably async) copy
|
||||
* engine, to use for TTM buffer moves.
|
||||
*/
|
||||
if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
|
||||
ret = nouveau_channel_new(drm, device,
|
||||
nvif_fifo_runlist_ce(device), 0,
|
||||
true, &drm->cechan);
|
||||
} else
|
||||
if (device->info.chipset >= 0xa3 &&
|
||||
device->info.chipset != 0xaa &&
|
||||
device->info.chipset != 0xac) {
|
||||
/* Prior to Kepler, there's only a single runlist, so all
|
||||
* engines can be accessed from any channel.
|
||||
*
|
||||
* We still want to use a separate channel though.
|
||||
*/
|
||||
ret = nouveau_channel_new(drm, device, NvDmaFB, NvDmaTT, false,
|
||||
&drm->cechan);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_accel_gr_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
nouveau_channel_idle(drm->channel);
|
||||
nvif_object_fini(&drm->ntfy);
|
||||
nvkm_gpuobj_del(&drm->notify);
|
||||
nvif_notify_fini(&drm->flip);
|
||||
nvif_object_fini(&drm->nvsw);
|
||||
nouveau_channel_del(&drm->channel);
|
||||
}
|
||||
|
||||
nouveau_channel_idle(drm->cechan);
|
||||
nvif_object_fini(&drm->ttm.copy);
|
||||
nouveau_channel_del(&drm->cechan);
|
||||
static void
|
||||
nouveau_accel_gr_init(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
u32 arg0, arg1;
|
||||
int ret;
|
||||
|
||||
/* Allocate channel that has access to the graphics engine. */
|
||||
if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
|
||||
arg0 = nvif_fifo_runlist(device, NV_DEVICE_INFO_ENGINE_GR);
|
||||
arg1 = 1;
|
||||
} else {
|
||||
arg0 = NvDmaFB;
|
||||
arg1 = NvDmaTT;
|
||||
}
|
||||
|
||||
ret = nouveau_channel_new(drm, device, arg0, arg1, false,
|
||||
&drm->channel);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
|
||||
nouveau_accel_gr_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* A SW class is used on pre-NV50 HW to assist with handling the
|
||||
* synchronisation of page flips, as well as to implement fences
|
||||
* on TNT/TNT2 HW that lacks any kind of support in host.
|
||||
*/
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nvif_object_init(&drm->channel->user, NVDRM_NVSW,
|
||||
nouveau_abi16_swclass(drm), NULL, 0,
|
||||
&drm->nvsw);
|
||||
if (ret == 0) {
|
||||
ret = RING_SPACE(drm->channel, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
|
||||
OUT_RING (drm->channel, drm->nvsw.handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate sw class, %d\n", ret);
|
||||
nouveau_accel_gr_fini(drm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* NvMemoryToMemoryFormat requires a notifier ctxdma for some reason,
|
||||
* even if notification is never requested, so, allocate a ctxdma on
|
||||
* any GPU where it's possible we'll end up using M2MF for BO moves.
|
||||
*/
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
|
||||
ret = nvkm_gpuobj_new(nvxx_device(device), 32, 0, false, NULL,
|
||||
&drm->notify);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
|
||||
nouveau_accel_gr_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nvif_object_init(&drm->channel->user, NvNotify0,
|
||||
NV_DMA_IN_MEMORY,
|
||||
&(struct nv_dma_v0) {
|
||||
.target = NV_DMA_V0_TARGET_VRAM,
|
||||
.access = NV_DMA_V0_ACCESS_RDWR,
|
||||
.start = drm->notify->addr,
|
||||
.limit = drm->notify->addr + 31
|
||||
}, sizeof(struct nv_dma_v0),
|
||||
&drm->ntfy);
|
||||
if (ret) {
|
||||
nouveau_accel_gr_fini(drm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_accel_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
nouveau_accel_ce_fini(drm);
|
||||
nouveau_accel_gr_fini(drm);
|
||||
if (drm->fence)
|
||||
nouveau_fence(drm)->dtor(drm);
|
||||
}
|
||||
@ -305,23 +422,16 @@ nouveau_accel_init(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nvif_device *device = &drm->client.device;
|
||||
struct nvif_sclass *sclass;
|
||||
u32 arg0, arg1;
|
||||
int ret, i, n;
|
||||
|
||||
if (nouveau_noaccel)
|
||||
return;
|
||||
|
||||
/* Initialise global support for channels, and synchronisation. */
|
||||
ret = nouveau_channels_init(drm);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_VOLTA) {
|
||||
ret = nvif_user_init(device);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialise synchronisation routines */
|
||||
/*XXX: this is crap, but the fence/channel stuff is a little
|
||||
* backwards in some places. this will be fixed.
|
||||
*/
|
||||
@ -368,95 +478,18 @@ nouveau_accel_init(struct nouveau_drm *drm)
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
|
||||
ret = nouveau_channel_new(drm, &drm->client.device,
|
||||
nvif_fifo_runlist_ce(device), 0,
|
||||
true, &drm->cechan);
|
||||
/* Volta requires access to a doorbell register for kickoff. */
|
||||
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_VOLTA) {
|
||||
ret = nvif_user_init(device);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
|
||||
|
||||
arg0 = nvif_fifo_runlist(device, NV_DEVICE_INFO_ENGINE_GR);
|
||||
arg1 = 1;
|
||||
} else
|
||||
if (device->info.chipset >= 0xa3 &&
|
||||
device->info.chipset != 0xaa &&
|
||||
device->info.chipset != 0xac) {
|
||||
ret = nouveau_channel_new(drm, &drm->client.device,
|
||||
NvDmaFB, NvDmaTT, false,
|
||||
&drm->cechan);
|
||||
if (ret)
|
||||
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
|
||||
|
||||
arg0 = NvDmaFB;
|
||||
arg1 = NvDmaTT;
|
||||
} else {
|
||||
arg0 = NvDmaFB;
|
||||
arg1 = NvDmaTT;
|
||||
}
|
||||
|
||||
ret = nouveau_channel_new(drm, &drm->client.device,
|
||||
arg0, arg1, false, &drm->channel);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
|
||||
nouveau_accel_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nvif_object_init(&drm->channel->user, NVDRM_NVSW,
|
||||
nouveau_abi16_swclass(drm), NULL, 0,
|
||||
&drm->nvsw);
|
||||
if (ret == 0) {
|
||||
ret = RING_SPACE(drm->channel, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
|
||||
OUT_RING (drm->channel, drm->nvsw.handle);
|
||||
}
|
||||
|
||||
ret = nvif_notify_init(&drm->nvsw,
|
||||
nouveau_flip_complete,
|
||||
false, NV04_NVSW_NTFY_UEVENT,
|
||||
NULL, 0, 0, &drm->flip);
|
||||
if (ret == 0)
|
||||
ret = nvif_notify_get(&drm->flip);
|
||||
if (ret) {
|
||||
nouveau_accel_fini(drm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate sw class, %d\n", ret);
|
||||
nouveau_accel_fini(drm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
|
||||
ret = nvkm_gpuobj_new(nvxx_device(&drm->client.device), 32, 0,
|
||||
false, NULL, &drm->notify);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
|
||||
nouveau_accel_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nvif_object_init(&drm->channel->user, NvNotify0,
|
||||
NV_DMA_IN_MEMORY,
|
||||
&(struct nv_dma_v0) {
|
||||
.target = NV_DMA_V0_TARGET_VRAM,
|
||||
.access = NV_DMA_V0_ACCESS_RDWR,
|
||||
.start = drm->notify->addr,
|
||||
.limit = drm->notify->addr + 31
|
||||
}, sizeof(struct nv_dma_v0),
|
||||
&drm->ntfy);
|
||||
if (ret) {
|
||||
nouveau_accel_fini(drm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate channels we need to support various functions. */
|
||||
nouveau_accel_gr_init(drm);
|
||||
nouveau_accel_ce_init(drm);
|
||||
|
||||
/* Initialise accelerated TTM buffer moves. */
|
||||
nouveau_bo_move_init(drm);
|
||||
}
|
||||
|
||||
@ -504,19 +537,22 @@ nouveau_drm_device_init(struct drm_device *dev)
|
||||
if (ret)
|
||||
goto fail_bios;
|
||||
|
||||
nouveau_accel_init(drm);
|
||||
|
||||
ret = nouveau_display_create(dev);
|
||||
if (ret)
|
||||
goto fail_dispctor;
|
||||
|
||||
if (dev->mode_config.num_crtc) {
|
||||
ret = nouveau_display_init(dev);
|
||||
ret = nouveau_display_init(dev, false, false);
|
||||
if (ret)
|
||||
goto fail_dispinit;
|
||||
}
|
||||
|
||||
nouveau_debugfs_init(drm);
|
||||
nouveau_hwmon_init(dev);
|
||||
nouveau_accel_init(drm);
|
||||
nouveau_svm_init(drm);
|
||||
nouveau_dmem_init(drm);
|
||||
nouveau_fbcon_init(dev);
|
||||
nouveau_led_init(dev);
|
||||
|
||||
@ -534,6 +570,7 @@ nouveau_drm_device_init(struct drm_device *dev)
|
||||
fail_dispinit:
|
||||
nouveau_display_destroy(dev);
|
||||
fail_dispctor:
|
||||
nouveau_accel_fini(drm);
|
||||
nouveau_bios_takedown(dev);
|
||||
fail_bios:
|
||||
nouveau_ttm_fini(drm);
|
||||
@ -559,7 +596,8 @@ nouveau_drm_device_fini(struct drm_device *dev)
|
||||
|
||||
nouveau_led_fini(dev);
|
||||
nouveau_fbcon_fini(dev);
|
||||
nouveau_accel_fini(drm);
|
||||
nouveau_dmem_fini(drm);
|
||||
nouveau_svm_fini(drm);
|
||||
nouveau_hwmon_fini(dev);
|
||||
nouveau_debugfs_fini(drm);
|
||||
|
||||
@ -567,6 +605,7 @@ nouveau_drm_device_fini(struct drm_device *dev)
|
||||
nouveau_display_fini(dev, false, false);
|
||||
nouveau_display_destroy(dev);
|
||||
|
||||
nouveau_accel_fini(drm);
|
||||
nouveau_bios_takedown(dev);
|
||||
|
||||
nouveau_ttm_fini(drm);
|
||||
@ -704,6 +743,8 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
int ret;
|
||||
|
||||
nouveau_svm_suspend(drm);
|
||||
nouveau_dmem_suspend(drm);
|
||||
nouveau_led_suspend(dev);
|
||||
|
||||
if (dev->mode_config.num_crtc) {
|
||||
@ -780,7 +821,8 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
|
||||
}
|
||||
|
||||
nouveau_led_resume(dev);
|
||||
|
||||
nouveau_dmem_resume(drm);
|
||||
nouveau_svm_resume(drm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1000,6 +1042,8 @@ nouveau_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_SVM_INIT, nouveau_svmm_init, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_SVM_BIND, nouveau_svmm_bind, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
|
@ -96,6 +96,7 @@ struct nouveau_cli {
|
||||
struct nvif_device device;
|
||||
struct nvif_mmu mmu;
|
||||
struct nouveau_vmm vmm;
|
||||
struct nouveau_vmm svm;
|
||||
const struct nvif_mclass *mem;
|
||||
|
||||
struct list_head head;
|
||||
@ -181,7 +182,6 @@ struct nouveau_drm {
|
||||
struct nouveau_fbdev *fbcon;
|
||||
struct nvif_object nvsw;
|
||||
struct nvif_object ntfy;
|
||||
struct nvif_notify flip;
|
||||
|
||||
/* nv10-nv40 tiling regions */
|
||||
struct {
|
||||
@ -210,6 +210,10 @@ struct nouveau_drm {
|
||||
bool have_disp_power_ref;
|
||||
|
||||
struct dev_pm_domain vga_pm_domain;
|
||||
|
||||
struct nouveau_svm *svm;
|
||||
|
||||
struct nouveau_dmem *dmem;
|
||||
};
|
||||
|
||||
static inline struct nouveau_drm *
|
||||
|
@ -353,7 +353,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
|
||||
|
||||
chan = nouveau_nofbaccel ? NULL : drm->channel;
|
||||
if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
||||
ret = nouveau_vma_new(nvbo, &drm->client.vmm, &fb->vma);
|
||||
ret = nouveau_vma_new(nvbo, chan->vmm, &fb->vma);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
|
||||
chan = NULL;
|
||||
|
@ -82,8 +82,6 @@ int nv50_fence_create(struct nouveau_drm *);
|
||||
int nv84_fence_create(struct nouveau_drm *);
|
||||
int nvc0_fence_create(struct nouveau_drm *);
|
||||
|
||||
int nouveau_flip_complete(struct nvif_notify *);
|
||||
|
||||
struct nv84_fence_chan {
|
||||
struct nouveau_fence_chan base;
|
||||
struct nouveau_vma *vma;
|
||||
|
@ -68,10 +68,11 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
|
||||
struct nouveau_bo *nvbo = nouveau_gem_object(gem);
|
||||
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
|
||||
struct device *dev = drm->dev->dev;
|
||||
struct nouveau_vmm *vmm = cli->svm.cli ? &cli->svm : &cli->vmm;
|
||||
struct nouveau_vma *vma;
|
||||
int ret;
|
||||
|
||||
if (cli->vmm.vmm.object.oclass < NVIF_CLASS_VMM_NV50)
|
||||
if (vmm->vmm.object.oclass < NVIF_CLASS_VMM_NV50)
|
||||
return 0;
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
|
||||
@ -82,7 +83,7 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
|
||||
if (ret < 0 && ret != -EACCES)
|
||||
goto out;
|
||||
|
||||
ret = nouveau_vma_new(nvbo, &cli->vmm, &vma);
|
||||
ret = nouveau_vma_new(nvbo, vmm, &vma);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
out:
|
||||
@ -142,17 +143,18 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
|
||||
struct nouveau_bo *nvbo = nouveau_gem_object(gem);
|
||||
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
|
||||
struct device *dev = drm->dev->dev;
|
||||
struct nouveau_vmm *vmm = cli->svm.cli ? &cli->svm : & cli->vmm;
|
||||
struct nouveau_vma *vma;
|
||||
int ret;
|
||||
|
||||
if (cli->vmm.vmm.object.oclass < NVIF_CLASS_VMM_NV50)
|
||||
if (vmm->vmm.object.oclass < NVIF_CLASS_VMM_NV50)
|
||||
return;
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
vma = nouveau_vma_find(nvbo, &cli->vmm);
|
||||
vma = nouveau_vma_find(nvbo, vmm);
|
||||
if (vma) {
|
||||
if (--vma->refs == 0) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
@ -219,6 +221,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(file_priv);
|
||||
struct nouveau_bo *nvbo = nouveau_gem_object(gem);
|
||||
struct nouveau_vmm *vmm = cli->svm.cli ? &cli->svm : &cli->vmm;
|
||||
struct nouveau_vma *vma;
|
||||
|
||||
if (is_power_of_2(nvbo->valid_domains))
|
||||
@ -228,8 +231,8 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
|
||||
else
|
||||
rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
|
||||
rep->offset = nvbo->bo.offset;
|
||||
if (cli->vmm.vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
vma = nouveau_vma_find(nvbo, &cli->vmm);
|
||||
if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
vma = nouveau_vma_find(nvbo, vmm);
|
||||
if (!vma)
|
||||
return -EINVAL;
|
||||
|
||||
@ -321,7 +324,8 @@ struct validate_op {
|
||||
};
|
||||
|
||||
static void
|
||||
validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence,
|
||||
validate_fini_no_ticket(struct validate_op *op, struct nouveau_channel *chan,
|
||||
struct nouveau_fence *fence,
|
||||
struct drm_nouveau_gem_pushbuf_bo *pbbo)
|
||||
{
|
||||
struct nouveau_bo *nvbo;
|
||||
@ -332,13 +336,11 @@ validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence,
|
||||
b = &pbbo[nvbo->pbbo_index];
|
||||
|
||||
if (likely(fence)) {
|
||||
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
|
||||
struct nouveau_vma *vma;
|
||||
|
||||
nouveau_bo_fence(nvbo, fence, !!b->write_domains);
|
||||
|
||||
if (drm->client.vmm.vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
vma = (void *)(unsigned long)b->user_priv;
|
||||
if (chan->vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
struct nouveau_vma *vma =
|
||||
(void *)(unsigned long)b->user_priv;
|
||||
nouveau_fence_unref(&vma->fence);
|
||||
dma_fence_get(&fence->base);
|
||||
vma->fence = fence;
|
||||
@ -358,10 +360,11 @@ validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence,
|
||||
}
|
||||
|
||||
static void
|
||||
validate_fini(struct validate_op *op, struct nouveau_fence *fence,
|
||||
validate_fini(struct validate_op *op, struct nouveau_channel *chan,
|
||||
struct nouveau_fence *fence,
|
||||
struct drm_nouveau_gem_pushbuf_bo *pbbo)
|
||||
{
|
||||
validate_fini_no_ticket(op, fence, pbbo);
|
||||
validate_fini_no_ticket(op, chan, fence, pbbo);
|
||||
ww_acquire_fini(&op->ticket);
|
||||
}
|
||||
|
||||
@ -416,7 +419,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
|
||||
list_splice_tail_init(&vram_list, &op->list);
|
||||
list_splice_tail_init(&gart_list, &op->list);
|
||||
list_splice_tail_init(&both_list, &op->list);
|
||||
validate_fini_no_ticket(op, NULL, NULL);
|
||||
validate_fini_no_ticket(op, chan, NULL, NULL);
|
||||
if (unlikely(ret == -EDEADLK)) {
|
||||
ret = ttm_bo_reserve_slowpath(&nvbo->bo, true,
|
||||
&op->ticket);
|
||||
@ -430,8 +433,8 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
|
||||
}
|
||||
}
|
||||
|
||||
if (cli->vmm.vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
struct nouveau_vmm *vmm = &cli->vmm;
|
||||
if (chan->vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50) {
|
||||
struct nouveau_vmm *vmm = chan->vmm;
|
||||
struct nouveau_vma *vma = nouveau_vma_find(nvbo, vmm);
|
||||
if (!vma) {
|
||||
NV_PRINTK(err, cli, "vma not found!\n");
|
||||
@ -471,7 +474,7 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
|
||||
list_splice_tail(&gart_list, &op->list);
|
||||
list_splice_tail(&both_list, &op->list);
|
||||
if (ret)
|
||||
validate_fini(op, NULL, NULL);
|
||||
validate_fini(op, chan, NULL, NULL);
|
||||
return ret;
|
||||
|
||||
}
|
||||
@ -563,7 +566,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
|
||||
if (unlikely(ret < 0)) {
|
||||
if (ret != -ERESTARTSYS)
|
||||
NV_PRINTK(err, cli, "validating bo list\n");
|
||||
validate_fini(op, NULL, NULL);
|
||||
validate_fini(op, chan, NULL, NULL);
|
||||
return ret;
|
||||
}
|
||||
*apply_relocs = ret;
|
||||
@ -842,7 +845,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
||||
}
|
||||
|
||||
out:
|
||||
validate_fini(&op, fence, bo);
|
||||
validate_fini(&op, chan, fence, bo);
|
||||
nouveau_fence_unref(&fence);
|
||||
|
||||
out_prevalid:
|
||||
|
835
drivers/gpu/drm/nouveau/nouveau_svm.c
Normal file
835
drivers/gpu/drm/nouveau/nouveau_svm.c
Normal file
@ -0,0 +1,835 @@
|
||||
/*
|
||||
* Copyright 2018 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 "nouveau_svm.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_chan.h"
|
||||
#include "nouveau_dmem.h"
|
||||
|
||||
#include <nvif/notify.h>
|
||||
#include <nvif/object.h>
|
||||
#include <nvif/vmm.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/clb069.h>
|
||||
#include <nvif/ifc00d.h>
|
||||
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/hmm.h>
|
||||
|
||||
struct nouveau_svm {
|
||||
struct nouveau_drm *drm;
|
||||
struct mutex mutex;
|
||||
struct list_head inst;
|
||||
|
||||
struct nouveau_svm_fault_buffer {
|
||||
int id;
|
||||
struct nvif_object object;
|
||||
u32 entries;
|
||||
u32 getaddr;
|
||||
u32 putaddr;
|
||||
u32 get;
|
||||
u32 put;
|
||||
struct nvif_notify notify;
|
||||
|
||||
struct nouveau_svm_fault {
|
||||
u64 inst;
|
||||
u64 addr;
|
||||
u64 time;
|
||||
u32 engine;
|
||||
u8 gpc;
|
||||
u8 hub;
|
||||
u8 access;
|
||||
u8 client;
|
||||
u8 fault;
|
||||
struct nouveau_svmm *svmm;
|
||||
} **fault;
|
||||
int fault_nr;
|
||||
} buffer[1];
|
||||
};
|
||||
|
||||
#define SVM_DBG(s,f,a...) NV_DEBUG((s)->drm, "svm: "f"\n", ##a)
|
||||
#define SVM_ERR(s,f,a...) NV_WARN((s)->drm, "svm: "f"\n", ##a)
|
||||
|
||||
struct nouveau_ivmm {
|
||||
struct nouveau_svmm *svmm;
|
||||
u64 inst;
|
||||
struct list_head head;
|
||||
};
|
||||
|
||||
static struct nouveau_ivmm *
|
||||
nouveau_ivmm_find(struct nouveau_svm *svm, u64 inst)
|
||||
{
|
||||
struct nouveau_ivmm *ivmm;
|
||||
list_for_each_entry(ivmm, &svm->inst, head) {
|
||||
if (ivmm->inst == inst)
|
||||
return ivmm;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nouveau_svmm {
|
||||
struct nouveau_vmm *vmm;
|
||||
struct {
|
||||
unsigned long start;
|
||||
unsigned long limit;
|
||||
} unmanaged;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
struct mm_struct *mm;
|
||||
struct hmm_mirror mirror;
|
||||
};
|
||||
|
||||
#define SVMM_DBG(s,f,a...) \
|
||||
NV_DEBUG((s)->vmm->cli->drm, "svm-%p: "f"\n", (s), ##a)
|
||||
#define SVMM_ERR(s,f,a...) \
|
||||
NV_WARN((s)->vmm->cli->drm, "svm-%p: "f"\n", (s), ##a)
|
||||
|
||||
int
|
||||
nouveau_svmm_bind(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(file_priv);
|
||||
struct drm_nouveau_svm_bind *args = data;
|
||||
unsigned target, cmd, priority;
|
||||
unsigned long addr, end, size;
|
||||
struct mm_struct *mm;
|
||||
|
||||
args->va_start &= PAGE_MASK;
|
||||
args->va_end &= PAGE_MASK;
|
||||
|
||||
/* Sanity check arguments */
|
||||
if (args->reserved0 || args->reserved1)
|
||||
return -EINVAL;
|
||||
if (args->header & (~NOUVEAU_SVM_BIND_VALID_MASK))
|
||||
return -EINVAL;
|
||||
if (args->va_start >= args->va_end)
|
||||
return -EINVAL;
|
||||
if (!args->npages)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT;
|
||||
cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK;
|
||||
switch (cmd) {
|
||||
case NOUVEAU_SVM_BIND_COMMAND__MIGRATE:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priority = args->header >> NOUVEAU_SVM_BIND_PRIORITY_SHIFT;
|
||||
priority &= NOUVEAU_SVM_BIND_PRIORITY_MASK;
|
||||
|
||||
/* FIXME support CPU target ie all target value < GPU_VRAM */
|
||||
target = args->header >> NOUVEAU_SVM_BIND_TARGET_SHIFT;
|
||||
target &= NOUVEAU_SVM_BIND_TARGET_MASK;
|
||||
switch (target) {
|
||||
case NOUVEAU_SVM_BIND_TARGET__GPU_VRAM:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: For now refuse non 0 stride, we need to change the migrate
|
||||
* kernel function to handle stride to avoid to create a mess within
|
||||
* each device driver.
|
||||
*/
|
||||
if (args->stride)
|
||||
return -EINVAL;
|
||||
|
||||
size = ((unsigned long)args->npages) << PAGE_SHIFT;
|
||||
if ((args->va_start + size) <= args->va_start)
|
||||
return -EINVAL;
|
||||
if ((args->va_start + size) > args->va_end)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Ok we are ask to do something sane, for now we only support migrate
|
||||
* commands but we will add things like memory policy (what to do on
|
||||
* page fault) and maybe some other commands.
|
||||
*/
|
||||
|
||||
mm = get_task_mm(current);
|
||||
down_read(&mm->mmap_sem);
|
||||
|
||||
for (addr = args->va_start, end = args->va_start + size; addr < end;) {
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long next;
|
||||
|
||||
vma = find_vma_intersection(mm, addr, end);
|
||||
if (!vma)
|
||||
break;
|
||||
|
||||
next = min(vma->vm_end, end);
|
||||
/* This is a best effort so we ignore errors */
|
||||
nouveau_dmem_migrate_vma(cli->drm, vma, addr, next);
|
||||
addr = next;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME Return the number of page we have migrated, again we need to
|
||||
* update the migrate API to return that information so that we can
|
||||
* report it to user space.
|
||||
*/
|
||||
args->result = 0;
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlink channel instance from SVMM. */
|
||||
void
|
||||
nouveau_svmm_part(struct nouveau_svmm *svmm, u64 inst)
|
||||
{
|
||||
struct nouveau_ivmm *ivmm;
|
||||
if (svmm) {
|
||||
mutex_lock(&svmm->vmm->cli->drm->svm->mutex);
|
||||
ivmm = nouveau_ivmm_find(svmm->vmm->cli->drm->svm, inst);
|
||||
if (ivmm) {
|
||||
list_del(&ivmm->head);
|
||||
kfree(ivmm);
|
||||
}
|
||||
mutex_unlock(&svmm->vmm->cli->drm->svm->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Link channel instance to SVMM. */
|
||||
int
|
||||
nouveau_svmm_join(struct nouveau_svmm *svmm, u64 inst)
|
||||
{
|
||||
struct nouveau_ivmm *ivmm;
|
||||
if (svmm) {
|
||||
if (!(ivmm = kmalloc(sizeof(*ivmm), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
ivmm->svmm = svmm;
|
||||
ivmm->inst = inst;
|
||||
|
||||
mutex_lock(&svmm->vmm->cli->drm->svm->mutex);
|
||||
list_add(&ivmm->head, &svmm->vmm->cli->drm->svm->inst);
|
||||
mutex_unlock(&svmm->vmm->cli->drm->svm->mutex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Invalidate SVMM address-range on GPU. */
|
||||
static void
|
||||
nouveau_svmm_invalidate(struct nouveau_svmm *svmm, u64 start, u64 limit)
|
||||
{
|
||||
if (limit > start) {
|
||||
bool super = svmm->vmm->vmm.object.client->super;
|
||||
svmm->vmm->vmm.object.client->super = true;
|
||||
nvif_object_mthd(&svmm->vmm->vmm.object, NVIF_VMM_V0_PFNCLR,
|
||||
&(struct nvif_vmm_pfnclr_v0) {
|
||||
.addr = start,
|
||||
.size = limit - start,
|
||||
}, sizeof(struct nvif_vmm_pfnclr_v0));
|
||||
svmm->vmm->vmm.object.client->super = super;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_svmm_sync_cpu_device_pagetables(struct hmm_mirror *mirror,
|
||||
const struct hmm_update *update)
|
||||
{
|
||||
struct nouveau_svmm *svmm = container_of(mirror, typeof(*svmm), mirror);
|
||||
unsigned long start = update->start;
|
||||
unsigned long limit = update->end;
|
||||
|
||||
if (!update->blockable)
|
||||
return -EAGAIN;
|
||||
|
||||
SVMM_DBG(svmm, "invalidate %016lx-%016lx", start, limit);
|
||||
|
||||
mutex_lock(&svmm->mutex);
|
||||
if (limit > svmm->unmanaged.start && start < svmm->unmanaged.limit) {
|
||||
if (start < svmm->unmanaged.start) {
|
||||
nouveau_svmm_invalidate(svmm, start,
|
||||
svmm->unmanaged.limit);
|
||||
}
|
||||
start = svmm->unmanaged.limit;
|
||||
}
|
||||
|
||||
nouveau_svmm_invalidate(svmm, start, limit);
|
||||
mutex_unlock(&svmm->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_svmm_release(struct hmm_mirror *mirror)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct hmm_mirror_ops
|
||||
nouveau_svmm = {
|
||||
.sync_cpu_device_pagetables = nouveau_svmm_sync_cpu_device_pagetables,
|
||||
.release = nouveau_svmm_release,
|
||||
};
|
||||
|
||||
void
|
||||
nouveau_svmm_fini(struct nouveau_svmm **psvmm)
|
||||
{
|
||||
struct nouveau_svmm *svmm = *psvmm;
|
||||
if (svmm) {
|
||||
hmm_mirror_unregister(&svmm->mirror);
|
||||
kfree(*psvmm);
|
||||
*psvmm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_svmm_init(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct nouveau_cli *cli = nouveau_cli(file_priv);
|
||||
struct nouveau_svmm *svmm;
|
||||
struct drm_nouveau_svm_init *args = data;
|
||||
int ret;
|
||||
|
||||
/* Allocate tracking for SVM-enabled VMM. */
|
||||
if (!(svmm = kzalloc(sizeof(*svmm), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
svmm->vmm = &cli->svm;
|
||||
svmm->unmanaged.start = args->unmanaged_addr;
|
||||
svmm->unmanaged.limit = args->unmanaged_addr + args->unmanaged_size;
|
||||
mutex_init(&svmm->mutex);
|
||||
|
||||
/* Check that SVM isn't already enabled for the client. */
|
||||
mutex_lock(&cli->mutex);
|
||||
if (cli->svm.cli) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Allocate a new GPU VMM that can support SVM (managed by the
|
||||
* client, with replayable faults enabled).
|
||||
*
|
||||
* All future channel/memory allocations will make use of this
|
||||
* VMM instead of the standard one.
|
||||
*/
|
||||
ret = nvif_vmm_init(&cli->mmu, cli->vmm.vmm.object.oclass, true,
|
||||
args->unmanaged_addr, args->unmanaged_size,
|
||||
&(struct gp100_vmm_v0) {
|
||||
.fault_replay = true,
|
||||
}, sizeof(struct gp100_vmm_v0), &cli->svm.vmm);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
/* Enable HMM mirroring of CPU address-space to VMM. */
|
||||
svmm->mm = get_task_mm(current);
|
||||
down_write(&svmm->mm->mmap_sem);
|
||||
svmm->mirror.ops = &nouveau_svmm;
|
||||
ret = hmm_mirror_register(&svmm->mirror, svmm->mm);
|
||||
if (ret == 0) {
|
||||
cli->svm.svmm = svmm;
|
||||
cli->svm.cli = cli;
|
||||
}
|
||||
up_write(&svmm->mm->mmap_sem);
|
||||
mmput(svmm->mm);
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
nouveau_svmm_fini(&svmm);
|
||||
mutex_unlock(&cli->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u64
|
||||
nouveau_svm_pfn_flags[HMM_PFN_FLAG_MAX] = {
|
||||
[HMM_PFN_VALID ] = NVIF_VMM_PFNMAP_V0_V,
|
||||
[HMM_PFN_WRITE ] = NVIF_VMM_PFNMAP_V0_W,
|
||||
[HMM_PFN_DEVICE_PRIVATE] = NVIF_VMM_PFNMAP_V0_VRAM,
|
||||
};
|
||||
|
||||
static const u64
|
||||
nouveau_svm_pfn_values[HMM_PFN_VALUE_MAX] = {
|
||||
[HMM_PFN_ERROR ] = ~NVIF_VMM_PFNMAP_V0_V,
|
||||
[HMM_PFN_NONE ] = NVIF_VMM_PFNMAP_V0_NONE,
|
||||
[HMM_PFN_SPECIAL] = ~NVIF_VMM_PFNMAP_V0_V,
|
||||
};
|
||||
|
||||
/* Issue fault replay for GPU to retry accesses that faulted previously. */
|
||||
static void
|
||||
nouveau_svm_fault_replay(struct nouveau_svm *svm)
|
||||
{
|
||||
SVM_DBG(svm, "replay");
|
||||
WARN_ON(nvif_object_mthd(&svm->drm->client.vmm.vmm.object,
|
||||
GP100_VMM_VN_FAULT_REPLAY,
|
||||
&(struct gp100_vmm_fault_replay_vn) {},
|
||||
sizeof(struct gp100_vmm_fault_replay_vn)));
|
||||
}
|
||||
|
||||
/* Cancel a replayable fault that could not be handled.
|
||||
*
|
||||
* Cancelling the fault will trigger recovery to reset the engine
|
||||
* and kill the offending channel (ie. GPU SIGSEGV).
|
||||
*/
|
||||
static void
|
||||
nouveau_svm_fault_cancel(struct nouveau_svm *svm,
|
||||
u64 inst, u8 hub, u8 gpc, u8 client)
|
||||
{
|
||||
SVM_DBG(svm, "cancel %016llx %d %02x %02x", inst, hub, gpc, client);
|
||||
WARN_ON(nvif_object_mthd(&svm->drm->client.vmm.vmm.object,
|
||||
GP100_VMM_VN_FAULT_CANCEL,
|
||||
&(struct gp100_vmm_fault_cancel_v0) {
|
||||
.hub = hub,
|
||||
.gpc = gpc,
|
||||
.client = client,
|
||||
.inst = inst,
|
||||
}, sizeof(struct gp100_vmm_fault_cancel_v0)));
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_svm_fault_cancel_fault(struct nouveau_svm *svm,
|
||||
struct nouveau_svm_fault *fault)
|
||||
{
|
||||
nouveau_svm_fault_cancel(svm, fault->inst,
|
||||
fault->hub,
|
||||
fault->gpc,
|
||||
fault->client);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_svm_fault_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct nouveau_svm_fault *fa = *(struct nouveau_svm_fault **)a;
|
||||
const struct nouveau_svm_fault *fb = *(struct nouveau_svm_fault **)b;
|
||||
int ret;
|
||||
if ((ret = (s64)fa->inst - fb->inst))
|
||||
return ret;
|
||||
if ((ret = (s64)fa->addr - fb->addr))
|
||||
return ret;
|
||||
/*XXX: atomic? */
|
||||
return (fa->access == 0 || fa->access == 3) -
|
||||
(fb->access == 0 || fb->access == 3);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_svm_fault_cache(struct nouveau_svm *svm,
|
||||
struct nouveau_svm_fault_buffer *buffer, u32 offset)
|
||||
{
|
||||
struct nvif_object *memory = &buffer->object;
|
||||
const u32 instlo = nvif_rd32(memory, offset + 0x00);
|
||||
const u32 insthi = nvif_rd32(memory, offset + 0x04);
|
||||
const u32 addrlo = nvif_rd32(memory, offset + 0x08);
|
||||
const u32 addrhi = nvif_rd32(memory, offset + 0x0c);
|
||||
const u32 timelo = nvif_rd32(memory, offset + 0x10);
|
||||
const u32 timehi = nvif_rd32(memory, offset + 0x14);
|
||||
const u32 engine = nvif_rd32(memory, offset + 0x18);
|
||||
const u32 info = nvif_rd32(memory, offset + 0x1c);
|
||||
const u64 inst = (u64)insthi << 32 | instlo;
|
||||
const u8 gpc = (info & 0x1f000000) >> 24;
|
||||
const u8 hub = (info & 0x00100000) >> 20;
|
||||
const u8 client = (info & 0x00007f00) >> 8;
|
||||
struct nouveau_svm_fault *fault;
|
||||
|
||||
//XXX: i think we're supposed to spin waiting */
|
||||
if (WARN_ON(!(info & 0x80000000)))
|
||||
return;
|
||||
|
||||
nvif_mask(memory, offset + 0x1c, 0x80000000, 0x00000000);
|
||||
|
||||
if (!buffer->fault[buffer->fault_nr]) {
|
||||
fault = kmalloc(sizeof(*fault), GFP_KERNEL);
|
||||
if (WARN_ON(!fault)) {
|
||||
nouveau_svm_fault_cancel(svm, inst, hub, gpc, client);
|
||||
return;
|
||||
}
|
||||
buffer->fault[buffer->fault_nr] = fault;
|
||||
}
|
||||
|
||||
fault = buffer->fault[buffer->fault_nr++];
|
||||
fault->inst = inst;
|
||||
fault->addr = (u64)addrhi << 32 | addrlo;
|
||||
fault->time = (u64)timehi << 32 | timelo;
|
||||
fault->engine = engine;
|
||||
fault->gpc = gpc;
|
||||
fault->hub = hub;
|
||||
fault->access = (info & 0x000f0000) >> 16;
|
||||
fault->client = client;
|
||||
fault->fault = (info & 0x0000001f);
|
||||
|
||||
SVM_DBG(svm, "fault %016llx %016llx %02x",
|
||||
fault->inst, fault->addr, fault->access);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_svm_fault(struct nvif_notify *notify)
|
||||
{
|
||||
struct nouveau_svm_fault_buffer *buffer =
|
||||
container_of(notify, typeof(*buffer), notify);
|
||||
struct nouveau_svm *svm =
|
||||
container_of(buffer, typeof(*svm), buffer[buffer->id]);
|
||||
struct nvif_object *device = &svm->drm->client.device.object;
|
||||
struct nouveau_svmm *svmm;
|
||||
struct {
|
||||
struct {
|
||||
struct nvif_ioctl_v0 i;
|
||||
struct nvif_ioctl_mthd_v0 m;
|
||||
struct nvif_vmm_pfnmap_v0 p;
|
||||
} i;
|
||||
u64 phys[16];
|
||||
} args;
|
||||
struct hmm_range range;
|
||||
struct vm_area_struct *vma;
|
||||
u64 inst, start, limit;
|
||||
int fi, fn, pi, fill;
|
||||
int replay = 0, ret;
|
||||
|
||||
/* Parse available fault buffer entries into a cache, and update
|
||||
* the GET pointer so HW can reuse the entries.
|
||||
*/
|
||||
SVM_DBG(svm, "fault handler");
|
||||
if (buffer->get == buffer->put) {
|
||||
buffer->put = nvif_rd32(device, buffer->putaddr);
|
||||
buffer->get = nvif_rd32(device, buffer->getaddr);
|
||||
if (buffer->get == buffer->put)
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
buffer->fault_nr = 0;
|
||||
|
||||
SVM_DBG(svm, "get %08x put %08x", buffer->get, buffer->put);
|
||||
while (buffer->get != buffer->put) {
|
||||
nouveau_svm_fault_cache(svm, buffer, buffer->get * 0x20);
|
||||
if (++buffer->get == buffer->entries)
|
||||
buffer->get = 0;
|
||||
}
|
||||
nvif_wr32(device, buffer->getaddr, buffer->get);
|
||||
SVM_DBG(svm, "%d fault(s) pending", buffer->fault_nr);
|
||||
|
||||
/* Sort parsed faults by instance pointer to prevent unnecessary
|
||||
* instance to SVMM translations, followed by address and access
|
||||
* type to reduce the amount of work when handling the faults.
|
||||
*/
|
||||
sort(buffer->fault, buffer->fault_nr, sizeof(*buffer->fault),
|
||||
nouveau_svm_fault_cmp, NULL);
|
||||
|
||||
/* Lookup SVMM structure for each unique instance pointer. */
|
||||
mutex_lock(&svm->mutex);
|
||||
for (fi = 0, svmm = NULL; fi < buffer->fault_nr; fi++) {
|
||||
if (!svmm || buffer->fault[fi]->inst != inst) {
|
||||
struct nouveau_ivmm *ivmm =
|
||||
nouveau_ivmm_find(svm, buffer->fault[fi]->inst);
|
||||
svmm = ivmm ? ivmm->svmm : NULL;
|
||||
inst = buffer->fault[fi]->inst;
|
||||
SVM_DBG(svm, "inst %016llx -> svm-%p", inst, svmm);
|
||||
}
|
||||
buffer->fault[fi]->svmm = svmm;
|
||||
}
|
||||
mutex_unlock(&svm->mutex);
|
||||
|
||||
/* Process list of faults. */
|
||||
args.i.i.version = 0;
|
||||
args.i.i.type = NVIF_IOCTL_V0_MTHD;
|
||||
args.i.m.version = 0;
|
||||
args.i.m.method = NVIF_VMM_V0_PFNMAP;
|
||||
args.i.p.version = 0;
|
||||
|
||||
for (fi = 0; fn = fi + 1, fi < buffer->fault_nr; fi = fn) {
|
||||
/* Cancel any faults from non-SVM channels. */
|
||||
if (!(svmm = buffer->fault[fi]->svmm)) {
|
||||
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
|
||||
continue;
|
||||
}
|
||||
SVMM_DBG(svmm, "addr %016llx", buffer->fault[fi]->addr);
|
||||
|
||||
/* We try and group handling of faults within a small
|
||||
* window into a single update.
|
||||
*/
|
||||
start = buffer->fault[fi]->addr;
|
||||
limit = start + (ARRAY_SIZE(args.phys) << PAGE_SHIFT);
|
||||
if (start < svmm->unmanaged.limit)
|
||||
limit = min_t(u64, limit, svmm->unmanaged.start);
|
||||
else
|
||||
if (limit > svmm->unmanaged.start)
|
||||
start = max_t(u64, start, svmm->unmanaged.limit);
|
||||
SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit);
|
||||
|
||||
/* Intersect fault window with the CPU VMA, cancelling
|
||||
* the fault if the address is invalid.
|
||||
*/
|
||||
down_read(&svmm->mm->mmap_sem);
|
||||
vma = find_vma_intersection(svmm->mm, start, limit);
|
||||
if (!vma) {
|
||||
SVMM_ERR(svmm, "wndw %016llx-%016llx", start, limit);
|
||||
up_read(&svmm->mm->mmap_sem);
|
||||
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
|
||||
continue;
|
||||
}
|
||||
start = max_t(u64, start, vma->vm_start);
|
||||
limit = min_t(u64, limit, vma->vm_end);
|
||||
SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit);
|
||||
|
||||
if (buffer->fault[fi]->addr != start) {
|
||||
SVMM_ERR(svmm, "addr %016llx", buffer->fault[fi]->addr);
|
||||
up_read(&svmm->mm->mmap_sem);
|
||||
nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Prepare the GPU-side update of all pages within the
|
||||
* fault window, determining required pages and access
|
||||
* permissions based on pending faults.
|
||||
*/
|
||||
args.i.p.page = PAGE_SHIFT;
|
||||
args.i.p.addr = start;
|
||||
for (fn = fi, pi = 0;;) {
|
||||
/* Determine required permissions based on GPU fault
|
||||
* access flags.
|
||||
*XXX: atomic?
|
||||
*/
|
||||
if (buffer->fault[fn]->access != 0 /* READ. */ &&
|
||||
buffer->fault[fn]->access != 3 /* PREFETCH. */) {
|
||||
args.phys[pi++] = NVIF_VMM_PFNMAP_V0_V |
|
||||
NVIF_VMM_PFNMAP_V0_W;
|
||||
} else {
|
||||
args.phys[pi++] = NVIF_VMM_PFNMAP_V0_V;
|
||||
}
|
||||
args.i.p.size = pi << PAGE_SHIFT;
|
||||
|
||||
/* It's okay to skip over duplicate addresses from the
|
||||
* same SVMM as faults are ordered by access type such
|
||||
* that only the first one needs to be handled.
|
||||
*
|
||||
* ie. WRITE faults appear first, thus any handling of
|
||||
* pending READ faults will already be satisfied.
|
||||
*/
|
||||
while (++fn < buffer->fault_nr &&
|
||||
buffer->fault[fn]->svmm == svmm &&
|
||||
buffer->fault[fn ]->addr ==
|
||||
buffer->fault[fn - 1]->addr);
|
||||
|
||||
/* If the next fault is outside the window, or all GPU
|
||||
* faults have been dealt with, we're done here.
|
||||
*/
|
||||
if (fn >= buffer->fault_nr ||
|
||||
buffer->fault[fn]->svmm != svmm ||
|
||||
buffer->fault[fn]->addr >= limit)
|
||||
break;
|
||||
|
||||
/* Fill in the gap between this fault and the next. */
|
||||
fill = (buffer->fault[fn ]->addr -
|
||||
buffer->fault[fn - 1]->addr) >> PAGE_SHIFT;
|
||||
while (--fill)
|
||||
args.phys[pi++] = NVIF_VMM_PFNMAP_V0_NONE;
|
||||
}
|
||||
|
||||
SVMM_DBG(svmm, "wndw %016llx-%016llx covering %d fault(s)",
|
||||
args.i.p.addr,
|
||||
args.i.p.addr + args.i.p.size, fn - fi);
|
||||
|
||||
/* Have HMM fault pages within the fault window to the GPU. */
|
||||
range.vma = vma;
|
||||
range.start = args.i.p.addr;
|
||||
range.end = args.i.p.addr + args.i.p.size;
|
||||
range.pfns = args.phys;
|
||||
range.flags = nouveau_svm_pfn_flags;
|
||||
range.values = nouveau_svm_pfn_values;
|
||||
range.pfn_shift = NVIF_VMM_PFNMAP_V0_ADDR_SHIFT;
|
||||
again:
|
||||
ret = hmm_vma_fault(&range, true);
|
||||
if (ret == 0) {
|
||||
mutex_lock(&svmm->mutex);
|
||||
if (!hmm_vma_range_done(&range)) {
|
||||
mutex_unlock(&svmm->mutex);
|
||||
goto again;
|
||||
}
|
||||
|
||||
nouveau_dmem_convert_pfn(svm->drm, &range);
|
||||
|
||||
svmm->vmm->vmm.object.client->super = true;
|
||||
ret = nvif_object_ioctl(&svmm->vmm->vmm.object,
|
||||
&args, sizeof(args.i) +
|
||||
pi * sizeof(args.phys[0]),
|
||||
NULL);
|
||||
svmm->vmm->vmm.object.client->super = false;
|
||||
mutex_unlock(&svmm->mutex);
|
||||
}
|
||||
up_read(&svmm->mm->mmap_sem);
|
||||
|
||||
/* Cancel any faults in the window whose pages didn't manage
|
||||
* to keep their valid bit, or stay writeable when required.
|
||||
*
|
||||
* If handling failed completely, cancel all faults.
|
||||
*/
|
||||
while (fi < fn) {
|
||||
struct nouveau_svm_fault *fault = buffer->fault[fi++];
|
||||
pi = (fault->addr - range.start) >> PAGE_SHIFT;
|
||||
if (ret ||
|
||||
!(range.pfns[pi] & NVIF_VMM_PFNMAP_V0_V) ||
|
||||
(!(range.pfns[pi] & NVIF_VMM_PFNMAP_V0_W) &&
|
||||
fault->access != 0 && fault->access != 3)) {
|
||||
nouveau_svm_fault_cancel_fault(svm, fault);
|
||||
continue;
|
||||
}
|
||||
replay++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Issue fault replay to the GPU. */
|
||||
if (replay)
|
||||
nouveau_svm_fault_replay(svm);
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_svm_fault_buffer_fini(struct nouveau_svm *svm, int id)
|
||||
{
|
||||
struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
|
||||
nvif_notify_put(&buffer->notify);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_svm_fault_buffer_init(struct nouveau_svm *svm, int id)
|
||||
{
|
||||
struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
|
||||
struct nvif_object *device = &svm->drm->client.device.object;
|
||||
buffer->get = nvif_rd32(device, buffer->getaddr);
|
||||
buffer->put = nvif_rd32(device, buffer->putaddr);
|
||||
SVM_DBG(svm, "get %08x put %08x (init)", buffer->get, buffer->put);
|
||||
return nvif_notify_get(&buffer->notify);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_svm_fault_buffer_dtor(struct nouveau_svm *svm, int id)
|
||||
{
|
||||
struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
|
||||
int i;
|
||||
|
||||
if (buffer->fault) {
|
||||
for (i = 0; buffer->fault[i] && i < buffer->entries; i++)
|
||||
kfree(buffer->fault[i]);
|
||||
kvfree(buffer->fault);
|
||||
}
|
||||
|
||||
nouveau_svm_fault_buffer_fini(svm, id);
|
||||
|
||||
nvif_notify_fini(&buffer->notify);
|
||||
nvif_object_fini(&buffer->object);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_svm_fault_buffer_ctor(struct nouveau_svm *svm, s32 oclass, int id)
|
||||
{
|
||||
struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
|
||||
struct nouveau_drm *drm = svm->drm;
|
||||
struct nvif_object *device = &drm->client.device.object;
|
||||
struct nvif_clb069_v0 args = {};
|
||||
int ret;
|
||||
|
||||
buffer->id = id;
|
||||
|
||||
ret = nvif_object_init(device, 0, oclass, &args, sizeof(args),
|
||||
&buffer->object);
|
||||
if (ret < 0) {
|
||||
SVM_ERR(svm, "Fault buffer allocation failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nvif_object_map(&buffer->object, NULL, 0);
|
||||
buffer->entries = args.entries;
|
||||
buffer->getaddr = args.get;
|
||||
buffer->putaddr = args.put;
|
||||
|
||||
ret = nvif_notify_init(&buffer->object, nouveau_svm_fault, true,
|
||||
NVB069_V0_NTFY_FAULT, NULL, 0, 0,
|
||||
&buffer->notify);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buffer->fault = kvzalloc(sizeof(*buffer->fault) * buffer->entries, GFP_KERNEL);
|
||||
if (!buffer->fault)
|
||||
return -ENOMEM;
|
||||
|
||||
return nouveau_svm_fault_buffer_init(svm, id);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_svm_resume(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_svm *svm = drm->svm;
|
||||
if (svm)
|
||||
nouveau_svm_fault_buffer_init(svm, 0);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_svm_suspend(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_svm *svm = drm->svm;
|
||||
if (svm)
|
||||
nouveau_svm_fault_buffer_fini(svm, 0);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_svm_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
struct nouveau_svm *svm = drm->svm;
|
||||
if (svm) {
|
||||
nouveau_svm_fault_buffer_dtor(svm, 0);
|
||||
kfree(drm->svm);
|
||||
drm->svm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_svm_init(struct nouveau_drm *drm)
|
||||
{
|
||||
static const struct nvif_mclass buffers[] = {
|
||||
{ VOLTA_FAULT_BUFFER_A, 0 },
|
||||
{ MAXWELL_FAULT_BUFFER_A, 0 },
|
||||
{}
|
||||
};
|
||||
struct nouveau_svm *svm;
|
||||
int ret;
|
||||
|
||||
/* Disable on Volta and newer until channel recovery is fixed,
|
||||
* otherwise clients will have a trivial way to trash the GPU
|
||||
* for everyone.
|
||||
*/
|
||||
if (drm->client.device.info.family > NV_DEVICE_INFO_V0_PASCAL)
|
||||
return;
|
||||
|
||||
if (!(drm->svm = svm = kzalloc(sizeof(*drm->svm), GFP_KERNEL)))
|
||||
return;
|
||||
|
||||
drm->svm->drm = drm;
|
||||
mutex_init(&drm->svm->mutex);
|
||||
INIT_LIST_HEAD(&drm->svm->inst);
|
||||
|
||||
ret = nvif_mclass(&drm->client.device.object, buffers);
|
||||
if (ret < 0) {
|
||||
SVM_DBG(svm, "No supported fault buffer class");
|
||||
nouveau_svm_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nouveau_svm_fault_buffer_ctor(svm, buffers[ret].oclass, 0);
|
||||
if (ret) {
|
||||
nouveau_svm_fini(drm);
|
||||
return;
|
||||
}
|
||||
|
||||
SVM_DBG(svm, "Initialised");
|
||||
}
|
48
drivers/gpu/drm/nouveau/nouveau_svm.h
Normal file
48
drivers/gpu/drm/nouveau/nouveau_svm.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __NOUVEAU_SVM_H__
|
||||
#define __NOUVEAU_SVM_H__
|
||||
#include <nvif/os.h>
|
||||
struct drm_device;
|
||||
struct drm_file;
|
||||
struct nouveau_drm;
|
||||
|
||||
struct nouveau_svmm;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM)
|
||||
void nouveau_svm_init(struct nouveau_drm *);
|
||||
void nouveau_svm_fini(struct nouveau_drm *);
|
||||
void nouveau_svm_suspend(struct nouveau_drm *);
|
||||
void nouveau_svm_resume(struct nouveau_drm *);
|
||||
|
||||
int nouveau_svmm_init(struct drm_device *, void *, struct drm_file *);
|
||||
void nouveau_svmm_fini(struct nouveau_svmm **);
|
||||
int nouveau_svmm_join(struct nouveau_svmm *, u64 inst);
|
||||
void nouveau_svmm_part(struct nouveau_svmm *, u64 inst);
|
||||
int nouveau_svmm_bind(struct drm_device *, void *, struct drm_file *);
|
||||
#else /* IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM) */
|
||||
static inline void nouveau_svm_init(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_svm_fini(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_svm_suspend(struct nouveau_drm *drm) {}
|
||||
static inline void nouveau_svm_resume(struct nouveau_drm *drm) {}
|
||||
|
||||
static inline int nouveau_svmm_init(struct drm_device *device, void *p,
|
||||
struct drm_file *file)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline void nouveau_svmm_fini(struct nouveau_svmm **svmmp) {}
|
||||
|
||||
static inline int nouveau_svmm_join(struct nouveau_svmm *svmm, u64 inst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void nouveau_svmm_part(struct nouveau_svmm *svmm, u64 inst) {}
|
||||
|
||||
static inline int nouveau_svmm_bind(struct drm_device *device, void *p,
|
||||
struct drm_file *file)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_DRM_NOUVEAU_SVM) */
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
#include "nouveau_vmm.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_bo.h"
|
||||
#include "nouveau_svm.h"
|
||||
#include "nouveau_mem.h"
|
||||
|
||||
void
|
||||
@ -119,6 +120,7 @@ nouveau_vma_new(struct nouveau_bo *nvbo, struct nouveau_vmm *vmm,
|
||||
void
|
||||
nouveau_vmm_fini(struct nouveau_vmm *vmm)
|
||||
{
|
||||
nouveau_svmm_fini(&vmm->svmm);
|
||||
nvif_vmm_fini(&vmm->vmm);
|
||||
vmm->cli = NULL;
|
||||
}
|
||||
@ -126,7 +128,7 @@ nouveau_vmm_fini(struct nouveau_vmm *vmm)
|
||||
int
|
||||
nouveau_vmm_init(struct nouveau_cli *cli, s32 oclass, struct nouveau_vmm *vmm)
|
||||
{
|
||||
int ret = nvif_vmm_init(&cli->mmu, oclass, PAGE_SIZE, 0, NULL, 0,
|
||||
int ret = nvif_vmm_init(&cli->mmu, oclass, false, PAGE_SIZE, 0, NULL, 0,
|
||||
&vmm->vmm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -25,6 +25,7 @@ void nouveau_vma_unmap(struct nouveau_vma *);
|
||||
struct nouveau_vmm {
|
||||
struct nouveau_cli *cli;
|
||||
struct nvif_vmm vmm;
|
||||
struct nouveau_svmm *svmm;
|
||||
};
|
||||
|
||||
int nouveau_vmm_init(struct nouveau_cli *, s32 oclass, struct nouveau_vmm *);
|
||||
|
@ -109,7 +109,6 @@ nv84_fence_context_del(struct nouveau_channel *chan)
|
||||
int
|
||||
nv84_fence_context_new(struct nouveau_channel *chan)
|
||||
{
|
||||
struct nouveau_cli *cli = (void *)chan->user.client;
|
||||
struct nv84_fence_priv *priv = chan->drm->fence;
|
||||
struct nv84_fence_chan *fctx;
|
||||
int ret;
|
||||
@ -127,7 +126,7 @@ nv84_fence_context_new(struct nouveau_channel *chan)
|
||||
fctx->base.sequence = nv84_fence_read(chan);
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
ret = nouveau_vma_new(priv->bo, &cli->vmm, &fctx->vma);
|
||||
ret = nouveau_vma_new(priv->bo, chan->vmm, &fctx->vma);
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
if (ret)
|
||||
|
@ -34,7 +34,7 @@ int
|
||||
nvif_disp_ctor(struct nvif_device *device, s32 oclass, struct nvif_disp *disp)
|
||||
{
|
||||
static const struct nvif_mclass disps[] = {
|
||||
{ TU104_DISP, -1 },
|
||||
{ TU102_DISP, -1 },
|
||||
{ GV100_DISP, -1 },
|
||||
{ GP102_DISP, -1 },
|
||||
{ GP100_DISP, -1 },
|
||||
|
@ -112,8 +112,8 @@ nvif_vmm_fini(struct nvif_vmm *vmm)
|
||||
}
|
||||
|
||||
int
|
||||
nvif_vmm_init(struct nvif_mmu *mmu, s32 oclass, u64 addr, u64 size,
|
||||
void *argv, u32 argc, struct nvif_vmm *vmm)
|
||||
nvif_vmm_init(struct nvif_mmu *mmu, s32 oclass, bool managed, u64 addr,
|
||||
u64 size, void *argv, u32 argc, struct nvif_vmm *vmm)
|
||||
{
|
||||
struct nvif_vmm_v0 *args;
|
||||
u32 argn = sizeof(*args) + argc;
|
||||
@ -125,6 +125,7 @@ nvif_vmm_init(struct nvif_mmu *mmu, s32 oclass, u64 addr, u64 size,
|
||||
if (!(args = kmalloc(argn, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
args->version = 0;
|
||||
args->managed = managed;
|
||||
args->addr = addr;
|
||||
args->size = size;
|
||||
memcpy(args->data, argv, argc);
|
||||
|
@ -39,6 +39,7 @@ nvkm_subdev_name[NVKM_SUBDEV_NR] = {
|
||||
[NVKM_SUBDEV_FB ] = "fb",
|
||||
[NVKM_SUBDEV_FUSE ] = "fuse",
|
||||
[NVKM_SUBDEV_GPIO ] = "gpio",
|
||||
[NVKM_SUBDEV_GSP ] = "gsp",
|
||||
[NVKM_SUBDEV_I2C ] = "i2c",
|
||||
[NVKM_SUBDEV_IBUS ] = "priv",
|
||||
[NVKM_SUBDEV_ICCSENSE] = "iccsense",
|
||||
|
@ -6,4 +6,4 @@ nvkm-y += nvkm/engine/ce/gm200.o
|
||||
nvkm-y += nvkm/engine/ce/gp100.o
|
||||
nvkm-y += nvkm/engine/ce/gp102.o
|
||||
nvkm-y += nvkm/engine/ce/gv100.o
|
||||
nvkm-y += nvkm/engine/ce/tu104.o
|
||||
nvkm-y += nvkm/engine/ce/tu102.o
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <nvif/class.h>
|
||||
|
||||
static const struct nvkm_engine_func
|
||||
tu104_ce = {
|
||||
tu102_ce = {
|
||||
.intr = gp100_ce_intr,
|
||||
.sclass = {
|
||||
{ -1, -1, TURING_DMA_COPY_A },
|
||||
@ -33,8 +33,8 @@ tu104_ce = {
|
||||
};
|
||||
|
||||
int
|
||||
tu104_ce_new(struct nvkm_device *device, int index,
|
||||
tu102_ce_new(struct nvkm_device *device, int index,
|
||||
struct nvkm_engine **pengine)
|
||||
{
|
||||
return nvkm_engine_new_(&tu104_ce, device, index, true, pengine);
|
||||
return nvkm_engine_new_(&tu102_ce, device, index, true, pengine);
|
||||
}
|
@ -1613,7 +1613,7 @@ nvd7_chipset = {
|
||||
.pci = gf106_pci_new,
|
||||
.therm = gf119_therm_new,
|
||||
.timer = nv41_timer_new,
|
||||
.volt = gf100_volt_new,
|
||||
.volt = gf117_volt_new,
|
||||
.ce[0] = gf100_ce_new,
|
||||
.disp = gf119_disp_new,
|
||||
.dma = gf119_dma_new,
|
||||
@ -2405,6 +2405,7 @@ nv140_chipset = {
|
||||
.fb = gv100_fb_new,
|
||||
.fuse = gm107_fuse_new,
|
||||
.gpio = gk104_gpio_new,
|
||||
.gsp = gv100_gsp_new,
|
||||
.i2c = gm200_i2c_new,
|
||||
.ibus = gm200_ibus_new,
|
||||
.imem = nv50_instmem_new,
|
||||
@ -2437,97 +2438,106 @@ nv140_chipset = {
|
||||
static const struct nvkm_device_chip
|
||||
nv162_chipset = {
|
||||
.name = "TU102",
|
||||
.bar = tu104_bar_new,
|
||||
.bar = tu102_bar_new,
|
||||
.bios = nvkm_bios_new,
|
||||
.bus = gf100_bus_new,
|
||||
.devinit = tu104_devinit_new,
|
||||
.fault = tu104_fault_new,
|
||||
.devinit = tu102_devinit_new,
|
||||
.fault = tu102_fault_new,
|
||||
.fb = gv100_fb_new,
|
||||
.fuse = gm107_fuse_new,
|
||||
.gpio = gk104_gpio_new,
|
||||
.gsp = gv100_gsp_new,
|
||||
.i2c = gm200_i2c_new,
|
||||
.ibus = gm200_ibus_new,
|
||||
.imem = nv50_instmem_new,
|
||||
.ltc = gp102_ltc_new,
|
||||
.mc = tu104_mc_new,
|
||||
.mmu = tu104_mmu_new,
|
||||
.mc = tu102_mc_new,
|
||||
.mmu = tu102_mmu_new,
|
||||
.pci = gp100_pci_new,
|
||||
.pmu = gp102_pmu_new,
|
||||
.therm = gp100_therm_new,
|
||||
.timer = gk20a_timer_new,
|
||||
.top = gk104_top_new,
|
||||
.ce[0] = tu104_ce_new,
|
||||
.ce[1] = tu104_ce_new,
|
||||
.ce[2] = tu104_ce_new,
|
||||
.ce[3] = tu104_ce_new,
|
||||
.ce[4] = tu104_ce_new,
|
||||
.disp = tu104_disp_new,
|
||||
.ce[0] = tu102_ce_new,
|
||||
.ce[1] = tu102_ce_new,
|
||||
.ce[2] = tu102_ce_new,
|
||||
.ce[3] = tu102_ce_new,
|
||||
.ce[4] = tu102_ce_new,
|
||||
.disp = tu102_disp_new,
|
||||
.dma = gv100_dma_new,
|
||||
.fifo = tu104_fifo_new,
|
||||
.fifo = tu102_fifo_new,
|
||||
.nvdec[0] = gp102_nvdec_new,
|
||||
.sec2 = tu102_sec2_new,
|
||||
};
|
||||
|
||||
static const struct nvkm_device_chip
|
||||
nv164_chipset = {
|
||||
.name = "TU104",
|
||||
.bar = tu104_bar_new,
|
||||
.bar = tu102_bar_new,
|
||||
.bios = nvkm_bios_new,
|
||||
.bus = gf100_bus_new,
|
||||
.devinit = tu104_devinit_new,
|
||||
.fault = tu104_fault_new,
|
||||
.devinit = tu102_devinit_new,
|
||||
.fault = tu102_fault_new,
|
||||
.fb = gv100_fb_new,
|
||||
.fuse = gm107_fuse_new,
|
||||
.gpio = gk104_gpio_new,
|
||||
.gsp = gv100_gsp_new,
|
||||
.i2c = gm200_i2c_new,
|
||||
.ibus = gm200_ibus_new,
|
||||
.imem = nv50_instmem_new,
|
||||
.ltc = gp102_ltc_new,
|
||||
.mc = tu104_mc_new,
|
||||
.mmu = tu104_mmu_new,
|
||||
.mc = tu102_mc_new,
|
||||
.mmu = tu102_mmu_new,
|
||||
.pci = gp100_pci_new,
|
||||
.pmu = gp102_pmu_new,
|
||||
.therm = gp100_therm_new,
|
||||
.timer = gk20a_timer_new,
|
||||
.top = gk104_top_new,
|
||||
.ce[0] = tu104_ce_new,
|
||||
.ce[1] = tu104_ce_new,
|
||||
.ce[2] = tu104_ce_new,
|
||||
.ce[3] = tu104_ce_new,
|
||||
.ce[4] = tu104_ce_new,
|
||||
.disp = tu104_disp_new,
|
||||
.ce[0] = tu102_ce_new,
|
||||
.ce[1] = tu102_ce_new,
|
||||
.ce[2] = tu102_ce_new,
|
||||
.ce[3] = tu102_ce_new,
|
||||
.ce[4] = tu102_ce_new,
|
||||
.disp = tu102_disp_new,
|
||||
.dma = gv100_dma_new,
|
||||
.fifo = tu104_fifo_new,
|
||||
.fifo = tu102_fifo_new,
|
||||
.nvdec[0] = gp102_nvdec_new,
|
||||
.sec2 = tu102_sec2_new,
|
||||
};
|
||||
|
||||
static const struct nvkm_device_chip
|
||||
nv166_chipset = {
|
||||
.name = "TU106",
|
||||
.bar = tu104_bar_new,
|
||||
.bar = tu102_bar_new,
|
||||
.bios = nvkm_bios_new,
|
||||
.bus = gf100_bus_new,
|
||||
.devinit = tu104_devinit_new,
|
||||
.fault = tu104_fault_new,
|
||||
.devinit = tu102_devinit_new,
|
||||
.fault = tu102_fault_new,
|
||||
.fb = gv100_fb_new,
|
||||
.fuse = gm107_fuse_new,
|
||||
.gpio = gk104_gpio_new,
|
||||
.gsp = gv100_gsp_new,
|
||||
.i2c = gm200_i2c_new,
|
||||
.ibus = gm200_ibus_new,
|
||||
.imem = nv50_instmem_new,
|
||||
.ltc = gp102_ltc_new,
|
||||
.mc = tu104_mc_new,
|
||||
.mmu = tu104_mmu_new,
|
||||
.mc = tu102_mc_new,
|
||||
.mmu = tu102_mmu_new,
|
||||
.pci = gp100_pci_new,
|
||||
.pmu = gp102_pmu_new,
|
||||
.therm = gp100_therm_new,
|
||||
.timer = gk20a_timer_new,
|
||||
.top = gk104_top_new,
|
||||
.ce[0] = tu104_ce_new,
|
||||
.ce[1] = tu104_ce_new,
|
||||
.ce[2] = tu104_ce_new,
|
||||
.ce[3] = tu104_ce_new,
|
||||
.ce[4] = tu104_ce_new,
|
||||
.disp = tu104_disp_new,
|
||||
.ce[0] = tu102_ce_new,
|
||||
.ce[1] = tu102_ce_new,
|
||||
.ce[2] = tu102_ce_new,
|
||||
.ce[3] = tu102_ce_new,
|
||||
.ce[4] = tu102_ce_new,
|
||||
.disp = tu102_disp_new,
|
||||
.dma = gv100_dma_new,
|
||||
.fifo = tu104_fifo_new,
|
||||
.fifo = tu102_fifo_new,
|
||||
.nvdec[0] = gp102_nvdec_new,
|
||||
.sec2 = tu102_sec2_new,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -2567,6 +2577,7 @@ nvkm_device_subdev(struct nvkm_device *device, int index)
|
||||
_(FB , device->fb , &device->fb->subdev);
|
||||
_(FUSE , device->fuse , &device->fuse->subdev);
|
||||
_(GPIO , device->gpio , &device->gpio->subdev);
|
||||
_(GSP , device->gsp , &device->gsp->subdev);
|
||||
_(I2C , device->i2c , &device->i2c->subdev);
|
||||
_(IBUS , device->ibus , device->ibus);
|
||||
_(ICCSENSE, device->iccsense, &device->iccsense->subdev);
|
||||
@ -3050,6 +3061,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
|
||||
_(NVKM_SUBDEV_FB , fb);
|
||||
_(NVKM_SUBDEV_FUSE , fuse);
|
||||
_(NVKM_SUBDEV_GPIO , gpio);
|
||||
_(NVKM_SUBDEV_GSP , gsp);
|
||||
_(NVKM_SUBDEV_I2C , i2c);
|
||||
_(NVKM_SUBDEV_IBUS , ibus);
|
||||
_(NVKM_SUBDEV_ICCSENSE, iccsense);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <subdev/fb.h>
|
||||
#include <subdev/fuse.h>
|
||||
#include <subdev/gpio.h>
|
||||
#include <subdev/gsp.h>
|
||||
#include <subdev/i2c.h>
|
||||
#include <subdev/ibus.h>
|
||||
#include <subdev/iccsense.h>
|
||||
|
@ -365,16 +365,15 @@ nvkm_udevice_child_get(struct nvkm_object *object, int index,
|
||||
}
|
||||
|
||||
if (!sclass) {
|
||||
switch (index) {
|
||||
case 0: sclass = &nvkm_control_oclass; break;
|
||||
case 1:
|
||||
if (!device->mmu)
|
||||
return -EINVAL;
|
||||
if (index-- == 0)
|
||||
sclass = &nvkm_control_oclass;
|
||||
else if (device->mmu && index-- == 0)
|
||||
sclass = &device->mmu->user;
|
||||
break;
|
||||
default:
|
||||
else if (device->fault && index-- == 0)
|
||||
sclass = &device->fault->user;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
oclass->base = sclass->base;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ nvkm-y += nvkm/engine/disp/gm200.o
|
||||
nvkm-y += nvkm/engine/disp/gp100.o
|
||||
nvkm-y += nvkm/engine/disp/gp102.o
|
||||
nvkm-y += nvkm/engine/disp/gv100.o
|
||||
nvkm-y += nvkm/engine/disp/tu104.o
|
||||
nvkm-y += nvkm/engine/disp/tu102.o
|
||||
nvkm-y += nvkm/engine/disp/vga.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/head.o
|
||||
@ -39,7 +39,7 @@ nvkm-y += nvkm/engine/disp/sorgk104.o
|
||||
nvkm-y += nvkm/engine/disp/sorgm107.o
|
||||
nvkm-y += nvkm/engine/disp/sorgm200.o
|
||||
nvkm-y += nvkm/engine/disp/sorgv100.o
|
||||
nvkm-y += nvkm/engine/disp/sortu104.o
|
||||
nvkm-y += nvkm/engine/disp/sortu102.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/outp.o
|
||||
nvkm-y += nvkm/engine/disp/dp.o
|
||||
@ -71,7 +71,7 @@ nvkm-y += nvkm/engine/disp/rootgm200.o
|
||||
nvkm-y += nvkm/engine/disp/rootgp100.o
|
||||
nvkm-y += nvkm/engine/disp/rootgp102.o
|
||||
nvkm-y += nvkm/engine/disp/rootgv100.o
|
||||
nvkm-y += nvkm/engine/disp/roottu104.o
|
||||
nvkm-y += nvkm/engine/disp/roottu102.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/channv50.o
|
||||
nvkm-y += nvkm/engine/disp/changf119.o
|
||||
|
@ -91,15 +91,21 @@ gf119_disp_intr_error(struct nv50_disp *disp, int chid)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
u32 mthd = nvkm_rd32(device, 0x6101f0 + (chid * 12));
|
||||
u32 stat = nvkm_rd32(device, 0x6101f0 + (chid * 12));
|
||||
u32 type = (stat & 0x00007000) >> 12;
|
||||
u32 mthd = (stat & 0x00000ffc);
|
||||
u32 data = nvkm_rd32(device, 0x6101f4 + (chid * 12));
|
||||
u32 unkn = nvkm_rd32(device, 0x6101f8 + (chid * 12));
|
||||
u32 code = nvkm_rd32(device, 0x6101f8 + (chid * 12));
|
||||
const struct nvkm_enum *reason =
|
||||
nvkm_enum_find(nv50_disp_intr_error_type, type);
|
||||
|
||||
nvkm_error(subdev, "chid %d mthd %04x data %08x %08x %08x\n",
|
||||
chid, (mthd & 0x0000ffc), data, mthd, unkn);
|
||||
nvkm_error(subdev, "chid %d stat %08x reason %d [%s] mthd %04x "
|
||||
"data %08x code %08x\n",
|
||||
chid, stat, type, reason ? reason->name : "",
|
||||
mthd, data, code);
|
||||
|
||||
if (chid < ARRAY_SIZE(disp->chan)) {
|
||||
switch (mthd & 0xffc) {
|
||||
switch (mthd) {
|
||||
case 0x0080:
|
||||
nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);
|
||||
break;
|
||||
|
@ -103,10 +103,13 @@ gv100_disp_exception(struct nv50_disp *disp, int chid)
|
||||
u32 mthd = (stat & 0x00000fff) << 2;
|
||||
u32 data = nvkm_rd32(device, 0x611024 + (chid * 12));
|
||||
u32 code = nvkm_rd32(device, 0x611028 + (chid * 12));
|
||||
const struct nvkm_enum *reason =
|
||||
nvkm_enum_find(nv50_disp_intr_error_type, type);
|
||||
|
||||
nvkm_error(subdev, "chid %d %08x [type %d mthd %04x] "
|
||||
nvkm_error(subdev, "chid %d stat %08x reason %d [%s] mthd %04x "
|
||||
"data %08x code %08x\n",
|
||||
chid, stat, type, mthd, data, code);
|
||||
chid, stat, type, reason ? reason->name : "",
|
||||
mthd, data, code);
|
||||
|
||||
if (chid < ARRAY_SIZE(disp->chan) && disp->chan[chid]) {
|
||||
switch (mthd) {
|
||||
|
@ -201,5 +201,5 @@ int gm200_sor_new(struct nvkm_disp *, int);
|
||||
int gv100_sor_cnt(struct nvkm_disp *, unsigned long *);
|
||||
int gv100_sor_new(struct nvkm_disp *, int);
|
||||
|
||||
int tu104_sor_new(struct nvkm_disp *, int);
|
||||
int tu102_sor_new(struct nvkm_disp *, int);
|
||||
#endif
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <core/enum.h>
|
||||
#include <core/ramht.h>
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/disp.h>
|
||||
@ -593,12 +592,15 @@ nv50_disp_super(struct work_struct *work)
|
||||
nvkm_wr32(device, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
static const struct nvkm_enum
|
||||
const struct nvkm_enum
|
||||
nv50_disp_intr_error_type[] = {
|
||||
{ 3, "ILLEGAL_MTHD" },
|
||||
{ 4, "INVALID_VALUE" },
|
||||
{ 0, "NONE" },
|
||||
{ 1, "PUSHBUFFER_ERR" },
|
||||
{ 2, "TRAP" },
|
||||
{ 3, "RESERVED_METHOD" },
|
||||
{ 4, "INVALID_ARG" },
|
||||
{ 5, "INVALID_STATE" },
|
||||
{ 7, "INVALID_HANDLE" },
|
||||
{ 7, "UNRESOLVABLE_HANDLE" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "priv.h"
|
||||
struct nvkm_head;
|
||||
|
||||
#include <core/enum.h>
|
||||
|
||||
struct nv50_disp {
|
||||
const struct nv50_disp_func *func;
|
||||
struct nvkm_disp base;
|
||||
@ -71,6 +73,7 @@ int nv50_disp_init(struct nv50_disp *);
|
||||
void nv50_disp_fini(struct nv50_disp *);
|
||||
void nv50_disp_intr(struct nv50_disp *);
|
||||
void nv50_disp_super(struct work_struct *);
|
||||
extern const struct nvkm_enum nv50_disp_intr_error_type[];
|
||||
|
||||
int gf119_disp_init(struct nv50_disp *);
|
||||
void gf119_disp_fini(struct nv50_disp *);
|
||||
|
@ -37,5 +37,5 @@ extern const struct nvkm_disp_oclass gm200_disp_root_oclass;
|
||||
extern const struct nvkm_disp_oclass gp100_disp_root_oclass;
|
||||
extern const struct nvkm_disp_oclass gp102_disp_root_oclass;
|
||||
extern const struct nvkm_disp_oclass gv100_disp_root_oclass;
|
||||
extern const struct nvkm_disp_oclass tu104_disp_root_oclass;
|
||||
extern const struct nvkm_disp_oclass tu102_disp_root_oclass;
|
||||
#endif
|
||||
|
@ -25,28 +25,28 @@
|
||||
#include <nvif/class.h>
|
||||
|
||||
static const struct nv50_disp_root_func
|
||||
tu104_disp_root = {
|
||||
tu102_disp_root = {
|
||||
.user = {
|
||||
{{0,0,TU104_DISP_CURSOR }, gv100_disp_curs_new },
|
||||
{{0,0,TU104_DISP_WINDOW_IMM_CHANNEL_DMA}, gv100_disp_wimm_new },
|
||||
{{0,0,TU104_DISP_CORE_CHANNEL_DMA }, gv100_disp_core_new },
|
||||
{{0,0,TU104_DISP_WINDOW_CHANNEL_DMA }, gv100_disp_wndw_new },
|
||||
{{0,0,TU102_DISP_CURSOR }, gv100_disp_curs_new },
|
||||
{{0,0,TU102_DISP_WINDOW_IMM_CHANNEL_DMA}, gv100_disp_wimm_new },
|
||||
{{0,0,TU102_DISP_CORE_CHANNEL_DMA }, gv100_disp_core_new },
|
||||
{{0,0,TU102_DISP_WINDOW_CHANNEL_DMA }, gv100_disp_wndw_new },
|
||||
{}
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
tu104_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
|
||||
tu102_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
|
||||
void *data, u32 size, struct nvkm_object **pobject)
|
||||
{
|
||||
return nv50_disp_root_new_(&tu104_disp_root, disp, oclass,
|
||||
return nv50_disp_root_new_(&tu102_disp_root, disp, oclass,
|
||||
data, size, pobject);
|
||||
}
|
||||
|
||||
const struct nvkm_disp_oclass
|
||||
tu104_disp_root_oclass = {
|
||||
.base.oclass = TU104_DISP,
|
||||
tu102_disp_root_oclass = {
|
||||
.base.oclass = TU102_DISP,
|
||||
.base.minver = -1,
|
||||
.base.maxver = -1,
|
||||
.ctor = tu104_disp_root_new,
|
||||
.ctor = tu102_disp_root_new,
|
||||
};
|
@ -24,7 +24,7 @@
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
tu104_sor_dp_vcpi(struct nvkm_ior *sor, int head,
|
||||
tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head,
|
||||
u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
@ -35,7 +35,7 @@ tu104_sor_dp_vcpi(struct nvkm_ior *sor, int head,
|
||||
}
|
||||
|
||||
static int
|
||||
tu104_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
||||
tu102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
@ -62,7 +62,7 @@ tu104_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
tu104_sor = {
|
||||
tu102_sor = {
|
||||
.route = {
|
||||
.get = gm200_sor_route_get,
|
||||
.set = gm200_sor_route_set,
|
||||
@ -75,11 +75,11 @@ tu104_sor = {
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 0, 1, 2, 3 },
|
||||
.links = tu104_sor_dp_links,
|
||||
.links = tu102_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = gm107_sor_dp_pattern,
|
||||
.drive = gm200_sor_dp_drive,
|
||||
.vcpi = tu104_sor_dp_vcpi,
|
||||
.vcpi = tu102_sor_dp_vcpi,
|
||||
.audio = gv100_sor_dp_audio,
|
||||
.audio_sym = gv100_sor_dp_audio_sym,
|
||||
.watermark = gv100_sor_dp_watermark,
|
||||
@ -91,7 +91,7 @@ tu104_sor = {
|
||||
};
|
||||
|
||||
int
|
||||
tu104_sor_new(struct nvkm_disp *disp, int id)
|
||||
tu102_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_ior_new_(&tu104_sor, disp, SOR, id);
|
||||
return nvkm_ior_new_(&tu102_sor, disp, SOR, id);
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static int
|
||||
tu104_disp_init(struct nv50_disp *disp)
|
||||
tu102_disp_init(struct nv50_disp *disp)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
struct nvkm_head *head;
|
||||
@ -132,21 +132,21 @@ tu104_disp_init(struct nv50_disp *disp)
|
||||
}
|
||||
|
||||
static const struct nv50_disp_func
|
||||
tu104_disp = {
|
||||
.init = tu104_disp_init,
|
||||
tu102_disp = {
|
||||
.init = tu102_disp_init,
|
||||
.fini = gv100_disp_fini,
|
||||
.intr = gv100_disp_intr,
|
||||
.uevent = &gv100_disp_chan_uevent,
|
||||
.super = gv100_disp_super,
|
||||
.root = &tu104_disp_root_oclass,
|
||||
.root = &tu102_disp_root_oclass,
|
||||
.wndw = { .cnt = gv100_disp_wndw_cnt },
|
||||
.head = { .cnt = gv100_head_cnt, .new = gv100_head_new },
|
||||
.sor = { .cnt = gv100_sor_cnt, .new = tu104_sor_new },
|
||||
.sor = { .cnt = gv100_sor_cnt, .new = tu102_sor_new },
|
||||
.ramht_size = 0x2000,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
|
||||
tu102_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
|
||||
{
|
||||
return nv50_disp_new_(&tu104_disp, device, index, pdisp);
|
||||
return nv50_disp_new_(&tu102_disp, device, index, pdisp);
|
||||
}
|
@ -16,7 +16,7 @@ nvkm-y += nvkm/engine/fifo/gm20b.o
|
||||
nvkm-y += nvkm/engine/fifo/gp100.o
|
||||
nvkm-y += nvkm/engine/fifo/gp10b.o
|
||||
nvkm-y += nvkm/engine/fifo/gv100.o
|
||||
nvkm-y += nvkm/engine/fifo/tu104.o
|
||||
nvkm-y += nvkm/engine/fifo/tu102.o
|
||||
|
||||
nvkm-y += nvkm/engine/fifo/chan.o
|
||||
nvkm-y += nvkm/engine/fifo/channv50.o
|
||||
@ -34,7 +34,7 @@ nvkm-y += nvkm/engine/fifo/gpfifog84.o
|
||||
nvkm-y += nvkm/engine/fifo/gpfifogf100.o
|
||||
nvkm-y += nvkm/engine/fifo/gpfifogk104.o
|
||||
nvkm-y += nvkm/engine/fifo/gpfifogv100.o
|
||||
nvkm-y += nvkm/engine/fifo/gpfifotu104.o
|
||||
nvkm-y += nvkm/engine/fifo/gpfifotu102.o
|
||||
|
||||
nvkm-y += nvkm/engine/fifo/usergv100.o
|
||||
nvkm-y += nvkm/engine/fifo/usertu104.o
|
||||
nvkm-y += nvkm/engine/fifo/usertu102.o
|
||||
|
@ -47,6 +47,6 @@ int gv100_fifo_gpfifo_engine_init(struct nvkm_fifo_chan *,
|
||||
int gv100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *,
|
||||
struct nvkm_engine *, bool);
|
||||
|
||||
int tu104_fifo_gpfifo_new(struct gk104_fifo *, const struct nvkm_oclass *,
|
||||
int tu102_fifo_gpfifo_new(struct gk104_fifo *, const struct nvkm_oclass *,
|
||||
void *data, u32 size, struct nvkm_object **);
|
||||
#endif
|
||||
|
@ -29,14 +29,14 @@
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
static u32
|
||||
tu104_fifo_gpfifo_submit_token(struct nvkm_fifo_chan *base)
|
||||
tu102_fifo_gpfifo_submit_token(struct nvkm_fifo_chan *base)
|
||||
{
|
||||
struct gk104_fifo_chan *chan = gk104_fifo_chan(base);
|
||||
return (chan->runl << 16) | chan->base.chid;
|
||||
}
|
||||
|
||||
static const struct nvkm_fifo_chan_func
|
||||
tu104_fifo_gpfifo = {
|
||||
tu102_fifo_gpfifo = {
|
||||
.dtor = gk104_fifo_gpfifo_dtor,
|
||||
.init = gk104_fifo_gpfifo_init,
|
||||
.fini = gk104_fifo_gpfifo_fini,
|
||||
@ -45,11 +45,11 @@ tu104_fifo_gpfifo = {
|
||||
.engine_dtor = gk104_fifo_gpfifo_engine_dtor,
|
||||
.engine_init = gv100_fifo_gpfifo_engine_init,
|
||||
.engine_fini = gv100_fifo_gpfifo_engine_fini,
|
||||
.submit_token = tu104_fifo_gpfifo_submit_token,
|
||||
.submit_token = tu102_fifo_gpfifo_submit_token,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass,
|
||||
tu102_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass,
|
||||
void *data, u32 size, struct nvkm_object **pobject)
|
||||
{
|
||||
struct nvkm_object *parent = oclass->parent;
|
||||
@ -67,7 +67,7 @@ tu104_fifo_gpfifo_new(struct gk104_fifo *fifo, const struct nvkm_oclass *oclass,
|
||||
args->v0.ilength, args->v0.runlist, args->v0.priv);
|
||||
if (args->v0.priv && !oclass->client->super)
|
||||
return -EINVAL;
|
||||
return gv100_fifo_gpfifo_new_(&tu104_fifo_gpfifo, fifo,
|
||||
return gv100_fifo_gpfifo_new_(&tu102_fifo_gpfifo, fifo,
|
||||
&args->v0.runlist,
|
||||
&args->v0.chid,
|
||||
args->v0.vmm,
|
@ -29,7 +29,7 @@
|
||||
#include <nvif/class.h>
|
||||
|
||||
static void
|
||||
tu104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl,
|
||||
tu102_fifo_runlist_commit(struct gk104_fifo *fifo, int runl,
|
||||
struct nvkm_memory *mem, int nr)
|
||||
{
|
||||
struct nvkm_device *device = fifo->base.engine.subdev.device;
|
||||
@ -44,15 +44,15 @@ tu104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl,
|
||||
}
|
||||
|
||||
const struct gk104_fifo_runlist_func
|
||||
tu104_fifo_runlist = {
|
||||
tu102_fifo_runlist = {
|
||||
.size = 16,
|
||||
.cgrp = gv100_fifo_runlist_cgrp,
|
||||
.chan = gv100_fifo_runlist_chan,
|
||||
.commit = tu104_fifo_runlist_commit,
|
||||
.commit = tu102_fifo_runlist_commit,
|
||||
};
|
||||
|
||||
static const struct nvkm_enum
|
||||
tu104_fifo_fault_engine[] = {
|
||||
tu102_fifo_fault_engine[] = {
|
||||
{ 0x01, "DISPLAY" },
|
||||
{ 0x03, "PTP" },
|
||||
{ 0x06, "PWR_PMU" },
|
||||
@ -80,7 +80,7 @@ tu104_fifo_fault_engine[] = {
|
||||
};
|
||||
|
||||
static void
|
||||
tu104_fifo_pbdma_init(struct gk104_fifo *fifo)
|
||||
tu102_fifo_pbdma_init(struct gk104_fifo *fifo)
|
||||
{
|
||||
struct nvkm_device *device = fifo->base.engine.subdev.device;
|
||||
const u32 mask = (1 << fifo->pbdma_nr) - 1;
|
||||
@ -89,28 +89,28 @@ tu104_fifo_pbdma_init(struct gk104_fifo *fifo)
|
||||
}
|
||||
|
||||
static const struct gk104_fifo_pbdma_func
|
||||
tu104_fifo_pbdma = {
|
||||
tu102_fifo_pbdma = {
|
||||
.nr = gm200_fifo_pbdma_nr,
|
||||
.init = tu104_fifo_pbdma_init,
|
||||
.init = tu102_fifo_pbdma_init,
|
||||
.init_timeout = gk208_fifo_pbdma_init_timeout,
|
||||
};
|
||||
|
||||
static const struct gk104_fifo_func
|
||||
tu104_fifo = {
|
||||
.pbdma = &tu104_fifo_pbdma,
|
||||
tu102_fifo = {
|
||||
.pbdma = &tu102_fifo_pbdma,
|
||||
.fault.access = gv100_fifo_fault_access,
|
||||
.fault.engine = tu104_fifo_fault_engine,
|
||||
.fault.engine = tu102_fifo_fault_engine,
|
||||
.fault.reason = gv100_fifo_fault_reason,
|
||||
.fault.hubclient = gv100_fifo_fault_hubclient,
|
||||
.fault.gpcclient = gv100_fifo_fault_gpcclient,
|
||||
.runlist = &tu104_fifo_runlist,
|
||||
.user = {{-1,-1,VOLTA_USERMODE_A }, tu104_fifo_user_new },
|
||||
.chan = {{ 0, 0,TURING_CHANNEL_GPFIFO_A}, tu104_fifo_gpfifo_new },
|
||||
.runlist = &tu102_fifo_runlist,
|
||||
.user = {{-1,-1,VOLTA_USERMODE_A }, tu102_fifo_user_new },
|
||||
.chan = {{ 0, 0,TURING_CHANNEL_GPFIFO_A}, tu102_fifo_gpfifo_new },
|
||||
.cgrp_force = true,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_fifo_new(struct nvkm_device *device, int index, struct nvkm_fifo **pfifo)
|
||||
tu102_fifo_new(struct nvkm_device *device, int index, struct nvkm_fifo **pfifo)
|
||||
{
|
||||
return gk104_fifo_new_(&tu104_fifo, device, index, 4096, pfifo);
|
||||
return gk104_fifo_new_(&tu102_fifo, device, index, 4096, pfifo);
|
||||
}
|
@ -3,6 +3,6 @@
|
||||
#include "priv.h"
|
||||
int gv100_fifo_user_new(const struct nvkm_oclass *, void *, u32,
|
||||
struct nvkm_object **);
|
||||
int tu104_fifo_user_new(const struct nvkm_oclass *, void *, u32,
|
||||
int tu102_fifo_user_new(const struct nvkm_oclass *, void *, u32,
|
||||
struct nvkm_object **);
|
||||
#endif
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "user.h"
|
||||
|
||||
static int
|
||||
tu104_fifo_user_map(struct nvkm_object *object, void *argv, u32 argc,
|
||||
tu102_fifo_user_map(struct nvkm_object *object, void *argv, u32 argc,
|
||||
enum nvkm_object_map *type, u64 *addr, u64 *size)
|
||||
{
|
||||
struct nvkm_device *device = object->engine->subdev.device;
|
||||
@ -33,13 +33,13 @@ tu104_fifo_user_map(struct nvkm_object *object, void *argv, u32 argc,
|
||||
}
|
||||
|
||||
static const struct nvkm_object_func
|
||||
tu104_fifo_user = {
|
||||
.map = tu104_fifo_user_map,
|
||||
tu102_fifo_user = {
|
||||
.map = tu102_fifo_user_map,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_fifo_user_new(const struct nvkm_oclass *oclass, void *argv, u32 argc,
|
||||
tu102_fifo_user_new(const struct nvkm_oclass *oclass, void *argv, u32 argc,
|
||||
struct nvkm_object **pobject)
|
||||
{
|
||||
return nvkm_object_new_(&tu104_fifo_user, oclass, argv, argc, pobject);
|
||||
return nvkm_object_new_(&tu102_fifo_user, oclass, argv, argc, pobject);
|
||||
}
|
@ -25,6 +25,33 @@
|
||||
|
||||
#include <engine/fifo.h>
|
||||
|
||||
u32
|
||||
nvkm_gr_ctxsw_inst(struct nvkm_device *device)
|
||||
{
|
||||
struct nvkm_gr *gr = device->gr;
|
||||
if (gr && gr->func->ctxsw.inst)
|
||||
return gr->func->ctxsw.inst(gr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_gr_ctxsw_resume(struct nvkm_device *device)
|
||||
{
|
||||
struct nvkm_gr *gr = device->gr;
|
||||
if (gr && gr->func->ctxsw.resume)
|
||||
return gr->func->ctxsw.resume(gr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_gr_ctxsw_pause(struct nvkm_device *device)
|
||||
{
|
||||
struct nvkm_gr *gr = device->gr;
|
||||
if (gr && gr->func->ctxsw.pause)
|
||||
return gr->func->ctxsw.pause(gr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
nvkm_gr_chsw_load(struct nvkm_engine *engine)
|
||||
{
|
||||
|
@ -1523,13 +1523,9 @@ gf100_grctx_generate(struct gf100_gr *gr)
|
||||
/* Make channel current. */
|
||||
addr = nvkm_memory_addr(inst) >> 12;
|
||||
if (gr->firmware) {
|
||||
nvkm_wr32(device, 0x409840, 0x00000030);
|
||||
nvkm_wr32(device, 0x409500, 0x80000000 | addr);
|
||||
nvkm_wr32(device, 0x409504, 0x00000003);
|
||||
nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800) & 0x00000010)
|
||||
break;
|
||||
);
|
||||
ret = gf100_gr_fecs_bind_pointer(gr, 0x80000000 | addr);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
nvkm_kmap(data);
|
||||
nvkm_wo32(data, 0x1c, 1);
|
||||
|
@ -715,6 +715,211 @@ gf100_gr_pack_mmio[] = {
|
||||
* PGRAPH engine/subdev functions
|
||||
******************************************************************************/
|
||||
|
||||
static u32
|
||||
gf100_gr_ctxsw_inst(struct nvkm_gr *gr)
|
||||
{
|
||||
return nvkm_rd32(gr->engine.subdev.device, 0x409b00);
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_ctrl_ctxsw(struct gf100_gr *gr, u32 mthd)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409804, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409504, mthd);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 stat = nvkm_rd32(device, 0x409804);
|
||||
if (stat == 0x00000002)
|
||||
return -EIO;
|
||||
if (stat == 0x00000001)
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int
|
||||
gf100_gr_fecs_start_ctxsw(struct nvkm_gr *base)
|
||||
{
|
||||
struct gf100_gr *gr = gf100_gr(base);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&gr->fecs.mutex);
|
||||
if (!--gr->fecs.disable) {
|
||||
if (WARN_ON(ret = gf100_gr_fecs_ctrl_ctxsw(gr, 0x39)))
|
||||
gr->fecs.disable++;
|
||||
}
|
||||
mutex_unlock(&gr->fecs.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
gf100_gr_fecs_stop_ctxsw(struct nvkm_gr *base)
|
||||
{
|
||||
struct gf100_gr *gr = gf100_gr(base);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&gr->fecs.mutex);
|
||||
if (!gr->fecs.disable++) {
|
||||
if (WARN_ON(ret = gf100_gr_fecs_ctrl_ctxsw(gr, 0x38)))
|
||||
gr->fecs.disable--;
|
||||
}
|
||||
mutex_unlock(&gr->fecs.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
gf100_gr_fecs_bind_pointer(struct gf100_gr *gr, u32 inst)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0x00000030);
|
||||
nvkm_wr32(device, 0x409500, inst);
|
||||
nvkm_wr32(device, 0x409504, 0x00000003);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 stat = nvkm_rd32(device, 0x409800);
|
||||
if (stat & 0x00000020)
|
||||
return -EIO;
|
||||
if (stat & 0x00000010)
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_set_reglist_virtual_address(struct gf100_gr *gr, u64 addr)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409810, addr >> 8);
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000032);
|
||||
nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800) == 0x00000001)
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_set_reglist_bind_instance(struct gf100_gr *gr, u32 inst)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409810, inst);
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000031);
|
||||
nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800) == 0x00000001)
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_discover_reglist_image_size(struct gf100_gr *gr, u32 *psize)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000030);
|
||||
nvkm_msec(device, 2000,
|
||||
if ((*psize = nvkm_rd32(device, 0x409800)))
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_elpg_bind(struct gf100_gr *gr)
|
||||
{
|
||||
u32 size;
|
||||
int ret;
|
||||
|
||||
ret = gf100_gr_fecs_discover_reglist_image_size(gr, &size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*XXX: We need to allocate + map the above into PMU's inst block,
|
||||
* which which means we probably need a proper PMU before we
|
||||
* even bother.
|
||||
*/
|
||||
|
||||
ret = gf100_gr_fecs_set_reglist_bind_instance(gr, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return gf100_gr_fecs_set_reglist_virtual_address(gr, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_discover_pm_image_size(struct gf100_gr *gr, u32 *psize)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000025);
|
||||
nvkm_msec(device, 2000,
|
||||
if ((*psize = nvkm_rd32(device, 0x409800)))
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_discover_zcull_image_size(struct gf100_gr *gr, u32 *psize)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000016);
|
||||
nvkm_msec(device, 2000,
|
||||
if ((*psize = nvkm_rd32(device, 0x409800)))
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
gf100_gr_fecs_discover_image_size(struct gf100_gr *gr, u32 *psize)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000010);
|
||||
nvkm_msec(device, 2000,
|
||||
if ((*psize = nvkm_rd32(device, 0x409800)))
|
||||
return 0;
|
||||
);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void
|
||||
gf100_gr_fecs_set_watchdog_timeout(struct gf100_gr *gr, u32 timeout)
|
||||
{
|
||||
struct nvkm_device *device = gr->base.engine.subdev.device;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, timeout);
|
||||
nvkm_wr32(device, 0x409504, 0x00000021);
|
||||
}
|
||||
|
||||
static bool
|
||||
gf100_gr_chsw_load(struct nvkm_gr *base)
|
||||
{
|
||||
@ -1487,6 +1692,7 @@ gf100_gr_init_ctxctl_ext(struct gf100_gr *gr)
|
||||
struct nvkm_device *device = subdev->device;
|
||||
struct nvkm_secboot *sb = device->secboot;
|
||||
u32 secboot_mask = 0;
|
||||
int ret;
|
||||
|
||||
/* load fuc microcode */
|
||||
nvkm_mc_unk260(device, 0);
|
||||
@ -1495,12 +1701,12 @@ gf100_gr_init_ctxctl_ext(struct gf100_gr *gr)
|
||||
if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_FECS))
|
||||
secboot_mask |= BIT(NVKM_SECBOOT_FALCON_FECS);
|
||||
else
|
||||
gf100_gr_init_fw(gr->fecs, &gr->fuc409c, &gr->fuc409d);
|
||||
gf100_gr_init_fw(gr->fecs.falcon, &gr->fuc409c, &gr->fuc409d);
|
||||
|
||||
if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_GPCCS))
|
||||
secboot_mask |= BIT(NVKM_SECBOOT_FALCON_GPCCS);
|
||||
else
|
||||
gf100_gr_init_fw(gr->gpccs, &gr->fuc41ac, &gr->fuc41ad);
|
||||
gf100_gr_init_fw(gr->gpccs.falcon, &gr->fuc41ac, &gr->fuc41ad);
|
||||
|
||||
if (secboot_mask != 0) {
|
||||
int ret = nvkm_secboot_reset(sb, secboot_mask);
|
||||
@ -1515,8 +1721,8 @@ gf100_gr_init_ctxctl_ext(struct gf100_gr *gr)
|
||||
nvkm_wr32(device, 0x41a10c, 0x00000000);
|
||||
nvkm_wr32(device, 0x40910c, 0x00000000);
|
||||
|
||||
nvkm_falcon_start(gr->gpccs);
|
||||
nvkm_falcon_start(gr->fecs);
|
||||
nvkm_falcon_start(gr->gpccs.falcon);
|
||||
nvkm_falcon_start(gr->fecs.falcon);
|
||||
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800) & 0x00000001)
|
||||
@ -1524,72 +1730,36 @@ gf100_gr_init_ctxctl_ext(struct gf100_gr *gr)
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x7fffffff);
|
||||
nvkm_wr32(device, 0x409504, 0x00000021);
|
||||
gf100_gr_fecs_set_watchdog_timeout(gr, 0x7fffffff);
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000010);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if ((gr->size = nvkm_rd32(device, 0x409800)))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
/* Determine how much memory is required to store main context image. */
|
||||
ret = gf100_gr_fecs_discover_image_size(gr, &gr->size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000016);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
/* Determine how much memory is required to store ZCULL image. */
|
||||
ret = gf100_gr_fecs_discover_zcull_image_size(gr, &gr->size_zcull);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvkm_wr32(device, 0x409840, 0xffffffff);
|
||||
nvkm_wr32(device, 0x409500, 0x00000000);
|
||||
nvkm_wr32(device, 0x409504, 0x00000025);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
/* Determine how much memory is required to store PerfMon image. */
|
||||
ret = gf100_gr_fecs_discover_pm_image_size(gr, &gr->size_pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (device->chipset >= 0xe0) {
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000030);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
nvkm_wr32(device, 0x409810, 0xb00095c8);
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000031);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
nvkm_wr32(device, 0x409810, 0x00080420);
|
||||
nvkm_wr32(device, 0x409800, 0x00000000);
|
||||
nvkm_wr32(device, 0x409500, 0x00000001);
|
||||
nvkm_wr32(device, 0x409504, 0x00000032);
|
||||
if (nvkm_msec(device, 2000,
|
||||
if (nvkm_rd32(device, 0x409800))
|
||||
break;
|
||||
) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
nvkm_wr32(device, 0x409614, 0x00000070);
|
||||
nvkm_wr32(device, 0x409614, 0x00000770);
|
||||
nvkm_wr32(device, 0x40802c, 0x00000001);
|
||||
/*XXX: We (likely) require PMU support to even bother with this.
|
||||
*
|
||||
* Also, it seems like not all GPUs support ELPG. Traces I
|
||||
* have here show RM enabling it on Kepler/Turing, but none
|
||||
* of the GPUs between those. NVGPU decides this by PCIID.
|
||||
*/
|
||||
if (0) {
|
||||
ret = gf100_gr_fecs_elpg_bind(gr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Generate golden context image. */
|
||||
if (gr->data == NULL) {
|
||||
int ret = gf100_grctx_generate(gr);
|
||||
if (ret) {
|
||||
@ -1614,15 +1784,19 @@ gf100_gr_init_ctxctl_int(struct gf100_gr *gr)
|
||||
|
||||
/* load HUB microcode */
|
||||
nvkm_mc_unk260(device, 0);
|
||||
nvkm_falcon_load_dmem(gr->fecs, gr->func->fecs.ucode->data.data, 0x0,
|
||||
nvkm_falcon_load_dmem(gr->fecs.falcon,
|
||||
gr->func->fecs.ucode->data.data, 0x0,
|
||||
gr->func->fecs.ucode->data.size, 0);
|
||||
nvkm_falcon_load_imem(gr->fecs, gr->func->fecs.ucode->code.data, 0x0,
|
||||
nvkm_falcon_load_imem(gr->fecs.falcon,
|
||||
gr->func->fecs.ucode->code.data, 0x0,
|
||||
gr->func->fecs.ucode->code.size, 0, 0, false);
|
||||
|
||||
/* load GPC microcode */
|
||||
nvkm_falcon_load_dmem(gr->gpccs, gr->func->gpccs.ucode->data.data, 0x0,
|
||||
nvkm_falcon_load_dmem(gr->gpccs.falcon,
|
||||
gr->func->gpccs.ucode->data.data, 0x0,
|
||||
gr->func->gpccs.ucode->data.size, 0);
|
||||
nvkm_falcon_load_imem(gr->gpccs, gr->func->gpccs.ucode->code.data, 0x0,
|
||||
nvkm_falcon_load_imem(gr->gpccs.falcon,
|
||||
gr->func->gpccs.ucode->code.data, 0x0,
|
||||
gr->func->gpccs.ucode->code.size, 0, 0, false);
|
||||
nvkm_mc_unk260(device, 1);
|
||||
|
||||
@ -1769,11 +1943,13 @@ gf100_gr_oneinit(struct nvkm_gr *base)
|
||||
int i, j;
|
||||
int ret;
|
||||
|
||||
ret = nvkm_falcon_v1_new(subdev, "FECS", 0x409000, &gr->fecs);
|
||||
ret = nvkm_falcon_v1_new(subdev, "FECS", 0x409000, &gr->fecs.falcon);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvkm_falcon_v1_new(subdev, "GPCCS", 0x41a000, &gr->gpccs);
|
||||
mutex_init(&gr->fecs.mutex);
|
||||
|
||||
ret = nvkm_falcon_v1_new(subdev, "GPCCS", 0x41a000, &gr->gpccs.falcon);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1816,11 +1992,11 @@ gf100_gr_init_(struct nvkm_gr *base)
|
||||
|
||||
nvkm_pmu_pgob(gr->base.engine.subdev.device->pmu, false);
|
||||
|
||||
ret = nvkm_falcon_get(gr->fecs, subdev);
|
||||
ret = nvkm_falcon_get(gr->fecs.falcon, subdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvkm_falcon_get(gr->gpccs, subdev);
|
||||
ret = nvkm_falcon_get(gr->gpccs.falcon, subdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1832,8 +2008,8 @@ gf100_gr_fini_(struct nvkm_gr *base, bool suspend)
|
||||
{
|
||||
struct gf100_gr *gr = gf100_gr(base);
|
||||
struct nvkm_subdev *subdev = &gr->base.engine.subdev;
|
||||
nvkm_falcon_put(gr->gpccs, subdev);
|
||||
nvkm_falcon_put(gr->fecs, subdev);
|
||||
nvkm_falcon_put(gr->gpccs.falcon, subdev);
|
||||
nvkm_falcon_put(gr->fecs.falcon, subdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1859,8 +2035,8 @@ gf100_gr_dtor(struct nvkm_gr *base)
|
||||
gr->func->dtor(gr);
|
||||
kfree(gr->data);
|
||||
|
||||
nvkm_falcon_del(&gr->gpccs);
|
||||
nvkm_falcon_del(&gr->fecs);
|
||||
nvkm_falcon_del(&gr->gpccs.falcon);
|
||||
nvkm_falcon_del(&gr->fecs.falcon);
|
||||
|
||||
gf100_gr_dtor_fw(&gr->fuc409c);
|
||||
gf100_gr_dtor_fw(&gr->fuc409d);
|
||||
@ -1886,6 +2062,9 @@ gf100_gr_ = {
|
||||
.chan_new = gf100_gr_chan_new,
|
||||
.object_get = gf100_gr_object_get,
|
||||
.chsw_load = gf100_gr_chsw_load,
|
||||
.ctxsw.pause = gf100_gr_fecs_stop_ctxsw,
|
||||
.ctxsw.resume = gf100_gr_fecs_start_ctxsw,
|
||||
.ctxsw.inst = gf100_gr_ctxsw_inst,
|
||||
};
|
||||
|
||||
int
|
||||
|
@ -82,8 +82,16 @@ struct gf100_gr {
|
||||
const struct gf100_gr_func *func;
|
||||
struct nvkm_gr base;
|
||||
|
||||
struct nvkm_falcon *fecs;
|
||||
struct nvkm_falcon *gpccs;
|
||||
struct {
|
||||
struct nvkm_falcon *falcon;
|
||||
struct mutex mutex;
|
||||
u32 disable;
|
||||
} fecs;
|
||||
|
||||
struct {
|
||||
struct nvkm_falcon *falcon;
|
||||
} gpccs;
|
||||
|
||||
struct gf100_gr_fuc fuc409c;
|
||||
struct gf100_gr_fuc fuc409d;
|
||||
struct gf100_gr_fuc fuc41ac;
|
||||
@ -128,6 +136,8 @@ struct gf100_gr {
|
||||
struct gf100_gr_mmio mmio_list[4096/8];
|
||||
u32 size;
|
||||
u32 *data;
|
||||
u32 size_zcull;
|
||||
u32 size_pm;
|
||||
};
|
||||
|
||||
int gf100_gr_ctor(const struct gf100_gr_func *, struct nvkm_device *,
|
||||
@ -136,6 +146,8 @@ int gf100_gr_new_(const struct gf100_gr_func *, struct nvkm_device *,
|
||||
int, struct nvkm_gr **);
|
||||
void *gf100_gr_dtor(struct nvkm_gr *);
|
||||
|
||||
int gf100_gr_fecs_bind_pointer(struct gf100_gr *, u32 inst);
|
||||
|
||||
struct gf100_gr_func_zbc {
|
||||
void (*clear_color)(struct gf100_gr *, int zbc);
|
||||
void (*clear_depth)(struct gf100_gr *, int zbc);
|
||||
|
@ -27,6 +27,11 @@ struct nvkm_gr_func {
|
||||
*/
|
||||
u64 (*units)(struct nvkm_gr *);
|
||||
bool (*chsw_load)(struct nvkm_gr *);
|
||||
struct {
|
||||
int (*pause)(struct nvkm_gr *);
|
||||
int (*resume)(struct nvkm_gr *);
|
||||
u32 (*inst)(struct nvkm_gr *);
|
||||
} ctxsw;
|
||||
struct nvkm_sclass sclass[];
|
||||
};
|
||||
|
||||
|
@ -21,13 +21,21 @@
|
||||
*/
|
||||
#include "priv.h"
|
||||
|
||||
#include <subdev/top.h>
|
||||
#include <engine/falcon.h>
|
||||
|
||||
static int
|
||||
nvkm_nvdec_oneinit(struct nvkm_engine *engine)
|
||||
{
|
||||
struct nvkm_nvdec *nvdec = nvkm_nvdec(engine);
|
||||
return nvkm_falcon_v1_new(&nvdec->engine.subdev, "NVDEC", 0x84000,
|
||||
struct nvkm_subdev *subdev = &nvdec->engine.subdev;
|
||||
|
||||
nvdec->addr = nvkm_top_addr(subdev->device, subdev->index);
|
||||
if (!nvdec->addr)
|
||||
return -EINVAL;
|
||||
|
||||
/*XXX: fix naming of this when adding support for multiple-NVDEC */
|
||||
return nvkm_falcon_v1_new(subdev, "NVDEC", nvdec->addr,
|
||||
&nvdec->falcon);
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
nvkm-y += nvkm/engine/sec2/base.o
|
||||
nvkm-y += nvkm/engine/sec2/gp102.o
|
||||
nvkm-y += nvkm/engine/sec2/tu102.o
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "priv.h"
|
||||
|
||||
#include <core/msgqueue.h>
|
||||
#include <subdev/top.h>
|
||||
#include <engine/falcon.h>
|
||||
|
||||
static void *
|
||||
@ -39,18 +40,18 @@ nvkm_sec2_intr(struct nvkm_engine *engine)
|
||||
struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
|
||||
struct nvkm_subdev *subdev = &engine->subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
u32 disp = nvkm_rd32(device, 0x8701c);
|
||||
u32 intr = nvkm_rd32(device, 0x87008) & disp & ~(disp >> 16);
|
||||
u32 disp = nvkm_rd32(device, sec2->addr + 0x01c);
|
||||
u32 intr = nvkm_rd32(device, sec2->addr + 0x008) & disp & ~(disp >> 16);
|
||||
|
||||
if (intr & 0x00000040) {
|
||||
schedule_work(&sec2->work);
|
||||
nvkm_wr32(device, 0x87004, 0x00000040);
|
||||
nvkm_wr32(device, sec2->addr + 0x004, 0x00000040);
|
||||
intr &= ~0x00000040;
|
||||
}
|
||||
|
||||
if (intr) {
|
||||
nvkm_error(subdev, "unhandled intr %08x\n", intr);
|
||||
nvkm_wr32(device, 0x87004, intr);
|
||||
nvkm_wr32(device, sec2->addr + 0x004, intr);
|
||||
|
||||
}
|
||||
}
|
||||
@ -74,8 +75,15 @@ static int
|
||||
nvkm_sec2_oneinit(struct nvkm_engine *engine)
|
||||
{
|
||||
struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
|
||||
return nvkm_falcon_v1_new(&sec2->engine.subdev, "SEC2", 0x87000,
|
||||
&sec2->falcon);
|
||||
struct nvkm_subdev *subdev = &sec2->engine.subdev;
|
||||
|
||||
if (!sec2->addr) {
|
||||
sec2->addr = nvkm_top_addr(subdev->device, subdev->index);
|
||||
if (WARN_ON(!sec2->addr))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return nvkm_falcon_v1_new(subdev, "SEC2", sec2->addr, &sec2->falcon);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -95,13 +103,14 @@ nvkm_sec2 = {
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_sec2_new_(struct nvkm_device *device, int index,
|
||||
nvkm_sec2_new_(struct nvkm_device *device, int index, u32 addr,
|
||||
struct nvkm_sec2 **psec2)
|
||||
{
|
||||
struct nvkm_sec2 *sec2;
|
||||
|
||||
if (!(sec2 = *psec2 = kzalloc(sizeof(*sec2), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
sec2->addr = addr;
|
||||
INIT_WORK(&sec2->work, nvkm_sec2_recv);
|
||||
|
||||
return nvkm_engine_ctor(&nvkm_sec2, device, index, true, &sec2->engine);
|
||||
|
@ -26,5 +26,5 @@ int
|
||||
gp102_sec2_new(struct nvkm_device *device, int index,
|
||||
struct nvkm_sec2 **psec2)
|
||||
{
|
||||
return nvkm_sec2_new_(device, index, psec2);
|
||||
return nvkm_sec2_new_(device, index, 0, psec2);
|
||||
}
|
||||
|
@ -5,6 +5,5 @@
|
||||
|
||||
#define nvkm_sec2(p) container_of((p), struct nvkm_sec2, engine)
|
||||
|
||||
int nvkm_sec2_new_(struct nvkm_device *, int, struct nvkm_sec2 **);
|
||||
|
||||
int nvkm_sec2_new_(struct nvkm_device *, int, u32 addr, struct nvkm_sec2 **);
|
||||
#endif
|
||||
|
33
drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c
Normal file
33
drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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 "priv.h"
|
||||
|
||||
int
|
||||
tu102_sec2_new(struct nvkm_device *device, int index,
|
||||
struct nvkm_sec2 **psec2)
|
||||
{
|
||||
/* TOP info wasn't updated on Turing to reflect the PRI
|
||||
* address change for some reason. We override it here.
|
||||
*/
|
||||
return nvkm_sec2_new_(device, index, 0x840000, psec2);
|
||||
}
|
@ -204,6 +204,9 @@ nvkm_falcon_ctor(const struct nvkm_falcon_func *func,
|
||||
debug_reg = 0x408;
|
||||
falcon->has_emem = true;
|
||||
break;
|
||||
case NVKM_SUBDEV_GSP:
|
||||
debug_reg = 0x0; /*XXX*/
|
||||
break;
|
||||
default:
|
||||
nvkm_warn(subdev, "unsupported falcon %s!\n",
|
||||
nvkm_subdev_name[subdev->index]);
|
||||
|
@ -269,7 +269,7 @@ cmd_write(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_hdr *cmd,
|
||||
commit = false;
|
||||
}
|
||||
|
||||
cmd_queue_close(priv, queue, commit);
|
||||
cmd_queue_close(priv, queue, commit);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -347,7 +347,7 @@ nvkm_msgqueue_post(struct nvkm_msgqueue *priv, enum msgqueue_msg_priority prio,
|
||||
ret = cmd_write(priv, cmd, queue);
|
||||
if (ret) {
|
||||
seq->state = SEQ_STATE_PENDING;
|
||||
msgqueue_seq_release(priv, seq);
|
||||
msgqueue_seq_release(priv, seq);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -373,7 +373,7 @@ msgqueue_msg_handle(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_hdr *hdr)
|
||||
if (seq->completion)
|
||||
complete(seq->completion);
|
||||
|
||||
msgqueue_seq_release(priv, seq);
|
||||
msgqueue_seq_release(priv, seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ include $(src)/nvkm/subdev/fault/Kbuild
|
||||
include $(src)/nvkm/subdev/fb/Kbuild
|
||||
include $(src)/nvkm/subdev/fuse/Kbuild
|
||||
include $(src)/nvkm/subdev/gpio/Kbuild
|
||||
include $(src)/nvkm/subdev/gsp/Kbuild
|
||||
include $(src)/nvkm/subdev/i2c/Kbuild
|
||||
include $(src)/nvkm/subdev/ibus/Kbuild
|
||||
include $(src)/nvkm/subdev/iccsense/Kbuild
|
||||
|
@ -5,4 +5,4 @@ nvkm-y += nvkm/subdev/bar/gf100.o
|
||||
nvkm-y += nvkm/subdev/bar/gk20a.o
|
||||
nvkm-y += nvkm/subdev/bar/gm107.o
|
||||
nvkm-y += nvkm/subdev/bar/gm20b.o
|
||||
nvkm-y += nvkm/subdev/bar/tu104.o
|
||||
nvkm-y += nvkm/subdev/bar/tu102.o
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
tu104_bar_bar2_wait(struct nvkm_bar *bar)
|
||||
tu102_bar_bar2_wait(struct nvkm_bar *bar)
|
||||
{
|
||||
struct nvkm_device *device = bar->subdev.device;
|
||||
nvkm_msec(device, 2000,
|
||||
@ -35,13 +35,13 @@ tu104_bar_bar2_wait(struct nvkm_bar *bar)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_bar_bar2_fini(struct nvkm_bar *bar)
|
||||
tu102_bar_bar2_fini(struct nvkm_bar *bar)
|
||||
{
|
||||
nvkm_mask(bar->subdev.device, 0xb80f48, 0x80000000, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_bar_bar2_init(struct nvkm_bar *base)
|
||||
tu102_bar_bar2_init(struct nvkm_bar *base)
|
||||
{
|
||||
struct nvkm_device *device = base->subdev.device;
|
||||
struct gf100_bar *bar = gf100_bar(base);
|
||||
@ -52,7 +52,7 @@ tu104_bar_bar2_init(struct nvkm_bar *base)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_bar_bar1_wait(struct nvkm_bar *bar)
|
||||
tu102_bar_bar1_wait(struct nvkm_bar *bar)
|
||||
{
|
||||
struct nvkm_device *device = bar->subdev.device;
|
||||
nvkm_msec(device, 2000,
|
||||
@ -62,13 +62,13 @@ tu104_bar_bar1_wait(struct nvkm_bar *bar)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_bar_bar1_fini(struct nvkm_bar *bar)
|
||||
tu102_bar_bar1_fini(struct nvkm_bar *bar)
|
||||
{
|
||||
nvkm_mask(bar->subdev.device, 0xb80f40, 0x80000000, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_bar_bar1_init(struct nvkm_bar *base)
|
||||
tu102_bar_bar1_init(struct nvkm_bar *base)
|
||||
{
|
||||
struct nvkm_device *device = base->subdev.device;
|
||||
struct gf100_bar *bar = gf100_bar(base);
|
||||
@ -77,22 +77,22 @@ tu104_bar_bar1_init(struct nvkm_bar *base)
|
||||
}
|
||||
|
||||
static const struct nvkm_bar_func
|
||||
tu104_bar = {
|
||||
tu102_bar = {
|
||||
.dtor = gf100_bar_dtor,
|
||||
.oneinit = gf100_bar_oneinit,
|
||||
.bar1.init = tu104_bar_bar1_init,
|
||||
.bar1.fini = tu104_bar_bar1_fini,
|
||||
.bar1.wait = tu104_bar_bar1_wait,
|
||||
.bar1.init = tu102_bar_bar1_init,
|
||||
.bar1.fini = tu102_bar_bar1_fini,
|
||||
.bar1.wait = tu102_bar_bar1_wait,
|
||||
.bar1.vmm = gf100_bar_bar1_vmm,
|
||||
.bar2.init = tu104_bar_bar2_init,
|
||||
.bar2.fini = tu104_bar_bar2_fini,
|
||||
.bar2.wait = tu104_bar_bar2_wait,
|
||||
.bar2.init = tu102_bar_bar2_init,
|
||||
.bar2.fini = tu102_bar_bar2_fini,
|
||||
.bar2.wait = tu102_bar_bar2_wait,
|
||||
.bar2.vmm = gf100_bar_bar2_vmm,
|
||||
.flush = g84_bar_flush,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_bar_new(struct nvkm_device *device, int index, struct nvkm_bar **pbar)
|
||||
tu102_bar_new(struct nvkm_device *device, int index, struct nvkm_bar **pbar)
|
||||
{
|
||||
return gf100_bar_new_(&tu104_bar, device, index, pbar);
|
||||
return gf100_bar_new_(&tu102_bar, device, index, pbar);
|
||||
}
|
@ -212,7 +212,7 @@ nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
|
||||
u16 data;
|
||||
|
||||
if (*ver >= 0x30) {
|
||||
const u8 vsoff[] = { 0, 4, 7, 9 };
|
||||
static const u8 vsoff[] = { 0, 4, 7, 9 };
|
||||
idx = (pc * 10) + vsoff[vs] + pe;
|
||||
if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12)
|
||||
idx += nvbios_rd08(bios, outp + 0x11) * 40;
|
||||
|
@ -806,12 +806,12 @@ init_generic_condition(struct nvbios_init *init)
|
||||
init->offset += 3;
|
||||
|
||||
switch (cond) {
|
||||
case 0:
|
||||
case 0: /* CONDITION_ID_INT_DP. */
|
||||
if (init_conn(init) != DCB_CONNECTOR_eDP)
|
||||
init_exec_set(init, false);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 1: /* CONDITION_ID_USE_SPPLL0. */
|
||||
case 2: /* CONDITION_ID_USE_SPPLL1. */
|
||||
if ( init->outp &&
|
||||
(data = nvbios_dpout_match(bios, DCB_OUTPUT_DP,
|
||||
(init->outp->or << 0) |
|
||||
@ -826,10 +826,13 @@ init_generic_condition(struct nvbios_init *init)
|
||||
if (init_exec(init))
|
||||
warn("script needs dp output table data\n");
|
||||
break;
|
||||
case 5:
|
||||
case 5: /* CONDITION_ID_ASSR_SUPPORT. */
|
||||
if (!(init_rdauxr(init, 0x0d) & 1))
|
||||
init_exec_set(init, false);
|
||||
break;
|
||||
case 7: /* CONDITION_ID_NO_PANEL_SEQ_DELAYS. */
|
||||
init_exec_set(init, false);
|
||||
break;
|
||||
default:
|
||||
warn("INIT_GENERIC_CONDITON: unknown 0x%02x\n", cond);
|
||||
init->offset += size;
|
||||
|
@ -13,4 +13,4 @@ nvkm-y += nvkm/subdev/devinit/gf100.o
|
||||
nvkm-y += nvkm/subdev/devinit/gm107.o
|
||||
nvkm-y += nvkm/subdev/devinit/gm200.o
|
||||
nvkm-y += nvkm/subdev/devinit/gv100.o
|
||||
nvkm-y += nvkm/subdev/devinit/tu104.o
|
||||
nvkm-y += nvkm/subdev/devinit/tu102.o
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <subdev/clk/pll.h>
|
||||
|
||||
static int
|
||||
tu104_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
|
||||
tu102_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &init->subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
@ -66,7 +66,7 @@ tu104_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
|
||||
}
|
||||
|
||||
static int
|
||||
tu104_devinit_post(struct nvkm_devinit *base, bool post)
|
||||
tu102_devinit_post(struct nvkm_devinit *base, bool post)
|
||||
{
|
||||
struct nv50_devinit *init = nv50_devinit(base);
|
||||
gm200_devinit_preos(init, post);
|
||||
@ -74,16 +74,16 @@ tu104_devinit_post(struct nvkm_devinit *base, bool post)
|
||||
}
|
||||
|
||||
static const struct nvkm_devinit_func
|
||||
tu104_devinit = {
|
||||
tu102_devinit = {
|
||||
.init = nv50_devinit_init,
|
||||
.post = tu104_devinit_post,
|
||||
.pll_set = tu104_devinit_pll_set,
|
||||
.post = tu102_devinit_post,
|
||||
.pll_set = tu102_devinit_pll_set,
|
||||
.disable = gm107_devinit_disable,
|
||||
};
|
||||
|
||||
int
|
||||
tu104_devinit_new(struct nvkm_device *device, int index,
|
||||
tu102_devinit_new(struct nvkm_device *device, int index,
|
||||
struct nvkm_devinit **pinit)
|
||||
{
|
||||
return nv50_devinit_new_(&tu104_devinit, device, index, pinit);
|
||||
return nv50_devinit_new_(&tu102_devinit, device, index, pinit);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
nvkm-y += nvkm/subdev/fault/base.o
|
||||
nvkm-y += nvkm/subdev/fault/user.o
|
||||
nvkm-y += nvkm/subdev/fault/gp100.o
|
||||
nvkm-y += nvkm/subdev/fault/gv100.o
|
||||
nvkm-y += nvkm/subdev/fault/tu104.o
|
||||
nvkm-y += nvkm/subdev/fault/tu102.o
|
||||
|
@ -176,5 +176,7 @@ nvkm_fault_new_(const struct nvkm_fault_func *func, struct nvkm_device *device,
|
||||
return -ENOMEM;
|
||||
nvkm_subdev_ctor(&nvkm_fault, device, index, &fault->subdev);
|
||||
fault->func = func;
|
||||
fault->user.ctor = nvkm_ufault_new;
|
||||
fault->user.base = func->user.base;
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include <subdev/mc.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
|
||||
static void
|
||||
gp100_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable)
|
||||
{
|
||||
@ -69,6 +71,7 @@ gp100_fault = {
|
||||
.buffer.init = gp100_fault_buffer_init,
|
||||
.buffer.fini = gp100_fault_buffer_fini,
|
||||
.buffer.intr = gp100_fault_buffer_intr,
|
||||
.user = { { 0, 0, MAXWELL_FAULT_BUFFER_A }, 0 },
|
||||
};
|
||||
|
||||
int
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <subdev/mmu.h>
|
||||
#include <engine/fifo.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
|
||||
static void
|
||||
gv100_fault_buffer_process(struct nvkm_fault_buffer *buffer)
|
||||
{
|
||||
@ -166,6 +168,13 @@ gv100_fault_intr(struct nvkm_fault *fault)
|
||||
}
|
||||
}
|
||||
|
||||
if (stat & 0x08000000) {
|
||||
if (fault->buffer[1]) {
|
||||
nvkm_event_send(&fault->event, 1, 1, NULL, 0);
|
||||
stat &= ~0x08000000;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat) {
|
||||
nvkm_debug(subdev, "intr %08x\n", stat);
|
||||
}
|
||||
@ -208,6 +217,13 @@ gv100_fault = {
|
||||
.buffer.init = gv100_fault_buffer_init,
|
||||
.buffer.fini = gv100_fault_buffer_fini,
|
||||
.buffer.intr = gv100_fault_buffer_intr,
|
||||
/*TODO: Figure out how to expose non-replayable fault buffer, which,
|
||||
* for some reason, is where recoverable CE faults appear...
|
||||
*
|
||||
* It's a bit tricky, as both NVKM and SVM will need access to
|
||||
* the non-replayable fault buffer.
|
||||
*/
|
||||
.user = { { 0, 0, VOLTA_FAULT_BUFFER_A }, 1 },
|
||||
};
|
||||
|
||||
int
|
||||
|
@ -34,7 +34,14 @@ struct nvkm_fault_func {
|
||||
void (*fini)(struct nvkm_fault_buffer *);
|
||||
void (*intr)(struct nvkm_fault_buffer *, bool enable);
|
||||
} buffer;
|
||||
struct {
|
||||
struct nvkm_sclass base;
|
||||
int rp;
|
||||
} user;
|
||||
};
|
||||
|
||||
int gv100_fault_oneinit(struct nvkm_fault *);
|
||||
|
||||
int nvkm_ufault_new(struct nvkm_device *, const struct nvkm_oclass *,
|
||||
void *, u32, struct nvkm_object **);
|
||||
#endif
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <nvif/class.h>
|
||||
|
||||
static void
|
||||
tu104_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable)
|
||||
tu102_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable)
|
||||
{
|
||||
/*XXX: Earlier versions of RM touched the old regs on Turing,
|
||||
* which don't appear to actually work anymore, but newer
|
||||
@ -37,7 +37,7 @@ tu104_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_buffer_fini(struct nvkm_fault_buffer *buffer)
|
||||
tu102_fault_buffer_fini(struct nvkm_fault_buffer *buffer)
|
||||
{
|
||||
struct nvkm_device *device = buffer->fault->subdev.device;
|
||||
const u32 foff = buffer->id * 0x20;
|
||||
@ -45,7 +45,7 @@ tu104_fault_buffer_fini(struct nvkm_fault_buffer *buffer)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_buffer_init(struct nvkm_fault_buffer *buffer)
|
||||
tu102_fault_buffer_init(struct nvkm_fault_buffer *buffer)
|
||||
{
|
||||
struct nvkm_device *device = buffer->fault->subdev.device;
|
||||
const u32 foff = buffer->id * 0x20;
|
||||
@ -57,7 +57,7 @@ tu104_fault_buffer_init(struct nvkm_fault_buffer *buffer)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_buffer_info(struct nvkm_fault_buffer *buffer)
|
||||
tu102_fault_buffer_info(struct nvkm_fault_buffer *buffer)
|
||||
{
|
||||
struct nvkm_device *device = buffer->fault->subdev.device;
|
||||
const u32 foff = buffer->id * 0x20;
|
||||
@ -70,7 +70,7 @@ tu104_fault_buffer_info(struct nvkm_fault_buffer *buffer)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_intr_fault(struct nvkm_fault *fault)
|
||||
tu102_fault_intr_fault(struct nvkm_fault *fault)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &fault->subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
@ -96,14 +96,14 @@ tu104_fault_intr_fault(struct nvkm_fault *fault)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_intr(struct nvkm_fault *fault)
|
||||
tu102_fault_intr(struct nvkm_fault *fault)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &fault->subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
u32 stat = nvkm_rd32(device, 0xb83094);
|
||||
|
||||
if (stat & 0x80000000) {
|
||||
tu104_fault_intr_fault(fault);
|
||||
tu102_fault_intr_fault(fault);
|
||||
nvkm_wr32(device, 0xb83094, 0x80000000);
|
||||
stat &= ~0x80000000;
|
||||
}
|
||||
@ -129,7 +129,7 @@ tu104_fault_intr(struct nvkm_fault *fault)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_fini(struct nvkm_fault *fault)
|
||||
tu102_fault_fini(struct nvkm_fault *fault)
|
||||
{
|
||||
nvkm_notify_put(&fault->nrpfb);
|
||||
if (fault->buffer[0])
|
||||
@ -138,7 +138,7 @@ tu104_fault_fini(struct nvkm_fault *fault)
|
||||
}
|
||||
|
||||
static void
|
||||
tu104_fault_init(struct nvkm_fault *fault)
|
||||
tu102_fault_init(struct nvkm_fault *fault)
|
||||
{
|
||||
/*XXX: enable priv faults */
|
||||
fault->func->buffer.init(fault->buffer[0]);
|
||||
@ -146,22 +146,23 @@ tu104_fault_init(struct nvkm_fault *fault)
|
||||
}
|
||||
|
||||
static const struct nvkm_fault_func
|
||||
tu104_fault = {
|
||||
tu102_fault = {
|
||||
.oneinit = gv100_fault_oneinit,
|
||||
.init = tu104_fault_init,
|
||||
.fini = tu104_fault_fini,
|
||||
.intr = tu104_fault_intr,
|
||||
.init = tu102_fault_init,
|
||||
.fini = tu102_fault_fini,
|
||||
.intr = tu102_fault_intr,
|
||||
.buffer.nr = 2,
|
||||
.buffer.entry_size = 32,
|
||||
.buffer.info = tu104_fault_buffer_info,
|
||||
.buffer.init = tu104_fault_buffer_init,
|
||||
.buffer.fini = tu104_fault_buffer_fini,
|
||||
.buffer.intr = tu104_fault_buffer_intr,
|
||||
.buffer.info = tu102_fault_buffer_info,
|
||||
.buffer.init = tu102_fault_buffer_init,
|
||||
.buffer.fini = tu102_fault_buffer_fini,
|
||||
.buffer.intr = tu102_fault_buffer_intr,
|
||||
.user = { { 0, 0, VOLTA_FAULT_BUFFER_A }, 1 },
|
||||
};
|
||||
|
||||
int
|
||||
tu104_fault_new(struct nvkm_device *device, int index,
|
||||
tu102_fault_new(struct nvkm_device *device, int index,
|
||||
struct nvkm_fault **pfault)
|
||||
{
|
||||
return nvkm_fault_new_(&tu104_fault, device, index, pfault);
|
||||
return nvkm_fault_new_(&tu102_fault, device, index, pfault);
|
||||
}
|
106
drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c
Normal file
106
drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2018 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 "priv.h"
|
||||
|
||||
#include <core/memory.h>
|
||||
#include <subdev/mmu.h>
|
||||
|
||||
#include <nvif/clb069.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
static int
|
||||
nvkm_ufault_map(struct nvkm_object *object, void *argv, u32 argc,
|
||||
enum nvkm_object_map *type, u64 *addr, u64 *size)
|
||||
{
|
||||
struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object);
|
||||
struct nvkm_device *device = buffer->fault->subdev.device;
|
||||
*type = NVKM_OBJECT_MAP_IO;
|
||||
*addr = device->func->resource_addr(device, 3) + buffer->addr;
|
||||
*size = nvkm_memory_size(buffer->mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_ufault_ntfy(struct nvkm_object *object, u32 type,
|
||||
struct nvkm_event **pevent)
|
||||
{
|
||||
struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object);
|
||||
if (type == NVB069_V0_NTFY_FAULT) {
|
||||
*pevent = &buffer->fault->event;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_ufault_fini(struct nvkm_object *object, bool suspend)
|
||||
{
|
||||
struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object);
|
||||
buffer->fault->func->buffer.fini(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_ufault_init(struct nvkm_object *object)
|
||||
{
|
||||
struct nvkm_fault_buffer *buffer = nvkm_fault_buffer(object);
|
||||
buffer->fault->func->buffer.init(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
nvkm_ufault_dtor(struct nvkm_object *object)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct nvkm_object_func
|
||||
nvkm_ufault = {
|
||||
.dtor = nvkm_ufault_dtor,
|
||||
.init = nvkm_ufault_init,
|
||||
.fini = nvkm_ufault_fini,
|
||||
.ntfy = nvkm_ufault_ntfy,
|
||||
.map = nvkm_ufault_map,
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_ufault_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
|
||||
void *argv, u32 argc, struct nvkm_object **pobject)
|
||||
{
|
||||
union {
|
||||
struct nvif_clb069_v0 v0;
|
||||
} *args = argv;
|
||||
struct nvkm_fault *fault = device->fault;
|
||||
struct nvkm_fault_buffer *buffer = fault->buffer[fault->func->user.rp];
|
||||
int ret = -ENOSYS;
|
||||
|
||||
if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
|
||||
args->v0.entries = buffer->entries;
|
||||
args->v0.get = buffer->get;
|
||||
args->v0.put = buffer->put;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
nvkm_object_ctor(&nvkm_ufault, oclass, &buffer->object);
|
||||
*pobject = &buffer->object;
|
||||
return 0;
|
||||
}
|
@ -87,7 +87,7 @@ nvkm_gddr3_calc(struct nvkm_ram *ram)
|
||||
WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
|
||||
/* XXX: Get these values from the VBIOS instead */
|
||||
DLL = !(ram->mr[1] & 0x1);
|
||||
RON = !(ram->mr[1] & 0x300) >> 8;
|
||||
RON = !((ram->mr[1] & 0x300) >> 8);
|
||||
break;
|
||||
default:
|
||||
return -ENOSYS;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user