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