aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/gpu/drm/nouveau/nvkm/engine
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c243
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c65
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c140
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c652
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h (renamed from drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h)38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c401
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c422
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c26
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c73
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c105
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h56
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c92
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h169
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c991
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c214
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c282
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c163
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c60
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c259
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c290
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c203
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c163
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c69
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c48
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c100
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c3
67 files changed, 3746 insertions, 3166 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index b690bc12a5b7..7bdc7a5ae723 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1257,7 +1257,7 @@ nvaa_chipset = {
.therm = g84_therm_new,
.timer = nv41_timer_new,
.volt = nv40_volt_new,
- .disp = g94_disp_new,
+ .disp = mcp77_disp_new,
.dma = nv50_dma_new,
.fifo = g84_fifo_new,
.gr = gt200_gr_new,
@@ -1289,7 +1289,7 @@ nvac_chipset = {
.therm = g84_therm_new,
.timer = nv41_timer_new,
.volt = nv40_volt_new,
- .disp = g94_disp_new,
+ .disp = mcp77_disp_new,
.dma = nv50_dma_new,
.fifo = g84_fifo_new,
.gr = mcp79_gr_new,
@@ -1323,7 +1323,7 @@ nvaf_chipset = {
.timer = nv41_timer_new,
.volt = nv40_volt_new,
.ce[0] = gt215_ce_new,
- .disp = gt215_disp_new,
+ .disp = mcp89_disp_new,
.dma = nv50_dma_new,
.fifo = g84_fifo_new,
.gr = mcp89_gr_new,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
index 6474bd2a6d07..189ed80e21ff 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
@@ -51,10 +51,12 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
reset_control_assert(tdev->rst);
udelay(10);
- ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
- if (ret)
- goto err_clamp;
- udelay(10);
+ if (!tdev->pdev->dev.pm_domain) {
+ ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
+ if (ret)
+ goto err_clamp;
+ udelay(10);
+ }
reset_control_deassert(tdev->rst);
udelay(10);
@@ -80,9 +82,6 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev)
{
int ret;
- reset_control_assert(tdev->rst);
- udelay(10);
-
clk_disable_unprepare(tdev->clk_pwr);
if (tdev->clk_ref)
clk_disable_unprepare(tdev->clk_ref);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
index fa05d16ae948..48ce6699183e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
@@ -4,7 +4,9 @@ nvkm-y += nvkm/engine/disp/nv50.o
nvkm-y += nvkm/engine/disp/g84.o
nvkm-y += nvkm/engine/disp/g94.o
nvkm-y += nvkm/engine/disp/gt200.o
+nvkm-y += nvkm/engine/disp/mcp77.o
nvkm-y += nvkm/engine/disp/gt215.o
+nvkm-y += nvkm/engine/disp/mcp89.o
nvkm-y += nvkm/engine/disp/gf119.o
nvkm-y += nvkm/engine/disp/gk104.o
nvkm-y += nvkm/engine/disp/gk110.o
@@ -12,29 +14,41 @@ nvkm-y += nvkm/engine/disp/gm107.o
nvkm-y += nvkm/engine/disp/gm200.o
nvkm-y += nvkm/engine/disp/gp100.o
nvkm-y += nvkm/engine/disp/gp102.o
+nvkm-y += nvkm/engine/disp/vga.o
-nvkm-y += nvkm/engine/disp/outp.o
-nvkm-y += nvkm/engine/disp/outpdp.o
+nvkm-y += nvkm/engine/disp/head.o
+nvkm-y += nvkm/engine/disp/headnv04.o
+nvkm-y += nvkm/engine/disp/headnv50.o
+nvkm-y += nvkm/engine/disp/headgf119.o
+
+nvkm-y += nvkm/engine/disp/ior.o
nvkm-y += nvkm/engine/disp/dacnv50.o
+nvkm-y += nvkm/engine/disp/dacgf119.o
nvkm-y += nvkm/engine/disp/piornv50.o
nvkm-y += nvkm/engine/disp/sornv50.o
+nvkm-y += nvkm/engine/disp/sorg84.o
nvkm-y += nvkm/engine/disp/sorg94.o
+nvkm-y += nvkm/engine/disp/sormcp77.o
+nvkm-y += nvkm/engine/disp/sorgt215.o
+nvkm-y += nvkm/engine/disp/sormcp89.o
nvkm-y += nvkm/engine/disp/sorgf119.o
+nvkm-y += nvkm/engine/disp/sorgk104.o
nvkm-y += nvkm/engine/disp/sorgm107.o
nvkm-y += nvkm/engine/disp/sorgm200.o
-nvkm-y += nvkm/engine/disp/dport.o
-nvkm-y += nvkm/engine/disp/conn.o
+nvkm-y += nvkm/engine/disp/outp.o
+nvkm-y += nvkm/engine/disp/dp.o
nvkm-y += nvkm/engine/disp/hdagt215.o
nvkm-y += nvkm/engine/disp/hdagf119.o
+nvkm-y += nvkm/engine/disp/hdmi.o
nvkm-y += nvkm/engine/disp/hdmig84.o
nvkm-y += nvkm/engine/disp/hdmigt215.o
nvkm-y += nvkm/engine/disp/hdmigf119.o
nvkm-y += nvkm/engine/disp/hdmigk104.o
-nvkm-y += nvkm/engine/disp/vga.o
+nvkm-y += nvkm/engine/disp/conn.o
nvkm-y += nvkm/engine/disp/rootnv04.o
nvkm-y += nvkm/engine/disp/rootnv50.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
index 1efe91b1e22b..c7c84d34d97e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
@@ -23,6 +23,9 @@
*/
#include "priv.h"
#include "conn.h"
+#include "dp.h"
+#include "head.h"
+#include "ior.h"
#include "outp.h"
#include <core/client.h>
@@ -37,17 +40,21 @@
#include <nvif/unpack.h>
static void
-nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int head)
+nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id)
{
struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
- disp->func->head.vblank_fini(disp, head);
+ struct nvkm_head *head = nvkm_head_find(disp, id);
+ if (head)
+ head->func->vblank_put(head);
}
static void
-nvkm_disp_vblank_init(struct nvkm_event *event, int type, int head)
+nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id)
{
struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
- disp->func->head.vblank_init(disp, head);
+ struct nvkm_head *head = nvkm_head_find(disp, id);
+ if (head)
+ head->func->vblank_get(head);
}
static int
@@ -96,7 +103,7 @@ nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size,
union {
struct nvif_notify_conn_req_v0 v0;
} *req = data;
- struct nvkm_output *outp;
+ struct nvkm_outp *outp;
int ret = -ENOSYS;
if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
@@ -210,15 +217,15 @@ static int
nvkm_disp_fini(struct nvkm_engine *engine, bool suspend)
{
struct nvkm_disp *disp = nvkm_disp(engine);
- struct nvkm_connector *conn;
- struct nvkm_output *outp;
+ struct nvkm_conn *conn;
+ struct nvkm_outp *outp;
list_for_each_entry(outp, &disp->outp, head) {
- nvkm_output_fini(outp);
+ nvkm_outp_fini(outp);
}
list_for_each_entry(conn, &disp->conn, head) {
- nvkm_connector_fini(conn);
+ nvkm_conn_fini(conn);
}
return 0;
@@ -228,129 +235,70 @@ static int
nvkm_disp_init(struct nvkm_engine *engine)
{
struct nvkm_disp *disp = nvkm_disp(engine);
- struct nvkm_connector *conn;
- struct nvkm_output *outp;
+ struct nvkm_conn *conn;
+ struct nvkm_outp *outp;
list_for_each_entry(conn, &disp->conn, head) {
- nvkm_connector_init(conn);
+ nvkm_conn_init(conn);
}
list_for_each_entry(outp, &disp->outp, head) {
- nvkm_output_init(outp);
+ nvkm_outp_init(outp);
}
return 0;
}
-static void *
-nvkm_disp_dtor(struct nvkm_engine *engine)
+static int
+nvkm_disp_oneinit(struct nvkm_engine *engine)
{
struct nvkm_disp *disp = nvkm_disp(engine);
- struct nvkm_connector *conn;
- struct nvkm_output *outp;
- void *data = disp;
-
- if (disp->func->dtor)
- data = disp->func->dtor(disp);
-
- nvkm_event_fini(&disp->vblank);
- nvkm_event_fini(&disp->hpd);
-
- while (!list_empty(&disp->outp)) {
- outp = list_first_entry(&disp->outp, typeof(*outp), head);
- list_del(&outp->head);
- nvkm_output_del(&outp);
- }
-
- while (!list_empty(&disp->conn)) {
- conn = list_first_entry(&disp->conn, typeof(*conn), head);
- list_del(&conn->head);
- nvkm_connector_del(&conn);
- }
-
- return data;
-}
-
-static const struct nvkm_engine_func
-nvkm_disp = {
- .dtor = nvkm_disp_dtor,
- .init = nvkm_disp_init,
- .fini = nvkm_disp_fini,
- .intr = nvkm_disp_intr,
- .base.sclass = nvkm_disp_class_get,
-};
-
-int
-nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
- int index, int heads, struct nvkm_disp *disp)
-{
- struct nvkm_bios *bios = device->bios;
- struct nvkm_output *outp, *outt, *pair;
- struct nvkm_connector *conn;
+ struct nvkm_subdev *subdev = &disp->engine.subdev;
+ struct nvkm_bios *bios = subdev->device->bios;
+ struct nvkm_outp *outp, *outt, *pair;
+ struct nvkm_conn *conn;
+ struct nvkm_head *head;
struct nvbios_connE connE;
struct dcb_output dcbE;
u8 hpd = 0, ver, hdr;
u32 data;
int ret, i;
- INIT_LIST_HEAD(&disp->outp);
- INIT_LIST_HEAD(&disp->conn);
- disp->func = func;
- disp->head.nr = heads;
-
- ret = nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
- if (ret)
- return ret;
-
- /* create output objects for each display path in the vbios */
+ /* Create output path objects for each VBIOS display path. */
i = -1;
while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
- const struct nvkm_disp_func_outp *outps;
- int (*ctor)(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-
if (dcbE.type == DCB_OUTPUT_UNUSED)
continue;
if (dcbE.type == DCB_OUTPUT_EOL)
break;
outp = NULL;
- switch (dcbE.location) {
- case 0: outps = &disp->func->outp.internal; break;
- case 1: outps = &disp->func->outp.external; break;
- default:
- nvkm_warn(&disp->engine.subdev,
- "dcb %d locn %d unknown\n", i, dcbE.location);
- continue;
- }
-
switch (dcbE.type) {
- case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break;
- case DCB_OUTPUT_TV : ctor = outps->tv ; break;
- case DCB_OUTPUT_TMDS : ctor = outps->tmds; break;
- case DCB_OUTPUT_LVDS : ctor = outps->lvds; break;
- case DCB_OUTPUT_DP : ctor = outps->dp ; break;
+ case DCB_OUTPUT_ANALOG:
+ case DCB_OUTPUT_TV:
+ case DCB_OUTPUT_TMDS:
+ case DCB_OUTPUT_LVDS:
+ ret = nvkm_outp_new(disp, i, &dcbE, &outp);
+ break;
+ case DCB_OUTPUT_DP:
+ ret = nvkm_dp_new(disp, i, &dcbE, &outp);
+ break;
default:
- nvkm_warn(&disp->engine.subdev,
- "dcb %d type %d unknown\n", i, dcbE.type);
+ nvkm_warn(subdev, "dcb %d type %d unknown\n",
+ i, dcbE.type);
continue;
}
- if (ctor)
- ret = ctor(disp, i, &dcbE, &outp);
- else
- ret = -ENODEV;
-
if (ret) {
- if (ret == -ENODEV) {
- nvkm_debug(&disp->engine.subdev,
- "dcb %d %d/%d not supported\n",
- i, dcbE.location, dcbE.type);
+ if (outp) {
+ if (ret != -ENODEV)
+ OUTP_ERR(outp, "ctor failed: %d", ret);
+ else
+ OUTP_DBG(outp, "not supported");
+ nvkm_outp_del(&outp);
continue;
}
- nvkm_error(&disp->engine.subdev,
- "failed to create output %d\n", i);
- nvkm_output_del(&outp);
+ nvkm_error(subdev, "failed to create outp %d\n", i);
continue;
}
@@ -358,18 +306,18 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
hpd = max(hpd, (u8)(dcbE.connector + 1));
}
- /* create connector objects based on the outputs we support */
+ /* Create connector objects based on available output paths. */
list_for_each_entry_safe(outp, outt, &disp->outp, head) {
- /* bios data *should* give us the most useful information */
+ /* VBIOS data *should* give us the most useful information. */
data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
&connE);
- /* no bios connector data... */
+ /* No bios connector data... */
if (!data) {
- /* heuristic: anything with the same ccb index is
+ /* Heuristic: anything with the same ccb index is
* considered to be on the same connector, any
* output path without an associated ccb entry will
- * be put on its own connector
+ * be put on its own connector.
*/
int ccb_index = outp->info.i2c_index;
if (ccb_index != 0xf) {
@@ -381,7 +329,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
}
}
- /* connector shared with another output path */
+ /* Connector shared with another output path. */
if (outp->conn)
continue;
@@ -392,7 +340,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
i = outp->info.connector;
}
- /* check that we haven't already created this connector */
+ /* Check that we haven't already created this connector. */
list_for_each_entry(conn, &disp->conn, head) {
if (conn->index == outp->info.connector) {
outp->conn = conn;
@@ -403,15 +351,15 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
if (outp->conn)
continue;
- /* apparently we need to create a new one! */
- ret = nvkm_connector_new(disp, i, &connE, &outp->conn);
+ /* 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 output %d conn: %d\n",
+ "failed to create outp %d conn: %d\n",
outp->index, ret);
- nvkm_connector_del(&outp->conn);
+ nvkm_conn_del(&outp->conn);
list_del(&outp->head);
- nvkm_output_del(&outp);
+ nvkm_outp_del(&outp);
continue;
}
@@ -422,18 +370,81 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
if (ret)
return ret;
- ret = nvkm_event_init(&nvkm_disp_vblank_func, 1, heads, &disp->vblank);
- if (ret)
- return ret;
+ i = 0;
+ list_for_each_entry(head, &disp->head, head)
+ i = max(i, head->id + 1);
- return 0;
+ return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank);
+}
+
+static void *
+nvkm_disp_dtor(struct nvkm_engine *engine)
+{
+ struct nvkm_disp *disp = nvkm_disp(engine);
+ struct nvkm_conn *conn;
+ struct nvkm_outp *outp;
+ void *data = disp;
+
+ if (disp->func->dtor)
+ data = disp->func->dtor(disp);
+
+ nvkm_event_fini(&disp->vblank);
+ nvkm_event_fini(&disp->hpd);
+
+ while (!list_empty(&disp->conn)) {
+ conn = list_first_entry(&disp->conn, typeof(*conn), head);
+ list_del(&conn->head);
+ nvkm_conn_del(&conn);
+ }
+
+ while (!list_empty(&disp->outp)) {
+ outp = list_first_entry(&disp->outp, typeof(*outp), head);
+ list_del(&outp->head);
+ nvkm_outp_del(&outp);
+ }
+
+ while (!list_empty(&disp->ior)) {
+ struct nvkm_ior *ior =
+ list_first_entry(&disp->ior, typeof(*ior), head);
+ nvkm_ior_del(&ior);
+ }
+
+ while (!list_empty(&disp->head)) {
+ struct nvkm_head *head =
+ list_first_entry(&disp->head, typeof(*head), head);
+ nvkm_head_del(&head);
+ }
+
+ return data;
+}
+
+static const struct nvkm_engine_func
+nvkm_disp = {
+ .dtor = nvkm_disp_dtor,
+ .oneinit = nvkm_disp_oneinit,
+ .init = nvkm_disp_init,
+ .fini = nvkm_disp_fini,
+ .intr = nvkm_disp_intr,
+ .base.sclass = nvkm_disp_class_get,
+};
+
+int
+nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
+ int index, struct nvkm_disp *disp)
+{
+ disp->func = func;
+ INIT_LIST_HEAD(&disp->head);
+ INIT_LIST_HEAD(&disp->ior);
+ INIT_LIST_HEAD(&disp->outp);
+ INIT_LIST_HEAD(&disp->conn);
+ return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
}
int
nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
- int index, int heads, struct nvkm_disp **pdisp)
+ int index, struct nvkm_disp **pdisp)
{
if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL)))
return -ENOMEM;
- return nvkm_disp_ctor(func, device, index, heads, *pdisp);
+ return nvkm_disp_ctor(func, device, index, *pdisp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c
index 83f152300ec0..f1d6b820d482 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
#include "dmacnv50.h"
+#include "head.h"
#include "rootnv50.h"
#include <core/client.h>
@@ -50,7 +51,7 @@ nv50_disp_base_new(const struct nv50_disp_dmac_func *func,
nvif_ioctl(parent, "create disp base channel dma vers %d "
"pushbuf %016llx head %d\n",
args->v0.version, args->v0.pushbuf, args->v0.head);
- if (args->v0.head > disp->base.head.nr)
+ if (!nvkm_head_find(&disp->base, args->v0.head))
return -EINVAL;
push = args->v0.pushbuf;
head = args->v0.head;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c
index 524a24eae1a0..0c0310498afd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c
@@ -25,6 +25,7 @@
#include "rootnv50.h"
#include <core/client.h>
+#include <core/notify.h>
#include <core/ramht.h>
#include <engine/dma.h>
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
index c6910d644a3d..febc5c274488 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c
@@ -30,9 +30,9 @@
#include <nvif/event.h>
static int
-nvkm_connector_hpd(struct nvkm_notify *notify)
+nvkm_conn_hpd(struct nvkm_notify *notify)
{
- struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
+ 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;
@@ -52,21 +52,21 @@ nvkm_connector_hpd(struct nvkm_notify *notify)
}
void
-nvkm_connector_fini(struct nvkm_connector *conn)
+nvkm_conn_fini(struct nvkm_conn *conn)
{
nvkm_notify_put(&conn->hpd);
}
void
-nvkm_connector_init(struct nvkm_connector *conn)
+nvkm_conn_init(struct nvkm_conn *conn)
{
nvkm_notify_get(&conn->hpd);
}
void
-nvkm_connector_del(struct nvkm_connector **pconn)
+nvkm_conn_del(struct nvkm_conn **pconn)
{
- struct nvkm_connector *conn = *pconn;
+ struct nvkm_conn *conn = *pconn;
if (conn) {
nvkm_notify_fini(&conn->hpd);
kfree(*pconn);
@@ -75,8 +75,8 @@ nvkm_connector_del(struct nvkm_connector **pconn)
}
static void
-nvkm_connector_ctor(struct nvkm_disp *disp, int index,
- struct nvbios_connE *info, struct nvkm_connector *conn)
+nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
+ struct nvkm_conn *conn)
{
static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
@@ -105,7 +105,7 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index,
return;
}
- ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
+ ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd,
true, &(struct nvkm_gpio_ntfy_req) {
.mask = NVKM_GPIO_TOGGLED,
.line = func.line,
@@ -122,11 +122,11 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index,
}
int
-nvkm_connector_new(struct nvkm_disp *disp, int index,
- struct nvbios_connE *info, struct nvkm_connector **pconn)
+nvkm_conn_new(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
+ struct nvkm_conn **pconn)
{
if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL)))
return -ENOMEM;
- nvkm_connector_ctor(disp, index, info, *pconn);
+ nvkm_conn_ctor(disp, index, info, *pconn);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
index ed32fe7f1864..de962b7b026d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h
@@ -6,7 +6,7 @@
#include <subdev/bios.h>
#include <subdev/bios/conn.h>
-struct nvkm_connector {
+struct nvkm_conn {
struct nvkm_disp *disp;
int index;
struct nvbios_connE info;
@@ -16,14 +16,14 @@ struct nvkm_connector {
struct list_head head;
};
-int nvkm_connector_new(struct nvkm_disp *, int index, struct nvbios_connE *,
- struct nvkm_connector **);
-void nvkm_connector_del(struct nvkm_connector **);
-void nvkm_connector_init(struct nvkm_connector *);
-void nvkm_connector_fini(struct nvkm_connector *);
+int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *,
+ struct nvkm_conn **);
+void nvkm_conn_del(struct nvkm_conn **);
+void nvkm_conn_init(struct nvkm_conn *);
+void nvkm_conn_fini(struct nvkm_conn *);
#define CONN_MSG(c,l,f,a...) do { \
- struct nvkm_connector *_conn = (c); \
+ struct nvkm_conn *_conn = (c); \
nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \
_conn->index, _conn->info.location, _conn->info.type, ##a); \
} while(0)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c
index 82ff82d8c1ab..ab51121b7982 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
#include "channv50.h"
+#include "head.h"
#include "rootnv50.h"
#include <core/client.h>
@@ -48,7 +49,7 @@ nv50_disp_curs_new(const struct nv50_disp_chan_func *func,
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(parent, "create disp cursor vers %d head %d\n",
args->v0.version, args->v0.head);
- if (args->v0.head > disp->base.head.nr)
+ if (!nvkm_head_find(&disp->base, args->v0.head))
return -EINVAL;
head = args->v0.head;
} else
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c
new file mode 100644
index 000000000000..dbd032ef352a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+static void
+gf119_dac_clock(struct nvkm_ior *dac)
+{
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 doff = nv50_ior_base(dac);
+ nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000);
+}
+
+static void
+gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
+{
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20;
+ u32 ctrl = nvkm_rd32(device, 0x640180 + coff);
+
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ switch (state->proto_evo) {
+ case 0: state->proto = CRT; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
+
+ state->head = ctrl & 0x0000000f;
+}
+
+static const struct nvkm_ior_func
+gf119_dac = {
+ .state = gf119_dac_state,
+ .power = nv50_dac_power,
+ .sense = nv50_dac_sense,
+ .clock = gf119_dac_clock,
+};
+
+int
+gf119_dac_new(struct nvkm_disp *disp, int id)
+{
+ struct nvkm_device *device = disp->engine.subdev.device;
+ if (!(nvkm_rd32(device, 0x612004) & (0x00000010 << id)))
+ return 0;
+ return nvkm_ior_new_(&gf119_dac, disp, DAC, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c
index c9b78b8f9c87..85e692b12260 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c
@@ -21,106 +21,96 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outp.h"
+#include "ior.h"
-#include <core/client.h>
#include <subdev/timer.h>
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-nv50_dac_power(NV50_DISP_MTHD_V1)
+static void
+nv50_dac_clock(struct nvkm_ior *dac)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 doff = outp->or * 0x800;
- union {
- struct nv50_disp_dac_pwr_v0 v0;
- } *args = data;
- u32 stat;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp dac pwr size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp dac pwr vers %d state %d data %d "
- "vsync %d hsync %d\n",
- args->v0.version, args->v0.state, args->v0.data,
- args->v0.vsync, args->v0.hsync);
- stat = 0x00000040 * !args->v0.state;
- stat |= 0x00000010 * !args->v0.data;
- stat |= 0x00000004 * !args->v0.vsync;
- stat |= 0x00000001 * !args->v0.hsync;
- } else
- return ret;
-
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
- break;
- );
- nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
- break;
- );
- return 0;
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 doff = nv50_ior_base(dac);
+ nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);
}
int
-nv50_dac_sense(NV50_DISP_MTHD_V1)
+nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)
{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- union {
- struct nv50_disp_dac_load_v0 v0;
- } *args = data;
- const u32 doff = outp->or * 0x800;
- u32 loadval;
- int ret = -ENOSYS;
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 doff = nv50_ior_base(dac);
- nvif_ioctl(object, "disp dac load size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp dac load vers %d data %08x\n",
- args->v0.version, args->v0.data);
- if (args->v0.data & 0xfff00000)
- return -EINVAL;
- loadval = args->v0.data;
- } else
- return ret;
-
- nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80150000);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
- break;
- );
+ dac->func->power(dac, false, true, false, false, false);
nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval);
mdelay(9);
udelay(500);
loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000);
- nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80550000);
+ dac->func->power(dac, false, false, false, false, false);
+ if (!(loadval & 0x80000000))
+ return -ETIMEDOUT;
+
+ return (loadval & 0x38000000) >> 27;
+}
+
+static void
+nv50_dac_power_wait(struct nvkm_device *device, const u32 doff)
+{
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
break;
);
+}
- nvkm_debug(subdev, "DAC%d sense: %08x\n", outp->or, loadval);
- if (!(loadval & 0x80000000))
- return -ETIMEDOUT;
+void
+nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu,
+ bool data, bool vsync, bool hsync)
+{
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 doff = nv50_ior_base(dac);
+ const u32 shift = normal ? 0 : 16;
+ const u32 state = 0x80000000 | (0x00000040 * ! pu |
+ 0x00000010 * ! data |
+ 0x00000004 * ! vsync |
+ 0x00000001 * ! hsync) << shift;
+ const u32 field = 0xc0000000 | (0x00000055 << shift);
+
+ nv50_dac_power_wait(device, doff);
+ nvkm_mask(device, 0x61a004 + doff, field, state);
+ nv50_dac_power_wait(device, doff);
+}
+
+static void
+nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
+{
+ struct nvkm_device *device = dac->disp->engine.subdev.device;
+ const u32 coff = dac->id * 8 + (state == &dac->arm) * 4;
+ u32 ctrl = nvkm_rd32(device, 0x610b58 + coff);
+
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ switch (state->proto_evo) {
+ case 0: state->proto = CRT; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
- args->v0.load = (loadval & 0x38000000) >> 27;
- return 0;
+ state->head = ctrl & 0x00000003;
}
-static const struct nvkm_output_func
-nv50_dac_output_func = {
+static const struct nvkm_ior_func
+nv50_dac = {
+ .state = nv50_dac_state,
+ .power = nv50_dac_power,
+ .sense = nv50_dac_sense,
+ .clock = nv50_dac_clock,
};
int
-nv50_dac_output_new(struct nvkm_disp *disp, int index,
- struct dcb_output *dcbE, struct nvkm_output **poutp)
+nv50_dac_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_new_(&nv50_dac_output_func, disp,
- index, dcbE, poutp);
+ struct nvkm_device *device = disp->engine.subdev.device;
+ if (!(nvkm_rd32(device, 0x610184) & (0x00100000 << id)))
+ return 0;
+ return nvkm_ior_new_(&nv50_dac, disp, DAC, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
new file mode 100644
index 000000000000..7c5bed29ffef
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright 2014 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 "dp.h"
+#include "conn.h"
+#include "head.h"
+#include "ior.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/i2c.h>
+
+#include <nvif/event.h>
+
+struct lt_state {
+ struct nvkm_dp *dp;
+ u8 stat[6];
+ u8 conf[4];
+ bool pc2;
+ u8 pc2stat;
+ u8 pc2conf[2];
+};
+
+static int
+nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay)
+{
+ struct nvkm_dp *dp = lt->dp;
+ int ret;
+
+ if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
+ mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
+ else
+ udelay(delay);
+
+ ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6);
+ if (ret)
+ return ret;
+
+ if (pc) {
+ ret = nvkm_rdaux(dp->aux, DPCD_LS0C, &lt->pc2stat, 1);
+ if (ret)
+ lt->pc2stat = 0x00;
+ OUTP_TRACE(&dp->outp, "status %6ph pc2 %02x",
+ lt->stat, lt->pc2stat);
+ } else {
+ OUTP_TRACE(&dp->outp, "status %6ph", lt->stat);
+ }
+
+ return 0;
+}
+
+static int
+nvkm_dp_train_drive(struct lt_state *lt, bool pc)
+{
+ struct nvkm_dp *dp = lt->dp;
+ struct nvkm_ior *ior = dp->outp.ior;
+ struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios;
+ struct nvbios_dpout info;
+ struct nvbios_dpcfg ocfg;
+ u8 ver, hdr, cnt, len;
+ u32 data;
+ int ret, i;
+
+ for (i = 0; i < ior->dp.nr; i++) {
+ u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+ u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3;
+ u8 lpre = (lane & 0x0c) >> 2;
+ u8 lvsw = (lane & 0x03) >> 0;
+ u8 hivs = 3 - lpre;
+ u8 hipe = 3;
+ u8 hipc = 3;
+
+ if (lpc2 >= hipc)
+ lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
+ if (lpre >= hipe) {
+ lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
+ lvsw = hivs = 3 - (lpre & 3);
+ } else
+ if (lvsw >= hivs) {
+ lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
+ }
+
+ lt->conf[i] = (lpre << 3) | lvsw;
+ lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
+
+ OUTP_TRACE(&dp->outp, "config lane %d %02x %02x",
+ i, lt->conf[i], lpc2);
+
+ data = nvbios_dpout_match(bios, dp->outp.info.hasht,
+ dp->outp.info.hashm,
+ &ver, &hdr, &cnt, &len, &info);
+ if (!data)
+ continue;
+
+ data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3,
+ lpre & 3, &ver, &hdr, &cnt, &len,
+ &ocfg);
+ if (!data)
+ continue;
+
+ ior->func->dp.drive(ior, i, ocfg.pc, ocfg.dc,
+ ocfg.pe, ocfg.tx_pu);
+ }
+
+ ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4);
+ if (ret)
+ return ret;
+
+ if (pc) {
+ ret = nvkm_wraux(dp->aux, DPCD_LC0F, lt->pc2conf, 2);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern)
+{
+ struct nvkm_dp *dp = lt->dp;
+ u8 sink_tp;
+
+ OUTP_TRACE(&dp->outp, "training pattern %d", pattern);
+ dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern);
+
+ nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+ sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
+ sink_tp |= pattern;
+ nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+}
+
+static int
+nvkm_dp_train_eq(struct lt_state *lt)
+{
+ bool eq_done = false, cr_done = true;
+ int tries = 0, i;
+
+ if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED)
+ nvkm_dp_train_pattern(lt, 3);
+ else
+ nvkm_dp_train_pattern(lt, 2);
+
+ do {
+ if ((tries &&
+ nvkm_dp_train_drive(lt, lt->pc2)) ||
+ nvkm_dp_train_sense(lt, lt->pc2, 400))
+ break;
+
+ eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
+ for (i = 0; i < lt->dp->outp.ior->dp.nr && eq_done; i++) {
+ u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+ if (!(lane & DPCD_LS02_LANE0_CR_DONE))
+ cr_done = false;
+ if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+ !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
+ eq_done = false;
+ }
+ } while (!eq_done && cr_done && ++tries <= 5);
+
+ return eq_done ? 0 : -1;
+}
+
+static int
+nvkm_dp_train_cr(struct lt_state *lt)
+{
+ bool cr_done = false, abort = false;
+ int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+ int tries = 0, i;
+
+ nvkm_dp_train_pattern(lt, 1);
+
+ do {
+ if (nvkm_dp_train_drive(lt, false) ||
+ nvkm_dp_train_sense(lt, false, 100))
+ break;
+
+ cr_done = true;
+ for (i = 0; i < lt->dp->outp.ior->dp.nr; i++) {
+ u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+ if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
+ cr_done = false;
+ if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
+ abort = true;
+ break;
+ }
+ }
+
+ if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
+ voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+ tries = 0;
+ }
+ } while (!cr_done && !abort && ++tries < 5);
+
+ return cr_done ? 0 : -1;
+}
+
+static int
+nvkm_dp_train_links(struct nvkm_dp *dp)
+{
+ struct nvkm_ior *ior = dp->outp.ior;
+ struct nvkm_disp *disp = dp->outp.disp;
+ struct nvkm_subdev *subdev = &disp->engine.subdev;
+ struct nvkm_bios *bios = subdev->device->bios;
+ struct lt_state lt = {
+ .dp = dp,
+ };
+ u32 lnkcmp;
+ u8 sink[2];
+ int ret;
+
+ OUTP_DBG(&dp->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 < 0xd0)
+ dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
+ lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED;
+
+ /* Set desired link configuration on the source. */
+ if ((lnkcmp = lt.dp->info.lnkcmp)) {
+ if (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 = &dp->outp.info;
+ init.or = ior->id;
+ init.link = ior->asy.link;
+ );
+ }
+
+ ret = ior->func->dp.links(ior, dp->aux);
+ if (ret) {
+ if (ret < 0) {
+ OUTP_ERR(&dp->outp, "train failed with %d", ret);
+ return ret;
+ }
+ return 0;
+ }
+
+ ior->func->dp.power(ior, ior->dp.nr);
+
+ /* Set desired link configuration on the sink. */
+ sink[0] = ior->dp.bw;
+ sink[1] = ior->dp.nr;
+ if (ior->dp.ef)
+ sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+ ret = nvkm_wraux(dp->aux, DPCD_LC00_LINK_BW_SET, sink, 2);
+ if (ret)
+ return ret;
+
+ /* Attempt to train the link in this configuration. */
+ memset(lt.stat, 0x00, sizeof(lt.stat));
+ ret = nvkm_dp_train_cr(&lt);
+ if (ret == 0)
+ ret = nvkm_dp_train_eq(&lt);
+ nvkm_dp_train_pattern(&lt, 0);
+ return ret;
+}
+
+static void
+nvkm_dp_train_fini(struct nvkm_dp *dp)
+{
+ /* Execute AfterLinkTraining script from DP Info table. */
+ nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[1],
+ init.outp = &dp->outp.info;
+ init.or = dp->outp.ior->id;
+ init.link = dp->outp.ior->asy.link;
+ );
+}
+
+static void
+nvkm_dp_train_init(struct nvkm_dp *dp)
+{
+ /* Execute EnableSpread/DisableSpread script from DP Info table. */
+ if (dp->dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) {
+ nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[2],
+ init.outp = &dp->outp.info;
+ init.or = dp->outp.ior->id;
+ init.link = dp->outp.ior->asy.link;
+ );
+ } else {
+ nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[3],
+ init.outp = &dp->outp.info;
+ init.or = dp->outp.ior->id;
+ init.link = dp->outp.ior->asy.link;
+ );
+ }
+
+ /* Execute BeforeLinkTraining script from DP Info table. */
+ nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0],
+ init.outp = &dp->outp.info;
+ init.or = dp->outp.ior->id;
+ init.link = dp->outp.ior->asy.link;
+ );
+}
+
+static const struct dp_rates {
+ u32 rate;
+ u8 bw;
+ u8 nr;
+} nvkm_dp_rates[] = {
+ { 2160000, 0x14, 4 },
+ { 1080000, 0x0a, 4 },
+ { 1080000, 0x14, 2 },
+ { 648000, 0x06, 4 },
+ { 540000, 0x0a, 2 },
+ { 540000, 0x14, 1 },
+ { 324000, 0x06, 2 },
+ { 270000, 0x0a, 1 },
+ { 162000, 0x06, 1 },
+ {}
+};
+
+static int
+nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
+{
+ struct nvkm_ior *ior = dp->outp.ior;
+ const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
+ const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
+ const u8 outp_nr = dp->outp.info.dpconf.link_nr;
+ const u8 outp_bw = dp->outp.info.dpconf.link_bw;
+ const struct dp_rates *failsafe = NULL, *cfg;
+ int ret = -EINVAL;
+ u8 pwr;
+
+ /* Find the lowest configuration of the OR that can support
+ * the required link rate.
+ *
+ * We will refuse to program the OR to lower rates, even if
+ * link training fails at higher rates (or even if the sink
+ * can't support the rate at all, though the DD is supposed
+ * to prevent such situations from happening).
+ *
+ * Attempting to do so can cause the entire display to hang,
+ * and it's better to have a failed modeset than that.
+ */
+ for (cfg = nvkm_dp_rates; cfg->rate; cfg++) {
+ if (cfg->nr <= outp_nr && cfg->nr <= outp_bw)
+ failsafe = cfg;
+ if (failsafe && cfg[1].rate < dataKBps)
+ break;
+ }
+
+ if (WARN_ON(!failsafe))
+ return ret;
+
+ /* Ensure sink is not in a low-power state. */
+ if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) {
+ if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
+ pwr &= ~DPCD_SC00_SET_POWER;
+ pwr |= DPCD_SC00_SET_POWER_D0;
+ nvkm_wraux(dp->aux, DPCD_SC00, &pwr, 1);
+ }
+ }
+
+ /* Link training. */
+ OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)",
+ failsafe->nr, failsafe->bw * 27);
+ nvkm_dp_train_init(dp);
+ for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) {
+ /* Skip configurations not supported by both OR and sink. */
+ if ((cfg->nr > outp_nr || cfg->bw > outp_bw ||
+ cfg->nr > sink_nr || cfg->bw > sink_bw)) {
+ if (cfg != failsafe)
+ continue;
+ OUTP_ERR(&dp->outp, "link rate unsupported by sink");
+ }
+ ior->dp.mst = dp->lt.mst;
+ ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP;
+ ior->dp.bw = cfg->bw;
+ ior->dp.nr = cfg->nr;
+
+ /* Program selected link configuration. */
+ ret = nvkm_dp_train_links(dp);
+ }
+ nvkm_dp_train_fini(dp);
+ if (ret < 0)
+ OUTP_ERR(&dp->outp, "training failed");
+ else
+ OUTP_DBG(&dp->outp, "training done");
+ atomic_set(&dp->lt.done, 1);
+ return ret;
+}
+
+static void
+nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
+{
+ struct nvkm_dp *dp = nvkm_dp(outp);
+
+ /* Prevent link from being retrained if sink sends an IRQ. */
+ atomic_set(&dp->lt.done, 0);
+ ior->dp.nr = 0;
+
+ /* Execute DisableLT script from DP Info Table. */
+ nvbios_init(&ior->disp->engine.subdev, dp->info.script[4],
+ init.outp = &dp->outp.info;
+ init.or = ior->id;
+ init.link = ior->arm.link;
+ );
+}
+
+static int
+nvkm_dp_acquire(struct nvkm_outp *outp)
+{
+ struct nvkm_dp *dp = nvkm_dp(outp);
+ struct nvkm_ior *ior = dp->outp.ior;
+ struct nvkm_head *head;
+ bool retrain = true;
+ u32 datakbps = 0;
+ u32 dataKBps;
+ u32 linkKBps;
+ u8 stat[3];
+ int ret, i;
+
+ mutex_lock(&dp->mutex);
+
+ /* Check that link configuration meets current requirements. */
+ list_for_each_entry(head, &outp->disp->head, head) {
+ if (ior->asy.head & (1 << head->id)) {
+ u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000;
+ datakbps += khz * head->asy.or.depth;
+ }
+ }
+
+ linkKBps = ior->dp.bw * 27000 * ior->dp.nr;
+ dataKBps = DIV_ROUND_UP(datakbps, 8);
+ OUTP_DBG(&dp->outp, "data %d KB/s link %d KB/s mst %d->%d",
+ dataKBps, linkKBps, ior->dp.mst, dp->lt.mst);
+ if (linkKBps < dataKBps || ior->dp.mst != dp->lt.mst) {
+ OUTP_DBG(&dp->outp, "link requirements changed");
+ goto done;
+ }
+
+ /* Check that link is still trained. */
+ ret = nvkm_rdaux(dp->aux, DPCD_LS02, stat, 3);
+ if (ret) {
+ OUTP_DBG(&dp->outp,
+ "failed to read link status, assuming no sink");
+ goto done;
+ }
+
+ if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
+ for (i = 0; i < ior->dp.nr; i++) {
+ u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
+ if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
+ !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+ !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
+ OUTP_DBG(&dp->outp,
+ "lane %d not equalised", lane);
+ goto done;
+ }
+ }
+ retrain = false;
+ } else {
+ OUTP_DBG(&dp->outp, "no inter-lane alignment");
+ }
+
+done:
+ if (retrain || !atomic_read(&dp->lt.done))
+ ret = nvkm_dp_train(dp, dataKBps);
+ mutex_unlock(&dp->mutex);
+ return ret;
+}
+
+static void
+nvkm_dp_enable(struct nvkm_dp *dp, bool enable)
+{
+ struct nvkm_i2c_aux *aux = dp->aux;
+
+ if (enable) {
+ if (!dp->present) {
+ OUTP_DBG(&dp->outp, "aux power -> always");
+ nvkm_i2c_aux_monitor(aux, true);
+ dp->present = true;
+ }
+
+ if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd,
+ sizeof(dp->dpcd)))
+ return;
+ }
+
+ if (dp->present) {
+ OUTP_DBG(&dp->outp, "aux power -> demand");
+ nvkm_i2c_aux_monitor(aux, false);
+ dp->present = false;
+ }
+
+ atomic_set(&dp->lt.done, 0);
+}
+
+static int
+nvkm_dp_hpd(struct nvkm_notify *notify)
+{
+ const struct nvkm_i2c_ntfy_rep *line = notify->data;
+ struct nvkm_dp *dp = container_of(notify, typeof(*dp), hpd);
+ struct nvkm_conn *conn = dp->outp.conn;
+ struct nvkm_disp *disp = dp->outp.disp;
+ struct nvif_notify_conn_rep_v0 rep = {};
+
+ OUTP_DBG(&dp->outp, "HPD: %d", line->mask);
+ if (line->mask & NVKM_I2C_IRQ) {
+ if (atomic_read(&dp->lt.done))
+ dp->outp.func->acquire(&dp->outp);
+ rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
+ } else {
+ nvkm_dp_enable(dp, true);
+ }
+
+ 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)
+{
+ struct nvkm_dp *dp = nvkm_dp(outp);
+ nvkm_notify_put(&dp->hpd);
+ nvkm_dp_enable(dp, false);
+}
+
+static void
+nvkm_dp_init(struct nvkm_outp *outp)
+{
+ struct nvkm_dp *dp = nvkm_dp(outp);
+ nvkm_notify_put(&dp->outp.conn->hpd);
+ nvkm_dp_enable(dp, true);
+ nvkm_notify_get(&dp->hpd);
+}
+
+static void *
+nvkm_dp_dtor(struct nvkm_outp *outp)
+{
+ struct nvkm_dp *dp = nvkm_dp(outp);
+ nvkm_notify_fini(&dp->hpd);
+ return dp;
+}
+
+static const struct nvkm_outp_func
+nvkm_dp_func = {
+ .dtor = nvkm_dp_dtor,
+ .init = nvkm_dp_init,
+ .fini = nvkm_dp_fini,
+ .acquire = nvkm_dp_acquire,
+ .release = nvkm_dp_release,
+};
+
+static int
+nvkm_dp_ctor(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
+ struct nvkm_i2c_aux *aux, struct nvkm_dp *dp)
+{
+ struct nvkm_device *device = disp->engine.subdev.device;
+ struct nvkm_bios *bios = device->bios;
+ struct nvkm_i2c *i2c = device->i2c;
+ u8 hdr, cnt, len;
+ u32 data;
+ int ret;
+
+ ret = nvkm_outp_ctor(&nvkm_dp_func, disp, index, dcbE, &dp->outp);
+ if (ret)
+ return ret;
+
+ dp->aux = aux;
+ if (!dp->aux) {
+ OUTP_ERR(&dp->outp, "no aux");
+ return -EINVAL;
+ }
+
+ /* bios data is not optional */
+ data = nvbios_dpout_match(bios, dp->outp.info.hasht,
+ dp->outp.info.hashm, &dp->version,
+ &hdr, &cnt, &len, &dp->info);
+ if (!data) {
+ OUTP_ERR(&dp->outp, "no bios dp data");
+ return -EINVAL;
+ }
+
+ OUTP_DBG(&dp->outp, "bios dp %02x %02x %02x %02x",
+ 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 = dp->aux->id,
+ },
+ sizeof(struct nvkm_i2c_ntfy_req),
+ sizeof(struct nvkm_i2c_ntfy_rep),
+ &dp->hpd);
+ if (ret) {
+ OUTP_ERR(&dp->outp, "error monitoring aux hpd: %d", ret);
+ return ret;
+ }
+
+ mutex_init(&dp->mutex);
+ atomic_set(&dp->lt.done, 0);
+ return 0;
+}
+
+int
+nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
+ struct nvkm_outp **poutp)
+{
+ struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
+ struct nvkm_i2c_aux *aux;
+ struct nvkm_dp *dp;
+
+ if (dcbE->location == 0)
+ aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index));
+ else
+ aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev));
+
+ if (!(dp = kzalloc(sizeof(*dp), GFP_KERNEL)))
+ return -ENOMEM;
+ *poutp = &dp->outp;
+
+ return nvkm_dp_ctor(disp, index, dcbE, aux, dp);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
index baf1dd9ff975..59173c290525 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
@@ -1,6 +1,36 @@
-#ifndef __NVKM_DISP_DPORT_H__
-#define __NVKM_DISP_DPORT_H__
-struct nvkm_output_dp;
+#ifndef __NVKM_DISP_DP_H__
+#define __NVKM_DISP_DP_H__
+#define nvkm_dp(p) container_of((p), struct nvkm_dp, outp)
+#include "outp.h"
+
+#include <core/notify.h>
+#include <subdev/bios.h>
+#include <subdev/bios/dp.h>
+
+struct nvkm_dp {
+ union {
+ struct nvkm_outp base;
+ struct nvkm_outp outp;
+ };
+
+ struct nvbios_dpout info;
+ u8 version;
+
+ struct nvkm_i2c_aux *aux;
+
+ struct nvkm_notify hpd;
+ bool present;
+ u8 dpcd[16];
+
+ struct mutex mutex;
+ struct {
+ atomic_t done;
+ bool mst;
+ } lt;
+};
+
+int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
+ struct nvkm_outp **);
/* DPCD Receiver Capabilities */
#define DPCD_RC00_DPCD_REV 0x00000
@@ -76,6 +106,4 @@ struct nvkm_output_dp;
#define DPCD_SC00_SET_POWER 0x03
#define DPCD_SC00_SET_POWER_D0 0x01
#define DPCD_SC00_SET_POWER_D3 0x03
-
-void nvkm_dp_train(struct nvkm_output_dp *);
#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c
deleted file mode 100644
index 4a93ceb850ac..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright 2013 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 "dport.h"
-#include "outpdp.h"
-#include "nv50.h"
-
-#include <subdev/bios.h>
-#include <subdev/bios/init.h>
-#include <subdev/i2c.h>
-
-#include <nvif/class.h>
-
-/******************************************************************************
- * link training
- *****************************************************************************/
-struct dp_state {
- struct nvkm_output_dp *outp;
- int link_nr;
- u32 link_bw;
- u8 stat[6];
- u8 conf[4];
- bool pc2;
- u8 pc2stat;
- u8 pc2conf[2];
-};
-
-static int
-dp_set_link_config(struct dp_state *dp)
-{
- struct nvkm_output_dp *outp = dp->outp;
- struct nvkm_disp *disp = outp->base.disp;
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvkm_bios *bios = subdev->device->bios;
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = bios,
- .offset = 0x0000,
- .outp = &outp->base.info,
- .crtc = -1,
- .execute = 1,
- };
- u32 lnkcmp;
- u8 sink[2];
- int ret;
-
- OUTP_DBG(&outp->base, "%d lanes at %d KB/s", dp->link_nr, dp->link_bw);
-
- /* set desired link configuration on the source */
- if ((lnkcmp = dp->outp->info.lnkcmp)) {
- if (outp->version < 0x30) {
- while ((dp->link_bw / 10) < nvbios_rd16(bios, lnkcmp))
- lnkcmp += 4;
- init.offset = nvbios_rd16(bios, lnkcmp + 2);
- } else {
- while ((dp->link_bw / 27000) < nvbios_rd08(bios, lnkcmp))
- lnkcmp += 3;
- init.offset = nvbios_rd16(bios, lnkcmp + 1);
- }
-
- nvbios_exec(&init);
- }
-
- ret = outp->func->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
- outp->dpcd[DPCD_RC02] &
- DPCD_RC02_ENHANCED_FRAME_CAP);
- if (ret) {
- if (ret < 0)
- OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret);
- return ret;
- }
-
- outp->func->lnk_pwr(outp, dp->link_nr);
-
- /* set desired link configuration on the sink */
- sink[0] = dp->link_bw / 27000;
- sink[1] = dp->link_nr;
- if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
- sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
-
- return nvkm_wraux(outp->aux, DPCD_LC00_LINK_BW_SET, sink, 2);
-}
-
-static void
-dp_set_training_pattern(struct dp_state *dp, u8 pattern)
-{
- struct nvkm_output_dp *outp = dp->outp;
- u8 sink_tp;
-
- OUTP_DBG(&outp->base, "training pattern %d", pattern);
- outp->func->pattern(outp, pattern);
-
- nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1);
- sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
- sink_tp |= pattern;
- nvkm_wraux(outp->aux, DPCD_LC02, &sink_tp, 1);
-}
-
-static int
-dp_link_train_commit(struct dp_state *dp, bool pc)
-{
- struct nvkm_output_dp *outp = dp->outp;
- int ret, i;
-
- for (i = 0; i < dp->link_nr; i++) {
- u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
- u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3;
- u8 lpre = (lane & 0x0c) >> 2;
- u8 lvsw = (lane & 0x03) >> 0;
- u8 hivs = 3 - lpre;
- u8 hipe = 3;
- u8 hipc = 3;
-
- if (lpc2 >= hipc)
- lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
- if (lpre >= hipe) {
- lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
- lvsw = hivs = 3 - (lpre & 3);
- } else
- if (lvsw >= hivs) {
- lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
- }
-
- dp->conf[i] = (lpre << 3) | lvsw;
- dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
-
- OUTP_DBG(&outp->base, "config lane %d %02x %02x",
- i, dp->conf[i], lpc2);
- outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
- }
-
- ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4);
- if (ret)
- return ret;
-
- if (pc) {
- ret = nvkm_wraux(outp->aux, DPCD_LC0F, dp->pc2conf, 2);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int
-dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
-{
- struct nvkm_output_dp *outp = dp->outp;
- int ret;
-
- if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
- mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
- else
- udelay(delay);
-
- ret = nvkm_rdaux(outp->aux, DPCD_LS02, dp->stat, 6);
- if (ret)
- return ret;
-
- if (pc) {
- ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1);
- if (ret)
- dp->pc2stat = 0x00;
- OUTP_DBG(&outp->base, "status %6ph pc2 %02x",
- dp->stat, dp->pc2stat);
- } else {
- OUTP_DBG(&outp->base, "status %6ph", dp->stat);
- }
-
- return 0;
-}
-
-static int
-dp_link_train_cr(struct dp_state *dp)
-{
- bool cr_done = false, abort = false;
- int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
- int tries = 0, i;
-
- dp_set_training_pattern(dp, 1);
-
- do {
- if (dp_link_train_commit(dp, false) ||
- dp_link_train_update(dp, false, 100))
- break;
-
- cr_done = true;
- for (i = 0; i < dp->link_nr; i++) {
- u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
- if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
- cr_done = false;
- if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
- abort = true;
- break;
- }
- }
-
- if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
- voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
- tries = 0;
- }
- } while (!cr_done && !abort && ++tries < 5);
-
- return cr_done ? 0 : -1;
-}
-
-static int
-dp_link_train_eq(struct dp_state *dp)
-{
- struct nvkm_output_dp *outp = dp->outp;
- bool eq_done = false, cr_done = true;
- int tries = 0, i;
-
- if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
- dp_set_training_pattern(dp, 3);
- else
- dp_set_training_pattern(dp, 2);
-
- do {
- if ((tries &&
- dp_link_train_commit(dp, dp->pc2)) ||
- dp_link_train_update(dp, dp->pc2, 400))
- break;
-
- eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
- for (i = 0; i < dp->link_nr && eq_done; i++) {
- u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
- if (!(lane & DPCD_LS02_LANE0_CR_DONE))
- cr_done = false;
- if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
- !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
- eq_done = false;
- }
- } while (!eq_done && cr_done && ++tries <= 5);
-
- return eq_done ? 0 : -1;
-}
-
-static void
-dp_link_train_init(struct dp_state *dp, bool spread)
-{
- struct nvkm_output_dp *outp = dp->outp;
- struct nvkm_disp *disp = outp->base.disp;
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = subdev->device->bios,
- .outp = &outp->base.info,
- .crtc = -1,
- .execute = 1,
- };
-
- /* set desired spread */
- if (spread)
- init.offset = outp->info.script[2];
- else
- init.offset = outp->info.script[3];
- nvbios_exec(&init);
-
- /* pre-train script */
- init.offset = outp->info.script[0];
- nvbios_exec(&init);
-}
-
-static void
-dp_link_train_fini(struct dp_state *dp)
-{
- struct nvkm_output_dp *outp = dp->outp;
- struct nvkm_disp *disp = outp->base.disp;
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = subdev->device->bios,
- .outp = &outp->base.info,
- .crtc = -1,
- .execute = 1,
- };
-
- /* post-train script */
- init.offset = outp->info.script[1],
- nvbios_exec(&init);
-}
-
-static const struct dp_rates {
- u32 rate;
- u8 bw;
- u8 nr;
-} nvkm_dp_rates[] = {
- { 2160000, 0x14, 4 },
- { 1080000, 0x0a, 4 },
- { 1080000, 0x14, 2 },
- { 648000, 0x06, 4 },
- { 540000, 0x0a, 2 },
- { 540000, 0x14, 1 },
- { 324000, 0x06, 2 },
- { 270000, 0x0a, 1 },
- { 162000, 0x06, 1 },
- {}
-};
-
-void
-nvkm_dp_train(struct nvkm_output_dp *outp)
-{
- struct nv50_disp *disp = nv50_disp(outp->base.disp);
- const struct dp_rates *cfg = nvkm_dp_rates;
- struct dp_state _dp = {
- .outp = outp,
- }, *dp = &_dp;
- u32 datarate = 0;
- u8 pwr;
- int ret;
-
- if (!outp->base.info.location && disp->func->sor.magic)
- disp->func->sor.magic(&outp->base);
-
- /* bring capabilities within encoder limits */
- if (disp->base.engine.subdev.device->chipset < 0xd0)
- outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
- if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
- outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
- outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
- }
- if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
- outp->dpcd[1] = outp->base.info.dpconf.link_bw;
- dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
-
- /* restrict link config to the lowest required rate, if requested */
- if (datarate) {
- datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */
- while (cfg[1].rate >= datarate)
- cfg++;
- }
- cfg--;
-
- /* ensure sink is not in a low-power state */
- if (!nvkm_rdaux(outp->aux, DPCD_SC00, &pwr, 1)) {
- if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
- pwr &= ~DPCD_SC00_SET_POWER;
- pwr |= DPCD_SC00_SET_POWER_D0;
- nvkm_wraux(outp->aux, DPCD_SC00, &pwr, 1);
- }
- }
-
- /* enable down-spreading and execute pre-train script from vbios */
- dp_link_train_init(dp, outp->dpcd[3] & 0x01);
-
- while (ret = -EIO, (++cfg)->rate) {
- /* select next configuration supported by encoder and sink */
- while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
- cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
- cfg++;
- dp->link_bw = cfg->bw * 27000;
- dp->link_nr = cfg->nr;
-
- /* program selected link configuration */
- ret = dp_set_link_config(dp);
- if (ret == 0) {
- /* attempt to train the link at this configuration */
- memset(dp->stat, 0x00, sizeof(dp->stat));
- if (!dp_link_train_cr(dp) &&
- !dp_link_train_eq(dp))
- break;
- } else
- if (ret) {
- /* dp_set_link_config() handled training, or
- * we failed to communicate with the sink.
- */
- break;
- }
- }
-
- /* finish link training and execute post-train script from vbios */
- dp_set_training_pattern(dp, 0);
- if (ret < 0)
- OUTP_ERR(&outp->base, "link training failed");
-
- dp_link_train_fini(dp);
-
- OUTP_DBG(&outp->base, "training complete");
- atomic_set(&outp->lt.done, 1);
-}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
index 3e3e592cd09f..842e1b72ee42 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c
@@ -22,30 +22,20 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
g84_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
- .super = nv50_disp_intr_supervisor,
+ .super = nv50_disp_super,
.root = &g84_disp_root_oclass,
- .head.vblank_init = nv50_disp_vblank_init,
- .head.vblank_fini = nv50_disp_vblank_fini,
- .head.scanoutpos = nv50_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.external.tmds = nv50_pior_output_new,
- .outp.external.dp = nv50_pior_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 2,
- .sor.power = nv50_sor_power,
- .sor.hdmi = g84_hdmi_ctrl,
- .pior.nr = 3,
- .pior.power = nv50_pior_power,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 2, .new = g84_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c
index 7a7af3b478f8..d184e6ab8918 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c
@@ -22,31 +22,20 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
g94_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
- .super = nv50_disp_intr_supervisor,
+ .super = nv50_disp_super,
.root = &g94_disp_root_oclass,
- .head.vblank_init = nv50_disp_vblank_init,
- .head.vblank_fini = nv50_disp_vblank_fini,
- .head.scanoutpos = nv50_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = g94_sor_dp_new,
- .outp.external.tmds = nv50_pior_output_new,
- .outp.external.dp = nv50_pior_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hdmi = g84_hdmi_ctrl,
- .pior.nr = 3,
- .pior.power = nv50_pior_power,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 4, .new = g94_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
index 7b346ccc38b7..d8765b57180b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
@@ -22,397 +22,63 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
-#include <subdev/bios.h>
-#include <subdev/bios/disp.h>
-#include <subdev/bios/init.h>
-#include <subdev/bios/pll.h>
-#include <subdev/devinit.h>
-
-void
-gf119_disp_vblank_init(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
-}
-
-void
-gf119_disp_vblank_fini(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
-}
-
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
- u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
- struct nvbios_outp *info)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_bios *bios = subdev->device->bios;
- struct nvkm_output *outp;
- u16 mask, type;
-
- if (or < 4) {
- type = DCB_OUTPUT_ANALOG;
- mask = 0;
- } else {
- or -= 4;
- switch (ctrl & 0x00000f00) {
- case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
- case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
- case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
- case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
- case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
- case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
- default:
- nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
- return NULL;
- }
- }
-
- mask = 0x00c0 & (mask << 6);
- mask |= 0x0001 << or;
- mask |= 0x0100 << head;
-
- list_for_each_entry(outp, &disp->base.outp, head) {
- if ((outp->info.hasht & 0xff) == type &&
- (outp->info.hashm & mask) == mask) {
- *data = nvbios_outp_match(bios, outp->info.hasht, mask,
- ver, hdr, cnt, len, info);
- if (!*data)
- return NULL;
- return outp;
- }
- }
-
- return NULL;
-}
-
-static struct nvkm_output *
-exec_script(struct nv50_disp *disp, int head, int id)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_bios *bios = device->bios;
- struct nvkm_output *outp;
- struct nvbios_outp info;
- u8 ver, hdr, cnt, len;
- u32 data, ctrl = 0;
- int or;
-
- for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
- ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20));
- if (ctrl & (1 << head))
- break;
- }
-
- if (or == 8)
- return NULL;
-
- outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
- if (outp) {
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = bios,
- .offset = info.script[id],
- .outp = &outp->info,
- .crtc = head,
- .execute = 1,
- };
-
- nvbios_exec(&init);
- }
-
- return outp;
-}
-
-static struct nvkm_output *
-exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_bios *bios = device->bios;
- struct nvkm_output *outp;
- struct nvbios_outp info1;
- struct nvbios_ocfg info2;
- u8 ver, hdr, cnt, len;
- u32 data, ctrl = 0;
- int or;
-
- for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
- ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20));
- if (ctrl & (1 << head))
- break;
- }
-
- if (or == 8)
- return NULL;
-
- outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
- if (!outp)
- return NULL;
-
- *conf = (ctrl & 0x00000f00) >> 8;
- switch (outp->info.type) {
- case DCB_OUTPUT_TMDS:
- if (*conf == 5)
- *conf |= 0x0100;
- break;
- case DCB_OUTPUT_LVDS:
- *conf |= disp->sor.lvdsconf;
- break;
- default:
- break;
- }
-
- data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
- &ver, &hdr, &cnt, &len, &info2);
- if (data && id < 0xff) {
- data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
- if (data) {
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = bios,
- .offset = data,
- .outp = &outp->info,
- .crtc = head,
- .execute = 1,
- };
-
- nvbios_exec(&init);
- }
- }
-
- return outp;
-}
-
-static void
-gf119_disp_intr_unk1_0(struct nv50_disp *disp, int head)
-{
- exec_script(disp, head, 1);
-}
-
-static void
-gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_output *outp = exec_script(disp, head, 2);
-
- /* see note in nv50_disp_intr_unk20_0() */
- if (outp && outp->info.type == DCB_OUTPUT_DP) {
- struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
- if (!outpdp->lt.mst) {
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = subdev->device->bios,
- .outp = &outp->info,
- .crtc = head,
- .offset = outpdp->info.script[4],
- .execute = 1,
- };
-
- nvkm_notify_put(&outpdp->irq);
- nvbios_exec(&init);
- atomic_set(&outpdp->lt.done, 0);
- }
- }
-}
-
-static void
-gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_devinit *devinit = device->devinit;
- u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
- if (pclk)
- nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk);
- nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000);
-}
-
-static void
-gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head,
- struct dcb_output *outp)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const int or = ffs(outp->or) - 1;
- const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020));
- const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300));
- const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff;
- const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff;
- const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff;
- const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
- const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
- const u32 hoff = (head * 0x800);
- const u32 soff = ( or * 0x800);
- const u32 loff = (link * 0x080) + soff;
- const u32 symbol = 100000;
- const u32 TU = 64;
- u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
- u32 clksor = nvkm_rd32(device, 0x612300 + soff);
- u32 datarate, link_nr, link_bw, bits;
- u64 ratio, value;
-
- link_nr = hweight32(dpctrl & 0x000f0000);
- link_bw = (clksor & 0x007c0000) >> 18;
- link_bw *= 27000;
-
- /* symbols/hblank - algorithm taken from comments in tegra driver */
- value = vblanke + vactive - vblanks - 7;
- value = value * link_bw;
- do_div(value, pclk);
- value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
- nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value);
-
- /* symbols/vblank - algorithm taken from comments in tegra driver */
- value = vblanks - vblanke - 25;
- value = value * link_bw;
- do_div(value, pclk);
- value = value - ((36 / link_nr) + 3) - 1;
- nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value);
-
- /* watermark */
- if ((conf & 0x3c0) == 0x180) bits = 30;
- else if ((conf & 0x3c0) == 0x140) bits = 24;
- else bits = 18;
- datarate = (pclk * bits) / 8;
-
- ratio = datarate;
- ratio *= symbol;
- do_div(ratio, link_nr * link_bw);
-
- value = (symbol - ratio) * TU;
- value *= ratio;
- do_div(value, symbol);
- do_div(value, symbol);
-
- value += 5;
- value |= 0x08000000;
-
- nvkm_wr32(device, 0x616610 + hoff, value);
-}
-
-static void
-gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_output *outp;
- u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
- u32 conf, addr, data;
-
- outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
- if (!outp)
- return;
-
- /* see note in nv50_disp_intr_unk20_2() */
- if (outp->info.type == DCB_OUTPUT_DP) {
- u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300));
- switch ((sync & 0x000003c0) >> 6) {
- case 6: pclk = pclk * 30; break;
- case 5: pclk = pclk * 24; break;
- case 2:
- default:
- pclk = pclk * 18;
- break;
- }
-
- if (nvkm_output_dp_train(outp, pclk))
- OUTP_ERR(outp, "link not trained before attach");
- } else {
- if (disp->func->sor.magic)
- disp->func->sor.magic(outp);
- }
-
- exec_clkcmp(disp, head, 0, pclk, &conf);
-
- if (outp->info.type == DCB_OUTPUT_ANALOG) {
- addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
- data = 0x00000000;
- } else {
- addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
- data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
- switch (outp->info.type) {
- case DCB_OUTPUT_TMDS:
- nvkm_mask(device, addr, 0x007c0000, 0x00280000);
- break;
- case DCB_OUTPUT_DP:
- gf119_disp_intr_unk2_2_tu(disp, head, &outp->info);
- break;
- default:
- break;
- }
- }
-
- nvkm_mask(device, addr, 0x00000707, data);
-}
-
-static void
-gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
- u32 conf;
-
- exec_clkcmp(disp, head, 1, pclk, &conf);
-}
-
void
-gf119_disp_intr_supervisor(struct work_struct *work)
+gf119_disp_super(struct work_struct *work)
{
struct nv50_disp *disp =
container_of(work, struct nv50_disp, supervisor);
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
struct nvkm_device *device = subdev->device;
+ struct nvkm_head *head;
u32 mask[4];
- int head;
nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super));
- for (head = 0; head < disp->base.head.nr; head++) {
- mask[head] = nvkm_rd32(device, 0x6101d4 + (head * 0x800));
- nvkm_debug(subdev, "head %d: %08x\n", head, mask[head]);
+ list_for_each_entry(head, &disp->base.head, head) {
+ mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800));
+ HEAD_DBG(head, "%08x", mask[head->id]);
}
if (disp->super & 0x00000001) {
nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(mask[head] & 0x00001000))
+ nv50_disp_super_1(disp);
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(mask[head->id] & 0x00001000))
continue;
- nvkm_debug(subdev, "supervisor 1.0 - head %d\n", head);
- gf119_disp_intr_unk1_0(disp, head);
+ nv50_disp_super_1_0(disp, head);
}
} else
if (disp->super & 0x00000002) {
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(mask[head] & 0x00001000))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(mask[head->id] & 0x00001000))
continue;
- nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head);
- gf119_disp_intr_unk2_0(disp, head);
+ nv50_disp_super_2_0(disp, head);
}
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(mask[head] & 0x00010000))
+ nvkm_outp_route(&disp->base);
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(mask[head->id] & 0x00010000))
continue;
- nvkm_debug(subdev, "supervisor 2.1 - head %d\n", head);
- gf119_disp_intr_unk2_1(disp, head);
+ nv50_disp_super_2_1(disp, head);
}
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(mask[head] & 0x00001000))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(mask[head->id] & 0x00001000))
continue;
- nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head);
- gf119_disp_intr_unk2_2(disp, head);
+ nv50_disp_super_2_2(disp, head);
}
} else
if (disp->super & 0x00000004) {
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(mask[head] & 0x00001000))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(mask[head->id] & 0x00001000))
continue;
- nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head);
- gf119_disp_intr_unk4_0(disp, head);
+ nv50_disp_super_3_0(disp, head);
}
}
- for (head = 0; head < disp->base.head.nr; head++)
- nvkm_wr32(device, 0x6101d4 + (head * 0x800), 0x00000000);
+ list_for_each_entry(head, &disp->base.head, head)
+ nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000);
nvkm_wr32(device, 0x6101d0, 0x80000000);
}
@@ -447,8 +113,8 @@ gf119_disp_intr(struct nv50_disp *disp)
{
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
struct nvkm_device *device = subdev->device;
+ struct nvkm_head *head;
u32 intr = nvkm_rd32(device, 0x610088);
- int i;
if (intr & 0x00000001) {
u32 stat = nvkm_rd32(device, 0x61008c);
@@ -472,7 +138,7 @@ gf119_disp_intr(struct nv50_disp *disp)
u32 stat = nvkm_rd32(device, 0x6100ac);
if (stat & 0x00000007) {
disp->super = (stat & 0x00000007);
- schedule_work(&disp->supervisor);
+ queue_work(disp->wq, &disp->supervisor);
nvkm_wr32(device, 0x6100ac, disp->super);
stat &= ~0x00000007;
}
@@ -485,14 +151,15 @@ gf119_disp_intr(struct nv50_disp *disp)
intr &= ~0x00100000;
}
- for (i = 0; i < disp->base.head.nr; i++) {
- u32 mask = 0x01000000 << i;
+ list_for_each_entry(head, &disp->base.head, head) {
+ const u32 hoff = head->id * 0x800;
+ u32 mask = 0x01000000 << head->id;
if (mask & intr) {
- u32 stat = nvkm_rd32(device, 0x6100bc + (i * 0x800));
+ u32 stat = nvkm_rd32(device, 0x6100bc + hoff);
if (stat & 0x00000001)
- nvkm_disp_vblank(&disp->base, i);
- nvkm_mask(device, 0x6100bc + (i * 0x800), 0, 0);
- nvkm_rd32(device, 0x6100c0 + (i * 0x800));
+ nvkm_disp_vblank(&disp->base, head->id);
+ nvkm_mask(device, 0x6100bc + hoff, 0, 0);
+ nvkm_rd32(device, 0x6100c0 + hoff);
}
}
}
@@ -510,22 +177,11 @@ gf119_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gf119_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gf119_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gf119_hdmi_ctrl,
+ .head.new = gf119_head_new,
+ .dac = { .nr = 3, .new = gf119_dac_new },
+ .sor = { .nr = 4, .new = gf119_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
index 37f145cf30d7..e8fe9f315d64 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
@@ -29,22 +31,11 @@ gk104_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gk104_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gf119_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
+ .head.new = gf119_head_new,
+ .dac = { .nr = 3, .new = gf119_dac_new },
+ .sor = { .nr = 4, .new = gk104_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c
index e14ac946608c..769687502e7a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
@@ -29,22 +31,11 @@ gk110_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gk110_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gf119_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
+ .head.new = gf119_head_new,
+ .dac = { .nr = 3, .new = gf119_dac_new },
+ .sor = { .nr = 4, .new = gk104_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
index 2f2437cc5891..ede70e5d188e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
@@ -29,22 +31,11 @@ gm107_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gm107_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gm107_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
+ .head.new = gf119_head_new,
+ .dac = { .nr = 3, .new = gf119_dac_new },
+ .sor = { .nr = 4, .new = gm107_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
index 9f368d4ee61e..292d3b5f9704 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
@@ -29,23 +31,11 @@ gm200_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gm200_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gm200_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
- .sor.magic = gm200_sor_magic,
+ .head.new = gf119_head_new,
+ .dac = { .nr = 3, .new = gf119_dac_new },
+ .sor = { .nr = 4, .new = gm200_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
index 4f81bf31435e..39eb98b2c3a2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
@@ -29,23 +31,10 @@ gp100_disp = {
.intr = gf119_disp_intr,
.intr_error = gf119_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gp100_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gm200_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
- .sor.magic = gm200_sor_magic,
+ .head.new = gf119_head_new,
+ .sor = { .nr = 4, .new = gm200_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c
index f5d613f82709..91d70fe18275 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static void
@@ -55,23 +57,10 @@ gp102_disp = {
.intr = gf119_disp_intr,
.intr_error = gp102_disp_intr_error,
.uevent = &gf119_disp_chan_uevent,
- .super = gf119_disp_intr_supervisor,
+ .super = gf119_disp_super,
.root = &gp102_disp_root_oclass,
- .head.vblank_init = gf119_disp_vblank_init,
- .head.vblank_fini = gf119_disp_vblank_fini,
- .head.scanoutpos = gf119_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = gm200_sor_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gf119_hda_eld,
- .sor.hdmi = gk104_hdmi_ctrl,
- .sor.magic = gm200_sor_magic,
+ .head.new = gf119_head_new,
+ .sor = { .nr = 4, .new = gm200_sor_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c
index 6bc3bf096001..bf00c4e3be3a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c
@@ -22,30 +22,20 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
gt200_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
- .super = nv50_disp_intr_supervisor,
+ .super = nv50_disp_super,
.root = &gt200_disp_root_oclass,
- .head.vblank_init = nv50_disp_vblank_init,
- .head.vblank_fini = nv50_disp_vblank_fini,
- .head.scanoutpos = nv50_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.external.tmds = nv50_pior_output_new,
- .outp.external.dp = nv50_pior_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 2,
- .sor.power = nv50_sor_power,
- .sor.hdmi = g84_hdmi_ctrl,
- .pior.nr = 3,
- .pior.power = nv50_pior_power,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 2, .new = g84_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
index 94026288ab4d..2cdd4d7a98d3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c
@@ -22,32 +22,20 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
static const struct nv50_disp_func
gt215_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
- .super = nv50_disp_intr_supervisor,
+ .super = nv50_disp_super,
.root = &gt215_disp_root_oclass,
- .head.vblank_init = nv50_disp_vblank_init,
- .head.vblank_fini = nv50_disp_vblank_fini,
- .head.scanoutpos = nv50_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.internal.dp = g94_sor_dp_new,
- .outp.external.tmds = nv50_pior_output_new,
- .outp.external.dp = nv50_pior_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 4,
- .sor.power = nv50_sor_power,
- .sor.hda_eld = gt215_hda_eld,
- .sor.hdmi = gt215_hdmi_ctrl,
- .pior.nr = 3,
- .pior.power = nv50_pior_power,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 4, .new = gt215_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c
index da6129b2b78f..0fa0ec0a1de0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c
@@ -21,63 +21,34 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outp.h"
+#include "ior.h"
-#include <core/client.h>
-#include <subdev/bios.h>
-#include <subdev/bios/dcb.h>
-#include <subdev/timer.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gf119_hda_eld(NV50_DISP_MTHD_V1)
+void
+gf119_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- union {
- struct nv50_disp_sor_hda_eld_v0 v0;
- } *args = data;
- const u32 soff = outp->or * 0x030;
- const u32 hoff = head * 0x800;
- int ret = -ENOSYS, i;
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 soff = 0x030 * ior->id;
+ int i;
- 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;
+ for (i = 0; i < size; i++)
+ nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]);
+ for (; i < 0x60; i++)
+ nvkm_wr32(device, 0x10ec00 + soff, (i << 8));
+ nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002);
+}
- if (size && args->v0.data[0]) {
- if (outp->info.type == DCB_OUTPUT_DP) {
- nvkm_mask(device, 0x616618 + hoff, 0x8000000c, 0x80000001);
- nvkm_msec(device, 2000,
- u32 tmp = nvkm_rd32(device, 0x616618 + hoff);
- if (!(tmp & 0x80000000))
- break;
- );
- }
+void
+gf119_hda_hpd(struct nvkm_ior *ior, int head, bool present)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 hoff = 0x800 * head;
+ u32 data = 0x80000000;
+ u32 mask = 0x80000001;
+ if (present) {
nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000);
- for (i = 0; i < size; i++)
- nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | args->v0.data[i]);
- for (; i < 0x60; i++)
- nvkm_wr32(device, 0x10ec00 + soff, (i << 8));
- nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000003);
+ data |= 0x00000001;
} else {
- if (outp->info.type == DCB_OUTPUT_DP) {
- nvkm_mask(device, 0x616618 + hoff, 0x80000001, 0x80000000);
- nvkm_msec(device, 2000,
- u32 tmp = nvkm_rd32(device, 0x616618 + hoff);
- if (!(tmp & 0x80000000))
- break;
- );
- }
- nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size);
+ mask |= 0x00000002;
}
-
- return 0;
+ nvkm_mask(device, 0x10ec10 + ior->id * 0x030, mask, data);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
index f8f2f16c22a2..4509d2ba880e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
@@ -21,59 +21,31 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outp.h"
+#include "ior.h"
-#include <core/client.h>
-#include <subdev/timer.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gt215_hda_eld(NV50_DISP_MTHD_V1)
+void
+gt215_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- union {
- struct nv50_disp_sor_hda_eld_v0 v0;
- } *args = data;
- const u32 soff = outp->or * 0x800;
- int ret = -ENOSYS, i;
-
- 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;
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 soff = ior->id * 0x800;
+ int i;
- if (size && args->v0.data[0]) {
- if (outp->info.type == DCB_OUTPUT_DP) {
- nvkm_mask(device, 0x61c1e0 + soff, 0x8000000d, 0x80000001);
- nvkm_msec(device, 2000,
- u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff);
- if (!(tmp & 0x80000000))
- break;
- );
- }
- for (i = 0; i < size; i++)
- nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]);
- for (; i < 0x60; i++)
- nvkm_wr32(device, 0x61c440 + soff, (i << 8));
- nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003);
- } else {
- if (outp->info.type == DCB_OUTPUT_DP) {
- nvkm_mask(device, 0x61c1e0 + soff, 0x80000001, 0x80000000);
- nvkm_msec(device, 2000,
- u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff);
- if (!(tmp & 0x80000000))
- break;
- );
- }
- nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size);
- }
+ for (i = 0; i < size; i++)
+ nvkm_wr32(device, 0x61c440 + soff, (i << 8) | data[i]);
+ for (; i < 0x60; i++)
+ nvkm_wr32(device, 0x61c440 + soff, (i << 8));
+ nvkm_mask(device, 0x61c448 + soff, 0x80000002, 0x80000002);
+}
- return 0;
+void
+gt215_hda_hpd(struct nvkm_ior *ior, int head, bool present)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ u32 data = 0x80000000;
+ u32 mask = 0x80000001;
+ if (present)
+ data |= 0x00000001;
+ else
+ mask |= 0x00000002;
+ nvkm_mask(device, 0x61c448 + ior->id * 0x800, mask, data);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c
new file mode 100644
index 000000000000..e82c68f18444
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c
@@ -0,0 +1,66 @@
+#include "hdmi.h"
+
+void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
+ u8 *raw_frame, ssize_t len)
+{
+ u32 header = 0;
+ u32 subpack0_low = 0;
+ u32 subpack0_high = 0;
+ u32 subpack1_low = 0;
+ u32 subpack1_high = 0;
+
+ switch (len) {
+ /*
+ * "When in doubt, use brute force."
+ * -- Ken Thompson.
+ */
+ default:
+ /*
+ * We presume that no valid frame is longer than 17
+ * octets, including header... And truncate to that
+ * if it's longer.
+ */
+ case 17:
+ subpack1_high = (raw_frame[16] << 16);
+ case 16:
+ subpack1_high |= (raw_frame[15] << 8);
+ case 15:
+ subpack1_high |= raw_frame[14];
+ case 14:
+ subpack1_low = (raw_frame[13] << 24);
+ case 13:
+ subpack1_low |= (raw_frame[12] << 16);
+ case 12:
+ subpack1_low |= (raw_frame[11] << 8);
+ case 11:
+ subpack1_low |= raw_frame[10];
+ case 10:
+ subpack0_high = (raw_frame[9] << 16);
+ case 9:
+ subpack0_high |= (raw_frame[8] << 8);
+ case 8:
+ subpack0_high |= raw_frame[7];
+ case 7:
+ subpack0_low = (raw_frame[6] << 24);
+ case 6:
+ subpack0_low |= (raw_frame[5] << 16);
+ case 5:
+ subpack0_low |= (raw_frame[4] << 8);
+ case 4:
+ subpack0_low |= raw_frame[3];
+ case 3:
+ header = (raw_frame[2] << 16);
+ case 2:
+ header |= (raw_frame[1] << 8);
+ case 1:
+ header |= raw_frame[0];
+ case 0:
+ break;
+ }
+
+ packed_frame->header = header;
+ packed_frame->subpack0_low = subpack0_low;
+ packed_frame->subpack0_high = subpack0_high;
+ packed_frame->subpack1_low = subpack1_low;
+ packed_frame->subpack1_high = subpack1_high;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h
new file mode 100644
index 000000000000..528f5621a496
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h
@@ -0,0 +1,15 @@
+#ifndef __NVKM_DISP_HDMI_H__
+#define __NVKM_DISP_HDMI_H__
+#include "ior.h"
+
+struct packed_hdmi_infoframe {
+ u32 header;
+ u32 subpack0_low;
+ u32 subpack0_high;
+ u32 subpack1_low;
+ u32 subpack1_high;
+};
+
+void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
+ u8 *raw_frame, ssize_t len);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c
index 1c4256e8cbd6..661410f9b457 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c
@@ -21,54 +21,42 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
+#include "hdmi.h"
-#include <core/client.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
+void
+g84_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)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 hoff = (head * 0x800);
- union {
- struct nv50_disp_sor_hdmi_pwr_v0 v0;
- } *args = data;
- u32 ctrl;
- int ret = -ENOSYS;
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 ctrl = 0x40000000 * enable |
+ 0x1f000000 /* ??? */ |
+ max_ac_packet << 16 |
+ rekey;
+ const u32 hoff = head * 0x800;
+ struct packed_hdmi_infoframe avi_infoframe;
+ struct packed_hdmi_infoframe vendor_infoframe;
- nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
- "max_ac_packet %d rekey %d\n",
- args->v0.version, args->v0.state,
- args->v0.max_ac_packet, args->v0.rekey);
- if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
- return -EINVAL;
- ctrl = 0x40000000 * !!args->v0.state;
- ctrl |= args->v0.max_ac_packet << 16;
- ctrl |= args->v0.rekey;
- ctrl |= 0x1f000000; /* ??? */
- } else
- return ret;
+ 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 0;
+ return;
}
/* AVI InfoFrame */
nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
- nvkm_wr32(device, 0x616528 + hoff, 0x000d0282);
- nvkm_wr32(device, 0x61652c + hoff, 0x0000006f);
- nvkm_wr32(device, 0x616530 + hoff, 0x00000000);
- nvkm_wr32(device, 0x616534 + hoff, 0x00000000);
- nvkm_wr32(device, 0x616538 + hoff, 0x00000000);
- nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001);
+ 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);
@@ -77,6 +65,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
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, ?? */
nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
@@ -88,5 +88,4 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
/* HDMI_CTRL */
nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl);
- return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c
index 632f02da1382..6cac0e72b4cc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c
@@ -21,53 +21,56 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
+#include "hdmi.h"
-#include <core/client.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gf119_hdmi_ctrl(NV50_DISP_MTHD_V1)
+void
+gf119_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)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 hoff = (head * 0x800);
- union {
- struct nv50_disp_sor_hdmi_pwr_v0 v0;
- } *args = data;
- u32 ctrl;
- int ret = -ENOSYS;
+ 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;
- nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
- "max_ac_packet %d rekey %d\n",
- args->v0.version, args->v0.state,
- args->v0.max_ac_packet, args->v0.rekey);
- if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
- return -EINVAL;
- ctrl = 0x40000000 * !!args->v0.state;
- ctrl |= args->v0.max_ac_packet << 16;
- ctrl |= args->v0.rekey;
- } else
- return ret;
+ 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);
+ nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
- return 0;
+ return;
}
/* AVI InfoFrame */
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
- nvkm_wr32(device, 0x61671c + hoff, 0x000d0282);
- nvkm_wr32(device, 0x616720 + hoff, 0x0000006f);
- nvkm_wr32(device, 0x616724 + hoff, 0x00000000);
- nvkm_wr32(device, 0x616728 + hoff, 0x00000000);
- nvkm_wr32(device, 0x61672c + hoff, 0x00000000);
- nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001);
+ 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);
@@ -76,5 +79,4 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1)
/* HDMI_CTRL */
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
- return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c
index 4e8067d511d7..ed0a6100d76b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c
@@ -21,54 +21,53 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
+#include "hdmi.h"
-#include <core/client.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gk104_hdmi_ctrl(NV50_DISP_MTHD_V1)
+void
+gk104_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)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 hoff = (head * 0x800);
- const u32 hdmi = (head * 0x400);
- union {
- struct nv50_disp_sor_hdmi_pwr_v0 v0;
- } *args = data;
- u32 ctrl;
- int ret = -ENOSYS;
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 ctrl = 0x40000000 * enable |
+ max_ac_packet << 16 |
+ rekey;
+ const u32 hoff = head * 0x800;
+ const u32 hdmi = head * 0x400;
+ struct packed_hdmi_infoframe avi_infoframe;
+ struct packed_hdmi_infoframe vendor_infoframe;
- nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
- "max_ac_packet %d rekey %d\n",
- args->v0.version, args->v0.state,
- args->v0.max_ac_packet, args->v0.rekey);
- if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
- return -EINVAL;
- ctrl = 0x40000000 * !!args->v0.state;
- ctrl |= args->v0.max_ac_packet << 16;
- ctrl |= args->v0.rekey;
- } else
- return ret;
+ 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);
+ nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000);
nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000);
- return 0;
+ return;
}
/* AVI InfoFrame */
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000);
- nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282);
- nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f);
- nvkm_wr32(device, 0x690010 + hdmi, 0x00000000);
- nvkm_wr32(device, 0x690014 + hdmi, 0x00000000);
- nvkm_wr32(device, 0x690018 + hdmi, 0x00000000);
- nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001);
+ 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);
@@ -80,5 +79,4 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1)
/* HDMI_CTRL */
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
- return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c
index f1afc16494b6..0993d223bb9c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c
@@ -21,55 +21,42 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outp.h"
+#include "hdmi.h"
-#include <core/client.h>
-
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
+void
+gt215_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)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 soff = outp->or * 0x800;
- union {
- struct nv50_disp_sor_hdmi_pwr_v0 v0;
- } *args = data;
- u32 ctrl;
- int ret = -ENOSYS;
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 ctrl = 0x40000000 * enable |
+ 0x1f000000 /* ??? */ |
+ max_ac_packet << 16 |
+ rekey;
+ const u32 soff = nv50_ior_base(ior);
+ struct packed_hdmi_infoframe avi_infoframe;
+ struct packed_hdmi_infoframe vendor_infoframe;
- nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
- "max_ac_packet %d rekey %d\n",
- args->v0.version, args->v0.state,
- args->v0.max_ac_packet, args->v0.rekey);
- if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
- return -EINVAL;
- ctrl = 0x40000000 * !!args->v0.state;
- ctrl |= args->v0.max_ac_packet << 16;
- ctrl |= args->v0.rekey;
- ctrl |= 0x1f000000; /* ??? */
- } else
- return ret;
+ 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);
+ nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000);
- return 0;
+ return;
}
/* AVI InfoFrame */
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
- nvkm_wr32(device, 0x61c528 + soff, 0x000d0282);
- nvkm_wr32(device, 0x61c52c + soff, 0x0000006f);
- nvkm_wr32(device, 0x61c530 + soff, 0x00000000);
- nvkm_wr32(device, 0x61c534 + soff, 0x00000000);
- nvkm_wr32(device, 0x61c538 + soff, 0x00000000);
- nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001);
+ 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);
@@ -78,6 +65,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
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 */
@@ -89,5 +88,4 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
/* HDMI_CTRL */
nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl);
- return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c
new file mode 100644
index 000000000000..5c557f3e6c2c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 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 <bskeggs@redhat.com>
+ */
+#include "head.h"
+
+#include <core/client.h>
+
+#include <nvif/cl0046.h>
+#include <nvif/unpack.h>
+
+struct nvkm_head *
+nvkm_head_find(struct nvkm_disp *disp, int id)
+{
+ struct nvkm_head *head;
+ list_for_each_entry(head, &disp->head, head) {
+ if (head->id == id)
+ return head;
+ }
+ 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)
+{
+ struct nvkm_head *head = *phead;
+ if (head) {
+ HEAD_DBG(head, "dtor");
+ list_del(&head->head);
+ kfree(*phead);
+ *phead = NULL;
+ }
+}
+
+int
+nvkm_head_new_(const struct nvkm_head_func *func,
+ struct nvkm_disp *disp, int id)
+{
+ struct nvkm_head *head;
+ if (!(head = kzalloc(sizeof(*head), GFP_KERNEL)))
+ return -ENOMEM;
+ head->func = func;
+ head->disp = disp;
+ head->id = id;
+ list_add_tail(&head->head, &disp->head);
+ HEAD_DBG(head, "ctor");
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
new file mode 100644
index 000000000000..b04c49d2eeeb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
@@ -0,0 +1,56 @@
+#ifndef __NVKM_DISP_HEAD_H__
+#define __NVKM_DISP_HEAD_H__
+#include "priv.h"
+
+struct nvkm_head {
+ const struct nvkm_head_func *func;
+ struct nvkm_disp *disp;
+ int id;
+
+ struct list_head head;
+
+ struct nvkm_head_state {
+ u16 htotal;
+ u16 hsynce;
+ u16 hblanke;
+ u16 hblanks;
+ u16 vtotal;
+ u16 vsynce;
+ u16 vblanke;
+ u16 vblanks;
+ u32 hz;
+
+ /* Prior to GF119, these are set by the OR. */
+ struct {
+ u8 depth;
+ } or;
+ } arm, asy;
+};
+
+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 {
+ void (*state)(struct nvkm_head *, struct nvkm_head_state *);
+ void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline);
+ void (*rgclk)(struct nvkm_head *, int div);
+ void (*vblank_get)(struct nvkm_head *);
+ void (*vblank_put)(struct nvkm_head *);
+};
+
+void nv50_head_rgpos(struct nvkm_head *, u16 *, u16 *);
+
+#define HEAD_MSG(h,l,f,a...) do { \
+ struct nvkm_head *_h = (h); \
+ nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \
+} while(0)
+#define HEAD_WARN(h,f,a...) HEAD_MSG((h), warn, f, ##a)
+#define HEAD_DBG(h,f,a...) HEAD_MSG((h), debug, f, ##a)
+
+int nv04_head_new(struct nvkm_disp *, int id);
+int nv50_head_new(struct nvkm_disp *, int id);
+int gf119_head_new(struct nvkm_disp *, int id);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c
new file mode 100644
index 000000000000..b33552757647
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 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 <bskeggs@redhat.com>
+ */
+#include "head.h"
+
+static void
+gf119_head_vblank_put(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = head->id * 0x800;
+ nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000);
+}
+
+static void
+gf119_head_vblank_get(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = head->id * 0x800;
+ nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001);
+}
+
+static void
+gf119_head_rgclk(struct nvkm_head *head, int div)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div);
+}
+
+static void
+gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300;
+ u32 data;
+
+ data = nvkm_rd32(device, 0x640414 + hoff);
+ state->vtotal = (data & 0xffff0000) >> 16;
+ state->htotal = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x640418 + hoff);
+ state->vsynce = (data & 0xffff0000) >> 16;
+ state->hsynce = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x64041c + hoff);
+ state->vblanke = (data & 0xffff0000) >> 16;
+ state->hblanke = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x640420 + hoff);
+ state->vblanks = (data & 0xffff0000) >> 16;
+ state->hblanks = (data & 0x0000ffff);
+ state->hz = nvkm_rd32(device, 0x640450 + hoff);
+
+ data = nvkm_rd32(device, 0x640404 + hoff);
+ switch ((data & 0x000003c0) >> 6) {
+ case 6: state->or.depth = 30; break;
+ case 5: state->or.depth = 24; break;
+ case 2: state->or.depth = 18; break;
+ case 0: state->or.depth = 18; break; /*XXX: "default" */
+ default:
+ state->or.depth = 18;
+ WARN_ON(1);
+ break;
+ }
+}
+
+static const struct nvkm_head_func
+gf119_head = {
+ .state = gf119_head_state,
+ .rgpos = nv50_head_rgpos,
+ .rgclk = gf119_head_rgclk,
+ .vblank_get = gf119_head_vblank_get,
+ .vblank_put = gf119_head_vblank_put,
+};
+
+int
+gf119_head_new(struct nvkm_disp *disp, int id)
+{
+ return nvkm_head_new_(&gf119_head, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c
new file mode 100644
index 000000000000..dcf459282aa1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 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 <bskeggs@redhat.com>
+ */
+#include "head.h"
+
+static void
+nv04_head_vblank_put(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000000);
+}
+
+static void
+nv04_head_vblank_get(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000001);
+}
+
+static void
+nv04_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ u32 data = nvkm_rd32(device, 0x600868 + (head->id * 0x2000));
+ *hline = (data & 0xffff0000) >> 16;
+ *vline = (data & 0x0000ffff);
+}
+
+static void
+nv04_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = head->id * 0x0200;
+ state->vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0x0000ffff;
+ state->vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0x0000ffff;
+ state->vblanke = state->vtotal - 1;
+ state->hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0x0000ffff;
+ state->htotal = nvkm_rd32(device, 0x680824 + hoff) & 0x0000ffff;
+ state->hblanke = state->htotal - 1;
+}
+
+static const struct nvkm_head_func
+nv04_head = {
+ .state = nv04_head_state,
+ .rgpos = nv04_head_rgpos,
+ .vblank_get = nv04_head_vblank_get,
+ .vblank_put = nv04_head_vblank_put,
+};
+
+int
+nv04_head_new(struct nvkm_disp *disp, int id)
+{
+ return nvkm_head_new_(&nv04_head, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c
new file mode 100644
index 000000000000..c80d06d5168f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017 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 <bskeggs@redhat.com>
+ */
+#include "head.h"
+
+static void
+nv50_head_vblank_put(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_mask(device, 0x61002c, (4 << head->id), 0);
+}
+
+static void
+nv50_head_vblank_get(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));
+}
+
+static void
+nv50_head_rgclk(struct nvkm_head *head, int div)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);
+}
+
+void
+nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = head->id * 0x800;
+ /* vline read locks hline. */
+ *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff;
+ *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff;
+}
+
+static void
+nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4;
+ u32 data;
+
+ data = nvkm_rd32(device, 0x610ae8 + hoff);
+ state->vblanke = (data & 0xffff0000) >> 16;
+ state->hblanke = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x610af0 + hoff);
+ state->vblanks = (data & 0xffff0000) >> 16;
+ state->hblanks = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x610af8 + hoff);
+ state->vtotal = (data & 0xffff0000) >> 16;
+ state->htotal = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x610b00 + hoff);
+ state->vsynce = (data & 0xffff0000) >> 16;
+ state->hsynce = (data & 0x0000ffff);
+ state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000;
+}
+
+static const struct nvkm_head_func
+nv50_head = {
+ .state = nv50_head_state,
+ .rgpos = nv50_head_rgpos,
+ .rgclk = nv50_head_rgclk,
+ .vblank_get = nv50_head_vblank_get,
+ .vblank_put = nv50_head_vblank_put,
+};
+
+int
+nv50_head_new(struct nvkm_disp *disp, int id)
+{
+ return nvkm_head_new_(&nv50_head, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c
new file mode 100644
index 000000000000..a475ea56795c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 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 <bskeggs@redhat.com>
+ */
+#include "ior.h"
+
+static const char *
+nvkm_ior_name[] = {
+ [DAC] = "DAC",
+ [SOR] = "SOR",
+ [PIOR] = "PIOR",
+};
+
+struct nvkm_ior *
+nvkm_ior_find(struct nvkm_disp *disp, enum nvkm_ior_type type, int id)
+{
+ struct nvkm_ior *ior;
+ list_for_each_entry(ior, &disp->ior, head) {
+ if (ior->type == type && (id < 0 || ior->id == id))
+ return ior;
+ }
+ return NULL;
+}
+
+void
+nvkm_ior_del(struct nvkm_ior **pior)
+{
+ struct nvkm_ior *ior = *pior;
+ if (ior) {
+ IOR_DBG(ior, "dtor");
+ list_del(&ior->head);
+ kfree(*pior);
+ *pior = NULL;
+ }
+}
+
+int
+nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp,
+ enum nvkm_ior_type type, int id)
+{
+ struct nvkm_ior *ior;
+ if (!(ior = kzalloc(sizeof(*ior), GFP_KERNEL)))
+ return -ENOMEM;
+ ior->func = func;
+ ior->disp = disp;
+ ior->type = type;
+ ior->id = id;
+ snprintf(ior->name, sizeof(ior->name), "%s-%d",
+ nvkm_ior_name[ior->type], ior->id);
+ list_add_tail(&ior->head, &disp->ior);
+ IOR_DBG(ior, "ctor");
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
new file mode 100644
index 000000000000..a24312fb0228
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
@@ -0,0 +1,169 @@
+#ifndef __NVKM_DISP_IOR_H__
+#define __NVKM_DISP_IOR_H__
+#include "priv.h"
+struct nvkm_i2c_aux;
+
+struct nvkm_ior {
+ const struct nvkm_ior_func *func;
+ struct nvkm_disp *disp;
+ enum nvkm_ior_type {
+ DAC,
+ SOR,
+ PIOR,
+ } type;
+ int id;
+ char name[8];
+
+ struct list_head head;
+
+ struct nvkm_ior_state {
+ struct nvkm_outp *outp;
+ unsigned rgdiv;
+ unsigned proto_evo:4;
+ enum nvkm_ior_proto {
+ CRT,
+ TMDS,
+ LVDS,
+ DP,
+ UNKNOWN
+ } proto:3;
+ unsigned link:2;
+ unsigned head:4;
+ } arm, asy;
+
+ /* Armed DP state. */
+ struct {
+ bool mst;
+ bool ef;
+ u8 nr;
+ u8 bw;
+ } dp;
+};
+
+struct nvkm_ior_func {
+ struct {
+ int (*get)(struct nvkm_outp *, int *link);
+ void (*set)(struct nvkm_outp *, struct nvkm_ior *);
+ } route;
+
+ void (*state)(struct nvkm_ior *, struct nvkm_ior_state *);
+ void (*power)(struct nvkm_ior *, bool normal, bool pu,
+ bool data, bool vsync, bool hsync);
+ int (*sense)(struct nvkm_ior *, u32 loadval);
+ void (*clock)(struct nvkm_ior *);
+ 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);
+ } hdmi;
+
+ struct {
+ u8 lanes[4];
+ int (*links)(struct nvkm_ior *, struct nvkm_i2c_aux *);
+ void (*power)(struct nvkm_ior *, int nr);
+ void (*pattern)(struct nvkm_ior *, int pattern);
+ void (*drive)(struct nvkm_ior *, int ln, int pc,
+ int dc, int pe, int tx_pu);
+ void (*vcpi)(struct nvkm_ior *, int head, u8 slot,
+ u8 slot_nr, u16 pbn, u16 aligned);
+ void (*audio)(struct nvkm_ior *, int head, bool enable);
+ void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v);
+ void (*activesym)(struct nvkm_ior *, int head,
+ u8 TU, u8 VTUa, u8 VTUf, u8 VTUi);
+ void (*watermark)(struct nvkm_ior *, int head, u8 watermark);
+ } dp;
+
+ struct {
+ void (*hpd)(struct nvkm_ior *, int head, bool present);
+ void (*eld)(struct nvkm_ior *, u8 *data, u8 size);
+ } hda;
+};
+
+int nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *,
+ enum nvkm_ior_type type, int id);
+void nvkm_ior_del(struct nvkm_ior **);
+struct nvkm_ior *nvkm_ior_find(struct nvkm_disp *, enum nvkm_ior_type, int id);
+
+static inline u32
+nv50_ior_base(struct nvkm_ior *ior)
+{
+ return ior->id * 0x800;
+}
+
+void nv50_dac_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
+int nv50_dac_sense(struct nvkm_ior *, u32);
+
+void nv50_pior_depth(struct nvkm_ior *, struct nvkm_ior_state *, u32 ctrl);
+
+static inline u32
+nv50_sor_link(struct nvkm_ior *ior)
+{
+ return nv50_ior_base(ior) + ((ior->asy.link == 2) * 0x80);
+}
+
+int nv50_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id);
+void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
+void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
+void nv50_sor_clock(struct nvkm_ior *);
+
+void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
+int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
+void g94_sor_dp_power(struct nvkm_ior *, int);
+void g94_sor_dp_pattern(struct nvkm_ior *, int);
+void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
+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_dp_audio(struct nvkm_ior *, int, bool);
+
+int gf119_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id);
+void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
+void gf119_sor_clock(struct nvkm_ior *);
+int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
+void gf119_sor_dp_pattern(struct nvkm_ior *, int);
+void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
+void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
+void gf119_sor_dp_audio(struct nvkm_ior *, int, bool);
+void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
+void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8);
+
+void gm107_sor_dp_pattern(struct nvkm_ior *, int);
+
+void g84_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+void gt215_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
+
+void gt215_hda_hpd(struct nvkm_ior *, int, bool);
+void gt215_hda_eld(struct nvkm_ior *, u8 *, u8);
+
+void gf119_hda_hpd(struct nvkm_ior *, int, bool);
+void gf119_hda_eld(struct nvkm_ior *, u8 *, u8);
+
+#define IOR_MSG(i,l,f,a...) do { \
+ struct nvkm_ior *_ior = (i); \
+ nvkm_##l(&_ior->disp->engine.subdev, "%s: "f, _ior->name, ##a); \
+} while(0)
+#define IOR_WARN(i,f,a...) IOR_MSG((i), warn, f, ##a)
+#define IOR_DBG(i,f,a...) IOR_MSG((i), debug, f, ##a)
+
+int nv50_dac_new(struct nvkm_disp *, int);
+int gf119_dac_new(struct nvkm_disp *, int);
+
+int nv50_pior_new(struct nvkm_disp *, int);
+
+int nv50_sor_new(struct nvkm_disp *, int);
+int g84_sor_new(struct nvkm_disp *, int);
+int g94_sor_new(struct nvkm_disp *, int);
+int mcp77_sor_new(struct nvkm_disp *, int);
+int gt215_sor_new(struct nvkm_disp *, int);
+int mcp89_sor_new(struct nvkm_disp *, int);
+int gf119_sor_new(struct nvkm_disp *, int);
+int gk104_sor_new(struct nvkm_disp *, int);
+int gm107_sor_new(struct nvkm_disp *, int);
+int gm200_sor_new(struct nvkm_disp *, int);
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c
new file mode 100644
index 000000000000..d7e0fbb12bf1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "nv50.h"
+#include "head.h"
+#include "ior.h"
+#include "rootnv50.h"
+
+static const struct nv50_disp_func
+mcp77_disp = {
+ .intr = nv50_disp_intr,
+ .uevent = &nv50_disp_chan_uevent,
+ .super = nv50_disp_super,
+ .root = &g94_disp_root_oclass,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 4, .new = mcp77_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
+};
+
+int
+mcp77_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
+{
+ return nv50_disp_new_(&mcp77_disp, device, index, 2, pdisp);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c
new file mode 100644
index 000000000000..7b75c57c12ed
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "nv50.h"
+#include "head.h"
+#include "ior.h"
+#include "rootnv50.h"
+
+static const struct nv50_disp_func
+mcp89_disp = {
+ .intr = nv50_disp_intr,
+ .uevent = &nv50_disp_chan_uevent,
+ .super = nv50_disp_super,
+ .root = &gt215_disp_root_oclass,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 4, .new = mcp89_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
+};
+
+int
+mcp89_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
+{
+ return nv50_disp_new_(&mcp89_disp, device, index, 2, pdisp);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c
index 67254ce6f83f..b780ba1a3bc7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
#include "priv.h"
+#include "head.h"
static const struct nvkm_disp_oclass *
nv04_disp_root(struct nvkm_disp *disp)
@@ -30,20 +31,6 @@ nv04_disp_root(struct nvkm_disp *disp)
}
static void
-nv04_disp_vblank_init(struct nvkm_disp *disp, int head)
-{
- struct nvkm_device *device = disp->engine.subdev.device;
- nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000001);
-}
-
-static void
-nv04_disp_vblank_fini(struct nvkm_disp *disp, int head)
-{
- struct nvkm_device *device = disp->engine.subdev.device;
- nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000000);
-}
-
-static void
nv04_disp_intr(struct nvkm_disp *disp)
{
struct nvkm_subdev *subdev = &disp->engine.subdev;
@@ -74,12 +61,22 @@ static const struct nvkm_disp_func
nv04_disp = {
.intr = nv04_disp_intr,
.root = nv04_disp_root,
- .head.vblank_init = nv04_disp_vblank_init,
- .head.vblank_fini = nv04_disp_vblank_fini,
};
int
nv04_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
{
- return nvkm_disp_new_(&nv04_disp, device, index, 2, pdisp);
+ int ret, i;
+
+ ret = nvkm_disp_new_(&nv04_disp, device, index, pdisp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = nv04_head_new(*pdisp, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
index 0db8efbf1c2e..0c570dbd3021 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs
*/
#include "nv50.h"
+#include "head.h"
+#include "ior.h"
#include "rootnv50.h"
#include <core/client.h>
@@ -40,77 +42,6 @@ nv50_disp_root_(struct nvkm_disp *base)
return nv50_disp(base)->func->root;
}
-static int
-nv50_disp_outp_internal_crt_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb, struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- return disp->func->outp.internal.crt(base, index, dcb, poutp);
-}
-
-static int
-nv50_disp_outp_internal_tmds_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb,
- struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- return disp->func->outp.internal.tmds(base, index, dcb, poutp);
-}
-
-static int
-nv50_disp_outp_internal_lvds_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb,
- struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- return disp->func->outp.internal.lvds(base, index, dcb, poutp);
-}
-
-static int
-nv50_disp_outp_internal_dp_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb, struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- if (disp->func->outp.internal.dp)
- return disp->func->outp.internal.dp(base, index, dcb, poutp);
- return -ENODEV;
-}
-
-static int
-nv50_disp_outp_external_tmds_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb,
- struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- if (disp->func->outp.external.tmds)
- return disp->func->outp.external.tmds(base, index, dcb, poutp);
- return -ENODEV;
-}
-
-static int
-nv50_disp_outp_external_dp_(struct nvkm_disp *base, int index,
- struct dcb_output *dcb, struct nvkm_output **poutp)
-{
- struct nv50_disp *disp = nv50_disp(base);
- if (disp->func->outp.external.dp)
- return disp->func->outp.external.dp(base, index, dcb, poutp);
- return -ENODEV;
-}
-
-static void
-nv50_disp_vblank_fini_(struct nvkm_disp *base, int head)
-{
- struct nv50_disp *disp = nv50_disp(base);
- disp->func->head.vblank_fini(disp, head);
-}
-
-static void
-nv50_disp_vblank_init_(struct nvkm_disp *base, int head)
-{
- struct nv50_disp *disp = nv50_disp(base);
- disp->func->head.vblank_init(disp, head);
-}
-
static void
nv50_disp_intr_(struct nvkm_disp *base)
{
@@ -123,6 +54,8 @@ nv50_disp_dtor_(struct nvkm_disp *base)
{
struct nv50_disp *disp = nv50_disp(base);
nvkm_event_fini(&disp->uevent);
+ if (disp->wq)
+ destroy_workqueue(disp->wq);
return disp;
}
@@ -131,14 +64,6 @@ nv50_disp_ = {
.dtor = nv50_disp_dtor_,
.intr = nv50_disp_intr_,
.root = nv50_disp_root_,
- .outp.internal.crt = nv50_disp_outp_internal_crt_,
- .outp.internal.tmds = nv50_disp_outp_internal_tmds_,
- .outp.internal.lvds = nv50_disp_outp_internal_lvds_,
- .outp.internal.dp = nv50_disp_outp_internal_dp_,
- .outp.external.tmds = nv50_disp_outp_external_tmds_,
- .outp.external.dp = nv50_disp_outp_external_dp_,
- .head.vblank_init = nv50_disp_vblank_init_,
- .head.vblank_fini = nv50_disp_vblank_fini_,
};
int
@@ -146,517 +71,227 @@ nv50_disp_new_(const struct nv50_disp_func *func, struct nvkm_device *device,
int index, int heads, struct nvkm_disp **pdisp)
{
struct nv50_disp *disp;
- int ret;
+ int ret, i;
if (!(disp = kzalloc(sizeof(*disp), GFP_KERNEL)))
return -ENOMEM;
- INIT_WORK(&disp->supervisor, func->super);
disp->func = func;
*pdisp = &disp->base;
- ret = nvkm_disp_ctor(&nv50_disp_, device, index, heads, &disp->base);
+ ret = nvkm_disp_ctor(&nv50_disp_, device, index, &disp->base);
if (ret)
return ret;
- return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent);
-}
-
-void
-nv50_disp_vblank_fini(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- nvkm_mask(device, 0x61002c, (4 << head), 0);
-}
-
-void
-nv50_disp_vblank_init(struct nv50_disp *disp, int head)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- nvkm_mask(device, 0x61002c, (4 << head), (4 << head));
-}
-
-static const struct nvkm_enum
-nv50_disp_intr_error_type[] = {
- { 3, "ILLEGAL_MTHD" },
- { 4, "INVALID_VALUE" },
- { 5, "INVALID_STATE" },
- { 7, "INVALID_HANDLE" },
- {}
-};
-
-static const struct nvkm_enum
-nv50_disp_intr_error_code[] = {
- { 0x00, "" },
- {}
-};
-
-static void
-nv50_disp_intr_error(struct nv50_disp *disp, int chid)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08));
- u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08));
- u32 code = (addr & 0x00ff0000) >> 16;
- u32 type = (addr & 0x00007000) >> 12;
- u32 mthd = (addr & 0x00000ffc);
- const struct nvkm_enum *ec, *et;
-
- et = nvkm_enum_find(nv50_disp_intr_error_type, type);
- ec = nvkm_enum_find(nv50_disp_intr_error_code, code);
-
- nvkm_error(subdev,
- "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",
- type, et ? et->name : "", code, ec ? ec->name : "",
- chid, mthd, data);
-
- if (chid < ARRAY_SIZE(disp->chan)) {
- switch (mthd) {
- case 0x0080:
- nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);
- break;
- default:
- break;
- }
- }
-
- nvkm_wr32(device, 0x610020, 0x00010000 << chid);
- nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000);
-}
-
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
- u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
- struct nvbios_outp *info)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_bios *bios = subdev->device->bios;
- struct nvkm_output *outp;
- u16 mask, type;
-
- if (or < 4) {
- type = DCB_OUTPUT_ANALOG;
- mask = 0;
- } else
- if (or < 8) {
- switch (ctrl & 0x00000f00) {
- case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
- case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
- case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
- case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
- case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
- case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
- default:
- nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
- return NULL;
- }
- or -= 4;
- } else {
- or = or - 8;
- type = 0x0010;
- mask = 0;
- switch (ctrl & 0x00000f00) {
- case 0x00000000: type |= disp->pior.type[or]; break;
- default:
- nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl);
- return NULL;
- }
- }
+ disp->wq = create_singlethread_workqueue("nvkm-disp");
+ if (!disp->wq)
+ return -ENOMEM;
+ INIT_WORK(&disp->supervisor, func->super);
- mask = 0x00c0 & (mask << 6);
- mask |= 0x0001 << or;
- mask |= 0x0100 << head;
-
- list_for_each_entry(outp, &disp->base.outp, head) {
- if ((outp->info.hasht & 0xff) == type &&
- (outp->info.hashm & mask) == mask) {
- *data = nvbios_outp_match(bios, outp->info.hasht, mask,
- ver, hdr, cnt, len, info);
- if (!*data)
- return NULL;
- return outp;
- }
+ for (i = 0; func->head.new && i < heads; i++) {
+ ret = func->head.new(&disp->base, i);
+ if (ret)
+ return ret;
}
- return NULL;
-}
-
-static struct nvkm_output *
-exec_script(struct nv50_disp *disp, int head, int id)
-{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_bios *bios = device->bios;
- struct nvkm_output *outp;
- struct nvbios_outp info;
- u8 ver, hdr, cnt, len;
- u32 data, ctrl = 0;
- u32 reg;
- int i;
-
- /* DAC */
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++)
- ctrl = nvkm_rd32(device, 0x610b5c + (i * 8));
-
- /* SOR */
- if (!(ctrl & (1 << head))) {
- if (device->chipset < 0x90 ||
- device->chipset == 0x92 ||
- device->chipset == 0xa0) {
- reg = 0x610b74;
- } else {
- reg = 0x610798;
- }
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++)
- ctrl = nvkm_rd32(device, reg + (i * 8));
- i += 4;
+ for (i = 0; func->dac.new && i < func->dac.nr; i++) {
+ ret = func->dac.new(&disp->base, i);
+ if (ret)
+ return ret;
}
- /* PIOR */
- if (!(ctrl & (1 << head))) {
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++)
- ctrl = nvkm_rd32(device, 0x610b84 + (i * 8));
- i += 8;
+ for (i = 0; func->pior.new && i < func->pior.nr; i++) {
+ ret = func->pior.new(&disp->base, i);
+ if (ret)
+ return ret;
}
- if (!(ctrl & (1 << head)))
- return NULL;
- i--;
-
- outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
- if (outp) {
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = bios,
- .offset = info.script[id],
- .outp = &outp->info,
- .crtc = head,
- .execute = 1,
- };
-
- nvbios_exec(&init);
+ for (i = 0; func->sor.new && i < func->sor.nr; i++) {
+ ret = func->sor.new(&disp->base, i);
+ if (ret)
+ return ret;
}
- return outp;
+ return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent);
}
-static struct nvkm_output *
-exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
+static u32
+nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_outp *iedt)
{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- struct nvkm_bios *bios = device->bios;
- struct nvkm_output *outp;
- struct nvbios_outp info1;
- struct nvbios_ocfg info2;
- u8 ver, hdr, cnt, len;
- u32 data, ctrl = 0;
- u32 reg;
- int i;
-
- /* DAC */
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++)
- ctrl = nvkm_rd32(device, 0x610b58 + (i * 8));
-
- /* SOR */
- if (!(ctrl & (1 << head))) {
- if (device->chipset < 0x90 ||
- device->chipset == 0x92 ||
- device->chipset == 0xa0) {
- reg = 0x610b70;
- } else {
- reg = 0x610794;
- }
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++)
- ctrl = nvkm_rd32(device, reg + (i * 8));
- i += 4;
- }
-
- /* PIOR */
- if (!(ctrl & (1 << head))) {
- for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++)
- ctrl = nvkm_rd32(device, 0x610b80 + (i * 8));
- i += 8;
- }
-
- if (!(ctrl & (1 << head)))
- return NULL;
- i--;
-
- outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
- if (!outp)
- return NULL;
-
- *conf = (ctrl & 0x00000f00) >> 8;
- if (outp->info.location == 0) {
- switch (outp->info.type) {
- case DCB_OUTPUT_TMDS:
- if (*conf == 5)
- *conf |= 0x0100;
- break;
- case DCB_OUTPUT_LVDS:
- *conf |= disp->sor.lvdsconf;
- break;
- default:
- break;
- }
- } else {
- *conf = (ctrl & 0x00000f00) >> 8;
- pclk = pclk / 2;
- }
-
- data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
- &ver, &hdr, &cnt, &len, &info2);
- if (data && id < 0xff) {
- data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
- if (data) {
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = bios,
- .offset = data,
- .outp = &outp->info,
- .crtc = head,
- .execute = 1,
- };
-
- nvbios_exec(&init);
- }
- }
-
- return outp;
+ struct nvkm_bios *bios = head->disp->engine.subdev.device->bios;
+ const u8 l = ffs(outp->info.link);
+ const u16 t = outp->info.hasht;
+ const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or;
+ u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt);
+ if (!data)
+ OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m);
+ return data;
}
-static bool
-nv50_disp_dptmds_war(struct nvkm_device *device)
+static void
+nv50_disp_super_ied_on(struct nvkm_head *head,
+ struct nvkm_ior *ior, int id, u32 khz)
{
- switch (device->chipset) {
- case 0x94:
- case 0x96:
- case 0x98:
- return true;
- default:
- break;
+ struct nvkm_subdev *subdev = &head->disp->engine.subdev;
+ struct nvkm_bios *bios = subdev->device->bios;
+ struct nvkm_outp *outp = ior->asy.outp;
+ struct nvbios_ocfg iedtrs;
+ struct nvbios_outp iedt;
+ u8 ver, hdr, cnt, len, flags = 0x00;
+ u32 data;
+
+ if (!outp) {
+ IOR_DBG(ior, "nothing to attach");
+ return;
}
- return false;
-}
-static bool
-nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 soff = __ffs(outp->or) * 0x800;
- if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) {
- switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
- case 0x00000000:
- case 0x00030000:
- return true;
- default:
- break;
+ /* Lookup IED table for the device. */
+ data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
+ if (!data)
+ return;
+
+ /* Lookup IEDT runtime settings for the current configuration. */
+ if (ior->type == SOR) {
+ if (ior->asy.proto == LVDS) {
+ if (head->asy.or.depth == 24)
+ flags |= 0x02;
}
+ if (ior->asy.link == 3)
+ flags |= 0x01;
}
- return false;
-
-}
-
-static void
-nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 soff = __ffs(outp->or) * 0x800;
- if (!nv50_disp_dptmds_war_needed(disp, outp))
+ data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags,
+ &ver, &hdr, &cnt, &len, &iedtrs);
+ if (!data) {
+ OUTP_DBG(outp, "missing IEDT RS for %02x:%02x",
+ ior->asy.proto_evo, flags);
return;
-
- nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
- nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
- nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);
-
- nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
- nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
- nvkm_usec(device, 400, NVKM_DELAY);
- nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
- nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);
-
- if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
- u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
- u32 pu_pc = seqctl & 0x0000000f;
- nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
}
-}
-static void
-nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 soff = __ffs(outp->or) * 0x800;
- u32 sorpwr;
-
- if (!nv50_disp_dptmds_war_needed(disp, outp))
+ /* Execute the OnInt[23] script for the current frequency. */
+ data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz);
+ if (!data) {
+ OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz",
+ id, ior->asy.proto_evo, flags, khz);
return;
-
- sorpwr = nvkm_rd32(device, 0x61c004 + soff);
- if (sorpwr & 0x00000001) {
- u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
- u32 pd_pc = (seqctl & 0x00000f00) >> 8;
- u32 pu_pc = seqctl & 0x0000000f;
-
- nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);
-
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
- break;
- );
- nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
- break;
- );
-
- nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
- nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
}
- nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
- nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);
-
- if (sorpwr & 0x00000001) {
- nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
- }
+ nvbios_init(subdev, data,
+ init.outp = &outp->info;
+ init.or = ior->id;
+ init.link = ior->asy.link;
+ init.head = head->id;
+ );
}
static void
-nv50_disp_update_sppll1(struct nv50_disp *disp)
+nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- bool used = false;
- int sor;
+ struct nvkm_outp *outp = ior->arm.outp;
+ struct nvbios_outp iedt;
+ u8 ver, hdr, cnt, len;
+ u32 data;
- if (!nv50_disp_dptmds_war(device))
+ if (!outp) {
+ IOR_DBG(ior, "nothing attached");
return;
-
- for (sor = 0; sor < disp->func->sor.nr; sor++) {
- u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
- switch (clksor & 0x03000000) {
- case 0x02000000:
- case 0x03000000:
- used = true;
- break;
- default:
- break;
- }
}
- if (used)
+ data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
+ if (!data)
return;
- nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
+ nvbios_init(&head->disp->engine.subdev, iedt.script[id],
+ init.outp = &outp->info;
+ init.or = ior->id;
+ init.link = ior->arm.link;
+ init.head = head->id;
+ );
}
-static void
-nv50_disp_intr_unk10_0(struct nv50_disp *disp, int head)
+static struct nvkm_ior *
+nv50_disp_super_ior_asy(struct nvkm_head *head)
{
- exec_script(disp, head, 1);
+ struct nvkm_ior *ior;
+ list_for_each_entry(ior, &head->disp->ior, head) {
+ if (ior->asy.head & (1 << head->id)) {
+ HEAD_DBG(head, "to %s", ior->name);
+ return ior;
+ }
+ }
+ HEAD_DBG(head, "nothing to attach");
+ return NULL;
}
-static void
-nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head)
+static struct nvkm_ior *
+nv50_disp_super_ior_arm(struct nvkm_head *head)
{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_output *outp = exec_script(disp, head, 2);
-
- /* the binary driver does this outside of the supervisor handling
- * (after the third supervisor from a detach). we (currently?)
- * allow both detach/attach to happen in the same set of
- * supervisor interrupts, so it would make sense to execute this
- * (full power down?) script after all the detach phases of the
- * supervisor handling. like with training if needed from the
- * second supervisor, nvidia doesn't do this, so who knows if it's
- * entirely safe, but it does appear to work..
- *
- * without this script being run, on some configurations i've
- * seen, switching from DP to TMDS on a DP connector may result
- * in a blank screen (SOR_PWR off/on can restore it)
- */
- if (outp && outp->info.type == DCB_OUTPUT_DP) {
- struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
- struct nvbios_init init = {
- .subdev = subdev,
- .bios = subdev->device->bios,
- .outp = &outp->info,
- .crtc = head,
- .offset = outpdp->info.script[4],
- .execute = 1,
- };
-
- nvkm_notify_put(&outpdp->irq);
- nvbios_exec(&init);
- atomic_set(&outpdp->lt.done, 0);
+ struct nvkm_ior *ior;
+ list_for_each_entry(ior, &head->disp->ior, head) {
+ if (ior->arm.head & (1 << head->id)) {
+ HEAD_DBG(head, "on %s", ior->name);
+ return ior;
+ }
}
+ HEAD_DBG(head, "nothing attached");
+ return NULL;
}
-static void
-nv50_disp_intr_unk20_1(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_devinit *devinit = device->devinit;
- u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- if (pclk)
- nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk);
+ struct nvkm_ior *ior;
+
+ /* Determine which OR, if any, we're attaching to the head. */
+ HEAD_DBG(head, "supervisor 3.0");
+ ior = nv50_disp_super_ior_asy(head);
+ if (!ior)
+ return;
+
+ /* Execute OnInt3 IED script. */
+ nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000);
+
+ /* OR-specific handling. */
+ if (ior->func->war_3)
+ ior->func->war_3(ior);
}
static void
-nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
- struct dcb_output *outp, u32 pclk)
+nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior)
{
- struct nvkm_subdev *subdev = &disp->base.engine.subdev;
- struct nvkm_device *device = subdev->device;
- const int link = !(outp->sorconf.link & 1);
- const int or = ffs(outp->or) - 1;
- const u32 soff = ( or * 0x800);
- const u32 loff = (link * 0x080) + soff;
- const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8));
- const u32 symbol = 100000;
- const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff;
- const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff;
- const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff;
- u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
- u32 clksor = nvkm_rd32(device, 0x614300 + soff);
+ struct nvkm_subdev *subdev = &head->disp->engine.subdev;
+ const u32 khz = head->asy.hz / 1000;
+ const u32 linkKBps = ior->dp.bw * 27000;
+ const u32 symbol = 100000;
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
int TU, VTUi, VTUf, VTUa;
u64 link_data_rate, link_ratio, unk;
u32 best_diff = 64 * symbol;
- u32 link_nr, link_bw, bits;
- u64 value;
-
- link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
- link_nr = hweight32(dpctrl & 0x000f0000);
+ u64 h, v;
/* symbols/hblank - algorithm taken from comments in tegra driver */
- value = vblanke + vactive - vblanks - 7;
- value = value * link_bw;
- do_div(value, pclk);
- value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
- nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value);
+ h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7;
+ h = h * linkKBps;
+ do_div(h, khz);
+ h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr);
/* symbols/vblank - algorithm taken from comments in tegra driver */
- value = vblanks - vblanke - 25;
- value = value * link_bw;
- do_div(value, pclk);
- value = value - ((36 / link_nr) + 3) - 1;
- nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value);
+ v = head->asy.vblanks - head->asy.vblanke - 25;
+ v = v * linkKBps;
+ do_div(v, khz);
+ v = v - ((36 / ior->dp.nr) + 3) - 1;
- /* watermark / activesym */
- if ((ctrl & 0xf0000) == 0x60000) bits = 30;
- else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
- else bits = 18;
+ ior->func->dp.audio_sym(ior, head->id, h, v);
- link_data_rate = (pclk * bits / 8) / link_nr;
+ /* watermark / activesym */
+ link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr;
/* calculate ratio of packed data rate to link symbol rate */
link_ratio = link_data_rate * symbol;
- do_div(link_ratio, link_bw);
+ do_div(link_ratio, linkKBps);
- for (TU = 64; TU >= 32; TU--) {
+ for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) {
/* calculate average number of valid symbols in each TU */
u32 tu_valid = link_ratio * TU;
u32 calc, diff;
@@ -707,9 +342,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
}
}
- if (!bestTU) {
- nvkm_error(subdev, "unable to find suitable dp config\n");
- return;
+ if (ior->func->dp.activesym) {
+ if (!bestTU) {
+ nvkm_error(subdev, "unable to determine dp config\n");
+ return;
+ }
+ ior->func->dp.activesym(ior, head->id, bestTU,
+ bestVTUa, bestVTUf, bestVTUi);
+ } else {
+ bestTU = 64;
}
/* XXX close to vbios numbers, but not right */
@@ -719,190 +360,223 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
do_div(unk, symbol);
unk += 6;
- nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2);
- nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
- bestVTUf << 16 |
- bestVTUi << 8 | unk);
+ ior->func->dp.watermark(ior, head->id, unk);
}
-static void
-nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_2_2(struct nv50_disp *disp, struct nvkm_head *head)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_output *outp;
- u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- u32 hval, hreg = 0x614200 + (head * 0x800);
- u32 oval, oreg;
- u32 mask, conf;
-
- outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
- if (!outp)
+ const u32 khz = head->asy.hz / 1000;
+ struct nvkm_outp *outp;
+ struct nvkm_ior *ior;
+
+ /* Determine which OR, if any, we're attaching from the head. */
+ HEAD_DBG(head, "supervisor 2.2");
+ ior = nv50_disp_super_ior_asy(head);
+ if (!ior)
return;
- /* we allow both encoder attach and detach operations to occur
- * within a single supervisor (ie. modeset) sequence. the
- * encoder detach scripts quite often switch off power to the
- * lanes, which requires the link to be re-trained.
- *
- * this is not generally an issue as the sink "must" (heh)
- * signal an irq when it's lost sync so the driver can
- * re-train.
+ /* For some reason, NVIDIA decided not to:
*
- * however, on some boards, if one does not configure at least
- * the gpu side of the link *before* attaching, then various
- * things can go horribly wrong (PDISP disappearing from mmio,
- * third supervisor never happens, etc).
+ * A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
+ * and
+ * B) Use SetControlOutputResource.PixelDepth on LVDS.
*
- * the solution is simply to retrain here, if necessary. last
- * i checked, the binary driver userspace does not appear to
- * trigger this situation (it forces an UPDATE between steps).
+ * Override the values we usually read from HW with the same
+ * data we pass though an ioctl instead.
*/
- if (outp->info.type == DCB_OUTPUT_DP) {
- u32 soff = (ffs(outp->info.or) - 1) * 0x08;
- u32 ctrl, datarate;
-
- if (outp->info.location == 0) {
- ctrl = nvkm_rd32(device, 0x610794 + soff);
- soff = 1;
- } else {
- ctrl = nvkm_rd32(device, 0x610b80 + soff);
- soff = 2;
- }
+ 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;
+ }
- switch ((ctrl & 0x000f0000) >> 16) {
- case 6: datarate = pclk * 30; break;
- case 5: datarate = pclk * 24; break;
- case 2:
- default:
- datarate = pclk * 18;
- break;
- }
+ /* Handle any link training, etc. */
+ if ((outp = ior->asy.outp) && outp->func->acquire)
+ outp->func->acquire(outp);
- if (nvkm_output_dp_train(outp, datarate / soff))
- OUTP_ERR(outp, "link not trained before attach");
- }
+ /* Execute OnInt2 IED script. */
+ nv50_disp_super_ied_on(head, ior, 0, khz);
- exec_clkcmp(disp, head, 0, pclk, &conf);
+ /* Program RG clock divider. */
+ head->func->rgclk(head, ior->asy.rgdiv);
- if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
- oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
- oval = 0x00000000;
- hval = 0x00000000;
- mask = 0xffffffff;
- } else
- if (!outp->info.location) {
- if (outp->info.type == DCB_OUTPUT_DP)
- nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk);
- oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
- oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
- hval = 0x00000000;
- mask = 0x00000707;
- } else {
- oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
- oval = 0x00000001;
- hval = 0x00000001;
- mask = 0x00000707;
- }
+ /* Mode-specific internal DP configuration. */
+ if (ior->type == SOR && ior->asy.proto == DP)
+ nv50_disp_super_2_2_dp(head, ior);
- nvkm_mask(device, hreg, 0x0000000f, hval);
- nvkm_mask(device, oreg, mask, oval);
+ /* OR-specific handling. */
+ ior->func->clock(ior);
+ if (ior->func->war_2)
+ ior->func->war_2(ior);
+}
- nv50_disp_dptmds_war_2(disp, &outp->info);
+void
+nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head)
+{
+ struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit;
+ const u32 khz = head->asy.hz / 1000;
+ HEAD_DBG(head, "supervisor 2.1 - %d khz", khz);
+ if (khz)
+ nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz);
}
-/* If programming a TMDS output on a SOR that can also be configured for
- * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
- *
- * It looks like the VBIOS TMDS scripts make an attempt at this, however,
- * the VBIOS scripts on at least one board I have only switch it off on
- * link 0, causing a blank display if the output has previously been
- * programmed for DisplayPort.
- */
-static void
-nv50_disp_intr_unk40_0_tmds(struct nv50_disp *disp,
- struct dcb_output *outp)
+void
+nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_bios *bios = device->bios;
- const int link = !(outp->sorconf.link & 1);
- const int or = ffs(outp->or) - 1;
- const u32 loff = (or * 0x800) + (link * 0x80);
- const u16 mask = (outp->sorconf.link << 6) | outp->or;
- struct dcb_output match;
- u8 ver, hdr;
-
- if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match))
- nvkm_mask(device, 0x61c10c + loff, 0x00000001, 0x00000000);
+ struct nvkm_outp *outp;
+ struct nvkm_ior *ior;
+
+ /* Determine which OR, if any, we're detaching from the head. */
+ HEAD_DBG(head, "supervisor 2.0");
+ ior = nv50_disp_super_ior_arm(head);
+ if (!ior)
+ return;
+
+ /* Execute OffInt2 IED script. */
+ nv50_disp_super_ied_off(head, ior, 2);
+
+ /* If we're shutting down the OR's only active head, execute
+ * the output path's release function.
+ */
+ if (ior->arm.head == (1 << head->id)) {
+ if ((outp = ior->arm.outp) && outp->func->release)
+ outp->func->release(outp, ior);
+ }
}
-static void
-nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_1_0(struct nv50_disp *disp, struct nvkm_head *head)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- struct nvkm_output *outp;
- u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- u32 conf;
+ struct nvkm_ior *ior;
- outp = exec_clkcmp(disp, head, 1, pclk, &conf);
- if (!outp)
+ /* Determine which OR, if any, we're detaching from the head. */
+ HEAD_DBG(head, "supervisor 1.0");
+ ior = nv50_disp_super_ior_arm(head);
+ if (!ior)
return;
- if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS)
- nv50_disp_intr_unk40_0_tmds(disp, &outp->info);
- nv50_disp_dptmds_war_3(disp, &outp->info);
+ /* Execute OffInt1 IED script. */
+ nv50_disp_super_ied_off(head, ior, 1);
}
void
-nv50_disp_intr_supervisor(struct work_struct *work)
+nv50_disp_super_1(struct nv50_disp *disp)
+{
+ struct nvkm_head *head;
+ struct nvkm_ior *ior;
+
+ list_for_each_entry(head, &disp->base.head, head) {
+ head->func->state(head, &head->arm);
+ head->func->state(head, &head->asy);
+ }
+
+ list_for_each_entry(ior, &disp->base.ior, head) {
+ ior->func->state(ior, &ior->arm);
+ ior->func->state(ior, &ior->asy);
+ }
+}
+
+void
+nv50_disp_super(struct work_struct *work)
{
struct nv50_disp *disp =
container_of(work, struct nv50_disp, supervisor);
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
struct nvkm_device *device = subdev->device;
+ struct nvkm_head *head;
u32 super = nvkm_rd32(device, 0x610030);
- int head;
nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super);
if (disp->super & 0x00000010) {
nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(super & (0x00000020 << head)))
+ nv50_disp_super_1(disp);
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(super & (0x00000020 << head->id)))
continue;
- if (!(super & (0x00000080 << head)))
+ if (!(super & (0x00000080 << head->id)))
continue;
- nv50_disp_intr_unk10_0(disp, head);
+ nv50_disp_super_1_0(disp, head);
}
} else
if (disp->super & 0x00000020) {
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(super & (0x00000080 << head)))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(super & (0x00000080 << head->id)))
continue;
- nv50_disp_intr_unk20_0(disp, head);
+ nv50_disp_super_2_0(disp, head);
}
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(super & (0x00000200 << head)))
+ nvkm_outp_route(&disp->base);
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(super & (0x00000200 << head->id)))
continue;
- nv50_disp_intr_unk20_1(disp, head);
+ nv50_disp_super_2_1(disp, head);
}
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(super & (0x00000080 << head)))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(super & (0x00000080 << head->id)))
continue;
- nv50_disp_intr_unk20_2(disp, head);
+ nv50_disp_super_2_2(disp, head);
}
} else
if (disp->super & 0x00000040) {
- for (head = 0; head < disp->base.head.nr; head++) {
- if (!(super & (0x00000080 << head)))
+ list_for_each_entry(head, &disp->base.head, head) {
+ if (!(super & (0x00000080 << head->id)))
continue;
- nv50_disp_intr_unk40_0(disp, head);
+ nv50_disp_super_3_0(disp, head);
}
- nv50_disp_update_sppll1(disp);
}
nvkm_wr32(device, 0x610030, 0x80000000);
}
+static const struct nvkm_enum
+nv50_disp_intr_error_type[] = {
+ { 3, "ILLEGAL_MTHD" },
+ { 4, "INVALID_VALUE" },
+ { 5, "INVALID_STATE" },
+ { 7, "INVALID_HANDLE" },
+ {}
+};
+
+static const struct nvkm_enum
+nv50_disp_intr_error_code[] = {
+ { 0x00, "" },
+ {}
+};
+
+static void
+nv50_disp_intr_error(struct nv50_disp *disp, int chid)
+{
+ struct nvkm_subdev *subdev = &disp->base.engine.subdev;
+ struct nvkm_device *device = subdev->device;
+ u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08));
+ u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08));
+ u32 code = (addr & 0x00ff0000) >> 16;
+ u32 type = (addr & 0x00007000) >> 12;
+ u32 mthd = (addr & 0x00000ffc);
+ const struct nvkm_enum *ec, *et;
+
+ et = nvkm_enum_find(nv50_disp_intr_error_type, type);
+ ec = nvkm_enum_find(nv50_disp_intr_error_code, code);
+
+ nvkm_error(subdev,
+ "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",
+ type, et ? et->name : "", code, ec ? ec->name : "",
+ chid, mthd, data);
+
+ if (chid < ARRAY_SIZE(disp->chan)) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);
+ break;
+ default:
+ break;
+ }
+ }
+
+ nvkm_wr32(device, 0x610020, 0x00010000 << chid);
+ nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000);
+}
+
void
nv50_disp_intr(struct nv50_disp *disp)
{
@@ -934,7 +608,7 @@ nv50_disp_intr(struct nv50_disp *disp)
if (intr1 & 0x00000070) {
disp->super = (intr1 & 0x00000070);
- schedule_work(&disp->supervisor);
+ queue_work(disp->wq, &disp->supervisor);
nvkm_wr32(device, 0x610024, disp->super);
}
}
@@ -943,23 +617,12 @@ static const struct nv50_disp_func
nv50_disp = {
.intr = nv50_disp_intr,
.uevent = &nv50_disp_chan_uevent,
- .super = nv50_disp_intr_supervisor,
+ .super = nv50_disp_super,
.root = &nv50_disp_root_oclass,
- .head.vblank_init = nv50_disp_vblank_init,
- .head.vblank_fini = nv50_disp_vblank_fini,
- .head.scanoutpos = nv50_disp_root_scanoutpos,
- .outp.internal.crt = nv50_dac_output_new,
- .outp.internal.tmds = nv50_sor_output_new,
- .outp.internal.lvds = nv50_sor_output_new,
- .outp.external.tmds = nv50_pior_output_new,
- .outp.external.dp = nv50_pior_dp_new,
- .dac.nr = 3,
- .dac.power = nv50_dac_power,
- .dac.sense = nv50_dac_sense,
- .sor.nr = 2,
- .sor.power = nv50_sor_power,
- .pior.nr = 3,
- .pior.power = nv50_pior_power,
+ .head.new = nv50_head_new,
+ .dac = { .nr = 3, .new = nv50_dac_new },
+ .sor = { .nr = 2, .new = nv50_sor_new },
+ .pior = { .nr = 3, .new = nv50_pior_new },
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
index 1e1de6bfe85a..19c635663399 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
@@ -2,18 +2,13 @@
#define __NV50_DISP_H__
#define nv50_disp(p) container_of((p), struct nv50_disp, base)
#include "priv.h"
-struct nvkm_output;
-struct nvkm_output_dp;
-
-#define NV50_DISP_MTHD_ struct nvkm_object *object, \
- struct nv50_disp *disp, void *data, u32 size
-#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head
-#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp
+struct nvkm_head;
struct nv50_disp {
const struct nv50_disp_func *func;
struct nvkm_disp base;
+ struct workqueue_struct *wq;
struct work_struct supervisor;
u32 super;
@@ -30,42 +25,18 @@ struct nv50_disp {
struct nv50_disp_chan *chan[17];
};
-int nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0);
-
-int gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0);
-
-int nv50_dac_power(NV50_DISP_MTHD_V1);
-int nv50_dac_sense(NV50_DISP_MTHD_V1);
-
-int gt215_hda_eld(NV50_DISP_MTHD_V1);
-int gf119_hda_eld(NV50_DISP_MTHD_V1);
-
-int g84_hdmi_ctrl(NV50_DISP_MTHD_V1);
-int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1);
-int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1);
-int gk104_hdmi_ctrl(NV50_DISP_MTHD_V1);
-
-int nv50_sor_power(NV50_DISP_MTHD_V1);
-int nv50_pior_power(NV50_DISP_MTHD_V1);
+void nv50_disp_super_1(struct nv50_disp *);
+void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_3_0(struct nv50_disp *, struct nvkm_head *);
int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
int index, int heads, struct nvkm_disp **);
int gf119_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
int index, struct nvkm_disp **);
-struct nv50_disp_func_outp {
- int (* crt)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (* tv)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (* dp)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
-};
-
struct nv50_disp_func {
void (*intr)(struct nv50_disp *);
void (*intr_error)(struct nv50_disp *, int chid);
@@ -76,44 +47,33 @@ struct nv50_disp_func {
const struct nvkm_disp_oclass *root;
struct {
- void (*vblank_init)(struct nv50_disp *, int head);
- void (*vblank_fini)(struct nv50_disp *, int head);
- int (*scanoutpos)(NV50_DISP_MTHD_V0);
+ int (*new)(struct nvkm_disp *, int id);
} head;
struct {
- const struct nv50_disp_func_outp internal;
- const struct nv50_disp_func_outp external;
- } outp;
-
- struct {
int nr;
- int (*power)(NV50_DISP_MTHD_V1);
- int (*sense)(NV50_DISP_MTHD_V1);
+ int (*new)(struct nvkm_disp *, int id);
} dac;
struct {
int nr;
- int (*power)(NV50_DISP_MTHD_V1);
- int (*hda_eld)(NV50_DISP_MTHD_V1);
- int (*hdmi)(NV50_DISP_MTHD_V1);
- void (*magic)(struct nvkm_output *);
+ int (*new)(struct nvkm_disp *, int id);
} sor;
struct {
int nr;
- int (*power)(NV50_DISP_MTHD_V1);
+ int (*new)(struct nvkm_disp *, int id);
} pior;
};
-void nv50_disp_vblank_init(struct nv50_disp *, int);
-void nv50_disp_vblank_fini(struct nv50_disp *, int);
void nv50_disp_intr(struct nv50_disp *);
-void nv50_disp_intr_supervisor(struct work_struct *);
+void nv50_disp_super(struct work_struct *);
-void gf119_disp_vblank_init(struct nv50_disp *, int);
-void gf119_disp_vblank_fini(struct nv50_disp *, int);
void gf119_disp_intr(struct nv50_disp *);
-void gf119_disp_intr_supervisor(struct work_struct *);
+void gf119_disp_super(struct work_struct *);
void gf119_disp_intr_error(struct nv50_disp *, int);
+
+void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *);
+void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *);
+void nv50_disp_update_sppll1(struct nv50_disp *);
#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c
index 07540f3d32dc..f3b0fa2c5924 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
#include "channv50.h"
+#include "head.h"
#include "rootnv50.h"
#include <core/client.h>
@@ -48,7 +49,7 @@ nv50_disp_oimm_new(const struct nv50_disp_chan_func *func,
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(parent, "create disp overlay vers %d head %d\n",
args->v0.version, args->v0.head);
- if (args->v0.head > disp->base.head.nr)
+ if (!nvkm_head_find(&disp->base, args->v0.head))
return -EINVAL;
head = args->v0.head;
} else
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
index bbe5ec0dedb2..85aff85394ac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
@@ -22,29 +22,207 @@
* Authors: Ben Skeggs
*/
#include "outp.h"
+#include "ior.h"
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/i2c.h>
void
-nvkm_output_fini(struct nvkm_output *outp)
+nvkm_outp_route(struct nvkm_disp *disp)
+{
+ struct nvkm_outp *outp;
+ struct nvkm_ior *ior;
+
+ list_for_each_entry(ior, &disp->ior, head) {
+ if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) {
+ OUTP_DBG(outp, "release %s", ior->name);
+ if (ior->func->route.set)
+ ior->func->route.set(outp, NULL);
+ ior->arm.outp = NULL;
+ }
+ }
+
+ list_for_each_entry(ior, &disp->ior, head) {
+ if ((outp = ior->asy.outp)) {
+ OUTP_DBG(outp, "acquire %s", ior->name);
+ if (ior->asy.outp != ior->arm.outp) {
+ if (ior->func->route.set)
+ ior->func->route.set(outp, ior);
+ ior->arm.outp = ior->asy.outp;
+ }
+ }
+ }
+}
+
+static enum nvkm_ior_proto
+nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type)
+{
+ switch (outp->info.location) {
+ case 0:
+ switch (outp->info.type) {
+ case DCB_OUTPUT_ANALOG: *type = DAC; return CRT;
+ case DCB_OUTPUT_TMDS : *type = SOR; return TMDS;
+ case DCB_OUTPUT_LVDS : *type = SOR; return LVDS;
+ case DCB_OUTPUT_DP : *type = SOR; return DP;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ switch (outp->info.type) {
+ case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS;
+ case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ WARN_ON(1);
+ return UNKNOWN;
+}
+
+void
+nvkm_outp_release(struct nvkm_outp *outp, u8 user)
+{
+ struct nvkm_ior *ior = outp->ior;
+ OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior);
+ if (ior) {
+ outp->acquired &= ~user;
+ if (!outp->acquired) {
+ outp->ior->asy.outp = NULL;
+ outp->ior = NULL;
+ }
+ }
+}
+
+static inline int
+nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
+{
+ outp->ior = ior;
+ outp->ior->asy.outp = outp;
+ outp->ior->asy.link = outp->info.sorconf.link;
+ outp->acquired |= user;
+ return 0;
+}
+
+int
+nvkm_outp_acquire(struct nvkm_outp *outp, u8 user)
+{
+ struct nvkm_ior *ior = outp->ior;
+ enum nvkm_ior_proto proto;
+ enum nvkm_ior_type type;
+
+ OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior);
+ if (ior) {
+ outp->acquired |= user;
+ return 0;
+ }
+
+ /* Lookup a compatible, and unused, OR to assign to the device. */
+ proto = nvkm_outp_xlat(outp, &type);
+ if (proto == UNKNOWN)
+ return -ENOSYS;
+
+ /* First preference is to reuse the OR that is currently armed
+ * on HW, if any, in order to prevent unnecessary switching.
+ */
+ list_for_each_entry(ior, &outp->disp->ior, head) {
+ if (!ior->asy.outp && ior->arm.outp == outp)
+ return nvkm_outp_acquire_ior(outp, user, ior);
+ }
+
+ /* Failing that, a completely unused OR is the next best thing. */
+ list_for_each_entry(ior, &outp->disp->ior, head) {
+ if (!ior->asy.outp && ior->type == type && !ior->arm.outp &&
+ (ior->func->route.set || ior->id == __ffs(outp->info.or)))
+ return nvkm_outp_acquire_ior(outp, user, ior);
+ }
+
+ /* Last resort is to assign an OR that's already active on HW,
+ * but will be released during the next modeset.
+ */
+ list_for_each_entry(ior, &outp->disp->ior, head) {
+ if (!ior->asy.outp && ior->type == type &&
+ (ior->func->route.set || ior->id == __ffs(outp->info.or)))
+ return nvkm_outp_acquire_ior(outp, user, ior);
+ }
+
+ return -ENOSPC;
+}
+
+void
+nvkm_outp_fini(struct nvkm_outp *outp)
{
if (outp->func->fini)
outp->func->fini(outp);
}
+static void
+nvkm_outp_init_route(struct nvkm_outp *outp)
+{
+ struct nvkm_disp *disp = outp->disp;
+ enum nvkm_ior_proto proto;
+ enum nvkm_ior_type type;
+ struct nvkm_ior *ior;
+ int id, link;
+
+ /* Find any OR from the class that is able to support this device. */
+ proto = nvkm_outp_xlat(outp, &type);
+ if (proto == UNKNOWN)
+ return;
+
+ ior = nvkm_ior_find(disp, type, -1);
+ if (!ior) {
+ WARN_ON(1);
+ return;
+ }
+
+ /* Determine the specific OR, if any, this device is attached to. */
+ if (ior->func->route.get) {
+ id = ior->func->route.get(outp, &link);
+ if (id < 0) {
+ OUTP_DBG(outp, "no route");
+ return;
+ }
+ } else {
+ /* Prior to DCB 4.1, this is hardwired like so. */
+ id = ffs(outp->info.or) - 1;
+ link = (ior->type == SOR) ? outp->info.sorconf.link : 0;
+ }
+
+ ior = nvkm_ior_find(disp, type, id);
+ if (!ior) {
+ WARN_ON(1);
+ return;
+ }
+
+ /* Determine if the OR is already configured for this device. */
+ ior->func->state(ior, &ior->arm);
+ if (!ior->arm.head || ior->arm.proto != proto) {
+ OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head,
+ ior->arm.proto, proto);
+ return;
+ }
+
+ OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link);
+ ior->arm.outp = outp;
+}
+
void
-nvkm_output_init(struct nvkm_output *outp)
+nvkm_outp_init(struct nvkm_outp *outp)
{
+ nvkm_outp_init_route(outp);
if (outp->func->init)
outp->func->init(outp);
}
void
-nvkm_output_del(struct nvkm_output **poutp)
+nvkm_outp_del(struct nvkm_outp **poutp)
{
- struct nvkm_output *outp = *poutp;
+ struct nvkm_outp *outp = *poutp;
if (outp && !WARN_ON(!outp->func)) {
if (outp->func->dtor)
*poutp = outp->func->dtor(outp);
@@ -53,11 +231,13 @@ nvkm_output_del(struct nvkm_output **poutp)
}
}
-void
-nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp,
- int index, struct dcb_output *dcbE, struct nvkm_output *outp)
+int
+nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp,
+ int index, struct dcb_output *dcbE, struct nvkm_outp *outp)
{
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
+ enum nvkm_ior_proto proto;
+ enum nvkm_ior_type type;
outp->func = func;
outp->disp = disp;
@@ -72,16 +252,24 @@ nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp,
outp->info.type >= 2 ? outp->info.sorconf.link : 0,
outp->info.connector, outp->info.i2c_index,
outp->info.bus, outp->info.heads);
+
+ /* Cull output paths we can't map to an output resource. */
+ proto = nvkm_outp_xlat(outp, &type);
+ if (proto == UNKNOWN)
+ return -ENODEV;
+
+ return 0;
}
+static const struct nvkm_outp_func
+nvkm_outp = {
+};
+
int
-nvkm_output_new_(const struct nvkm_output_func *func,
- struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_output **poutp)
+nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
+ struct nvkm_outp **poutp)
{
if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL)))
return -ENOMEM;
-
- nvkm_output_ctor(func, disp, index, dcbE, *poutp);
- return 0;
+ return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
index 07727198d7ce..146d101d4891 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -5,47 +5,46 @@
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
-struct nvkm_output {
- const struct nvkm_output_func *func;
+struct nvkm_outp {
+ const struct nvkm_outp_func *func;
struct nvkm_disp *disp;
int index;
struct dcb_output info;
- // whatever (if anything) is pointed at by the dcb device entry
struct nvkm_i2c_bus *i2c;
int or;
struct list_head head;
- struct nvkm_connector *conn;
-};
+ struct nvkm_conn *conn;
-struct nvkm_output_func {
- void *(*dtor)(struct nvkm_output *);
- void (*init)(struct nvkm_output *);
- void (*fini)(struct nvkm_output *);
+ /* Assembly state. */
+#define NVKM_OUTP_PRIV 1
+#define NVKM_OUTP_USER 2
+ u8 acquired:2;
+ struct nvkm_ior *ior;
};
-void nvkm_output_ctor(const struct nvkm_output_func *, struct nvkm_disp *,
- int index, struct dcb_output *, struct nvkm_output *);
-int nvkm_output_new_(const struct nvkm_output_func *, struct nvkm_disp *,
- int index, struct dcb_output *, struct nvkm_output **);
-void nvkm_output_del(struct nvkm_output **);
-void nvkm_output_init(struct nvkm_output *);
-void nvkm_output_fini(struct nvkm_output *);
-
-int nv50_dac_output_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-int nv50_sor_output_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-int nv50_pior_output_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-
-u32 g94_sor_dp_lane_map(struct nvkm_device *, u8 lane);
-
-void gm200_sor_magic(struct nvkm_output *outp);
+int nvkm_outp_ctor(const struct nvkm_outp_func *, struct nvkm_disp *,
+ int index, struct dcb_output *, struct nvkm_outp *);
+int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *,
+ struct nvkm_outp **);
+void nvkm_outp_del(struct nvkm_outp **);
+void nvkm_outp_init(struct nvkm_outp *);
+void nvkm_outp_fini(struct nvkm_outp *);
+int nvkm_outp_acquire(struct nvkm_outp *, u8 user);
+void nvkm_outp_release(struct nvkm_outp *, u8 user);
+void nvkm_outp_route(struct nvkm_disp *);
+
+struct nvkm_outp_func {
+ void *(*dtor)(struct nvkm_outp *);
+ void (*init)(struct nvkm_outp *);
+ void (*fini)(struct nvkm_outp *);
+ int (*acquire)(struct nvkm_outp *);
+ void (*release)(struct nvkm_outp *, struct nvkm_ior *);
+};
#define OUTP_MSG(o,l,f,a...) do { \
- struct nvkm_output *_outp = (o); \
+ struct nvkm_outp *_outp = (o); \
nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \
_outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \
} while(0)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c
deleted file mode 100644
index de36f73b14dc..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright 2014 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 "outpdp.h"
-#include "conn.h"
-#include "dport.h"
-#include "priv.h"
-
-#include <subdev/i2c.h>
-
-#include <nvif/event.h>
-
-int
-nvkm_output_dp_train(struct nvkm_output *base, u32 datarate)
-{
- struct nvkm_output_dp *outp = nvkm_output_dp(base);
- bool retrain = true;
- u8 link[2], stat[3];
- u32 linkrate;
- int ret, i;
-
- mutex_lock(&outp->mutex);
-
- /* check that the link is trained at a high enough rate */
- ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2);
- if (ret) {
- OUTP_DBG(&outp->base,
- "failed to read link config, assuming no sink");
- goto done;
- }
-
- linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
- linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
- datarate = (datarate + 9) / 10; /* -> decakilobits */
- if (linkrate < datarate) {
- OUTP_DBG(&outp->base, "link not trained at sufficient rate");
- goto done;
- }
-
- /* check that link is still trained */
- ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3);
- if (ret) {
- OUTP_DBG(&outp->base,
- "failed to read link status, assuming no sink");
- goto done;
- }
-
- if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
- for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
- u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
- if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
- !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
- !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
- OUTP_DBG(&outp->base,
- "lane %d not equalised", lane);
- goto done;
- }
- }
- retrain = false;
- } else {
- OUTP_DBG(&outp->base, "no inter-lane alignment");
- }
-
-done:
- if (retrain || !atomic_read(&outp->lt.done)) {
- /* no sink, but still need to configure source */
- if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
- outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
- outp->base.info.dpconf.link_bw;
- outp->dpcd[DPCD_RC02] =
- outp->base.info.dpconf.link_nr;
- }
- nvkm_dp_train(outp);
- }
-
- mutex_unlock(&outp->mutex);
- return ret;
-}
-
-static void
-nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
-{
- struct nvkm_i2c_aux *aux = outp->aux;
-
- if (enable) {
- if (!outp->present) {
- OUTP_DBG(&outp->base, "aux power -> always");
- nvkm_i2c_aux_monitor(aux, true);
- outp->present = true;
- }
-
- if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dpcd,
- sizeof(outp->dpcd))) {
- nvkm_output_dp_train(&outp->base, 0);
- return;
- }
- }
-
- if (outp->present) {
- OUTP_DBG(&outp->base, "aux power -> demand");
- nvkm_i2c_aux_monitor(aux, false);
- outp->present = false;
- }
-
- atomic_set(&outp->lt.done, 0);
-}
-
-static int
-nvkm_output_dp_hpd(struct nvkm_notify *notify)
-{
- const struct nvkm_i2c_ntfy_rep *line = notify->data;
- struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd);
- struct nvkm_connector *conn = outp->base.conn;
- struct nvkm_disp *disp = outp->base.disp;
- struct nvif_notify_conn_rep_v0 rep = {};
-
- OUTP_DBG(&outp->base, "HPD: %d", line->mask);
- nvkm_output_dp_enable(outp, true);
-
- 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 int
-nvkm_output_dp_irq(struct nvkm_notify *notify)
-{
- const struct nvkm_i2c_ntfy_rep *line = notify->data;
- struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
- struct nvkm_connector *conn = outp->base.conn;
- struct nvkm_disp *disp = outp->base.disp;
- struct nvif_notify_conn_rep_v0 rep = {
- .mask = NVIF_NOTIFY_CONN_V0_IRQ,
- };
-
- OUTP_DBG(&outp->base, "IRQ: %d", line->mask);
- nvkm_output_dp_train(&outp->base, 0);
-
- nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
- return NVKM_NOTIFY_KEEP;
-}
-
-static void
-nvkm_output_dp_fini(struct nvkm_output *base)
-{
- struct nvkm_output_dp *outp = nvkm_output_dp(base);
- nvkm_notify_put(&outp->hpd);
- nvkm_notify_put(&outp->irq);
- nvkm_output_dp_enable(outp, false);
-}
-
-static void
-nvkm_output_dp_init(struct nvkm_output *base)
-{
- struct nvkm_output_dp *outp = nvkm_output_dp(base);
- nvkm_notify_put(&outp->base.conn->hpd);
- nvkm_output_dp_enable(outp, true);
- nvkm_notify_get(&outp->irq);
- nvkm_notify_get(&outp->hpd);
-}
-
-static void *
-nvkm_output_dp_dtor(struct nvkm_output *base)
-{
- struct nvkm_output_dp *outp = nvkm_output_dp(base);
- nvkm_notify_fini(&outp->hpd);
- nvkm_notify_fini(&outp->irq);
- return outp;
-}
-
-static const struct nvkm_output_func
-nvkm_output_dp_func = {
- .dtor = nvkm_output_dp_dtor,
- .init = nvkm_output_dp_init,
- .fini = nvkm_output_dp_fini,
-};
-
-int
-nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func,
- struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp)
-{
- struct nvkm_device *device = disp->engine.subdev.device;
- struct nvkm_bios *bios = device->bios;
- struct nvkm_i2c *i2c = device->i2c;
- u8 hdr, cnt, len;
- u32 data;
- int ret;
-
- nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base);
- outp->func = func;
- outp->aux = aux;
- if (!outp->aux) {
- OUTP_ERR(&outp->base, "no aux");
- return -ENODEV;
- }
-
- /* bios data is not optional */
- data = nvbios_dpout_match(bios, outp->base.info.hasht,
- outp->base.info.hashm, &outp->version,
- &hdr, &cnt, &len, &outp->info);
- if (!data) {
- OUTP_ERR(&outp->base, "no bios dp data");
- return -ENODEV;
- }
-
- OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x",
- outp->version, hdr, cnt, len);
-
- /* link maintenance */
- ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
- &(struct nvkm_i2c_ntfy_req) {
- .mask = NVKM_I2C_IRQ,
- .port = outp->aux->id,
- },
- sizeof(struct nvkm_i2c_ntfy_req),
- sizeof(struct nvkm_i2c_ntfy_rep),
- &outp->irq);
- if (ret) {
- OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret);
- return ret;
- }
-
- mutex_init(&outp->mutex);
- atomic_set(&outp->lt.done, 0);
-
- /* hotplug detect, replaces gpio-based mechanism with aux events */
- ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
- &(struct nvkm_i2c_ntfy_req) {
- .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
- .port = outp->aux->id,
- },
- sizeof(struct nvkm_i2c_ntfy_req),
- sizeof(struct nvkm_i2c_ntfy_rep),
- &outp->hpd);
- if (ret) {
- OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret);
- return ret;
- }
-
- return 0;
-}
-
-int
-nvkm_output_dp_new_(const struct nvkm_output_dp_func *func,
- struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_output **poutp)
-{
- struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
- struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index);
- struct nvkm_output_dp *outp;
-
- if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
- return -ENOMEM;
- *poutp = &outp->base;
-
- return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp);
-}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h
deleted file mode 100644
index 3c83a561cd88..000000000000
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#ifndef __NVKM_DISP_OUTP_DP_H__
-#define __NVKM_DISP_OUTP_DP_H__
-#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base)
-#ifndef MSG
-#define MSG(l,f,a...) \
- nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \
- outp->base.index, outp->base.info.hasht, \
- outp->base.info.hashm, ##a)
-#define DBG(f,a...) MSG(debug, f, ##a)
-#define ERR(f,a...) MSG(error, f, ##a)
-#endif
-#include "outp.h"
-
-#include <core/notify.h>
-#include <subdev/bios.h>
-#include <subdev/bios/dp.h>
-
-struct nvkm_output_dp {
- const struct nvkm_output_dp_func *func;
- struct nvkm_output base;
-
- struct nvbios_dpout info;
- u8 version;
-
- struct nvkm_i2c_aux *aux;
-
- struct nvkm_notify irq;
- struct nvkm_notify hpd;
- bool present;
- u8 dpcd[16];
-
- struct mutex mutex;
- struct {
- atomic_t done;
- bool mst;
- } lt;
-};
-
-struct nvkm_output_dp_func {
- int (*pattern)(struct nvkm_output_dp *, int);
- int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
- int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
- int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc);
- void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot,
- u8 num_slots, u16 pbn, u16 aligned_pbn);
-};
-
-int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
-
-int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *,
- int index, struct dcb_output *, struct nvkm_i2c_aux *,
- struct nvkm_output_dp *);
-int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *,
- int index, struct dcb_output *,
- struct nvkm_output **);
-
-int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-
-int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
-
-int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool);
-int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int);
-void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16);
-
-int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-int gm107_sor_dp_pattern(struct nvkm_output_dp *, int);
-
-int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
- struct nvkm_output **);
-#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c
index 2a49c46425cd..9ebaaa6e9e33 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
#include "dmacnv50.h"
+#include "head.h"
#include "rootnv50.h"
#include <core/client.h>
@@ -50,7 +51,7 @@ nv50_disp_ovly_new(const struct nv50_disp_dmac_func *func,
nvif_ioctl(parent, "create disp overlay channel dma vers %d "
"pushbuf %016llx head %d\n",
args->v0.version, args->v0.pushbuf, args->v0.head);
- if (args->v0.head > disp->base.head.nr)
+ if (!nvkm_head_find(&disp->base, args->v0.head))
return -EINVAL;
push = args->v0.pushbuf;
head = args->v0.head;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c
index 6c532eadba17..99b3b9050635 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c
@@ -21,111 +21,114 @@
*
* Authors: Ben Skeggs
*/
-#include "outpdp.h"
-#include "nv50.h"
+#include "ior.h"
+#include "head.h"
-#include <core/client.h>
#include <subdev/i2c.h>
#include <subdev/timer.h>
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-nv50_pior_power(NV50_DISP_MTHD_V1)
+static void
+nv50_pior_clock(struct nvkm_ior *pior)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 soff = outp->or * 0x800;
- union {
- struct nv50_disp_pior_pwr_v0 v0;
- } *args = data;
- u32 ctrl, type;
- int ret = -ENOSYS;
+ struct nvkm_device *device = pior->disp->engine.subdev.device;
+ const u32 poff = nv50_ior_base(pior);
+ nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);
+}
- nvif_ioctl(object, "disp pior pwr size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n",
- args->v0.version, args->v0.state, args->v0.type);
- if (args->v0.type > 0x0f)
- return -EINVAL;
- ctrl = !!args->v0.state;
- type = args->v0.type;
- } else
+static int
+nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)
+{
+ int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw,
+ pior->dp.ef);
+ if (ret)
return ret;
+ return 1;
+}
+static void
+nv50_pior_power_wait(struct nvkm_device *device, u32 poff)
+{
nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
- break;
- );
- nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
+ if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000))
break;
);
- disp->pior.type[outp->or] = type;
- return 0;
}
-/******************************************************************************
- * TMDS
- *****************************************************************************/
-static const struct nvkm_output_func
-nv50_pior_output_func = {
-};
-
-int
-nv50_pior_output_new(struct nvkm_disp *disp, int index,
- struct dcb_output *dcbE, struct nvkm_output **poutp)
+static void
+nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu,
+ bool data, bool vsync, bool hsync)
{
- return nvkm_output_new_(&nv50_pior_output_func, disp,
- index, dcbE, poutp);
-}
+ struct nvkm_device *device = pior->disp->engine.subdev.device;
+ const u32 poff = nv50_ior_base(pior);
+ const u32 shift = normal ? 0 : 16;
+ const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
+ const u32 field = 0x80000000 | (0x00000101 << shift);
-/******************************************************************************
- * DisplayPort
- *****************************************************************************/
-static int
-nv50_pior_output_dp_pattern(struct nvkm_output_dp *outp, int pattern)
-{
- return 0;
+ nv50_pior_power_wait(device, poff);
+ nvkm_mask(device, 0x61e004 + poff, field, state);
+ nv50_pior_power_wait(device, poff);
}
-static int
-nv50_pior_output_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
+void
+nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl)
{
- return 0;
+ /* GF119 moves this information to per-head methods, which is
+ * a lot more convenient, and where our shared code expect it.
+ */
+ if (state->head && state == &ior->asy) {
+ struct nvkm_head *head =
+ nvkm_head_find(ior->disp, __ffs(state->head));
+ if (!WARN_ON(!head)) {
+ struct nvkm_head_state *state = &head->asy;
+ switch ((ctrl & 0x000f0000) >> 16) {
+ case 6: state->or.depth = 30; break;
+ case 5: state->or.depth = 24; break;
+ case 2: state->or.depth = 18; break;
+ case 0: state->or.depth = 18; break; /*XXX*/
+ default:
+ state->or.depth = 18;
+ WARN_ON(1);
+ break;
+ }
+ }
+ }
}
-static int
-nv50_pior_output_dp_lnk_ctl(struct nvkm_output_dp *outp,
- int nr, int bw, bool ef)
+static void
+nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state)
{
- int ret = nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef);
- if (ret)
- return ret;
- return 1;
+ struct nvkm_device *device = pior->disp->engine.subdev.device;
+ const u32 coff = pior->id * 8 + (state == &pior->arm) * 4;
+ u32 ctrl = nvkm_rd32(device, 0x610b80 + coff);
+
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ state->rgdiv = 1;
+ switch (state->proto_evo) {
+ case 0: state->proto = TMDS; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
+
+ state->head = ctrl & 0x00000003;
+ nv50_pior_depth(pior, state, ctrl);
}
-static const struct nvkm_output_dp_func
-nv50_pior_output_dp_func = {
- .pattern = nv50_pior_output_dp_pattern,
- .lnk_pwr = nv50_pior_output_dp_lnk_pwr,
- .lnk_ctl = nv50_pior_output_dp_lnk_ctl,
+static const struct nvkm_ior_func
+nv50_pior = {
+ .state = nv50_pior_state,
+ .power = nv50_pior_power,
+ .clock = nv50_pior_clock,
+ .dp = {
+ .links = nv50_pior_dp_links,
+ },
};
int
-nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_output **poutp)
+nv50_pior_new(struct nvkm_disp *disp, int id)
{
- struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
- struct nvkm_i2c_aux *aux =
- nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev));
- struct nvkm_output_dp *outp;
-
- if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
- return -ENOMEM;
- *poutp = &outp->base;
-
- return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp,
- index, dcbE, aux, outp);
+ struct nvkm_device *device = disp->engine.subdev.device;
+ if (!(nvkm_rd32(device, 0x610184) & (0x10000000 << id)))
+ return 0;
+ return nvkm_ior_new_(&nv50_pior, disp, PIOR, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
index c2452957fc57..5772f0094129 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
@@ -2,42 +2,18 @@
#define __NVKM_DISP_PRIV_H__
#include <engine/disp.h>
#include "outp.h"
-#include "outpdp.h"
int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *,
- int index, int heads, struct nvkm_disp *);
+ int index, struct nvkm_disp *);
int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *,
- int index, int heads, struct nvkm_disp **);
+ int index, struct nvkm_disp **);
void nvkm_disp_vblank(struct nvkm_disp *, int head);
-struct nvkm_disp_func_outp {
- int (* crt)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (* tv)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
- int (* dp)(struct nvkm_disp *, int index, struct dcb_output *,
- struct nvkm_output **);
-};
-
struct nvkm_disp_func {
void *(*dtor)(struct nvkm_disp *);
void (*intr)(struct nvkm_disp *);
const struct nvkm_disp_oclass *(*root)(struct nvkm_disp *);
-
- struct {
- void (*vblank_init)(struct nvkm_disp *, int head);
- void (*vblank_fini)(struct nvkm_disp *, int head);
- } head;
-
- struct {
- const struct nvkm_disp_func_outp internal;
- const struct nvkm_disp_func_outp external;
- } outp;
};
int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c
index 335d88823c22..333c8424b413 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c
@@ -22,49 +22,13 @@
* Authors: Ben Skeggs
*/
#include "rootnv50.h"
+#include "head.h"
#include "dmacnv50.h"
-#include <core/client.h>
#include <core/ramht.h>
#include <subdev/timer.h>
#include <nvif/class.h>
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 total = nvkm_rd32(device, 0x640414 + (head * 0x300));
- const u32 blanke = nvkm_rd32(device, 0x64041c + (head * 0x300));
- const u32 blanks = nvkm_rd32(device, 0x640420 + (head * 0x300));
- union {
- struct nv50_disp_scanoutpos_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp scanoutpos size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp scanoutpos vers %d\n",
- args->v0.version);
- args->v0.vblanke = (blanke & 0xffff0000) >> 16;
- args->v0.hblanke = (blanke & 0x0000ffff);
- args->v0.vblanks = (blanks & 0xffff0000) >> 16;
- args->v0.hblanks = (blanks & 0x0000ffff);
- args->v0.vtotal = ( total & 0xffff0000) >> 16;
- args->v0.htotal = ( total & 0x0000ffff);
- args->v0.time[0] = ktime_to_ns(ktime_get());
- args->v0.vline = /* vline read locks hline */
- nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
- args->v0.time[1] = ktime_to_ns(ktime_get());
- args->v0.hline =
- nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
- } else
- return ret;
-
- return 0;
-}
void
gf119_disp_root_fini(struct nv50_disp_root *root)
@@ -78,6 +42,7 @@ int
gf119_disp_root_init(struct nv50_disp_root *root)
{
struct nv50_disp *disp = root->disp;
+ struct nvkm_head *head;
struct nvkm_device *device = disp->base.engine.subdev.device;
u32 tmp;
int i;
@@ -88,13 +53,14 @@ gf119_disp_root_init(struct nv50_disp_root *root)
*/
/* ... CRTC caps */
- for (i = 0; i < disp->base.head.nr; i++) {
- tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
- nvkm_wr32(device, 0x6101b4 + (i * 0x800), tmp);
- tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
- nvkm_wr32(device, 0x6101b8 + (i * 0x800), tmp);
- tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
- nvkm_wr32(device, 0x6101bc + (i * 0x800), tmp);
+ list_for_each_entry(head, &disp->base.head, head) {
+ const u32 hoff = head->id * 0x800;
+ tmp = nvkm_rd32(device, 0x616104 + hoff);
+ nvkm_wr32(device, 0x6101b4 + hoff, tmp);
+ tmp = nvkm_rd32(device, 0x616108 + hoff);
+ nvkm_wr32(device, 0x6101b8 + hoff, tmp);
+ tmp = nvkm_rd32(device, 0x61610c + hoff);
+ nvkm_wr32(device, 0x6101bc + hoff, tmp);
}
/* ... DAC caps */
@@ -134,8 +100,10 @@ gf119_disp_root_init(struct nv50_disp_root *root)
*
* ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt
*/
- for (i = 0; i < disp->base.head.nr; i++)
- nvkm_mask(device, 0x616308 + (i * 0x800), 0x00000111, 0x00000010);
+ list_for_each_entry(head, &disp->base.head, head) {
+ const u32 hoff = head->id * 0x800;
+ nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c
index f535f43231e2..7f3e2554a83d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c
@@ -23,6 +23,7 @@
*/
#define nv04_disp_root(p) container_of((p), struct nv04_disp_root, object)
#include "priv.h"
+#include "head.h"
#include <core/client.h>
@@ -36,73 +37,30 @@ struct nv04_disp_root {
};
static int
-nv04_disp_scanoutpos(struct nv04_disp_root *root,
- void *data, u32 size, int head)
-{
- struct nvkm_device *device = root->disp->engine.subdev.device;
- struct nvkm_object *object = &root->object;
- const u32 hoff = head * 0x2000;
- union {
- struct nv04_disp_scanoutpos_v0 v0;
- } *args = data;
- u32 line;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp scanoutpos size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp scanoutpos vers %d\n",
- args->v0.version);
- args->v0.vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0xffff;
- args->v0.vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0xffff;
- args->v0.vblanke = args->v0.vtotal - 1;
-
- args->v0.hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0xffff;
- args->v0.htotal = nvkm_rd32(device, 0x680824 + hoff) & 0xffff;
- args->v0.hblanke = args->v0.htotal - 1;
-
- /*
- * If output is vga instead of digital then vtotal/htotal is
- * invalid 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());
- line = nvkm_rd32(device, 0x600868 + hoff);
- args->v0.time[1] = ktime_to_ns(ktime_get());
- args->v0.hline = (line & 0xffff0000) >> 16;
- args->v0.vline = (line & 0x0000ffff);
- } else
- return ret;
-
- return 0;
-}
-
-static int
nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
struct nv04_disp_root *root = nv04_disp_root(object);
union {
struct nv04_disp_mthd_v0 v0;
} *args = data;
- int head, ret = -ENOSYS;
+ 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;
- head = args->v0.head;
+ id = args->v0.head;
} else
return ret;
- if (head < 0 || head >= 2)
+ if (!(head = nvkm_head_find(root->disp, id)))
return -ENXIO;
switch (mthd) {
case NV04_DISP_SCANOUTPOS:
- return nv04_disp_scanoutpos(root, data, size, head);
+ return nvkm_head_mthd_scanoutpos(object, head, data, size);
default:
break;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
index e70dc6a9ff7d..1208524aae14 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
@@ -23,6 +23,9 @@
*/
#include "rootnv50.h"
#include "dmacnv50.h"
+#include "dp.h"
+#include "head.h"
+#include "ior.h"
#include <core/client.h>
#include <core/ramht.h>
@@ -32,40 +35,6 @@
#include <nvif/cl5070.h>
#include <nvif/unpack.h>
-int
-nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
-{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540));
- const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540));
- const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540));
- union {
- struct nv50_disp_scanoutpos_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp scanoutpos size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp scanoutpos vers %d\n",
- args->v0.version);
- args->v0.vblanke = (blanke & 0xffff0000) >> 16;
- args->v0.hblanke = (blanke & 0x0000ffff);
- args->v0.vblanks = (blanks & 0xffff0000) >> 16;
- args->v0.hblanks = (blanks & 0x0000ffff);
- args->v0.vtotal = ( total & 0xffff0000) >> 16;
- args->v0.htotal = ( total & 0x0000ffff);
- args->v0.time[0] = ktime_to_ns(ktime_get());
- args->v0.vline = /* vline read locks hline */
- nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
- args->v0.time[1] = ktime_to_ns(ktime_get());
- args->v0.hline =
- nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
- } else
- return ret;
-
- return 0;
-}
-
static int
nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
@@ -75,11 +44,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
} *args = data;
struct nv50_disp_root *root = nv50_disp_root(object);
struct nv50_disp *disp = root->disp;
- const struct nv50_disp_func *func = disp->func;
- struct nvkm_output *outp = NULL;
- struct nvkm_output *temp;
+ struct nvkm_outp *temp, *outp = NULL;
+ struct nvkm_head *head;
u16 type, mask = 0;
- int head, ret = -ENOSYS;
+ int hidx, ret = -ENOSYS;
if (mthd != NV50_DISP_MTHD)
return -EINVAL;
@@ -89,7 +57,7 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
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;
- head = args->v0.head;
+ 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 "
@@ -99,11 +67,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
mthd = args->v1.method;
type = args->v1.hasht;
mask = args->v1.hashm;
- head = ffs((mask >> 8) & 0x0f) - 1;
+ hidx = ffs((mask >> 8) & 0x0f) - 1;
} else
return ret;
- if (head < 0 || head >= disp->base.head.nr)
+ if (!(head = nvkm_head_find(&disp->base, hidx)))
return -ENXIO;
if (mask) {
@@ -119,27 +87,126 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
}
switch (mthd) {
- case NV50_DISP_SCANOUTPOS:
- return func->head.scanoutpos(object, disp, data, size, head);
+ case NV50_DISP_SCANOUTPOS: {
+ return nvkm_head_mthd_scanoutpos(object, head, data, size);
+ }
default:
break;
}
switch (mthd * !!outp) {
- case NV50_DISP_MTHD_V1_DAC_PWR:
- return func->dac.power(object, disp, data, size, head, outp);
- case NV50_DISP_MTHD_V1_DAC_LOAD:
- return func->dac.sense(object, disp, data, size, head, outp);
- case NV50_DISP_MTHD_V1_SOR_PWR:
- return func->sor.power(object, disp, data, size, head, outp);
- case NV50_DISP_MTHD_V1_SOR_HDA_ELD:
- if (!func->sor.hda_eld)
+ 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);
+ 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_DAC_LOAD: {
+ union {
+ struct nv50_disp_dac_load_v0 v0;
+ } *args = data;
+ int ret = -ENOSYS;
+ if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
+ if (args->v0.data & 0xfff00000)
+ return -EINVAL;
+ ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV);
+ if (ret)
+ return ret;
+ ret = outp->ior->func->sense(outp->ior, args->v0.data);
+ nvkm_outp_release(outp, NVKM_OUTP_PRIV);
+ if (ret < 0)
+ return ret;
+ args->v0.load = ret;
+ return 0;
+ } else
+ return ret;
+ }
+ break;
+ 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->func->hda.hpd)
return -ENODEV;
- return func->sor.hda_eld(object, disp, data, size, head, outp);
- case NV50_DISP_MTHD_V1_SOR_HDMI_PWR:
- if (!func->sor.hdmi)
+
+ 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, 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\n",
+ args->v0.version, args->v0.state,
+ args->v0.max_ac_packet, args->v0.rekey);
+ 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;
- return func->sor.hdmi(object, disp, data, size, head, outp);
+
+ 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);
+ return 0;
+ }
+ break;
case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
union {
struct nv50_disp_sor_lvds_script_v0 v0;
@@ -156,32 +223,8 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
return ret;
}
break;
- case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
- struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
- union {
- struct nv50_disp_sor_dp_pwr_v0 v0;
- } *args = data;
- int ret = -ENOSYS;
- nvif_ioctl(object, "disp sor dp pwr size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n",
- args->v0.version, args->v0.state);
- if (args->v0.state == 0) {
- nvkm_notify_put(&outpdp->irq);
- outpdp->func->lnk_pwr(outpdp, 0);
- atomic_set(&outpdp->lt.done, 0);
- return 0;
- } else
- if (args->v0.state != 0) {
- nvkm_output_dp_train(&outpdp->base, 0);
- return 0;
- }
- } else
- return ret;
- }
- break;
case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
- struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
+ struct nvkm_dp *dp = nvkm_dp(outp);
union {
struct nv50_disp_sor_dp_mst_link_v0 v0;
} *args = data;
@@ -190,18 +233,13 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 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);
- if (outpdp->lt.mst != !!args->v0.state) {
- outpdp->lt.mst = !!args->v0.state;
- atomic_set(&outpdp->lt.done, 0);
- nvkm_output_dp_train(&outpdp->base, 0);
- }
+ dp->lt.mst = !!args->v0.state;
return 0;
} else
return ret;
}
break;
case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
- struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
union {
struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
} *args = data;
@@ -213,20 +251,18 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
args->v0.version, args->v0.start_slot,
args->v0.num_slots, args->v0.pbn,
args->v0.aligned_pbn);
- if (!outpdp->func->vcpi)
+ if (!outp->ior->func->dp.vcpi)
return -ENODEV;
- outpdp->func->vcpi(outpdp, head, args->v0.start_slot,
- args->v0.num_slots, args->v0.pbn,
- args->v0.aligned_pbn);
+ 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;
- case NV50_DISP_MTHD_V1_PIOR_PWR:
- if (!func->pior.power)
- return -ENODEV;
- return func->pior.power(object, disp, data, size, head, outp);
default:
break;
}
@@ -291,7 +327,21 @@ static int
nv50_disp_root_init_(struct nvkm_object *object)
{
struct nv50_disp_root *root = nv50_disp_root(object);
- return root->func->init(root);
+ struct nvkm_ior *ior;
+ int ret;
+
+ ret = root->func->init(root);
+ if (ret)
+ return ret;
+
+ /* Set 'normal' (ie. when it's attached to a head) state for
+ * each output resource to 'fully enabled'.
+ */
+ list_for_each_entry(ior, &root->disp->base.ior, head) {
+ ior->func->power(ior, true, true, true, true, true);
+ }
+
+ return 0;
}
static void *
@@ -352,6 +402,7 @@ int
nv50_disp_root_init(struct nv50_disp_root *root)
{
struct nv50_disp *disp = root->disp;
+ struct nvkm_head *head;
struct nvkm_device *device = disp->base.engine.subdev.device;
u32 tmp;
int i;
@@ -364,15 +415,15 @@ nv50_disp_root_init(struct nv50_disp_root *root)
nvkm_wr32(device, 0x610184, tmp);
/* ... CRTC caps */
- for (i = 0; i < disp->base.head.nr; i++) {
- tmp = nvkm_rd32(device, 0x616100 + (i * 0x800));
- nvkm_wr32(device, 0x610190 + (i * 0x10), tmp);
- tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
- nvkm_wr32(device, 0x610194 + (i * 0x10), tmp);
- tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
- nvkm_wr32(device, 0x610198 + (i * 0x10), tmp);
- tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
- nvkm_wr32(device, 0x61019c + (i * 0x10), tmp);
+ list_for_each_entry(head, &disp->base.head, head) {
+ tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800));
+ nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp);
+ tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800));
+ nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp);
+ tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800));
+ nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp);
+ tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800));
+ nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp);
}
/* ... DAC caps */
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c
new file mode 100644
index 000000000000..f40b909b4ca2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+static const struct nvkm_ior_func
+g84_sor = {
+ .state = nv50_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
+ .hdmi = {
+ .ctrl = g84_hdmi_ctrl,
+ },
+};
+
+int
+g84_sor_new(struct nvkm_disp *disp, int id)
+{
+ return nv50_sor_new_(&g84_sor, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
index 627b9ee1ddd2..49aeafde0031 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
@@ -21,58 +21,75 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outpdp.h"
+#include "ior.h"
#include <subdev/timer.h>
-static inline u32
-g94_sor_soff(struct nvkm_output_dp *outp)
+void
+g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
{
- return (ffs(outp->base.info.or) - 1) * 0x800;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
+ nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark);
}
-static inline u32
-g94_sor_loff(struct nvkm_output_dp *outp)
+void
+g94_sor_dp_activesym(struct nvkm_ior *sor, int head,
+ u8 TU, u8 VTUa, u8 VTUf, u8 VTUi)
{
- return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
+ nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2);
+ nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 |
+ VTUf << 16 |
+ VTUi << 8);
}
-/*******************************************************************************
- * DisplayPort
- ******************************************************************************/
-u32
-g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
+void
+g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
{
- static const u8 gm100[] = { 0, 8, 16, 24 };
- static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
- static const u8 g94[] = { 16, 8, 0, 24 };
- if (device->chipset >= 0x110)
- return gm100[lane];
- if (device->chipset == 0xaf)
- return mcp89[lane];
- return g94[lane];
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h);
+ nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v);
}
-static int
-g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
+void
+g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 loff = g94_sor_loff(outp);
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
+ const u32 shift = sor->func->dp.lanes[ln] * 8;
+ u32 data[3];
+
+ data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
+ data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
+ data[2] = nvkm_rd32(device, 0x61c130 + loff);
+ if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0)
+ data[2] = (data[2] & ~0x0000ff00) | (pu << 8);
+ nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
+ nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
+ nvkm_wr32(device, 0x61c130 + loff, data[2]);
+}
+
+void
+g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
- return 0;
}
-int
-g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
+void
+g94_sor_dp_power(struct nvkm_ior *sor, int nr)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = g94_sor_soff(outp);
- const u32 loff = g94_sor_loff(outp);
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ const u32 loff = nv50_sor_link(sor);
u32 mask = 0, i;
for (i = 0; i < nr; i++)
- mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3);
+ mask |= 1 << sor->func->dp.lanes[i];
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
@@ -80,22 +97,21 @@ g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
break;
);
- return 0;
}
-static int
-g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
+int
+g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = g94_sor_soff(outp);
- const u32 loff = g94_sor_loff(outp);
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ const u32 loff = nv50_sor_link(sor);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- dpctrl |= ((1 << nr) - 1) << 16;
- if (ef)
+ dpctrl |= ((1 << sor->dp.nr) - 1) << 16;
+ if (sor->dp.ef)
dpctrl |= 0x00004000;
- if (bw > 0x06)
+ if (sor->dp.bw > 0x06)
clksor |= 0x00040000;
nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor);
@@ -103,51 +119,165 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
return 0;
}
-static int
-g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
+static bool
+g94_sor_war_needed(struct nvkm_ior *sor)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- struct nvkm_bios *bios = device->bios;
- const u32 shift = g94_sor_dp_lane_map(device, ln);
- const u32 loff = g94_sor_loff(outp);
- u32 addr, data[3];
- u8 ver, hdr, cnt, len;
- struct nvbios_dpout info;
- struct nvbios_dpcfg ocfg;
-
- addr = nvbios_dpout_match(bios, outp->base.info.hasht,
- outp->base.info.hashm,
- &ver, &hdr, &cnt, &len, &info);
- if (!addr)
- return -ENODEV;
-
- addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
- &ver, &hdr, &cnt, &len, &ocfg);
- if (!addr)
- return -EINVAL;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ if (sor->asy.proto == TMDS) {
+ switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
+ case 0x00000000:
+ case 0x00030000:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
- data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
- data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
- data[2] = nvkm_rd32(device, 0x61c130 + loff);
- if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
- data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
- nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
- nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
- nvkm_wr32(device, 0x61c130 + loff, data[2]);
- return 0;
+static void
+g94_sor_war_update_sppll1(struct nvkm_disp *disp)
+{
+ struct nvkm_device *device = disp->engine.subdev.device;
+ struct nvkm_ior *ior;
+ bool used = false;
+ u32 clksor;
+
+ list_for_each_entry(ior, &disp->ior, head) {
+ if (ior->type != SOR)
+ continue;
+
+ clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior));
+ switch (clksor & 0x03000000) {
+ case 0x02000000:
+ case 0x03000000:
+ used = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (used)
+ return;
+
+ nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
+}
+
+static void
+g94_sor_war_3(struct nvkm_ior *sor)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ u32 sorpwr;
+
+ if (!g94_sor_war_needed(sor))
+ return;
+
+ sorpwr = nvkm_rd32(device, 0x61c004 + soff);
+ if (sorpwr & 0x00000001) {
+ u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
+ u32 pd_pc = (seqctl & 0x00000f00) >> 8;
+ u32 pu_pc = seqctl & 0x0000000f;
+
+ nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);
+
+ nvkm_msec(device, 2000,
+ if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
+ break;
+ );
+ nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
+ nvkm_msec(device, 2000,
+ if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
+ break;
+ );
+
+ nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
+ nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
+ }
+
+ nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
+ nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);
+
+ if (sorpwr & 0x00000001) {
+ nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
+ }
+
+ g94_sor_war_update_sppll1(sor->disp);
+}
+
+static void
+g94_sor_war_2(struct nvkm_ior *sor)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+
+ if (!g94_sor_war_needed(sor))
+ return;
+
+ nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
+ nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
+ nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);
+
+ nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
+ nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
+ nvkm_usec(device, 400, NVKM_DELAY);
+ nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
+ nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);
+
+ if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
+ u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
+ u32 pu_pc = seqctl & 0x0000000f;
+ nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
+ }
+}
+
+void
+g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
+ u32 ctrl = nvkm_rd32(device, 0x610794 + coff);
+
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ switch (state->proto_evo) {
+ case 0: state->proto = LVDS; state->link = 1; break;
+ case 1: state->proto = TMDS; state->link = 1; break;
+ case 2: state->proto = TMDS; state->link = 2; break;
+ case 5: state->proto = TMDS; state->link = 3; break;
+ case 8: state->proto = DP; state->link = 1; break;
+ case 9: state->proto = DP; state->link = 2; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
+
+ state->head = ctrl & 0x00000003;
+ nv50_pior_depth(sor, state, ctrl);
}
-static const struct nvkm_output_dp_func
-g94_sor_dp_func = {
- .pattern = g94_sor_dp_pattern,
- .lnk_pwr = g94_sor_dp_lnk_pwr,
- .lnk_ctl = g94_sor_dp_lnk_ctl,
- .drv_ctl = g94_sor_dp_drv_ctl,
+static const struct nvkm_ior_func
+g94_sor = {
+ .state = g94_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
+ .war_2 = g94_sor_war_2,
+ .war_3 = g94_sor_war_3,
+ .dp = {
+ .lanes = { 2, 1, 0, 3},
+ .links = g94_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = g94_sor_dp_pattern,
+ .drive = g94_sor_dp_drive,
+ .audio_sym = g94_sor_dp_audio_sym,
+ .activesym = g94_sor_dp_activesym,
+ .watermark = g94_sor_dp_watermark,
+ },
};
int
-g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_output **poutp)
+g94_sor_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
+ return nv50_sor_new_(&g94_sor, disp, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
index 6ffdaa65aa77..a2978a37b4f3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
@@ -21,44 +21,94 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outpdp.h"
+#include "ior.h"
-static inline u32
-gf119_sor_soff(struct nvkm_output_dp *outp)
+#include <subdev/timer.h>
+
+void
+gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hoff = head * 0x800;
+ nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark);
+}
+
+void
+gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
{
- return (ffs(outp->base.info.or) - 1) * 0x800;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hoff = head * 0x800;
+ nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h);
+ nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v);
}
-static inline u32
-gf119_sor_loff(struct nvkm_output_dp *outp)
+void
+gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
{
- return gf119_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hoff = 0x800 * head;
+ const u32 data = 0x80000000 | (0x00000001 * enable);
+ const u32 mask = 0x8000000d;
+ nvkm_mask(device, 0x616618 + hoff, mask, data);
+ nvkm_msec(device, 2000,
+ if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000))
+ break;
+ );
+}
+
+void
+gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head,
+ u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hoff = head * 0x800;
+
+ nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
+ nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
+}
+
+void
+gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
+ const u32 shift = sor->func->dp.lanes[ln] * 8;
+ u32 data[4];
+
+ data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
+ data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
+ data[2] = nvkm_rd32(device, 0x61c130 + loff);
+ if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0)
+ data[2] = (data[2] & ~0x0000ff00) | (pu << 8);
+ nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
+ nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
+ nvkm_wr32(device, 0x61c130 + loff, data[2]);
+ data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
+ nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift));
}
-static int
-gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
+void
+gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = gf119_sor_soff(outp);
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
- return 0;
}
int
-gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
+gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = gf119_sor_soff(outp);
- const u32 loff = gf119_sor_loff(outp);
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ const u32 loff = nv50_sor_link(sor);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- clksor |= bw << 18;
- dpctrl |= ((1 << nr) - 1) << 16;
- if (outp->lt.mst)
+ clksor |= sor->dp.bw << 18;
+ dpctrl |= ((1 << sor->dp.nr) - 1) << 16;
+ if (sor->dp.mst)
dpctrl |= 0x40000000;
- if (ef)
+ if (sor->dp.ef)
dpctrl |= 0x00004000;
nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor);
@@ -66,66 +116,77 @@ gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
return 0;
}
-int
-gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
- int ln, int vs, int pe, int pc)
+void
+gf119_sor_clock(struct nvkm_ior *sor)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- struct nvkm_bios *bios = device->bios;
- const u32 shift = g94_sor_dp_lane_map(device, ln);
- const u32 loff = gf119_sor_loff(outp);
- u32 addr, data[4];
- u8 ver, hdr, cnt, len;
- struct nvbios_dpout info;
- struct nvbios_dpcfg ocfg;
-
- addr = nvbios_dpout_match(bios, outp->base.info.hasht,
- outp->base.info.hashm,
- &ver, &hdr, &cnt, &len, &info);
- if (!addr)
- return -ENODEV;
-
- addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
- &ver, &hdr, &cnt, &len, &ocfg);
- if (!addr)
- return -EINVAL;
-
- data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
- data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
- data[2] = nvkm_rd32(device, 0x61c130 + loff);
- if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
- data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
- nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
- nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
- nvkm_wr32(device, 0x61c130 + loff, data[2]);
- data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
- nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
- return 0;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const int div = sor->asy.link == 3;
+ const u32 soff = nv50_ior_base(sor);
+ if (sor->asy.proto == TMDS) {
+ /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
+ nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
+ }
+ nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
}
void
-gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot,
- u8 slot_nr, u16 pbn, u16 aligned)
+gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 hoff = head * 0x800;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20;
+ u32 ctrl = nvkm_rd32(device, 0x640200 + coff);
- nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
- nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ switch (state->proto_evo) {
+ case 0: state->proto = LVDS; state->link = 1; break;
+ case 1: state->proto = TMDS; state->link = 1; break;
+ case 2: state->proto = TMDS; state->link = 2; break;
+ case 5: state->proto = TMDS; state->link = 3; break;
+ case 8: state->proto = DP; state->link = 1; break;
+ case 9: state->proto = DP; state->link = 2; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
+
+ state->head = ctrl & 0x0000000f;
+}
+
+int
+gf119_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id)
+{
+ struct nvkm_device *device = disp->engine.subdev.device;
+ if (!(nvkm_rd32(device, 0x612004) & (0x00000100 << id)))
+ return 0;
+ return nvkm_ior_new_(func, disp, SOR, id);
}
-static const struct nvkm_output_dp_func
-gf119_sor_dp_func = {
- .pattern = gf119_sor_dp_pattern,
- .lnk_pwr = g94_sor_dp_lnk_pwr,
- .lnk_ctl = gf119_sor_dp_lnk_ctl,
- .drv_ctl = gf119_sor_dp_drv_ctl,
- .vcpi = gf119_sor_dp_vcpi,
+static const struct nvkm_ior_func
+gf119_sor = {
+ .state = gf119_sor_state,
+ .power = nv50_sor_power,
+ .clock = gf119_sor_clock,
+ .hdmi = {
+ .ctrl = gf119_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 2, 1, 0, 3 },
+ .links = gf119_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = gf119_sor_dp_pattern,
+ .vcpi = gf119_sor_dp_vcpi,
+ .audio = gf119_sor_dp_audio,
+ .audio_sym = gf119_sor_dp_audio_sym,
+ .watermark = gf119_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gf119_hda_hpd,
+ .eld = gf119_hda_eld,
+ },
};
int
-gf119_sor_dp_new(struct nvkm_disp *disp, int index,
- struct dcb_output *dcbE, struct nvkm_output **poutp)
+gf119_sor_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_dp_new_(&gf119_sor_dp_func, disp, index, dcbE, poutp);
+ return gf119_sor_new_(&gf119_sor, disp, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c
new file mode 100644
index 000000000000..a1547bdf490b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+static const struct nvkm_ior_func
+gk104_sor = {
+ .state = gf119_sor_state,
+ .power = nv50_sor_power,
+ .clock = gf119_sor_clock,
+ .hdmi = {
+ .ctrl = gk104_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 2, 1, 0, 3 },
+ .links = gf119_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = gf119_sor_dp_pattern,
+ .drive = gf119_sor_dp_drive,
+ .vcpi = gf119_sor_dp_vcpi,
+ .audio = gf119_sor_dp_audio,
+ .audio_sym = gf119_sor_dp_audio_sym,
+ .watermark = gf119_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gf119_hda_hpd,
+ .eld = gf119_hda_eld,
+ },
+};
+
+int
+gk104_sor_new(struct nvkm_disp *disp, int id)
+{
+ return gf119_sor_new_(&gk104_sor, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c
index 4cf8ad4d18ab..60230957d82b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c
@@ -21,34 +21,47 @@
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nv50.h"
-#include "outpdp.h"
+#include "ior.h"
-int
-gm107_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
+void
+gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = outp->base.or * 0x800;
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
const u32 data = 0x01010101 * pattern;
- if (outp->base.info.sorconf.link & 1)
+ if (sor->asy.link & 1)
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data);
else
nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data);
- return 0;
}
-static const struct nvkm_output_dp_func
-gm107_sor_dp_func = {
- .pattern = gm107_sor_dp_pattern,
- .lnk_pwr = g94_sor_dp_lnk_pwr,
- .lnk_ctl = gf119_sor_dp_lnk_ctl,
- .drv_ctl = gf119_sor_dp_drv_ctl,
- .vcpi = gf119_sor_dp_vcpi,
+static const struct nvkm_ior_func
+gm107_sor = {
+ .state = gf119_sor_state,
+ .power = nv50_sor_power,
+ .clock = gf119_sor_clock,
+ .hdmi = {
+ .ctrl = gk104_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 0, 1, 2, 3 },
+ .links = gf119_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = gm107_sor_dp_pattern,
+ .drive = gf119_sor_dp_drive,
+ .vcpi = gf119_sor_dp_vcpi,
+ .audio = gf119_sor_dp_audio,
+ .audio_sym = gf119_sor_dp_audio_sym,
+ .watermark = gf119_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gf119_hda_hpd,
+ .eld = gf119_hda_eld,
+ },
};
int
-gm107_sor_dp_new(struct nvkm_disp *disp, int index,
- struct dcb_output *dcbE, struct nvkm_output **poutp)
+gm107_sor_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_dp_new_(&gm107_sor_dp_func, disp, index, dcbE, poutp);
+ return gf119_sor_new_(&gm107_sor, disp, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c
index 81b788fa61be..f9b8107aa2a2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c
@@ -21,111 +21,104 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outpdp.h"
+#include "ior.h"
-#include <subdev/timer.h>
-
-static inline u32
-gm200_sor_soff(struct nvkm_output_dp *outp)
-{
- return (ffs(outp->base.info.or) - 1) * 0x800;
-}
-
-static inline u32
-gm200_sor_loff(struct nvkm_output_dp *outp)
+static void
+gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
{
- return gm200_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
-}
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 loff = nv50_sor_link(sor);
+ const u32 shift = sor->func->dp.lanes[ln] * 8;
+ u32 data[4];
-void
-gm200_sor_magic(struct nvkm_output *outp)
-{
- struct nvkm_device *device = outp->disp->engine.subdev.device;
- const u32 soff = outp->or * 0x100;
- const u32 data = outp->or + 1;
- if (outp->info.sorconf.link & 1)
- nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data);
- if (outp->info.sorconf.link & 2)
- nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data);
-}
+ pu &= 0x0f;
-static inline u32
-gm200_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
-{
- return lane * 0x08;
+ data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
+ data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
+ data[2] = nvkm_rd32(device, 0x61c130 + loff);
+ if ((data[2] & 0x00000f00) < (pu << 8) || ln == 0)
+ data[2] = (data[2] & ~0x00000f00) | (pu << 8);
+ nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
+ nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
+ nvkm_wr32(device, 0x61c130 + loff, data[2]);
+ data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
+ nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift));
}
-static int
-gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
+static void
+gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 soff = gm200_sor_soff(outp);
- const u32 loff = gm200_sor_loff(outp);
- u32 mask = 0, i;
+ struct nvkm_device *device = outp->disp->engine.subdev.device;
+ const u32 moff = __ffs(outp->info.or) * 0x100;
+ const u32 sor = ior ? ior->id + 1 : 0;
+ u32 link = ior ? (ior->asy.link == 2) : 0;
- for (i = 0; i < nr; i++)
- mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3);
+ if (outp->info.sorconf.link & 1) {
+ nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor);
+ link++;
+ }
- nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
- nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
- break;
- );
- return 0;
+ if (outp->info.sorconf.link & 2)
+ nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor);
}
static int
-gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
- int ln, int vs, int pe, int pc)
+gm200_sor_route_get(struct nvkm_outp *outp, int *link)
{
- struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- struct nvkm_bios *bios = device->bios;
- const u32 shift = gm200_sor_dp_lane_map(device, ln);
- const u32 loff = gm200_sor_loff(outp);
- u32 addr, data[4];
- u8 ver, hdr, cnt, len;
- struct nvbios_dpout info;
- struct nvbios_dpcfg ocfg;
+ struct nvkm_device *device = outp->disp->engine.subdev.device;
+ const int sublinks = outp->info.sorconf.link;
+ int lnk[2], sor[2], m, s;
- addr = nvbios_dpout_match(bios, outp->base.info.hasht,
- outp->base.info.hashm,
- &ver, &hdr, &cnt, &len, &info);
- if (!addr)
- return -ENODEV;
+ for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) {
+ if (sublinks & BIT(s)) {
+ u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80));
+ lnk[s] = (data & 0x00000010) >> 4;
+ sor[s] = (data & 0x0000000f);
+ if (!sor[s])
+ return -1;
+ *link |= lnk[s];
+ }
+ }
- addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
- &ver, &hdr, &cnt, &len, &ocfg);
- if (!addr)
- return -EINVAL;
- ocfg.tx_pu &= 0x0f;
+ if (sublinks == 3) {
+ if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1]))
+ return -1;
+ }
- data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
- data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
- data[2] = nvkm_rd32(device, 0x61c130 + loff);
- if ((data[2] & 0x00000f00) < (ocfg.tx_pu << 8) || ln == 0)
- data[2] = (data[2] & ~0x00000f00) | (ocfg.tx_pu << 8);
- nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
- nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
- nvkm_wr32(device, 0x61c130 + loff, data[2]);
- data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
- nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
- return 0;
+ return ((sublinks & 1) ? sor[0] : sor[1]) - 1;
}
-static const struct nvkm_output_dp_func
-gm200_sor_dp_func = {
- .pattern = gm107_sor_dp_pattern,
- .lnk_pwr = gm200_sor_dp_lnk_pwr,
- .lnk_ctl = gf119_sor_dp_lnk_ctl,
- .drv_ctl = gm200_sor_dp_drv_ctl,
- .vcpi = gf119_sor_dp_vcpi,
+static const struct nvkm_ior_func
+gm200_sor = {
+ .route = {
+ .get = gm200_sor_route_get,
+ .set = gm200_sor_route_set,
+ },
+ .state = gf119_sor_state,
+ .power = nv50_sor_power,
+ .clock = gf119_sor_clock,
+ .hdmi = {
+ .ctrl = gk104_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 0, 1, 2, 3 },
+ .links = gf119_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = gm107_sor_dp_pattern,
+ .drive = gm200_sor_dp_drive,
+ .vcpi = gf119_sor_dp_vcpi,
+ .audio = gf119_sor_dp_audio,
+ .audio_sym = gf119_sor_dp_audio_sym,
+ .watermark = gf119_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gf119_hda_hpd,
+ .eld = gf119_hda_eld,
+ },
};
int
-gm200_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
- struct nvkm_output **poutp)
+gm200_sor_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_dp_new_(&gm200_sor_dp_func, disp, index, dcbE, poutp);
+ return gf119_sor_new_(&gm200_sor, disp, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c
new file mode 100644
index 000000000000..da228b54b43e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+#include <subdev/timer.h>
+
+void
+gt215_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ const u32 data = 0x80000000 | (0x00000001 * enable);
+ const u32 mask = 0x8000000d;
+ nvkm_mask(device, 0x61c1e0 + soff, mask, data);
+ nvkm_msec(device, 2000,
+ if (!(nvkm_rd32(device, 0x61c1e0 + soff) & 0x80000000))
+ break;
+ );
+}
+
+static const struct nvkm_ior_func
+gt215_sor = {
+ .state = g94_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
+ .hdmi = {
+ .ctrl = gt215_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 2, 1, 0, 3 },
+ .links = g94_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = g94_sor_dp_pattern,
+ .drive = g94_sor_dp_drive,
+ .audio = gt215_sor_dp_audio,
+ .audio_sym = g94_sor_dp_audio_sym,
+ .activesym = g94_sor_dp_activesym,
+ .watermark = g94_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gt215_hda_hpd,
+ .eld = gt215_hda_eld,
+ },
+};
+
+int
+gt215_sor_new(struct nvkm_disp *disp, int id)
+{
+ return nv50_sor_new_(&gt215_sor, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c
new file mode 100644
index 000000000000..c0179ccb956d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+static const struct nvkm_ior_func
+mcp77_sor = {
+ .state = g94_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
+ .hdmi = {
+ .ctrl = g84_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 2, 1, 0, 3},
+ .links = g94_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = g94_sor_dp_pattern,
+ .drive = g94_sor_dp_drive,
+ .audio_sym = g94_sor_dp_audio_sym,
+ .activesym = g94_sor_dp_activesym,
+ .watermark = g94_sor_dp_watermark,
+ },
+};
+
+int
+mcp77_sor_new(struct nvkm_disp *disp, int id)
+{
+ return nv50_sor_new_(&mcp77_sor, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c
new file mode 100644
index 000000000000..9bb01cd96697
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 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.
+ */
+#include "ior.h"
+
+static const struct nvkm_ior_func
+mcp89_sor = {
+ .state = g94_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
+ .hdmi = {
+ .ctrl = gt215_hdmi_ctrl,
+ },
+ .dp = {
+ .lanes = { 3, 2, 1, 0 },
+ .links = g94_sor_dp_links,
+ .power = g94_sor_dp_power,
+ .pattern = g94_sor_dp_pattern,
+ .drive = g94_sor_dp_drive,
+ .audio = gt215_sor_dp_audio,
+ .audio_sym = g94_sor_dp_audio_sym,
+ .activesym = g94_sor_dp_activesym,
+ .watermark = g94_sor_dp_watermark,
+ },
+ .hda = {
+ .hpd = gt215_hda_hpd,
+ .eld = gt215_hda_eld,
+ },
+};
+
+int
+mcp89_sor_new(struct nvkm_disp *disp, int id)
+{
+ return nv50_sor_new_(&mcp89_sor, disp, id);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c
index 53596bed3c36..f3ebd0c22e7d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c
@@ -21,59 +21,87 @@
*
* Authors: Ben Skeggs
*/
-#include "nv50.h"
-#include "outp.h"
+#include "ior.h"
-#include <core/client.h>
#include <subdev/timer.h>
-#include <nvif/cl5070.h>
-#include <nvif/unpack.h>
-
-int
-nv50_sor_power(NV50_DISP_MTHD_V1)
+void
+nv50_sor_clock(struct nvkm_ior *sor)
{
- struct nvkm_device *device = disp->base.engine.subdev.device;
- union {
- struct nv50_disp_sor_pwr_v0 v0;
- } *args = data;
- const u32 soff = outp->or * 0x800;
- u32 stat;
- int ret = -ENOSYS;
-
- nvif_ioctl(object, "disp sor pwr size %d\n", size);
- if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
- nvif_ioctl(object, "disp sor pwr vers %d state %d\n",
- args->v0.version, args->v0.state);
- stat = !!args->v0.state;
- } else
- return ret;
-
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const int div = sor->asy.link == 3;
+ const u32 soff = nv50_ior_base(sor);
+ nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);
+}
+static void
+nv50_sor_power_wait(struct nvkm_device *device, u32 soff)
+{
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))
break;
);
- nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000 | stat);
- nvkm_msec(device, 2000,
- if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))
- break;
- );
+}
+
+void
+nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu,
+ bool data, bool vsync, bool hsync)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 soff = nv50_ior_base(sor);
+ const u32 shift = normal ? 0 : 16;
+ const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
+ const u32 field = 0x80000000 | (0x00000001 << shift);
+
+ nv50_sor_power_wait(device, soff);
+ nvkm_mask(device, 0x61c004 + soff, field, state);
+ nv50_sor_power_wait(device, soff);
+
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
break;
);
- return 0;
}
-static const struct nvkm_output_func
-nv50_sor_output_func = {
+void
+nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
+ u32 ctrl = nvkm_rd32(device, 0x610b70 + coff);
+
+ state->proto_evo = (ctrl & 0x00000f00) >> 8;
+ switch (state->proto_evo) {
+ case 0: state->proto = LVDS; state->link = 1; break;
+ case 1: state->proto = TMDS; state->link = 1; break;
+ case 2: state->proto = TMDS; state->link = 2; break;
+ case 5: state->proto = TMDS; state->link = 3; break;
+ default:
+ state->proto = UNKNOWN;
+ break;
+ }
+
+ state->head = ctrl & 0x00000003;
+}
+
+int
+nv50_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id)
+{
+ struct nvkm_device *device = disp->engine.subdev.device;
+ if (!(nvkm_rd32(device, 0x610184) & (0x01000000 << id)))
+ return 0;
+ return nvkm_ior_new_(func, disp, SOR, id);
+}
+
+static const struct nvkm_ior_func
+nv50_sor = {
+ .state = nv50_sor_state,
+ .power = nv50_sor_power,
+ .clock = nv50_sor_clock,
};
int
-nv50_sor_output_new(struct nvkm_disp *disp, int index,
- struct dcb_output *dcbE, struct nvkm_output **poutp)
+nv50_sor_new(struct nvkm_disp *disp, int id)
{
- return nvkm_output_new_(&nv50_sor_output_func, disp,
- index, dcbE, poutp);
+ return nv50_sor_new_(&nv50_sor, disp, id);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
index 3a24788c3185..a7e55c422501 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
@@ -148,7 +148,7 @@ gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl)
case NVKM_MEM_TARGET_NCOH: target = 3; break;
default:
WARN_ON(1);
- return;
+ goto unlock;
}
nvkm_wr32(device, 0x002270, (nvkm_memory_addr(mem) >> 12) |
@@ -160,6 +160,7 @@ gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl)
& 0x00100000),
msecs_to_jiffies(2000)) == 0)
nvkm_error(subdev, "runlist %d update timeout\n", runl);
+unlock:
mutex_unlock(&subdev->mutex);
}