drm/nouveau/disp: add output detect method
This will check the relevant hotplug pin and skip the DDC probe we currently do if a display is present. - preparation for GSP-RM. Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Acked-by: Danilo Krummrich <me@dakr.org> Signed-off-by: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230919220442.202488-8-lyude@redhat.com
This commit is contained in:
@@ -18,11 +18,6 @@ nvif_conn_id(struct nvif_conn *conn)
|
||||
return conn->object.handle;
|
||||
}
|
||||
|
||||
#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 *);
|
||||
|
||||
int nvif_conn_event_ctor(struct nvif_conn *, const char *name, nvif_event_func, u8 types,
|
||||
struct nvif_event *);
|
||||
#endif
|
||||
|
||||
@@ -20,15 +20,4 @@ union nvif_conn_event_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
|
||||
|
||||
@@ -12,6 +12,8 @@ union nvif_outp_args {
|
||||
} v0;
|
||||
};
|
||||
|
||||
#define NVIF_OUTP_V0_DETECT 0x00
|
||||
|
||||
#define NVIF_OUTP_V0_ACQUIRE 0x11
|
||||
#define NVIF_OUTP_V0_RELEASE 0x12
|
||||
|
||||
@@ -24,6 +26,16 @@ union nvif_outp_args {
|
||||
#define NVIF_OUTP_V0_DP_RETRAIN 0x73
|
||||
#define NVIF_OUTP_V0_DP_MST_VCPI 0x78
|
||||
|
||||
union nvif_outp_detect_args {
|
||||
struct nvif_outp_detect_v0 {
|
||||
__u8 version;
|
||||
#define NVIF_OUTP_DETECT_V0_NOT_PRESENT 0x00
|
||||
#define NVIF_OUTP_DETECT_V0_PRESENT 0x01
|
||||
#define NVIF_OUTP_DETECT_V0_UNKNOWN 0x02
|
||||
__u8 status;
|
||||
} v0;
|
||||
};
|
||||
|
||||
union nvif_outp_load_detect_args {
|
||||
struct nvif_outp_load_detect_v0 {
|
||||
__u8 version;
|
||||
|
||||
@@ -17,6 +17,15 @@ struct nvif_outp {
|
||||
|
||||
int nvif_outp_ctor(struct nvif_disp *, const char *name, int id, struct nvif_outp *);
|
||||
void nvif_outp_dtor(struct nvif_outp *);
|
||||
|
||||
enum nvif_outp_detect_status {
|
||||
NOT_PRESENT,
|
||||
PRESENT,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
enum nvif_outp_detect_status nvif_outp_detect(struct nvif_outp *);
|
||||
|
||||
int nvif_outp_load_detect(struct nvif_outp *, u32 loadval);
|
||||
int nvif_outp_acquire_rgb_crt(struct nvif_outp *);
|
||||
int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
|
||||
|
||||
@@ -413,6 +413,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
||||
struct nouveau_connector *conn = nouveau_connector(connector);
|
||||
struct nouveau_encoder *nv_encoder = NULL, *found = NULL;
|
||||
struct drm_encoder *encoder;
|
||||
int ret;
|
||||
@@ -421,33 +422,48 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
drm_connector_for_each_possible_encoder(connector, encoder) {
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case DCB_OUTPUT_DP:
|
||||
ret = nouveau_dp_detect(nouveau_connector(connector),
|
||||
nv_encoder);
|
||||
if (ret == NOUVEAU_DP_MST)
|
||||
return NULL;
|
||||
else if (ret == NOUVEAU_DP_SST)
|
||||
found = nv_encoder;
|
||||
if (nvif_object_constructed(&nv_encoder->outp.object)) {
|
||||
enum nvif_outp_detect_status status;
|
||||
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
|
||||
ret = nouveau_dp_detect(conn, nv_encoder);
|
||||
if (ret == NOUVEAU_DP_MST)
|
||||
return NULL;
|
||||
if (ret != NOUVEAU_DP_SST)
|
||||
continue;
|
||||
|
||||
return nv_encoder;
|
||||
} else {
|
||||
status = nvif_outp_detect(&nv_encoder->outp);
|
||||
switch (status) {
|
||||
case PRESENT:
|
||||
return nv_encoder;
|
||||
case NOT_PRESENT:
|
||||
continue;
|
||||
case UNKNOWN:
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nv_encoder->i2c)
|
||||
continue;
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) {
|
||||
switcheroo_ddc = !!(vga_switcheroo_handler_flags() &
|
||||
VGA_SWITCHEROO_CAN_SWITCH_DDC);
|
||||
fallthrough;
|
||||
default:
|
||||
if (!nv_encoder->i2c)
|
||||
break;
|
||||
|
||||
if (switcheroo_ddc)
|
||||
vga_switcheroo_lock_ddc(pdev);
|
||||
if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
|
||||
found = nv_encoder;
|
||||
if (switcheroo_ddc)
|
||||
vga_switcheroo_unlock_ddc(pdev);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (switcheroo_ddc)
|
||||
vga_switcheroo_lock_ddc(pdev);
|
||||
if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
|
||||
found = nv_encoder;
|
||||
if (switcheroo_ddc)
|
||||
vga_switcheroo_unlock_ddc(pdev);
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -132,14 +132,8 @@ 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) {
|
||||
hpd = nvif_outp_detect(&nv_encoder->outp);
|
||||
if (hpd == NOT_PRESENT) {
|
||||
nvif_outp_dp_aux_pwr(&nv_encoder->outp, false);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -45,20 +45,6 @@ nvif_conn_event_ctor(struct nvif_conn *conn, const char *name, nvif_event_func f
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -210,6 +210,31 @@ nvif_outp_load_detect(struct nvif_outp *outp, u32 loadval)
|
||||
return ret < 0 ? ret : args.load;
|
||||
}
|
||||
|
||||
enum nvif_outp_detect_status
|
||||
nvif_outp_detect(struct nvif_outp *outp)
|
||||
{
|
||||
struct nvif_outp_detect_v0 args;
|
||||
int ret;
|
||||
|
||||
args.version = 0;
|
||||
|
||||
ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_DETECT, &args, sizeof(args));
|
||||
NVIF_ERRON(ret, &outp->object, "[DETECT] status:%02x", args.status);
|
||||
if (ret)
|
||||
return UNKNOWN;
|
||||
|
||||
switch (args.status) {
|
||||
case NVIF_OUTP_DETECT_V0_NOT_PRESENT: return NOT_PRESENT;
|
||||
case NVIF_OUTP_DETECT_V0_PRESENT: return PRESENT;
|
||||
case NVIF_OUTP_DETECT_V0_UNKNOWN: return UNKNOWN;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
void
|
||||
nvif_outp_dtor(struct nvif_outp *outp)
|
||||
{
|
||||
|
||||
@@ -807,6 +807,7 @@ nvkm_dp_func = {
|
||||
.dtor = nvkm_dp_dtor,
|
||||
.init = nvkm_dp_init,
|
||||
.fini = nvkm_dp_fini,
|
||||
.detect = nvkm_outp_detect,
|
||||
.acquire = nvkm_dp_acquire,
|
||||
.release = nvkm_dp_release,
|
||||
.disable = nvkm_dp_disable,
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "outp.h"
|
||||
#include "conn.h"
|
||||
#include "dp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dcb.h>
|
||||
#include <subdev/gpio.h>
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
void
|
||||
@@ -207,6 +209,31 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda)
|
||||
return nvkm_outp_acquire_hda(outp, type, user, false);
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_outp_detect(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (outp->conn->info.hpd != DCB_GPIO_UNUSED) {
|
||||
ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, outp->conn->info.hpd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
/*TODO: Look into returning NOT_PRESENT if !HPD on DVI/HDMI.
|
||||
*
|
||||
* It's uncertain whether this is accurate for all older chipsets,
|
||||
* so we're returning UNKNOWN, and the DRM will probe DDC instead.
|
||||
*/
|
||||
if (outp->info.type == DCB_OUTPUT_DP)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_outp_fini(struct nvkm_outp *outp)
|
||||
{
|
||||
@@ -328,6 +355,7 @@ nvkm_outp_new_(const struct nvkm_outp_func *func, struct nvkm_disp *disp,
|
||||
|
||||
static const struct nvkm_outp_func
|
||||
nvkm_outp = {
|
||||
.detect = nvkm_outp_detect,
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -74,6 +74,9 @@ int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, struct nvk
|
||||
void nvkm_outp_del(struct nvkm_outp **);
|
||||
void nvkm_outp_init(struct nvkm_outp *);
|
||||
void nvkm_outp_fini(struct nvkm_outp *);
|
||||
|
||||
int nvkm_outp_detect(struct nvkm_outp *);
|
||||
|
||||
int nvkm_outp_acquire(struct nvkm_outp *, u8 user, bool hda);
|
||||
void nvkm_outp_release(struct nvkm_outp *, u8 user);
|
||||
void nvkm_outp_route(struct nvkm_disp *);
|
||||
@@ -82,6 +85,9 @@ struct nvkm_outp_func {
|
||||
void *(*dtor)(struct nvkm_outp *);
|
||||
void (*init)(struct nvkm_outp *);
|
||||
void (*fini)(struct nvkm_outp *);
|
||||
|
||||
int (*detect)(struct nvkm_outp *);
|
||||
|
||||
int (*acquire)(struct nvkm_outp *);
|
||||
void (*release)(struct nvkm_outp *);
|
||||
void (*disable)(struct nvkm_outp *, struct nvkm_ior *);
|
||||
|
||||
@@ -100,46 +100,6 @@ nvkm_uconn_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_
|
||||
nvkm_uconn_uevent_gpio);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -155,7 +115,6 @@ nvkm_uconn_dtor(struct nvkm_object *object)
|
||||
static const struct nvkm_object_func
|
||||
nvkm_uconn = {
|
||||
.dtor = nvkm_uconn_dtor,
|
||||
.mthd = nvkm_uconn_mthd,
|
||||
.uevent = nvkm_uconn_uevent,
|
||||
};
|
||||
|
||||
|
||||
@@ -275,6 +275,29 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_detect(struct nvkm_outp *outp, void *argv, u32 argc)
|
||||
{
|
||||
union nvif_outp_detect_args *args = argv;
|
||||
int ret;
|
||||
|
||||
if (argc != sizeof(args->v0) || args->v0.version != 0)
|
||||
return -ENOSYS;
|
||||
if (!outp->func->detect)
|
||||
return -EINVAL;
|
||||
|
||||
ret = outp->func->detect(outp);
|
||||
switch (ret) {
|
||||
case 0: args->v0.status = NVIF_OUTP_DETECT_V0_NOT_PRESENT; break;
|
||||
case 1: args->v0.status = NVIF_OUTP_DETECT_V0_PRESENT; break;
|
||||
default:
|
||||
args->v0.status = NVIF_OUTP_DETECT_V0_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
|
||||
{
|
||||
@@ -295,6 +318,7 @@ static int
|
||||
nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
|
||||
{
|
||||
switch (mthd) {
|
||||
case NVIF_OUTP_V0_DETECT : return nvkm_uoutp_mthd_detect (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_ACQUIRE : return nvkm_uoutp_mthd_acquire (outp, argv, argc);
|
||||
case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
|
||||
case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
|
||||
|
||||
Reference in New Issue
Block a user