From 66bff06a0748b935e455d4ab75c7347bc392f81d Mon Sep 17 00:00:00 2001
From: Lyude Paul <lyude@redhat.com>
Date: Thu, 6 Feb 2020 15:08:29 -0500
Subject: [PATCH] squash! drm/nouveau/kms/nvd9-: Add CRC support

(Mostly) fix CRC enablement on Volta on heads other then the first head.
The nouveau_crc tests are still showing that we're losing track of the
association between each CRC entry and it's respective vblank sometimes
though, so we need to investigate why that's happening (maybe we need to
be more careful about draining pending CRC events in the atomic commit
path again?)
---
 drivers/gpu/drm/nouveau/dispnv50/crc.c     | 58 ++++++++++++++++------
 drivers/gpu/drm/nouveau/dispnv50/crc.h     | 24 ++++-----
 drivers/gpu/drm/nouveau/dispnv50/crc907d.c |  8 +--
 drivers/gpu/drm/nouveau/dispnv50/crcc37d.c | 21 +++-----
 drivers/gpu/drm/nouveau/dispnv50/head.c    | 10 ++--
 5 files changed, 72 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c
index c22b9c3ba7677..d3780b5968c6f 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/crc.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c
@@ -366,18 +366,49 @@ void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state)
 	}
 }
 
-void nv50_crc_atomic_check(struct nv50_head *head,
-			   struct nv50_head_atom *asyh,
-			   struct nv50_head_atom *armh)
+int nv50_crc_atomic_check(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_atomic_state *state = asyh->state.state;
+	struct drm_device *dev = head->base.base.dev;
+	struct nv50_atom *atom = nv50_atom(state);
+	struct nv50_disp *disp = nv50_disp(dev);
 	bool changed = armh->crc.src != asyh->crc.src;
 
-	if ((armh->crc.src || asyh->crc.src) &&
-	    (drm_atomic_crtc_needs_modeset(&asyh->state) || changed)) {
+	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;
+		changed = true;
+	}
+
+	if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed) {
 		asyh->clr.crc = armh->crc.src && armh->state.active;
 		asyh->set.crc = asyh->crc.src && asyh->state.active;
-		if (changed)
+		if (changed && asyh->set.crc)
 			asyh->set.or |= armh->or.crc_raster !=
 					asyh->or.crc_raster;
 		if (asyh->set.crc && asyh->clr.crc)
@@ -386,6 +417,8 @@ void nv50_crc_atomic_check(struct nv50_head *head,
 		asyh->set.crc = false;
 		asyh->clr.crc = false;
 	}
+
+	return 0;
 }
 
 static enum nv50_crc_source_type
@@ -411,7 +444,6 @@ nv50_crc_source_type(struct nouveau_encoder *outp,
 }
 
 void nv50_crc_atomic_set(struct nv50_head *head,
-			 struct nv50_head_atom *armh,
 			 struct nv50_head_atom *asyh)
 {
 	struct drm_crtc *crtc = &head->base.base;
@@ -423,19 +455,15 @@ void nv50_crc_atomic_set(struct nv50_head *head,
 
 	func->set_src(head, outp->or,
 		      nv50_crc_source_type(outp, asyh->crc.src),
-		      NV50_CRC_SOURCE_TYPE_NONE, &crc->ctx[crc->ctx_idx]);
+		      &crc->ctx[crc->ctx_idx], asyh->crc.wndw);
 }
 
-void nv50_crc_atomic_clr(struct nv50_head *head,
-			 struct nv50_head_atom *armh)
+void nv50_crc_atomic_clr(struct nv50_head *head)
 {
 	const struct nv50_crc_func *func =
 		nv50_disp(head->base.base.dev)->core->func->crc;
-	struct nouveau_encoder *outp =
-		nv50_real_outp(nv50_head_atom_get_encoder(armh));
 
-	func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE,
-		      nv50_crc_source_type(outp, armh->crc.src), NULL);
+	func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0);
 }
 
 #define NV50_CRC_RASTER_ACTIVE   0
diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.h b/drivers/gpu/drm/nouveau/dispnv50/crc.h
index 6e72f2f9e2ece..73b2193e44ccd 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/crc.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/crc.h
@@ -43,13 +43,13 @@ struct nv50_crc_notifier_ctx {
 
 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 to,
-			enum nv50_crc_source_type from,
-			struct nv50_crc_notifier_ctx *);
+	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 *);
 	bool (*get_entry)(struct nv50_head *, struct nv50_crc_notifier_ctx *,
 			  enum nv50_crc_source, int idx, u32 *);
@@ -81,14 +81,13 @@ 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 *);
 
-void nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *,
-			   struct nv50_head_atom *);
+int nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *,
+			  struct nv50_head_atom *);
 void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *);
 void nv50_crc_atomic_prepare_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 *armh,
-			 struct nv50_head_atom *asyh);
-void nv50_crc_atomic_clr(struct nv50_head *, struct nv50_head_atom *armh);
+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;
@@ -107,7 +106,7 @@ static inline void nv50_crc_fini(struct nv50_crc *) {}
 static inline void nv50_crc_get_entries(struct nv50_head *) {}
 static inline int nv50_crc_late_register(nv50_head *) { return 0; }
 
-static inline void
+static inline int
 nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *,
 		      struct nv50_head_atom *) {}
 static inline void
@@ -117,10 +116,9 @@ nv50_crc_atomic_prepare_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 *,
-		    struct nv50_head_atom *) {}
+nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *) {}
 static inline void
-nv50_crc_atomic_clr(struct nv50_head *, struct nv50_head_atom *) {}
+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
index 1a76b996e696b..947856315666e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/crc907d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c
@@ -19,8 +19,8 @@ struct crc907d_notifier {
 
 static void
 crc907d_set_src(struct nv50_head *head, int or,
-		enum nv50_crc_source_type to, enum nv50_crc_source_type from,
-		struct nv50_crc_notifier_ctx *ctx)
+		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;
@@ -28,7 +28,7 @@ crc907d_set_src(struct nv50_head *head, int or,
 	u32 *push;
 	u32 crc_args = 0xfff00000;
 
-	switch (to) {
+	switch (source) {
 	case NV50_CRC_SOURCE_TYPE_SOR:
 		crc_args |= (0x00000f0f + or * 16) << 8;
 		break;
@@ -53,7 +53,7 @@ crc907d_set_src(struct nv50_head *head, int or,
 	if (!push)
 		return;
 
-	if (to) {
+	if (source) {
 		evo_mthd(push, 0x0438 + hoff, 1);
 		evo_data(push, ctx->ntfy.handle);
 		evo_mthd(push, 0x0430 + hoff, 1);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c
index 0e99dc1de22a6..e75d79c459b14 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c
@@ -31,16 +31,15 @@ struct crcc37d_notifier {
 
 static void
 crcc37d_set_src(struct nv50_head *head, int or,
-		enum nv50_crc_source_type to, enum nv50_crc_source_type from,
-		struct nv50_crc_notifier_ctx *ctx)
+		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 (to) {
+	switch (source) {
 	case NV50_CRC_SOURCE_TYPE_SOR:
 		crc_args = (0x00000050 + or) << 12;
 		break;
@@ -59,18 +58,14 @@ crcc37d_set_src(struct nv50_head *head, int or,
 	if (!push)
 		return;
 
-	if (to) {
+	if (source) {
 		evo_mthd(push, 0x2180 + hoff, 1);
 		evo_data(push, ctx->ntfy.handle);
-		if (to != NV50_CRC_SOURCE_TYPE_RG) {
-			evo_mthd(push, 0x2184 + hoff, 1);
-			evo_data(push, crc_args);
-		}
+		evo_mthd(push, 0x2184 + hoff, 1);
+		evo_data(push, crc_args | wndw);
 	} else {
-		if (from != NV50_CRC_SOURCE_TYPE_RG) {
-			evo_mthd(push, 0x2184 + hoff, 1);
-			evo_data(push, crc_args);
-		}
+		evo_mthd(push, 0x2184 + hoff, 1);
+		evo_data(push, 0);
 		evo_mthd(push, 0x2180 + hoff, 1);
 		evo_data(push, 0);
 	}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c
index 6c02f721a2799..11ac42f6c67e9 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/head.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/head.c
@@ -44,7 +44,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, armh);
+	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);
@@ -70,7 +70,7 @@ nv50_head_flush_set(struct nv50_head *head,
 	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, armh, asyh);
+	if (asyh->set.crc    ) nv50_crc_atomic_set (head, asyh);
 	if (asyh->set.or     ) head->func->or      (head, asyh);
 }
 
@@ -321,7 +321,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) {
@@ -416,7 +416,9 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
 		asyh->set.curs = asyh->curs.visible;
 	}
 
-	nv50_crc_atomic_check(head, asyh, armh);
+	ret = nv50_crc_atomic_check(head, asyh, armh);
+	if (ret)
+		return ret;
 
 	if (asyh->clr.mask || asyh->set.mask)
 		nv50_atom(asyh->state.state)->lock_core = true;
-- 
GitLab