aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv50_sor.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_sor.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c213
1 files changed, 195 insertions, 18 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index c4423ba9c9bf..a7844ab6a50c 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -36,6 +36,193 @@
36#include "nouveau_crtc.h" 36#include "nouveau_crtc.h"
37#include "nv50_display.h" 37#include "nv50_display.h"
38 38
39static u32
40nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane)
41{
42 struct drm_nouveau_private *dev_priv = dev->dev_private;
43 static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
44 static const u8 nv50[] = { 16, 8, 0, 24 };
45 if (dev_priv->card_type == 0xaf)
46 return nvaf[lane];
47 return nv50[lane];
48}
49
50static void
51nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern)
52{
53 u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
54 nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24);
55}
56
57static void
58nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb,
59 u8 lane, u8 swing, u8 preem)
60{
61 u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
62 u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane);
63 u32 mask = 0x000000ff << shift;
64 u8 *table, *entry, *config;
65
66 table = nouveau_dp_bios_data(dev, dcb, &entry);
67 if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
68 NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
69 return;
70 }
71
72 config = entry + table[4];
73 while (config[0] != swing || config[1] != preem) {
74 config += table[5];
75 if (config >= entry + table[4] + entry[4] * table[5])
76 return;
77 }
78
79 nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift);
80 nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift);
81 nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8);
82}
83
84static void
85nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc,
86 int link_nr, u32 link_bw, bool enhframe)
87{
88 u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
89 u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000;
90 u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000;
91 u8 *table, *entry, mask;
92 int i;
93
94 table = nouveau_dp_bios_data(dev, dcb, &entry);
95 if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
96 NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
97 return;
98 }
99
100 entry = ROMPTR(dev, entry[10]);
101 if (entry) {
102 while (link_bw < ROM16(entry[0]) * 10)
103 entry += 4;
104
105 nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc);
106 }
107
108 dpctrl |= ((1 << link_nr) - 1) << 16;
109 if (enhframe)
110 dpctrl |= 0x00004000;
111
112 if (link_bw > 162000)
113 clksor |= 0x00040000;
114
115 nv_wr32(dev, 0x614300 + (or * 0x800), clksor);
116 nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl);
117
118 mask = 0;
119 for (i = 0; i < link_nr; i++)
120 mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3);
121 nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask);
122}
123
124static void
125nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw)
126{
127 u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000;
128 u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800));
129 if (clksor & 0x000c0000)
130 *bw = 270000;
131 else
132 *bw = 162000;
133
134 if (dpctrl > 0x00030000) *nr = 4;
135 else if (dpctrl > 0x00010000) *nr = 2;
136 else *nr = 1;
137}
138
139void
140nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
141{
142 const u32 symbol = 100000;
143 int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
144 int TU, VTUi, VTUf, VTUa;
145 u64 link_data_rate, link_ratio, unk;
146 u32 best_diff = 64 * symbol;
147 u32 link_nr, link_bw, r;
148
149 /* calculate packed data rate for each lane */
150 nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw);
151 link_data_rate = (clk * bpp / 8) / link_nr;
152
153 /* calculate ratio of packed data rate to link symbol rate */
154 link_ratio = link_data_rate * symbol;
155 r = do_div(link_ratio, link_bw);
156
157 for (TU = 64; TU >= 32; TU--) {
158 /* calculate average number of valid symbols in each TU */
159 u32 tu_valid = link_ratio * TU;
160 u32 calc, diff;
161
162 /* find a hw representation for the fraction.. */
163 VTUi = tu_valid / symbol;
164 calc = VTUi * symbol;
165 diff = tu_valid - calc;
166 if (diff) {
167 if (diff >= (symbol / 2)) {
168 VTUf = symbol / (symbol - diff);
169 if (symbol - (VTUf * diff))
170 VTUf++;
171
172 if (VTUf <= 15) {
173 VTUa = 1;
174 calc += symbol - (symbol / VTUf);
175 } else {
176 VTUa = 0;
177 VTUf = 1;
178 calc += symbol;
179 }
180 } else {
181 VTUa = 0;
182 VTUf = min((int)(symbol / diff), 15);
183 calc += symbol / VTUf;
184 }
185
186 diff = calc - tu_valid;
187 } else {
188 /* no remainder, but the hw doesn't like the fractional
189 * part to be zero. decrement the integer part and
190 * have the fraction add a whole symbol back
191 */
192 VTUa = 0;
193 VTUf = 1;
194 VTUi--;
195 }
196
197 if (diff < best_diff) {
198 best_diff = diff;
199 bestTU = TU;
200 bestVTUa = VTUa;
201 bestVTUf = VTUf;
202 bestVTUi = VTUi;
203 if (diff == 0)
204 break;
205 }
206 }
207
208 if (!bestTU) {
209 NV_ERROR(dev, "DP: unable to find suitable config\n");
210 return;
211 }
212
213 /* XXX close to vbios numbers, but not right */
214 unk = (symbol - link_ratio) * bestTU;
215 unk *= link_ratio;
216 r = do_div(unk, symbol);
217 r = do_div(unk, symbol);
218 unk += 6;
219
220 nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
221 nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
222 bestVTUf << 16 |
223 bestVTUi << 8 |
224 unk);
225}
39static void 226static void
40nv50_sor_disconnect(struct drm_encoder *encoder) 227nv50_sor_disconnect(struct drm_encoder *encoder)
41{ 228{
@@ -117,20 +304,13 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
117 } 304 }
118 305
119 if (nv_encoder->dcb->type == OUTPUT_DP) { 306 if (nv_encoder->dcb->type == OUTPUT_DP) {
120 struct nouveau_i2c_chan *auxch; 307 struct dp_train_func func = {
121 308 .link_set = nv50_sor_dp_link_set,
122 auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); 309 .train_set = nv50_sor_dp_train_set,
123 if (!auxch) 310 .train_adj = nv50_sor_dp_train_adj
124 return; 311 };
125 312
126 if (mode == DRM_MODE_DPMS_ON) { 313 nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func);
127 u8 status = DP_SET_POWER_D0;
128 nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
129 nouveau_dp_link_train(encoder, nv_encoder->dp.datarate);
130 } else {
131 u8 status = DP_SET_POWER_D3;
132 nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
133 }
134 } 314 }
135} 315}
136 316
@@ -162,11 +342,8 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
162 } 342 }
163 343
164 if (connector->scaling_mode != DRM_MODE_SCALE_NONE && 344 if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
165 connector->native_mode) { 345 connector->native_mode)
166 int id = adjusted_mode->base.id; 346 drm_mode_copy(adjusted_mode, connector->native_mode);
167 *adjusted_mode = *connector->native_mode;
168 adjusted_mode->base.id = id;
169 }
170 347
171 return true; 348 return true;
172} 349}