aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2017-05-19 09:59:35 -0400
committerBen Skeggs <bskeggs@redhat.com>2017-06-16 00:04:57 -0400
commit6c22ea3747fd36409ce4a1e1a0cbac40f93e1e71 (patch)
tree3e59368a13cff78bed2206404138b0849b56c69d
parent3c66c87dc96b3113b5ee84604800c2aabbb48994 (diff)
drm/nouveau/disp: introduce acquire/release display path methods
These exist to give NVKM information on the set of display paths that the DD needs to be active at any given time. Previously, the supervisor attempted to determine this solely from OR state, but there's a few configurations where this information on its own isn't enough to determine the specific display paths in question: - ANX9805, where the PIOR protocol for both DP and TMDS is TMDS. - On a device using DCB Switched Outputs. - On GM20x and newer, with a crossbar between the SOR and macro links. After this commit, the DD tells NVKM *exactly* which display path it's attempting a modeset on. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl5070.h9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h1
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c131
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c49
16 files changed, 287 insertions, 40 deletions
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 {
27 27
28struct nv50_disp_mthd_v1 { 28struct nv50_disp_mthd_v1 {
29 __u8 version; 29 __u8 version;
30#define NV50_DISP_MTHD_V1_ACQUIRE 0x01
31#define NV50_DISP_MTHD_V1_RELEASE 0x02
30#define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 32#define NV50_DISP_MTHD_V1_DAC_LOAD 0x11
31#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 33#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21
32#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 34#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22
@@ -39,6 +41,13 @@ struct nv50_disp_mthd_v1 {
39 __u8 pad06[2]; 41 __u8 pad06[2];
40}; 42};
41 43
44struct nv50_disp_acquire_v0 {
45 __u8 version;
46 __u8 or;
47 __u8 link;
48 __u8 pad03[5];
49};
50
42struct nv50_disp_dac_load_v0 { 51struct nv50_disp_dac_load_v0 {
43 __u8 version; 52 __u8 version;
44 __u8 load; 53 __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,
1533 if (conf & 0x100000) 1533 if (conf & 0x100000)
1534 entry->i2c_upper_default = true; 1534 entry->i2c_upper_default = true;
1535 1535
1536 entry->hasht = (entry->location << 4) | entry->type; 1536 entry->hasht = (entry->extdev << 8) | (entry->location << 4) |
1537 entry->type;
1537 entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; 1538 entry->hashm = (entry->heads << 8) | (link << 6) | entry->or;
1538 return true; 1539 return true;
1539} 1540}
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 {
42 42
43 struct dcb_output *dcb; 43 struct dcb_output *dcb;
44 int or; 44 int or;
45 int link;
45 46
46 struct i2c_adapter *i2c; 47 struct i2c_adapter *i2c;
47 struct nvkm_i2c_aux *aux; 48 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:
2403/****************************************************************************** 2403/******************************************************************************
2404 * Output path helpers 2404 * Output path helpers
2405 *****************************************************************************/ 2405 *****************************************************************************/
2406static void
2407nv50_outp_release(struct nouveau_encoder *nv_encoder)
2408{
2409 struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev);
2410 struct {
2411 struct nv50_disp_mthd_v1 base;
2412 } args = {
2413 .base.version = 1,
2414 .base.method = NV50_DISP_MTHD_V1_RELEASE,
2415 .base.hasht = nv_encoder->dcb->hasht,
2416 .base.hashm = nv_encoder->dcb->hashm,
2417 };
2418
2419 nvif_mthd(disp->disp, 0, &args, sizeof(args));
2420 nv_encoder->or = -1;
2421 nv_encoder->link = 0;
2422}
2423
2424static int
2425nv50_outp_acquire(struct nouveau_encoder *nv_encoder)
2426{
2427 struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
2428 struct nv50_disp *disp = nv50_disp(drm->dev);
2429 struct {
2430 struct nv50_disp_mthd_v1 base;
2431 struct nv50_disp_acquire_v0 info;
2432 } args = {
2433 .base.version = 1,
2434 .base.method = NV50_DISP_MTHD_V1_ACQUIRE,
2435 .base.hasht = nv_encoder->dcb->hasht,
2436 .base.hashm = nv_encoder->dcb->hashm,
2437 };
2438 int ret;
2439
2440 ret = nvif_mthd(disp->disp, 0, &args, sizeof(args));
2441 if (ret) {
2442 NV_ERROR(drm, "error acquiring output path: %d\n", ret);
2443 return ret;
2444 }
2445
2446 nv_encoder->or = args.info.or;
2447 nv_encoder->link = args.info.link;
2448 return 0;
2449}
2450
2406static int 2451static int
2407nv50_outp_atomic_check_view(struct drm_encoder *encoder, 2452nv50_outp_atomic_check_view(struct drm_encoder *encoder,
2408 struct drm_crtc_state *crtc_state, 2453 struct drm_crtc_state *crtc_state,
@@ -2482,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder)
2482 } 2527 }
2483 2528
2484 nv_encoder->crtc = NULL; 2529 nv_encoder->crtc = NULL;
2530 nv50_outp_release(nv_encoder);
2485} 2531}
2486 2532
2487static void 2533static void
@@ -2493,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder)
2493 struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; 2539 struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode;
2494 u32 *push; 2540 u32 *push;
2495 2541
2542 nv50_outp_acquire(nv_encoder);
2543
2496 push = evo_wait(mast, 8); 2544 push = evo_wait(mast, 8);
2497 if (push) { 2545 if (push) {
2498 if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { 2546 if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
@@ -2592,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
2592 if (!nv_encoder) 2640 if (!nv_encoder)
2593 return -ENOMEM; 2641 return -ENOMEM;
2594 nv_encoder->dcb = dcbe; 2642 nv_encoder->dcb = dcbe;
2595 nv_encoder->or = ffs(dcbe->or) - 1;
2596 2643
2597 bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); 2644 bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index);
2598 if (bus) 2645 if (bus)
@@ -2759,6 +2806,8 @@ struct nv50_mstm {
2759 struct nv50_msto *msto[4]; 2806 struct nv50_msto *msto[4];
2760 2807
2761 bool modified; 2808 bool modified;
2809 bool disabled;
2810 int links;
2762}; 2811};
2763 2812
2764struct nv50_mstc { 2813struct nv50_mstc {
@@ -2907,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder)
2907 r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); 2956 r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots);
2908 WARN_ON(!r); 2957 WARN_ON(!r);
2909 2958
2910 if (mstm->outp->dcb->sorconf.link & 1) 2959 if (!mstm->links++)
2960 nv50_outp_acquire(mstm->outp);
2961
2962 if (mstm->outp->link & 1)
2911 proto = 0x8; 2963 proto = 0x8;
2912 else 2964 else
2913 proto = 0x9; 2965 proto = 0x9;
@@ -2939,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder)
2939 2991
2940 mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); 2992 mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0);
2941 mstm->modified = true; 2993 mstm->modified = true;
2994 if (!--mstm->links)
2995 mstm->disabled = true;
2942 msto->disabled = true; 2996 msto->disabled = true;
2943} 2997}
2944 2998
@@ -3154,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm)
3154 nv50_msto_prepare(msto); 3208 nv50_msto_prepare(msto);
3155 } 3209 }
3156 } 3210 }
3211
3212 if (mstm->disabled) {
3213 if (!mstm->links)
3214 nv50_outp_release(mstm->outp);
3215 mstm->disabled = false;
3216 }
3157} 3217}
3158 3218
3159static void 3219static void
@@ -3452,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder)
3452 nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); 3512 nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0);
3453 nv50_audio_disable(encoder, nv_crtc); 3513 nv50_audio_disable(encoder, nv_crtc);
3454 nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); 3514 nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc);
3515 nv50_outp_release(nv_encoder);
3455 } 3516 }
3456} 3517}
3457 3518
@@ -3480,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder)
3480 3541
3481 nv_connector = nouveau_encoder_connector_get(nv_encoder); 3542 nv_connector = nouveau_encoder_connector_get(nv_encoder);
3482 nv_encoder->crtc = encoder->crtc; 3543 nv_encoder->crtc = encoder->crtc;
3544 nv50_outp_acquire(nv_encoder);
3483 3545
3484 switch (nv_encoder->dcb->type) { 3546 switch (nv_encoder->dcb->type) {
3485 case DCB_OUTPUT_TMDS: 3547 case DCB_OUTPUT_TMDS:
3486 if (nv_encoder->dcb->sorconf.link & 1) { 3548 if (nv_encoder->link & 1) {
3487 proto = 0x1; 3549 proto = 0x1;
3488 /* Only enable dual-link if: 3550 /* Only enable dual-link if:
3489 * - Need to (i.e. rate > 165MHz) 3551 * - Need to (i.e. rate > 165MHz)
@@ -3541,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
3541 else 3603 else
3542 depth = 0x6; 3604 depth = 0x6;
3543 3605
3544 if (nv_encoder->dcb->sorconf.link & 1) 3606 if (nv_encoder->link & 1)
3545 proto = 0x8; 3607 proto = 0x8;
3546 else 3608 else
3547 proto = 0x9; 3609 proto = 0x9;
@@ -3600,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
3600 if (!nv_encoder) 3662 if (!nv_encoder)
3601 return -ENOMEM; 3663 return -ENOMEM;
3602 nv_encoder->dcb = dcbe; 3664 nv_encoder->dcb = dcbe;
3603 nv_encoder->or = ffs(dcbe->or) - 1;
3604 nv_encoder->update = nv50_sor_update; 3665 nv_encoder->update = nv50_sor_update;
3605 3666
3606 encoder = to_drm_encoder(nv_encoder); 3667 encoder = to_drm_encoder(nv_encoder);
@@ -3673,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder)
3673 } 3734 }
3674 3735
3675 nv_encoder->crtc = NULL; 3736 nv_encoder->crtc = NULL;
3737 nv50_outp_release(nv_encoder);
3676} 3738}
3677 3739
3678static void 3740static void
@@ -3687,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder)
3687 u8 proto, depth; 3749 u8 proto, depth;
3688 u32 *push; 3750 u32 *push;
3689 3751
3752 nv50_outp_acquire(nv_encoder);
3753
3690 nv_connector = nouveau_encoder_connector_get(nv_encoder); 3754 nv_connector = nouveau_encoder_connector_get(nv_encoder);
3691 switch (nv_connector->base.display_info.bpc) { 3755 switch (nv_connector->base.display_info.bpc) {
3692 case 10: depth = 0x6; break; 3756 case 10: depth = 0x6; break;
@@ -3774,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
3774 if (!nv_encoder) 3838 if (!nv_encoder)
3775 return -ENOMEM; 3839 return -ENOMEM;
3776 nv_encoder->dcb = dcbe; 3840 nv_encoder->dcb = dcbe;
3777 nv_encoder->or = ffs(dcbe->or) - 1;
3778 nv_encoder->i2c = ddc; 3841 nv_encoder->i2c = ddc;
3779 nv_encoder->aux = aux; 3842 nv_encoder->aux = aux;
3780 3843
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 @@
24#include "dp.h" 24#include "dp.h"
25#include "conn.h" 25#include "conn.h"
26#include "ior.h" 26#include "ior.h"
27#include "nv50.h"
28 27
29#include <subdev/bios.h> 28#include <subdev/bios.h>
30#include <subdev/bios/init.h> 29#include <subdev/bios/init.h>
@@ -351,7 +350,6 @@ static const struct dp_rates {
351static int 350static int
352nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) 351nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
353{ 352{
354 struct nv50_disp *disp = nv50_disp(dp->outp.disp);
355 struct nvkm_ior *ior = dp->outp.ior; 353 struct nvkm_ior *ior = dp->outp.ior;
356 const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; 354 const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
357 const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; 355 const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
@@ -361,9 +359,6 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
361 int ret = -EINVAL; 359 int ret = -EINVAL;
362 u8 pwr; 360 u8 pwr;
363 361
364 if (!dp->outp.info.location && disp->func->sor.magic)
365 disp->func->sor.magic(&dp->outp);
366
367 /* Find the lowest configuration of the OR that can support 362 /* Find the lowest configuration of the OR that can support
368 * the required link rate. 363 * the required link rate.
369 * 364 *
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)
306 306
307 if (nvkm_output_dp_train(outp, pclk)) 307 if (nvkm_output_dp_train(outp, pclk))
308 OUTP_ERR(outp, "link not trained before attach"); 308 OUTP_ERR(outp, "link not trained before attach");
309 } else {
310 if (disp->func->sor.magic)
311 disp->func->sor.magic(outp);
312 } 309 }
313 310
314 exec_clkcmp(disp, head, 0, pclk, &conf); 311 exec_clkcmp(disp, head, 0, pclk, &conf);
@@ -377,6 +374,7 @@ gf119_disp_super(struct work_struct *work)
377 nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head->id); 374 nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head->id);
378 gf119_disp_intr_unk2_0(disp, head->id); 375 gf119_disp_intr_unk2_0(disp, head->id);
379 } 376 }
377 nvkm_outp_route(&disp->base);
380 list_for_each_entry(head, &disp->base.head, head) { 378 list_for_each_entry(head, &disp->base.head, head) {
381 if (!(mask[head->id] & 0x00010000)) 379 if (!(mask[head->id] & 0x00010000))
382 continue; 380 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 = {
35 .root = &gm200_disp_root_oclass, 35 .root = &gm200_disp_root_oclass,
36 .head.new = gf119_head_new, 36 .head.new = gf119_head_new,
37 .dac = { .nr = 3, .new = gf119_dac_new }, 37 .dac = { .nr = 3, .new = gf119_dac_new },
38 .sor.nr = 4, 38 .sor = { .nr = 4, .new = gm200_sor_new },
39 .sor.new = gm200_sor_new,
40 .sor.magic = gm200_sor_magic,
41}; 39};
42 40
43int 41int
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 = {
34 .super = gf119_disp_super, 34 .super = gf119_disp_super,
35 .root = &gp100_disp_root_oclass, 35 .root = &gp100_disp_root_oclass,
36 .head.new = gf119_head_new, 36 .head.new = gf119_head_new,
37 .sor.nr = 4, 37 .sor = { .nr = 4, .new = gm200_sor_new },
38 .sor.new = gm200_sor_new,
39 .sor.magic = gm200_sor_magic,
40}; 38};
41 39
42int 40int
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 = {
60 .super = gf119_disp_super, 60 .super = gf119_disp_super,
61 .root = &gp102_disp_root_oclass, 61 .root = &gp102_disp_root_oclass,
62 .head.new = gf119_head_new, 62 .head.new = gf119_head_new,
63 .sor.nr = 4, 63 .sor = { .nr = 4, .new = gm200_sor_new },
64 .sor.new = gm200_sor_new,
65 .sor.magic = gm200_sor_magic,
66}; 64};
67 65
68int 66int
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 {
17 struct list_head head; 17 struct list_head head;
18 18
19 struct nvkm_ior_state { 19 struct nvkm_ior_state {
20 struct nvkm_outp *outp;
20 unsigned rgdiv; 21 unsigned rgdiv;
21 unsigned proto_evo:4; 22 unsigned proto_evo:4;
22 enum nvkm_ior_proto { 23 enum nvkm_ior_proto {
@@ -40,6 +41,11 @@ struct nvkm_ior {
40}; 41};
41 42
42struct nvkm_ior_func { 43struct nvkm_ior_func {
44 struct {
45 int (*get)(struct nvkm_outp *, int *link);
46 void (*set)(struct nvkm_outp *, struct nvkm_ior *);
47 } route;
48
43 void (*state)(struct nvkm_ior *, struct nvkm_ior_state *); 49 void (*state)(struct nvkm_ior *, struct nvkm_ior_state *);
44 void (*power)(struct nvkm_ior *, bool normal, bool pu, 50 void (*power)(struct nvkm_ior *, bool normal, bool pu,
45 bool data, bool vsync, bool hsync); 51 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)
661 continue; 661 continue;
662 nv50_disp_intr_unk20_0(disp, head->id); 662 nv50_disp_intr_unk20_0(disp, head->id);
663 } 663 }
664 nvkm_outp_route(&disp->base);
664 list_for_each_entry(head, &disp->base.head, head) { 665 list_for_each_entry(head, &disp->base.head, head) {
665 if (!(super & (0x00000200 << head->id))) 666 if (!(super & (0x00000200 << head->id)))
666 continue; 667 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 {
53 struct { 53 struct {
54 int nr; 54 int nr;
55 int (*new)(struct nvkm_disp *, int id); 55 int (*new)(struct nvkm_disp *, int id);
56 void (*magic)(struct nvkm_output *);
57 } sor; 56 } sor;
58 57
59 struct { 58 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 @@
28#include <subdev/bios/dcb.h> 28#include <subdev/bios/dcb.h>
29#include <subdev/i2c.h> 29#include <subdev/i2c.h>
30 30
31void
32nvkm_outp_route(struct nvkm_disp *disp)
33{
34 struct nvkm_outp *outp;
35 struct nvkm_ior *ior;
36
37 list_for_each_entry(ior, &disp->ior, head) {
38 if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) {
39 OUTP_DBG(outp, "release %s", ior->name);
40 if (ior->func->route.set)
41 ior->func->route.set(outp, NULL);
42 ior->arm.outp = NULL;
43 }
44 }
45
46 list_for_each_entry(ior, &disp->ior, head) {
47 if ((outp = ior->asy.outp)) {
48 OUTP_DBG(outp, "acquire %s", ior->name);
49 if (ior->asy.outp != ior->arm.outp) {
50 if (ior->func->route.set)
51 ior->func->route.set(outp, ior);
52 ior->arm.outp = ior->asy.outp;
53 }
54 }
55 }
56}
57
31static enum nvkm_ior_proto 58static enum nvkm_ior_proto
32nvkm_outp_xlat(struct nvkm_output *outp, enum nvkm_ior_type *type) 59nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type)
33{ 60{
34 switch (outp->info.location) { 61 switch (outp->info.location) {
35 case 0: 62 case 0:
@@ -58,6 +85,75 @@ nvkm_outp_xlat(struct nvkm_output *outp, enum nvkm_ior_type *type)
58} 85}
59 86
60void 87void
88nvkm_outp_release(struct nvkm_outp *outp, u8 user)
89{
90 struct nvkm_ior *ior = outp->ior;
91 OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior);
92 if (ior) {
93 outp->acquired &= ~user;
94 if (!outp->acquired) {
95 outp->ior->asy.outp = NULL;
96 outp->ior = NULL;
97 }
98 }
99}
100
101static inline int
102nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
103{
104 outp->ior = ior;
105 outp->ior->asy.outp = outp;
106 outp->ior->asy.link = outp->info.sorconf.link;
107 outp->acquired |= user;
108 return 0;
109}
110
111int
112nvkm_outp_acquire(struct nvkm_outp *outp, u8 user)
113{
114 struct nvkm_ior *ior = outp->ior;
115 enum nvkm_ior_proto proto;
116 enum nvkm_ior_type type;
117
118 OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior);
119 if (ior) {
120 outp->acquired |= user;
121 return 0;
122 }
123
124 /* Lookup a compatible, and unused, OR to assign to the device. */
125 proto = nvkm_outp_xlat(outp, &type);
126 if (proto == UNKNOWN)
127 return -ENOSYS;
128
129 /* First preference is to reuse the OR that is currently armed
130 * on HW, if any, in order to prevent unnecessary switching.
131 */
132 list_for_each_entry(ior, &outp->disp->ior, head) {
133 if (!ior->asy.outp && ior->arm.outp == outp)
134 return nvkm_outp_acquire_ior(outp, user, ior);
135 }
136
137 /* Failing that, a completely unused OR is the next best thing. */
138 list_for_each_entry(ior, &outp->disp->ior, head) {
139 if (!ior->asy.outp && ior->type == type && !ior->arm.outp &&
140 ior->id == __ffs(outp->info.or))
141 return nvkm_outp_acquire_ior(outp, user, ior);
142 }
143
144 /* Last resort is to assign an OR that's already active on HW,
145 * but will be released during the next modeset.
146 */
147 list_for_each_entry(ior, &outp->disp->ior, head) {
148 if (!ior->asy.outp && ior->type == type &&
149 ior->id == __ffs(outp->info.or))
150 return nvkm_outp_acquire_ior(outp, user, ior);
151 }
152
153 return -ENOSPC;
154}
155
156void
61nvkm_outp_fini(struct nvkm_outp *outp) 157nvkm_outp_fini(struct nvkm_outp *outp)
62{ 158{
63 if (outp->func->fini) 159 if (outp->func->fini)
@@ -65,22 +161,36 @@ nvkm_outp_fini(struct nvkm_outp *outp)
65} 161}
66 162
67static void 163static void
68nvkm_outp_init_route(struct nvkm_output *outp) 164nvkm_outp_init_route(struct nvkm_outp *outp)
69{ 165{
70 struct nvkm_disp *disp = outp->disp; 166 struct nvkm_disp *disp = outp->disp;
71 enum nvkm_ior_proto proto; 167 enum nvkm_ior_proto proto;
72 enum nvkm_ior_type type; 168 enum nvkm_ior_type type;
73 struct nvkm_ior *ior; 169 struct nvkm_ior *ior;
74 int id; 170 int id, link;
75 171
172 /* Find any OR from the class that is able to support this device. */
76 proto = nvkm_outp_xlat(outp, &type); 173 proto = nvkm_outp_xlat(outp, &type);
77 if (proto == UNKNOWN) 174 if (proto == UNKNOWN)
78 return; 175 return;
79 176
177 ior = nvkm_ior_find(disp, type, -1);
178 if (!ior) {
179 WARN_ON(1);
180 return;
181 }
182
80 /* Determine the specific OR, if any, this device is attached to. */ 183 /* Determine the specific OR, if any, this device is attached to. */
81 if (1) { 184 if (ior->func->route.get) {
185 id = ior->func->route.get(outp, &link);
186 if (id < 0) {
187 OUTP_DBG(outp, "no route");
188 return;
189 }
190 } else {
82 /* Prior to DCB 4.1, this is hardwired like so. */ 191 /* Prior to DCB 4.1, this is hardwired like so. */
83 id = ffs(outp->info.or) - 1; 192 id = ffs(outp->info.or) - 1;
193 link = (ior->type == SOR) ? outp->info.sorconf.link : 0;
84 } 194 }
85 195
86 ior = nvkm_ior_find(disp, type, id); 196 ior = nvkm_ior_find(disp, type, id);
@@ -89,7 +199,16 @@ nvkm_outp_init_route(struct nvkm_output *outp)
89 return; 199 return;
90 } 200 }
91 201
92 outp->ior = ior; 202 /* Determine if the OR is already configured for this device. */
203 ior->func->state(ior, &ior->arm);
204 if (!ior->arm.head || ior->arm.proto != proto) {
205 OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head,
206 ior->arm.proto, proto);
207 return;
208 }
209
210 OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link);
211 ior->arm.outp = outp;
93} 212}
94 213
95void 214void
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 {
18 struct nvkm_conn *conn; 18 struct nvkm_conn *conn;
19 19
20 /* Assembly state. */ 20 /* Assembly state. */
21#define NVKM_OUTP_PRIV 1
22#define NVKM_OUTP_USER 2
23 u8 acquired:2;
21 struct nvkm_ior *ior; 24 struct nvkm_ior *ior;
22}; 25};
23 26
@@ -28,6 +31,9 @@ int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *,
28void nvkm_outp_del(struct nvkm_outp **); 31void nvkm_outp_del(struct nvkm_outp **);
29void nvkm_outp_init(struct nvkm_outp *); 32void nvkm_outp_init(struct nvkm_outp *);
30void nvkm_outp_fini(struct nvkm_outp *); 33void nvkm_outp_fini(struct nvkm_outp *);
34int nvkm_outp_acquire(struct nvkm_outp *, u8 user);
35void nvkm_outp_release(struct nvkm_outp *, u8 user);
36void nvkm_outp_route(struct nvkm_disp *);
31 37
32struct nvkm_outp_func { 38struct nvkm_outp_func {
33 void *(*dtor)(struct nvkm_outp *); 39 void *(*dtor)(struct nvkm_outp *);
@@ -39,8 +45,6 @@ struct nvkm_outp_func {
39#define nvkm_output_func nvkm_outp_func 45#define nvkm_output_func nvkm_outp_func
40#define nvkm_output_new_ nvkm_outp_new_ 46#define nvkm_output_new_ nvkm_outp_new_
41 47
42void gm200_sor_magic(struct nvkm_output *outp);
43
44#define OUTP_MSG(o,l,f,a...) do { \ 48#define OUTP_MSG(o,l,f,a...) do { \
45 struct nvkm_outp *_outp = (o); \ 49 struct nvkm_outp *_outp = (o); \
46 nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ 50 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)
94 } 94 }
95 95
96 switch (mthd * !!outp) { 96 switch (mthd * !!outp) {
97 case NV50_DISP_MTHD_V1_ACQUIRE: {
98 union {
99 struct nv50_disp_acquire_v0 v0;
100 } *args = data;
101 int ret = -ENOSYS;
102 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
103 ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER);
104 if (ret == 0) {
105 args->v0.or = outp->ior->id;
106 args->v0.link = outp->ior->asy.link;
107 }
108 }
109 return ret;
110 }
111 break;
112 case NV50_DISP_MTHD_V1_RELEASE:
113 nvkm_outp_release(outp, NVKM_OUTP_USER);
114 return 0;
97 case NV50_DISP_MTHD_V1_DAC_LOAD: { 115 case NV50_DISP_MTHD_V1_DAC_LOAD: {
98 union { 116 union {
99 struct nv50_disp_dac_load_v0 v0; 117 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)
102 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 120 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
103 if (args->v0.data & 0xfff00000) 121 if (args->v0.data & 0xfff00000)
104 return -EINVAL; 122 return -EINVAL;
123 ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV);
124 if (ret)
125 return ret;
105 ret = outp->ior->func->sense(outp->ior, args->v0.data); 126 ret = outp->ior->func->sense(outp->ior, args->v0.data);
127 nvkm_outp_release(outp, NVKM_OUTP_PRIV);
106 if (ret < 0) 128 if (ret < 0)
107 return ret; 129 return ret;
108 args->v0.load = ret; 130 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)
45 nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); 45 nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift));
46} 46}
47 47
48void 48static void
49gm200_sor_magic(struct nvkm_output *outp) 49gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior)
50{ 50{
51 struct nvkm_device *device = outp->disp->engine.subdev.device; 51 struct nvkm_device *device = outp->disp->engine.subdev.device;
52 const u32 soff = outp->or * 0x100; 52 const u32 moff = __ffs(outp->info.or) * 0x100;
53 const u32 data = outp->or + 1; 53 const u32 sor = ior ? ior->id + 1 : 0;
54 if (outp->info.sorconf.link & 1) 54 u32 link = ior ? (ior->asy.link == 2) : 0;
55 nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data); 55
56 if (outp->info.sorconf.link & 1) {
57 nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor);
58 link++;
59 }
60
56 if (outp->info.sorconf.link & 2) 61 if (outp->info.sorconf.link & 2)
57 nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data); 62 nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor);
63}
64
65static int
66gm200_sor_route_get(struct nvkm_outp *outp, int *link)
67{
68 struct nvkm_device *device = outp->disp->engine.subdev.device;
69 const int sublinks = outp->info.sorconf.link;
70 int lnk[2], sor[2], m, s;
71
72 for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) {
73 if (sublinks & BIT(s)) {
74 u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80));
75 lnk[s] = (data & 0x00000010) >> 4;
76 sor[s] = (data & 0x0000000f);
77 if (!sor[s])
78 return -1;
79 *link |= lnk[s];
80 }
81 }
82
83 if (sublinks == 3) {
84 if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1]))
85 return -1;
86 }
87
88 return ((sublinks & 1) ? sor[0] : sor[1]) - 1;
58} 89}
59 90
60static const struct nvkm_ior_func 91static const struct nvkm_ior_func
61gm200_sor = { 92gm200_sor = {
93 .route = {
94 .get = gm200_sor_route_get,
95 .set = gm200_sor_route_set,
96 },
62 .state = gf119_sor_state, 97 .state = gf119_sor_state,
63 .power = nv50_sor_power, 98 .power = nv50_sor_power,
64 .hdmi = { 99 .hdmi = {