summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm/engine/disp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c39
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c283
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c91
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c85
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c89
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c95
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c250
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uhead.c127
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c250
30 files changed, 938 insertions, 866 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
index 600072a904be..e1aecd3fe96c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
@@ -28,9 +28,7 @@ nvkm-y += nvkm/engine/disp/gv100.o
nvkm-y += nvkm/engine/disp/tu102.o
nvkm-y += nvkm/engine/disp/ga102.o
-nvkm-y += nvkm/engine/disp/rootnv04.o
-nvkm-y += nvkm/engine/disp/rootnv50.o
-
nvkm-y += nvkm/engine/disp/udisp.o
nvkm-y += nvkm/engine/disp/uconn.o
nvkm-y += nvkm/engine/disp/uoutp.o
+nvkm-y += nvkm/engine/disp/uhead.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
index 65c99d948b68..73104b59f97f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
@@ -29,7 +29,6 @@
#include "outp.h"
#include <core/client.h>
-#include <core/notify.h>
#include <core/ramht.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -57,32 +56,8 @@ nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id)
head->func->vblank_get(head);
}
-static int
-nvkm_disp_vblank_ctor(struct nvkm_object *object, void *data, u32 size,
- struct nvkm_notify *notify)
-{
- struct nvkm_disp *disp =
- container_of(notify->event, typeof(*disp), vblank);
- union {
- struct nvif_notify_head_req_v0 v0;
- } *req = data;
- int ret = -ENOSYS;
-
- if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
- notify->size = sizeof(struct nvif_notify_head_rep_v0);
- if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) {
- notify->types = 1;
- notify->index = req->v0.head;
- return 0;
- }
- }
-
- return ret;
-}
-
static const struct nvkm_event_func
nvkm_disp_vblank_func = {
- .ctor = nvkm_disp_vblank_ctor,
.init = nvkm_disp_vblank_init,
.fini = nvkm_disp_vblank_fini,
};
@@ -90,59 +65,7 @@ nvkm_disp_vblank_func = {
void
nvkm_disp_vblank(struct nvkm_disp *disp, int head)
{
- struct nvif_notify_head_rep_v0 rep = {};
- nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep));
-}
-
-static int
-nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size,
- struct nvkm_notify *notify)
-{
- struct nvkm_disp *disp =
- container_of(notify->event, typeof(*disp), hpd);
- union {
- struct nvif_notify_conn_req_v0 v0;
- } *req = data;
- struct nvkm_outp *outp;
- int ret = -ENOSYS;
-
- if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
- notify->size = sizeof(struct nvif_notify_conn_rep_v0);
- list_for_each_entry(outp, &disp->outps, head) {
- if (ret = -ENXIO, outp->conn->index == req->v0.conn) {
- if (ret = -ENODEV, outp->conn->hpd.event) {
- notify->types = req->v0.mask;
- notify->index = req->v0.conn;
- ret = 0;
- }
- break;
- }
- }
- }
-
- return ret;
-}
-
-static const struct nvkm_event_func
-nvkm_disp_hpd_func = {
- .ctor = nvkm_disp_hpd_ctor
-};
-
-int
-nvkm_disp_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **event)
-{
- struct nvkm_disp *disp = nvkm_disp(object->engine);
- switch (type) {
- case NV04_DISP_NTFY_VBLANK:
- *event = &disp->vblank;
- return 0;
- case NV04_DISP_NTFY_CONN:
- *event = &disp->hpd;
- return 0;
- default:
- break;
- }
- return -EINVAL;
+ nvkm_event_ntfy(&disp->vblank, head, NVKM_DISP_HEAD_EVENT_VBLANK);
}
static int
@@ -343,9 +266,7 @@ nvkm_disp_oneinit(struct nvkm_engine *engine)
/* Apparently we need to create a new one! */
ret = nvkm_conn_new(disp, i, &connE, &outp->conn);
if (ret) {
- nvkm_error(&disp->engine.subdev,
- "failed to create outp %d conn: %d\n",
- outp->index, ret);
+ nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret);
nvkm_conn_del(&outp->conn);
list_del(&outp->head);
nvkm_outp_del(&outp);
@@ -355,10 +276,6 @@ nvkm_disp_oneinit(struct nvkm_engine *engine)
list_add_tail(&outp->conn->head, &disp->conns);
}
- ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd);
- if (ret)
- return ret;
-
if (disp->func->oneinit) {
ret = disp->func->oneinit(disp);
if (ret)
@@ -382,7 +299,7 @@ nvkm_disp_oneinit(struct nvkm_engine *engine)
list_for_each_entry(head, &disp->heads, head)
i = max(i, head->id + 1);
- return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank);
+ return nvkm_event_init(&nvkm_disp_vblank_func, subdev, 1, i, &disp->vblank);
}
static void *
@@ -406,7 +323,6 @@ nvkm_disp_dtor(struct nvkm_engine *engine)
}
nvkm_event_fini(&disp->vblank);
- nvkm_event_fini(&disp->hpd);
while (!list_empty(&disp->conns)) {
conn = list_first_entry(&disp->conns, typeof(*conn), head);
@@ -473,5 +389,6 @@ nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
mutex_init(&disp->super.mutex);
}
- return nvkm_event_init(func->uevent, 1, ARRAY_SIZE(disp->chan), &disp->uevent);
+ return nvkm_event_init(func->uevent, &disp->engine.subdev, 1, ARRAY_SIZE(disp->chan),
+ &disp->uevent);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
index 7ed11801a3ae..fbdae1137864 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
@@ -29,38 +29,14 @@
#include <nvif/event.h>
-static int
-nvkm_conn_hpd(struct nvkm_notify *notify)
-{
- struct nvkm_conn *conn = container_of(notify, typeof(*conn), hpd);
- struct nvkm_disp *disp = conn->disp;
- struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
- const struct nvkm_gpio_ntfy_rep *line = notify->data;
- struct nvif_notify_conn_rep_v0 rep;
- int index = conn->index;
-
- CONN_DBG(conn, "HPD: %d", line->mask);
-
- if (!nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.index))
- rep.mask = NVIF_NOTIFY_CONN_V0_UNPLUG;
- else
- rep.mask = NVIF_NOTIFY_CONN_V0_PLUG;
- rep.version = 0;
-
- nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep));
- return NVKM_NOTIFY_KEEP;
-}
-
void
nvkm_conn_fini(struct nvkm_conn *conn)
{
- nvkm_notify_put(&conn->hpd);
}
void
nvkm_conn_init(struct nvkm_conn *conn)
{
- nvkm_notify_get(&conn->hpd);
}
void
@@ -68,7 +44,6 @@ nvkm_conn_del(struct nvkm_conn **pconn)
{
struct nvkm_conn *conn = *pconn;
if (conn) {
- nvkm_notify_fini(&conn->hpd);
kfree(*pconn);
*pconn = NULL;
}
@@ -106,20 +81,6 @@ nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
}
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,
- .line = func.line,
- },
- sizeof(struct nvkm_gpio_ntfy_req),
- sizeof(struct nvkm_gpio_ntfy_rep),
- &conn->hpd);
- if (ret) {
- CONN_ERR(conn, "func %02x failed, %d", info->hpd, ret);
- } else {
- CONN_DBG(conn, "func %02x (HPD)", info->hpd);
- }
}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
index f109634ce5ca..a0600e72b0ec 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
@@ -3,7 +3,6 @@
#define __NVKM_DISP_CONN_H__
#include "priv.h"
-#include <core/notify.h>
#include <subdev/bios.h>
#include <subdev/bios/conn.h>
@@ -12,8 +11,6 @@ struct nvkm_conn {
int index;
struct nvbios_connE info;
- struct nvkm_notify hpd;
-
struct list_head head;
struct nvkm_object object;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
index c1b3206f27e6..40c8ea43c42f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -274,70 +274,17 @@ 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 %d x %d MB/s", ior->dp.nr, ior->dp.bw * 27);
-
- /* 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);
+ OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw);
/* 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) {
@@ -455,6 +477,21 @@ nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
/* Link training. */
OUTP_DBG(outp, "training");
nvkm_dp_train_init(outp);
+
+ /* Validate and train at configuration requested (if any) on ACQUIRE. */
+ if (outp->dp.lt.nr) {
+ for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
+ for (rate = 0; nr == outp->dp.lt.nr && rate < outp->dp.rates; rate++) {
+ if (outp->dp.rate[rate].rate / 27000 == outp->dp.lt.bw) {
+ ior->dp.bw = outp->dp.rate[rate].rate / 27000;
+ ior->dp.nr = nr;
+ ret = nvkm_dp_train_links(outp, rate);
+ }
+ }
+ }
+ }
+
+ /* Otherwise, loop through all valid link configurations that support the data rate. */
for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) {
if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
@@ -465,6 +502,8 @@ nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
}
}
}
+
+ /* Finish up. */
nvkm_dp_train_fini(outp);
if (ret < 0)
OUTP_ERR(outp, "training failed");
@@ -595,18 +634,38 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp)
return outp->dp.rates != 0;
}
-static bool
-nvkm_dp_enable(struct nvkm_outp *outp, bool enable)
+void
+nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
{
+ struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
struct nvkm_i2c_aux *aux = outp->dp.aux;
- if (enable) {
- if (!outp->dp.present) {
- OUTP_DBG(outp, "aux power -> always");
- nvkm_i2c_aux_monitor(aux, true);
- outp->dp.present = true;
+ if (auxpwr && !outp->dp.aux_pwr) {
+ /* eDP panels need powering on by us (if the VBIOS doesn't default it
+ * to on) before doing any AUX channel transactions. LVDS panel power
+ * is handled by the SOR itself, and not required for LVDS DDC.
+ */
+ if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
+ int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff);
+ if (power == 0) {
+ nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1);
+ outp->dp.aux_pwr_pu = true;
+ }
+
+ /* We delay here unconditionally, even if already powered,
+ * because some laptop panels having a significant resume
+ * delay before the panel begins responding.
+ *
+ * This is likely a bit of a hack, but no better idea for
+ * handling this at the moment.
+ */
+ msleep(300);
}
+ OUTP_DBG(outp, "aux power -> always");
+ nvkm_i2c_aux_monitor(aux, true);
+ outp->dp.aux_pwr = true;
+
/* Detect any LTTPRs before reading DPCD receiver caps. */
if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) &&
outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) {
@@ -659,96 +718,41 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool enable)
outp->dp.rates++;
}
}
-
- return true;
}
- }
-
- if (outp->dp.present) {
+ } else
+ if (!auxpwr && outp->dp.aux_pwr) {
OUTP_DBG(outp, "aux power -> demand");
nvkm_i2c_aux_monitor(aux, false);
- outp->dp.present = false;
- }
-
- atomic_set(&outp->dp.lt.done, 0);
- return false;
-}
-
-static int
-nvkm_dp_hpd(struct nvkm_notify *notify)
-{
- const struct nvkm_i2c_ntfy_rep *line = notify->data;
- struct nvkm_outp *outp = container_of(notify, typeof(*outp), dp.hpd);
- struct nvkm_conn *conn = outp->conn;
- struct nvkm_disp *disp = outp->disp;
- struct nvif_notify_conn_rep_v0 rep = {};
+ outp->dp.aux_pwr = false;
+ atomic_set(&outp->dp.lt.done, 0);
- OUTP_DBG(outp, "HPD: %d", line->mask);
- if (line->mask & NVKM_I2C_IRQ) {
- if (atomic_read(&outp->dp.lt.done))
- outp->func->acquire(outp);
- rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
- } else {
- nvkm_dp_enable(outp, true);
+ /* Restore eDP panel GPIO to its prior state if we changed it, as
+ * it could potentially interfere with other outputs.
+ */
+ if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
+ if (outp->dp.aux_pwr_pu) {
+ nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0);
+ outp->dp.aux_pwr_pu = false;
+ }
+ }
}
-
- if (line->mask & NVKM_I2C_UNPLUG)
- rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
- if (line->mask & NVKM_I2C_PLUG)
- rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
-
- nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
- return NVKM_NOTIFY_KEEP;
}
static void
nvkm_dp_fini(struct nvkm_outp *outp)
{
- nvkm_notify_put(&outp->dp.hpd);
nvkm_dp_enable(outp, false);
}
static void
nvkm_dp_init(struct nvkm_outp *outp)
{
- struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
-
- nvkm_notify_put(&outp->conn->hpd);
-
- /* eDP panels need powering on by us (if the VBIOS doesn't default it
- * to on) before doing any AUX channel transactions. LVDS panel power
- * is handled by the SOR itself, and not required for LVDS DDC.
- */
- if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
- int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff);
- if (power == 0)
- nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1);
-
- /* We delay here unconditionally, even if already powered,
- * because some laptop panels having a significant resume
- * delay before the panel begins responding.
- *
- * This is likely a bit of a hack, but no better idea for
- * handling this at the moment.
- */
- msleep(300);
-
- /* If the eDP panel can't be detected, we need to restore
- * the panel power GPIO to avoid breaking another output.
- */
- if (!nvkm_dp_enable(outp, true) && power == 0)
- nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0);
- } else {
- nvkm_dp_enable(outp, true);
- }
-
- nvkm_notify_get(&outp->dp.hpd);
+ nvkm_dp_enable(outp, outp->dp.enabled);
}
static void *
nvkm_dp_dtor(struct nvkm_outp *outp)
{
- nvkm_notify_fini(&outp->dp.hpd);
return outp;
}
@@ -797,21 +801,6 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n
OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len);
- /* hotplug detect, replaces gpio-based mechanism with aux events */
- ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true,
- &(struct nvkm_i2c_ntfy_req) {
- .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG |
- NVKM_I2C_IRQ,
- .port = outp->dp.aux->id,
- },
- sizeof(struct nvkm_i2c_ntfy_req),
- sizeof(struct nvkm_i2c_ntfy_rep),
- &outp->dp.hpd);
- if (ret) {
- OUTP_ERR(outp, "error monitoring aux hpd: %d", ret);
- return ret;
- }
-
mutex_init(&outp->dp.mutex);
atomic_set(&outp->dp.lt.done, 0);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
index 1d86baa6a424..9a6be43916bc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
@@ -6,6 +6,7 @@
int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
struct nvkm_outp **);
void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
+void nvkm_dp_enable(struct nvkm_outp *, bool auxpwr);
/* DPCD Receiver Capabilities */
#define DPCD_RC00_DPCD_REV 0x00000
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
index 4966a51af3d7..23ae451ba473 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
@@ -29,9 +29,54 @@
#include <nvif/class.h>
-void
-g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
- u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
+static void
+g84_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe vsi;
+ const u32 hoff = head * 0x800;
+
+ nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000);
+ if (!size)
+ return;
+
+ pack_hdmi_infoframe(&vsi, data, size);
+
+ nvkm_wr32(device, 0x616544 + hoff, vsi.header);
+ nvkm_wr32(device, 0x616548 + hoff, vsi.subpack0_low);
+ nvkm_wr32(device, 0x61654c + hoff, vsi.subpack0_high);
+ /* Is there a second (or up to fourth?) set of subpack registers here? */
+ /* nvkm_wr32(device, 0x616550 + hoff, vsi.subpack1_low); */
+ /* nvkm_wr32(device, 0x616554 + hoff, vsi.subpack1_high); */
+
+ nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001);
+}
+
+static void
+g84_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe avi;
+ const u32 hoff = head * 0x800;
+
+ pack_hdmi_infoframe(&avi, data, size);
+
+ nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x616528 + hoff, avi.header);
+ nvkm_wr32(device, 0x61652c + hoff, avi.subpack0_low);
+ nvkm_wr32(device, 0x616530 + hoff, avi.subpack0_high);
+ nvkm_wr32(device, 0x616534 + hoff, avi.subpack1_low);
+ nvkm_wr32(device, 0x616538 + hoff, avi.subpack1_high);
+
+ nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001);
+}
+
+
+static void
+g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 ctrl = 0x40000000 * enable |
@@ -39,31 +84,13 @@ g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
max_ac_packet << 16 |
rekey;
const u32 hoff = head * 0x800;
- struct packed_hdmi_infoframe avi_infoframe;
- struct packed_hdmi_infoframe vendor_infoframe;
-
- pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
- pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
if (!(ctrl & 0x40000000)) {
nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000);
- nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000);
- nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000);
return;
}
- /* AVI InfoFrame */
- nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
- if (avi_size) {
- nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header);
- nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low);
- nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high);
- nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low);
- nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high);
- nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001);
- }
-
/* Audio InfoFrame */
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x616508 + hoff, 0x000a0184);
@@ -71,17 +98,6 @@ g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
nvkm_wr32(device, 0x616510 + hoff, 0x00000000);
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001);
- /* Vendor InfoFrame */
- nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000);
- if (vendor_size) {
- nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header);
- nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low);
- nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high);
- /* Is there a second (or up to fourth?) set of subpack registers here? */
- /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */
- /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */
- nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001);
- }
nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
@@ -96,14 +112,19 @@ g84_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl);
}
+const struct nvkm_ior_func_hdmi
+g84_sor_hdmi = {
+ .ctrl = g84_sor_hdmi_ctrl,
+ .infoframe_avi = g84_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = g84_sor_hdmi_infoframe_vsi,
+};
+
static const struct nvkm_ior_func
g84_sor = {
.state = nv50_sor_state,
.power = nv50_sor_power,
.clock = nv50_sor_clock,
- .hdmi = {
- .ctrl = g84_sor_hdmi_ctrl,
- },
+ .hdmi = &g84_sor_hdmi,
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
index 7489d0d7fce0..52099b75f52a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
@@ -105,10 +105,7 @@ ga102_sor = {
.state = gv100_sor_state,
.power = nv50_sor_power,
.clock = ga102_sor_clock,
- .hdmi = {
- .ctrl = gv100_sor_hdmi_ctrl,
- .scdc = gm200_sor_hdmi_scdc,
- },
+ .hdmi = &gv100_sor_hdmi,
.dp = &ga102_sor_dp,
.hda = &gv100_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
index 39822f1b5b95..a48e9bdf4cd0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
@@ -202,19 +202,61 @@ gf119_sor_dp = {
};
static void
-gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
- u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
+gf119_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe vsi;
+ const u32 hoff = head * 0x800;
+
+ pack_hdmi_infoframe(&vsi, data, size);
+
+ nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000);
+ if (!size)
+ return;
+
+ /*
+ * These appear to be the audio infoframe registers,
+ * but no other set of infoframe registers has yet
+ * been found.
+ */
+ nvkm_wr32(device, 0x616738 + hoff, vsi.header);
+ nvkm_wr32(device, 0x61673c + hoff, vsi.subpack0_low);
+ nvkm_wr32(device, 0x616740 + hoff, vsi.subpack0_high);
+ /* Is there a second (or further?) set of subpack registers here? */
+
+ nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001);
+}
+
+static void
+gf119_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe avi;
+ const u32 hoff = head * 0x800;
+
+ pack_hdmi_infoframe(&avi, data, size);
+
+ nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x61671c + hoff, avi.header);
+ nvkm_wr32(device, 0x616720 + hoff, avi.subpack0_low);
+ nvkm_wr32(device, 0x616724 + hoff, avi.subpack0_high);
+ nvkm_wr32(device, 0x616728 + hoff, avi.subpack1_low);
+ nvkm_wr32(device, 0x61672c + hoff, avi.subpack1_high);
+
+ nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001);
+}
+
+static void
+gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 ctrl = 0x40000000 * enable |
max_ac_packet << 16 |
rekey;
const u32 hoff = head * 0x800;
- struct packed_hdmi_infoframe avi_infoframe;
- struct packed_hdmi_infoframe vendor_infoframe;
-
- pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
- pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
if (!(ctrl & 0x40000000)) {
nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000);
@@ -224,32 +266,6 @@ gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
return;
}
- /* AVI InfoFrame */
- nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
- if (avi_size) {
- nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header);
- nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low);
- nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high);
- nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low);
- nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high);
- nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001);
- }
-
- /* GENERIC(?) / Vendor InfoFrame? */
- nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000);
- if (vendor_size) {
- /*
- * These appear to be the audio infoframe registers,
- * but no other set of infoframe registers has yet
- * been found.
- */
- nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header);
- nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low);
- nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high);
- /* Is there a second (or further?) set of subpack registers here? */
- nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001);
- }
-
/* ??? InfoFrame? */
nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x6167ac + hoff, 0x00000010);
@@ -259,6 +275,13 @@ gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
}
+static const struct nvkm_ior_func_hdmi
+gf119_sor_hdmi = {
+ .ctrl = gf119_sor_hdmi_ctrl,
+ .infoframe_avi = gf119_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = gf119_sor_hdmi_infoframe_vsi,
+};
+
void
gf119_sor_clock(struct nvkm_ior *sor)
{
@@ -305,9 +328,7 @@ gf119_sor = {
.state = gf119_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gf119_sor_hdmi_ctrl,
- },
+ .hdmi = &gf119_sor_hdmi,
.dp = &gf119_sor_dp,
.hda = &gf119_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
index 7248e9ec835e..876a21a0cebb 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
@@ -30,8 +30,51 @@
#include <nvif/class.h>
void
-gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
- u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
+gk104_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe vsi;
+ const u32 hoff = head * 0x400;
+
+ pack_hdmi_infoframe(&vsi, data, size);
+
+ /* GENERIC(?) / Vendor InfoFrame? */
+ nvkm_mask(device, 0x690100 + hoff, 0x00010001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x690108 + hoff, vsi.header);
+ nvkm_wr32(device, 0x69010c + hoff, vsi.subpack0_low);
+ nvkm_wr32(device, 0x690110 + hoff, vsi.subpack0_high);
+ /* Is there a second (or further?) set of subpack registers here? */
+ nvkm_mask(device, 0x690100 + hoff, 0x00000001, 0x00000001);
+}
+
+void
+gk104_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe avi;
+ const u32 hoff = head * 0x400;
+
+ pack_hdmi_infoframe(&avi, data, size);
+
+ /* AVI InfoFrame */
+ nvkm_mask(device, 0x690000 + hoff, 0x00000001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x690008 + hoff, avi.header);
+ nvkm_wr32(device, 0x69000c + hoff, avi.subpack0_low);
+ nvkm_wr32(device, 0x690010 + hoff, avi.subpack0_high);
+ nvkm_wr32(device, 0x690014 + hoff, avi.subpack1_low);
+ nvkm_wr32(device, 0x690018 + hoff, avi.subpack1_high);
+
+ nvkm_mask(device, 0x690000 + hoff, 0x00000001, 0x00000001);
+}
+
+void
+gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 ctrl = 0x40000000 * enable |
@@ -39,11 +82,6 @@ gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
rekey;
const u32 hoff = head * 0x800;
const u32 hdmi = head * 0x400;
- struct packed_hdmi_infoframe avi_infoframe;
- struct packed_hdmi_infoframe vendor_infoframe;
-
- pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
- pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
if (!(ctrl & 0x40000000)) {
nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000);
@@ -53,28 +91,6 @@ gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
return;
}
- /* AVI InfoFrame */
- nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000);
- if (avi_size) {
- nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header);
- nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low);
- nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high);
- nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low);
- nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high);
- nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001);
- }
-
- /* GENERIC(?) / Vendor InfoFrame? */
- nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000);
- if (vendor_size) {
- nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header);
- nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low);
- nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high);
- /* Is there a second (or further?) set of subpack registers here? */
- nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001);
- }
-
-
/* ??? InfoFrame? */
nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x6900cc + hdmi, 0x00000010);
@@ -87,14 +103,19 @@ gk104_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
}
+const struct nvkm_ior_func_hdmi
+gk104_sor_hdmi = {
+ .ctrl = gk104_sor_hdmi_ctrl,
+ .infoframe_avi = gk104_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = gk104_sor_hdmi_infoframe_vsi,
+};
+
static const struct nvkm_ior_func
gk104_sor = {
.state = gf119_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gk104_sor_hdmi_ctrl,
- },
+ .hdmi = &gk104_sor_hdmi,
.dp = &gf119_sor_dp,
.hda = &gf119_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
index 9e9ef49bd8ac..b4d8e868616f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
@@ -70,9 +70,7 @@ gm107_sor = {
.state = gf119_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gk104_sor_hdmi_ctrl,
- },
+ .hdmi = &gk104_sor_hdmi,
.dp = &gm107_sor_dp,
.hda = &gf119_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
index 4ecc8f98af6e..562ebae57d44 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
@@ -79,6 +79,14 @@ gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u8 scdc)
ior->tmds.high_speed = !!(scdc & 0x2);
}
+const struct nvkm_ior_func_hdmi
+gm200_sor_hdmi = {
+ .ctrl = gk104_sor_hdmi_ctrl,
+ .scdc = gm200_sor_hdmi_scdc,
+ .infoframe_avi = gk104_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = gk104_sor_hdmi_infoframe_vsi,
+};
+
void
gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior)
{
@@ -131,10 +139,7 @@ gm200_sor = {
.state = gf119_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gk104_sor_hdmi_ctrl,
- .scdc = gm200_sor_hdmi_scdc,
- },
+ .hdmi = &gm200_sor_hdmi,
.dp = &gm200_sor_dp,
.hda = &gf119_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
index 7172a9dfd89b..7f1eb4332040 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
@@ -37,10 +37,7 @@ gp100_sor = {
.state = gf119_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gk104_sor_hdmi_ctrl,
- .scdc = gm200_sor_hdmi_scdc,
- },
+ .hdmi = &gm200_sor_hdmi,
.dp = &gm200_sor_dp,
.hda = &gf119_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
index 70c49e7af9cf..a2c7c6f83dcd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
@@ -92,9 +92,53 @@ gt215_sor_dp = {
.watermark = g94_sor_dp_watermark,
};
-void
-gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
- u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
+static void
+gt215_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe vsi;
+ const u32 soff = nv50_ior_base(ior);
+
+ pack_hdmi_infoframe(&vsi, data, size);
+
+ nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x61c544 + soff, vsi.header);
+ nvkm_wr32(device, 0x61c548 + soff, vsi.subpack0_low);
+ nvkm_wr32(device, 0x61c54c + soff, vsi.subpack0_high);
+ /* Is there a second (or up to fourth?) set of subpack registers here? */
+ /* nvkm_wr32(device, 0x61c550 + soff, vsi.subpack1_low); */
+ /* nvkm_wr32(device, 0x61c554 + soff, vsi.subpack1_high); */
+
+ nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001);
+}
+
+static void
+gt215_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe avi;
+ const u32 soff = nv50_ior_base(ior);
+
+ pack_hdmi_infoframe(&avi, data, size);
+
+ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
+ if (size)
+ return;
+
+ nvkm_wr32(device, 0x61c528 + soff, avi.header);
+ nvkm_wr32(device, 0x61c52c + soff, avi.subpack0_low);
+ nvkm_wr32(device, 0x61c530 + soff, avi.subpack0_high);
+ nvkm_wr32(device, 0x61c534 + soff, avi.subpack1_low);
+ nvkm_wr32(device, 0x61c538 + soff, avi.subpack1_high);
+
+ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001);
+}
+
+static void
+gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 ctrl = 0x40000000 * enable |
@@ -102,11 +146,6 @@ gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
max_ac_packet << 16 |
rekey;
const u32 soff = nv50_ior_base(ior);
- struct packed_hdmi_infoframe avi_infoframe;
- struct packed_hdmi_infoframe vendor_infoframe;
-
- pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
- pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
if (!(ctrl & 0x40000000)) {
nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000);
@@ -116,17 +155,6 @@ gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
return;
}
- /* AVI InfoFrame */
- nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
- if (avi_size) {
- nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header);
- nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low);
- nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high);
- nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low);
- nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high);
- nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001);
- }
-
/* Audio InfoFrame */
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x61c508 + soff, 0x000a0184);
@@ -134,18 +162,6 @@ gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
nvkm_wr32(device, 0x61c510 + soff, 0x00000000);
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001);
- /* Vendor InfoFrame */
- nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000);
- if (vendor_size) {
- nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header);
- nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low);
- nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high);
- /* Is there a second (or up to fourth?) set of subpack registers here? */
- /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */
- /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */
- nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001);
- }
-
nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
@@ -159,14 +175,19 @@ gt215_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl);
}
+const struct nvkm_ior_func_hdmi
+gt215_sor_hdmi = {
+ .ctrl = gt215_sor_hdmi_ctrl,
+ .infoframe_avi = gt215_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = gt215_sor_hdmi_infoframe_vsi,
+};
+
static const struct nvkm_ior_func
gt215_sor = {
.state = g94_sor_state,
.power = nv50_sor_power,
.clock = nv50_sor_clock,
- .hdmi = {
- .ctrl = gt215_sor_hdmi_ctrl,
- },
+ .hdmi = &gt215_sor_hdmi,
.dp = &gt215_sor_dp,
.hda = &gt215_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
index 6b9d49270fa7..115d0997fd62 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
@@ -96,9 +96,54 @@ gv100_sor_dp = {
.watermark = gv100_sor_dp_watermark,
};
-void
-gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
- u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
+static void
+gv100_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe vsi;
+ const u32 hoff = head * 0x400;
+
+ pack_hdmi_infoframe(&vsi, data, size);
+
+ nvkm_mask(device, 0x6f0100 + hoff, 0x00010001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x6f0108 + hoff, vsi.header);
+ nvkm_wr32(device, 0x6f010c + hoff, vsi.subpack0_low);
+ nvkm_wr32(device, 0x6f0110 + hoff, vsi.subpack0_high);
+ nvkm_wr32(device, 0x6f0114 + hoff, 0x00000000);
+ nvkm_wr32(device, 0x6f0118 + hoff, 0x00000000);
+ nvkm_wr32(device, 0x6f011c + hoff, 0x00000000);
+ nvkm_wr32(device, 0x6f0120 + hoff, 0x00000000);
+ nvkm_wr32(device, 0x6f0124 + hoff, 0x00000000);
+ nvkm_mask(device, 0x6f0100 + hoff, 0x00000001, 0x00000001);
+}
+
+static void
+gv100_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ struct packed_hdmi_infoframe avi;
+ const u32 hoff = head * 0x400;
+
+ pack_hdmi_infoframe(&avi, data, size);
+
+ nvkm_mask(device, 0x6f0000 + hoff, 0x00000001, 0x00000000);
+ if (!size)
+ return;
+
+ nvkm_wr32(device, 0x6f0008 + hoff, avi.header);
+ nvkm_wr32(device, 0x6f000c + hoff, avi.subpack0_low);
+ nvkm_wr32(device, 0x6f0010 + hoff, avi.subpack0_high);
+ nvkm_wr32(device, 0x6f0014 + hoff, avi.subpack1_low);
+ nvkm_wr32(device, 0x6f0018 + hoff, avi.subpack1_high);
+
+ nvkm_mask(device, 0x6f0000 + hoff, 0x00000001, 0x00000001);
+}
+
+static void
+gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 ctrl = 0x40000000 * enable |
@@ -106,11 +151,6 @@ gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
rekey;
const u32 hoff = head * 0x800;
const u32 hdmi = head * 0x400;
- struct packed_hdmi_infoframe avi_infoframe;
- struct packed_hdmi_infoframe vendor_infoframe;
-
- pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
- pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
if (!(ctrl & 0x40000000)) {
nvkm_mask(device, 0x6165c0 + hoff, 0x40000000, 0x00000000);
@@ -120,32 +160,6 @@ gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
return;
}
- /* AVI InfoFrame (AVI). */
- nvkm_mask(device, 0x6f0000 + hdmi, 0x00000001, 0x00000000);
- if (avi_size) {
- nvkm_wr32(device, 0x6f0008 + hdmi, avi_infoframe.header);
- nvkm_wr32(device, 0x6f000c + hdmi, avi_infoframe.subpack0_low);
- nvkm_wr32(device, 0x6f0010 + hdmi, avi_infoframe.subpack0_high);
- nvkm_wr32(device, 0x6f0014 + hdmi, avi_infoframe.subpack1_low);
- nvkm_wr32(device, 0x6f0018 + hdmi, avi_infoframe.subpack1_high);
- nvkm_mask(device, 0x6f0000 + hdmi, 0x00000001, 0x00000001);
- }
-
- /* Vendor-specific InfoFrame (VSI). */
- nvkm_mask(device, 0x6f0100 + hdmi, 0x00010001, 0x00000000);
- if (vendor_size) {
- nvkm_wr32(device, 0x6f0108 + hdmi, vendor_infoframe.header);
- nvkm_wr32(device, 0x6f010c + hdmi, vendor_infoframe.subpack0_low);
- nvkm_wr32(device, 0x6f0110 + hdmi, vendor_infoframe.subpack0_high);
- nvkm_wr32(device, 0x6f0114 + hdmi, 0x00000000);
- nvkm_wr32(device, 0x6f0118 + hdmi, 0x00000000);
- nvkm_wr32(device, 0x6f011c + hdmi, 0x00000000);
- nvkm_wr32(device, 0x6f0120 + hdmi, 0x00000000);
- nvkm_wr32(device, 0x6f0124 + hdmi, 0x00000000);
- nvkm_mask(device, 0x6f0100 + hdmi, 0x00000001, 0x00000001);
- }
-
-
/* General Control (GCP). */
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x6f00cc + hdmi, 0x00000010);
@@ -158,6 +172,14 @@ gv100_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packe
nvkm_mask(device, 0x6165c0 + hoff, 0x401f007f, ctrl);
}
+const struct nvkm_ior_func_hdmi
+gv100_sor_hdmi = {
+ .ctrl = gv100_sor_hdmi_ctrl,
+ .scdc = gm200_sor_hdmi_scdc,
+ .infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
void
gv100_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
{
@@ -190,10 +212,7 @@ gv100_sor = {
.state = gv100_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gv100_sor_hdmi_ctrl,
- .scdc = gm200_sor_hdmi_scdc,
- },
+ .hdmi = &gv100_sor_hdmi,
.dp = &gv100_sor_dp,
.hda = &gv100_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c
index 83152c26fe3e..7f5d13d13c94 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c
@@ -39,44 +39,6 @@ nvkm_head_find(struct nvkm_disp *disp, int id)
return NULL;
}
-int
-nvkm_head_mthd_scanoutpos(struct nvkm_object *object,
- struct nvkm_head *head, void *data, u32 size)
-{
- union {
- struct nv04_disp_scanoutpos_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "head scanoutpos size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "head scanoutpos vers %d\n",
- args->v0.version);
-
- head->func->state(head, &head->arm);
- args->v0.vtotal = head->arm.vtotal;
- args->v0.vblanks = head->arm.vblanks;
- args->v0.vblanke = head->arm.vblanke;
- args->v0.htotal = head->arm.htotal;
- args->v0.hblanks = head->arm.hblanks;
- args->v0.hblanke = head->arm.hblanke;
-
- /* We don't support reading htotal/vtotal on pre-NV50 VGA,
- * so we have to give up and trigger the timestamping
- * fallback in the drm core.
- */
- if (!args->v0.vtotal || !args->v0.htotal)
- return -ENOTSUPP;
-
- args->v0.time[0] = ktime_to_ns(ktime_get());
- head->func->rgpos(head, &args->v0.hline, &args->v0.vline);
- args->v0.time[1] = ktime_to_ns(ktime_get());
- } else
- return ret;
-
- return 0;
-}
-
void
nvkm_head_del(struct nvkm_head **phead)
{
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
index 84a2989193cf..856252bf559a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_DISP_HEAD_H__
#define __NVKM_DISP_HEAD_H__
+#include <nvif/object.h>
#include "priv.h"
struct nvkm_head {
@@ -26,12 +27,12 @@ struct nvkm_head {
u8 depth;
} or;
} arm, asy;
+
+ struct nvkm_object object;
};
int nvkm_head_new_(const struct nvkm_head_func *, struct nvkm_disp *, int id);
void nvkm_head_del(struct nvkm_head **);
-int nvkm_head_mthd_scanoutpos(struct nvkm_object *,
- struct nvkm_head *, void *, u32);
struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id);
struct nvkm_head_func {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
index 671c4674ffcc..da1b1a626ef2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
@@ -63,12 +63,12 @@ struct nvkm_ior_func {
void (*war_2)(struct nvkm_ior *);
void (*war_3)(struct nvkm_ior *);
- struct {
- void (*ctrl)(struct nvkm_ior *, int head, bool enable,
- u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size,
- u8 *vendor, u8 vendor_size);
+ const struct nvkm_ior_func_hdmi {
+ void (*ctrl)(struct nvkm_ior *, int head, bool enable, u8 max_ac_packet, u8 rekey);
void (*scdc)(struct nvkm_ior *, u8 scdc);
- } hdmi;
+ void (*infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
+ void (*infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size);
+ } *hdmi;
const struct nvkm_ior_func_dp {
u8 lanes[4];
@@ -124,9 +124,10 @@ void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
void nv50_sor_clock(struct nvkm_ior *);
int g84_sor_new(struct nvkm_disp *, int);
-void g84_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+extern const struct nvkm_ior_func_hdmi g84_sor_hdmi;
int g94_sor_cnt(struct nvkm_disp *, unsigned long *);
+
void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
extern const struct nvkm_ior_func_dp g94_sor_dp;
int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
@@ -137,7 +138,7 @@ void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8);
void g94_sor_dp_watermark(struct nvkm_ior *, int, u8);
-void gt215_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+extern const struct nvkm_ior_func_hdmi gt215_sor_hdmi;
void gt215_sor_dp_audio(struct nvkm_ior *, int, bool);
extern const struct nvkm_ior_func_hda gt215_sor_hda;
@@ -156,12 +157,16 @@ void gf119_sor_hda_hpd(struct nvkm_ior *, int, bool);
void gf119_sor_hda_eld(struct nvkm_ior *, int, u8 *, u8);
int gk104_sor_new(struct nvkm_disp *, int);
-void gk104_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+extern const struct nvkm_ior_func_hdmi gk104_sor_hdmi;
+void gk104_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8);
+void gk104_sor_hdmi_infoframe_avi(struct nvkm_ior *, int, void *, u32);
+void gk104_sor_hdmi_infoframe_vsi(struct nvkm_ior *, int, void *, u32);
void gm107_sor_dp_pattern(struct nvkm_ior *, int);
void gm200_sor_route_set(struct nvkm_outp *, struct nvkm_ior *);
int gm200_sor_route_get(struct nvkm_outp *, int *);
+extern const struct nvkm_ior_func_hdmi gm200_sor_hdmi;
void gm200_sor_hdmi_scdc(struct nvkm_ior *, u8);
extern const struct nvkm_ior_func_dp gm200_sor_dp;
void gm200_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
@@ -170,7 +175,7 @@ int gp100_sor_new(struct nvkm_disp *, int);
int gv100_sor_cnt(struct nvkm_disp *, unsigned long *);
void gv100_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
-void gv100_sor_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+extern const struct nvkm_ior_func_hdmi gv100_sor_hdmi;
void gv100_sor_dp_audio(struct nvkm_ior *, int, bool);
void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c
index 916b1d477b0b..841e3b69fcaf 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c
@@ -31,9 +31,7 @@ mcp77_sor = {
.state = g94_sor_state,
.power = nv50_sor_power,
.clock = nv50_sor_clock,
- .hdmi = {
- .ctrl = g84_sor_hdmi_ctrl,
- },
+ .hdmi = &g84_sor_hdmi,
.dp = &g94_sor_dp,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c
index a5a0b9439374..f96ba4752655 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c
@@ -44,9 +44,7 @@ mcp89_sor = {
.state = g94_sor_state,
.power = nv50_sor_power,
.clock = nv50_sor_clock,
- .hdmi = {
- .ctrl = gt215_sor_hdmi_ctrl,
- },
+ .hdmi = &gt215_sor_hdmi,
.dp = &mcp89_sor_dp,
.hda = &gt215_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
index a46e13cc9ff1..be8116802960 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
@@ -503,7 +503,7 @@ nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
void
nv50_disp_chan_uevent_send(struct nvkm_disp *disp, int chid)
{
- nvkm_event_send(&disp->uevent, NVKM_DISP_EVENT_CHAN_AWAKEN, chid, NULL, 0);
+ nvkm_event_ntfy(&disp->uevent, chid, NVKM_DISP_EVENT_CHAN_AWAKEN);
}
const struct nvkm_event_func
@@ -1238,6 +1238,8 @@ nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head)
if (!ior)
return;
+ outp = ior->asy.outp;
+
/* For some reason, NVIDIA decided not to:
*
* A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
@@ -1247,13 +1249,13 @@ nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head)
* Override the values we usually read from HW with the same
* data we pass though an ioctl instead.
*/
- if (ior->type == SOR && ior->asy.proto == LVDS) {
- head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18;
- ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1;
+ if (outp && ior->type == SOR && ior->asy.proto == LVDS) {
+ head->asy.or.depth = outp->lvds.bpc8 ? 24 : 18;
+ ior->asy.link = outp->lvds.dual ? 3 : 1;
}
/* Handle any link training, etc. */
- if ((outp = ior->asy.outp) && outp->func->acquire)
+ if (outp && outp->func->acquire)
outp->func->acquire(outp);
/* Execute OnInt2 IED script. */
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
index 3f3924c41957..b7631c1ab242 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -2,7 +2,6 @@
#ifndef __NVKM_DISP_OUTP_H__
#define __NVKM_DISP_OUTP_H__
#include "priv.h"
-#include <core/notify.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -28,13 +27,19 @@ struct nvkm_outp {
union {
struct {
+ bool dual;
+ bool bpc8;
+ } lvds;
+
+ struct {
struct nvbios_dpout info;
u8 version;
struct nvkm_i2c_aux *aux;
- struct nvkm_notify hpd;
- bool present;
+ bool enabled;
+ bool aux_pwr;
+ bool aux_pwr_pu;
u8 lttpr[6];
u8 lttprs;
u8 dpcd[16];
@@ -49,12 +54,17 @@ struct nvkm_outp {
struct mutex mutex;
struct {
atomic_t done;
+ u8 nr;
+ u8 bw;
bool mst;
} lt;
} dp;
};
struct nvkm_object object;
+ struct {
+ struct nvkm_head *head;
+ } asy;
};
int nvkm_outp_new_(const struct nvkm_outp_func *, struct nvkm_disp *, int index,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
index cb25dfe849f0..ec5292a8f3c8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
@@ -42,10 +42,6 @@ struct nvkm_disp_func {
} user[];
};
-int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **);
-int nv04_disp_mthd(struct nvkm_object *, u32, void *, u32);
-int nv50_disp_root_mthd_(struct nvkm_object *, u32, void *, u32);
-
int nv50_disp_oneinit(struct nvkm_disp *);
int nv50_disp_init(struct nvkm_disp *);
void nv50_disp_fini(struct nvkm_disp *);
@@ -86,4 +82,5 @@ extern const struct nvkm_event_func gv100_disp_chan_uevent;
int nvkm_udisp_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
int nvkm_uconn_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
int nvkm_uoutp_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
+int nvkm_uhead_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c
deleted file mode 100644
index 9acaec5c271e..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-#include "priv.h"
-#include "head.h"
-
-#include <core/client.h>
-
-#include <nvif/cl0046.h>
-#include <nvif/unpack.h>
-
-int
-nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
-{
- struct nvkm_disp *disp = nvkm_disp(object->engine);
- union {
- struct nv04_disp_mthd_v0 v0;
- } *args = data;
- struct nvkm_head *head;
- int id, ret = -ENOSYS;
-
- nvif_ioctl(object, "disp mthd size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
- nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
- args->v0.version, args->v0.method, args->v0.head);
- mthd = args->v0.method;
- id = args->v0.head;
- } else
- return ret;
-
- if (!(head = nvkm_head_find(disp, id)))
- return -ENXIO;
-
- switch (mthd) {
- case NV04_DISP_SCANOUTPOS:
- return nvkm_head_mthd_scanoutpos(object, head, data, size);
- default:
- break;
- }
-
- return -EINVAL;
-}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
deleted file mode 100644
index 0af45ccd140c..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright 2012 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: Ben Skeggs
- */
-#include "chan.h"
-#include "head.h"
-#include "ior.h"
-#include "outp.h"
-
-#include <core/client.h>
-
-#include <nvif/class.h>
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
-{
- union {
- struct nv50_disp_mthd_v0 v0;
- struct nv50_disp_mthd_v1 v1;
- } *args = data;
- struct nvkm_disp *disp = nvkm_udisp(object);
- struct nvkm_outp *temp, *outp = NULL;
- struct nvkm_head *head;
- u16 type, mask = 0;
- int hidx, ret = -ENOSYS;
-
- if (mthd != NV50_DISP_MTHD)
- return -EINVAL;
-
- nvif_ioctl(object, "disp mthd size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
- nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
- args->v0.version, args->v0.method, args->v0.head);
- mthd = args->v0.method;
- hidx = args->v0.head;
- } else
- if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
- nvif_ioctl(object, "disp mthd vers %d mthd %02x "
- "type %04x mask %04x\n",
- args->v1.version, args->v1.method,
- args->v1.hasht, args->v1.hashm);
- mthd = args->v1.method;
- type = args->v1.hasht;
- mask = args->v1.hashm;
- hidx = ffs((mask >> 8) & 0x0f) - 1;
- } else
- return ret;
-
- if (!(head = nvkm_head_find(disp, hidx)))
- return -ENXIO;
-
- if (mask) {
- list_for_each_entry(temp, &disp->outps, head) {
- if ((temp->info.hasht == type) &&
- (temp->info.hashm & mask) == mask) {
- outp = temp;
- break;
- }
- }
- if (outp == NULL)
- return -ENXIO;
- }
-
- switch (mthd) {
- case NV50_DISP_SCANOUTPOS: {
- return nvkm_head_mthd_scanoutpos(object, head, data, size);
- }
- default:
- break;
- }
-
- switch (mthd * !!outp) {
- case NV50_DISP_MTHD_V1_ACQUIRE: {
- union {
- struct nv50_disp_acquire_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, args->v0.hda);
- if (ret == 0) {
- args->v0.or = outp->ior->id;
- args->v0.link = outp->ior->asy.link;
- }
- }
- return ret;
- }
- break;
- case NV50_DISP_MTHD_V1_RELEASE:
- nvkm_outp_release(outp, NVKM_OUTP_USER);
- return 0;
- case NV50_DISP_MTHD_V1_SOR_HDA_ELD: {
- union {
- struct nv50_disp_sor_hda_eld_v0 v0;
- } *args = data;
- struct nvkm_ior *ior = outp->ior;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp sor hda eld size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
- nvif_ioctl(object, "disp sor hda eld vers %d\n",
- args->v0.version);
- if (size > 0x60)
- return -E2BIG;
- } else
- return ret;
-
- if (!ior->hda)
- return -ENODEV;
-
- if (size && args->v0.data[0]) {
- if (outp->info.type == DCB_OUTPUT_DP)
- ior->func->dp->audio(ior, hidx, true);
- ior->func->hda->hpd(ior, hidx, true);
- ior->func->hda->eld(ior, hidx, data, size);
- } else {
- if (outp->info.type == DCB_OUTPUT_DP)
- ior->func->dp->audio(ior, hidx, false);
- ior->func->hda->hpd(ior, hidx, false);
- }
-
- return 0;
- }
- break;
- case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: {
- union {
- struct nv50_disp_sor_hdmi_pwr_v0 v0;
- } *args = data;
- u8 *vendor, vendor_size;
- u8 *avi, avi_size;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
- nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
- "max_ac_packet %d rekey %d scdc %d\n",
- args->v0.version, args->v0.state,
- args->v0.max_ac_packet, args->v0.rekey,
- args->v0.scdc);
- if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
- return -EINVAL;
- if ((args->v0.avi_infoframe_length
- + args->v0.vendor_infoframe_length) > size)
- return -EINVAL;
- else
- if ((args->v0.avi_infoframe_length
- + args->v0.vendor_infoframe_length) < size)
- return -E2BIG;
- avi = data;
- avi_size = args->v0.avi_infoframe_length;
- vendor = avi + avi_size;
- vendor_size = args->v0.vendor_infoframe_length;
- } else
- return ret;
-
- if (!outp->ior->func->hdmi.ctrl)
- return -ENODEV;
-
- outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state,
- args->v0.max_ac_packet,
- args->v0.rekey, avi, avi_size,
- vendor, vendor_size);
-
- if (outp->ior->func->hdmi.scdc)
- outp->ior->func->hdmi.scdc(outp->ior, args->v0.scdc);
-
- return 0;
- }
- break;
- case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
- union {
- struct nv50_disp_sor_lvds_script_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
- nvif_ioctl(object, "disp sor lvds script size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor lvds script "
- "vers %d name %04x\n",
- args->v0.version, args->v0.script);
- disp->sor.lvdsconf = args->v0.script;
- return 0;
- } else
- return ret;
- }
- break;
- case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
- union {
- struct nv50_disp_sor_dp_mst_link_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
- nvif_ioctl(object, "disp sor dp mst link size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
- args->v0.version, args->v0.state);
- outp->dp.lt.mst = !!args->v0.state;
- return 0;
- } else
- return ret;
- }
- break;
- case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
- union {
- struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
- nvif_ioctl(object, "disp sor dp mst vcpi size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor dp mst vcpi vers %d "
- "slot %02x/%02x pbn %04x/%04x\n",
- args->v0.version, args->v0.start_slot,
- args->v0.num_slots, args->v0.pbn,
- args->v0.aligned_pbn);
- if (!outp->ior->func->dp->vcpi)
- return -ENODEV;
- outp->ior->func->dp->vcpi(outp->ior, hidx,
- args->v0.start_slot,
- args->v0.num_slots,
- args->v0.pbn,
- args->v0.aligned_pbn);
- return 0;
- } else
- return ret;
- }
- break;
- default:
- break;
- }
-
- return -EINVAL;
-}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
index e4ad1a6f6c88..f5242a672279 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
@@ -88,10 +88,7 @@ tu102_sor = {
.state = gv100_sor_state,
.power = nv50_sor_power,
.clock = gf119_sor_clock,
- .hdmi = {
- .ctrl = gv100_sor_hdmi_ctrl,
- .scdc = gm200_sor_hdmi_scdc,
- },
+ .hdmi = &gv100_sor_hdmi,
.dp = &tu102_sor_dp,
.hda = &gv100_sor_hda,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
index fd9f18144c26..dad942be6679 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
@@ -21,12 +21,86 @@
*/
#define nvkm_uconn(p) container_of((p), struct nvkm_conn, object)
#include "conn.h"
+#include "outp.h"
+#include <core/client.h>
+#include <core/event.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
#include <nvif/if0011.h>
static int
+nvkm_uconn_uevent_aux(struct nvkm_object *object, u64 token, u32 bits)
+{
+ union nvif_conn_event_args args;
+
+ args.v0.version = 0;
+ args.v0.types = 0;
+ if (bits & NVKM_I2C_PLUG)
+ args.v0.types |= NVIF_CONN_EVENT_V0_PLUG;
+ if (bits & NVKM_I2C_UNPLUG)
+ args.v0.types |= NVIF_CONN_EVENT_V0_UNPLUG;
+ if (bits & NVKM_I2C_IRQ)
+ args.v0.types |= NVIF_CONN_EVENT_V0_IRQ;
+
+ return object->client->event(token, &args, sizeof(args.v0));
+}
+
+static int
+nvkm_uconn_uevent_gpio(struct nvkm_object *object, u64 token, u32 bits)
+{
+ union nvif_conn_event_args args;
+
+ args.v0.version = 0;
+ args.v0.types = 0;
+ if (bits & NVKM_GPIO_HI)
+ args.v0.types |= NVIF_CONN_EVENT_V0_PLUG;
+ if (bits & NVKM_GPIO_LO)
+ args.v0.types |= NVIF_CONN_EVENT_V0_UNPLUG;
+
+ return object->client->event(token, &args, sizeof(args.v0));
+}
+
+static int
+nvkm_uconn_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_uevent *uevent)
+{
+ struct nvkm_conn *conn = nvkm_uconn(object);
+ struct nvkm_device *device = conn->disp->engine.subdev.device;
+ struct nvkm_outp *outp;
+ union nvif_conn_event_args *args = argv;
+ u64 bits = 0;
+
+ if (!uevent) {
+ if (conn->info.hpd == DCB_GPIO_UNUSED)
+ return -ENOSYS;
+ return 0;
+ }
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+
+ list_for_each_entry(outp, &conn->disp->outps, head) {
+ if (outp->info.connector == conn->index && outp->dp.aux) {
+ if (args->v0.types & NVIF_CONN_EVENT_V0_PLUG ) bits |= NVKM_I2C_PLUG;
+ if (args->v0.types & NVIF_CONN_EVENT_V0_UNPLUG) bits |= NVKM_I2C_UNPLUG;
+ if (args->v0.types & NVIF_CONN_EVENT_V0_IRQ ) bits |= NVKM_I2C_IRQ;
+
+ return nvkm_uevent_add(uevent, &device->i2c->event, outp->dp.aux->id, bits,
+ nvkm_uconn_uevent_aux);
+ }
+ }
+
+ if (args->v0.types & NVIF_CONN_EVENT_V0_PLUG ) bits |= NVKM_GPIO_HI;
+ if (args->v0.types & NVIF_CONN_EVENT_V0_UNPLUG) bits |= NVKM_GPIO_LO;
+ if (args->v0.types & NVIF_CONN_EVENT_V0_IRQ)
+ return -EINVAL;
+
+ return nvkm_uevent_add(uevent, &device->gpio->event, conn->info.hpd, bits,
+ 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;
@@ -82,6 +156,7 @@ static const struct nvkm_object_func
nvkm_uconn = {
.dtor = nvkm_uconn_dtor,
.mthd = nvkm_uconn_mthd,
+ .uevent = nvkm_uconn_uevent,
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c
index 0841e7ce0343..0268d1d75805 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c
@@ -21,6 +21,7 @@
*/
#include "priv.h"
#include "conn.h"
+#include "head.h"
#include "outp.h"
#include <nvif/class.h>
@@ -43,6 +44,12 @@ nvkm_udisp_sclass(struct nvkm_object *object, int index, struct nvkm_oclass *scl
return 0;
}
+ if (index-- == 0) {
+ sclass->base = (struct nvkm_sclass) { 0, 0, NVIF_CLASS_HEAD };
+ sclass->ctor = nvkm_uhead_new;
+ return 0;
+ }
+
if (disp->func->user[index].ctor) {
sclass->base = disp->func->user[index].base;
sclass->ctor = disp->func->user[index].ctor;
@@ -52,17 +59,6 @@ nvkm_udisp_sclass(struct nvkm_object *object, int index, struct nvkm_oclass *scl
return -EINVAL;
}
-static int
-nvkm_udisp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
-{
- struct nvkm_disp *disp = nvkm_udisp(object);
-
- if (disp->engine.subdev.device->card_type >= NV_50)
- return nv50_disp_root_mthd_(object, mthd, argv, argc);
-
- return nv04_disp_mthd(object, mthd, argv, argc);
-}
-
static void *
nvkm_udisp_dtor(struct nvkm_object *object)
{
@@ -78,8 +74,6 @@ nvkm_udisp_dtor(struct nvkm_object *object)
static const struct nvkm_object_func
nvkm_udisp = {
.dtor = nvkm_udisp_dtor,
- .mthd = nvkm_udisp_mthd,
- .ntfy = nvkm_disp_ntfy,
.sclass = nvkm_udisp_sclass,
};
@@ -89,6 +83,7 @@ nvkm_udisp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv
struct nvkm_disp *disp = nvkm_disp(oclass->engine);
struct nvkm_conn *conn;
struct nvkm_outp *outp;
+ struct nvkm_head *head;
union nvif_disp_args *args = argv;
if (argc != sizeof(args->v0) || args->v0.version != 0)
@@ -111,5 +106,9 @@ nvkm_udisp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv
list_for_each_entry(outp, &disp->outps, head)
args->v0.outp_mask |= BIT(outp->index);
+ args->v0.head_mask = 0;
+ list_for_each_entry(head, &disp->heads, head)
+ args->v0.head_mask |= BIT(head->id);
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uhead.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uhead.c
new file mode 100644
index 000000000000..f072cec16040
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uhead.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#define nvkm_uhead(p) container_of((p), struct nvkm_head, object)
+#include "head.h"
+#include <core/event.h>
+
+#include <nvif/if0013.h>
+
+#include <nvif/event.h>
+
+static int
+nvkm_uhead_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_uevent *uevent)
+{
+ struct nvkm_head *head = nvkm_uhead(object);
+ union nvif_head_event_args *args = argv;
+
+ if (!uevent)
+ return 0;
+ if (argc != sizeof(args->vn))
+ return -ENOSYS;
+
+ return nvkm_uevent_add(uevent, &head->disp->vblank, head->id,
+ NVKM_DISP_HEAD_EVENT_VBLANK, NULL);
+}
+
+static int
+nvkm_uhead_mthd_scanoutpos(struct nvkm_head *head, void *argv, u32 argc)
+{
+ union nvif_head_scanoutpos_args *args = argv;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+
+ head->func->state(head, &head->arm);
+ args->v0.vtotal = head->arm.vtotal;
+ args->v0.vblanks = head->arm.vblanks;
+ args->v0.vblanke = head->arm.vblanke;
+ args->v0.htotal = head->arm.htotal;
+ args->v0.hblanks = head->arm.hblanks;
+ args->v0.hblanke = head->arm.hblanke;
+
+ /* We don't support reading htotal/vtotal on pre-NV50 VGA,
+ * so we have to give up and trigger the timestamping
+ * fallback in the drm core.
+ */
+ if (!args->v0.vtotal || !args->v0.htotal)
+ return -ENOTSUPP;
+
+ args->v0.time[0] = ktime_to_ns(ktime_get());
+ head->func->rgpos(head, &args->v0.hline, &args->v0.vline);
+ args->v0.time[1] = ktime_to_ns(ktime_get());
+ return 0;
+}
+
+static int
+nvkm_uhead_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
+{
+ struct nvkm_head *head = nvkm_uhead(object);
+
+ switch (mthd) {
+ case NVIF_HEAD_V0_SCANOUTPOS: return nvkm_uhead_mthd_scanoutpos(head, argv, argc);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void *
+nvkm_uhead_dtor(struct nvkm_object *object)
+{
+ struct nvkm_head *head = nvkm_uhead(object);
+ struct nvkm_disp *disp = head->disp;
+
+ spin_lock(&disp->client.lock);
+ head->object.func = NULL;
+ spin_unlock(&disp->client.lock);
+ return NULL;
+}
+
+static const struct nvkm_object_func
+nvkm_uhead = {
+ .dtor = nvkm_uhead_dtor,
+ .mthd = nvkm_uhead_mthd,
+ .uevent = nvkm_uhead_uevent,
+};
+
+int
+nvkm_uhead_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject)
+{
+ struct nvkm_disp *disp = nvkm_udisp(oclass->parent);
+ struct nvkm_head *head;
+ union nvif_head_args *args = argv;
+ int ret;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+ if (!(head = nvkm_head_find(disp, args->v0.id)))
+ return -EINVAL;
+
+ ret = -EBUSY;
+ spin_lock(&disp->client.lock);
+ if (!head->object.func) {
+ nvkm_object_ctor(&nvkm_uhead, oclass, &head->object);
+ *pobject = &head->object;
+ ret = 0;
+ }
+ spin_unlock(&disp->client.lock);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
index abedb3e86361..4f0ca709c85a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
@@ -21,11 +21,238 @@
*/
#define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)
#include "outp.h"
+#include "dp.h"
+#include "head.h"
#include "ior.h"
#include <nvif/if0012.h>
static int
+nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ struct nvkm_ior *ior = outp->ior;
+ union nvif_outp_dp_mst_vcpi_args *args = argv;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+ if (!ior->func->dp || !ior->func->dp->vcpi || !nvkm_head_find(outp->disp, args->v0.head))
+ return -EINVAL;
+
+ ior->func->dp->vcpi(ior, args->v0.head, args->v0.start_slot, args->v0.num_slots,
+ args->v0.pbn, args->v0.aligned_pbn);
+ return 0;
+}
+
+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;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+
+ outp->dp.enabled = !!args->v0.state;
+ nvkm_dp_enable(outp, outp->dp.enabled);
+ return 0;
+}
+
+static int
+nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ struct nvkm_ior *ior = outp->ior;
+ union nvif_outp_hda_eld_args *args = argv;
+
+ if (argc < sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+ argc -= sizeof(args->v0);
+
+ if (!ior->hda || !nvkm_head_find(outp->disp, args->v0.head))
+ return -EINVAL;
+ if (argc > 0x60)
+ return -E2BIG;
+
+ if (argc && args->v0.data[0]) {
+ if (outp->info.type == DCB_OUTPUT_DP)
+ ior->func->dp->audio(ior, args->v0.head, true);
+ ior->func->hda->hpd(ior, args->v0.head, true);
+ ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc);
+ } else {
+ if (outp->info.type == DCB_OUTPUT_DP)
+ ior->func->dp->audio(ior, args->v0.head, false);
+ ior->func->hda->hpd(ior, args->v0.head, false);
+ }
+
+ return 0;
+}
+
+static int
+nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ struct nvkm_ior *ior = outp->ior;
+ union nvif_outp_infoframe_args *args = argv;
+ ssize_t size = argc - sizeof(*args);
+
+ if (argc < sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+ if (!nvkm_head_find(outp->disp, args->v0.head))
+ return -EINVAL;
+
+ switch (ior->func->hdmi ? args->v0.type : 0xff) {
+ case NVIF_OUTP_INFOFRAME_V0_AVI:
+ ior->func->hdmi->infoframe_avi(ior, args->v0.head, &args->v0.data, size);
+ return 0;
+ case NVIF_OUTP_INFOFRAME_V0_VSI:
+ ior->func->hdmi->infoframe_vsi(ior, args->v0.head, &args->v0.data, size);
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int
+nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ struct nvkm_head *head = outp->asy.head;
+ struct nvkm_ior *ior = outp->ior;
+ union nvif_outp_release_args *args = argv;
+
+ if (argc != sizeof(args->vn))
+ return -ENOSYS;
+
+ if (ior->func->hdmi && head) {
+ ior->func->hdmi->infoframe_avi(ior, head->id, NULL, 0);
+ ior->func->hdmi->infoframe_vsi(ior, head->id, NULL, 0);
+ ior->func->hdmi->ctrl(ior, head->id, false, 0, 0);
+ }
+
+ nvkm_outp_release(outp, NVKM_OUTP_USER);
+ return 0;
+}
+
+static int
+nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[16],
+ u8 link_nr, u8 link_bw, bool hda, bool mst)
+{
+ int ret;
+
+ ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda);
+ if (ret)
+ return ret;
+
+ memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd));
+ outp->dp.lt.nr = link_nr;
+ outp->dp.lt.bw = link_bw;
+ outp->dp.lt.mst = mst;
+ return 0;
+}
+
+static int
+nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet,
+ u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda)
+{
+ struct nvkm_ior *ior;
+ int ret;
+
+ if (!(outp->asy.head = nvkm_head_find(outp->disp, head)))
+ return -EINVAL;
+
+ ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hdmi && hdmi_hda);
+ if (ret)
+ return ret;
+
+ ior = outp->ior;
+
+ if (hdmi) {
+ if (!ior->func->hdmi ||
+ hdmi_max_ac_packet > 0x1f || hdmi_rekey > 0x7f ||
+ (hdmi_scdc && !ior->func->hdmi->scdc)) {
+ nvkm_outp_release(outp, NVKM_OUTP_USER);
+ return -EINVAL;
+ }
+
+ ior->func->hdmi->ctrl(ior, head, hdmi, hdmi_max_ac_packet, hdmi_rekey);
+ if (ior->func->hdmi->scdc)
+ ior->func->hdmi->scdc(ior, hdmi_scdc);
+ }
+
+ return 0;
+}
+
+static int
+nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp *outp, bool dual, bool bpc8)
+{
+ if (outp->info.type != DCB_OUTPUT_LVDS)
+ return -EINVAL;
+
+ outp->lvds.dual = dual;
+ outp->lvds.bpc8 = bpc8;
+
+ return nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
+}
+
+static int
+nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+ union nvif_outp_acquire_args *args = argv;
+ int ret;
+
+ if (argc != sizeof(args->v0) || args->v0.version != 0)
+ return -ENOSYS;
+ if (outp->ior)
+ return -EBUSY;
+
+ switch (args->v0.proto) {
+ case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
+ ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
+ break;
+ case NVIF_OUTP_ACQUIRE_V0_TMDS:
+ ret = nvkm_uoutp_mthd_acquire_tmds(outp, args->v0.tmds.head,
+ args->v0.tmds.hdmi,
+ args->v0.tmds.hdmi_max_ac_packet,
+ args->v0.tmds.hdmi_rekey,
+ args->v0.tmds.hdmi_scdc,
+ args->v0.tmds.hdmi_hda);
+ break;
+ case NVIF_OUTP_ACQUIRE_V0_LVDS:
+ ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
+ break;
+ case NVIF_OUTP_ACQUIRE_V0_DP:
+ ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd,
+ args->v0.dp.link_nr,
+ args->v0.dp.link_bw,
+ args->v0.dp.hda != 0,
+ args->v0.dp.mst != 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ args->v0.or = outp->ior->id;
+ args->v0.link = outp->ior->asy.link;
+ return 0;
+}
+
+static int
nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
{
union nvif_outp_load_detect_args *args = argv;
@@ -49,10 +276,28 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
}
static int
+nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
+{
+ switch (mthd) {
+ 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);
+ case NVIF_OUTP_V0_DP_MST_VCPI: return nvkm_uoutp_mthd_dp_mst_vcpi(outp, argv, argc);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int
nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
{
switch (mthd) {
case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
+ case NVIF_OUTP_V0_ACQUIRE : return nvkm_uoutp_mthd_acquire (outp, argv, argc);
+ case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
default:
break;
}
@@ -73,6 +318,11 @@ nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
if (ret <= 0)
goto done;
+ if (outp->ior)
+ ret = nvkm_uoutp_mthd_acquired(outp, mthd, argv, argc);
+ else
+ ret = -EIO;
+
done:
mutex_unlock(&disp->super.mutex);
return ret;