summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0012.h6
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/outp.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvif/outp.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c138
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c15
8 files changed, 124 insertions, 62 deletions
diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0012.h b/drivers/gpu/drm/nouveau/include/nvif/if0012.h
index d1583271f5d4..0d2e9a949d98 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/if0012.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/if0012.h
@@ -16,6 +16,7 @@ union nvif_outp_args {
#define NVIF_OUTP_V0_INFOFRAME 0x03
#define NVIF_OUTP_V0_HDA_ELD 0x04
#define NVIF_OUTP_V0_DP_AUX_PWR 0x05
+#define NVIF_OUTP_V0_DP_RETRAIN 0x06
union nvif_outp_load_detect_args {
struct nvif_outp_load_detect_v0 {
@@ -100,4 +101,9 @@ union nvif_outp_dp_aux_pwr_args {
__u8 pad02[6];
} v0;
};
+
+union nvif_outp_dp_retrain_args {
+ struct nvif_outp_dp_retrain_vn {
+ } vn;
+};
#endif
diff --git a/drivers/gpu/drm/nouveau/include/nvif/outp.h b/drivers/gpu/drm/nouveau/include/nvif/outp.h
index 1c960f6fdd7a..ea3e0a936c8a 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/outp.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/outp.h
@@ -27,4 +27,5 @@ void nvif_outp_release(struct nvif_outp *);
int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable);
+int nvif_outp_dp_retrain(struct nvif_outp *);
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index a21dc2a0f92b..965e1c62d497 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -506,8 +506,10 @@ nouveau_display_hpd_work(struct work_struct *work)
!!(bits & NVIF_NOTIFY_CONN_V0_UNPLUG),
!!(bits & NVIF_NOTIFY_CONN_V0_IRQ));
- if (bits & NVIF_NOTIFY_CONN_V0_IRQ)
- continue;
+ if (bits & NVIF_NOTIFY_CONN_V0_IRQ) {
+ if (nouveau_dp_link_check(nv_connector))
+ continue;
+ }
connector->status = drm_helper_probe_detect(connector, NULL, false);
if (old_epoch_counter == connector->epoch_counter)
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index d349dc24a003..bde58d00fbf5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -211,6 +211,17 @@ out:
return ret;
}
+bool
+nouveau_dp_link_check(struct nouveau_connector *nv_connector)
+{
+ struct nouveau_encoder *nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
+
+ if (!nv_encoder || nv_encoder->outp.or.id < 0)
+ return true;
+
+ return nvif_outp_dp_retrain(&nv_encoder->outp) == 0;
+}
+
void nouveau_dp_irq(struct nouveau_drm *drm,
struct nouveau_connector *nv_connector)
{
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index b6c51fc60d13..8524d211f4e1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -141,6 +141,7 @@ enum nouveau_dp_status {
};
int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *);
+bool nouveau_dp_link_check(struct nouveau_connector *);
void nouveau_dp_irq(struct nouveau_drm *drm,
struct nouveau_connector *nv_connector);
enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *,
diff --git a/drivers/gpu/drm/nouveau/nvif/outp.c b/drivers/gpu/drm/nouveau/nvif/outp.c
index 9bd9cc21a039..56b7c703b137 100644
--- a/drivers/gpu/drm/nouveau/nvif/outp.c
+++ b/drivers/gpu/drm/nouveau/nvif/outp.c
@@ -26,6 +26,14 @@
#include <nvif/class.h>
int
+nvif_outp_dp_retrain(struct nvif_outp *outp)
+{
+ int ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RETRAIN, NULL, 0);
+ NVIF_ERRON(ret, &outp->object, "[DP_RETRAIN]");
+ return ret;
+}
+
+int
nvif_outp_dp_aux_pwr(struct nvif_outp *outp, bool enable)
{
struct nvif_outp_dp_aux_pwr_v0 args;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
index 92c9faecffae..730c3a6f3362 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -274,71 +274,18 @@ nvkm_dp_train_cr(struct lt_state *lt)
}
static int
-nvkm_dp_train_links(struct nvkm_outp *outp, int rate)
+nvkm_dp_train_link(struct nvkm_outp *outp, int rate)
{
struct nvkm_ior *ior = outp->ior;
- struct nvkm_disp *disp = outp->disp;
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvkm_bios *bios = subdev->device->bios;
struct lt_state lt = {
.outp = outp,
+ .pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED,
};
- u32 lnkcmp;
u8 sink[2], data;
int ret;
OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw);
- /* Intersect misc. capabilities of the OR and sink. */
- if (disp->engine.subdev.device->chipset < 0x110)
- outp->dp.dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED;
- if (disp->engine.subdev.device->chipset < 0xd0)
- outp->dp.dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
- lt.pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED;
-
- if (AMPERE_IED_HACK(disp) && (lnkcmp = lt.outp->dp.info.script[0])) {
- /* Execute BeforeLinkTraining script from DP Info table. */
- while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
- lnkcmp += 3;
- lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
-
- nvbios_init(&outp->disp->engine.subdev, lnkcmp,
- init.outp = &outp->info;
- init.or = ior->id;
- init.link = ior->asy.link;
- );
- }
-
- /* Set desired link configuration on the source. */
- if ((lnkcmp = lt.outp->dp.info.lnkcmp)) {
- if (outp->dp.version < 0x30) {
- while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp))
- lnkcmp += 4;
- lnkcmp = nvbios_rd16(bios, lnkcmp + 2);
- } else {
- while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
- lnkcmp += 3;
- lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
- }
-
- nvbios_init(subdev, lnkcmp,
- init.outp = &outp->info;
- init.or = ior->id;
- init.link = ior->asy.link;
- );
- }
-
- ret = ior->func->dp->links(ior, outp->dp.aux);
- if (ret) {
- if (ret < 0) {
- OUTP_ERR(outp, "train failed with %d", ret);
- return ret;
- }
- return 0;
- }
-
- ior->func->dp->power(ior, ior->dp.nr);
-
/* Select LTTPR non-transparent mode if we have a valid configuration,
* use transparent mode otherwise.
*/
@@ -393,6 +340,71 @@ nvkm_dp_train_links(struct nvkm_outp *outp, int rate)
return ret;
}
+static int
+nvkm_dp_train_links(struct nvkm_outp *outp, int rate)
+{
+ struct nvkm_ior *ior = outp->ior;
+ struct nvkm_disp *disp = outp->disp;
+ struct nvkm_subdev *subdev = &disp->engine.subdev;
+ struct nvkm_bios *bios = subdev->device->bios;
+ u32 lnkcmp;
+ int ret;
+
+ OUTP_DBG(outp, "programming link for %dx%02x", ior->dp.nr, ior->dp.bw);
+
+ /* Intersect misc. capabilities of the OR and sink. */
+ if (disp->engine.subdev.device->chipset < 0x110)
+ outp->dp.dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED;
+ if (disp->engine.subdev.device->chipset < 0xd0)
+ outp->dp.dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
+
+ if (AMPERE_IED_HACK(disp) && (lnkcmp = outp->dp.info.script[0])) {
+ /* Execute BeforeLinkTraining script from DP Info table. */
+ while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
+ lnkcmp += 3;
+ lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
+
+ nvbios_init(&outp->disp->engine.subdev, lnkcmp,
+ init.outp = &outp->info;
+ init.or = ior->id;
+ init.link = ior->asy.link;
+ );
+ }
+
+ /* Set desired link configuration on the source. */
+ if ((lnkcmp = outp->dp.info.lnkcmp)) {
+ if (outp->dp.version < 0x30) {
+ while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp))
+ lnkcmp += 4;
+ lnkcmp = nvbios_rd16(bios, lnkcmp + 2);
+ } else {
+ while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
+ lnkcmp += 3;
+ lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
+ }
+
+ nvbios_init(subdev, lnkcmp,
+ init.outp = &outp->info;
+ init.or = ior->id;
+ init.link = ior->asy.link;
+ );
+ }
+
+ ret = ior->func->dp->links(ior, outp->dp.aux);
+ if (ret) {
+ if (ret < 0) {
+ OUTP_ERR(outp, "train failed with %d", ret);
+ return ret;
+ }
+ return 0;
+ }
+
+ ior->func->dp->power(ior, ior->dp.nr);
+
+ /* Attempt to train the link in this configuration. */
+ return nvkm_dp_train_link(outp, rate);
+}
+
static void
nvkm_dp_train_fini(struct nvkm_outp *outp)
{
@@ -439,6 +451,16 @@ nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
int ret = -EINVAL, nr, rate;
u8 pwr;
+ /* Retraining link? Skip source configuration, it can mess up the active modeset. */
+ if (atomic_read(&outp->dp.lt.done)) {
+ for (rate = 0; rate < outp->dp.rates; rate++) {
+ if (outp->dp.rate[rate].rate == ior->dp.bw * 27000)
+ return nvkm_dp_train_link(outp, ret);
+ }
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
/* Ensure sink is not in a low-power state. */
if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) {
if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
@@ -726,12 +748,8 @@ nvkm_dp_hpd(struct nvkm_notify *notify)
struct nvif_notify_conn_rep_v0 rep = {};
OUTP_DBG(outp, "HPD: %d", line->mask);
- if (line->mask & NVKM_I2C_IRQ) {
- if (atomic_read(&outp->dp.lt.done))
- outp->func->acquire(outp);
+ if (line->mask & NVKM_I2C_IRQ)
rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
- }
-
if (line->mask & NVKM_I2C_UNPLUG)
rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
if (line->mask & NVKM_I2C_PLUG)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
index 3bfc3e39778e..e12a31d428d6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
@@ -28,6 +28,20 @@
#include <nvif/if0012.h>
static int
+nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ union nvif_outp_dp_retrain_args *args = argv;
+
+ if (argc != sizeof(args->vn))
+ return -ENOSYS;
+
+ if (!atomic_read(&outp->dp.lt.done))
+ return 0;
+
+ return outp->func->acquire(outp);
+}
+
+static int
nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc)
{
union nvif_outp_dp_aux_pwr_args *args = argv;
@@ -251,6 +265,7 @@ nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
case NVIF_OUTP_V0_RELEASE : return nvkm_uoutp_mthd_release (outp, argv, argc);
case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc);
case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc);
+ case NVIF_OUTP_V0_DP_RETRAIN : return nvkm_uoutp_mthd_dp_retrain (outp, argv, argc);
default:
break;
}