aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv04_dac.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2009-12-11 04:24:15 -0500
committerDave Airlie <airlied@redhat.com>2009-12-11 06:29:34 -0500
commit6ee738610f41b59733f63718f0bdbcba7d3a3f12 (patch)
treeeccb9f07671998c50a1bc606a54cd6f82ba43e0a /drivers/gpu/drm/nouveau/nv04_dac.c
parentd1ede145cea25c5b6d2ebb19b167af14e374bb45 (diff)
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Koƛcielnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_dac.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dac.c528
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 000000000000..a5fa51714e87
--- /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
37int 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
62static 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
118static 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
196out:
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
215enum 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
322static 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
329static 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
349static 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
390static 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
404void 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
428static 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
443static 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
453static 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
465static 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
475static 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
486static 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
497static const struct drm_encoder_funcs nv04_dac_funcs = {
498 .destroy = nv04_dac_destroy,
499};
500
501int 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}