diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index e3f5659295e2..542d95145a67 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -27,6 +27,8 @@ struct nv50_disp_scanoutpos_v0 { struct nv50_disp_mthd_v1 { __u8 version; +#define NV50_DISP_MTHD_V1_ACQUIRE 0x01 +#define NV50_DISP_MTHD_V1_RELEASE 0x02 #define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 #define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 #define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 @@ -39,6 +41,13 @@ struct nv50_disp_mthd_v1 { __u8 pad06[2]; }; +struct nv50_disp_acquire_v0 { + __u8 version; + __u8 or; + __u8 link; + __u8 pad03[5]; +}; + struct nv50_disp_dac_load_v0 { __u8 version; __u8 load; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 9a0772ad495a..b998c33af18a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1533,7 +1533,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, if (conf & 0x100000) entry->i2c_upper_default = true; - entry->hasht = (entry->location << 4) | entry->type; + entry->hasht = (entry->extdev << 8) | (entry->location << 4) | + entry->type; entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 198e5f27682f..e28d966946a1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -42,6 +42,7 @@ struct nouveau_encoder { struct dcb_output *dcb; int or; + int link; struct i2c_adapter *i2c; struct nvkm_i2c_aux *aux; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index e6cc1d843c92..e3132a2ce34d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2403,6 +2403,51 @@ out: /****************************************************************************** * Output path helpers *****************************************************************************/ +static void +nv50_outp_release(struct nouveau_encoder *nv_encoder) +{ + struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); + struct { + struct nv50_disp_mthd_v1 base; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_RELEASE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nv_encoder->or = -1; + nv_encoder->link = 0; +} + +static int +nv50_outp_acquire(struct nouveau_encoder *nv_encoder) +{ + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nv50_disp *disp = nv50_disp(drm->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_acquire_v0 info; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_ACQUIRE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + int ret; + + ret = nvif_mthd(disp->disp, 0, &args, sizeof(args)); + if (ret) { + NV_ERROR(drm, "error acquiring output path: %d\n", ret); + return ret; + } + + nv_encoder->or = args.info.or; + nv_encoder->link = args.info.link; + return 0; +} + static int nv50_outp_atomic_check_view(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -2482,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -2493,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder) struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; u32 *push; + nv50_outp_acquire(nv_encoder); + push = evo_wait(mast, 8); if (push) { if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { @@ -2592,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) @@ -2759,6 +2806,8 @@ struct nv50_mstm { struct nv50_msto *msto[4]; bool modified; + bool disabled; + int links; }; struct nv50_mstc { @@ -2907,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder) r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); WARN_ON(!r); - if (mstm->outp->dcb->sorconf.link & 1) + if (!mstm->links++) + nv50_outp_acquire(mstm->outp); + + if (mstm->outp->link & 1) proto = 0x8; else proto = 0x9; @@ -2939,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder) mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); mstm->modified = true; + if (!--mstm->links) + mstm->disabled = true; msto->disabled = true; } @@ -3154,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm) nv50_msto_prepare(msto); } } + + if (mstm->disabled) { + if (!mstm->links) + nv50_outp_release(mstm->outp); + mstm->disabled = false; + } } static void @@ -3452,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder) nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); nv50_audio_disable(encoder, nv_crtc); nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); + nv50_outp_release(nv_encoder); } } @@ -3480,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder) nv_connector = nouveau_encoder_connector_get(nv_encoder); nv_encoder->crtc = encoder->crtc; + nv50_outp_acquire(nv_encoder); switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: - if (nv_encoder->dcb->sorconf.link & 1) { + if (nv_encoder->link & 1) { proto = 0x1; /* Only enable dual-link if: * - Need to (i.e. rate > 165MHz) @@ -3541,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder) else depth = 0x6; - if (nv_encoder->dcb->sorconf.link & 1) + if (nv_encoder->link & 1) proto = 0x8; else proto = 0x9; @@ -3600,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->update = nv50_sor_update; encoder = to_drm_encoder(nv_encoder); @@ -3673,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -3687,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder) u8 proto, depth; u32 *push; + nv50_outp_acquire(nv_encoder); + nv_connector = nouveau_encoder_connector_get(nv_encoder); switch (nv_connector->base.display_info.bpc) { case 10: depth = 0x6; break; @@ -3774,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->i2c = ddc; nv_encoder->aux = aux; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index b531890b1a6f..0f1c223cc7a8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -24,7 +24,6 @@ #include "dp.h" #include "conn.h" #include "ior.h" -#include "nv50.h" #include #include @@ -351,7 +350,6 @@ static const struct dp_rates { static int nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) { - struct nv50_disp *disp = nv50_disp(dp->outp.disp); 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]; @@ -361,9 +359,6 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) int ret = -EINVAL; u8 pwr; - if (!dp->outp.info.location && disp->func->sor.magic) - disp->func->sor.magic(&dp->outp); - /* Find the lowest configuration of the OR that can support * the required link rate. * diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index c0d730af4c97..7a8dff7b8c95 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -306,9 +306,6 @@ gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head) 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); @@ -377,6 +374,7 @@ gf119_disp_super(struct work_struct *work) nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head->id); gf119_disp_intr_unk2_0(disp, head->id); } + nvkm_outp_route(&disp->base); list_for_each_entry(head, &disp->base.head, head) { if (!(mask[head->id] & 0x00010000)) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c index ae0b97332e46..292d3b5f9704 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c @@ -35,9 +35,7 @@ gm200_disp = { .root = &gm200_disp_root_oclass, .head.new = gf119_head_new, .dac = { .nr = 3, .new = gf119_dac_new }, - .sor.nr = 4, - .sor.new = gm200_sor_new, - .sor.magic = gm200_sor_magic, + .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 c6fe7797a803..39eb98b2c3a2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c @@ -34,9 +34,7 @@ gp100_disp = { .super = gf119_disp_super, .root = &gp100_disp_root_oclass, .head.new = gf119_head_new, - .sor.nr = 4, - .sor.new = gm200_sor_new, - .sor.magic = gm200_sor_magic, + .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 279d125fe265..91d70fe18275 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c @@ -60,9 +60,7 @@ gp102_disp = { .super = gf119_disp_super, .root = &gp102_disp_root_oclass, .head.new = gf119_head_new, - .sor.nr = 4, - .sor.new = gm200_sor_new, - .sor.magic = gm200_sor_magic, + .sor = { .nr = 4, .new = gm200_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index a43a924debfe..a2e38d4780b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -17,6 +17,7 @@ struct nvkm_ior { struct list_head head; struct nvkm_ior_state { + struct nvkm_outp *outp; unsigned rgdiv; unsigned proto_evo:4; enum nvkm_ior_proto { @@ -40,6 +41,11 @@ struct nvkm_ior { }; 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); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index b7365b56ed19..58d46cefe7b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -661,6 +661,7 @@ nv50_disp_super(struct work_struct *work) continue; nv50_disp_intr_unk20_0(disp, head->id); } + nvkm_outp_route(&disp->base); list_for_each_entry(head, &disp->base.head, head) { if (!(super & (0x00000200 << head->id))) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index f87422de12b3..65d445687038 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -53,7 +53,6 @@ struct nv50_disp_func { struct { int nr; int (*new)(struct nvkm_disp *, int id); - void (*magic)(struct nvkm_output *); } sor; struct { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index 09e8ebbd4ee9..ef201f1597c7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -28,8 +28,35 @@ #include #include +void +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_output *outp, enum nvkm_ior_type *type) +nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) { switch (outp->info.location) { case 0: @@ -57,6 +84,75 @@ nvkm_outp_xlat(struct nvkm_output *outp, enum nvkm_ior_type *type) 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->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->id == __ffs(outp->info.or)) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + return -ENOSPC; +} + void nvkm_outp_fini(struct nvkm_outp *outp) { @@ -65,22 +161,36 @@ nvkm_outp_fini(struct nvkm_outp *outp) } static void -nvkm_outp_init_route(struct nvkm_output *outp) +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; + 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 (1) { + 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; + id = ffs(outp->info.or) - 1; + link = (ior->type == SOR) ? outp->info.sorconf.link : 0; } ior = nvkm_ior_find(disp, type, id); @@ -89,7 +199,16 @@ nvkm_outp_init_route(struct nvkm_output *outp) return; } - outp->ior = ior; + /* 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 diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index 785a920eaf74..106141eb3e32 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -18,6 +18,9 @@ struct nvkm_outp { struct nvkm_conn *conn; /* Assembly state. */ +#define NVKM_OUTP_PRIV 1 +#define NVKM_OUTP_USER 2 + u8 acquired:2; struct nvkm_ior *ior; }; @@ -28,6 +31,9 @@ int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, 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 *); @@ -39,8 +45,6 @@ struct nvkm_outp_func { #define nvkm_output_func nvkm_outp_func #define nvkm_output_new_ nvkm_outp_new_ -void gm200_sor_magic(struct nvkm_output *outp); - #define OUTP_MSG(o,l,f,a...) do { \ struct nvkm_outp *_outp = (o); \ nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index f3ebdd032b9b..4c7b7090b1a0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -94,6 +94,24 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) } 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); + 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; @@ -102,7 +120,11 @@ 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))) { 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; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index 0ba7d03efa78..25528a15516b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c @@ -45,20 +45,55 @@ gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); } -void -gm200_sor_magic(struct nvkm_output *outp) +static void +gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior) { 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); + const u32 moff = __ffs(outp->info.or) * 0x100; + const u32 sor = ior ? ior->id + 1 : 0; + u32 link = ior ? (ior->asy.link == 2) : 0; + + if (outp->info.sorconf.link & 1) { + nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor); + link++; + } + if (outp->info.sorconf.link & 2) - nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data); + nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor); +} + +static int +gm200_sor_route_get(struct nvkm_outp *outp, int *link) +{ + struct nvkm_device *device = outp->disp->engine.subdev.device; + const int sublinks = outp->info.sorconf.link; + int lnk[2], sor[2], m, s; + + 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]; + } + } + + if (sublinks == 3) { + if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1])) + return -1; + } + + return ((sublinks & 1) ? sor[0] : sor[1]) - 1; } 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, .hdmi = {