1*677dec6eSriastradh /*	$NetBSD: nouveau_dispnv04_dac.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $	*/
29d5db0c2Sriastradh 
39d5db0c2Sriastradh /*
49d5db0c2Sriastradh  * Copyright 2003 NVIDIA, Corporation
59d5db0c2Sriastradh  * Copyright 2006 Dave Airlie
69d5db0c2Sriastradh  * Copyright 2007 Maarten Maathuis
79d5db0c2Sriastradh  * Copyright 2007-2009 Stuart Bennett
89d5db0c2Sriastradh  *
99d5db0c2Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
109d5db0c2Sriastradh  * copy of this software and associated documentation files (the "Software"),
119d5db0c2Sriastradh  * to deal in the Software without restriction, including without limitation
129d5db0c2Sriastradh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
139d5db0c2Sriastradh  * and/or sell copies of the Software, and to permit persons to whom the
149d5db0c2Sriastradh  * Software is furnished to do so, subject to the following conditions:
159d5db0c2Sriastradh  *
169d5db0c2Sriastradh  * The above copyright notice and this permission notice (including the next
179d5db0c2Sriastradh  * paragraph) shall be included in all copies or substantial portions of the
189d5db0c2Sriastradh  * Software.
199d5db0c2Sriastradh  *
209d5db0c2Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
219d5db0c2Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
229d5db0c2Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
239d5db0c2Sriastradh  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
249d5db0c2Sriastradh  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
259d5db0c2Sriastradh  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
269d5db0c2Sriastradh  * DEALINGS IN THE SOFTWARE.
279d5db0c2Sriastradh  */
289d5db0c2Sriastradh 
299d5db0c2Sriastradh #include <sys/cdefs.h>
30*677dec6eSriastradh __KERNEL_RCSID(0, "$NetBSD: nouveau_dispnv04_dac.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $");
319d5db0c2Sriastradh 
329d5db0c2Sriastradh #include <drm/drm_crtc_helper.h>
339d5db0c2Sriastradh 
34*677dec6eSriastradh #include "nouveau_drv.h"
359d5db0c2Sriastradh #include "nouveau_encoder.h"
369d5db0c2Sriastradh #include "nouveau_connector.h"
379d5db0c2Sriastradh #include "nouveau_crtc.h"
389d5db0c2Sriastradh #include "hw.h"
399d5db0c2Sriastradh #include "nvreg.h"
409d5db0c2Sriastradh 
419d5db0c2Sriastradh #include <subdev/bios/gpio.h>
429d5db0c2Sriastradh #include <subdev/gpio.h>
439d5db0c2Sriastradh #include <subdev/timer.h>
449d5db0c2Sriastradh 
nv04_dac_output_offset(struct drm_encoder * encoder)459d5db0c2Sriastradh int nv04_dac_output_offset(struct drm_encoder *encoder)
469d5db0c2Sriastradh {
479d5db0c2Sriastradh 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
489d5db0c2Sriastradh 	int offset = 0;
499d5db0c2Sriastradh 
509d5db0c2Sriastradh 	if (dcb->or & (8 | DCB_OUTPUT_C))
519d5db0c2Sriastradh 		offset += 0x68;
529d5db0c2Sriastradh 	if (dcb->or & (8 | DCB_OUTPUT_B))
539d5db0c2Sriastradh 		offset += 0x2000;
549d5db0c2Sriastradh 
559d5db0c2Sriastradh 	return offset;
569d5db0c2Sriastradh }
579d5db0c2Sriastradh 
589d5db0c2Sriastradh /*
599d5db0c2Sriastradh  * arbitrary limit to number of sense oscillations tolerated in one sample
609d5db0c2Sriastradh  * period (observed to be at least 13 in "nvidia")
619d5db0c2Sriastradh  */
629d5db0c2Sriastradh #define MAX_HBLANK_OSC 20
639d5db0c2Sriastradh 
649d5db0c2Sriastradh /*
659d5db0c2Sriastradh  * arbitrary limit to number of conflicting sample pairs to tolerate at a
669d5db0c2Sriastradh  * voltage step (observed to be at least 5 in "nvidia")
679d5db0c2Sriastradh  */
689d5db0c2Sriastradh #define MAX_SAMPLE_PAIRS 10
699d5db0c2Sriastradh 
sample_load_twice(struct drm_device * dev,bool sense[2])709d5db0c2Sriastradh static int sample_load_twice(struct drm_device *dev, bool sense[2])
719d5db0c2Sriastradh {
72d350ecf5Sriastradh 	struct nouveau_drm *drm = nouveau_drm(dev);
73*677dec6eSriastradh 	struct nvif_object *device = &drm->client.device.object;
749d5db0c2Sriastradh 	int i;
759d5db0c2Sriastradh 
769d5db0c2Sriastradh 	for (i = 0; i < 2; i++) {
779d5db0c2Sriastradh 		bool sense_a, sense_b, sense_b_prime;
789d5db0c2Sriastradh 		int j = 0;
799d5db0c2Sriastradh 
809d5db0c2Sriastradh 		/*
819d5db0c2Sriastradh 		 * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
829d5db0c2Sriastradh 		 * then wait for transition 0x4->0x5->0x4: enter hblank, leave
839d5db0c2Sriastradh 		 * hblank again
849d5db0c2Sriastradh 		 * use a 10ms timeout (guards against crtc being inactive, in
859d5db0c2Sriastradh 		 * which case blank state would never change)
869d5db0c2Sriastradh 		 */
87*677dec6eSriastradh 		if (nvif_msec(&drm->client.device, 10,
88d350ecf5Sriastradh 			if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1))
89d350ecf5Sriastradh 				break;
90d350ecf5Sriastradh 		) < 0)
919d5db0c2Sriastradh 			return -EBUSY;
92d350ecf5Sriastradh 
93*677dec6eSriastradh 		if (nvif_msec(&drm->client.device, 10,
94d350ecf5Sriastradh 			if ( (nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1))
95d350ecf5Sriastradh 				break;
96d350ecf5Sriastradh 		) < 0)
979d5db0c2Sriastradh 			return -EBUSY;
98d350ecf5Sriastradh 
99*677dec6eSriastradh 		if (nvif_msec(&drm->client.device, 10,
100d350ecf5Sriastradh 			if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1))
101d350ecf5Sriastradh 				break;
102d350ecf5Sriastradh 		) < 0)
1039d5db0c2Sriastradh 			return -EBUSY;
1049d5db0c2Sriastradh 
1059d5db0c2Sriastradh 		udelay(100);
1069d5db0c2Sriastradh 		/* when level triggers, sense is _LO_ */
107d350ecf5Sriastradh 		sense_a = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
1089d5db0c2Sriastradh 
1099d5db0c2Sriastradh 		/* take another reading until it agrees with sense_a... */
1109d5db0c2Sriastradh 		do {
1119d5db0c2Sriastradh 			udelay(100);
112d350ecf5Sriastradh 			sense_b = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
1139d5db0c2Sriastradh 			if (sense_a != sense_b) {
1149d5db0c2Sriastradh 				sense_b_prime =
115d350ecf5Sriastradh 					nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
1169d5db0c2Sriastradh 				if (sense_b == sense_b_prime) {
1179d5db0c2Sriastradh 					/* ... unless two consecutive subsequent
1189d5db0c2Sriastradh 					 * samples agree; sense_a is replaced */
1199d5db0c2Sriastradh 					sense_a = sense_b;
1209d5db0c2Sriastradh 					/* force mis-match so we loop */
1219d5db0c2Sriastradh 					sense_b = !sense_a;
1229d5db0c2Sriastradh 				}
1239d5db0c2Sriastradh 			}
1249d5db0c2Sriastradh 		} while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
1259d5db0c2Sriastradh 
1269d5db0c2Sriastradh 		if (j == MAX_HBLANK_OSC)
1279d5db0c2Sriastradh 			/* with so much oscillation, default to sense:LO */
1289d5db0c2Sriastradh 			sense[i] = false;
1299d5db0c2Sriastradh 		else
1309d5db0c2Sriastradh 			sense[i] = sense_a;
1319d5db0c2Sriastradh 	}
1329d5db0c2Sriastradh 
1339d5db0c2Sriastradh 	return 0;
1349d5db0c2Sriastradh }
1359d5db0c2Sriastradh 
nv04_dac_detect(struct drm_encoder * encoder,struct drm_connector * connector)1369d5db0c2Sriastradh static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
1379d5db0c2Sriastradh 						 struct drm_connector *connector)
1389d5db0c2Sriastradh {
1399d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
140*677dec6eSriastradh 	struct nvif_object *device = &nouveau_drm(dev)->client.device.object;
1419d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(dev);
1429d5db0c2Sriastradh 	uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode;
1439d5db0c2Sriastradh 	uint8_t saved_palette0[3], saved_palette_mask;
1449d5db0c2Sriastradh 	uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
1459d5db0c2Sriastradh 	int i;
1469d5db0c2Sriastradh 	uint8_t blue;
1479d5db0c2Sriastradh 	bool sense = true;
1489d5db0c2Sriastradh 
1499d5db0c2Sriastradh 	/*
1509d5db0c2Sriastradh 	 * for this detection to work, there needs to be a mode set up on the
1519d5db0c2Sriastradh 	 * CRTC.  this is presumed to be the case
1529d5db0c2Sriastradh 	 */
1539d5db0c2Sriastradh 
1549d5db0c2Sriastradh 	if (nv_two_heads(dev))
1559d5db0c2Sriastradh 		/* only implemented for head A for now */
1569d5db0c2Sriastradh 		NVSetOwner(dev, 0);
1579d5db0c2Sriastradh 
1589d5db0c2Sriastradh 	saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX);
1599d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80);
1609d5db0c2Sriastradh 
1619d5db0c2Sriastradh 	saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
1629d5db0c2Sriastradh 	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
1639d5db0c2Sriastradh 
1649d5db0c2Sriastradh 	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
1659d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
1669d5db0c2Sriastradh 		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
1679d5db0c2Sriastradh 
1689d5db0c2Sriastradh 	msleep(10);
1699d5db0c2Sriastradh 
1709d5db0c2Sriastradh 	saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
1719d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
1729d5db0c2Sriastradh 		       saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
1739d5db0c2Sriastradh 	saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
1749d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
1759d5db0c2Sriastradh 
176d350ecf5Sriastradh 	nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
1779d5db0c2Sriastradh 	for (i = 0; i < 3; i++)
178d350ecf5Sriastradh 		saved_palette0[i] = nvif_rd08(device, NV_PRMDIO_PALETTE_DATA);
179d350ecf5Sriastradh 	saved_palette_mask = nvif_rd08(device, NV_PRMDIO_PIXEL_MASK);
180d350ecf5Sriastradh 	nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, 0);
1819d5db0c2Sriastradh 
1829d5db0c2Sriastradh 	saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
1839d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
1849d5db0c2Sriastradh 		      (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
1859d5db0c2Sriastradh 					   NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
1869d5db0c2Sriastradh 		      NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
1879d5db0c2Sriastradh 
1889d5db0c2Sriastradh 	blue = 8;	/* start of test range */
1899d5db0c2Sriastradh 
1909d5db0c2Sriastradh 	do {
1919d5db0c2Sriastradh 		bool sense_pair[2];
1929d5db0c2Sriastradh 
193d350ecf5Sriastradh 		nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
194d350ecf5Sriastradh 		nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
195d350ecf5Sriastradh 		nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
1969d5db0c2Sriastradh 		/* testing blue won't find monochrome monitors.  I don't care */
197d350ecf5Sriastradh 		nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, blue);
1989d5db0c2Sriastradh 
1999d5db0c2Sriastradh 		i = 0;
2009d5db0c2Sriastradh 		/* take sample pairs until both samples in the pair agree */
2019d5db0c2Sriastradh 		do {
2029d5db0c2Sriastradh 			if (sample_load_twice(dev, sense_pair))
2039d5db0c2Sriastradh 				goto out;
2049d5db0c2Sriastradh 		} while ((sense_pair[0] != sense_pair[1]) &&
2059d5db0c2Sriastradh 							++i < MAX_SAMPLE_PAIRS);
2069d5db0c2Sriastradh 
2079d5db0c2Sriastradh 		if (i == MAX_SAMPLE_PAIRS)
2089d5db0c2Sriastradh 			/* too much oscillation defaults to LO */
2099d5db0c2Sriastradh 			sense = false;
2109d5db0c2Sriastradh 		else
2119d5db0c2Sriastradh 			sense = sense_pair[0];
2129d5db0c2Sriastradh 
2139d5db0c2Sriastradh 	/*
2149d5db0c2Sriastradh 	 * if sense goes LO before blue ramps to 0x18, monitor is not connected.
2159d5db0c2Sriastradh 	 * ergo, if blue gets to 0x18, monitor must be connected
2169d5db0c2Sriastradh 	 */
2179d5db0c2Sriastradh 	} while (++blue < 0x18 && sense);
2189d5db0c2Sriastradh 
2199d5db0c2Sriastradh out:
220d350ecf5Sriastradh 	nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
2219d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
222d350ecf5Sriastradh 	nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
2239d5db0c2Sriastradh 	for (i = 0; i < 3; i++)
224d350ecf5Sriastradh 		nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
2259d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
2269d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
2279d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
2289d5db0c2Sriastradh 	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
2299d5db0c2Sriastradh 	NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode);
2309d5db0c2Sriastradh 
2319d5db0c2Sriastradh 	if (blue == 0x18) {
2329d5db0c2Sriastradh 		NV_DEBUG(drm, "Load detected on head A\n");
2339d5db0c2Sriastradh 		return connector_status_connected;
2349d5db0c2Sriastradh 	}
2359d5db0c2Sriastradh 
2369d5db0c2Sriastradh 	return connector_status_disconnected;
2379d5db0c2Sriastradh }
2389d5db0c2Sriastradh 
nv17_dac_sample_load(struct drm_encoder * encoder)2399d5db0c2Sriastradh uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
2409d5db0c2Sriastradh {
2419d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
2429d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(dev);
243*677dec6eSriastradh 	struct nvif_object *device = &nouveau_drm(dev)->client.device.object;
244*677dec6eSriastradh 	struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device);
2459d5db0c2Sriastradh 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
2469d5db0c2Sriastradh 	uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder);
2479d5db0c2Sriastradh 	uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
2489d5db0c2Sriastradh 		saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput;
2499d5db0c2Sriastradh 	int head;
2509d5db0c2Sriastradh 
2519d5db0c2Sriastradh #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
2529d5db0c2Sriastradh 	if (dcb->type == DCB_OUTPUT_TV) {
2539d5db0c2Sriastradh 		testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
2549d5db0c2Sriastradh 
2559d5db0c2Sriastradh 		if (drm->vbios.tvdactestval)
2569d5db0c2Sriastradh 			testval = drm->vbios.tvdactestval;
2579d5db0c2Sriastradh 	} else {
2589d5db0c2Sriastradh 		testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
2599d5db0c2Sriastradh 
2609d5db0c2Sriastradh 		if (drm->vbios.dactestval)
2619d5db0c2Sriastradh 			testval = drm->vbios.dactestval;
2629d5db0c2Sriastradh 	}
2639d5db0c2Sriastradh 
2649d5db0c2Sriastradh 	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
2659d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
2669d5db0c2Sriastradh 		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
2679d5db0c2Sriastradh 
268d350ecf5Sriastradh 	saved_powerctrl_2 = nvif_rd32(device, NV_PBUS_POWERCTRL_2);
2699d5db0c2Sriastradh 
270d350ecf5Sriastradh 	nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
2719d5db0c2Sriastradh 	if (regoffset == 0x68) {
272d350ecf5Sriastradh 		saved_powerctrl_4 = nvif_rd32(device, NV_PBUS_POWERCTRL_4);
273d350ecf5Sriastradh 		nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
2749d5db0c2Sriastradh 	}
2759d5db0c2Sriastradh 
2769d5db0c2Sriastradh 	if (gpio) {
277d350ecf5Sriastradh 		saved_gpio1 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC1, 0xff);
278d350ecf5Sriastradh 		saved_gpio0 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC0, 0xff);
279d350ecf5Sriastradh 		nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV);
280d350ecf5Sriastradh 		nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV);
2819d5db0c2Sriastradh 	}
2829d5db0c2Sriastradh 
2839d5db0c2Sriastradh 	msleep(4);
2849d5db0c2Sriastradh 
2859d5db0c2Sriastradh 	saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
2869d5db0c2Sriastradh 	head = (saved_routput & 0x100) >> 8;
2879d5db0c2Sriastradh 
2889d5db0c2Sriastradh 	/* if there's a spare crtc, using it will minimise flicker */
2899d5db0c2Sriastradh 	if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0))
2909d5db0c2Sriastradh 		head ^= 1;
2919d5db0c2Sriastradh 
2929d5db0c2Sriastradh 	/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
2939d5db0c2Sriastradh 	routput = (saved_routput & 0xfffffece) | head << 8;
2949d5db0c2Sriastradh 
295*677dec6eSriastradh 	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CURIE) {
2969d5db0c2Sriastradh 		if (dcb->type == DCB_OUTPUT_TV)
2979d5db0c2Sriastradh 			routput |= 0x1a << 16;
2989d5db0c2Sriastradh 		else
2999d5db0c2Sriastradh 			routput &= ~(0x1a << 16);
3009d5db0c2Sriastradh 	}
3019d5db0c2Sriastradh 
3029d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
3039d5db0c2Sriastradh 	msleep(1);
3049d5db0c2Sriastradh 
3059d5db0c2Sriastradh 	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
3069d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
3079d5db0c2Sriastradh 
3089d5db0c2Sriastradh 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
3099d5db0c2Sriastradh 		      NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
3109d5db0c2Sriastradh 	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
3119d5db0c2Sriastradh 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
3129d5db0c2Sriastradh 		      temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
3139d5db0c2Sriastradh 	msleep(5);
3149d5db0c2Sriastradh 
3159d5db0c2Sriastradh 	sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
3169d5db0c2Sriastradh 	/* do it again just in case it's a residual current */
3179d5db0c2Sriastradh 	sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
3189d5db0c2Sriastradh 
3199d5db0c2Sriastradh 	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
3209d5db0c2Sriastradh 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
3219d5db0c2Sriastradh 		      temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
3229d5db0c2Sriastradh 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
3239d5db0c2Sriastradh 
3249d5db0c2Sriastradh 	/* bios does something more complex for restoring, but I think this is good enough */
3259d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
3269d5db0c2Sriastradh 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
3279d5db0c2Sriastradh 	if (regoffset == 0x68)
328d350ecf5Sriastradh 		nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
329d350ecf5Sriastradh 	nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
3309d5db0c2Sriastradh 
3319d5db0c2Sriastradh 	if (gpio) {
332d350ecf5Sriastradh 		nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1);
333d350ecf5Sriastradh 		nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0);
3349d5db0c2Sriastradh 	}
3359d5db0c2Sriastradh 
3369d5db0c2Sriastradh 	return sample;
3379d5db0c2Sriastradh }
3389d5db0c2Sriastradh 
3399d5db0c2Sriastradh static enum drm_connector_status
nv17_dac_detect(struct drm_encoder * encoder,struct drm_connector * connector)3409d5db0c2Sriastradh nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
3419d5db0c2Sriastradh {
3429d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(encoder->dev);
3439d5db0c2Sriastradh 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
3449d5db0c2Sriastradh 
3459d5db0c2Sriastradh 	if (nv04_dac_in_use(encoder))
3469d5db0c2Sriastradh 		return connector_status_disconnected;
3479d5db0c2Sriastradh 
3489d5db0c2Sriastradh 	if (nv17_dac_sample_load(encoder) &
3499d5db0c2Sriastradh 	    NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) {
3509d5db0c2Sriastradh 		NV_DEBUG(drm, "Load detected on output %c\n",
3519d5db0c2Sriastradh 			 '@' + ffs(dcb->or));
3529d5db0c2Sriastradh 		return connector_status_connected;
3539d5db0c2Sriastradh 	} else {
3549d5db0c2Sriastradh 		return connector_status_disconnected;
3559d5db0c2Sriastradh 	}
3569d5db0c2Sriastradh }
3579d5db0c2Sriastradh 
nv04_dac_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)3589d5db0c2Sriastradh static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
3599d5db0c2Sriastradh 				const struct drm_display_mode *mode,
3609d5db0c2Sriastradh 				struct drm_display_mode *adjusted_mode)
3619d5db0c2Sriastradh {
3629d5db0c2Sriastradh 	if (nv04_dac_in_use(encoder))
3639d5db0c2Sriastradh 		return false;
3649d5db0c2Sriastradh 
3659d5db0c2Sriastradh 	return true;
3669d5db0c2Sriastradh }
3679d5db0c2Sriastradh 
nv04_dac_prepare(struct drm_encoder * encoder)3689d5db0c2Sriastradh static void nv04_dac_prepare(struct drm_encoder *encoder)
3699d5db0c2Sriastradh {
370d350ecf5Sriastradh 	const struct drm_encoder_helper_funcs *helper = encoder->helper_private;
3719d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
3729d5db0c2Sriastradh 	int head = nouveau_crtc(encoder->crtc)->index;
3739d5db0c2Sriastradh 
3749d5db0c2Sriastradh 	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
3759d5db0c2Sriastradh 
3769d5db0c2Sriastradh 	nv04_dfp_disable(dev, head);
3779d5db0c2Sriastradh }
3789d5db0c2Sriastradh 
nv04_dac_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)3799d5db0c2Sriastradh static void nv04_dac_mode_set(struct drm_encoder *encoder,
3809d5db0c2Sriastradh 			      struct drm_display_mode *mode,
3819d5db0c2Sriastradh 			      struct drm_display_mode *adjusted_mode)
3829d5db0c2Sriastradh {
3839d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
3849d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(dev);
3859d5db0c2Sriastradh 	int head = nouveau_crtc(encoder->crtc)->index;
3869d5db0c2Sriastradh 
3879d5db0c2Sriastradh 	if (nv_gf4_disp_arch(dev)) {
3889d5db0c2Sriastradh 		struct drm_encoder *rebind;
3899d5db0c2Sriastradh 		uint32_t dac_offset = nv04_dac_output_offset(encoder);
3909d5db0c2Sriastradh 		uint32_t otherdac;
3919d5db0c2Sriastradh 
3929d5db0c2Sriastradh 		/* bit 16-19 are bits that are set on some G70 cards,
3939d5db0c2Sriastradh 		 * but don't seem to have much effect */
3949d5db0c2Sriastradh 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
3959d5db0c2Sriastradh 			      head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
3969d5db0c2Sriastradh 		/* force any other vga encoders to bind to the other crtc */
3979d5db0c2Sriastradh 		list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
3989d5db0c2Sriastradh 			if (rebind == encoder
3999d5db0c2Sriastradh 			    || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG)
4009d5db0c2Sriastradh 				continue;
4019d5db0c2Sriastradh 
4029d5db0c2Sriastradh 			dac_offset = nv04_dac_output_offset(rebind);
4039d5db0c2Sriastradh 			otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
4049d5db0c2Sriastradh 			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
4059d5db0c2Sriastradh 				      (otherdac & ~0x0100) | (head ^ 1) << 8);
4069d5db0c2Sriastradh 		}
4079d5db0c2Sriastradh 	}
4089d5db0c2Sriastradh 
4099d5db0c2Sriastradh 	/* This could use refinement for flatpanels, but it should work this way */
410*677dec6eSriastradh 	if (drm->client.device.info.chipset < 0x44)
4119d5db0c2Sriastradh 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
4129d5db0c2Sriastradh 	else
4139d5db0c2Sriastradh 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
4149d5db0c2Sriastradh }
4159d5db0c2Sriastradh 
nv04_dac_commit(struct drm_encoder * encoder)4169d5db0c2Sriastradh static void nv04_dac_commit(struct drm_encoder *encoder)
4179d5db0c2Sriastradh {
4189d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
4199d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(encoder->dev);
4209d5db0c2Sriastradh 	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
421d350ecf5Sriastradh 	const struct drm_encoder_helper_funcs *helper = encoder->helper_private;
4229d5db0c2Sriastradh 
4239d5db0c2Sriastradh 	helper->dpms(encoder, DRM_MODE_DPMS_ON);
4249d5db0c2Sriastradh 
4259d5db0c2Sriastradh 	NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
426d350ecf5Sriastradh 		 nouveau_encoder_connector_get(nv_encoder)->base.name,
4279d5db0c2Sriastradh 		 nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
4289d5db0c2Sriastradh }
4299d5db0c2Sriastradh 
nv04_dac_update_dacclk(struct drm_encoder * encoder,bool enable)4309d5db0c2Sriastradh void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
4319d5db0c2Sriastradh {
4329d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
4339d5db0c2Sriastradh 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
4349d5db0c2Sriastradh 
4359d5db0c2Sriastradh 	if (nv_gf4_disp_arch(dev)) {
4369d5db0c2Sriastradh 		uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1];
4379d5db0c2Sriastradh 		int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
4389d5db0c2Sriastradh 		uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
4399d5db0c2Sriastradh 
4409d5db0c2Sriastradh 		if (enable) {
4419d5db0c2Sriastradh 			*dac_users |= 1 << dcb->index;
4429d5db0c2Sriastradh 			NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
4439d5db0c2Sriastradh 
4449d5db0c2Sriastradh 		} else {
4459d5db0c2Sriastradh 			*dac_users &= ~(1 << dcb->index);
4469d5db0c2Sriastradh 			if (!*dac_users)
4479d5db0c2Sriastradh 				NVWriteRAMDAC(dev, 0, dacclk_off,
4489d5db0c2Sriastradh 					dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
4499d5db0c2Sriastradh 		}
4509d5db0c2Sriastradh 	}
4519d5db0c2Sriastradh }
4529d5db0c2Sriastradh 
4539d5db0c2Sriastradh /* Check if the DAC corresponding to 'encoder' is being used by
4549d5db0c2Sriastradh  * someone else. */
nv04_dac_in_use(struct drm_encoder * encoder)4559d5db0c2Sriastradh bool nv04_dac_in_use(struct drm_encoder *encoder)
4569d5db0c2Sriastradh {
4579d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
4589d5db0c2Sriastradh 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
4599d5db0c2Sriastradh 
4609d5db0c2Sriastradh 	return nv_gf4_disp_arch(encoder->dev) &&
4619d5db0c2Sriastradh 		(nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index));
4629d5db0c2Sriastradh }
4639d5db0c2Sriastradh 
nv04_dac_dpms(struct drm_encoder * encoder,int mode)4649d5db0c2Sriastradh static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
4659d5db0c2Sriastradh {
4669d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
4679d5db0c2Sriastradh 	struct nouveau_drm *drm = nouveau_drm(encoder->dev);
4689d5db0c2Sriastradh 
4699d5db0c2Sriastradh 	if (nv_encoder->last_dpms == mode)
4709d5db0c2Sriastradh 		return;
4719d5db0c2Sriastradh 	nv_encoder->last_dpms = mode;
4729d5db0c2Sriastradh 
4739d5db0c2Sriastradh 	NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n",
4749d5db0c2Sriastradh 		 mode, nv_encoder->dcb->index);
4759d5db0c2Sriastradh 
4769d5db0c2Sriastradh 	nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
4779d5db0c2Sriastradh }
4789d5db0c2Sriastradh 
nv04_dac_save(struct drm_encoder * encoder)4799d5db0c2Sriastradh static void nv04_dac_save(struct drm_encoder *encoder)
4809d5db0c2Sriastradh {
4819d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
4829d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
4839d5db0c2Sriastradh 
4849d5db0c2Sriastradh 	if (nv_gf4_disp_arch(dev))
4859d5db0c2Sriastradh 		nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
4869d5db0c2Sriastradh 							  nv04_dac_output_offset(encoder));
4879d5db0c2Sriastradh }
4889d5db0c2Sriastradh 
nv04_dac_restore(struct drm_encoder * encoder)4899d5db0c2Sriastradh static void nv04_dac_restore(struct drm_encoder *encoder)
4909d5db0c2Sriastradh {
4919d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
4929d5db0c2Sriastradh 	struct drm_device *dev = encoder->dev;
4939d5db0c2Sriastradh 
4949d5db0c2Sriastradh 	if (nv_gf4_disp_arch(dev))
4959d5db0c2Sriastradh 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
4969d5db0c2Sriastradh 			      nv_encoder->restore.output);
4979d5db0c2Sriastradh 
4989d5db0c2Sriastradh 	nv_encoder->last_dpms = NV_DPMS_CLEARED;
4999d5db0c2Sriastradh }
5009d5db0c2Sriastradh 
nv04_dac_destroy(struct drm_encoder * encoder)5019d5db0c2Sriastradh static void nv04_dac_destroy(struct drm_encoder *encoder)
5029d5db0c2Sriastradh {
5039d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
5049d5db0c2Sriastradh 
5059d5db0c2Sriastradh 	drm_encoder_cleanup(encoder);
5069d5db0c2Sriastradh 	kfree(nv_encoder);
5079d5db0c2Sriastradh }
5089d5db0c2Sriastradh 
5099d5db0c2Sriastradh static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
5109d5db0c2Sriastradh 	.dpms = nv04_dac_dpms,
5119d5db0c2Sriastradh 	.mode_fixup = nv04_dac_mode_fixup,
5129d5db0c2Sriastradh 	.prepare = nv04_dac_prepare,
5139d5db0c2Sriastradh 	.commit = nv04_dac_commit,
5149d5db0c2Sriastradh 	.mode_set = nv04_dac_mode_set,
5159d5db0c2Sriastradh 	.detect = nv04_dac_detect
5169d5db0c2Sriastradh };
5179d5db0c2Sriastradh 
5189d5db0c2Sriastradh static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
5199d5db0c2Sriastradh 	.dpms = nv04_dac_dpms,
5209d5db0c2Sriastradh 	.mode_fixup = nv04_dac_mode_fixup,
5219d5db0c2Sriastradh 	.prepare = nv04_dac_prepare,
5229d5db0c2Sriastradh 	.commit = nv04_dac_commit,
5239d5db0c2Sriastradh 	.mode_set = nv04_dac_mode_set,
5249d5db0c2Sriastradh 	.detect = nv17_dac_detect
5259d5db0c2Sriastradh };
5269d5db0c2Sriastradh 
5279d5db0c2Sriastradh static const struct drm_encoder_funcs nv04_dac_funcs = {
5289d5db0c2Sriastradh 	.destroy = nv04_dac_destroy,
5299d5db0c2Sriastradh };
5309d5db0c2Sriastradh 
5319d5db0c2Sriastradh int
nv04_dac_create(struct drm_connector * connector,struct dcb_output * entry)5329d5db0c2Sriastradh nv04_dac_create(struct drm_connector *connector, struct dcb_output *entry)
5339d5db0c2Sriastradh {
5349d5db0c2Sriastradh 	const struct drm_encoder_helper_funcs *helper;
5359d5db0c2Sriastradh 	struct nouveau_encoder *nv_encoder = NULL;
5369d5db0c2Sriastradh 	struct drm_device *dev = connector->dev;
5379d5db0c2Sriastradh 	struct drm_encoder *encoder;
5389d5db0c2Sriastradh 
5399d5db0c2Sriastradh 	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
5409d5db0c2Sriastradh 	if (!nv_encoder)
5419d5db0c2Sriastradh 		return -ENOMEM;
5429d5db0c2Sriastradh 
5439d5db0c2Sriastradh 	encoder = to_drm_encoder(nv_encoder);
5449d5db0c2Sriastradh 
5459d5db0c2Sriastradh 	nv_encoder->dcb = entry;
5469d5db0c2Sriastradh 	nv_encoder->or = ffs(entry->or) - 1;
5479d5db0c2Sriastradh 
548*677dec6eSriastradh 	nv_encoder->enc_save = nv04_dac_save;
549*677dec6eSriastradh 	nv_encoder->enc_restore = nv04_dac_restore;
550*677dec6eSriastradh 
5519d5db0c2Sriastradh 	if (nv_gf4_disp_arch(dev))
5529d5db0c2Sriastradh 		helper = &nv17_dac_helper_funcs;
5539d5db0c2Sriastradh 	else
5549d5db0c2Sriastradh 		helper = &nv04_dac_helper_funcs;
5559d5db0c2Sriastradh 
556*677dec6eSriastradh 	drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC,
557*677dec6eSriastradh 			 NULL);
5589d5db0c2Sriastradh 	drm_encoder_helper_add(encoder, helper);
5599d5db0c2Sriastradh 
5609d5db0c2Sriastradh 	encoder->possible_crtcs = entry->heads;
5619d5db0c2Sriastradh 	encoder->possible_clones = 0;
5629d5db0c2Sriastradh 
563*677dec6eSriastradh 	drm_connector_attach_encoder(connector, encoder);
5649d5db0c2Sriastradh 	return 0;
5659d5db0c2Sriastradh }
566