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