diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
37 files changed, 1528 insertions, 169 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 2de589caf508..4989627b7802 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -44,6 +44,9 @@ #include <subdev/bios/pll.h> #include <subdev/clk.h> +#include <nvif/event.h> +#include <nvif/cl0046.h> + static int nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); @@ -756,6 +759,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + nvif_notify_fini(&nv_crtc->vblank); kfree(nv_crtc); } @@ -845,7 +849,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, } nvbo = nouveau_gem_object(drm_fb->obj[0]); - nv_crtc->fb.offset = nvbo->bo.offset; + nv_crtc->fb.offset = nvbo->offset; if (nv_crtc->lut.depth != drm_fb->format->depth) { nv_crtc->lut.depth = drm_fb->format->depth; @@ -1013,11 +1017,11 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); nouveau_bo_unmap(cursor); - nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; + nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->offset; nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); nv_crtc->cursor.show(nv_crtc, true); out: - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return ret; } @@ -1192,7 +1196,7 @@ nv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* 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 }; + new_bo->offset }; /* Keep vblanks on during flip, for the target crtc of this flip */ drm_crtc_vblank_get(crtc); @@ -1297,9 +1301,19 @@ create_primary_plane(struct drm_device *dev) return primary; } +static int nv04_crtc_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + drm_crtc_handle_vblank(&nv_crtc->base); + return NVIF_NOTIFY_KEEP; +} + int nv04_crtc_create(struct drm_device *dev, int crtc_num) { + struct nouveau_display *disp = nouveau_display(dev); struct nouveau_crtc *nv_crtc; int ret; @@ -1337,5 +1351,14 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) nv04_cursor_init(nv_crtc); - return 0; + ret = nvif_notify_init(&disp->disp.object, nv04_crtc_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + + return ret; } diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 0f4ebefed1fd..76be805fc488 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -152,7 +152,8 @@ nv04_display_init(struct drm_device *dev, bool resume, bool runtime) continue; if (nv_crtc->cursor.set_offset) - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset); + nv_crtc->cursor.set_offset(nv_crtc, + nv_crtc->cursor.nvbo->offset); nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, nv_crtc->cursor_saved_y); } diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 6248fd1dbc6d..193ba9498f3d 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -152,7 +152,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0); nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0); - nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nvbo->bo.offset); + nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nvbo->offset); nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w); nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x); nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w); @@ -174,7 +174,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (format & NV_PVIDEO_FORMAT_PLANAR) { nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), - nvbo->bo.offset + fb->offsets[1]); + nvbo->offset + fb->offsets[1]); } nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format | fb->pitches[0]); nvif_wr32(dev, NV_PVIDEO_STOP, 0); @@ -399,7 +399,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, for (i = 0; i < 2; i++) { nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i, - nvbo->bo.offset); + nvbo->offset); nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, fb->pitches[0]); nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); diff --git a/drivers/gpu/drm/nouveau/dispnv50/Kbuild b/drivers/gpu/drm/nouveau/dispnv50/Kbuild index e0c435eae664..6fdddb266fb1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/Kbuild +++ b/drivers/gpu/drm/nouveau/dispnv50/Kbuild @@ -10,6 +10,10 @@ nouveau-y += dispnv50/core917d.o nouveau-y += dispnv50/corec37d.o nouveau-y += dispnv50/corec57d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc907d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crcc37d.o + nouveau-y += dispnv50/dac507d.o nouveau-y += dispnv50/dac907d.o diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 24f7700768da..3d82b3c67dec 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -2,6 +2,9 @@ #define __NV50_KMS_ATOM_H__ #define nv50_atom(p) container_of((p), struct nv50_atom, state) #include <drm/drm_atomic.h> +#include "crc.h" + +struct nouveau_encoder; struct nv50_atom { struct drm_atomic_state state; @@ -18,6 +21,7 @@ struct nv50_head_atom { struct { u32 mask; + u32 owned; u32 olut; } wndw; @@ -114,9 +118,12 @@ struct nv50_head_atom { u8 nhsync:1; u8 nvsync:1; u8 depth:4; + u8 crc_raster:2; u8 bpc; } or; + struct nv50_crc_atom crc; + /* Currently only used for MST */ struct { int pbn; @@ -134,6 +141,7 @@ struct nv50_head_atom { bool ovly:1; bool dither:1; bool procamp:1; + bool crc:1; bool or:1; }; u16 mask; @@ -149,6 +157,19 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc) return nv50_head_atom(statec); } +static inline struct drm_encoder * +nv50_head_atom_get_encoder(struct nv50_head_atom *atom) +{ + struct drm_encoder *encoder = NULL; + + /* We only ever have a single encoder */ + drm_for_each_encoder_mask(encoder, atom->state.crtc->dev, + atom->state.encoder_mask) + break; + + return encoder; +} + #define nv50_wndw_atom(p) container_of((p), struct nv50_wndw_atom, state) struct nv50_wndw_atom { diff --git a/drivers/gpu/drm/nouveau/dispnv50/base507c.c b/drivers/gpu/drm/nouveau/dispnv50/base507c.c index 511258bfbcbc..ba20a7722900 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base507c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base507c.c @@ -276,7 +276,7 @@ base507c_new_(const struct nv50_wndw_func *func, const u32 *format, ret = nv50_dmac_create(&drm->client.device, &disp->disp.object, &oclass, head, &args, sizeof(args), - disp50->sync->bo.offset, &wndw->wndw); + disp50->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "base%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index 99157dc94d23..e021cb340569 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -2,6 +2,7 @@ #define __NV50_KMS_CORE_H__ #include "disp.h" #include "atom.h" +#include "crc.h" #include <nouveau_encoder.h> struct nv50_core { @@ -26,6 +27,9 @@ struct nv50_core_func { } wndw; const struct nv50_head_func *head; +#if IS_ENABLED(CONFIG_DEBUG_FS) + const struct nv50_crc_func *crc; +#endif const struct nv50_outp_func { void (*ctrl)(struct nv50_core *, int or, u32 ctrl, struct nv50_head_atom *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/core507d.c b/drivers/gpu/drm/nouveau/dispnv50/core507d.c index e341f572c269..1d66f694b945 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core507d.c @@ -115,7 +115,7 @@ core507d_new_(const struct nv50_core_func *func, struct nouveau_drm *drm, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &core->chan); + disp->sync->offset, &core->chan); if (ret) { NV_ERROR(drm, "core%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index 271629832629..b17c03529c78 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -30,6 +30,9 @@ core907d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 5cc072d4c30f..66846f372080 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -30,6 +30,9 @@ core917d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c index e0c8811fb8e4..ec83189a1d48 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c @@ -142,6 +142,9 @@ corec37d = { .wndw.owner = corec37d_wndw_owner, .head = &headc37d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c index 10ba9e9e4ae6..e1c11eba0ce1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c @@ -52,6 +52,9 @@ corec57d = { .wndw.owner = corec37d_wndw_owner, .head = &headc57d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c new file mode 100644 index 000000000000..f17fb6d56757 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c @@ -0,0 +1,751 @@ +// SPDX-License-Identifier: MIT +#include <linux/string.h> +#include <drm/drm_crtc.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_vblank.h> +#include <drm/drm_vblank_work.h> + +#include <nvif/class.h> +#include <nvif/cl0002.h> +#include <nvif/timer.h> + +#include "nouveau_drv.h" +#include "core.h" +#include "head.h" +#include "wndw.h" +#include "handles.h" +#include "crc.h" + +static const char * const nv50_crc_sources[] = { + [NV50_CRC_SOURCE_NONE] = "none", + [NV50_CRC_SOURCE_AUTO] = "auto", + [NV50_CRC_SOURCE_RG] = "rg", + [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active", + [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete", + [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive", +}; + +static int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) +{ + int i; + + if (!buf) { + *s = NV50_CRC_SOURCE_NONE; + return 0; + } + + i = match_string(nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), buf); + if (i < 0) + return i; + + *s = i; + return 0; +} + +int +nv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + enum nv50_crc_source source; + + if (nv50_crc_parse_source(source_name, &source) < 0) { + NV_DEBUG(drm, "unknown source %s\n", source_name); + return -EINVAL; + } + + *values_cnt = 1; + return 0; +} + +const char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) +{ + *count = ARRAY_SIZE(nv50_crc_sources); + return nv50_crc_sources; +} + +static void +nv50_crc_program_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_disp *disp = nv50_disp(head->base.base.dev); + struct nv50_core *core = disp->core; + u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; + + core->func->crc->set_ctx(head, ctx); + core->func->update(core, interlock, false); +} + +static void nv50_crc_ctx_flip_work(struct kthread_work *base) +{ + struct drm_vblank_work *work = to_drm_vblank_work(base); + struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); + struct nv50_head *head = container_of(crc, struct nv50_head, crc); + struct drm_crtc *crtc = &head->base.base; + struct nv50_disp *disp = nv50_disp(crtc->dev); + u8 new_idx = crc->ctx_idx ^ 1; + + /* + * We don't want to accidentally wait for longer then the vblank, so + * try again for the next vblank if we don't grab the lock + */ + if (!mutex_trylock(&disp->mutex)) { + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Lock contended, delaying CRC ctx flip for head-%d\n", + head->base.index); + drm_vblank_work_schedule(work, + drm_crtc_vblank_count(crtc) + 1, + true); + return; + } + + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Flipping notifier ctx for head %d (%d -> %d)\n", + drm_crtc_index(crtc), crc->ctx_idx, new_idx); + + nv50_crc_program_ctx(head, NULL); + nv50_crc_program_ctx(head, &crc->ctx[new_idx]); + mutex_unlock(&disp->mutex); + + spin_lock_irq(&crc->lock); + crc->ctx_changed = true; + spin_unlock_irq(&crc->lock); +} + +static inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) +{ + memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); +} + +static void +nv50_crc_get_entries(struct nv50_head *head, + const struct nv50_crc_func *func, + enum nv50_crc_source source) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + u32 output_crc; + + while (crc->entry_idx < func->num_entries) { + /* + * While Nvidia's documentation says CRCs are written on each + * subsequent vblank after being enabled, in practice they + * aren't written immediately. + */ + output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], + source, crc->entry_idx); + if (!output_crc) + return; + + drm_crtc_add_crc_entry(crtc, true, crc->frame, &output_crc); + crc->frame++; + crc->entry_idx++; + } +} + +void nv50_crc_handle_vblank(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + struct nv50_crc_notifier_ctx *ctx; + bool need_reschedule = false; + + if (!func) + return; + + /* + * We don't lose events if we aren't able to report CRCs until the + * next vblank, so only report CRCs if the locks we need aren't + * contended to prevent missing an actual vblank event + */ + if (!spin_trylock(&crc->lock)) + return; + + if (!crc->src) + goto out; + + ctx = &crc->ctx[crc->ctx_idx]; + if (crc->ctx_changed && func->ctx_finished(head, ctx)) { + nv50_crc_get_entries(head, func, crc->src); + + crc->ctx_idx ^= 1; + crc->entry_idx = 0; + crc->ctx_changed = false; + + /* + * Unfortunately when notifier contexts are changed during CRC + * capture, we will inevitably lose the CRC entry for the + * frame where the hardware actually latched onto the first + * UPDATE. According to Nvidia's hardware engineers, there's + * no workaround for this. + * + * Now, we could try to be smart here and calculate the number + * of missed CRCs based on audit timestamps, but those were + * removed starting with volta. Since we always flush our + * updates back-to-back without waiting, we'll just be + * optimistic and assume we always miss exactly one frame. + */ + DRM_DEV_DEBUG_KMS(head->base.base.dev->dev, + "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n", + head->base.index, crc->frame); + crc->frame++; + + nv50_crc_reset_ctx(ctx); + need_reschedule = true; + } + + nv50_crc_get_entries(head, func, crc->src); + + if (need_reschedule) + drm_vblank_work_schedule(&crc->flip_work, + drm_crtc_vblank_count(crtc) + + crc->flip_threshold + - crc->entry_idx, + true); + +out: + spin_unlock(&crc->lock); +} + +static void nv50_crc_wait_ctx_finished(struct nv50_head *head, + const struct nv50_crc_func *func, + struct nv50_crc_notifier_ctx *ctx) +{ + struct drm_device *dev = head->base.base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + s64 ret; + + ret = nvif_msec(&drm->client.device, 50, + if (func->ctx_finished(head, ctx)) break;); + if (ret == -ETIMEDOUT) + NV_ERROR(drm, + "CRC notifier ctx for head %d not finished after 50ms\n", + head->base.index); + else if (ret) + NV_ATOMIC(drm, + "CRC notifier ctx for head-%d finished after %lldns\n", + head->base.index, ret); +} + +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + + if (!asyh->clr.crc) + continue; + + spin_lock_irq(&crc->lock); + crc->src = NV50_CRC_SOURCE_NONE; + spin_unlock_irq(&crc->lock); + + drm_crtc_vblank_put(crtc); + drm_vblank_work_cancel_sync(&crc->flip_work); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d disabled\n", + head->base.index); + + /* CRC generation is still enabled in hw, we'll just report + * any remaining CRC entries ourselves after it gets disabled + * in hardware + */ + } +} + +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + int i; + + if (!asyh->set.crc) + continue; + + crc->entry_idx = 0; + crc->ctx_changed = false; + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_reset_ctx(&crc->ctx[i]); + } +} + +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) +{ + const struct nv50_crc_func *func = + nv50_disp(state->dev)->core->func->crc; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; + + if (!asyh->clr.crc) + continue; + + if (crc->ctx_changed) { + nv50_crc_wait_ctx_finished(head, func, ctx); + ctx = &crc->ctx[crc->ctx_idx ^ 1]; + } + nv50_crc_wait_ctx_finished(head, func, ctx); + } +} + +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + u64 vbl_count; + + if (!asyh->set.crc) + continue; + + drm_crtc_vblank_get(crtc); + + spin_lock_irq(&crc->lock); + vbl_count = drm_crtc_vblank_count(crtc); + crc->frame = vbl_count; + crc->src = asyh->crc.src; + drm_vblank_work_schedule(&crc->flip_work, + vbl_count + crc->flip_threshold, + true); + spin_unlock_irq(&crc->lock); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d enabled\n", + head->base.index); + } +} + +int nv50_crc_atomic_check_head(struct nv50_head *head, + struct nv50_head_atom *asyh, + struct nv50_head_atom *armh) +{ + struct nv50_atom *atom = nv50_atom(asyh->state.state); + struct drm_device *dev = head->base.base.dev; + struct nv50_disp *disp = nv50_disp(dev); + bool changed = armh->crc.src != asyh->crc.src; + + if (!armh->crc.src && !asyh->crc.src) { + asyh->set.crc = false; + asyh->clr.crc = false; + return 0; + } + + /* While we don't care about entry tags, Volta+ hw always needs the + * controlling wndw channel programmed to a wndw that's owned by our + * head + */ + if (asyh->crc.src && disp->disp->object.oclass >= GV100_DISP && + !(BIT(asyh->crc.wndw) & asyh->wndw.owned)) { + if (!asyh->wndw.owned) { + /* TODO: once we support flexible channel ownership, + * we should write some code here to handle attempting + * to "steal" a plane: e.g. take a plane that is + * currently not-visible and owned by another head, + * and reassign it to this head. If we fail to do so, + * we shuld reject the mode outright as CRC capture + * then becomes impossible. + */ + NV_ATOMIC(nouveau_drm(dev), + "No available wndws for CRC readback\n"); + return -EINVAL; + } + asyh->crc.wndw = ffs(asyh->wndw.owned) - 1; + } + + if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed || + armh->crc.wndw != asyh->crc.wndw) { + asyh->clr.crc = armh->crc.src && armh->state.active; + asyh->set.crc = asyh->crc.src && asyh->state.active; + if (changed) + asyh->set.or |= armh->or.crc_raster != + asyh->or.crc_raster; + + if (asyh->clr.crc && asyh->set.crc) + atom->flush_disable = true; + } else { + asyh->set.crc = false; + asyh->clr.crc = false; + } + + return 0; +} + +void nv50_crc_atomic_check_outp(struct nv50_atom *atom) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; + + if (atom->flush_disable) + return; + + for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, + new_crtc_state, i) { + struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_outp_atom *outp_atom; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(armh)); + struct drm_encoder *encoder = &outp->base.base; + + if (!asyh->clr.crc) + continue; + + /* + * Re-programming ORs can't be done in the same flush as + * disabling CRCs + */ + list_for_each_entry(outp_atom, &atom->outp, head) { + if (outp_atom->encoder == encoder) { + if (outp_atom->set.mask) { + atom->flush_disable = true; + return; + } else { + break; + } + } + } + } +} + +static enum nv50_crc_source_type +nv50_crc_source_type(struct nouveau_encoder *outp, + enum nv50_crc_source source) +{ + struct dcb_output *dcbe = outp->dcb; + + switch (source) { + case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; + case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; + default: break; + } + + if (dcbe->location != DCB_LOC_ON_CHIP) + return NV50_CRC_SOURCE_TYPE_PIOR; + + switch (dcbe->type) { + case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; + case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; + default: return NV50_CRC_SOURCE_TYPE_SOR; + } +} + +void nv50_crc_atomic_set(struct nv50_head *head, + struct nv50_head_atom *asyh) +{ + struct drm_crtc *crtc = &head->base.base; + struct drm_device *dev = crtc->dev; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(asyh)); + + func->set_src(head, outp->or, + nv50_crc_source_type(outp, asyh->crc.src), + &crc->ctx[crc->ctx_idx], asyh->crc.wndw); +} + +void nv50_crc_atomic_clr(struct nv50_head *head) +{ + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + + func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0); +} + +#define NV50_CRC_RASTER_ACTIVE 0 +#define NV50_CRC_RASTER_COMPLETE 1 +#define NV50_CRC_RASTER_INACTIVE 2 + +static inline int +nv50_crc_raster_type(enum nv50_crc_source source) +{ + switch (source) { + case NV50_CRC_SOURCE_NONE: + case NV50_CRC_SOURCE_AUTO: + case NV50_CRC_SOURCE_RG: + case NV50_CRC_SOURCE_OUTP_ACTIVE: + return NV50_CRC_RASTER_ACTIVE; + case NV50_CRC_SOURCE_OUTP_COMPLETE: + return NV50_CRC_RASTER_COMPLETE; + case NV50_CRC_SOURCE_OUTP_INACTIVE: + return NV50_CRC_RASTER_INACTIVE; + } + + return 0; +} + +/* We handle mapping the memory for CRC notifiers ourselves, since each + * notifier needs it's own handle + */ +static inline int +nv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, + struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) +{ + struct nv50_core *core = nv50_disp(head->base.base.dev)->core; + int ret; + + ret = nvif_mem_init_map(mmu, NVIF_MEM_VRAM, len, &ctx->mem); + if (ret) + return ret; + + ret = nvif_object_init(&core->chan.base.user, + NV50_DISP_HANDLE_CRC_CTX(head, idx), + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = ctx->mem.addr, + .limit = ctx->mem.addr + + ctx->mem.size - 1, + }, sizeof(struct nv_dma_v0), + &ctx->ntfy); + if (ret) + goto fail_fini; + + return 0; + +fail_fini: + nvif_mem_fini(&ctx->mem); + return ret; +} + +static inline void +nv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) +{ + nvif_object_fini(&ctx->ntfy); + nvif_mem_fini(&ctx->mem); +} + +int nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct nv50_head *head = nv50_head(crtc); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; + struct nv50_head_atom *asyh; + struct drm_crtc_state *crtc_state; + enum nv50_crc_source source; + int ret = 0, ctx_flags = 0, i; + + ret = nv50_crc_parse_source(source_str, &source); + if (ret) + return ret; + + /* + * Since we don't want the user to accidentally interrupt us as we're + * disabling CRCs + */ + if (source) + ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; + drm_modeset_acquire_init(&ctx, ctx_flags); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out_acquire_fini; + } + state->acquire_ctx = &ctx; + + if (source) { + for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { + ret = nv50_crc_ctx_init(head, mmu, &crc->ctx[i], + func->notifier_len, i); + if (ret) + goto out_ctx_fini; + } + } + +retry: + crtc_state = drm_atomic_get_crtc_state(state, &head->base.base); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + } + asyh = nv50_head_atom(crtc_state); + asyh->crc.src = source; + asyh->or.crc_raster = nv50_crc_raster_type(source); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + + if (!source) { + /* + * If the user specified a custom flip threshold through + * debugfs, reset it + */ + crc->flip_threshold = func->flip_threshold; + } + +out_drop_locks: + drm_modeset_drop_locks(&ctx); +out_ctx_fini: + if (!source || ret) { + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_ctx_fini(&crc->ctx[i]); + } + drm_atomic_state_put(state); +out_acquire_fini: + drm_modeset_acquire_fini(&ctx); + return ret; + +deadlock: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + goto retry; +} + +static int +nv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) +{ + struct nv50_head *head = m->private; + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + int ret; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + seq_printf(m, "%d\n", crc->flip_threshold); + + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static int +nv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) +{ + return single_open(file, nv50_crc_debugfs_flip_threshold_get, + inode->i_private); +} + +static ssize_t +nv50_crc_debugfs_flip_threshold_set(struct file *file, + const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct nv50_head *head = m->private; + struct nv50_head_atom *armh; + struct drm_crtc *crtc = &head->base.base; + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + int value, ret; + + ret = kstrtoint_from_user(ubuf, len, 10, &value); + if (ret) + return ret; + + if (value > func->flip_threshold) + return -EINVAL; + else if (value == -1) + value = func->flip_threshold; + else if (value < -1) + return -EINVAL; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + armh = nv50_head_atom(crtc->state); + if (armh->crc.src) { + ret = -EBUSY; + goto out; + } + + NV_DEBUG(drm, + "Changing CRC flip threshold for next capture on head-%d to %d\n", + head->base.index, value); + crc->flip_threshold = value; + ret = len; + +out: + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static const struct file_operations nv50_crc_flip_threshold_fops = { + .owner = THIS_MODULE, + .open = nv50_crc_debugfs_flip_threshold_open, + .read = seq_read, + .write = nv50_crc_debugfs_flip_threshold_set, +}; + +int nv50_head_crc_late_register(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + struct dentry *root; + + if (!func || !crtc->debugfs_entry) + return 0; + + root = debugfs_create_dir("nv_crc", crtc->debugfs_entry); + debugfs_create_file("flip_threshold", 0644, root, head, + &nv50_crc_flip_threshold_fops); + + return 0; +} + +static inline void +nv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, + struct nv50_head *head) +{ + struct nv50_crc *crc = &head->crc; + + crc->flip_threshold = func->flip_threshold; + spin_lock_init(&crc->lock); + drm_vblank_work_init(&crc->flip_work, &head->base.base, + nv50_crc_ctx_flip_work); +} + +void nv50_crc_init(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct drm_crtc *crtc; + const struct nv50_crc_func *func = disp->core->func->crc; + + if (!func) + return; + + drm_for_each_crtc(crtc, dev) + nv50_crc_init_head(disp, func, nv50_head(crtc)); +} diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.h b/drivers/gpu/drm/nouveau/dispnv50/crc.h new file mode 100644 index 000000000000..4bc59e779315 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_CRC_H__ +#define __NV50_CRC_H__ + +#include <linux/mutex.h> +#include <drm/drm_crtc.h> +#include <drm/drm_vblank_work.h> + +#include <nvif/mem.h> +#include <nvkm/subdev/bios.h> +#include "nouveau_encoder.h" + +struct nv50_atom; +struct nv50_disp; +struct nv50_head; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +enum nv50_crc_source { + NV50_CRC_SOURCE_NONE = 0, + NV50_CRC_SOURCE_AUTO, + NV50_CRC_SOURCE_RG, + NV50_CRC_SOURCE_OUTP_ACTIVE, + NV50_CRC_SOURCE_OUTP_COMPLETE, + NV50_CRC_SOURCE_OUTP_INACTIVE, +}; + +/* RG -> SF (DP only) + * -> SOR + * -> PIOR + * -> DAC + */ +enum nv50_crc_source_type { + NV50_CRC_SOURCE_TYPE_NONE = 0, + NV50_CRC_SOURCE_TYPE_SOR, + NV50_CRC_SOURCE_TYPE_PIOR, + NV50_CRC_SOURCE_TYPE_DAC, + NV50_CRC_SOURCE_TYPE_RG, + NV50_CRC_SOURCE_TYPE_SF, +}; + +struct nv50_crc_notifier_ctx { + struct nvif_mem mem; + struct nvif_object ntfy; +}; + +struct nv50_crc_atom { + enum nv50_crc_source src; + /* Only used for gv100+ */ + u8 wndw : 4; +}; + +struct nv50_crc_func { + void (*set_src)(struct nv50_head *, int or, enum nv50_crc_source_type, + struct nv50_crc_notifier_ctx *, u32 wndw); + void (*set_ctx)(struct nv50_head *, struct nv50_crc_notifier_ctx *); + u32 (*get_entry)(struct nv50_head *, struct nv50_crc_notifier_ctx *, + enum nv50_crc_source, int idx); + bool (*ctx_finished)(struct nv50_head *, + struct nv50_crc_notifier_ctx *); + short flip_threshold; + short num_entries; + size_t notifier_len; +}; + +struct nv50_crc { + spinlock_t lock; + struct nv50_crc_notifier_ctx ctx[2]; + struct drm_vblank_work flip_work; + enum nv50_crc_source src; + + u64 frame; + short entry_idx; + short flip_threshold; + u8 ctx_idx : 1; + bool ctx_changed : 1; +}; + +void nv50_crc_init(struct drm_device *dev); +int nv50_head_crc_late_register(struct nv50_head *); +void nv50_crc_handle_vblank(struct nv50_head *head); + +int nv50_crc_verify_source(struct drm_crtc *, const char *, size_t *); +const char *const *nv50_crc_get_sources(struct drm_crtc *, size_t *); +int nv50_crc_set_source(struct drm_crtc *, const char *); + +int nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *); +void nv50_crc_atomic_check_outp(struct nv50_atom *atom); +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *); +void nv50_crc_atomic_clr(struct nv50_head *); + +extern const struct nv50_crc_func crc907d; +extern const struct nv50_crc_func crcc37d; + +#else /* IS_ENABLED(CONFIG_DEBUG_FS) */ +struct nv50_crc {}; +struct nv50_crc_func {}; +struct nv50_crc_atom {}; + +#define nv50_crc_verify_source NULL +#define nv50_crc_get_sources NULL +#define nv50_crc_set_source NULL + +static inline void nv50_crc_init(struct drm_device *dev) {} +static inline int nv50_head_crc_late_register(struct nv50_head *) {} +static inline void +nv50_crc_handle_vblank(struct nv50_head *head) { return 0; } + +static inline int +nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *) {} +static inline void nv50_crc_atomic_check_outp(struct nv50_atom *atom) {} +static inline void +nv50_crc_atomic_stop_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_start_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *) {} +static inline void +nv50_crc_atomic_clr(struct nv50_head *) {} + +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ +#endif /* !__NV50_CRC_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc907d.c b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c new file mode 100644 index 000000000000..92e907de7645 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT +#include <drm/drm_crtc.h> + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRC907D_MAX_ENTRIES 255 + +struct crc907d_notifier { + u32 status; + u32 :32; /* reserved */ + struct crc907d_entry { + u32 status; + u32 compositor_crc; + u32 output_crc[2]; + } entries[CRC907D_MAX_ENTRIES]; +} __packed; + +static void +crc907d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x300; + u32 *push; + u32 crc_args = 0xfff00000; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args |= (0x00000f0f + or * 16) << 8; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args |= (0x000000ff + or * 256) << 8; + break; + case NV50_CRC_SOURCE_TYPE_DAC: + crc_args |= (0x00000ff0 + or) << 8; + break; + case NV50_CRC_SOURCE_TYPE_RG: + crc_args |= (0x00000ff8 + drm_crtc_index(crtc)) << 8; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args |= (0x00000f8f + drm_crtc_index(crtc) * 16) << 8; + break; + case NV50_CRC_SOURCE_NONE: + crc_args |= 0x000fff00; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + } else { + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, 0); + } + evo_kick(push, core); +} + +static void crc907d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x0438 + (head->base.index * 0x300), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crc907d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + + return ioread32_native(¬ifier->entries[idx].output_crc[0]); +} + +static bool crc907d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000003e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "DSI"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "CRC output 1"; break; + case 0x00000020: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crc907d = { + .set_src = crc907d_set_src, + .set_ctx = crc907d_set_ctx, + .get_entry = crc907d_get_entry, + .ctx_finished = crc907d_ctx_finished, + .flip_threshold = CRC907D_MAX_ENTRIES - 10, + .num_entries = CRC907D_MAX_ENTRIES, + .notifier_len = sizeof(struct crc907d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c new file mode 100644 index 000000000000..940cefd5517d --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +#include <drm/drm_crtc.h> + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRCC37D_MAX_ENTRIES 2047 + +struct crcc37d_notifier { + u32 status; + + /* reserved */ + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + + struct crcc37d_entry { + u32 status[2]; + u32 :32; /* reserved */ + u32 compositor_crc; + u32 rg_crc; + u32 output_crc[2]; + u32 :32; /* reserved */ + } entries[CRCC37D_MAX_ENTRIES]; +} __packed; + +static void +crcc37d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x400; + u32 *push; + u32 crc_args; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args = (0x00000050 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args = (0x00000060 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args = 0x00000030 << 12; + break; + default: + crc_args = 0; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, crc_args | wndw); + } else { + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, 0); + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, 0); + } + + evo_kick(push, core); +} + +static void crcc37d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x2180 + (head->base.index * 0x400), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crcc37d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + struct crcc37d_entry __iomem *entry = ¬ifier->entries[idx]; + u32 __iomem *crc_addr; + + if (source == NV50_CRC_SOURCE_RG) + crc_addr = &entry->rg_crc; + else + crc_addr = &entry->output_crc[0]; + + return ioread32_native(crc_addr); +} + +static bool crcc37d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000007e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "Front End"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "RG"; break; + case 0x00000020: engine = "CRC output 1"; break; + case 0x00000040: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crcc37d = { + .set_src = crcc37d_set_src, + .set_ctx = crcc37d_set_ctx, + .get_entry = crcc37d_get_entry, + .ctx_finished = crcc37d_ctx_finished, + .flip_threshold = CRCC37D_MAX_ENTRIES - 30, + .num_entries = CRCC37D_MAX_ENTRIES, + .notifier_len = sizeof(struct crcc37d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 519f99868e35..4c2894d8e15b 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -26,6 +26,7 @@ #include "core.h" #include "head.h" #include "wndw.h" +#include "handles.h" #include <linux/dma-mapping.h> #include <linux/hdmi.h> @@ -58,24 +59,6 @@ #include <subdev/bios/dp.h> /****************************************************************************** - * Atomic state - *****************************************************************************/ - -struct nv50_outp_atom { - struct list_head head; - - struct drm_encoder *encoder; - bool flush_disable; - - union nv50_outp_atom_mask { - struct { - bool ctrl:1; - }; - u8 mask; - } set, clr; -}; - -/****************************************************************************** * EVO channel *****************************************************************************/ @@ -172,7 +155,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (!syncbuf) return 0; - ret = nvif_object_init(&dmac->base.user, 0xf0000000, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_SYNCBUF, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, @@ -183,7 +167,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (ret) return ret; - ret = nvif_object_init(&dmac->base.user, 0xf0000001, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_VRAM, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, @@ -801,6 +786,19 @@ struct nv50_msto { bool disabled; }; +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) +{ + struct nv50_msto *msto; + + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) + return nouveau_encoder(encoder); + + msto = nv50_msto(encoder); + if (!msto->mstc) + return NULL; + return msto->mstc->mstm->outp; +} + static struct drm_dp_payload * nv50_msto_payload(struct nv50_msto *msto) { @@ -1948,8 +1946,10 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) struct nv50_outp_atom *outp, *outt; u32 interlock[NV50_DISP_INTERLOCK__SIZE] = {}; int i; + bool flushed = false; NV_ATOMIC(drm, "commit %d %d\n", atom->lock_core, atom->flush_disable); + nv50_crc_atomic_stop_reporting(state); drm_atomic_helper_wait_for_fences(dev, state, false); drm_atomic_helper_wait_for_dependencies(state); drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -2007,6 +2007,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } } @@ -2017,9 +2019,15 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } + if (flushed) + nv50_crc_atomic_release_notifier_contexts(state); + nv50_crc_atomic_init_notifier_contexts(state); + /* Update output path(s). */ list_for_each_entry_safe(outp, outt, &atom->outp, head) { const struct drm_encoder_helper_funcs *help; @@ -2133,6 +2141,9 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) } } + nv50_crc_atomic_start_reporting(state); + if (!flushed) + nv50_crc_atomic_release_notifier_contexts(state); drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_commit_cleanup_done(state); @@ -2290,12 +2301,28 @@ static int nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct nv50_atom *atom = nv50_atom(state); + struct nv50_core *core = nv50_disp(dev)->core; struct drm_connector_state *old_connector_state, *new_connector_state; struct drm_connector *connector; struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; + struct nv50_head *head; + struct nv50_head_atom *asyh; int ret, i; + if (core->assign_windows && core->func->head->static_wndw_map) { + drm_for_each_crtc(crtc, dev) { + new_crtc_state = drm_atomic_get_crtc_state(state, + crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + head = nv50_head(crtc); + asyh = nv50_head_atom(new_crtc_state); + core->func->head->static_wndw_map(head, asyh); + } + } + /* We need to handle colour management on a per-plane basis. */ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->color_mgmt_changed) { @@ -2323,6 +2350,8 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) if (ret) return ret; + nv50_crc_atomic_check_outp(atom); + return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index 696e70a6b98b..1968c6921f9e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -1,10 +1,12 @@ #ifndef __NV50_KMS_H__ #define __NV50_KMS_H__ +#include <linux/workqueue.h> #include <nvif/mem.h> #include "nouveau_display.h" struct nv50_msto; +struct nouveau_encoder; struct nv50_disp { struct nvif_disp *disp; @@ -71,11 +73,33 @@ struct nv50_dmac { struct mutex lock; }; +struct nv50_outp_atom { + struct list_head head; + + struct drm_encoder *encoder; + bool flush_disable; + + union nv50_outp_atom_mask { + struct { + bool ctrl:1; + }; + u8 mask; + } set, clr; +}; + int nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, const s32 *oclass, u8 head, void *data, u32 size, u64 syncbuf, struct nv50_dmac *dmac); void nv50_dmac_destroy(struct nv50_dmac *); +/* + * For normal encoders this just returns the encoder. For active MST encoders, + * this returns the real outp that's driving displays on the topology. + * Inactive MST encoders return NULL, since they would have no real outp to + * return anyway. + */ +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder); + u32 *evo_wait(struct nv50_dmac *, int nr); void evo_kick(u32 *, struct nv50_dmac *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/handles.h b/drivers/gpu/drm/nouveau/dispnv50/handles.h new file mode 100644 index 000000000000..a97a7bd29243 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/handles.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_KMS_HANDLES_H__ +#define __NV50_KMS_HANDLES_H__ + +/* + * Various hard-coded object handles that nouveau uses. These are made-up by + * nouveau developers, not Nvidia. The only significance of the handles chosen + * is that they must all be unique. + */ +#define NV50_DISP_HANDLE_SYNCBUF 0xf0000000 +#define NV50_DISP_HANDLE_VRAM 0xf0000001 + +#define NV50_DISP_HANDLE_WNDW_CTX(kind) (0xfb000000 | kind) +#define NV50_DISP_HANDLE_CRC_CTX(head, i) (0xfc000000 | head->base.index << 1 | i) + +#endif /* !__NV50_KMS_HANDLES_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 8f6455697ba7..9a10ec267d1f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -24,13 +24,17 @@ #include "core.h" #include "curs.h" #include "ovly.h" +#include "crc.h" #include <nvif/class.h> +#include <nvif/event.h> +#include <nvif/cl0046.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_vblank.h> #include "nouveau_connector.h" + void nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool flush) @@ -38,6 +42,7 @@ nv50_head_flush_clr(struct nv50_head *head, union nv50_head_atom_mask clr = { .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), }; + if (clr.crc) nv50_crc_atomic_clr(head); if (clr.olut) head->func->olut_clr(head); if (clr.core) head->func->core_clr(head); if (clr.curs) head->func->curs_clr(head); @@ -61,6 +66,7 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) if (asyh->set.ovly ) head->func->ovly (head, asyh); if (asyh->set.dither ) head->func->dither (head, asyh); if (asyh->set.procamp) head->func->procamp (head, asyh); + if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh); if (asyh->set.or ) head->func->or (head, asyh); } @@ -84,18 +90,20 @@ nv50_head_atomic_check_dither(struct nv50_head_atom *armh, { u32 mode = 0x00; - if (asyc->dither.mode == DITHERING_MODE_AUTO) { - if (asyh->base.depth > asyh->or.bpc * 3) - mode = DITHERING_MODE_DYNAMIC2X2; - } else { - mode = asyc->dither.mode; - } + if (asyc->dither.mode) { + if (asyc->dither.mode == DITHERING_MODE_AUTO) { + if (asyh->base.depth > asyh->or.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = asyc->dither.mode; + } - if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { - if (asyh->or.bpc >= 8) - mode |= DITHERING_DEPTH_8BPC; - } else { - mode |= asyc->dither.depth; + if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { + if (asyh->or.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= asyc->dither.depth; + } } asyh->dither.enable = mode; @@ -311,7 +319,7 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) struct nouveau_conn_atom *asyc = NULL; struct drm_connector_state *conns; struct drm_connector *conn; - int i; + int i, ret; NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); if (asyh->state.active) { @@ -406,6 +414,10 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) asyh->set.curs = asyh->curs.visible; } + ret = nv50_crc_atomic_check_head(head, asyh, armh); + if (ret) + return ret; + if (asyh->clr.mask || asyh->set.mask) nv50_atom(asyh->state.state)->lock_core = true; return 0; @@ -444,6 +456,7 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) asyh->ovly = armh->ovly; asyh->dither = armh->dither; asyh->procamp = armh->procamp; + asyh->crc = armh->crc; asyh->or = armh->or; asyh->dp = armh->dp; asyh->clr.mask = 0; @@ -465,10 +478,18 @@ nv50_head_reset(struct drm_crtc *crtc) __drm_atomic_helper_crtc_reset(crtc, &asyh->state); } +static int +nv50_head_late_register(struct drm_crtc *crtc) +{ + return nv50_head_crc_late_register(nv50_head(crtc)); +} + static void nv50_head_destroy(struct drm_crtc *crtc) { struct nv50_head *head = nv50_head(crtc); + + nvif_notify_fini(&head->base.vblank); nv50_lut_fini(&head->olut); drm_crtc_cleanup(crtc); kfree(head); @@ -486,8 +507,38 @@ nv50_head_func = { .enable_vblank = nouveau_display_vblank_enable, .disable_vblank = nouveau_display_vblank_disable, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .late_register = nv50_head_late_register, +}; + +static const struct drm_crtc_funcs +nvd9_head_func = { + .reset = nv50_head_reset, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .destroy = nv50_head_destroy, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = nv50_head_atomic_duplicate_state, + .atomic_destroy_state = nv50_head_atomic_destroy_state, + .enable_vblank = nouveau_display_vblank_enable, + .disable_vblank = nouveau_display_vblank_disable, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .verify_crc_source = nv50_crc_verify_source, + .get_crc_sources = nv50_crc_get_sources, + .set_crc_source = nv50_crc_set_source, + .late_register = nv50_head_late_register, }; +static int nv50_head_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + if (drm_crtc_handle_vblank(&nv_crtc->base)) + nv50_crc_handle_vblank(nv50_head(&nv_crtc->base)); + + return NVIF_NOTIFY_KEEP; +} + struct nv50_head * nv50_head_create(struct drm_device *dev, int index) { @@ -495,7 +546,9 @@ nv50_head_create(struct drm_device *dev, int index) struct nv50_disp *disp = nv50_disp(dev); struct nv50_head *head; struct nv50_wndw *base, *ovly, *curs; + struct nouveau_crtc *nv_crtc; struct drm_crtc *crtc; + const struct drm_crtc_funcs *funcs; int ret; head = kzalloc(sizeof(*head), GFP_KERNEL); @@ -505,6 +558,11 @@ nv50_head_create(struct drm_device *dev, int index) head->func = disp->core->func->head; head->base.index = index; + if (disp->disp->object.oclass < GF110_DISP) + funcs = &nv50_head_func; + else + funcs = &nvd9_head_func; + if (disp->disp->object.oclass < GV100_DISP) { ret = nv50_base_new(drm, head->base.index, &base); ret = nv50_ovly_new(drm, head->base.index, &ovly); @@ -521,9 +579,10 @@ nv50_head_create(struct drm_device *dev, int index) return ERR_PTR(ret); } - crtc = &head->base.base; + nv_crtc = &head->base; + crtc = &nv_crtc->base; drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, - &nv50_head_func, "head-%d", head->base.index); + funcs, "head-%d", head->base.index); drm_crtc_helper_add(crtc, &nv50_head_help); /* Keep the legacy gamma size at 256 to avoid compatibility issues */ drm_mode_crtc_set_gamma_size(crtc, 256); @@ -539,5 +598,16 @@ nv50_head_create(struct drm_device *dev, int index) } } + ret = nvif_notify_init(&disp->disp->object, nv50_head_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + if (ret) + return ERR_PTR(ret); + return head; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index c32b27cdaefc..30501ad1824e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -1,22 +1,28 @@ #ifndef __NV50_KMS_HEAD_H__ #define __NV50_KMS_HEAD_H__ #define nv50_head(c) container_of((c), struct nv50_head, base.base) +#include <linux/workqueue.h> + #include "disp.h" #include "atom.h" +#include "crc.h" #include "lut.h" #include "nouveau_crtc.h" +#include "nouveau_encoder.h" struct nv50_head { const struct nv50_head_func *func; struct nouveau_crtc base; + struct nv50_crc crc; struct nv50_lut olut; struct nv50_msto *msto; }; struct nv50_head *nv50_head_create(struct drm_device *, int index); -void nv50_head_flush_set(struct nv50_head *, struct nv50_head_atom *); -void nv50_head_flush_clr(struct nv50_head *, struct nv50_head_atom *, bool y); +void nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh); +void nv50_head_flush_clr(struct nv50_head *head, + struct nv50_head_atom *asyh, bool flush); struct nv50_head_func { void (*view)(struct nv50_head *, struct nv50_head_atom *); @@ -40,6 +46,7 @@ struct nv50_head_func { void (*dither)(struct nv50_head *, struct nv50_head_atom *); void (*procamp)(struct nv50_head *, struct nv50_head_atom *); void (*or)(struct nv50_head *, struct nv50_head_atom *); + void (*static_wndw_map)(struct nv50_head *, struct nv50_head_atom *); }; extern const struct nv50_head_func head507d; @@ -86,6 +93,7 @@ int headc37d_curs_format(struct nv50_head *, struct nv50_wndw_atom *, void headc37d_curs_set(struct nv50_head *, struct nv50_head_atom *); void headc37d_curs_clr(struct nv50_head *); void headc37d_dither(struct nv50_head *, struct nv50_head_atom *); +void headc37d_static_wndw_map(struct nv50_head *, struct nv50_head_atom *); extern const struct nv50_head_func headc57d; #endif diff --git a/drivers/gpu/drm/nouveau/dispnv50/head907d.c b/drivers/gpu/drm/nouveau/dispnv50/head907d.c index 3002ec23d7a6..63a0b45d96d6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head907d.c @@ -19,8 +19,15 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include <drm/drm_connector.h> +#include <drm/drm_mode_config.h> +#include <drm/drm_vblank.h> +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_connector.h" #include "head.h" #include "core.h" +#include "crc.h" void head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) @@ -29,9 +36,10 @@ head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) u32 *push; if ((push = evo_wait(core, 3))) { evo_mthd(push, 0x0404 + (head->base.index * 0x300), 2); - evo_data(push, 0x00000001 | asyh->or.depth << 6 | - asyh->or.nvsync << 4 | - asyh->or.nhsync << 3); + evo_data(push, asyh->or.depth << 6 | + asyh->or.nvsync << 4 | + asyh->or.nhsync << 3 | + asyh->or.crc_raster); evo_data(push, 0x31ec6000 | head->base.index << 25 | asyh->mode.interlace); evo_kick(push, core); diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index 4a9a32b89f74..35fcdf8825b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -27,26 +27,29 @@ static void headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0x00000001 | - asyh->or.depth << 4 | + evo_data(push, depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } @@ -201,6 +204,15 @@ headc37d_view(struct nv50_head *head, struct nv50_head_atom *asyh) } } +void +headc37d_static_wndw_map(struct nv50_head *head, struct nv50_head_atom *asyh) +{ + int i, end; + + for (i = head->base.index * 2, end = i + 2; i < end; i++) + asyh->wndw.owned |= BIT(i); +} + const struct nv50_head_func headc37d = { .view = headc37d_view, @@ -216,4 +228,5 @@ headc37d = { .dither = headc37d_dither, .procamp = headc37d_procamp, .or = headc37d_or, + .static_wndw_map = headc37d_static_wndw_map, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 859131a8bc3c..c7d04dd935fd 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -27,26 +27,30 @@ static void headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0xfc000001 | - asyh->or.depth << 4 | + evo_data(push, 0xfc000000 | + depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } @@ -208,4 +212,6 @@ headc57d = { .dither = headc37d_dither, .procamp = headc57d_procamp, .or = headc57d_or, + /* TODO: flexible window mappings */ + .static_wndw_map = headc37d_static_wndw_map, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c index 8ccd96113bad..4cce1078140a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c @@ -186,7 +186,7 @@ ovly507e_new_(const struct nv50_wndw_func *func, const u32 *format, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &wndw->wndw); + disp->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "ovly%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 99b9b681736d..293ccfdba17e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -21,6 +21,7 @@ */ #include "wndw.h" #include "wimm.h" +#include "handles.h" #include <nvif/class.h> #include <nvif/cl0002.h> @@ -59,7 +60,7 @@ nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb) int ret; nouveau_framebuffer_get_layout(fb, &unused, &kind); - handle = 0xfb000000 | kind; + handle = NV50_DISP_HANDLE_WNDW_CTX(kind); list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) { if (ctxdma->object.handle == handle) @@ -526,7 +527,7 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) } asyw->state.fence = dma_resv_get_excl_rcu(nvbo->bo.base.resv); - asyw->image.offset[0] = nvbo->bo.offset; + asyw->image.offset[0] = nvbo->offset; if (wndw->func->prepare) { asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc); diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c index b92dc3461bbd..bb84e4d54a33 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c @@ -298,7 +298,7 @@ wndwc37e_new_(const struct nv50_wndw_func *func, struct nouveau_drm *drm, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &wndw->wndw); + disp->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "qndw%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index e2bae1424502..5b2406950e53 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -139,7 +139,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16, if (chan->ntfy) { nouveau_vma_del(&chan->ntfy_vma); nouveau_bo_unpin(chan->ntfy); - drm_gem_object_put_unlocked(&chan->ntfy->bo.base); + drm_gem_object_put(&chan->ntfy->bo.base); } if (chan->heap.block_size) @@ -558,13 +558,13 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) if (drm->agp.bridge) { args.target = NV_DMA_V0_TARGET_AGP; args.access = NV_DMA_V0_ACCESS_RDWR; - args.start += drm->agp.base + chan->ntfy->bo.offset; - args.limit += drm->agp.base + chan->ntfy->bo.offset; + args.start += drm->agp.base + chan->ntfy->offset; + args.limit += drm->agp.base + chan->ntfy->offset; } else { args.target = NV_DMA_V0_TARGET_VM; args.access = NV_DMA_V0_ACCESS_RDWR; - args.start += chan->ntfy->bo.offset; - args.limit += chan->ntfy->bo.offset; + args.start += chan->ntfy->offset; + args.limit += chan->ntfy->offset; } client->route = NVDRM_OBJECT_ABI16; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index c40f127de3d0..4ccf937df0d0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -655,13 +655,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; @@ -675,7 +674,6 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, } man->func = &nouveau_vram_manager; - man->io_reserve_fastpath = false; man->use_io_reserve_lru = true; } else { man->func = &ttm_bo_manager_func; @@ -691,13 +689,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &ttm_bo_manager_func; if (drm->agp.bridge) { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; } else { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | - TTM_MEMTYPE_FLAG_CMA; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; } @@ -1317,6 +1314,14 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, bool evict, nouveau_vma_unmap(vma); } } + + if (new_reg) { + if (new_reg->mm_node) + nvbo->offset = (new_reg->start << PAGE_SHIFT); + else + nvbo->offset = 0; + } + } static int @@ -1431,7 +1436,6 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) static int nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) { - struct ttm_mem_type_manager *man = &bdev->man[reg->mem_type]; struct nouveau_drm *drm = nouveau_bdev(bdev); struct nvkm_device *device = nvxx_device(&drm->client.device); struct nouveau_mem *mem = nouveau_mem(reg); @@ -1441,8 +1445,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) reg->bus.size = reg->num_pages << PAGE_SHIFT; reg->bus.base = 0; reg->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (reg->mem_type) { case TTM_PL_SYSTEM: /* System memory */ @@ -1497,8 +1500,6 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) if (ret != 1) { if (WARN_ON(ret == 0)) return -EINVAL; - if (ret == -ENOSPC) - return -EAGAIN; return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 38f9d8350963..e944b4aa5547 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -24,6 +24,9 @@ struct nouveau_bo { int pbbo_index; bool validate_mapped; + /* GPU address space is independent of CPU word size */ + uint64_t offset; + struct list_head vma_list; unsigned contig:1; diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index d9381a053169..3d71dfcb2fde 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -162,7 +162,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device, * pushbuf lives in, this is because the GEM code requires that * we be able to call out to other (indirect) push buffers */ - chan->push.addr = chan->push.buffer->bo.offset; + chan->push.addr = chan->push.buffer->offset; if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { ret = nouveau_vma_new(chan->push.buffer, chan->vmm, diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1b383ae0248f..ab2c2b2cab10 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -60,7 +60,6 @@ nouveau_conn_native_mode(struct drm_connector *connector) int high_w = 0, high_h = 0, high_v = 0; list_for_each_entry(mode, &connector->probed_modes, head) { - mode->vrefresh = drm_mode_vrefresh(mode); if (helper->mode_valid(connector, mode) != MODE_OK || (mode->flags & DRM_MODE_FLAG_INTERLACE)) continue; @@ -81,12 +80,12 @@ nouveau_conn_native_mode(struct drm_connector *connector) continue; if (mode->hdisplay == high_w && mode->vdisplay == high_h && - mode->vrefresh < high_v) + drm_mode_vrefresh(mode) < high_v) continue; high_w = mode->hdisplay; high_h = mode->vdisplay; - high_v = mode->vrefresh; + high_v = drm_mode_vrefresh(mode); largest = mode; } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 496c4621cc78..657554cf011e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -44,15 +44,7 @@ #include <nvif/class.h> #include <nvif/cl0046.h> #include <nvif/event.h> - -static int -nouveau_display_vblank_handler(struct nvif_notify *notify) -{ - struct nouveau_crtc *nv_crtc = - container_of(notify, typeof(*nv_crtc), vblank); - drm_crtc_handle_vblank(&nv_crtc->base); - return NVIF_NOTIFY_KEEP; -} +#include <dispnv50/crc.h> int nouveau_display_vblank_enable(struct drm_crtc *crtc) @@ -136,50 +128,6 @@ nouveau_display_scanoutpos(struct drm_crtc *crtc, stime, etime); } -static void -nouveau_display_vblank_fini(struct drm_device *dev) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - nvif_notify_fini(&nv_crtc->vblank); - } -} - -static int -nouveau_display_vblank_init(struct drm_device *dev) -{ - struct nouveau_display *disp = nouveau_display(dev); - struct drm_crtc *crtc; - int ret; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - ret = nvif_notify_init(&disp->disp.object, - nouveau_display_vblank_handler, false, - NV04_DISP_NTFY_VBLANK, - &(struct nvif_notify_head_req_v0) { - .head = nv_crtc->index, - }, - sizeof(struct nvif_notify_head_req_v0), - sizeof(struct nvif_notify_head_rep_v0), - &nv_crtc->vblank); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - } - - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - - return 0; -} - static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, @@ -426,7 +374,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev, if (ret == 0) return fb; - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return ERR_PTR(ret); } @@ -705,9 +653,12 @@ nouveau_display_create(struct drm_device *dev) drm_mode_config_reset(dev); if (dev->mode_config.num_crtc) { - ret = nouveau_display_vblank_init(dev); + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) goto vblank_err; + + if (disp->disp.object.oclass >= NV50_DISP) + nv50_crc_init(dev); } INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); @@ -734,7 +685,6 @@ nouveau_display_destroy(struct drm_device *dev) #ifdef CONFIG_ACPI unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); #endif - nouveau_display_vblank_fini(dev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -808,7 +758,7 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, return ret; ret = drm_gem_handle_create(file_priv, &bo->bo.base, &args->handle); - drm_gem_object_put_unlocked(&bo->bo.base); + drm_gem_object_put(&bo->bo.base); return ret; } @@ -823,7 +773,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv, if (gem) { struct nouveau_bo *bo = nouveau_gem_object(gem); *poffset = drm_vma_node_offset_addr(&bo->bo.base.vma_node); - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index cc9993837508..462ee0493651 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -100,7 +100,7 @@ unsigned long nouveau_dmem_page_addr(struct page *page) unsigned long off = (page_to_pfn(page) << PAGE_SHIFT) - chunk->pagemap.res.start; - return chunk->bo->bo.offset + off; + return chunk->bo->offset + off; } static void nouveau_dmem_page_free(struct page *page) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 3d11b84d4cf9..f9f5a13fdb80 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -393,7 +393,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, /* To allow resizeing without swapping buffers */ NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n", - fb->width, fb->height, nvbo->bo.offset, nvbo); + fb->width, fb->height, nvbo->offset, nvbo); vga_switcheroo_client_fb_set(dev->pdev, info); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 4c3f131ad31d..63b832585390 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -234,7 +234,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, rep->domain = NOUVEAU_GEM_DOMAIN_GART; else rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; - rep->offset = nvbo->bo.offset; + rep->offset = nvbo->offset; if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50) { vma = nouveau_vma_find(nvbo, vmm); if (!vma) @@ -281,7 +281,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, } /* drop reference from allocate - handle holds it now */ - drm_gem_object_put_unlocked(&nvbo->bo.base); + drm_gem_object_put(&nvbo->bo.base); return ret; } @@ -360,7 +360,7 @@ validate_fini_no_ticket(struct validate_op *op, struct nouveau_channel *chan, list_del(&nvbo->entry); nvbo->reserved_by = NULL; ttm_bo_unreserve(&nvbo->bo); - drm_gem_object_put_unlocked(&nvbo->bo.base); + drm_gem_object_put(&nvbo->bo.base); } } @@ -407,14 +407,14 @@ retry: nvbo = nouveau_gem_object(gem); if (nvbo == res_bo) { res_bo = NULL; - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); continue; } if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { NV_PRINTK(err, cli, "multiple instances of buffer %d on " "validation list\n", b->handle); - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); ret = -EINVAL; break; } @@ -518,7 +518,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, } if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { - if (nvbo->bo.offset == b->presumed.offset && + if (nvbo->offset == b->presumed.offset && ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || (nvbo->bo.mem.mem_type == TTM_PL_TT && @@ -529,7 +529,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; else b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; - b->presumed.offset = nvbo->bo.offset; + b->presumed.offset = nvbo->offset; b->presumed.valid = 0; relocs++; } @@ -807,7 +807,7 @@ revalidate: struct nouveau_bo *nvbo = (void *)(unsigned long) bo[push[i].bo_index].user_priv; - OUT_RING(chan, (nvbo->bo.offset + push[i].offset) | 2); + OUT_RING(chan, (nvbo->offset + push[i].offset) | 2); OUT_RING(chan, 0); } } else { @@ -842,7 +842,7 @@ revalidate: } OUT_RING(chan, 0x20000000 | - (nvbo->bo.offset + push[i].offset)); + (nvbo->offset + push[i].offset)); OUT_RING(chan, 0); for (j = 0; j < NOUVEAU_DMA_SKIPS; j++) OUT_RING(chan, 0); @@ -931,7 +931,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, ret = lret; nouveau_bo_sync_for_cpu(nvbo); - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return ret; } @@ -950,7 +950,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, nvbo = nouveau_gem_object(gem); nouveau_bo_sync_for_device(nvbo); - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return 0; } @@ -967,7 +967,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, return -ENOENT; ret = nouveau_gem_info(file_priv, gem, req); - drm_gem_object_put_unlocked(gem); + drm_gem_object_put(gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 7ca0a2498532..e89ea052cf71 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -75,10 +75,6 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, ret = nouveau_mem_vram(reg, nvbo->contig, nvbo->page); if (ret) { nouveau_mem_del(reg); - if (ret == -ENOSPC) { - reg->mm_node = NULL; - return 0; - } return ret; } @@ -139,10 +135,6 @@ nv04_gart_manager_new(struct ttm_mem_type_manager *man, reg->num_pages << PAGE_SHIFT, &mem->vma[0]); if (ret) { nouveau_mem_del(reg); - if (ret == -ENOSPC) { - reg->mm_node = NULL; - return 0; - } return ret; } |