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