diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-05-15 20:49:28 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-06-11 02:09:13 -0400 |
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) | |||
293 | nvbios_exec(&init); | 293 | nvbios_exec(&init); |
294 | } | 294 | } |
295 | 295 | ||
296 | static const struct dp_rates { | ||
297 | u32 rate; | ||
298 | u8 bw; | ||
299 | u8 nr; | ||
300 | } nouveau_dp_rates[] = { | ||
301 | { 2160000, 0x14, 4 }, | ||
302 | { 1080000, 0x0a, 4 }, | ||
303 | { 1080000, 0x14, 2 }, | ||
304 | { 648000, 0x06, 4 }, | ||
305 | { 540000, 0x0a, 2 }, | ||
306 | { 540000, 0x14, 1 }, | ||
307 | { 324000, 0x06, 2 }, | ||
308 | { 270000, 0x0a, 1 }, | ||
309 | { 162000, 0x06, 1 }, | ||
310 | {} | ||
311 | }; | ||
312 | |||
296 | int | 313 | int |
297 | nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, | 314 | nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, |
298 | struct dcb_output *outp, int head, u32 datarate) | 315 | struct dcb_output *outp, int head, u32 datarate) |
299 | { | 316 | { |
300 | struct nouveau_bios *bios = nouveau_bios(disp); | 317 | struct nouveau_bios *bios = nouveau_bios(disp); |
301 | struct nouveau_i2c *i2c = nouveau_i2c(disp); | 318 | struct nouveau_i2c *i2c = nouveau_i2c(disp); |
319 | const struct dp_rates *cfg = nouveau_dp_rates; | ||
302 | struct dp_state _dp = { | 320 | struct dp_state _dp = { |
303 | .disp = disp, | 321 | .disp = disp, |
304 | .func = func, | 322 | .func = func, |
305 | .outp = outp, | 323 | .outp = outp, |
306 | .head = head, | 324 | .head = head, |
307 | }, *dp = &_dp; | 325 | }, *dp = &_dp; |
308 | const u32 bw_list[] = { 540000, 270000, 162000, 0 }; | ||
309 | const u32 *link_bw = bw_list; | ||
310 | u8 hdr, cnt, len; | 326 | u8 hdr, cnt, len; |
311 | u32 data; | 327 | u32 data; |
312 | int ret; | 328 | int ret; |
@@ -338,8 +354,8 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, | |||
338 | * so results at best in an UPDATE hanging, and at worst | 354 | * so results at best in an UPDATE hanging, and at worst |
339 | * with PDISP running away to join the circus. | 355 | * with PDISP running away to join the circus. |
340 | */ | 356 | */ |
341 | dp->dpcd[1] = link_bw[0] / 27000; | 357 | dp->dpcd[1] = dp->outp->dpconf.link_bw; |
342 | dp->dpcd[2] = 4; | 358 | dp->dpcd[2] = dp->outp->dpconf.link_nr; |
343 | dp->dpcd[3] = 0x00; | 359 | dp->dpcd[3] = 0x00; |
344 | ERR("failed to read DPCD\n"); | 360 | ERR("failed to read DPCD\n"); |
345 | } | 361 | } |
@@ -355,26 +371,24 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, | |||
355 | dp->dpcd[1] = dp->outp->dpconf.link_bw; | 371 | dp->dpcd[1] = dp->outp->dpconf.link_bw; |
356 | dp->pc2 = dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; | 372 | dp->pc2 = dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; |
357 | 373 | ||
358 | /* adjust required bandwidth for 8B/10B coding overhead */ | 374 | /* restrict link config to the lowest required rate, if requested */ |
359 | datarate = (datarate / 8) * 10; | 375 | if (datarate) { |
376 | datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ | ||
377 | while (cfg[1].rate >= datarate) | ||
378 | cfg++; | ||
379 | } | ||
380 | cfg--; | ||
360 | 381 | ||
361 | /* enable down-spreading and execute pre-train script from vbios */ | 382 | /* enable down-spreading and execute pre-train script from vbios */ |
362 | dp_link_train_init(dp, dp->dpcd[3] & 0x01); | 383 | dp_link_train_init(dp, dp->dpcd[3] & 0x01); |
363 | 384 | ||
364 | /* start off at highest link rate supported by encoder and display */ | 385 | while (ret = -EIO, (++cfg)->rate) { |
365 | while (*link_bw > (dp->dpcd[1] * 27000)) | 386 | /* select next configuration supported by encoder and sink */ |
366 | link_bw++; | 387 | while (cfg->nr > (dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || |
367 | 388 | cfg->bw > (dp->dpcd[DPCD_RC01_MAX_LINK_RATE])) | |
368 | while ((ret = -EIO) && link_bw[0]) { | 389 | cfg++; |
369 | /* find minimum required lane count at this link rate */ | 390 | dp->link_bw = cfg->bw * 27000; |
370 | dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; | 391 | dp->link_nr = cfg->nr; |
371 | while ((dp->link_nr >> 1) * link_bw[0] > datarate) | ||
372 | dp->link_nr >>= 1; | ||
373 | |||
374 | /* drop link rate to minimum with this lane count */ | ||
375 | while ((link_bw[1] * dp->link_nr) > datarate) | ||
376 | link_bw++; | ||
377 | dp->link_bw = link_bw[0]; | ||
378 | 392 | ||
379 | /* program selected link configuration */ | 393 | /* program selected link configuration */ |
380 | ret = dp_set_link_config(dp); | 394 | ret = dp_set_link_config(dp); |
@@ -391,9 +405,6 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, | |||
391 | */ | 405 | */ |
392 | break; | 406 | break; |
393 | } | 407 | } |
394 | |||
395 | /* retry at lower rate */ | ||
396 | link_bw++; | ||
397 | } | 408 | } |
398 | 409 | ||
399 | /* finish link training */ | 410 | /* finish link training */ |