drm/nouveau/dp: move all nv50/sor-specific code out of nouveau_dp.c
authorBen Skeggs <bskeggs@redhat.com>
Fri, 9 Mar 2012 06:22:56 +0000 (16:22 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 13 Mar 2012 07:15:03 +0000 (17:15 +1000)
Off-chip encoders (which we don't support yet anyway), and newer chipsets
(such as NVD9...), will need their own code for this.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_sor.c

index 1a613ab..f38f561 100644 (file)
@@ -161,116 +161,6 @@ out:
        return ret;
 }
 
-static u32
-dp_link_bw_get(struct drm_device *dev, int or, int link)
-{
-       u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800));
-       if (!(ctrl & 0x000c0000))
-               return 162000;
-       return 270000;
-}
-
-static int
-dp_lane_count_get(struct drm_device *dev, int or, int link)
-{
-       u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
-       switch (ctrl & 0x000f0000) {
-       case 0x00010000: return 1;
-       case 0x00030000: return 2;
-       default:
-               return 4;
-       }
-}
-
-void
-nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
-{
-       const u32 symbol = 100000;
-       int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
-       int TU, VTUi, VTUf, VTUa;
-       u64 link_data_rate, link_ratio, unk;
-       u32 best_diff = 64 * symbol;
-       u32 link_nr, link_bw, r;
-
-       /* calculate packed data rate for each lane */
-       link_nr = dp_lane_count_get(dev, or, link);
-       link_data_rate = (clk * bpp / 8) / link_nr;
-
-       /* calculate ratio of packed data rate to link symbol rate */
-       link_bw = dp_link_bw_get(dev, or, link);
-       link_ratio = link_data_rate * symbol;
-       r = do_div(link_ratio, link_bw);
-
-       for (TU = 64; TU >= 32; TU--) {
-               /* calculate average number of valid symbols in each TU */
-               u32 tu_valid = link_ratio * TU;
-               u32 calc, diff;
-
-               /* find a hw representation for the fraction.. */
-               VTUi = tu_valid / symbol;
-               calc = VTUi * symbol;
-               diff = tu_valid - calc;
-               if (diff) {
-                       if (diff >= (symbol / 2)) {
-                               VTUf = symbol / (symbol - diff);
-                               if (symbol - (VTUf * diff))
-                                       VTUf++;
-
-                               if (VTUf <= 15) {
-                                       VTUa  = 1;
-                                       calc += symbol - (symbol / VTUf);
-                               } else {
-                                       VTUa  = 0;
-                                       VTUf  = 1;
-                                       calc += symbol;
-                               }
-                       } else {
-                               VTUa  = 0;
-                               VTUf  = min((int)(symbol / diff), 15);
-                               calc += symbol / VTUf;
-                       }
-
-                       diff = calc - tu_valid;
-               } else {
-                       /* no remainder, but the hw doesn't like the fractional
-                        * part to be zero.  decrement the integer part and
-                        * have the fraction add a whole symbol back
-                        */
-                       VTUa = 0;
-                       VTUf = 1;
-                       VTUi--;
-               }
-
-               if (diff < best_diff) {
-                       best_diff = diff;
-                       bestTU = TU;
-                       bestVTUa = VTUa;
-                       bestVTUf = VTUf;
-                       bestVTUi = VTUi;
-                       if (diff == 0)
-                               break;
-               }
-       }
-
-       if (!bestTU) {
-               NV_ERROR(dev, "DP: unable to find suitable config\n");
-               return;
-       }
-
-       /* XXX close to vbios numbers, but not right */
-       unk  = (symbol - link_ratio) * bestTU;
-       unk *= link_ratio;
-       r = do_div(unk, symbol);
-       r = do_div(unk, symbol);
-       unk += 6;
-
-       nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
-       nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
-                                                            bestVTUf << 16 |
-                                                            bestVTUi << 8 |
-                                                            unk);
-}
-
 u8 *
 nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
 {
@@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
  * link training
  *****************************************************************************/
 struct dp_state {
+       struct dp_train_func *func;
        struct dcb_entry *dcb;
-       u8 *table;
-       u8 *entry;
        int auxch;
        int crtc;
-       int or;
-       int link;
        u8 *dpcd;
        int link_nr;
        u32 link_bw;
@@ -335,100 +222,48 @@ struct dp_state {
 static void
 dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
 {
-       int or = dp->or, link = dp->link;
-       u8 *entry, sink[2];
-       u32 dp_ctrl;
-       u16 script;
+       u8 sink[2];
 
        NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
-       /* set selected link rate on source */
-       switch (dp->link_bw) {
-       case 270000:
-               nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000);
-               sink[0] = DP_LINK_BW_2_7;
-               break;
-       default:
-               nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000);
-               sink[0] = DP_LINK_BW_1_62;
-               break;
-       }
-
-       /* offset +0x0a of each dp encoder table entry is a pointer to another
-        * table, that has (among other things) pointers to more scripts that
-        * need to be executed, this time depending on link speed.
-        */
-       entry = ROMPTR(dev, dp->entry[10]);
-       if (entry) {
-               if (dp->table[0] < 0x30) {
-                       while (dp->link_bw < (ROM16(entry[0]) * 10))
-                               entry += 4;
-                       script = ROM16(entry[2]);
-               } else {
-                       while (dp->link_bw < (entry[0] * 27000))
-                               entry += 3;
-                       script = ROM16(entry[1]);
-               }
+       /* set desired link configuration on the source */
+       dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw,
+                          dp->dpcd[2] & DP_ENHANCED_FRAME_CAP);
 
-               nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
-       }
-
-       /* configure lane count on the source */
-       dp_ctrl = ((1 << dp->link_nr) - 1) << 16;
+       /* inform the sink of the new configuration */
+       sink[0] = dp->link_bw / 27000;
        sink[1] = dp->link_nr;
-       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) {
-               dp_ctrl |= 0x00004000;
+       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
                sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-       }
-
-       nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl);
 
-       /* inform the sink of the new configuration */
        auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2);
 }
 
 static void
-dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp)
+dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern)
 {
        u8 sink_tp;
 
-       NV_DEBUG_KMS(dev, "training pattern %d\n", tp);
+       NV_DEBUG_KMS(dev, "training pattern %d\n", pattern);
 
-       nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24);
+       dp->func->train_set(dev, dp->dcb, pattern);
 
        auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
        sink_tp &= ~DP_TRAINING_PATTERN_MASK;
-       sink_tp |= tp;
+       sink_tp |= pattern;
        auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
 }
 
-static const u8 nv50_lane_map[] = { 16, 8, 0, 24 };
-static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 };
-
 static int
 dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       u32 mask = 0, drv = 0, pre = 0, unk = 0;
-       const u8 *shifts;
-       int link = dp->link;
-       int or = dp->or;
        int i;
 
-       if (dev_priv->chipset != 0xaf)
-               shifts = nv50_lane_map;
-       else
-               shifts = nvaf_lane_map;
-
        for (i = 0; i < dp->link_nr; i++) {
-               u8 *conf = dp->entry + dp->table[4];
                u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
                u8 lpre = (lane & 0x0c) >> 2;
                u8 lvsw = (lane & 0x03) >> 0;
 
-               mask |= 0xff << shifts[i];
-               unk |= 1 << (shifts[i] >> 3);
-
                dp->conf[i] = (lpre << 3) | lvsw;
                if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200)
                        dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
@@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
                        dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
                NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]);
-
-               if (dp->table[0] < 0x30) {
-                       u8 *last = conf + (dp->entry[4] * dp->table[5]);
-                       while (lvsw != conf[0] || lpre != conf[1]) {
-                               conf += dp->table[5];
-                               if (conf >= last)
-                                       return -EINVAL;
-                       }
-
-                       conf += 2;
-               } else {
-                       /* no lookup table anymore, set entries for each
-                        * combination of voltage swing and pre-emphasis
-                        * level allowed by the DP spec.
-                        */
-                       switch (lvsw) {
-                       case 0: lpre += 0; break;
-                       case 1: lpre += 4; break;
-                       case 2: lpre += 7; break;
-                       case 3: lpre += 9; break;
-                       }
-
-                       conf = conf + (lpre * dp->table[5]);
-                       conf++;
-               }
-
-               drv |= conf[0] << shifts[i];
-               pre |= conf[1] << shifts[i];
-               unk  = (unk & ~0x0000ff00) | (conf[2] << 8);
+               dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre);
        }
 
-       nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv);
-       nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre);
-       nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk);
-
        return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4);
 }
 
@@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp)
 }
 
 bool
-nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
+nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate,
+                     struct dp_train_func *func)
 {
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
@@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
        if (!auxch)
                return false;
 
-       dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry);
-       if (!dp.table)
-               return -EINVAL;
-
+       dp.func = func;
        dp.dcb = nv_encoder->dcb;
        dp.crtc = nv_crtc->index;
        dp.auxch = auxch->drive;
-       dp.or = nv_encoder->or;
-       dp.link = !(nv_encoder->dcb->sorconf.link & 1);
        dp.dpcd = nv_encoder->dp.dpcd;
 
        /* some sinks toggle hotplug in response to some of the actions
index 4bb6aee..cf0e74a 100644 (file)
@@ -1162,14 +1162,6 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
 /* nouveau_hdmi.c */
 void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
 
-/* nouveau_dp.c */
-int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
-                    uint8_t *data, int data_nr);
-bool nouveau_dp_detect(struct drm_encoder *);
-bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
-void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
-u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
-
 /* nv04_fb.c */
 extern int  nv04_fb_vram_init(struct drm_device *);
 extern int  nv04_fb_init(struct drm_device *);
index e5d6e3f..fa43174 100644 (file)
 
 #define NV_DPMS_CLEARED 0x80
 
+struct dp_train_func {
+       void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc,
+                        int nr, u32 bw, bool enhframe);
+       void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern);
+       void (*train_adj)(struct drm_device *, struct dcb_entry *,
+                         u8 lane, u8 swing, u8 preem);
+};
+
 struct nouveau_encoder {
        struct drm_encoder_slave base;
 
@@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc)
        return to_encoder_slave(enc)->slave_funcs;
 }
 
+/* nouveau_dp.c */
+int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+                    uint8_t *data, int data_nr);
+bool nouveau_dp_detect(struct drm_encoder *);
+bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate,
+                          struct dp_train_func *);
+u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
+
 struct nouveau_connector *
 nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
 int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
+void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32);
 int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
 
+
 #endif /* __NOUVEAU_ENCODER_H__ */
index ce440e2..0e47a89 100644 (file)
@@ -863,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev)
        if (type == OUTPUT_DP) {
                int link = !(dcb->dpconf.sor.link & 1);
                if ((mc & 0x000f0000) == 0x00020000)
-                       nouveau_dp_tu_update(dev, or, link, pclk, 18);
+                       nv50_sor_dp_calc_tu(dev, or, link, pclk, 18);
                else
-                       nouveau_dp_tu_update(dev, or, link, pclk, 24);
+                       nv50_sor_dp_calc_tu(dev, or, link, pclk, 24);
        }
 
        if (dcb->type != OUTPUT_ANALOG) {
index c4423ba..ba1b8cc 100644 (file)
 #include "nouveau_crtc.h"
 #include "nv50_display.h"
 
+static u32
+nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
+       static const u8 nv50[] = { 16, 8, 0, 24 };
+       if (dev_priv->card_type == 0xaf)
+               return nvaf[lane];
+       return nv50[lane];
+}
+
+static void
+nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern)
+{
+       u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+       nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24);
+}
+
+static void
+nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb,
+                     u8 lane, u8 swing, u8 preem)
+{
+       u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+       u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane);
+       u32 mask = 0x000000ff << shift;
+       u8 *table, *entry, *config;
+
+       table = nouveau_dp_bios_data(dev, dcb, &entry);
+       if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
+               NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
+               return;
+       }
+
+       config = entry + table[4];
+       while (config[0] != swing || config[1] != preem) {
+               config += table[5];
+               if (config >= entry + table[4] + entry[4] * table[5])
+                       return;
+       }
+
+       nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift);
+       nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift);
+       nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8);
+}
+
+static void
+nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc,
+                    int link_nr, u32 link_bw, bool enhframe)
+{
+       u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+       u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000;
+       u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000;
+       u8 *table, *entry, mask;
+       int i;
+
+       table = nouveau_dp_bios_data(dev, dcb, &entry);
+       if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
+               NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
+               return;
+       }
+
+       entry = ROMPTR(dev, entry[10]);
+       if (entry) {
+               while (link_bw < ROM16(entry[0]) * 10)
+                       entry += 4;
+
+               nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc);
+       }
+
+       dpctrl |= ((1 << link_nr) - 1) << 16;
+       if (enhframe)
+               dpctrl |= 0x00004000;
+
+       if (link_bw > 162000)
+               clksor |= 0x00040000;
+
+       nv_wr32(dev, 0x614300 + (or * 0x800), clksor);
+       nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl);
+
+       mask = 0;
+       for (i = 0; i < link_nr; i++)
+               mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3);
+       nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask);
+}
+
+static void
+nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw)
+{
+       u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000;
+       u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800));
+       if (clksor & 0x000c0000)
+               *bw = 270000;
+       else
+               *bw = 162000;
+
+       if      (dpctrl > 0x00030000) *nr = 4;
+       else if (dpctrl > 0x00010000) *nr = 2;
+       else                          *nr = 1;
+}
+
+void
+nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
+{
+       const u32 symbol = 100000;
+       int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
+       int TU, VTUi, VTUf, VTUa;
+       u64 link_data_rate, link_ratio, unk;
+       u32 best_diff = 64 * symbol;
+       u32 link_nr, link_bw, r;
+
+       /* calculate packed data rate for each lane */
+       nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw);
+       link_data_rate = (clk * bpp / 8) / link_nr;
+
+       /* calculate ratio of packed data rate to link symbol rate */
+       link_ratio = link_data_rate * symbol;
+       r = do_div(link_ratio, link_bw);
+
+       for (TU = 64; TU >= 32; TU--) {
+               /* calculate average number of valid symbols in each TU */
+               u32 tu_valid = link_ratio * TU;
+               u32 calc, diff;
+
+               /* find a hw representation for the fraction.. */
+               VTUi = tu_valid / symbol;
+               calc = VTUi * symbol;
+               diff = tu_valid - calc;
+               if (diff) {
+                       if (diff >= (symbol / 2)) {
+                               VTUf = symbol / (symbol - diff);
+                               if (symbol - (VTUf * diff))
+                                       VTUf++;
+
+                               if (VTUf <= 15) {
+                                       VTUa  = 1;
+                                       calc += symbol - (symbol / VTUf);
+                               } else {
+                                       VTUa  = 0;
+                                       VTUf  = 1;
+                                       calc += symbol;
+                               }
+                       } else {
+                               VTUa  = 0;
+                               VTUf  = min((int)(symbol / diff), 15);
+                               calc += symbol / VTUf;
+                       }
+
+                       diff = calc - tu_valid;
+               } else {
+                       /* no remainder, but the hw doesn't like the fractional
+                        * part to be zero.  decrement the integer part and
+                        * have the fraction add a whole symbol back
+                        */
+                       VTUa = 0;
+                       VTUf = 1;
+                       VTUi--;
+               }
+
+               if (diff < best_diff) {
+                       best_diff = diff;
+                       bestTU = TU;
+                       bestVTUa = VTUa;
+                       bestVTUf = VTUf;
+                       bestVTUi = VTUi;
+                       if (diff == 0)
+                               break;
+               }
+       }
+
+       if (!bestTU) {
+               NV_ERROR(dev, "DP: unable to find suitable config\n");
+               return;
+       }
+
+       /* XXX close to vbios numbers, but not right */
+       unk  = (symbol - link_ratio) * bestTU;
+       unk *= link_ratio;
+       r = do_div(unk, symbol);
+       r = do_div(unk, symbol);
+       unk += 6;
+
+       nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
+       nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
+                                                            bestVTUf << 16 |
+                                                            bestVTUi << 8 |
+                                                            unk);
+}
 static void
 nv50_sor_disconnect(struct drm_encoder *encoder)
 {
@@ -124,9 +311,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
                        return;
 
                if (mode == DRM_MODE_DPMS_ON) {
+                       struct dp_train_func func = {
+                               .link_set = nv50_sor_dp_link_set,
+                               .train_set = nv50_sor_dp_train_set,
+                               .train_adj = nv50_sor_dp_train_adj
+                       };
+                       u32 rate = nv_encoder->dp.datarate;
                        u8 status = DP_SET_POWER_D0;
+
                        nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
-                       nouveau_dp_link_train(encoder, nv_encoder->dp.datarate);
+                       nouveau_dp_link_train(encoder, rate, &func);
                } else {
                        u8 status = DP_SET_POWER_D3;
                        nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);