]>
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; | |
e7e65cae | 122 | uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; |
6ee73861 BS |
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 | ||
e7e65cae FJ |
138 | saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); |
139 | NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); | |
140 | ||
6ee73861 BS |
141 | saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); |
142 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); | |
143 | ||
144 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); | |
145 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, | |
146 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
147 | ||
148 | msleep(10); | |
149 | ||
150 | saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); | |
151 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, | |
152 | saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); | |
153 | saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); | |
154 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); | |
155 | ||
156 | nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); | |
157 | for (i = 0; i < 3; i++) | |
158 | saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA); | |
159 | saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK); | |
160 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0); | |
161 | ||
162 | saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); | |
163 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, | |
164 | (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | | |
165 | NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | | |
166 | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); | |
167 | ||
168 | blue = 8; /* start of test range */ | |
169 | ||
170 | do { | |
171 | bool sense_pair[2]; | |
172 | ||
173 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
174 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
175 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
176 | /* testing blue won't find monochrome monitors. I don't care */ | |
177 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue); | |
178 | ||
179 | i = 0; | |
180 | /* take sample pairs until both samples in the pair agree */ | |
181 | do { | |
182 | if (sample_load_twice(dev, sense_pair)) | |
183 | goto out; | |
184 | } while ((sense_pair[0] != sense_pair[1]) && | |
185 | ++i < MAX_SAMPLE_PAIRS); | |
186 | ||
187 | if (i == MAX_SAMPLE_PAIRS) | |
188 | /* too much oscillation defaults to LO */ | |
189 | sense = false; | |
190 | else | |
191 | sense = sense_pair[0]; | |
192 | ||
193 | /* | |
194 | * if sense goes LO before blue ramps to 0x18, monitor is not connected. | |
195 | * ergo, if blue gets to 0x18, monitor must be connected | |
196 | */ | |
197 | } while (++blue < 0x18 && sense); | |
198 | ||
199 | out: | |
200 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); | |
201 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); | |
202 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
203 | for (i = 0; i < 3; i++) | |
204 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); | |
205 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); | |
206 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); | |
207 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); | |
208 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); | |
e7e65cae | 209 | NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); |
6ee73861 BS |
210 | |
211 | if (blue == 0x18) { | |
ef2bb506 | 212 | NV_INFO(dev, "Load detected on head A\n"); |
6ee73861 BS |
213 | return connector_status_connected; |
214 | } | |
215 | ||
216 | return connector_status_disconnected; | |
217 | } | |
218 | ||
11d6eb2a | 219 | uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) |
6ee73861 BS |
220 | { |
221 | struct drm_device *dev = encoder->dev; | |
222 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
223 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
11d6eb2a | 224 | uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); |
6ee73861 BS |
225 | uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, |
226 | saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; | |
11d6eb2a | 227 | int head; |
6ee73861 BS |
228 | |
229 | #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) | |
230 | if (dcb->type == OUTPUT_TV) { | |
231 | testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); | |
232 | ||
04a39c57 BS |
233 | if (dev_priv->vbios.tvdactestval) |
234 | testval = dev_priv->vbios.tvdactestval; | |
6ee73861 BS |
235 | } else { |
236 | testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ | |
237 | ||
04a39c57 BS |
238 | if (dev_priv->vbios.dactestval) |
239 | testval = dev_priv->vbios.dactestval; | |
6ee73861 BS |
240 | } |
241 | ||
242 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
243 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, | |
244 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
245 | ||
246 | saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2); | |
247 | ||
248 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); | |
249 | if (regoffset == 0x68) { | |
250 | saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4); | |
251 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); | |
252 | } | |
253 | ||
254 | saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); | |
255 | saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); | |
256 | ||
257 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); | |
258 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); | |
259 | ||
260 | msleep(4); | |
261 | ||
262 | saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
263 | head = (saved_routput & 0x100) >> 8; | |
264 | #if 0 | |
265 | /* if there's a spare crtc, using it will minimise flicker for the case | |
266 | * where the in-use crtc is in use by an off-chip tmds encoder */ | |
267 | if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled) | |
268 | head ^= 1; | |
269 | #endif | |
270 | /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ | |
271 | routput = (saved_routput & 0xfffffece) | head << 8; | |
272 | ||
273 | if (dev_priv->card_type >= NV_40) { | |
274 | if (dcb->type == OUTPUT_TV) | |
275 | routput |= 0x1a << 16; | |
276 | else | |
277 | routput &= ~(0x1a << 16); | |
278 | } | |
279 | ||
280 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); | |
281 | msleep(1); | |
282 | ||
283 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
284 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); | |
285 | ||
286 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, | |
287 | NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); | |
288 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
289 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
290 | temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
291 | msleep(5); | |
292 | ||
11d6eb2a | 293 | sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); |
6ee73861 BS |
294 | |
295 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
296 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
297 | temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
298 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); | |
299 | ||
300 | /* bios does something more complex for restoring, but I think this is good enough */ | |
301 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); | |
302 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); | |
303 | if (regoffset == 0x68) | |
304 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); | |
305 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); | |
306 | ||
307 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); | |
308 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); | |
309 | ||
11d6eb2a FJ |
310 | return sample; |
311 | } | |
312 | ||
313 | static enum drm_connector_status | |
314 | nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) | |
315 | { | |
316 | struct drm_device *dev = encoder->dev; | |
317 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
318 | uint32_t sample = nv17_dac_sample_load(encoder); | |
319 | ||
320 | if (sample & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { | |
321 | NV_INFO(dev, "Load detected on output %c\n", | |
322 | '@' + ffs(dcb->or)); | |
6ee73861 | 323 | return connector_status_connected; |
11d6eb2a FJ |
324 | } else { |
325 | return connector_status_disconnected; | |
6ee73861 | 326 | } |
6ee73861 BS |
327 | } |
328 | ||
6ee73861 BS |
329 | static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, |
330 | struct drm_display_mode *mode, | |
331 | struct drm_display_mode *adjusted_mode) | |
332 | { | |
333 | return true; | |
334 | } | |
335 | ||
336 | static void nv04_dac_prepare(struct drm_encoder *encoder) | |
337 | { | |
338 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
339 | struct drm_device *dev = encoder->dev; | |
340 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
341 | int head = nouveau_crtc(encoder->crtc)->index; | |
342 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | |
343 | ||
344 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | |
345 | ||
346 | nv04_dfp_disable(dev, head); | |
347 | ||
348 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | |
349 | * at LCD__INDEX which we don't alter | |
350 | */ | |
351 | if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44)) | |
352 | crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0; | |
353 | } | |
354 | ||
355 | ||
356 | static void nv04_dac_mode_set(struct drm_encoder *encoder, | |
357 | struct drm_display_mode *mode, | |
358 | struct drm_display_mode *adjusted_mode) | |
359 | { | |
6ee73861 BS |
360 | struct drm_device *dev = encoder->dev; |
361 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
362 | int head = nouveau_crtc(encoder->crtc)->index; | |
363 | ||
6ee73861 BS |
364 | if (nv_gf4_disp_arch(dev)) { |
365 | struct drm_encoder *rebind; | |
366 | uint32_t dac_offset = nv04_dac_output_offset(encoder); | |
367 | uint32_t otherdac; | |
368 | ||
369 | /* bit 16-19 are bits that are set on some G70 cards, | |
370 | * but don't seem to have much effect */ | |
371 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
372 | head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
373 | /* force any other vga encoders to bind to the other crtc */ | |
374 | list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { | |
375 | if (rebind == encoder | |
376 | || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG) | |
377 | continue; | |
378 | ||
379 | dac_offset = nv04_dac_output_offset(rebind); | |
380 | otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); | |
381 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
382 | (otherdac & ~0x0100) | (head ^ 1) << 8); | |
383 | } | |
384 | } | |
385 | ||
386 | /* This could use refinement for flatpanels, but it should work this way */ | |
387 | if (dev_priv->chipset < 0x44) | |
388 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | |
389 | else | |
390 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | |
391 | } | |
392 | ||
393 | static void nv04_dac_commit(struct drm_encoder *encoder) | |
394 | { | |
395 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
396 | struct drm_device *dev = encoder->dev; | |
397 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | |
398 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
399 | ||
400 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | |
401 | ||
402 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | |
403 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | |
404 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | |
405 | } | |
406 | ||
407 | void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) | |
408 | { | |
409 | struct drm_device *dev = encoder->dev; | |
410 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
411 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
412 | ||
413 | if (nv_gf4_disp_arch(dev)) { | |
414 | uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1]; | |
415 | int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); | |
416 | uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); | |
417 | ||
418 | if (enable) { | |
419 | *dac_users |= 1 << dcb->index; | |
420 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
421 | ||
422 | } else { | |
423 | *dac_users &= ~(1 << dcb->index); | |
424 | if (!*dac_users) | |
425 | NVWriteRAMDAC(dev, 0, dacclk_off, | |
426 | dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
427 | } | |
428 | } | |
429 | } | |
430 | ||
431 | static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) | |
432 | { | |
433 | struct drm_device *dev = encoder->dev; | |
434 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
435 | ||
436 | if (nv_encoder->last_dpms == mode) | |
437 | return; | |
438 | nv_encoder->last_dpms = mode; | |
439 | ||
440 | NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n", | |
441 | mode, nv_encoder->dcb->index); | |
442 | ||
443 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | |
444 | } | |
445 | ||
446 | static void nv04_dac_save(struct drm_encoder *encoder) | |
447 | { | |
448 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
449 | struct drm_device *dev = encoder->dev; | |
450 | ||
451 | if (nv_gf4_disp_arch(dev)) | |
452 | nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | |
453 | nv04_dac_output_offset(encoder)); | |
454 | } | |
455 | ||
456 | static void nv04_dac_restore(struct drm_encoder *encoder) | |
457 | { | |
458 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
459 | struct drm_device *dev = encoder->dev; | |
460 | ||
461 | if (nv_gf4_disp_arch(dev)) | |
462 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), | |
463 | nv_encoder->restore.output); | |
464 | ||
465 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | |
466 | } | |
467 | ||
468 | static void nv04_dac_destroy(struct drm_encoder *encoder) | |
469 | { | |
470 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
471 | ||
ef2bb506 | 472 | NV_DEBUG_KMS(encoder->dev, "\n"); |
6ee73861 BS |
473 | |
474 | drm_encoder_cleanup(encoder); | |
475 | kfree(nv_encoder); | |
476 | } | |
477 | ||
478 | static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { | |
479 | .dpms = nv04_dac_dpms, | |
480 | .save = nv04_dac_save, | |
481 | .restore = nv04_dac_restore, | |
482 | .mode_fixup = nv04_dac_mode_fixup, | |
483 | .prepare = nv04_dac_prepare, | |
484 | .commit = nv04_dac_commit, | |
485 | .mode_set = nv04_dac_mode_set, | |
486 | .detect = nv04_dac_detect | |
487 | }; | |
488 | ||
489 | static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { | |
490 | .dpms = nv04_dac_dpms, | |
491 | .save = nv04_dac_save, | |
492 | .restore = nv04_dac_restore, | |
493 | .mode_fixup = nv04_dac_mode_fixup, | |
494 | .prepare = nv04_dac_prepare, | |
495 | .commit = nv04_dac_commit, | |
496 | .mode_set = nv04_dac_mode_set, | |
497 | .detect = nv17_dac_detect | |
498 | }; | |
499 | ||
500 | static const struct drm_encoder_funcs nv04_dac_funcs = { | |
501 | .destroy = nv04_dac_destroy, | |
502 | }; | |
503 | ||
504 | int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) | |
505 | { | |
506 | const struct drm_encoder_helper_funcs *helper; | |
507 | struct drm_encoder *encoder; | |
508 | struct nouveau_encoder *nv_encoder = NULL; | |
509 | ||
510 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
511 | if (!nv_encoder) | |
512 | return -ENOMEM; | |
513 | ||
514 | encoder = to_drm_encoder(nv_encoder); | |
515 | ||
516 | nv_encoder->dcb = entry; | |
517 | nv_encoder->or = ffs(entry->or) - 1; | |
518 | ||
519 | if (nv_gf4_disp_arch(dev)) | |
520 | helper = &nv17_dac_helper_funcs; | |
521 | else | |
522 | helper = &nv04_dac_helper_funcs; | |
523 | ||
524 | drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); | |
525 | drm_encoder_helper_add(encoder, helper); | |
526 | ||
527 | encoder->possible_crtcs = entry->heads; | |
528 | encoder->possible_clones = 0; | |
529 | ||
530 | return 0; | |
531 | } |