diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-07-01 07:51:49 +0200 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-09-20 08:09:42 +0200 |
commit | 46959b7790e3609e795c3b5e70e58dcd22c9e207 (patch) | |
tree | 0f287b3800a232ce45684a84c2fadee21154a3d4 /drivers/gpu/drm | |
parent | drm/nouveau/dp: rewrite auxch transaction routines (diff) | |
download | linux-46959b7790e3609e795c3b5e70e58dcd22c9e207.tar.xz linux-46959b7790e3609e795c3b5e70e58dcd22c9e207.zip |
drm/nouveau/dp: remove reliance on vbios for native displayport
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 111 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_encoder.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_reg.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 39 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_sor.c | 33 |
6 files changed, 126 insertions, 63 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index f8ebd09ee3a6..ae1b6e00bd96 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -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; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index bc035c4f42a8..ee0f0d129d3e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -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 *); diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index ae69b61d93db..2bb316d2421c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -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; diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index f18cdfc3400f..d9632ae38c6c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -843,7 +843,7 @@ #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) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 8260303c2fca..d23ca00e7d62 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -702,37 +702,6 @@ ack: } 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) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -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)); diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index ffe8b483b7b0..f359f94626c2 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -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; } |