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" | |
a0b25635 | 35 | #include "nouveau_gpio.h" |
6ee73861 BS |
36 | #include "nvreg.h" |
37 | ||
38 | int nv04_dac_output_offset(struct drm_encoder *encoder) | |
39 | { | |
40 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
41 | int offset = 0; | |
42 | ||
43 | if (dcb->or & (8 | OUTPUT_C)) | |
44 | offset += 0x68; | |
45 | if (dcb->or & (8 | OUTPUT_B)) | |
46 | offset += 0x2000; | |
47 | ||
48 | return offset; | |
49 | } | |
50 | ||
51 | /* | |
52 | * arbitrary limit to number of sense oscillations tolerated in one sample | |
53 | * period (observed to be at least 13 in "nvidia") | |
54 | */ | |
55 | #define MAX_HBLANK_OSC 20 | |
56 | ||
57 | /* | |
58 | * arbitrary limit to number of conflicting sample pairs to tolerate at a | |
59 | * voltage step (observed to be at least 5 in "nvidia") | |
60 | */ | |
61 | #define MAX_SAMPLE_PAIRS 10 | |
62 | ||
63 | static int sample_load_twice(struct drm_device *dev, bool sense[2]) | |
64 | { | |
65 | int i; | |
66 | ||
67 | for (i = 0; i < 2; i++) { | |
68 | bool sense_a, sense_b, sense_b_prime; | |
69 | int j = 0; | |
70 | ||
71 | /* | |
72 | * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), | |
73 | * then wait for transition 0x4->0x5->0x4: enter hblank, leave | |
74 | * hblank again | |
75 | * use a 10ms timeout (guards against crtc being inactive, in | |
76 | * which case blank state would never change) | |
77 | */ | |
12fb9525 BS |
78 | if (!nouveau_wait_eq(dev, 10000000, NV_PRMCIO_INP0__COLOR, |
79 | 0x00000001, 0x00000000)) | |
6ee73861 | 80 | return -EBUSY; |
12fb9525 BS |
81 | if (!nouveau_wait_eq(dev, 10000000, NV_PRMCIO_INP0__COLOR, |
82 | 0x00000001, 0x00000001)) | |
6ee73861 | 83 | return -EBUSY; |
12fb9525 BS |
84 | if (!nouveau_wait_eq(dev, 10000000, NV_PRMCIO_INP0__COLOR, |
85 | 0x00000001, 0x00000000)) | |
6ee73861 BS |
86 | return -EBUSY; |
87 | ||
88 | udelay(100); | |
89 | /* when level triggers, sense is _LO_ */ | |
90 | sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
91 | ||
92 | /* take another reading until it agrees with sense_a... */ | |
93 | do { | |
94 | udelay(100); | |
95 | sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
96 | if (sense_a != sense_b) { | |
97 | sense_b_prime = | |
98 | nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | |
99 | if (sense_b == sense_b_prime) { | |
100 | /* ... unless two consecutive subsequent | |
101 | * samples agree; sense_a is replaced */ | |
102 | sense_a = sense_b; | |
103 | /* force mis-match so we loop */ | |
104 | sense_b = !sense_a; | |
105 | } | |
106 | } | |
107 | } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); | |
108 | ||
109 | if (j == MAX_HBLANK_OSC) | |
110 | /* with so much oscillation, default to sense:LO */ | |
111 | sense[i] = false; | |
112 | else | |
113 | sense[i] = sense_a; | |
114 | } | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, | |
120 | struct drm_connector *connector) | |
121 | { | |
122 | struct drm_device *dev = encoder->dev; | |
e7e65cae | 123 | uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; |
6ee73861 BS |
124 | uint8_t saved_palette0[3], saved_palette_mask; |
125 | uint32_t saved_rtest_ctrl, saved_rgen_ctrl; | |
126 | int i; | |
127 | uint8_t blue; | |
128 | bool sense = true; | |
129 | ||
130 | /* | |
131 | * for this detection to work, there needs to be a mode set up on the | |
132 | * CRTC. this is presumed to be the case | |
133 | */ | |
134 | ||
135 | if (nv_two_heads(dev)) | |
136 | /* only implemented for head A for now */ | |
137 | NVSetOwner(dev, 0); | |
138 | ||
e7e65cae FJ |
139 | saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); |
140 | NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); | |
141 | ||
6ee73861 BS |
142 | saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); |
143 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); | |
144 | ||
145 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); | |
146 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, | |
147 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
148 | ||
149 | msleep(10); | |
150 | ||
151 | saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); | |
152 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, | |
153 | saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); | |
154 | saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); | |
155 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); | |
156 | ||
157 | nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); | |
158 | for (i = 0; i < 3; i++) | |
159 | saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA); | |
160 | saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK); | |
161 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0); | |
162 | ||
163 | saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); | |
164 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, | |
165 | (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | | |
166 | NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | | |
167 | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); | |
168 | ||
169 | blue = 8; /* start of test range */ | |
170 | ||
171 | do { | |
172 | bool sense_pair[2]; | |
173 | ||
174 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
175 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
176 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | |
177 | /* testing blue won't find monochrome monitors. I don't care */ | |
178 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue); | |
179 | ||
180 | i = 0; | |
181 | /* take sample pairs until both samples in the pair agree */ | |
182 | do { | |
183 | if (sample_load_twice(dev, sense_pair)) | |
184 | goto out; | |
185 | } while ((sense_pair[0] != sense_pair[1]) && | |
186 | ++i < MAX_SAMPLE_PAIRS); | |
187 | ||
188 | if (i == MAX_SAMPLE_PAIRS) | |
189 | /* too much oscillation defaults to LO */ | |
190 | sense = false; | |
191 | else | |
192 | sense = sense_pair[0]; | |
193 | ||
194 | /* | |
195 | * if sense goes LO before blue ramps to 0x18, monitor is not connected. | |
196 | * ergo, if blue gets to 0x18, monitor must be connected | |
197 | */ | |
198 | } while (++blue < 0x18 && sense); | |
199 | ||
200 | out: | |
201 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); | |
202 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); | |
203 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | |
204 | for (i = 0; i < 3; i++) | |
205 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); | |
206 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); | |
207 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); | |
208 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); | |
209 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); | |
e7e65cae | 210 | NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); |
6ee73861 BS |
211 | |
212 | if (blue == 0x18) { | |
ef2bb506 | 213 | NV_INFO(dev, "Load detected on head A\n"); |
6ee73861 BS |
214 | return connector_status_connected; |
215 | } | |
216 | ||
217 | return connector_status_disconnected; | |
218 | } | |
219 | ||
11d6eb2a | 220 | uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) |
6ee73861 BS |
221 | { |
222 | struct drm_device *dev = encoder->dev; | |
223 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
224 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
11d6eb2a | 225 | uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); |
6ee73861 BS |
226 | uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, |
227 | saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; | |
11d6eb2a | 228 | int head; |
6ee73861 BS |
229 | |
230 | #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) | |
231 | if (dcb->type == OUTPUT_TV) { | |
232 | testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); | |
233 | ||
04a39c57 BS |
234 | if (dev_priv->vbios.tvdactestval) |
235 | testval = dev_priv->vbios.tvdactestval; | |
6ee73861 BS |
236 | } else { |
237 | testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ | |
238 | ||
04a39c57 BS |
239 | if (dev_priv->vbios.dactestval) |
240 | testval = dev_priv->vbios.dactestval; | |
6ee73861 BS |
241 | } |
242 | ||
243 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
244 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, | |
245 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | |
246 | ||
247 | saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2); | |
248 | ||
249 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); | |
250 | if (regoffset == 0x68) { | |
251 | saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4); | |
252 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); | |
253 | } | |
254 | ||
a0b25635 BS |
255 | saved_gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1); |
256 | saved_gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0); | |
6ee73861 | 257 | |
a0b25635 BS |
258 | nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); |
259 | nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); | |
6ee73861 BS |
260 | |
261 | msleep(4); | |
262 | ||
263 | saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | |
264 | head = (saved_routput & 0x100) >> 8; | |
be8860ac FJ |
265 | |
266 | /* if there's a spare crtc, using it will minimise flicker */ | |
267 | if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) | |
6ee73861 | 268 | head ^= 1; |
be8860ac | 269 | |
6ee73861 BS |
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); |
7314dec9 FJ |
294 | /* do it again just in case it's a residual current */ |
295 | sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | |
6ee73861 BS |
296 | |
297 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | |
298 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | |
299 | temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | |
300 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); | |
301 | ||
302 | /* bios does something more complex for restoring, but I think this is good enough */ | |
303 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); | |
304 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); | |
305 | if (regoffset == 0x68) | |
306 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); | |
307 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); | |
308 | ||
a0b25635 BS |
309 | nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); |
310 | nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); | |
6ee73861 | 311 | |
11d6eb2a FJ |
312 | return sample; |
313 | } | |
314 | ||
315 | static enum drm_connector_status | |
316 | nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) | |
317 | { | |
318 | struct drm_device *dev = encoder->dev; | |
319 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
11d6eb2a | 320 | |
8ccfe9e0 FJ |
321 | if (nv04_dac_in_use(encoder)) |
322 | return connector_status_disconnected; | |
323 | ||
324 | if (nv17_dac_sample_load(encoder) & | |
325 | NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { | |
11d6eb2a FJ |
326 | NV_INFO(dev, "Load detected on output %c\n", |
327 | '@' + ffs(dcb->or)); | |
6ee73861 | 328 | return connector_status_connected; |
11d6eb2a FJ |
329 | } else { |
330 | return connector_status_disconnected; | |
6ee73861 | 331 | } |
6ee73861 BS |
332 | } |
333 | ||
6ee73861 | 334 | static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, |
e811f5ae | 335 | const struct drm_display_mode *mode, |
6ee73861 BS |
336 | struct drm_display_mode *adjusted_mode) |
337 | { | |
8ccfe9e0 FJ |
338 | if (nv04_dac_in_use(encoder)) |
339 | return false; | |
340 | ||
6ee73861 BS |
341 | return true; |
342 | } | |
343 | ||
344 | static void nv04_dac_prepare(struct drm_encoder *encoder) | |
345 | { | |
346 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
347 | struct drm_device *dev = encoder->dev; | |
6ee73861 | 348 | int head = nouveau_crtc(encoder->crtc)->index; |
6ee73861 BS |
349 | |
350 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | |
351 | ||
352 | nv04_dfp_disable(dev, head); | |
6ee73861 BS |
353 | } |
354 | ||
6ee73861 BS |
355 | static void nv04_dac_mode_set(struct drm_encoder *encoder, |
356 | struct drm_display_mode *mode, | |
357 | struct drm_display_mode *adjusted_mode) | |
358 | { | |
6ee73861 BS |
359 | struct drm_device *dev = encoder->dev; |
360 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
361 | int head = nouveau_crtc(encoder->crtc)->index; | |
362 | ||
6ee73861 BS |
363 | if (nv_gf4_disp_arch(dev)) { |
364 | struct drm_encoder *rebind; | |
365 | uint32_t dac_offset = nv04_dac_output_offset(encoder); | |
366 | uint32_t otherdac; | |
367 | ||
368 | /* bit 16-19 are bits that are set on some G70 cards, | |
369 | * but don't seem to have much effect */ | |
370 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
371 | head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
372 | /* force any other vga encoders to bind to the other crtc */ | |
373 | list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { | |
374 | if (rebind == encoder | |
375 | || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG) | |
376 | continue; | |
377 | ||
378 | dac_offset = nv04_dac_output_offset(rebind); | |
379 | otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); | |
380 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | |
381 | (otherdac & ~0x0100) | (head ^ 1) << 8); | |
382 | } | |
383 | } | |
384 | ||
385 | /* This could use refinement for flatpanels, but it should work this way */ | |
386 | if (dev_priv->chipset < 0x44) | |
387 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | |
388 | else | |
389 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | |
390 | } | |
391 | ||
392 | static void nv04_dac_commit(struct drm_encoder *encoder) | |
393 | { | |
394 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
395 | struct drm_device *dev = encoder->dev; | |
396 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | |
397 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | |
398 | ||
399 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | |
400 | ||
401 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | |
402 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | |
403 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | |
404 | } | |
405 | ||
406 | void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) | |
407 | { | |
408 | struct drm_device *dev = encoder->dev; | |
409 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
410 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
411 | ||
412 | if (nv_gf4_disp_arch(dev)) { | |
413 | uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1]; | |
414 | int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); | |
415 | uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); | |
416 | ||
417 | if (enable) { | |
418 | *dac_users |= 1 << dcb->index; | |
419 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
420 | ||
421 | } else { | |
422 | *dac_users &= ~(1 << dcb->index); | |
423 | if (!*dac_users) | |
424 | NVWriteRAMDAC(dev, 0, dacclk_off, | |
425 | dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); | |
426 | } | |
427 | } | |
428 | } | |
429 | ||
8ccfe9e0 FJ |
430 | /* Check if the DAC corresponding to 'encoder' is being used by |
431 | * someone else. */ | |
432 | bool nv04_dac_in_use(struct drm_encoder *encoder) | |
433 | { | |
434 | struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; | |
435 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | |
436 | ||
437 | return nv_gf4_disp_arch(encoder->dev) && | |
438 | (dev_priv->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); | |
439 | } | |
440 | ||
6ee73861 BS |
441 | static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) |
442 | { | |
443 | struct drm_device *dev = encoder->dev; | |
444 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
445 | ||
446 | if (nv_encoder->last_dpms == mode) | |
447 | return; | |
448 | nv_encoder->last_dpms = mode; | |
449 | ||
450 | NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n", | |
451 | mode, nv_encoder->dcb->index); | |
452 | ||
453 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | |
454 | } | |
455 | ||
456 | static void nv04_dac_save(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 | nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | |
463 | nv04_dac_output_offset(encoder)); | |
464 | } | |
465 | ||
466 | static void nv04_dac_restore(struct drm_encoder *encoder) | |
467 | { | |
468 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
469 | struct drm_device *dev = encoder->dev; | |
470 | ||
471 | if (nv_gf4_disp_arch(dev)) | |
472 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), | |
473 | nv_encoder->restore.output); | |
474 | ||
475 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | |
476 | } | |
477 | ||
478 | static void nv04_dac_destroy(struct drm_encoder *encoder) | |
479 | { | |
480 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
481 | ||
ef2bb506 | 482 | NV_DEBUG_KMS(encoder->dev, "\n"); |
6ee73861 BS |
483 | |
484 | drm_encoder_cleanup(encoder); | |
485 | kfree(nv_encoder); | |
486 | } | |
487 | ||
488 | static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { | |
489 | .dpms = nv04_dac_dpms, | |
490 | .save = nv04_dac_save, | |
491 | .restore = nv04_dac_restore, | |
492 | .mode_fixup = nv04_dac_mode_fixup, | |
493 | .prepare = nv04_dac_prepare, | |
494 | .commit = nv04_dac_commit, | |
495 | .mode_set = nv04_dac_mode_set, | |
496 | .detect = nv04_dac_detect | |
497 | }; | |
498 | ||
499 | static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { | |
500 | .dpms = nv04_dac_dpms, | |
501 | .save = nv04_dac_save, | |
502 | .restore = nv04_dac_restore, | |
503 | .mode_fixup = nv04_dac_mode_fixup, | |
504 | .prepare = nv04_dac_prepare, | |
505 | .commit = nv04_dac_commit, | |
506 | .mode_set = nv04_dac_mode_set, | |
507 | .detect = nv17_dac_detect | |
508 | }; | |
509 | ||
510 | static const struct drm_encoder_funcs nv04_dac_funcs = { | |
511 | .destroy = nv04_dac_destroy, | |
512 | }; | |
513 | ||
8f1a6086 BS |
514 | int |
515 | nv04_dac_create(struct drm_connector *connector, struct dcb_entry *entry) | |
6ee73861 BS |
516 | { |
517 | const struct drm_encoder_helper_funcs *helper; | |
6ee73861 | 518 | struct nouveau_encoder *nv_encoder = NULL; |
8f1a6086 BS |
519 | struct drm_device *dev = connector->dev; |
520 | struct drm_encoder *encoder; | |
6ee73861 BS |
521 | |
522 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | |
523 | if (!nv_encoder) | |
524 | return -ENOMEM; | |
525 | ||
526 | encoder = to_drm_encoder(nv_encoder); | |
527 | ||
528 | nv_encoder->dcb = entry; | |
529 | nv_encoder->or = ffs(entry->or) - 1; | |
530 | ||
531 | if (nv_gf4_disp_arch(dev)) | |
532 | helper = &nv17_dac_helper_funcs; | |
533 | else | |
534 | helper = &nv04_dac_helper_funcs; | |
535 | ||
536 | drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); | |
537 | drm_encoder_helper_add(encoder, helper); | |
538 | ||
539 | encoder->possible_crtcs = entry->heads; | |
540 | encoder->possible_clones = 0; | |
541 | ||
8f1a6086 | 542 | drm_mode_connector_attach_encoder(connector, encoder); |
6ee73861 BS |
543 | return 0; |
544 | } |