drm/nouveau/dp: remove reliance on vbios for native displayport
authorBen Skeggs <bskeggs@redhat.com>
Fri, 1 Jul 2011 05:51:49 +0000 (15:51 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 20 Sep 2011 06:09:42 +0000 (16:09 +1000)
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/nouveau_reg.h
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_sor.c

index f8ebd09ee3a6a56b27ce432817cd224e21d2f60e..ae1b6e00bd96ca93c6633fcbd6dc9bf573272cb8 100644 (file)
@@ -194,6 +194,116 @@ auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
        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);
+}
+
 static int
 nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
 {
@@ -617,7 +727,6 @@ static int
 nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
        struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
-       struct drm_device *dev = auxch->dev;
        struct i2c_msg *msg = msgs;
        int ret, mcnt = num;
 
index bc035c4f42a8337c48282d41f04e36bb56bce49b..ee0f0d129d3e5aa29802f4cd2c64f1a086a3333b 100644 (file)
@@ -1101,6 +1101,7 @@ 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 *);
+void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
 
 /* nv04_fb.c */
 extern int  nv04_fb_init(struct drm_device *);
index ae69b61d93dbbd2b3e0caf052b4faae4070c5505..2bb316d2421cbbec319afe49550f16bc00d9d0d1 100644 (file)
@@ -49,9 +49,6 @@ struct nouveau_encoder {
 
        union {
                struct {
-                       int mc_unknown;
-                       uint32_t unk0;
-                       uint32_t unk1;
                        int dpcd_version;
                        int link_nr;
                        int link_bw;
index f18cdfc3400fc00894dd8f40c3e3239114e2a2e5..d9632ae38c6cbb9d74f573b2ea6b50daec36ed2c 100644 (file)
 #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2                          0x02000000
 #define NV50_SOR_DP_UNK118(i, l)         (0x0061c118 + (i) * 0x800 + (l) * 0x80)
 #define NV50_SOR_DP_UNK120(i, l)         (0x0061c120 + (i) * 0x800 + (l) * 0x80)
-#define NV50_SOR_DP_UNK128(i, l)         (0x0061c128 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_SCFG(i, l)           (0x0061c128 + (i) * 0x800 + (l) * 0x80)
 #define NV50_SOR_DP_UNK130(i, l)         (0x0061c130 + (i) * 0x800 + (l) * 0x80)
 
 #define NV50_PDISPLAY_USER(i)                        ((i) * 0x1000 + 0x00640000)
index 8260303c2fcabe5178d2574ad390468b32326f4a..d23ca00e7d627c65e3814891c0a8aa0136f58395 100644 (file)
@@ -701,37 +701,6 @@ ack:
        nv_wr32(dev, 0x610030, 0x80000000);
 }
 
-static void
-nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
-{
-       int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
-       struct drm_encoder *encoder;
-       uint32_t tmp, unk0 = 0, unk1 = 0;
-
-       if (dcb->type != OUTPUT_DP)
-               return;
-
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
-               if (nv_encoder->dcb == dcb) {
-                       unk0 = nv_encoder->dp.unk0;
-                       unk1 = nv_encoder->dp.unk1;
-                       break;
-               }
-       }
-
-       if (unk0 || unk1) {
-               tmp  = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
-               tmp &= 0xfffffe03;
-               nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0);
-
-               tmp  = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
-               tmp &= 0xfef080c0;
-               nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1);
-       }
-}
-
 static void
 nv50_display_unk20_handler(struct drm_device *dev)
 {
@@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev)
        script = nv50_display_script_select(dev, dcb, mc, pclk);
        nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
 
-       nv50_display_unk20_dp_hack(dev, dcb);
+       if (type == OUTPUT_DP) {
+               int link = !(dcb->dpconf.sor.link & 1);
+               if ((mc & 0x000f0000) == 0x00020000)
+                       nouveau_dp_tu_update(dev, or, link, pclk, 18);
+               else
+                       nouveau_dp_tu_update(dev, or, link, pclk, 24);
+       }
 
        if (dcb->type != OUTPUT_ANALOG) {
                tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
index ffe8b483b7b0735a175d49ef0601d313c247f944..f359f94626c2ec97d59878cec02b3720a43388fa 100644 (file)
@@ -187,6 +187,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
+       struct nouveau_connector *nv_connector;
        uint32_t mode_ctl = 0;
        int ret;
 
@@ -206,7 +207,12 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                        mode_ctl = 0x0200;
                break;
        case OUTPUT_DP:
-               mode_ctl |= (nv_encoder->dp.mc_unknown << 16);
+               nv_connector = nouveau_encoder_connector_get(nv_encoder);
+               if (nv_connector && nv_connector->base.display_info.bpc == 6)
+                       mode_ctl |= 0x00020000;
+               else
+                       mode_ctl |= 0x00050000;
+
                if (nv_encoder->dcb->sorconf.link & 1)
                        mode_ctl |= 0x00000800;
                else
@@ -313,31 +319,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
        encoder->possible_crtcs = entry->heads;
        encoder->possible_clones = 0;
 
-       if (nv_encoder->dcb->type == OUTPUT_DP) {
-               int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1);
-               uint32_t tmp;
-
-               tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
-               if (!tmp)
-                       tmp = nv_rd32(dev, 0x610798 + (or * 8));
-
-               switch ((tmp & 0x00000f00) >> 8) {
-               case 8:
-               case 9:
-                       nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16;
-                       tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
-                       nv_encoder->dp.unk0 = tmp & 0x000001fc;
-                       tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
-                       nv_encoder->dp.unk1 = tmp & 0x010f7f3f;
-                       break;
-               default:
-                       break;
-               }
-
-               if (!nv_encoder->dp.mc_unknown)
-                       nv_encoder->dp.mc_unknown = 5;
-       }
-
        drm_mode_connector_attach_encoder(connector, encoder);
        return 0;
 }