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 #include +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 + #include +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