drm/nouveau/disp: semi-complete link training sequence even if display disappears
authorBen Skeggs <bskeggs@redhat.com>
Mon, 4 Nov 2013 03:40:36 +0000 (13:40 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 8 Nov 2013 05:39:57 +0000 (15:39 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/engine/disp/dport.c

index 15448b9abac89f49bc5814d74228f9eda95df900..1bd4c63369c16f96fd30762c1faaddb60acdf337 100644 (file)
@@ -70,17 +70,10 @@ dp_set_link_config(struct dp_state *dp)
        };
        u32 lnkcmp;
        u8 sink[2];
+       int ret;
 
        DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
-       /* set desired link configuration on the sink */
-       sink[0] = dp->link_bw / 27000;
-       sink[1] = dp->link_nr;
-       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
-               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
-
-       nv_wraux(dp->aux, DPCD_LC00, sink, 2);
-
        /* set desired link configuration on the source */
        if ((lnkcmp = dp->info.lnkcmp)) {
                if (dp->version < 0x30) {
@@ -96,10 +89,22 @@ dp_set_link_config(struct dp_state *dp)
                nvbios_exec(&init);
        }
 
-       return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
-                                dp->link_nr, dp->link_bw / 27000,
-                                dp->dpcd[DPCD_RC02] &
-                                         DPCD_RC02_ENHANCED_FRAME_CAP);
+       ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
+                               dp->link_nr, dp->link_bw / 27000,
+                               dp->dpcd[DPCD_RC02] &
+                                        DPCD_RC02_ENHANCED_FRAME_CAP);
+       if (ret) {
+               ERR("lnk_ctl failed with %d\n", ret);
+               return ret;
+       }
+
+       /* set desired link configuration on the sink */
+       sink[0] = dp->link_bw / 27000;
+       sink[1] = dp->link_nr;
+       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+       return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
 }
 
 static void
@@ -294,8 +299,17 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
 
        ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
        if (ret) {
+               /* it's possible the display has been unplugged before we
+                * get here.  we still need to execute the full set of
+                * vbios scripts, and program the OR at a high enough
+                * frequency to satisfy the target mode.  failure to do
+                * so results at best in an UPDATE hanging, and at worst
+                * with PDISP running away to join the circus.
+                */
+               dp->dpcd[1] = link_bw[0] / 27000;
+               dp->dpcd[2] = 4;
+               dp->dpcd[3] = 0x00;
                ERR("failed to read DPCD\n");
-               return ret;
        }
 
        /* adjust required bandwidth for 8B/10B coding overhead */
@@ -328,8 +342,10 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
                            !dp_link_train_eq(dp))
                                break;
                } else
-               if (ret >= 1) {
-                       /* dp_set_link_config() handled training */
+               if (ret) {
+                       /* dp_set_link_config() handled training, or
+                        * we failed to communicate with the sink.
+                        */
                        break;
                }
 
@@ -344,5 +360,5 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
 
        /* execute post-train script from vbios */
        dp_link_train_fini(dp);
-       return true;
+       return (ret < 0) ? false : true;
 }