diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_dac.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_dac.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c new file mode 100644 index 00000000000..a5fa51714e8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_dac.c | |||
@@ -0,0 +1,528 @@ | |||
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 | } | ||