Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright (C) 2008 Maarten Maathuis. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining | |
6 | * a copy of this software and associated documentation files (the | |
7 | * "Software"), to deal in the Software without restriction, including | |
8 | * without limitation the rights to use, copy, modify, merge, publish, | |
9 | * distribute, sublicense, and/or sell copies of the Software, and to | |
10 | * permit persons to whom the Software is furnished to do so, subject to | |
11 | * the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice (including the | |
14 | * next paragraph) shall be included in all copies or substantial | |
15 | * portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
24 | * | |
25 | */ | |
26 | ||
760285e7 DH |
27 | #include <drm/drmP.h> |
28 | #include <drm/drm_crtc_helper.h> | |
6ee73861 BS |
29 | |
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | |
31 | #include "nouveau_reg.h" | |
77145f1c | 32 | #include "nouveau_drm.h" |
6ee73861 BS |
33 | #include "nouveau_dma.h" |
34 | #include "nouveau_encoder.h" | |
35 | #include "nouveau_connector.h" | |
36 | #include "nouveau_crtc.h" | |
37 | #include "nv50_display.h" | |
38 | ||
77145f1c BS |
39 | #include <subdev/timer.h> |
40 | ||
8663bc7c | 41 | static u32 |
cb75d97e | 42 | nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_output *dcb, u8 lane) |
8663bc7c | 43 | { |
77145f1c | 44 | struct nouveau_drm *drm = nouveau_drm(dev); |
8663bc7c BS |
45 | static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ |
46 | static const u8 nv50[] = { 16, 8, 0, 24 }; | |
77145f1c | 47 | if (nv_device(drm->device)->chipset == 0xaf) |
8663bc7c BS |
48 | return nvaf[lane]; |
49 | return nv50[lane]; | |
50 | } | |
51 | ||
52 | static void | |
cb75d97e | 53 | nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_output *dcb, u8 pattern) |
8663bc7c | 54 | { |
77145f1c | 55 | struct nouveau_device *device = nouveau_dev(dev); |
8663bc7c | 56 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); |
77145f1c | 57 | nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); |
8663bc7c BS |
58 | } |
59 | ||
60 | static void | |
cb75d97e | 61 | nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_output *dcb, |
8663bc7c BS |
62 | u8 lane, u8 swing, u8 preem) |
63 | { | |
77145f1c BS |
64 | struct nouveau_device *device = nouveau_dev(dev); |
65 | struct nouveau_drm *drm = nouveau_drm(dev); | |
8663bc7c BS |
66 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); |
67 | u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); | |
68 | u32 mask = 0x000000ff << shift; | |
69 | u8 *table, *entry, *config; | |
70 | ||
71 | table = nouveau_dp_bios_data(dev, dcb, &entry); | |
72 | if (!table || (table[0] != 0x20 && table[0] != 0x21)) { | |
77145f1c | 73 | NV_ERROR(drm, "PDISP: unsupported DP table for chipset\n"); |
8663bc7c BS |
74 | return; |
75 | } | |
76 | ||
77 | config = entry + table[4]; | |
78 | while (config[0] != swing || config[1] != preem) { | |
79 | config += table[5]; | |
80 | if (config >= entry + table[4] + entry[4] * table[5]) | |
81 | return; | |
82 | } | |
83 | ||
77145f1c BS |
84 | nv_mask(device, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); |
85 | nv_mask(device, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); | |
86 | nv_mask(device, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); | |
8663bc7c BS |
87 | } |
88 | ||
89 | static void | |
cb75d97e | 90 | nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc, |
8663bc7c BS |
91 | int link_nr, u32 link_bw, bool enhframe) |
92 | { | |
77145f1c BS |
93 | struct nouveau_device *device = nouveau_dev(dev); |
94 | struct nouveau_drm *drm = nouveau_drm(dev); | |
8663bc7c | 95 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); |
77145f1c BS |
96 | u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; |
97 | u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800)) & ~0x000c0000; | |
8663bc7c BS |
98 | u8 *table, *entry, mask; |
99 | int i; | |
100 | ||
101 | table = nouveau_dp_bios_data(dev, dcb, &entry); | |
102 | if (!table || (table[0] != 0x20 && table[0] != 0x21)) { | |
77145f1c | 103 | NV_ERROR(drm, "PDISP: unsupported DP table for chipset\n"); |
8663bc7c BS |
104 | return; |
105 | } | |
106 | ||
107 | entry = ROMPTR(dev, entry[10]); | |
108 | if (entry) { | |
109 | while (link_bw < ROM16(entry[0]) * 10) | |
110 | entry += 4; | |
111 | ||
112 | nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); | |
113 | } | |
114 | ||
115 | dpctrl |= ((1 << link_nr) - 1) << 16; | |
116 | if (enhframe) | |
117 | dpctrl |= 0x00004000; | |
118 | ||
119 | if (link_bw > 162000) | |
120 | clksor |= 0x00040000; | |
121 | ||
77145f1c BS |
122 | nv_wr32(device, 0x614300 + (or * 0x800), clksor); |
123 | nv_wr32(device, NV50_SOR_DP_CTRL(or, link), dpctrl); | |
8663bc7c BS |
124 | |
125 | mask = 0; | |
126 | for (i = 0; i < link_nr; i++) | |
127 | mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); | |
77145f1c | 128 | nv_mask(device, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); |
8663bc7c BS |
129 | } |
130 | ||
131 | static void | |
132 | nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) | |
133 | { | |
77145f1c BS |
134 | struct nouveau_device *device = nouveau_dev(dev); |
135 | u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; | |
136 | u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800)); | |
8663bc7c BS |
137 | if (clksor & 0x000c0000) |
138 | *bw = 270000; | |
139 | else | |
140 | *bw = 162000; | |
141 | ||
142 | if (dpctrl > 0x00030000) *nr = 4; | |
143 | else if (dpctrl > 0x00010000) *nr = 2; | |
144 | else *nr = 1; | |
145 | } | |
146 | ||
147 | void | |
148 | nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | |
149 | { | |
77145f1c BS |
150 | struct nouveau_device *device = nouveau_dev(dev); |
151 | struct nouveau_drm *drm = nouveau_drm(dev); | |
8663bc7c BS |
152 | const u32 symbol = 100000; |
153 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | |
154 | int TU, VTUi, VTUf, VTUa; | |
155 | u64 link_data_rate, link_ratio, unk; | |
156 | u32 best_diff = 64 * symbol; | |
157 | u32 link_nr, link_bw, r; | |
158 | ||
159 | /* calculate packed data rate for each lane */ | |
160 | nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); | |
161 | link_data_rate = (clk * bpp / 8) / link_nr; | |
162 | ||
163 | /* calculate ratio of packed data rate to link symbol rate */ | |
164 | link_ratio = link_data_rate * symbol; | |
165 | r = do_div(link_ratio, link_bw); | |
166 | ||
167 | for (TU = 64; TU >= 32; TU--) { | |
168 | /* calculate average number of valid symbols in each TU */ | |
169 | u32 tu_valid = link_ratio * TU; | |
170 | u32 calc, diff; | |
171 | ||
172 | /* find a hw representation for the fraction.. */ | |
173 | VTUi = tu_valid / symbol; | |
174 | calc = VTUi * symbol; | |
175 | diff = tu_valid - calc; | |
176 | if (diff) { | |
177 | if (diff >= (symbol / 2)) { | |
178 | VTUf = symbol / (symbol - diff); | |
179 | if (symbol - (VTUf * diff)) | |
180 | VTUf++; | |
181 | ||
182 | if (VTUf <= 15) { | |
183 | VTUa = 1; | |
184 | calc += symbol - (symbol / VTUf); | |
185 | } else { | |
186 | VTUa = 0; | |
187 | VTUf = 1; | |
188 | calc += symbol; | |
189 | } | |
190 | } else { | |
191 | VTUa = 0; | |
192 | VTUf = min((int)(symbol / diff), 15); | |
193 | calc += symbol / VTUf; | |
194 | } | |
195 | ||
196 | diff = calc - tu_valid; | |
197 | } else { | |
198 | /* no remainder, but the hw doesn't like the fractional | |
199 | * part to be zero. decrement the integer part and | |
200 | * have the fraction add a whole symbol back | |
201 | */ | |
202 | VTUa = 0; | |
203 | VTUf = 1; | |
204 | VTUi--; | |
205 | } | |
206 | ||
207 | if (diff < best_diff) { | |
208 | best_diff = diff; | |
209 | bestTU = TU; | |
210 | bestVTUa = VTUa; | |
211 | bestVTUf = VTUf; | |
212 | bestVTUi = VTUi; | |
213 | if (diff == 0) | |
214 | break; | |
215 | } | |
216 | } | |
217 | ||
218 | if (!bestTU) { | |
77145f1c | 219 | NV_ERROR(drm, "DP: unable to find suitable config\n"); |
8663bc7c BS |
220 | return; |
221 | } | |
222 | ||
223 | /* XXX close to vbios numbers, but not right */ | |
224 | unk = (symbol - link_ratio) * bestTU; | |
225 | unk *= link_ratio; | |
226 | r = do_div(unk, symbol); | |
227 | r = do_div(unk, symbol); | |
228 | unk += 6; | |
229 | ||
77145f1c BS |
230 | nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); |
231 | nv_mask(device, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | | |
8663bc7c BS |
232 | bestVTUf << 16 | |
233 | bestVTUi << 8 | | |
234 | unk); | |
235 | } | |
6ee73861 | 236 | static void |
ec7fc4a1 | 237 | nv50_sor_disconnect(struct drm_encoder *encoder) |
6ee73861 | 238 | { |
ec7fc4a1 | 239 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
77145f1c | 240 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
ec7fc4a1 | 241 | struct drm_device *dev = encoder->dev; |
59c0f578 | 242 | struct nouveau_channel *evo = nv50_display(dev)->master; |
6ee73861 BS |
243 | int ret; |
244 | ||
ec7fc4a1 BS |
245 | if (!nv_encoder->crtc) |
246 | return; | |
835aadbe | 247 | nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true); |
ec7fc4a1 | 248 | |
77145f1c | 249 | NV_DEBUG(drm, "Disconnecting SOR %d\n", nv_encoder->or); |
6ee73861 | 250 | |
835aadbe | 251 | ret = RING_SPACE(evo, 4); |
6ee73861 | 252 | if (ret) { |
77145f1c | 253 | NV_ERROR(drm, "no space while disconnecting SOR\n"); |
6ee73861 BS |
254 | return; |
255 | } | |
6d597027 | 256 | BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); |
835aadbe | 257 | OUT_RING (evo, 0); |
6d597027 | 258 | BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); |
835aadbe | 259 | OUT_RING (evo, 0); |
ec7fc4a1 | 260 | |
25575b41 BS |
261 | nouveau_hdmi_mode_set(encoder, NULL); |
262 | ||
ec7fc4a1 BS |
263 | nv_encoder->crtc = NULL; |
264 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; | |
6ee73861 BS |
265 | } |
266 | ||
6ee73861 BS |
267 | static void |
268 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | |
269 | { | |
77145f1c BS |
270 | struct nouveau_device *device = nouveau_dev(encoder->dev); |
271 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); | |
6ee73861 BS |
272 | struct drm_device *dev = encoder->dev; |
273 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
16226536 | 274 | struct drm_encoder *enc; |
6ee73861 BS |
275 | uint32_t val; |
276 | int or = nv_encoder->or; | |
277 | ||
77145f1c | 278 | NV_DEBUG(drm, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode); |
6ee73861 | 279 | |
16226536 BS |
280 | nv_encoder->last_dpms = mode; |
281 | list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { | |
282 | struct nouveau_encoder *nvenc = nouveau_encoder(enc); | |
283 | ||
284 | if (nvenc == nv_encoder || | |
cb75d97e BS |
285 | (nvenc->dcb->type != DCB_OUTPUT_TMDS && |
286 | nvenc->dcb->type != DCB_OUTPUT_LVDS && | |
287 | nvenc->dcb->type != DCB_OUTPUT_DP) || | |
16226536 BS |
288 | nvenc->dcb->or != nv_encoder->dcb->or) |
289 | continue; | |
290 | ||
291 | if (nvenc->last_dpms == DRM_MODE_DPMS_ON) | |
292 | return; | |
293 | } | |
294 | ||
6ee73861 | 295 | /* wait for it to be done */ |
77145f1c | 296 | if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or), |
6ee73861 | 297 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { |
77145f1c BS |
298 | NV_ERROR(drm, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); |
299 | NV_ERROR(drm, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, | |
300 | nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); | |
6ee73861 BS |
301 | } |
302 | ||
77145f1c | 303 | val = nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); |
6ee73861 BS |
304 | |
305 | if (mode == DRM_MODE_DPMS_ON) | |
306 | val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
307 | else | |
308 | val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
309 | ||
77145f1c | 310 | nv_wr32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | |
6ee73861 | 311 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); |
77145f1c | 312 | if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_STATE(or), |
6ee73861 | 313 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { |
77145f1c BS |
314 | NV_ERROR(drm, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); |
315 | NV_ERROR(drm, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, | |
316 | nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_STATE(or))); | |
6ee73861 BS |
317 | } |
318 | ||
cb75d97e | 319 | if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { |
f14d9a4d BS |
320 | struct dp_train_func func = { |
321 | .link_set = nv50_sor_dp_link_set, | |
322 | .train_set = nv50_sor_dp_train_set, | |
323 | .train_adj = nv50_sor_dp_train_adj | |
324 | }; | |
1f403d9c | 325 | |
f14d9a4d | 326 | nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); |
1f403d9c | 327 | } |
6ee73861 BS |
328 | } |
329 | ||
330 | static void | |
331 | nv50_sor_save(struct drm_encoder *encoder) | |
332 | { | |
77145f1c BS |
333 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
334 | NV_ERROR(drm, "!!\n"); | |
6ee73861 BS |
335 | } |
336 | ||
337 | static void | |
338 | nv50_sor_restore(struct drm_encoder *encoder) | |
339 | { | |
77145f1c BS |
340 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
341 | NV_ERROR(drm, "!!\n"); | |
6ee73861 BS |
342 | } |
343 | ||
344 | static bool | |
e811f5ae LP |
345 | nv50_sor_mode_fixup(struct drm_encoder *encoder, |
346 | const struct drm_display_mode *mode, | |
6ee73861 BS |
347 | struct drm_display_mode *adjusted_mode) |
348 | { | |
77145f1c | 349 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
6ee73861 BS |
350 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
351 | struct nouveau_connector *connector; | |
352 | ||
77145f1c | 353 | NV_DEBUG(drm, "or %d\n", nv_encoder->or); |
6ee73861 BS |
354 | |
355 | connector = nouveau_encoder_connector_get(nv_encoder); | |
356 | if (!connector) { | |
77145f1c | 357 | NV_ERROR(drm, "Encoder has no connector\n"); |
6ee73861 BS |
358 | return false; |
359 | } | |
360 | ||
361 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | |
c3c50e8b VS |
362 | connector->native_mode) |
363 | drm_mode_copy(adjusted_mode, connector->native_mode); | |
6ee73861 BS |
364 | |
365 | return true; | |
366 | } | |
367 | ||
368 | static void | |
369 | nv50_sor_prepare(struct drm_encoder *encoder) | |
370 | { | |
9976f15c | 371 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
7ae494e8 | 372 | nv50_sor_disconnect(encoder); |
cb75d97e | 373 | if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { |
9976f15c BS |
374 | /* avoid race between link training and supervisor intr */ |
375 | nv50_display_sync(encoder->dev); | |
376 | } | |
6ee73861 BS |
377 | } |
378 | ||
379 | static void | |
380 | nv50_sor_commit(struct drm_encoder *encoder) | |
381 | { | |
382 | } | |
383 | ||
384 | static void | |
a03a8623 BS |
385 | nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, |
386 | struct drm_display_mode *mode) | |
6ee73861 | 387 | { |
59c0f578 | 388 | struct nouveau_channel *evo = nv50_display(encoder->dev)->master; |
6ee73861 | 389 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
77145f1c | 390 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
6ee73861 | 391 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); |
46959b77 | 392 | struct nouveau_connector *nv_connector; |
6ee73861 BS |
393 | uint32_t mode_ctl = 0; |
394 | int ret; | |
395 | ||
77145f1c | 396 | NV_DEBUG(drm, "or %d type %d -> crtc %d\n", |
04412921 | 397 | nv_encoder->or, nv_encoder->dcb->type, crtc->index); |
52c7bcdb | 398 | nv_encoder->crtc = encoder->crtc; |
6ee73861 | 399 | |
6ee73861 | 400 | switch (nv_encoder->dcb->type) { |
cb75d97e | 401 | case DCB_OUTPUT_TMDS: |
6ee73861 | 402 | if (nv_encoder->dcb->sorconf.link & 1) { |
a03a8623 | 403 | if (mode->clock < 165000) |
6ee73861 BS |
404 | mode_ctl = 0x0100; |
405 | else | |
406 | mode_ctl = 0x0500; | |
407 | } else | |
408 | mode_ctl = 0x0200; | |
25575b41 | 409 | |
a03a8623 | 410 | nouveau_hdmi_mode_set(encoder, mode); |
6ee73861 | 411 | break; |
cb75d97e | 412 | case DCB_OUTPUT_DP: |
46959b77 | 413 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
a002fece | 414 | if (nv_connector && nv_connector->base.display_info.bpc == 6) { |
a03a8623 | 415 | nv_encoder->dp.datarate = mode->clock * 18 / 8; |
46959b77 | 416 | mode_ctl |= 0x00020000; |
a002fece | 417 | } else { |
a03a8623 | 418 | nv_encoder->dp.datarate = mode->clock * 24 / 8; |
46959b77 | 419 | mode_ctl |= 0x00050000; |
a002fece | 420 | } |
46959b77 | 421 | |
6ee73861 BS |
422 | if (nv_encoder->dcb->sorconf.link & 1) |
423 | mode_ctl |= 0x00000800; | |
424 | else | |
425 | mode_ctl |= 0x00000900; | |
426 | break; | |
427 | default: | |
428 | break; | |
429 | } | |
430 | ||
431 | if (crtc->index == 1) | |
432 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; | |
433 | else | |
434 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; | |
435 | ||
a03a8623 | 436 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
6ee73861 BS |
437 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; |
438 | ||
a03a8623 | 439 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
6ee73861 BS |
440 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; |
441 | ||
a002fece BS |
442 | nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); |
443 | ||
6ee73861 BS |
444 | ret = RING_SPACE(evo, 2); |
445 | if (ret) { | |
77145f1c | 446 | NV_ERROR(drm, "no space while connecting SOR\n"); |
52c7bcdb | 447 | nv_encoder->crtc = NULL; |
6ee73861 BS |
448 | return; |
449 | } | |
6d597027 | 450 | BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); |
6ee73861 | 451 | OUT_RING(evo, mode_ctl); |
ec7fc4a1 BS |
452 | } |
453 | ||
454 | static struct drm_crtc * | |
455 | nv50_sor_crtc_get(struct drm_encoder *encoder) | |
456 | { | |
457 | return nouveau_encoder(encoder)->crtc; | |
6ee73861 BS |
458 | } |
459 | ||
460 | static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { | |
461 | .dpms = nv50_sor_dpms, | |
462 | .save = nv50_sor_save, | |
463 | .restore = nv50_sor_restore, | |
464 | .mode_fixup = nv50_sor_mode_fixup, | |
465 | .prepare = nv50_sor_prepare, | |
466 | .commit = nv50_sor_commit, | |
467 | .mode_set = nv50_sor_mode_set, | |
ec7fc4a1 BS |
468 | .get_crtc = nv50_sor_crtc_get, |
469 | .detect = NULL, | |
470 | .disable = nv50_sor_disconnect | |
6ee73861 BS |
471 | }; |
472 | ||
473 | static void | |
474 | nv50_sor_destroy(struct drm_encoder *encoder) | |
475 | { | |
476 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
77145f1c | 477 | struct nouveau_drm *drm = nouveau_drm(encoder->dev); |
6ee73861 | 478 | |
77145f1c | 479 | NV_DEBUG(drm, "\n"); |
6ee73861 BS |
480 | |
481 | drm_encoder_cleanup(encoder); | |
482 | ||
483 | kfree(nv_encoder); | |
484 | } | |
485 | ||
486 | static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { | |
487 | .destroy = nv50_sor_destroy, | |
488 | }; | |
489 | ||
490 | int | |
cb75d97e | 491 | nv50_sor_create(struct drm_connector *connector, struct dcb_output *entry) |
6ee73861 BS |
492 | { |
493 | struct nouveau_encoder *nv_encoder = NULL; | |
8f1a6086 | 494 | struct drm_device *dev = connector->dev; |
77145f1c | 495 | struct nouveau_drm *drm = nouveau_drm(dev); |
6ee73861 | 496 | struct drm_encoder *encoder; |
6ee73861 BS |
497 | int type; |
498 | ||
77145f1c | 499 | NV_DEBUG(drm, "\n"); |
6ee73861 BS |
500 | |
501 | switch (entry->type) { | |
cb75d97e BS |
502 | case DCB_OUTPUT_TMDS: |
503 | case DCB_OUTPUT_DP: | |
6ee73861 BS |
504 | type = DRM_MODE_ENCODER_TMDS; |
505 | break; | |
cb75d97e | 506 | case DCB_OUTPUT_LVDS: |
6ee73861 | 507 | type = DRM_MODE_ENCODER_LVDS; |
6ee73861 BS |
508 | break; |
509 | default: | |
510 | return -EINVAL; | |
511 | } | |
512 | ||
513 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
514 | if (!nv_encoder) | |
515 | return -ENOMEM; | |
516 | encoder = to_drm_encoder(nv_encoder); | |
517 | ||
518 | nv_encoder->dcb = entry; | |
519 | nv_encoder->or = ffs(entry->or) - 1; | |
ec7fc4a1 | 520 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; |
6ee73861 BS |
521 | |
522 | drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); | |
523 | drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); | |
524 | ||
525 | encoder->possible_crtcs = entry->heads; | |
526 | encoder->possible_clones = 0; | |
527 | ||
8f1a6086 | 528 | drm_mode_connector_attach_encoder(connector, encoder); |
6ee73861 BS |
529 | return 0; |
530 | } |