diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-07-01 01:51:49 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-09-20 02:09:42 -0400 |
commit | 46959b7790e3609e795c3b5e70e58dcd22c9e207 (patch) | |
tree | 0f287b3800a232ce45684a84c2fadee21154a3d4 /drivers/gpu/drm | |
parent | 43720133888f3713b534aec520783498f1bf5db3 (diff) |
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) | |||
194 | return ret; | 194 | return ret; |
195 | } | 195 | } |
196 | 196 | ||
197 | static u32 | ||
198 | dp_link_bw_get(struct drm_device *dev, int or, int link) | ||
199 | { | ||
200 | u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); | ||
201 | if (!(ctrl & 0x000c0000)) | ||
202 | return 162000; | ||
203 | return 270000; | ||
204 | } | ||
205 | |||
206 | static int | ||
207 | dp_lane_count_get(struct drm_device *dev, int or, int link) | ||
208 | { | ||
209 | u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
210 | switch (ctrl & 0x000f0000) { | ||
211 | case 0x00010000: return 1; | ||
212 | case 0x00030000: return 2; | ||
213 | default: | ||
214 | return 4; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | void | ||
219 | nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | ||
220 | { | ||
221 | const u32 symbol = 100000; | ||
222 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | ||
223 | int TU, VTUi, VTUf, VTUa; | ||
224 | u64 link_data_rate, link_ratio, unk; | ||
225 | u32 best_diff = 64 * symbol; | ||
226 | u32 link_nr, link_bw, r; | ||
227 | |||
228 | /* calculate packed data rate for each lane */ | ||
229 | link_nr = dp_lane_count_get(dev, or, link); | ||
230 | link_data_rate = (clk * bpp / 8) / link_nr; | ||
231 | |||
232 | /* calculate ratio of packed data rate to link symbol rate */ | ||
233 | link_bw = dp_link_bw_get(dev, or, link); | ||
234 | link_ratio = link_data_rate * symbol; | ||
235 | r = do_div(link_ratio, link_bw); | ||
236 | |||
237 | for (TU = 64; TU >= 32; TU--) { | ||
238 | /* calculate average number of valid symbols in each TU */ | ||
239 | u32 tu_valid = link_ratio * TU; | ||
240 | u32 calc, diff; | ||
241 | |||
242 | /* find a hw representation for the fraction.. */ | ||
243 | VTUi = tu_valid / symbol; | ||
244 | calc = VTUi * symbol; | ||
245 | diff = tu_valid - calc; | ||
246 | if (diff) { | ||
247 | if (diff >= (symbol / 2)) { | ||
248 | VTUf = symbol / (symbol - diff); | ||
249 | if (symbol - (VTUf * diff)) | ||
250 | VTUf++; | ||
251 | |||
252 | if (VTUf <= 15) { | ||
253 | VTUa = 1; | ||
254 | calc += symbol - (symbol / VTUf); | ||
255 | } else { | ||
256 | VTUa = 0; | ||
257 | VTUf = 1; | ||
258 | calc += symbol; | ||
259 | } | ||
260 | } else { | ||
261 | VTUa = 0; | ||
262 | VTUf = min((int)(symbol / diff), 15); | ||
263 | calc += symbol / VTUf; | ||
264 | } | ||
265 | |||
266 | diff = calc - tu_valid; | ||
267 | } else { | ||
268 | /* no remainder, but the hw doesn't like the fractional | ||
269 | * part to be zero. decrement the integer part and | ||
270 | * have the fraction add a whole symbol back | ||
271 | */ | ||
272 | VTUa = 0; | ||
273 | VTUf = 1; | ||
274 | VTUi--; | ||
275 | } | ||
276 | |||
277 | if (diff < best_diff) { | ||
278 | best_diff = diff; | ||
279 | bestTU = TU; | ||
280 | bestVTUa = VTUa; | ||
281 | bestVTUf = VTUf; | ||
282 | bestVTUi = VTUi; | ||
283 | if (diff == 0) | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | if (!bestTU) { | ||
289 | NV_ERROR(dev, "DP: unable to find suitable config\n"); | ||
290 | return; | ||
291 | } | ||
292 | |||
293 | /* XXX close to vbios numbers, but not right */ | ||
294 | unk = (symbol - link_ratio) * bestTU; | ||
295 | unk *= link_ratio; | ||
296 | r = do_div(unk, symbol); | ||
297 | r = do_div(unk, symbol); | ||
298 | unk += 6; | ||
299 | |||
300 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); | ||
301 | nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | | ||
302 | bestVTUf << 16 | | ||
303 | bestVTUi << 8 | | ||
304 | unk); | ||
305 | } | ||
306 | |||
197 | static int | 307 | static int |
198 | nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) | 308 | nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) |
199 | { | 309 | { |
@@ -617,7 +727,6 @@ static int | |||
617 | nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | 727 | nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) |
618 | { | 728 | { |
619 | struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; | 729 | struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; |
620 | struct drm_device *dev = auxch->dev; | ||
621 | struct i2c_msg *msg = msgs; | 730 | struct i2c_msg *msg = msgs; |
622 | int ret, mcnt = num; | 731 | int ret, mcnt = num; |
623 | 732 | ||
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, | |||
1101 | uint8_t *data, int data_nr); | 1101 | uint8_t *data, int data_nr); |
1102 | bool nouveau_dp_detect(struct drm_encoder *); | 1102 | bool nouveau_dp_detect(struct drm_encoder *); |
1103 | bool nouveau_dp_link_train(struct drm_encoder *); | 1103 | bool nouveau_dp_link_train(struct drm_encoder *); |
1104 | void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); | ||
1104 | 1105 | ||
1105 | /* nv04_fb.c */ | 1106 | /* nv04_fb.c */ |
1106 | extern int nv04_fb_init(struct drm_device *); | 1107 | 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 { | |||
49 | 49 | ||
50 | union { | 50 | union { |
51 | struct { | 51 | struct { |
52 | int mc_unknown; | ||
53 | uint32_t unk0; | ||
54 | uint32_t unk1; | ||
55 | int dpcd_version; | 52 | int dpcd_version; |
56 | int link_nr; | 53 | int link_nr; |
57 | int link_bw; | 54 | 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 @@ | |||
843 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 | 843 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 |
844 | #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) | 844 | #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) |
845 | #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) | 845 | #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) |
846 | #define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) | 846 | #define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) |
847 | #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) | 847 | #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) |
848 | 848 | ||
849 | #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) | 849 | #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: | |||
702 | } | 702 | } |
703 | 703 | ||
704 | static void | 704 | static void |
705 | nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) | ||
706 | { | ||
707 | int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); | ||
708 | struct drm_encoder *encoder; | ||
709 | uint32_t tmp, unk0 = 0, unk1 = 0; | ||
710 | |||
711 | if (dcb->type != OUTPUT_DP) | ||
712 | return; | ||
713 | |||
714 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
715 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
716 | |||
717 | if (nv_encoder->dcb == dcb) { | ||
718 | unk0 = nv_encoder->dp.unk0; | ||
719 | unk1 = nv_encoder->dp.unk1; | ||
720 | break; | ||
721 | } | ||
722 | } | ||
723 | |||
724 | if (unk0 || unk1) { | ||
725 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
726 | tmp &= 0xfffffe03; | ||
727 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0); | ||
728 | |||
729 | tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); | ||
730 | tmp &= 0xfef080c0; | ||
731 | nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1); | ||
732 | } | ||
733 | } | ||
734 | |||
735 | static void | ||
736 | nv50_display_unk20_handler(struct drm_device *dev) | 705 | nv50_display_unk20_handler(struct drm_device *dev) |
737 | { | 706 | { |
738 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 707 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
@@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev) | |||
830 | script = nv50_display_script_select(dev, dcb, mc, pclk); | 799 | script = nv50_display_script_select(dev, dcb, mc, pclk); |
831 | nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); | 800 | nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); |
832 | 801 | ||
833 | nv50_display_unk20_dp_hack(dev, dcb); | 802 | if (type == OUTPUT_DP) { |
803 | int link = !(dcb->dpconf.sor.link & 1); | ||
804 | if ((mc & 0x000f0000) == 0x00020000) | ||
805 | nouveau_dp_tu_update(dev, or, link, pclk, 18); | ||
806 | else | ||
807 | nouveau_dp_tu_update(dev, or, link, pclk, 24); | ||
808 | } | ||
834 | 809 | ||
835 | if (dcb->type != OUTPUT_ANALOG) { | 810 | if (dcb->type != OUTPUT_ANALOG) { |
836 | tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); | 811 | 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, | |||
187 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 187 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
188 | struct drm_device *dev = encoder->dev; | 188 | struct drm_device *dev = encoder->dev; |
189 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | 189 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); |
190 | struct nouveau_connector *nv_connector; | ||
190 | uint32_t mode_ctl = 0; | 191 | uint32_t mode_ctl = 0; |
191 | int ret; | 192 | int ret; |
192 | 193 | ||
@@ -206,7 +207,12 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | |||
206 | mode_ctl = 0x0200; | 207 | mode_ctl = 0x0200; |
207 | break; | 208 | break; |
208 | case OUTPUT_DP: | 209 | case OUTPUT_DP: |
209 | mode_ctl |= (nv_encoder->dp.mc_unknown << 16); | 210 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
211 | if (nv_connector && nv_connector->base.display_info.bpc == 6) | ||
212 | mode_ctl |= 0x00020000; | ||
213 | else | ||
214 | mode_ctl |= 0x00050000; | ||
215 | |||
210 | if (nv_encoder->dcb->sorconf.link & 1) | 216 | if (nv_encoder->dcb->sorconf.link & 1) |
211 | mode_ctl |= 0x00000800; | 217 | mode_ctl |= 0x00000800; |
212 | else | 218 | else |
@@ -313,31 +319,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry) | |||
313 | encoder->possible_crtcs = entry->heads; | 319 | encoder->possible_crtcs = entry->heads; |
314 | encoder->possible_clones = 0; | 320 | encoder->possible_clones = 0; |
315 | 321 | ||
316 | if (nv_encoder->dcb->type == OUTPUT_DP) { | ||
317 | int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1); | ||
318 | uint32_t tmp; | ||
319 | |||
320 | tmp = nv_rd32(dev, 0x61c700 + (or * 0x800)); | ||
321 | if (!tmp) | ||
322 | tmp = nv_rd32(dev, 0x610798 + (or * 8)); | ||
323 | |||
324 | switch ((tmp & 0x00000f00) >> 8) { | ||
325 | case 8: | ||
326 | case 9: | ||
327 | nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16; | ||
328 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
329 | nv_encoder->dp.unk0 = tmp & 0x000001fc; | ||
330 | tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link)); | ||
331 | nv_encoder->dp.unk1 = tmp & 0x010f7f3f; | ||
332 | break; | ||
333 | default: | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | if (!nv_encoder->dp.mc_unknown) | ||
338 | nv_encoder->dp.mc_unknown = 5; | ||
339 | } | ||
340 | |||
341 | drm_mode_connector_attach_encoder(connector, encoder); | 322 | drm_mode_connector_attach_encoder(connector, encoder); |
342 | return 0; | 323 | return 0; |
343 | } | 324 | } |