Blob Blame History Raw
From 4fb97a68301e7b2370d1c15101055f259ca09bec Mon Sep 17 00:00:00 2001
From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 1 Jun 2022 20:46:30 +1000
Subject: drm/nouveau/disp: add conn method to query HPD pin status
Git-commit: 32dd9236698bcd2ffdb69954b167a851fd50182a
Patch-mainline: v6.0-rc1
References: jsc#PED-1166 jsc#PED-1168 jsc#PED-1170 jsc#PED-1218 jsc#PED-1220 jsc#PED-1222 jsc#PED-1223 jsc#PED-1225 jsc#PED-2849

And use it to bail early in DP detection and avoid futile AUX transactions.

This could be used on other connector types too in theory, but it's not
something we've ever done before and I'd rather not risk breaking working
systems without looking into it more closely.

It's safe for DP though.  We already do this by checking an AUX register
that contains HPD status and aborting the transaction.  However, this is
much deeper in the stack - after taking various mutexes, poking HW for no
good reason, and making a mess in debug logs.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Acked-by: Patrik Jakobsson <pjakobsson@suse.de>
---
 drivers/gpu/drm/nouveau/include/nvif/conn.h   |  5 +++
 drivers/gpu/drm/nouveau/include/nvif/if0011.h | 11 +++++
 drivers/gpu/drm/nouveau/nouveau_dp.c          | 12 +++++-
 drivers/gpu/drm/nouveau/nvif/conn.c           | 14 ++++++
 .../gpu/drm/nouveau/nvkm/engine/disp/conn.c   |  6 ++-
 .../gpu/drm/nouveau/nvkm/engine/disp/uconn.c  | 43 +++++++++++++++++++
 6 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/include/nvif/conn.h b/drivers/gpu/drm/nouveau/include/nvif/conn.h
index ad52cdafef18..f72a8f138f47 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/conn.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/conn.h
@@ -10,4 +10,9 @@ struct nvif_conn {
 
 int nvif_conn_ctor(struct nvif_disp *, const char *name, int id, struct nvif_conn *);
 void nvif_conn_dtor(struct nvif_conn *);
+
+#define NVIF_CONN_HPD_STATUS_UNSUPPORTED 0 /* negative if query fails */
+#define NVIF_CONN_HPD_STATUS_NOT_PRESENT 1
+#define NVIF_CONN_HPD_STATUS_PRESENT     2
+int nvif_conn_hpd_status(struct nvif_conn *);
 #endif
diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0011.h b/drivers/gpu/drm/nouveau/include/nvif/if0011.h
index 9c910b29a730..04ba6581f840 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/if0011.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/if0011.h
@@ -9,4 +9,15 @@ union nvif_conn_args {
 		__u8 pad02[6];
 	} v0;
 };
+
+#define NVIF_CONN_V0_HPD_STATUS 0x00000000
+
+union nvif_conn_hpd_status_args {
+	struct nvif_conn_hpd_status_v0 {
+		__u8 version;
+		__u8 support;
+		__u8 present;
+		__u8 pad03[5];
+	} v0;
+};
 #endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index c36f510d5d4c..20db8ea1a0ba 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -107,7 +107,7 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
 	struct nv50_mstm *mstm = nv_encoder->dp.mstm;
 	enum drm_connector_status status;
 	u8 *dpcd = nv_encoder->dp.dpcd;
-	int ret = NOUVEAU_DP_NONE;
+	int ret = NOUVEAU_DP_NONE, hpd;
 
 	/* If we've already read the DPCD on an eDP device, we don't need to
 	 * reread it as it won't change
@@ -133,6 +133,16 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
 		}
 	}
 
+	/* Check status of HPD pin before attempting an AUX transaction that
+	 * would result in a number of (futile) retries on a connector which
+	 * has no display plugged.
+	 *
+	 * TODO: look into checking this before probing I2C to detect DVI/HDMI
+	 */
+	hpd = nvif_conn_hpd_status(&nv_connector->conn);
+	if (hpd == NVIF_CONN_HPD_STATUS_NOT_PRESENT)
+		goto out;
+
 	status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder);
 	if (status == connector_status_disconnected)
 		goto out;
diff --git a/drivers/gpu/drm/nouveau/nvif/conn.c b/drivers/gpu/drm/nouveau/nvif/conn.c
index a83b8a4a57e6..4ce935d58c90 100644
--- a/drivers/gpu/drm/nouveau/nvif/conn.c
+++ b/drivers/gpu/drm/nouveau/nvif/conn.c
@@ -26,6 +26,20 @@
 #include <nvif/class.h>
 #include <nvif/if0011.h>
 
+int
+nvif_conn_hpd_status(struct nvif_conn *conn)
+{
+	struct nvif_conn_hpd_status_v0 args;
+	int ret;
+
+	args.version = 0;
+
+	ret = nvif_mthd(&conn->object, NVIF_CONN_V0_HPD_STATUS, &args, sizeof(args));
+	NVIF_ERRON(ret, &conn->object, "[HPD_STATUS] support:%d present:%d",
+		   args.support, args.present);
+	return ret ? ret : !!args.support + !!args.present;
+}
+
 void
 nvif_conn_dtor(struct nvif_conn *conn)
 {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
index febc5c274488..7ed11801a3ae 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
@@ -86,6 +86,7 @@ nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
 	conn->disp = disp;
 	conn->index = index;
 	conn->info = *info;
+	conn->info.hpd = DCB_GPIO_UNUSED;
 
 	CONN_DBG(conn, "type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x",
 		 info->type, info->location, info->hpd, info->dp,
@@ -100,11 +101,12 @@ nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
 
 		ret = nvkm_gpio_find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func);
 		if (ret) {
-			CONN_ERR(conn, "func %02x lookup failed, %d",
-				 info->hpd, ret);
+			CONN_ERR(conn, "func %02x lookup failed, %d", info->hpd, ret);
 			return;
 		}
 
+		conn->info.hpd = func.line;
+
 		ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd,
 				       true, &(struct nvkm_gpio_ntfy_req) {
 					.mask = NVKM_GPIO_TOGGLED,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
index 3fbbb6e6a66b..fd9f18144c26 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
@@ -22,8 +22,50 @@
 #define nvkm_uconn(p) container_of((p), struct nvkm_conn, object)
 #include "conn.h"
 
+#include <subdev/gpio.h>
+
 #include <nvif/if0011.h>
 
+static int
+nvkm_uconn_mthd_hpd_status(struct nvkm_conn *conn, void *argv, u32 argc)
+{
+	struct nvkm_gpio *gpio = conn->disp->engine.subdev.device->gpio;
+	union nvif_conn_hpd_status_args *args = argv;
+
+	if (argc != sizeof(args->v0) || args->v0.version != 0)
+		return -ENOSYS;
+
+	args->v0.support = gpio && conn->info.hpd != DCB_GPIO_UNUSED;
+	args->v0.present = 0;
+
+	if (args->v0.support) {
+		int ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->info.hpd);
+
+		if (WARN_ON(ret < 0)) {
+			args->v0.support = false;
+			return 0;
+		}
+
+		args->v0.present = ret;
+	}
+
+	return 0;
+}
+
+static int
+nvkm_uconn_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
+{
+	struct nvkm_conn *conn = nvkm_uconn(object);
+
+	switch (mthd) {
+	case NVIF_CONN_V0_HPD_STATUS: return nvkm_uconn_mthd_hpd_status(conn, argv, argc);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
 static void *
 nvkm_uconn_dtor(struct nvkm_object *object)
 {
@@ -39,6 +81,7 @@ nvkm_uconn_dtor(struct nvkm_object *object)
 static const struct nvkm_object_func
 nvkm_uconn = {
 	.dtor = nvkm_uconn_dtor,
+	.mthd = nvkm_uconn_mthd,
 };
 
 int
-- 
2.38.1