drm/nouveau/dp: add support for displayport table 0x30
authorBen Skeggs <bskeggs@redhat.com>
Fri, 5 Aug 2011 04:47:28 +0000 (14:47 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 20 Sep 2011 06:11:21 +0000 (16:11 +1000)
Written from observations of my NVD9's vbios, completely untested due to
my NVD9 lacking actual DisplayPort connectors..

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

index 25ecb77..de5efe7 100644 (file)
@@ -298,6 +298,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
        switch (table[0]) {
        case 0x20:
        case 0x21:
+       case 0x30:
                break;
        default:
                NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]);
@@ -339,6 +340,7 @@ 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;
 
        NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
@@ -360,10 +362,17 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
         */
        entry = ROMPTR(&dev_priv->vbios, dp->entry[10]);
        if (entry) {
-               while (dp->link_bw < (ROM16(entry[0]) * 10))
-                       entry += 4;
+               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]);
+               }
 
-               nouveau_bios_run_init_table(dev, ROM16(entry[2]), dp->dcb, dp->crtc);
+               nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
        }
 
        /* configure lane count on the source */
@@ -414,33 +423,50 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
                shifts = nvaf_lane_map;
 
        for (i = 0; i < dp->link_nr; i++) {
-               u8  lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
                u8 *conf = dp->entry + dp->table[4];
-               u8 *last = conf + (dp->entry[4] * dp->table[5]);
-
-               while (conf < last) {
-                       if ((lane  & 3) == conf[0] &&
-                           (lane >> 2) == conf[1])
-                               break;
-                       conf += 5;
-               }
+               u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+               u8 lpre = (lane & 0x0c) >> 2;
+               u8 lvsw = (lane & 0x03) >> 0;
 
-               if (conf == last)
-                       return -EINVAL;
+               mask |= 0xff << shifts[i];
+               unk |= 1 << (shifts[i] >> 3);
 
-               dp->conf[i] = (conf[1] << 3) | conf[0];
-               if (conf[0] == DP_TRAIN_VOLTAGE_SWING_1200)
+               dp->conf[i] = (lpre << 3) | lvsw;
+               if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200)
                        dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
-               if (conf[1] == DP_TRAIN_PRE_EMPHASIS_9_5)
+               if (lpre == DP_TRAIN_PRE_EMPHASIS_9_5)
                        dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
                NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]);
 
-               mask |= 0xff << shifts[i];
-               drv  |= conf[2] << shifts[i];
-               pre  |= conf[3] << shifts[i];
-               unk   = (unk & ~0x0000ff00) | (conf[4] << 8);
-               unk  |= 1 << (shifts[i] >> 3);
+               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);
        }
 
        nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv);