diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-03-09 01:22:56 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-03-13 03:15:03 -0400 |
commit | 8663bc7cde00c8e832d985354f8a6d57a52f7d92 (patch) | |
tree | e11120d8757c5c80b346ab819a6cfced10c69d43 /drivers/gpu/drm/nouveau/nouveau_dp.c | |
parent | 8c1dcb6573ae71ffae392edf5f458543d310607e (diff) |
drm/nouveau/dp: move all nv50/sor-specific code out of nouveau_dp.c
Off-chip encoders (which we don't support yet anyway), and newer chipsets
(such as NVD9...), will need their own code for this.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_dp.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 233 |
1 files changed, 16 insertions, 217 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 1a613ab4e08f..f38f561a0058 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
@@ -161,116 +161,6 @@ out: | |||
161 | return ret; | 161 | return ret; |
162 | } | 162 | } |
163 | 163 | ||
164 | static u32 | ||
165 | dp_link_bw_get(struct drm_device *dev, int or, int link) | ||
166 | { | ||
167 | u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); | ||
168 | if (!(ctrl & 0x000c0000)) | ||
169 | return 162000; | ||
170 | return 270000; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | dp_lane_count_get(struct drm_device *dev, int or, int link) | ||
175 | { | ||
176 | u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
177 | switch (ctrl & 0x000f0000) { | ||
178 | case 0x00010000: return 1; | ||
179 | case 0x00030000: return 2; | ||
180 | default: | ||
181 | return 4; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | void | ||
186 | nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | ||
187 | { | ||
188 | const u32 symbol = 100000; | ||
189 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | ||
190 | int TU, VTUi, VTUf, VTUa; | ||
191 | u64 link_data_rate, link_ratio, unk; | ||
192 | u32 best_diff = 64 * symbol; | ||
193 | u32 link_nr, link_bw, r; | ||
194 | |||
195 | /* calculate packed data rate for each lane */ | ||
196 | link_nr = dp_lane_count_get(dev, or, link); | ||
197 | link_data_rate = (clk * bpp / 8) / link_nr; | ||
198 | |||
199 | /* calculate ratio of packed data rate to link symbol rate */ | ||
200 | link_bw = dp_link_bw_get(dev, or, link); | ||
201 | link_ratio = link_data_rate * symbol; | ||
202 | r = do_div(link_ratio, link_bw); | ||
203 | |||
204 | for (TU = 64; TU >= 32; TU--) { | ||
205 | /* calculate average number of valid symbols in each TU */ | ||
206 | u32 tu_valid = link_ratio * TU; | ||
207 | u32 calc, diff; | ||
208 | |||
209 | /* find a hw representation for the fraction.. */ | ||
210 | VTUi = tu_valid / symbol; | ||
211 | calc = VTUi * symbol; | ||
212 | diff = tu_valid - calc; | ||
213 | if (diff) { | ||
214 | if (diff >= (symbol / 2)) { | ||
215 | VTUf = symbol / (symbol - diff); | ||
216 | if (symbol - (VTUf * diff)) | ||
217 | VTUf++; | ||
218 | |||
219 | if (VTUf <= 15) { | ||
220 | VTUa = 1; | ||
221 | calc += symbol - (symbol / VTUf); | ||
222 | } else { | ||
223 | VTUa = 0; | ||
224 | VTUf = 1; | ||
225 | calc += symbol; | ||
226 | } | ||
227 | } else { | ||
228 | VTUa = 0; | ||
229 | VTUf = min((int)(symbol / diff), 15); | ||
230 | calc += symbol / VTUf; | ||
231 | } | ||
232 | |||
233 | diff = calc - tu_valid; | ||
234 | } else { | ||
235 | /* no remainder, but the hw doesn't like the fractional | ||
236 | * part to be zero. decrement the integer part and | ||
237 | * have the fraction add a whole symbol back | ||
238 | */ | ||
239 | VTUa = 0; | ||
240 | VTUf = 1; | ||
241 | VTUi--; | ||
242 | } | ||
243 | |||
244 | if (diff < best_diff) { | ||
245 | best_diff = diff; | ||
246 | bestTU = TU; | ||
247 | bestVTUa = VTUa; | ||
248 | bestVTUf = VTUf; | ||
249 | bestVTUi = VTUi; | ||
250 | if (diff == 0) | ||
251 | break; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | if (!bestTU) { | ||
256 | NV_ERROR(dev, "DP: unable to find suitable config\n"); | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | /* XXX close to vbios numbers, but not right */ | ||
261 | unk = (symbol - link_ratio) * bestTU; | ||
262 | unk *= link_ratio; | ||
263 | r = do_div(unk, symbol); | ||
264 | r = do_div(unk, symbol); | ||
265 | unk += 6; | ||
266 | |||
267 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); | ||
268 | nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | | ||
269 | bestVTUf << 16 | | ||
270 | bestVTUi << 8 | | ||
271 | unk); | ||
272 | } | ||
273 | |||
274 | u8 * | 164 | u8 * |
275 | nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) | 165 | nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) |
276 | { | 166 | { |
@@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) | |||
318 | * link training | 208 | * link training |
319 | *****************************************************************************/ | 209 | *****************************************************************************/ |
320 | struct dp_state { | 210 | struct dp_state { |
211 | struct dp_train_func *func; | ||
321 | struct dcb_entry *dcb; | 212 | struct dcb_entry *dcb; |
322 | u8 *table; | ||
323 | u8 *entry; | ||
324 | int auxch; | 213 | int auxch; |
325 | int crtc; | 214 | int crtc; |
326 | int or; | ||
327 | int link; | ||
328 | u8 *dpcd; | 215 | u8 *dpcd; |
329 | int link_nr; | 216 | int link_nr; |
330 | u32 link_bw; | 217 | u32 link_bw; |
@@ -335,100 +222,48 @@ struct dp_state { | |||
335 | static void | 222 | static void |
336 | dp_set_link_config(struct drm_device *dev, struct dp_state *dp) | 223 | dp_set_link_config(struct drm_device *dev, struct dp_state *dp) |
337 | { | 224 | { |
338 | int or = dp->or, link = dp->link; | 225 | u8 sink[2]; |
339 | u8 *entry, sink[2]; | ||
340 | u32 dp_ctrl; | ||
341 | u16 script; | ||
342 | 226 | ||
343 | NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); | 227 | NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); |
344 | 228 | ||
345 | /* set selected link rate on source */ | 229 | /* set desired link configuration on the source */ |
346 | switch (dp->link_bw) { | 230 | dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, |
347 | case 270000: | 231 | dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); |
348 | nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); | ||
349 | sink[0] = DP_LINK_BW_2_7; | ||
350 | break; | ||
351 | default: | ||
352 | nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); | ||
353 | sink[0] = DP_LINK_BW_1_62; | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | /* offset +0x0a of each dp encoder table entry is a pointer to another | ||
358 | * table, that has (among other things) pointers to more scripts that | ||
359 | * need to be executed, this time depending on link speed. | ||
360 | */ | ||
361 | entry = ROMPTR(dev, dp->entry[10]); | ||
362 | if (entry) { | ||
363 | if (dp->table[0] < 0x30) { | ||
364 | while (dp->link_bw < (ROM16(entry[0]) * 10)) | ||
365 | entry += 4; | ||
366 | script = ROM16(entry[2]); | ||
367 | } else { | ||
368 | while (dp->link_bw < (entry[0] * 27000)) | ||
369 | entry += 3; | ||
370 | script = ROM16(entry[1]); | ||
371 | } | ||
372 | 232 | ||
373 | nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); | 233 | /* inform the sink of the new configuration */ |
374 | } | 234 | sink[0] = dp->link_bw / 27000; |
375 | |||
376 | /* configure lane count on the source */ | ||
377 | dp_ctrl = ((1 << dp->link_nr) - 1) << 16; | ||
378 | sink[1] = dp->link_nr; | 235 | sink[1] = dp->link_nr; |
379 | if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { | 236 | if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) |
380 | dp_ctrl |= 0x00004000; | ||
381 | sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; | 237 | sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
382 | } | ||
383 | |||
384 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); | ||
385 | 238 | ||
386 | /* inform the sink of the new configuration */ | ||
387 | auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); | 239 | auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); |
388 | } | 240 | } |
389 | 241 | ||
390 | static void | 242 | static void |
391 | dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) | 243 | dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) |
392 | { | 244 | { |
393 | u8 sink_tp; | 245 | u8 sink_tp; |
394 | 246 | ||
395 | NV_DEBUG_KMS(dev, "training pattern %d\n", tp); | 247 | NV_DEBUG_KMS(dev, "training pattern %d\n", pattern); |
396 | 248 | ||
397 | nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); | 249 | dp->func->train_set(dev, dp->dcb, pattern); |
398 | 250 | ||
399 | auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); | 251 | auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); |
400 | sink_tp &= ~DP_TRAINING_PATTERN_MASK; | 252 | sink_tp &= ~DP_TRAINING_PATTERN_MASK; |
401 | sink_tp |= tp; | 253 | sink_tp |= pattern; |
402 | auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); | 254 | auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); |
403 | } | 255 | } |
404 | 256 | ||
405 | static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; | ||
406 | static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; | ||
407 | |||
408 | static int | 257 | static int |
409 | dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) | 258 | dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) |
410 | { | 259 | { |
411 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
412 | u32 mask = 0, drv = 0, pre = 0, unk = 0; | ||
413 | const u8 *shifts; | ||
414 | int link = dp->link; | ||
415 | int or = dp->or; | ||
416 | int i; | 260 | int i; |
417 | 261 | ||
418 | if (dev_priv->chipset != 0xaf) | ||
419 | shifts = nv50_lane_map; | ||
420 | else | ||
421 | shifts = nvaf_lane_map; | ||
422 | |||
423 | for (i = 0; i < dp->link_nr; i++) { | 262 | for (i = 0; i < dp->link_nr; i++) { |
424 | u8 *conf = dp->entry + dp->table[4]; | ||
425 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; | 263 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; |
426 | u8 lpre = (lane & 0x0c) >> 2; | 264 | u8 lpre = (lane & 0x0c) >> 2; |
427 | u8 lvsw = (lane & 0x03) >> 0; | 265 | u8 lvsw = (lane & 0x03) >> 0; |
428 | 266 | ||
429 | mask |= 0xff << shifts[i]; | ||
430 | unk |= 1 << (shifts[i] >> 3); | ||
431 | |||
432 | dp->conf[i] = (lpre << 3) | lvsw; | 267 | dp->conf[i] = (lpre << 3) | lvsw; |
433 | if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) | 268 | if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) |
434 | dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; | 269 | dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; |
@@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) | |||
436 | dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; | 271 | dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; |
437 | 272 | ||
438 | NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); | 273 | NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); |
439 | 274 | dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); | |
440 | if (dp->table[0] < 0x30) { | ||
441 | u8 *last = conf + (dp->entry[4] * dp->table[5]); | ||
442 | while (lvsw != conf[0] || lpre != conf[1]) { | ||
443 | conf += dp->table[5]; | ||
444 | if (conf >= last) | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | conf += 2; | ||
449 | } else { | ||
450 | /* no lookup table anymore, set entries for each | ||
451 | * combination of voltage swing and pre-emphasis | ||
452 | * level allowed by the DP spec. | ||
453 | */ | ||
454 | switch (lvsw) { | ||
455 | case 0: lpre += 0; break; | ||
456 | case 1: lpre += 4; break; | ||
457 | case 2: lpre += 7; break; | ||
458 | case 3: lpre += 9; break; | ||
459 | } | ||
460 | |||
461 | conf = conf + (lpre * dp->table[5]); | ||
462 | conf++; | ||
463 | } | ||
464 | |||
465 | drv |= conf[0] << shifts[i]; | ||
466 | pre |= conf[1] << shifts[i]; | ||
467 | unk = (unk & ~0x0000ff00) | (conf[2] << 8); | ||
468 | } | 275 | } |
469 | 276 | ||
470 | nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); | ||
471 | nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); | ||
472 | nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); | ||
473 | |||
474 | return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); | 277 | return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); |
475 | } | 278 | } |
476 | 279 | ||
@@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) | |||
598 | } | 401 | } |
599 | 402 | ||
600 | bool | 403 | bool |
601 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) | 404 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, |
405 | struct dp_train_func *func) | ||
602 | { | 406 | { |
603 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 407 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
604 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | 408 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); |
@@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) | |||
614 | if (!auxch) | 418 | if (!auxch) |
615 | return false; | 419 | return false; |
616 | 420 | ||
617 | dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); | 421 | dp.func = func; |
618 | if (!dp.table) | ||
619 | return -EINVAL; | ||
620 | |||
621 | dp.dcb = nv_encoder->dcb; | 422 | dp.dcb = nv_encoder->dcb; |
622 | dp.crtc = nv_crtc->index; | 423 | dp.crtc = nv_crtc->index; |
623 | dp.auxch = auxch->drive; | 424 | dp.auxch = auxch->drive; |
624 | dp.or = nv_encoder->or; | ||
625 | dp.link = !(nv_encoder->dcb->sorconf.link & 1); | ||
626 | dp.dpcd = nv_encoder->dp.dpcd; | 425 | dp.dpcd = nv_encoder->dp.dpcd; |
627 | 426 | ||
628 | /* some sinks toggle hotplug in response to some of the actions | 427 | /* some sinks toggle hotplug in response to some of the actions |