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) { | |
ef2bb506 | 208 | NV_INFO(dev, "Load detected on head A\n"); |
6ee73861 BS |
209 | return connector_status_connected; |
210 | } | |
211 | ||
212 | return connector_status_disconnected; | |
213 | } | |
214 | ||
02076da9 | 215 | uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) |
6ee73861 BS |
216 | { |
217 | struct drm_device *dev = encoder->dev; | |
218 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
219 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
02076da9 | 220 | uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); |
6ee73861 BS |
221 | uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, |
222 | saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; | |
02076da9 | 223 | int head; |
6ee73861 BS |
224 | |
225 | #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) | |
226 | if (dcb->type == OUTPUT_TV) { | |
227 | testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); | |
228 | ||
229 | if (dev_priv->vbios->tvdactestval) | |
230 | testval = dev_priv->vbios->tvdactestval; | |
231 | } else { | |
232 | testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ | |
233 | ||
234 | if (dev_priv->vbios->dactestval) | |
235 | testval = dev_priv->vbios->dactestval; | |
236 | } | |
237 | ||
238 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
239 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, | |
240 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
241 | ||
242 | saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2); | |
243 | ||
244 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); | |
245 | if (regoffset == 0x68) { | |
246 | saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4); | |
247 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); | |
248 | } | |
249 | ||
250 | saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); | |
251 | saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); | |
252 | ||
253 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); | |
254 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); | |
255 | ||
256 | msleep(4); | |
257 | ||
258 | saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
259 | head = (saved_routput & 0x100) >> 8; | |
260 | #if 0 | |
261 | /* if there's a spare crtc, using it will minimise flicker for the case | |
262 | * where the in-use crtc is in use by an off-chip tmds encoder */ | |
263 | if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled) | |
264 | head ^= 1; | |
265 | #endif | |
266 | /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ | |
267 | routput = (saved_routput & 0xfffffece) | head << 8; | |
268 | ||
269 | if (dev_priv->card_type >= NV_40) { | |
270 | if (dcb->type == OUTPUT_TV) | |
271 | routput |= 0x1a << 16; | |
272 | else | |
273 | routput &= ~(0x1a << 16); | |
274 | } | |
275 | ||
276 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); | |
277 | msleep(1); | |
278 | ||
279 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
280 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); | |
281 | ||
282 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, | |
283 | NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); | |
284 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
285 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
286 | temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
287 | msleep(5); | |
288 | ||
02076da9 | 289 | sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); |
6ee73861 BS |
290 | |
291 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
292 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
293 | temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
294 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); | |
295 | ||
296 | /* bios does something more complex for restoring, but I think this is good enough */ | |
297 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); | |
298 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); | |
299 | if (regoffset == 0x68) | |
300 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); | |
301 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); | |
302 | ||
303 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); | |
304 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); | |
305 | ||
02076da9 FJ |
306 | return sample; |
307 | } | |
308 | ||
309 | static enum drm_connector_status | |
310 | nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) | |
311 | { | |
312 | struct drm_device *dev = encoder->dev; | |
313 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
314 | uint32_t sample = nv17_dac_sample_load(encoder); | |
315 | ||
316 | if (sample & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { | |
317 | NV_INFO(dev, "Load detected on output %c\n", | |
318 | '@' + ffs(dcb->or)); | |
6ee73861 | 319 | return connector_status_connected; |
02076da9 FJ |
320 | } else { |
321 | return connector_status_disconnected; | |
6ee73861 | 322 | } |
6ee73861 BS |
323 | } |
324 | ||
6ee73861 BS |
325 | static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, |
326 | struct drm_display_mode *mode, | |
327 | struct drm_display_mode *adjusted_mode) | |
328 | { | |
329 | return true; | |
330 | } | |
331 | ||
332 | static void nv04_dac_prepare(struct drm_encoder *encoder) | |
333 | { | |
334 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
335 | struct drm_device *dev = encoder->dev; | |
336 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
337 | int head = nouveau_crtc(encoder->crtc)->index; | |
338 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | |
339 | ||
340 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | |
341 | ||
342 | nv04_dfp_disable(dev, head); | |
343 | ||
344 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | |
345 | * at LCD__INDEX which we don't alter | |
346 | */ | |
347 | if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44)) | |
348 | crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0; | |
349 | } | |
350 | ||
351 | ||
352 | static void nv04_dac_mode_set(struct drm_encoder *encoder, | |
353 | struct drm_display_mode *mode, | |
354 | struct drm_display_mode *adjusted_mode) | |
355 | { | |
6ee73861 BS |
356 | struct drm_device *dev = encoder->dev; |
357 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
358 | int head = nouveau_crtc(encoder->crtc)->index; | |
359 | ||
6ee73861 BS |
360 | if (nv_gf4_disp_arch(dev)) { |
361 | struct drm_encoder *rebind; | |
362 | uint32_t dac_offset = nv04_dac_output_offset(encoder); | |
363 | uint32_t otherdac; | |
364 | ||
365 | /* bit 16-19 are bits that are set on some G70 cards, | |
366 | * but don't seem to have much effect */ | |
367 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
368 | head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
369 | /* force any other vga encoders to bind to the other crtc */ | |
370 | list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { | |
371 | if (rebind == encoder | |
372 | || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG) | |
373 | continue; | |
374 | ||
375 | dac_offset = nv04_dac_output_offset(rebind); | |
376 | otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); | |
377 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
378 | (otherdac & ~0x0100) | (head ^ 1) << 8); | |
379 | } | |
380 | } | |
381 | ||
382 | /* This could use refinement for flatpanels, but it should work this way */ | |
383 | if (dev_priv->chipset < 0x44) | |
384 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | |
385 | else | |
386 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | |
387 | } | |
388 | ||
389 | static void nv04_dac_commit(struct drm_encoder *encoder) | |
390 | { | |
391 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
392 | struct drm_device *dev = encoder->dev; | |
393 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | |
394 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
395 | ||
396 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | |
397 | ||
398 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | |
399 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | |
400 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | |
401 | } | |
402 | ||
403 | void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) | |
404 | { | |
405 | struct drm_device *dev = encoder->dev; | |
406 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
407 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
408 | ||
409 | if (nv_gf4_disp_arch(dev)) { | |
410 | uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1]; | |
411 | int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); | |
412 | uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); | |
413 | ||
414 | if (enable) { | |
415 | *dac_users |= 1 << dcb->index; | |
416 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
417 | ||
418 | } else { | |
419 | *dac_users &= ~(1 << dcb->index); | |
420 | if (!*dac_users) | |
421 | NVWriteRAMDAC(dev, 0, dacclk_off, | |
422 | dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
423 | } | |
424 | } | |
425 | } | |
426 | ||
427 | static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) | |
428 | { | |
429 | struct drm_device *dev = encoder->dev; | |
430 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
431 | ||
432 | if (nv_encoder->last_dpms == mode) | |
433 | return; | |
434 | nv_encoder->last_dpms = mode; | |
435 | ||
436 | NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n", | |
437 | mode, nv_encoder->dcb->index); | |
438 | ||
439 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | |
440 | } | |
441 | ||
442 | static void nv04_dac_save(struct drm_encoder *encoder) | |
443 | { | |
444 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
445 | struct drm_device *dev = encoder->dev; | |
446 | ||
447 | if (nv_gf4_disp_arch(dev)) | |
448 | nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | |
449 | nv04_dac_output_offset(encoder)); | |
450 | } | |
451 | ||
452 | static void nv04_dac_restore(struct drm_encoder *encoder) | |
453 | { | |
454 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
455 | struct drm_device *dev = encoder->dev; | |
456 | ||
457 | if (nv_gf4_disp_arch(dev)) | |
458 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), | |
459 | nv_encoder->restore.output); | |
460 | ||
461 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | |
462 | } | |
463 | ||
464 | static void nv04_dac_destroy(struct drm_encoder *encoder) | |
465 | { | |
466 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
467 | ||
ef2bb506 | 468 | NV_DEBUG_KMS(encoder->dev, "\n"); |
6ee73861 BS |
469 | |
470 | drm_encoder_cleanup(encoder); | |
471 | kfree(nv_encoder); | |
472 | } | |
473 | ||
474 | static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { | |
475 | .dpms = nv04_dac_dpms, | |
476 | .save = nv04_dac_save, | |
477 | .restore = nv04_dac_restore, | |
478 | .mode_fixup = nv04_dac_mode_fixup, | |
479 | .prepare = nv04_dac_prepare, | |
480 | .commit = nv04_dac_commit, | |
481 | .mode_set = nv04_dac_mode_set, | |
482 | .detect = nv04_dac_detect | |
483 | }; | |
484 | ||
485 | static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { | |
486 | .dpms = nv04_dac_dpms, | |
487 | .save = nv04_dac_save, | |
488 | .restore = nv04_dac_restore, | |
489 | .mode_fixup = nv04_dac_mode_fixup, | |
490 | .prepare = nv04_dac_prepare, | |
491 | .commit = nv04_dac_commit, | |
492 | .mode_set = nv04_dac_mode_set, | |
493 | .detect = nv17_dac_detect | |
494 | }; | |
495 | ||
496 | static const struct drm_encoder_funcs nv04_dac_funcs = { | |
497 | .destroy = nv04_dac_destroy, | |
498 | }; | |
499 | ||
500 | int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) | |
501 | { | |
502 | const struct drm_encoder_helper_funcs *helper; | |
503 | struct drm_encoder *encoder; | |
504 | struct nouveau_encoder *nv_encoder = NULL; | |
505 | ||
506 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
507 | if (!nv_encoder) | |
508 | return -ENOMEM; | |
509 | ||
510 | encoder = to_drm_encoder(nv_encoder); | |
511 | ||
512 | nv_encoder->dcb = entry; | |
513 | nv_encoder->or = ffs(entry->or) - 1; | |
514 | ||
515 | if (nv_gf4_disp_arch(dev)) | |
516 | helper = &nv17_dac_helper_funcs; | |
517 | else | |
518 | helper = &nv04_dac_helper_funcs; | |
519 | ||
520 | drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); | |
521 | drm_encoder_helper_add(encoder, helper); | |
522 | ||
523 | encoder->possible_crtcs = entry->heads; | |
524 | encoder->possible_clones = 0; | |
525 | ||
526 | return 0; | |
527 | } |