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" | |
32 | #include "nouveau_drv.h" | |
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 | ||
8663bc7c BS |
39 | static u32 |
40 | nv50_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 }; | |
273a50fb | 45 | if (dev_priv->chipset == 0xaf) |
8663bc7c BS |
46 | return nvaf[lane]; |
47 | return nv50[lane]; | |
48 | } | |
49 | ||
50 | static void | |
51 | nv50_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 | ||
57 | static void | |
58 | nv50_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 | ||
84 | static void | |
85 | nv50_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 | ||
124 | static void | |
125 | nv50_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 | ||
139 | void | |
140 | nv50_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 | } | |
6ee73861 | 226 | static void |
ec7fc4a1 | 227 | nv50_sor_disconnect(struct drm_encoder *encoder) |
6ee73861 | 228 | { |
ec7fc4a1 BS |
229 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
230 | struct drm_device *dev = encoder->dev; | |
59c0f578 | 231 | struct nouveau_channel *evo = nv50_display(dev)->master; |
6ee73861 BS |
232 | int ret; |
233 | ||
ec7fc4a1 BS |
234 | if (!nv_encoder->crtc) |
235 | return; | |
835aadbe | 236 | nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true); |
ec7fc4a1 | 237 | |
ef2bb506 | 238 | NV_DEBUG_KMS(dev, "Disconnecting SOR %d\n", nv_encoder->or); |
6ee73861 | 239 | |
835aadbe | 240 | ret = RING_SPACE(evo, 4); |
6ee73861 BS |
241 | if (ret) { |
242 | NV_ERROR(dev, "no space while disconnecting SOR\n"); | |
243 | return; | |
244 | } | |
6d597027 | 245 | BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); |
835aadbe | 246 | OUT_RING (evo, 0); |
6d597027 | 247 | BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); |
835aadbe | 248 | OUT_RING (evo, 0); |
ec7fc4a1 | 249 | |
25575b41 BS |
250 | nouveau_hdmi_mode_set(encoder, NULL); |
251 | ||
ec7fc4a1 BS |
252 | nv_encoder->crtc = NULL; |
253 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; | |
6ee73861 BS |
254 | } |
255 | ||
6ee73861 BS |
256 | static void |
257 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | |
258 | { | |
259 | struct drm_device *dev = encoder->dev; | |
260 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
16226536 | 261 | struct drm_encoder *enc; |
6ee73861 BS |
262 | uint32_t val; |
263 | int or = nv_encoder->or; | |
264 | ||
ec7fc4a1 | 265 | NV_DEBUG_KMS(dev, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode); |
6ee73861 | 266 | |
16226536 BS |
267 | nv_encoder->last_dpms = mode; |
268 | list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { | |
269 | struct nouveau_encoder *nvenc = nouveau_encoder(enc); | |
270 | ||
271 | if (nvenc == nv_encoder || | |
ec7fc4a1 BS |
272 | (nvenc->dcb->type != OUTPUT_TMDS && |
273 | nvenc->dcb->type != OUTPUT_LVDS && | |
274 | nvenc->dcb->type != OUTPUT_DP) || | |
16226536 BS |
275 | nvenc->dcb->or != nv_encoder->dcb->or) |
276 | continue; | |
277 | ||
278 | if (nvenc->last_dpms == DRM_MODE_DPMS_ON) | |
279 | return; | |
280 | } | |
281 | ||
6ee73861 | 282 | /* wait for it to be done */ |
4b5c152a | 283 | if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), |
6ee73861 BS |
284 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { |
285 | NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); | |
286 | NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, | |
287 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); | |
288 | } | |
289 | ||
290 | val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); | |
291 | ||
292 | if (mode == DRM_MODE_DPMS_ON) | |
293 | val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
294 | else | |
295 | val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | |
296 | ||
297 | nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | | |
298 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); | |
4b5c152a | 299 | if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or), |
6ee73861 BS |
300 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { |
301 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); | |
302 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, | |
303 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or))); | |
304 | } | |
305 | ||
1f403d9c | 306 | if (nv_encoder->dcb->type == OUTPUT_DP) { |
f14d9a4d BS |
307 | struct dp_train_func func = { |
308 | .link_set = nv50_sor_dp_link_set, | |
309 | .train_set = nv50_sor_dp_train_set, | |
310 | .train_adj = nv50_sor_dp_train_adj | |
311 | }; | |
1f403d9c | 312 | |
f14d9a4d | 313 | nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); |
1f403d9c | 314 | } |
6ee73861 BS |
315 | } |
316 | ||
317 | static void | |
318 | nv50_sor_save(struct drm_encoder *encoder) | |
319 | { | |
320 | NV_ERROR(encoder->dev, "!!\n"); | |
321 | } | |
322 | ||
323 | static void | |
324 | nv50_sor_restore(struct drm_encoder *encoder) | |
325 | { | |
326 | NV_ERROR(encoder->dev, "!!\n"); | |
327 | } | |
328 | ||
329 | static bool | |
e811f5ae LP |
330 | nv50_sor_mode_fixup(struct drm_encoder *encoder, |
331 | const struct drm_display_mode *mode, | |
6ee73861 BS |
332 | struct drm_display_mode *adjusted_mode) |
333 | { | |
334 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
335 | struct nouveau_connector *connector; | |
336 | ||
ef2bb506 | 337 | NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or); |
6ee73861 BS |
338 | |
339 | connector = nouveau_encoder_connector_get(nv_encoder); | |
340 | if (!connector) { | |
341 | NV_ERROR(encoder->dev, "Encoder has no connector\n"); | |
342 | return false; | |
343 | } | |
344 | ||
345 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | |
c3c50e8b VS |
346 | connector->native_mode) |
347 | drm_mode_copy(adjusted_mode, connector->native_mode); | |
6ee73861 BS |
348 | |
349 | return true; | |
350 | } | |
351 | ||
352 | static void | |
353 | nv50_sor_prepare(struct drm_encoder *encoder) | |
354 | { | |
9976f15c | 355 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
7ae494e8 | 356 | nv50_sor_disconnect(encoder); |
9976f15c BS |
357 | if (nv_encoder->dcb->type == OUTPUT_DP) { |
358 | /* avoid race between link training and supervisor intr */ | |
359 | nv50_display_sync(encoder->dev); | |
360 | } | |
6ee73861 BS |
361 | } |
362 | ||
363 | static void | |
364 | nv50_sor_commit(struct drm_encoder *encoder) | |
365 | { | |
366 | } | |
367 | ||
368 | static void | |
a03a8623 BS |
369 | nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, |
370 | struct drm_display_mode *mode) | |
6ee73861 | 371 | { |
59c0f578 | 372 | struct nouveau_channel *evo = nv50_display(encoder->dev)->master; |
6ee73861 BS |
373 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
374 | struct drm_device *dev = encoder->dev; | |
375 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | |
46959b77 | 376 | struct nouveau_connector *nv_connector; |
6ee73861 BS |
377 | uint32_t mode_ctl = 0; |
378 | int ret; | |
379 | ||
04412921 BS |
380 | NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n", |
381 | nv_encoder->or, nv_encoder->dcb->type, crtc->index); | |
52c7bcdb | 382 | nv_encoder->crtc = encoder->crtc; |
6ee73861 | 383 | |
6ee73861 BS |
384 | switch (nv_encoder->dcb->type) { |
385 | case OUTPUT_TMDS: | |
386 | if (nv_encoder->dcb->sorconf.link & 1) { | |
a03a8623 | 387 | if (mode->clock < 165000) |
6ee73861 BS |
388 | mode_ctl = 0x0100; |
389 | else | |
390 | mode_ctl = 0x0500; | |
391 | } else | |
392 | mode_ctl = 0x0200; | |
25575b41 | 393 | |
a03a8623 | 394 | nouveau_hdmi_mode_set(encoder, mode); |
6ee73861 BS |
395 | break; |
396 | case OUTPUT_DP: | |
46959b77 | 397 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
a002fece | 398 | if (nv_connector && nv_connector->base.display_info.bpc == 6) { |
a03a8623 | 399 | nv_encoder->dp.datarate = mode->clock * 18 / 8; |
46959b77 | 400 | mode_ctl |= 0x00020000; |
a002fece | 401 | } else { |
a03a8623 | 402 | nv_encoder->dp.datarate = mode->clock * 24 / 8; |
46959b77 | 403 | mode_ctl |= 0x00050000; |
a002fece | 404 | } |
46959b77 | 405 | |
6ee73861 BS |
406 | if (nv_encoder->dcb->sorconf.link & 1) |
407 | mode_ctl |= 0x00000800; | |
408 | else | |
409 | mode_ctl |= 0x00000900; | |
410 | break; | |
411 | default: | |
412 | break; | |
413 | } | |
414 | ||
415 | if (crtc->index == 1) | |
416 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; | |
417 | else | |
418 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; | |
419 | ||
a03a8623 | 420 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
6ee73861 BS |
421 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; |
422 | ||
a03a8623 | 423 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
6ee73861 BS |
424 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; |
425 | ||
a002fece BS |
426 | nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); |
427 | ||
6ee73861 BS |
428 | ret = RING_SPACE(evo, 2); |
429 | if (ret) { | |
430 | NV_ERROR(dev, "no space while connecting SOR\n"); | |
52c7bcdb | 431 | nv_encoder->crtc = NULL; |
6ee73861 BS |
432 | return; |
433 | } | |
6d597027 | 434 | BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); |
6ee73861 | 435 | OUT_RING(evo, mode_ctl); |
ec7fc4a1 BS |
436 | } |
437 | ||
438 | static struct drm_crtc * | |
439 | nv50_sor_crtc_get(struct drm_encoder *encoder) | |
440 | { | |
441 | return nouveau_encoder(encoder)->crtc; | |
6ee73861 BS |
442 | } |
443 | ||
444 | static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { | |
445 | .dpms = nv50_sor_dpms, | |
446 | .save = nv50_sor_save, | |
447 | .restore = nv50_sor_restore, | |
448 | .mode_fixup = nv50_sor_mode_fixup, | |
449 | .prepare = nv50_sor_prepare, | |
450 | .commit = nv50_sor_commit, | |
451 | .mode_set = nv50_sor_mode_set, | |
ec7fc4a1 BS |
452 | .get_crtc = nv50_sor_crtc_get, |
453 | .detect = NULL, | |
454 | .disable = nv50_sor_disconnect | |
6ee73861 BS |
455 | }; |
456 | ||
457 | static void | |
458 | nv50_sor_destroy(struct drm_encoder *encoder) | |
459 | { | |
460 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
461 | ||
462 | if (!encoder) | |
463 | return; | |
464 | ||
ef2bb506 | 465 | NV_DEBUG_KMS(encoder->dev, "\n"); |
6ee73861 BS |
466 | |
467 | drm_encoder_cleanup(encoder); | |
468 | ||
469 | kfree(nv_encoder); | |
470 | } | |
471 | ||
472 | static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { | |
473 | .destroy = nv50_sor_destroy, | |
474 | }; | |
475 | ||
476 | int | |
8f1a6086 | 477 | nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry) |
6ee73861 BS |
478 | { |
479 | struct nouveau_encoder *nv_encoder = NULL; | |
8f1a6086 | 480 | struct drm_device *dev = connector->dev; |
6ee73861 | 481 | struct drm_encoder *encoder; |
6ee73861 BS |
482 | int type; |
483 | ||
ef2bb506 | 484 | NV_DEBUG_KMS(dev, "\n"); |
6ee73861 BS |
485 | |
486 | switch (entry->type) { | |
487 | case OUTPUT_TMDS: | |
8f1a6086 | 488 | case OUTPUT_DP: |
6ee73861 BS |
489 | type = DRM_MODE_ENCODER_TMDS; |
490 | break; | |
491 | case OUTPUT_LVDS: | |
6ee73861 | 492 | type = DRM_MODE_ENCODER_LVDS; |
6ee73861 BS |
493 | break; |
494 | default: | |
495 | return -EINVAL; | |
496 | } | |
497 | ||
498 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
499 | if (!nv_encoder) | |
500 | return -ENOMEM; | |
501 | encoder = to_drm_encoder(nv_encoder); | |
502 | ||
503 | nv_encoder->dcb = entry; | |
504 | nv_encoder->or = ffs(entry->or) - 1; | |
ec7fc4a1 | 505 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; |
6ee73861 BS |
506 | |
507 | drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); | |
508 | drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); | |
509 | ||
510 | encoder->possible_crtcs = entry->heads; | |
511 | encoder->possible_clones = 0; | |
512 | ||
8f1a6086 | 513 | drm_mode_connector_attach_encoder(connector, encoder); |
6ee73861 BS |
514 | return 0; |
515 | } |