drm/nv50: prevent accidently turning off encoders we're actually using
authorBen Skeggs <bskeggs@redhat.com>
Sun, 17 Jan 2010 22:52:35 +0000 (08:52 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Sun, 17 Jan 2010 23:56:27 +0000 (09:56 +1000)
On most cards the DisplayPort connector is created with 2 encoders sharing
a single SOR (for native DP, and for DVI-over-DP).  The previous logic
for turning off unused encoders didn't take into account that we could
have multiple drm_encoders on a single hw encoder and ended up turning off
encoders that were actually being used still.

This patch fixes that issue.  We probably want to look at something a bit
better later on, and only expose one drm_encoder per hw encoder block.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nv50_crtc.c

index 118d328..40b7360 100644 (file)
@@ -432,6 +432,7 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
        struct drm_device *dev = crtc->dev;
        struct drm_encoder *encoder;
+       uint32_t dac = 0, sor = 0;
 
        NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
 
@@ -439,9 +440,28 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
 
-               if (drm_helper_encoder_in_use(encoder))
+               if (!drm_helper_encoder_in_use(encoder))
                        continue;
 
+               if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
+                   nv_encoder->dcb->type == OUTPUT_TV)
+                       dac |= (1 << nv_encoder->or);
+               else
+                       sor |= (1 << nv_encoder->or);
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+               if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
+                   nv_encoder->dcb->type == OUTPUT_TV) {
+                       if (dac & (1 << nv_encoder->or))
+                               continue;
+               } else {
+                       if (sor & (1 << nv_encoder->or))
+                               continue;
+               }
+
                nv_encoder->disconnect(nv_encoder);
        }