diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-05-16 10:49:28 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-06-11 16:09:13 +1000 |
commit | 1f86ca1a2e555abb11681f31c17d2fd1b11ac6cf (patch) | |
tree | 48b1e0420c34ebe47ecdca860d3ba05ac7900aae | |
parent | 04e7e92d53d83e3595373311ca21aad6a33ba2f6 (diff) |
drm/nouveau/disp/dp: support training to highest rate, rather than a target
We really want this for, at least, MST devices.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/disp/dport.c | 57 |
1 files changed, 34 insertions, 23 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c index 758d2eace928..3814c3d33812 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c @@ -293,20 +293,36 @@ dp_link_train_fini(struct dp_state *dp) nvbios_exec(&init); } +static const struct dp_rates { + u32 rate; + u8 bw; + u8 nr; +} nouveau_dp_rates[] = { + { 2160000, 0x14, 4 }, + { 1080000, 0x0a, 4 }, + { 1080000, 0x14, 2 }, + { 648000, 0x06, 4 }, + { 540000, 0x0a, 2 }, + { 540000, 0x14, 1 }, + { 324000, 0x06, 2 }, + { 270000, 0x0a, 1 }, + { 162000, 0x06, 1 }, + {} +}; + int nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, struct dcb_output *outp, int head, u32 datarate) { struct nouveau_bios *bios = nouveau_bios(disp); struct nouveau_i2c *i2c = nouveau_i2c(disp); + const struct dp_rates *cfg = nouveau_dp_rates; struct dp_state _dp = { .disp = disp, .func = func, .outp = outp, .head = head, }, *dp = &_dp; - const u32 bw_list[] = { 540000, 270000, 162000, 0 }; - const u32 *link_bw = bw_list; u8 hdr, cnt, len; u32 data; int ret; @@ -338,8 +354,8 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, * 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[1] = dp->outp->dpconf.link_bw; + dp->dpcd[2] = dp->outp->dpconf.link_nr; dp->dpcd[3] = 0x00; ERR("failed to read DPCD\n"); } @@ -355,26 +371,24 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, dp->dpcd[1] = dp->outp->dpconf.link_bw; dp->pc2 = dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; - /* adjust required bandwidth for 8B/10B coding overhead */ - datarate = (datarate / 8) * 10; + /* restrict link config to the lowest required rate, if requested */ + if (datarate) { + datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ + while (cfg[1].rate >= datarate) + cfg++; + } + cfg--; /* enable down-spreading and execute pre-train script from vbios */ dp_link_train_init(dp, dp->dpcd[3] & 0x01); - /* start off at highest link rate supported by encoder and display */ - while (*link_bw > (dp->dpcd[1] * 27000)) - link_bw++; - - while ((ret = -EIO) && link_bw[0]) { - /* find minimum required lane count at this link rate */ - dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; - while ((dp->link_nr >> 1) * link_bw[0] > datarate) - dp->link_nr >>= 1; - - /* drop link rate to minimum with this lane count */ - while ((link_bw[1] * dp->link_nr) > datarate) - link_bw++; - dp->link_bw = link_bw[0]; + while (ret = -EIO, (++cfg)->rate) { + /* select next configuration supported by encoder and sink */ + while (cfg->nr > (dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || + cfg->bw > (dp->dpcd[DPCD_RC01_MAX_LINK_RATE])) + cfg++; + dp->link_bw = cfg->bw * 27000; + dp->link_nr = cfg->nr; /* program selected link configuration */ ret = dp_set_link_config(dp); @@ -391,9 +405,6 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, */ break; } - - /* retry at lower rate */ - link_bw++; } /* finish link training */ |