Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright 2003 NVIDIA, Corporation | |
3 | * Copyright 2006 Dave Airlie | |
4 | * Copyright 2007 Maarten Maathuis | |
5 | * Copyright 2007-2009 Stuart Bennett | |
6 | * | |
7 | * Permission is hereby granted, free of charge, to any person obtaining a | |
8 | * copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation | |
10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
11 | * and/or sell copies of the Software, and to permit persons to whom the | |
12 | * Software is furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice (including the next | |
15 | * paragraph) shall be included in all copies or substantial portions of the | |
16 | * Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
24 | * DEALINGS IN THE SOFTWARE. | |
25 | */ | |
26 | ||
27 | #include "drmP.h" | |
28 | #include "drm_crtc_helper.h" | |
29 | ||
30 | #include "nouveau_drv.h" | |
31 | #include "nouveau_encoder.h" | |
32 | #include "nouveau_connector.h" | |
33 | #include "nouveau_crtc.h" | |
34 | #include "nouveau_hw.h" | |
35 | #include "nvreg.h" | |
36 | ||
37 | int nv04_dac_output_offset(struct drm_encoder *encoder) | |
38 | { | |
39 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
40 | int offset = 0; | |
41 | ||
42 | if (dcb->or & (8 | OUTPUT_C)) | |
43 | offset += 0x68; | |
44 | if (dcb->or & (8 | OUTPUT_B)) | |
45 | offset += 0x2000; | |
46 | ||
47 | return offset; | |
48 | } | |
49 | ||
50 | /* | |
51 | * arbitrary limit to number of sense oscillations tolerated in one sample | |
52 | * period (observed to be at least 13 in "nvidia") | |
53 | */ | |
54 | #define MAX_HBLANK_OSC 20 | |
55 | ||
56 | /* | |
57 | * arbitrary limit to number of conflicting sample pairs to tolerate at a | |
58 | * voltage step (observed to be at least 5 in "nvidia") | |
59 | */ | |
60 | #define MAX_SAMPLE_PAIRS 10 | |
61 | ||
62 | static int sample_load_twice(struct drm_device *dev, bool sense[2]) | |
63 | { | |
64 | int i; | |
65 | ||
66 | for (i = 0; i < 2; i++) { | |
67 | bool sense_a, sense_b, sense_b_prime; | |
68 | int j = 0; | |
69 | ||
70 | /* | |
71 | * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), | |
72 | * then wait for transition 0x4->0x5->0x4: enter hblank, leave | |
73 | * hblank again | |
74 | * use a 10ms timeout (guards against crtc being inactive, in | |
75 | * which case blank state would never change) | |
76 | */ | |
77 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | |
78 | 0x00000001, 0x00000000)) | |
79 | return -EBUSY; | |
80 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | |
81 | 0x00000001, 0x00000001)) | |
82 | return -EBUSY; | |
83 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | |
84 | 0x00000001, 0x00000000)) | |
85 | return -EBUSY; | |
86 | ||
87 | udelay(100); | |
88 | /* when level triggers, sense is _LO_ */ | |
89 | sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
90 | ||
91 | /* take another reading until it agrees with sense_a... */ | |
92 | do { | |
93 | udelay(100); | |
94 | sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
95 | if (sense_a != sense_b) { | |
96 | sense_b_prime = | |
97 | nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
98 | if (sense_b == sense_b_prime) { | |
99 | /* ... unless two consecutive subsequent | |
100 | * samples agree; sense_a is replaced */ | |
101 | sense_a = sense_b; | |
102 | /* force mis-match so we loop */ | |
103 | sense_b = !sense_a; | |
104 | } | |
105 | } | |
106 | } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); | |
107 | ||
108 | if (j == MAX_HBLANK_OSC) | |
109 | /* with so much oscillation, default to sense:LO */ | |
110 | sense[i] = false; | |
111 | else | |
112 | sense[i] = sense_a; | |
113 | } | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
118 | static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, | |
119 | struct drm_connector *connector) | |
120 | { | |
121 | struct drm_device *dev = encoder->dev; | |
122 | uint8_t saved_seq1, saved_pi, saved_rpc1; | |
123 | uint8_t saved_palette0[3], saved_palette_mask; | |
124 | uint32_t saved_rtest_ctrl, saved_rgen_ctrl; | |
125 | int i; | |
126 | uint8_t blue; | |
127 | bool sense = true; | |
128 | ||
129 | /* | |
130 | * for this detection to work, there needs to be a mode set up on the | |
131 | * CRTC. this is presumed to be the case | |
132 | */ | |
133 | ||
134 | if (nv_two_heads(dev)) | |
135 | /* only implemented for head A for now */ | |
136 | NVSetOwner(dev, 0); | |
137 | ||
138 | saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); | |
139 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); | |
140 | ||
141 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); | |
142 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, | |
143 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
144 | ||
145 | msleep(10); | |
146 | ||
147 | saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); | |
148 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, | |
149 | saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); | |
150 | saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); | |
151 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); | |
152 | ||
153 | nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); | |
154 | for (i = 0; i < 3; i++) | |
155 | saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA); | |
156 | saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK); | |
157 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0); | |
158 | ||
159 | saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); | |
160 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, | |
161 | (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | | |
162 | NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | | |
163 | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); | |
164 | ||
165 | blue = 8; /* start of test range */ | |
166 | ||
167 | do { | |
168 | bool sense_pair[2]; | |
169 | ||
170 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
171 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
172 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
173 | /* testing blue won't find monochrome monitors. I don't care */ | |
174 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue); | |
175 | ||
176 | i = 0; | |
177 | /* take sample pairs until both samples in the pair agree */ | |
178 | do { | |
179 | if (sample_load_twice(dev, sense_pair)) | |
180 | goto out; | |
181 | } while ((sense_pair[0] != sense_pair[1]) && | |
182 | ++i < MAX_SAMPLE_PAIRS); | |
183 | ||
184 | if (i == MAX_SAMPLE_PAIRS) | |
185 | /* too much oscillation defaults to LO */ | |
186 | sense = false; | |
187 | else | |
188 | sense = sense_pair[0]; | |
189 | ||
190 | /* | |
191 | * if sense goes LO before blue ramps to 0x18, monitor is not connected. | |
192 | * ergo, if blue gets to 0x18, monitor must be connected | |
193 | */ | |
194 | } while (++blue < 0x18 && sense); | |
195 | ||
196 | out: | |
197 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); | |
198 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); | |
199 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
200 | for (i = 0; i < 3; i++) | |
201 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); | |
202 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); | |
203 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); | |
204 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); | |
205 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); | |
206 | ||
207 | if (blue == 0x18) { | |
208 | NV_TRACE(dev, "Load detected on head A\n"); | |
209 | return connector_status_connected; | |
210 | } | |
211 | ||
212 | return connector_status_disconnected; | |
213 | } | |
214 | ||
215 | enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, | |
216 | struct drm_connector *connector) | |
217 | { | |
218 | struct drm_device *dev = encoder->dev; | |
219 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
220 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
221 | uint32_t testval, regoffset = nv04_dac_output_offset(encoder); | |
222 | uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, | |
223 | saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; | |
224 | int head, present = 0; | |
225 | ||
226 | #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) | |
227 | if (dcb->type == OUTPUT_TV) { | |
228 | testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); | |
229 | ||
230 | if (dev_priv->vbios->tvdactestval) | |
231 | testval = dev_priv->vbios->tvdactestval; | |
232 | } else { | |
233 | testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ | |
234 | ||
235 | if (dev_priv->vbios->dactestval) | |
236 | testval = dev_priv->vbios->dactestval; | |
237 | } | |
238 | ||
239 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
240 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, | |
241 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
242 | ||
243 | saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2); | |
244 | ||
245 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); | |
246 | if (regoffset == 0x68) { | |
247 | saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4); | |
248 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); | |
249 | } | |
250 | ||
251 | saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); | |
252 | saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); | |
253 | ||
254 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); | |
255 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); | |
256 | ||
257 | msleep(4); | |
258 | ||
259 | saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
260 | head = (saved_routput & 0x100) >> 8; | |
261 | #if 0 | |
262 | /* if there's a spare crtc, using it will minimise flicker for the case | |
263 | * where the in-use crtc is in use by an off-chip tmds encoder */ | |
264 | if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled) | |
265 | head ^= 1; | |
266 | #endif | |
267 | /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ | |
268 | routput = (saved_routput & 0xfffffece) | head << 8; | |
269 | ||
270 | if (dev_priv->card_type >= NV_40) { | |
271 | if (dcb->type == OUTPUT_TV) | |
272 | routput |= 0x1a << 16; | |
273 | else | |
274 | routput &= ~(0x1a << 16); | |
275 | } | |
276 | ||
277 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); | |
278 | msleep(1); | |
279 | ||
280 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
281 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); | |
282 | ||
283 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, | |
284 | NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); | |
285 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
286 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
287 | temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
288 | msleep(5); | |
289 | ||
290 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
291 | ||
292 | if (dcb->type == OUTPUT_TV) | |
293 | present = (nv17_tv_detect(encoder, connector, temp) | |
294 | == connector_status_connected); | |
295 | else | |
296 | present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI; | |
297 | ||
298 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
299 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
300 | temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
301 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); | |
302 | ||
303 | /* bios does something more complex for restoring, but I think this is good enough */ | |
304 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); | |
305 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); | |
306 | if (regoffset == 0x68) | |
307 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); | |
308 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); | |
309 | ||
310 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); | |
311 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); | |
312 | ||
313 | if (present) { | |
314 | NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or)); | |
315 | return connector_status_connected; | |
316 | } | |
317 | ||
318 | return connector_status_disconnected; | |
319 | } | |
320 | ||
321 | ||
322 | static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, | |
323 | struct drm_display_mode *mode, | |
324 | struct drm_display_mode *adjusted_mode) | |
325 | { | |
326 | return true; | |
327 | } | |
328 | ||
329 | static void nv04_dac_prepare(struct drm_encoder *encoder) | |
330 | { | |
331 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
332 | struct drm_device *dev = encoder->dev; | |
333 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
334 | int head = nouveau_crtc(encoder->crtc)->index; | |
335 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | |
336 | ||
337 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | |
338 | ||
339 | nv04_dfp_disable(dev, head); | |
340 | ||
341 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | |
342 | * at LCD__INDEX which we don't alter | |
343 | */ | |
344 | if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44)) | |
345 | crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0; | |
346 | } | |
347 | ||
348 | ||
349 | static void nv04_dac_mode_set(struct drm_encoder *encoder, | |
350 | struct drm_display_mode *mode, | |
351 | struct drm_display_mode *adjusted_mode) | |
352 | { | |
353 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
354 | struct drm_device *dev = encoder->dev; | |
355 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
356 | int head = nouveau_crtc(encoder->crtc)->index; | |
357 | ||
358 | NV_TRACE(dev, "%s called for encoder %d\n", __func__, | |
359 | nv_encoder->dcb->index); | |
360 | ||
361 | if (nv_gf4_disp_arch(dev)) { | |
362 | struct drm_encoder *rebind; | |
363 | uint32_t dac_offset = nv04_dac_output_offset(encoder); | |
364 | uint32_t otherdac; | |
365 | ||
366 | /* bit 16-19 are bits that are set on some G70 cards, | |
367 | * but don't seem to have much effect */ | |
368 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
369 | head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
370 | /* force any other vga encoders to bind to the other crtc */ | |
371 | list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { | |
372 | if (rebind == encoder | |
373 | || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG) | |
374 | continue; | |
375 | ||
376 | dac_offset = nv04_dac_output_offset(rebind); | |
377 | otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); | |
378 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
379 | (otherdac & ~0x0100) | (head ^ 1) << 8); | |
380 | } | |
381 | } | |
382 | ||
383 | /* This could use refinement for flatpanels, but it should work this way */ | |
384 | if (dev_priv->chipset < 0x44) | |
385 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | |
386 | else | |
387 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | |
388 | } | |
389 | ||
390 | static void nv04_dac_commit(struct drm_encoder *encoder) | |
391 | { | |
392 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
393 | struct drm_device *dev = encoder->dev; | |
394 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | |
395 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
396 | ||
397 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | |
398 | ||
399 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | |
400 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | |
401 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | |
402 | } | |
403 | ||
404 | void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) | |
405 | { | |
406 | struct drm_device *dev = encoder->dev; | |
407 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
408 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
409 | ||
410 | if (nv_gf4_disp_arch(dev)) { | |
411 | uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1]; | |
412 | int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); | |
413 | uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); | |
414 | ||
415 | if (enable) { | |
416 | *dac_users |= 1 << dcb->index; | |
417 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
418 | ||
419 | } else { | |
420 | *dac_users &= ~(1 << dcb->index); | |
421 | if (!*dac_users) | |
422 | NVWriteRAMDAC(dev, 0, dacclk_off, | |
423 | dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
424 | } | |
425 | } | |
426 | } | |
427 | ||
428 | static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) | |
429 | { | |
430 | struct drm_device *dev = encoder->dev; | |
431 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
432 | ||
433 | if (nv_encoder->last_dpms == mode) | |
434 | return; | |
435 | nv_encoder->last_dpms = mode; | |
436 | ||
437 | NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n", | |
438 | mode, nv_encoder->dcb->index); | |
439 | ||
440 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | |
441 | } | |
442 | ||
443 | static void nv04_dac_save(struct drm_encoder *encoder) | |
444 | { | |
445 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
446 | struct drm_device *dev = encoder->dev; | |
447 | ||
448 | if (nv_gf4_disp_arch(dev)) | |
449 | nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | |
450 | nv04_dac_output_offset(encoder)); | |
451 | } | |
452 | ||
453 | static void nv04_dac_restore(struct drm_encoder *encoder) | |
454 | { | |
455 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
456 | struct drm_device *dev = encoder->dev; | |
457 | ||
458 | if (nv_gf4_disp_arch(dev)) | |
459 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), | |
460 | nv_encoder->restore.output); | |
461 | ||
462 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | |
463 | } | |
464 | ||
465 | static void nv04_dac_destroy(struct drm_encoder *encoder) | |
466 | { | |
467 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
468 | ||
469 | NV_DEBUG(encoder->dev, "\n"); | |
470 | ||
471 | drm_encoder_cleanup(encoder); | |
472 | kfree(nv_encoder); | |
473 | } | |
474 | ||
475 | static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { | |
476 | .dpms = nv04_dac_dpms, | |
477 | .save = nv04_dac_save, | |
478 | .restore = nv04_dac_restore, | |
479 | .mode_fixup = nv04_dac_mode_fixup, | |
480 | .prepare = nv04_dac_prepare, | |
481 | .commit = nv04_dac_commit, | |
482 | .mode_set = nv04_dac_mode_set, | |
483 | .detect = nv04_dac_detect | |
484 | }; | |
485 | ||
486 | static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { | |
487 | .dpms = nv04_dac_dpms, | |
488 | .save = nv04_dac_save, | |
489 | .restore = nv04_dac_restore, | |
490 | .mode_fixup = nv04_dac_mode_fixup, | |
491 | .prepare = nv04_dac_prepare, | |
492 | .commit = nv04_dac_commit, | |
493 | .mode_set = nv04_dac_mode_set, | |
494 | .detect = nv17_dac_detect | |
495 | }; | |
496 | ||
497 | static const struct drm_encoder_funcs nv04_dac_funcs = { | |
498 | .destroy = nv04_dac_destroy, | |
499 | }; | |
500 | ||
501 | int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) | |
502 | { | |
503 | const struct drm_encoder_helper_funcs *helper; | |
504 | struct drm_encoder *encoder; | |
505 | struct nouveau_encoder *nv_encoder = NULL; | |
506 | ||
507 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
508 | if (!nv_encoder) | |
509 | return -ENOMEM; | |
510 | ||
511 | encoder = to_drm_encoder(nv_encoder); | |
512 | ||
513 | nv_encoder->dcb = entry; | |
514 | nv_encoder->or = ffs(entry->or) - 1; | |
515 | ||
516 | if (nv_gf4_disp_arch(dev)) | |
517 | helper = &nv17_dac_helper_funcs; | |
518 | else | |
519 | helper = &nv04_dac_helper_funcs; | |
520 | ||
521 | drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); | |
522 | drm_encoder_helper_add(encoder, helper); | |
523 | ||
524 | encoder->possible_crtcs = entry->heads; | |
525 | encoder->possible_clones = 0; | |
526 | ||
527 | return 0; | |
528 | } |