xref: /openbmc/linux/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c (revision 1a6463425552a8b9960e5a19b25421895846925c)
1*1a646342SBen Skeggs /*
2*1a646342SBen Skeggs  * Copyright (C) 2009 Francisco Jerez.
3*1a646342SBen Skeggs  * All Rights Reserved.
4*1a646342SBen Skeggs  *
5*1a646342SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining
6*1a646342SBen Skeggs  * a copy of this software and associated documentation files (the
7*1a646342SBen Skeggs  * "Software"), to deal in the Software without restriction, including
8*1a646342SBen Skeggs  * without limitation the rights to use, copy, modify, merge, publish,
9*1a646342SBen Skeggs  * distribute, sublicense, and/or sell copies of the Software, and to
10*1a646342SBen Skeggs  * permit persons to whom the Software is furnished to do so, subject to
11*1a646342SBen Skeggs  * the following conditions:
12*1a646342SBen Skeggs  *
13*1a646342SBen Skeggs  * The above copyright notice and this permission notice (including the
14*1a646342SBen Skeggs  * next paragraph) shall be included in all copies or substantial
15*1a646342SBen Skeggs  * portions of the Software.
16*1a646342SBen Skeggs  *
17*1a646342SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18*1a646342SBen Skeggs  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19*1a646342SBen Skeggs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20*1a646342SBen Skeggs  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21*1a646342SBen Skeggs  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22*1a646342SBen Skeggs  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23*1a646342SBen Skeggs  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24*1a646342SBen Skeggs  *
25*1a646342SBen Skeggs  */
26*1a646342SBen Skeggs 
27*1a646342SBen Skeggs #include <drm/drmP.h>
28*1a646342SBen Skeggs #include <drm/drm_crtc_helper.h>
29*1a646342SBen Skeggs #include "nouveau_drm.h"
30*1a646342SBen Skeggs #include "nouveau_reg.h"
31*1a646342SBen Skeggs #include "nouveau_encoder.h"
32*1a646342SBen Skeggs #include "nouveau_connector.h"
33*1a646342SBen Skeggs #include "nouveau_crtc.h"
34*1a646342SBen Skeggs #include "hw.h"
35*1a646342SBen Skeggs #include "tvnv17.h"
36*1a646342SBen Skeggs 
37*1a646342SBen Skeggs #include <core/device.h>
38*1a646342SBen Skeggs 
39*1a646342SBen Skeggs #include <subdev/bios/gpio.h>
40*1a646342SBen Skeggs #include <subdev/gpio.h>
41*1a646342SBen Skeggs 
42*1a646342SBen Skeggs MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
43*1a646342SBen Skeggs 		 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
44*1a646342SBen Skeggs 		 "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
45*1a646342SBen Skeggs 		 "\t\tDefault: PAL\n"
46*1a646342SBen Skeggs 		 "\t\t*NOTE* Ignored for cards with external TV encoders.");
47*1a646342SBen Skeggs static char *nouveau_tv_norm;
48*1a646342SBen Skeggs module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
49*1a646342SBen Skeggs 
50*1a646342SBen Skeggs static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
51*1a646342SBen Skeggs {
52*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
53*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
54*1a646342SBen Skeggs 	struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
55*1a646342SBen Skeggs 	uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
56*1a646342SBen Skeggs 	uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end,
57*1a646342SBen Skeggs 		fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c;
58*1a646342SBen Skeggs 	uint32_t sample = 0;
59*1a646342SBen Skeggs 	int head;
60*1a646342SBen Skeggs 
61*1a646342SBen Skeggs #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
62*1a646342SBen Skeggs 	testval = RGB_TEST_DATA(0x82, 0xeb, 0x82);
63*1a646342SBen Skeggs 	if (drm->vbios.tvdactestval)
64*1a646342SBen Skeggs 		testval = drm->vbios.tvdactestval;
65*1a646342SBen Skeggs 
66*1a646342SBen Skeggs 	dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
67*1a646342SBen Skeggs 	head = (dacclk & 0x100) >> 8;
68*1a646342SBen Skeggs 
69*1a646342SBen Skeggs 	/* Save the previous state. */
70*1a646342SBen Skeggs 	gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff);
71*1a646342SBen Skeggs 	gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff);
72*1a646342SBen Skeggs 	fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL);
73*1a646342SBen Skeggs 	fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START);
74*1a646342SBen Skeggs 	fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END);
75*1a646342SBen Skeggs 	fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
76*1a646342SBen Skeggs 	test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
77*1a646342SBen Skeggs 	ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c);
78*1a646342SBen Skeggs 	ctv_14 = NVReadRAMDAC(dev, head, 0x680c14);
79*1a646342SBen Skeggs 	ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c);
80*1a646342SBen Skeggs 
81*1a646342SBen Skeggs 	/* Prepare the DAC for load detection.  */
82*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, true);
83*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, true);
84*1a646342SBen Skeggs 
85*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343);
86*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047);
87*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183);
88*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
89*1a646342SBen Skeggs 		      NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
90*1a646342SBen Skeggs 		      NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 |
91*1a646342SBen Skeggs 		      NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
92*1a646342SBen Skeggs 		      NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |
93*1a646342SBen Skeggs 		      NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS);
94*1a646342SBen Skeggs 
95*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0);
96*1a646342SBen Skeggs 
97*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
98*1a646342SBen Skeggs 		      (dacclk & ~0xff) | 0x22);
99*1a646342SBen Skeggs 	msleep(1);
100*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
101*1a646342SBen Skeggs 		      (dacclk & ~0xff) | 0x21);
102*1a646342SBen Skeggs 
103*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20);
104*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16);
105*1a646342SBen Skeggs 
106*1a646342SBen Skeggs 	/* Sample pin 0x4 (usually S-video luma). */
107*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff);
108*1a646342SBen Skeggs 	msleep(20);
109*1a646342SBen Skeggs 	sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
110*1a646342SBen Skeggs 		& 0x4 << 28;
111*1a646342SBen Skeggs 
112*1a646342SBen Skeggs 	/* Sample the remaining pins. */
113*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff);
114*1a646342SBen Skeggs 	msleep(20);
115*1a646342SBen Skeggs 	sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
116*1a646342SBen Skeggs 		& 0xa << 28;
117*1a646342SBen Skeggs 
118*1a646342SBen Skeggs 	/* Restore the previous state. */
119*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c);
120*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c14, ctv_14);
121*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c);
122*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk);
123*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl);
124*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control);
125*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end);
126*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start);
127*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal);
128*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, gpio1);
129*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, gpio0);
130*1a646342SBen Skeggs 
131*1a646342SBen Skeggs 	return sample;
132*1a646342SBen Skeggs }
133*1a646342SBen Skeggs 
134*1a646342SBen Skeggs static bool
135*1a646342SBen Skeggs get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask)
136*1a646342SBen Skeggs {
137*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
138*1a646342SBen Skeggs 	struct nouveau_object *device = drm->device;
139*1a646342SBen Skeggs 
140*1a646342SBen Skeggs 	/* Zotac FX5200 */
141*1a646342SBen Skeggs 	if (nv_device_match(device, 0x0322, 0x19da, 0x1035) ||
142*1a646342SBen Skeggs 	    nv_device_match(device, 0x0322, 0x19da, 0x2035)) {
143*1a646342SBen Skeggs 		*pin_mask = 0xc;
144*1a646342SBen Skeggs 		return false;
145*1a646342SBen Skeggs 	}
146*1a646342SBen Skeggs 
147*1a646342SBen Skeggs 	/* MSI nForce2 IGP */
148*1a646342SBen Skeggs 	if (nv_device_match(device, 0x01f0, 0x1462, 0x5710)) {
149*1a646342SBen Skeggs 		*pin_mask = 0xc;
150*1a646342SBen Skeggs 		return false;
151*1a646342SBen Skeggs 	}
152*1a646342SBen Skeggs 
153*1a646342SBen Skeggs 	return true;
154*1a646342SBen Skeggs }
155*1a646342SBen Skeggs 
156*1a646342SBen Skeggs static enum drm_connector_status
157*1a646342SBen Skeggs nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
158*1a646342SBen Skeggs {
159*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
160*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
161*1a646342SBen Skeggs 	struct drm_mode_config *conf = &dev->mode_config;
162*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
163*1a646342SBen Skeggs 	struct dcb_output *dcb = tv_enc->base.dcb;
164*1a646342SBen Skeggs 	bool reliable = get_tv_detect_quirks(dev, &tv_enc->pin_mask);
165*1a646342SBen Skeggs 
166*1a646342SBen Skeggs 	if (nv04_dac_in_use(encoder))
167*1a646342SBen Skeggs 		return connector_status_disconnected;
168*1a646342SBen Skeggs 
169*1a646342SBen Skeggs 	if (reliable) {
170*1a646342SBen Skeggs 		if (nv_device(drm->device)->chipset == 0x42 ||
171*1a646342SBen Skeggs 		    nv_device(drm->device)->chipset == 0x43)
172*1a646342SBen Skeggs 			tv_enc->pin_mask =
173*1a646342SBen Skeggs 				nv42_tv_sample_load(encoder) >> 28 & 0xe;
174*1a646342SBen Skeggs 		else
175*1a646342SBen Skeggs 			tv_enc->pin_mask =
176*1a646342SBen Skeggs 				nv17_dac_sample_load(encoder) >> 28 & 0xe;
177*1a646342SBen Skeggs 	}
178*1a646342SBen Skeggs 
179*1a646342SBen Skeggs 	switch (tv_enc->pin_mask) {
180*1a646342SBen Skeggs 	case 0x2:
181*1a646342SBen Skeggs 	case 0x4:
182*1a646342SBen Skeggs 		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
183*1a646342SBen Skeggs 		break;
184*1a646342SBen Skeggs 	case 0xc:
185*1a646342SBen Skeggs 		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
186*1a646342SBen Skeggs 		break;
187*1a646342SBen Skeggs 	case 0xe:
188*1a646342SBen Skeggs 		if (dcb->tvconf.has_component_output)
189*1a646342SBen Skeggs 			tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
190*1a646342SBen Skeggs 		else
191*1a646342SBen Skeggs 			tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
192*1a646342SBen Skeggs 		break;
193*1a646342SBen Skeggs 	default:
194*1a646342SBen Skeggs 		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
195*1a646342SBen Skeggs 		break;
196*1a646342SBen Skeggs 	}
197*1a646342SBen Skeggs 
198*1a646342SBen Skeggs 	drm_object_property_set_value(&connector->base,
199*1a646342SBen Skeggs 					 conf->tv_subconnector_property,
200*1a646342SBen Skeggs 					 tv_enc->subconnector);
201*1a646342SBen Skeggs 
202*1a646342SBen Skeggs 	if (!reliable) {
203*1a646342SBen Skeggs 		return connector_status_unknown;
204*1a646342SBen Skeggs 	} else if (tv_enc->subconnector) {
205*1a646342SBen Skeggs 		NV_INFO(drm, "Load detected on output %c\n",
206*1a646342SBen Skeggs 			'@' + ffs(dcb->or));
207*1a646342SBen Skeggs 		return connector_status_connected;
208*1a646342SBen Skeggs 	} else {
209*1a646342SBen Skeggs 		return connector_status_disconnected;
210*1a646342SBen Skeggs 	}
211*1a646342SBen Skeggs }
212*1a646342SBen Skeggs 
213*1a646342SBen Skeggs static int nv17_tv_get_ld_modes(struct drm_encoder *encoder,
214*1a646342SBen Skeggs 				struct drm_connector *connector)
215*1a646342SBen Skeggs {
216*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
217*1a646342SBen Skeggs 	const struct drm_display_mode *tv_mode;
218*1a646342SBen Skeggs 	int n = 0;
219*1a646342SBen Skeggs 
220*1a646342SBen Skeggs 	for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
221*1a646342SBen Skeggs 		struct drm_display_mode *mode;
222*1a646342SBen Skeggs 
223*1a646342SBen Skeggs 		mode = drm_mode_duplicate(encoder->dev, tv_mode);
224*1a646342SBen Skeggs 
225*1a646342SBen Skeggs 		mode->clock = tv_norm->tv_enc_mode.vrefresh *
226*1a646342SBen Skeggs 			mode->htotal / 1000 *
227*1a646342SBen Skeggs 			mode->vtotal / 1000;
228*1a646342SBen Skeggs 
229*1a646342SBen Skeggs 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
230*1a646342SBen Skeggs 			mode->clock *= 2;
231*1a646342SBen Skeggs 
232*1a646342SBen Skeggs 		if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
233*1a646342SBen Skeggs 		    mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
234*1a646342SBen Skeggs 			mode->type |= DRM_MODE_TYPE_PREFERRED;
235*1a646342SBen Skeggs 
236*1a646342SBen Skeggs 		drm_mode_probed_add(connector, mode);
237*1a646342SBen Skeggs 		n++;
238*1a646342SBen Skeggs 	}
239*1a646342SBen Skeggs 
240*1a646342SBen Skeggs 	return n;
241*1a646342SBen Skeggs }
242*1a646342SBen Skeggs 
243*1a646342SBen Skeggs static int nv17_tv_get_hd_modes(struct drm_encoder *encoder,
244*1a646342SBen Skeggs 				struct drm_connector *connector)
245*1a646342SBen Skeggs {
246*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
247*1a646342SBen Skeggs 	struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode;
248*1a646342SBen Skeggs 	struct drm_display_mode *mode;
249*1a646342SBen Skeggs 	const struct {
250*1a646342SBen Skeggs 		int hdisplay;
251*1a646342SBen Skeggs 		int vdisplay;
252*1a646342SBen Skeggs 	} modes[] = {
253*1a646342SBen Skeggs 		{ 640, 400 },
254*1a646342SBen Skeggs 		{ 640, 480 },
255*1a646342SBen Skeggs 		{ 720, 480 },
256*1a646342SBen Skeggs 		{ 720, 576 },
257*1a646342SBen Skeggs 		{ 800, 600 },
258*1a646342SBen Skeggs 		{ 1024, 768 },
259*1a646342SBen Skeggs 		{ 1280, 720 },
260*1a646342SBen Skeggs 		{ 1280, 1024 },
261*1a646342SBen Skeggs 		{ 1920, 1080 }
262*1a646342SBen Skeggs 	};
263*1a646342SBen Skeggs 	int i, n = 0;
264*1a646342SBen Skeggs 
265*1a646342SBen Skeggs 	for (i = 0; i < ARRAY_SIZE(modes); i++) {
266*1a646342SBen Skeggs 		if (modes[i].hdisplay > output_mode->hdisplay ||
267*1a646342SBen Skeggs 		    modes[i].vdisplay > output_mode->vdisplay)
268*1a646342SBen Skeggs 			continue;
269*1a646342SBen Skeggs 
270*1a646342SBen Skeggs 		if (modes[i].hdisplay == output_mode->hdisplay &&
271*1a646342SBen Skeggs 		    modes[i].vdisplay == output_mode->vdisplay) {
272*1a646342SBen Skeggs 			mode = drm_mode_duplicate(encoder->dev, output_mode);
273*1a646342SBen Skeggs 			mode->type |= DRM_MODE_TYPE_PREFERRED;
274*1a646342SBen Skeggs 
275*1a646342SBen Skeggs 		} else {
276*1a646342SBen Skeggs 			mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
277*1a646342SBen Skeggs 					    modes[i].vdisplay, 60, false,
278*1a646342SBen Skeggs 					    (output_mode->flags &
279*1a646342SBen Skeggs 					     DRM_MODE_FLAG_INTERLACE), false);
280*1a646342SBen Skeggs 		}
281*1a646342SBen Skeggs 
282*1a646342SBen Skeggs 		/* CVT modes are sometimes unsuitable... */
283*1a646342SBen Skeggs 		if (output_mode->hdisplay <= 720
284*1a646342SBen Skeggs 		    || output_mode->hdisplay >= 1920) {
285*1a646342SBen Skeggs 			mode->htotal = output_mode->htotal;
286*1a646342SBen Skeggs 			mode->hsync_start = (mode->hdisplay + (mode->htotal
287*1a646342SBen Skeggs 					     - mode->hdisplay) * 9 / 10) & ~7;
288*1a646342SBen Skeggs 			mode->hsync_end = mode->hsync_start + 8;
289*1a646342SBen Skeggs 		}
290*1a646342SBen Skeggs 
291*1a646342SBen Skeggs 		if (output_mode->vdisplay >= 1024) {
292*1a646342SBen Skeggs 			mode->vtotal = output_mode->vtotal;
293*1a646342SBen Skeggs 			mode->vsync_start = output_mode->vsync_start;
294*1a646342SBen Skeggs 			mode->vsync_end = output_mode->vsync_end;
295*1a646342SBen Skeggs 		}
296*1a646342SBen Skeggs 
297*1a646342SBen Skeggs 		mode->type |= DRM_MODE_TYPE_DRIVER;
298*1a646342SBen Skeggs 		drm_mode_probed_add(connector, mode);
299*1a646342SBen Skeggs 		n++;
300*1a646342SBen Skeggs 	}
301*1a646342SBen Skeggs 
302*1a646342SBen Skeggs 	return n;
303*1a646342SBen Skeggs }
304*1a646342SBen Skeggs 
305*1a646342SBen Skeggs static int nv17_tv_get_modes(struct drm_encoder *encoder,
306*1a646342SBen Skeggs 			     struct drm_connector *connector)
307*1a646342SBen Skeggs {
308*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
309*1a646342SBen Skeggs 
310*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE)
311*1a646342SBen Skeggs 		return nv17_tv_get_hd_modes(encoder, connector);
312*1a646342SBen Skeggs 	else
313*1a646342SBen Skeggs 		return nv17_tv_get_ld_modes(encoder, connector);
314*1a646342SBen Skeggs }
315*1a646342SBen Skeggs 
316*1a646342SBen Skeggs static int nv17_tv_mode_valid(struct drm_encoder *encoder,
317*1a646342SBen Skeggs 			      struct drm_display_mode *mode)
318*1a646342SBen Skeggs {
319*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
320*1a646342SBen Skeggs 
321*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE) {
322*1a646342SBen Skeggs 		struct drm_display_mode *output_mode =
323*1a646342SBen Skeggs 						&tv_norm->ctv_enc_mode.mode;
324*1a646342SBen Skeggs 
325*1a646342SBen Skeggs 		if (mode->clock > 400000)
326*1a646342SBen Skeggs 			return MODE_CLOCK_HIGH;
327*1a646342SBen Skeggs 
328*1a646342SBen Skeggs 		if (mode->hdisplay > output_mode->hdisplay ||
329*1a646342SBen Skeggs 		    mode->vdisplay > output_mode->vdisplay)
330*1a646342SBen Skeggs 			return MODE_BAD;
331*1a646342SBen Skeggs 
332*1a646342SBen Skeggs 		if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
333*1a646342SBen Skeggs 		    (output_mode->flags & DRM_MODE_FLAG_INTERLACE))
334*1a646342SBen Skeggs 			return MODE_NO_INTERLACE;
335*1a646342SBen Skeggs 
336*1a646342SBen Skeggs 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
337*1a646342SBen Skeggs 			return MODE_NO_DBLESCAN;
338*1a646342SBen Skeggs 
339*1a646342SBen Skeggs 	} else {
340*1a646342SBen Skeggs 		const int vsync_tolerance = 600;
341*1a646342SBen Skeggs 
342*1a646342SBen Skeggs 		if (mode->clock > 70000)
343*1a646342SBen Skeggs 			return MODE_CLOCK_HIGH;
344*1a646342SBen Skeggs 
345*1a646342SBen Skeggs 		if (abs(drm_mode_vrefresh(mode) * 1000 -
346*1a646342SBen Skeggs 			tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
347*1a646342SBen Skeggs 			return MODE_VSYNC;
348*1a646342SBen Skeggs 
349*1a646342SBen Skeggs 		/* The encoder takes care of the actual interlacing */
350*1a646342SBen Skeggs 		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
351*1a646342SBen Skeggs 			return MODE_NO_INTERLACE;
352*1a646342SBen Skeggs 	}
353*1a646342SBen Skeggs 
354*1a646342SBen Skeggs 	return MODE_OK;
355*1a646342SBen Skeggs }
356*1a646342SBen Skeggs 
357*1a646342SBen Skeggs static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
358*1a646342SBen Skeggs 			       const struct drm_display_mode *mode,
359*1a646342SBen Skeggs 			       struct drm_display_mode *adjusted_mode)
360*1a646342SBen Skeggs {
361*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
362*1a646342SBen Skeggs 
363*1a646342SBen Skeggs 	if (nv04_dac_in_use(encoder))
364*1a646342SBen Skeggs 		return false;
365*1a646342SBen Skeggs 
366*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE)
367*1a646342SBen Skeggs 		adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
368*1a646342SBen Skeggs 	else
369*1a646342SBen Skeggs 		adjusted_mode->clock = 90000;
370*1a646342SBen Skeggs 
371*1a646342SBen Skeggs 	return true;
372*1a646342SBen Skeggs }
373*1a646342SBen Skeggs 
374*1a646342SBen Skeggs static void  nv17_tv_dpms(struct drm_encoder *encoder, int mode)
375*1a646342SBen Skeggs {
376*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
377*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
378*1a646342SBen Skeggs 	struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
379*1a646342SBen Skeggs 	struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
380*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
381*1a646342SBen Skeggs 
382*1a646342SBen Skeggs 	if (nouveau_encoder(encoder)->last_dpms == mode)
383*1a646342SBen Skeggs 		return;
384*1a646342SBen Skeggs 	nouveau_encoder(encoder)->last_dpms = mode;
385*1a646342SBen Skeggs 
386*1a646342SBen Skeggs 	NV_INFO(drm, "Setting dpms mode %d on TV encoder (output %d)\n",
387*1a646342SBen Skeggs 		 mode, nouveau_encoder(encoder)->dcb->index);
388*1a646342SBen Skeggs 
389*1a646342SBen Skeggs 	regs->ptv_200 &= ~1;
390*1a646342SBen Skeggs 
391*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE) {
392*1a646342SBen Skeggs 		nv04_dfp_update_fp_control(encoder, mode);
393*1a646342SBen Skeggs 
394*1a646342SBen Skeggs 	} else {
395*1a646342SBen Skeggs 		nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
396*1a646342SBen Skeggs 
397*1a646342SBen Skeggs 		if (mode == DRM_MODE_DPMS_ON)
398*1a646342SBen Skeggs 			regs->ptv_200 |= 1;
399*1a646342SBen Skeggs 	}
400*1a646342SBen Skeggs 
401*1a646342SBen Skeggs 	nv_load_ptv(dev, regs, 200);
402*1a646342SBen Skeggs 
403*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, mode == DRM_MODE_DPMS_ON);
404*1a646342SBen Skeggs 	gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, mode == DRM_MODE_DPMS_ON);
405*1a646342SBen Skeggs 
406*1a646342SBen Skeggs 	nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
407*1a646342SBen Skeggs }
408*1a646342SBen Skeggs 
409*1a646342SBen Skeggs static void nv17_tv_prepare(struct drm_encoder *encoder)
410*1a646342SBen Skeggs {
411*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
412*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
413*1a646342SBen Skeggs 	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
414*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
415*1a646342SBen Skeggs 	int head = nouveau_crtc(encoder->crtc)->index;
416*1a646342SBen Skeggs 	uint8_t *cr_lcd = &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[
417*1a646342SBen Skeggs 							NV_CIO_CRE_LCD__INDEX];
418*1a646342SBen Skeggs 	uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
419*1a646342SBen Skeggs 					nv04_dac_output_offset(encoder);
420*1a646342SBen Skeggs 	uint32_t dacclk;
421*1a646342SBen Skeggs 
422*1a646342SBen Skeggs 	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
423*1a646342SBen Skeggs 
424*1a646342SBen Skeggs 	nv04_dfp_disable(dev, head);
425*1a646342SBen Skeggs 
426*1a646342SBen Skeggs 	/* Unbind any FP encoders from this head if we need the FP
427*1a646342SBen Skeggs 	 * stuff enabled. */
428*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE) {
429*1a646342SBen Skeggs 		struct drm_encoder *enc;
430*1a646342SBen Skeggs 
431*1a646342SBen Skeggs 		list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
432*1a646342SBen Skeggs 			struct dcb_output *dcb = nouveau_encoder(enc)->dcb;
433*1a646342SBen Skeggs 
434*1a646342SBen Skeggs 			if ((dcb->type == DCB_OUTPUT_TMDS ||
435*1a646342SBen Skeggs 			     dcb->type == DCB_OUTPUT_LVDS) &&
436*1a646342SBen Skeggs 			     !enc->crtc &&
437*1a646342SBen Skeggs 			     nv04_dfp_get_bound_head(dev, dcb) == head) {
438*1a646342SBen Skeggs 				nv04_dfp_bind_head(dev, dcb, head ^ 1,
439*1a646342SBen Skeggs 						drm->vbios.fp.dual_link);
440*1a646342SBen Skeggs 			}
441*1a646342SBen Skeggs 		}
442*1a646342SBen Skeggs 
443*1a646342SBen Skeggs 	}
444*1a646342SBen Skeggs 
445*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE)
446*1a646342SBen Skeggs 		*cr_lcd |= 0x1 | (head ? 0x0 : 0x8);
447*1a646342SBen Skeggs 
448*1a646342SBen Skeggs 	/* Set the DACCLK register */
449*1a646342SBen Skeggs 	dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
450*1a646342SBen Skeggs 
451*1a646342SBen Skeggs 	if (nv_device(drm->device)->card_type == NV_40)
452*1a646342SBen Skeggs 		dacclk |= 0x1a << 16;
453*1a646342SBen Skeggs 
454*1a646342SBen Skeggs 	if (tv_norm->kind == CTV_ENC_MODE) {
455*1a646342SBen Skeggs 		dacclk |=  0x20;
456*1a646342SBen Skeggs 
457*1a646342SBen Skeggs 		if (head)
458*1a646342SBen Skeggs 			dacclk |= 0x100;
459*1a646342SBen Skeggs 		else
460*1a646342SBen Skeggs 			dacclk &= ~0x100;
461*1a646342SBen Skeggs 
462*1a646342SBen Skeggs 	} else {
463*1a646342SBen Skeggs 		dacclk |= 0x10;
464*1a646342SBen Skeggs 
465*1a646342SBen Skeggs 	}
466*1a646342SBen Skeggs 
467*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
468*1a646342SBen Skeggs }
469*1a646342SBen Skeggs 
470*1a646342SBen Skeggs static void nv17_tv_mode_set(struct drm_encoder *encoder,
471*1a646342SBen Skeggs 			     struct drm_display_mode *drm_mode,
472*1a646342SBen Skeggs 			     struct drm_display_mode *adjusted_mode)
473*1a646342SBen Skeggs {
474*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
475*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
476*1a646342SBen Skeggs 	int head = nouveau_crtc(encoder->crtc)->index;
477*1a646342SBen Skeggs 	struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head];
478*1a646342SBen Skeggs 	struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
479*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
480*1a646342SBen Skeggs 	int i;
481*1a646342SBen Skeggs 
482*1a646342SBen Skeggs 	regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
483*1a646342SBen Skeggs 	regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
484*1a646342SBen Skeggs 	regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
485*1a646342SBen Skeggs 	regs->tv_setup = 1;
486*1a646342SBen Skeggs 	regs->ramdac_8c0 = 0x0;
487*1a646342SBen Skeggs 
488*1a646342SBen Skeggs 	if (tv_norm->kind == TV_ENC_MODE) {
489*1a646342SBen Skeggs 		tv_regs->ptv_200 = 0x13111100;
490*1a646342SBen Skeggs 		if (head)
491*1a646342SBen Skeggs 			tv_regs->ptv_200 |= 0x10;
492*1a646342SBen Skeggs 
493*1a646342SBen Skeggs 		tv_regs->ptv_20c = 0x808010;
494*1a646342SBen Skeggs 		tv_regs->ptv_304 = 0x2d00000;
495*1a646342SBen Skeggs 		tv_regs->ptv_600 = 0x0;
496*1a646342SBen Skeggs 		tv_regs->ptv_60c = 0x0;
497*1a646342SBen Skeggs 		tv_regs->ptv_610 = 0x1e00000;
498*1a646342SBen Skeggs 
499*1a646342SBen Skeggs 		if (tv_norm->tv_enc_mode.vdisplay == 576) {
500*1a646342SBen Skeggs 			tv_regs->ptv_508 = 0x1200000;
501*1a646342SBen Skeggs 			tv_regs->ptv_614 = 0x33;
502*1a646342SBen Skeggs 
503*1a646342SBen Skeggs 		} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
504*1a646342SBen Skeggs 			tv_regs->ptv_508 = 0xf00000;
505*1a646342SBen Skeggs 			tv_regs->ptv_614 = 0x13;
506*1a646342SBen Skeggs 		}
507*1a646342SBen Skeggs 
508*1a646342SBen Skeggs 		if (nv_device(drm->device)->card_type >= NV_30) {
509*1a646342SBen Skeggs 			tv_regs->ptv_500 = 0xe8e0;
510*1a646342SBen Skeggs 			tv_regs->ptv_504 = 0x1710;
511*1a646342SBen Skeggs 			tv_regs->ptv_604 = 0x0;
512*1a646342SBen Skeggs 			tv_regs->ptv_608 = 0x0;
513*1a646342SBen Skeggs 		} else {
514*1a646342SBen Skeggs 			if (tv_norm->tv_enc_mode.vdisplay == 576) {
515*1a646342SBen Skeggs 				tv_regs->ptv_604 = 0x20;
516*1a646342SBen Skeggs 				tv_regs->ptv_608 = 0x10;
517*1a646342SBen Skeggs 				tv_regs->ptv_500 = 0x19710;
518*1a646342SBen Skeggs 				tv_regs->ptv_504 = 0x68f0;
519*1a646342SBen Skeggs 
520*1a646342SBen Skeggs 			} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
521*1a646342SBen Skeggs 				tv_regs->ptv_604 = 0x10;
522*1a646342SBen Skeggs 				tv_regs->ptv_608 = 0x20;
523*1a646342SBen Skeggs 				tv_regs->ptv_500 = 0x4b90;
524*1a646342SBen Skeggs 				tv_regs->ptv_504 = 0x1b480;
525*1a646342SBen Skeggs 			}
526*1a646342SBen Skeggs 		}
527*1a646342SBen Skeggs 
528*1a646342SBen Skeggs 		for (i = 0; i < 0x40; i++)
529*1a646342SBen Skeggs 			tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
530*1a646342SBen Skeggs 
531*1a646342SBen Skeggs 	} else {
532*1a646342SBen Skeggs 		struct drm_display_mode *output_mode =
533*1a646342SBen Skeggs 						&tv_norm->ctv_enc_mode.mode;
534*1a646342SBen Skeggs 
535*1a646342SBen Skeggs 		/* The registers in PRAMDAC+0xc00 control some timings and CSC
536*1a646342SBen Skeggs 		 * parameters for the CTV encoder (It's only used for "HD" TV
537*1a646342SBen Skeggs 		 * modes, I don't think I have enough working to guess what
538*1a646342SBen Skeggs 		 * they exactly mean...), it's probably connected at the
539*1a646342SBen Skeggs 		 * output of the FP encoder, but it also needs the analog
540*1a646342SBen Skeggs 		 * encoder in its OR enabled and routed to the head it's
541*1a646342SBen Skeggs 		 * using. It's enabled with the DACCLK register, bits [5:4].
542*1a646342SBen Skeggs 		 */
543*1a646342SBen Skeggs 		for (i = 0; i < 38; i++)
544*1a646342SBen Skeggs 			regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
545*1a646342SBen Skeggs 
546*1a646342SBen Skeggs 		regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
547*1a646342SBen Skeggs 		regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
548*1a646342SBen Skeggs 		regs->fp_horiz_regs[FP_SYNC_START] =
549*1a646342SBen Skeggs 						output_mode->hsync_start - 1;
550*1a646342SBen Skeggs 		regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
551*1a646342SBen Skeggs 		regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
552*1a646342SBen Skeggs 			max((output_mode->hdisplay-600)/40 - 1, 1);
553*1a646342SBen Skeggs 
554*1a646342SBen Skeggs 		regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
555*1a646342SBen Skeggs 		regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
556*1a646342SBen Skeggs 		regs->fp_vert_regs[FP_SYNC_START] =
557*1a646342SBen Skeggs 						output_mode->vsync_start - 1;
558*1a646342SBen Skeggs 		regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
559*1a646342SBen Skeggs 		regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
560*1a646342SBen Skeggs 
561*1a646342SBen Skeggs 		regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
562*1a646342SBen Skeggs 			NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
563*1a646342SBen Skeggs 			NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
564*1a646342SBen Skeggs 
565*1a646342SBen Skeggs 		if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
566*1a646342SBen Skeggs 			regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
567*1a646342SBen Skeggs 		if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
568*1a646342SBen Skeggs 			regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
569*1a646342SBen Skeggs 
570*1a646342SBen Skeggs 		regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
571*1a646342SBen Skeggs 			NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
572*1a646342SBen Skeggs 			NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
573*1a646342SBen Skeggs 			NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
574*1a646342SBen Skeggs 			NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
575*1a646342SBen Skeggs 			NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
576*1a646342SBen Skeggs 			NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
577*1a646342SBen Skeggs 
578*1a646342SBen Skeggs 		regs->fp_debug_2 = 0;
579*1a646342SBen Skeggs 
580*1a646342SBen Skeggs 		regs->fp_margin_color = 0x801080;
581*1a646342SBen Skeggs 
582*1a646342SBen Skeggs 	}
583*1a646342SBen Skeggs }
584*1a646342SBen Skeggs 
585*1a646342SBen Skeggs static void nv17_tv_commit(struct drm_encoder *encoder)
586*1a646342SBen Skeggs {
587*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
588*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
589*1a646342SBen Skeggs 	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
590*1a646342SBen Skeggs 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
591*1a646342SBen Skeggs 	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
592*1a646342SBen Skeggs 
593*1a646342SBen Skeggs 	if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
594*1a646342SBen Skeggs 		nv17_tv_update_rescaler(encoder);
595*1a646342SBen Skeggs 		nv17_tv_update_properties(encoder);
596*1a646342SBen Skeggs 	} else {
597*1a646342SBen Skeggs 		nv17_ctv_update_rescaler(encoder);
598*1a646342SBen Skeggs 	}
599*1a646342SBen Skeggs 
600*1a646342SBen Skeggs 	nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
601*1a646342SBen Skeggs 
602*1a646342SBen Skeggs 	/* This could use refinement for flatpanels, but it should work */
603*1a646342SBen Skeggs 	if (nv_device(drm->device)->chipset < 0x44)
604*1a646342SBen Skeggs 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
605*1a646342SBen Skeggs 					nv04_dac_output_offset(encoder),
606*1a646342SBen Skeggs 					0xf0000000);
607*1a646342SBen Skeggs 	else
608*1a646342SBen Skeggs 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
609*1a646342SBen Skeggs 					nv04_dac_output_offset(encoder),
610*1a646342SBen Skeggs 					0x00100000);
611*1a646342SBen Skeggs 
612*1a646342SBen Skeggs 	helper->dpms(encoder, DRM_MODE_DPMS_ON);
613*1a646342SBen Skeggs 
614*1a646342SBen Skeggs 	NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n",
615*1a646342SBen Skeggs 		drm_get_connector_name(
616*1a646342SBen Skeggs 			&nouveau_encoder_connector_get(nv_encoder)->base),
617*1a646342SBen Skeggs 		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
618*1a646342SBen Skeggs }
619*1a646342SBen Skeggs 
620*1a646342SBen Skeggs static void nv17_tv_save(struct drm_encoder *encoder)
621*1a646342SBen Skeggs {
622*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
623*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
624*1a646342SBen Skeggs 
625*1a646342SBen Skeggs 	nouveau_encoder(encoder)->restore.output =
626*1a646342SBen Skeggs 					NVReadRAMDAC(dev, 0,
627*1a646342SBen Skeggs 					NV_PRAMDAC_DACCLK +
628*1a646342SBen Skeggs 					nv04_dac_output_offset(encoder));
629*1a646342SBen Skeggs 
630*1a646342SBen Skeggs 	nv17_tv_state_save(dev, &tv_enc->saved_state);
631*1a646342SBen Skeggs 
632*1a646342SBen Skeggs 	tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
633*1a646342SBen Skeggs }
634*1a646342SBen Skeggs 
635*1a646342SBen Skeggs static void nv17_tv_restore(struct drm_encoder *encoder)
636*1a646342SBen Skeggs {
637*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
638*1a646342SBen Skeggs 
639*1a646342SBen Skeggs 	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
640*1a646342SBen Skeggs 				nv04_dac_output_offset(encoder),
641*1a646342SBen Skeggs 				nouveau_encoder(encoder)->restore.output);
642*1a646342SBen Skeggs 
643*1a646342SBen Skeggs 	nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
644*1a646342SBen Skeggs 
645*1a646342SBen Skeggs 	nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED;
646*1a646342SBen Skeggs }
647*1a646342SBen Skeggs 
648*1a646342SBen Skeggs static int nv17_tv_create_resources(struct drm_encoder *encoder,
649*1a646342SBen Skeggs 				    struct drm_connector *connector)
650*1a646342SBen Skeggs {
651*1a646342SBen Skeggs 	struct drm_device *dev = encoder->dev;
652*1a646342SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
653*1a646342SBen Skeggs 	struct drm_mode_config *conf = &dev->mode_config;
654*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
655*1a646342SBen Skeggs 	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
656*1a646342SBen Skeggs 	int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
657*1a646342SBen Skeggs 							NUM_LD_TV_NORMS;
658*1a646342SBen Skeggs 	int i;
659*1a646342SBen Skeggs 
660*1a646342SBen Skeggs 	if (nouveau_tv_norm) {
661*1a646342SBen Skeggs 		for (i = 0; i < num_tv_norms; i++) {
662*1a646342SBen Skeggs 			if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
663*1a646342SBen Skeggs 				tv_enc->tv_norm = i;
664*1a646342SBen Skeggs 				break;
665*1a646342SBen Skeggs 			}
666*1a646342SBen Skeggs 		}
667*1a646342SBen Skeggs 
668*1a646342SBen Skeggs 		if (i == num_tv_norms)
669*1a646342SBen Skeggs 			NV_WARN(drm, "Invalid TV norm setting \"%s\"\n",
670*1a646342SBen Skeggs 				nouveau_tv_norm);
671*1a646342SBen Skeggs 	}
672*1a646342SBen Skeggs 
673*1a646342SBen Skeggs 	drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
674*1a646342SBen Skeggs 
675*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
676*1a646342SBen Skeggs 					conf->tv_select_subconnector_property,
677*1a646342SBen Skeggs 					tv_enc->select_subconnector);
678*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
679*1a646342SBen Skeggs 					conf->tv_subconnector_property,
680*1a646342SBen Skeggs 					tv_enc->subconnector);
681*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
682*1a646342SBen Skeggs 					conf->tv_mode_property,
683*1a646342SBen Skeggs 					tv_enc->tv_norm);
684*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
685*1a646342SBen Skeggs 					conf->tv_flicker_reduction_property,
686*1a646342SBen Skeggs 					tv_enc->flicker);
687*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
688*1a646342SBen Skeggs 					conf->tv_saturation_property,
689*1a646342SBen Skeggs 					tv_enc->saturation);
690*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
691*1a646342SBen Skeggs 					conf->tv_hue_property,
692*1a646342SBen Skeggs 					tv_enc->hue);
693*1a646342SBen Skeggs 	drm_object_attach_property(&connector->base,
694*1a646342SBen Skeggs 					conf->tv_overscan_property,
695*1a646342SBen Skeggs 					tv_enc->overscan);
696*1a646342SBen Skeggs 
697*1a646342SBen Skeggs 	return 0;
698*1a646342SBen Skeggs }
699*1a646342SBen Skeggs 
700*1a646342SBen Skeggs static int nv17_tv_set_property(struct drm_encoder *encoder,
701*1a646342SBen Skeggs 				struct drm_connector *connector,
702*1a646342SBen Skeggs 				struct drm_property *property,
703*1a646342SBen Skeggs 				uint64_t val)
704*1a646342SBen Skeggs {
705*1a646342SBen Skeggs 	struct drm_mode_config *conf = &encoder->dev->mode_config;
706*1a646342SBen Skeggs 	struct drm_crtc *crtc = encoder->crtc;
707*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
708*1a646342SBen Skeggs 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
709*1a646342SBen Skeggs 	bool modes_changed = false;
710*1a646342SBen Skeggs 
711*1a646342SBen Skeggs 	if (property == conf->tv_overscan_property) {
712*1a646342SBen Skeggs 		tv_enc->overscan = val;
713*1a646342SBen Skeggs 		if (encoder->crtc) {
714*1a646342SBen Skeggs 			if (tv_norm->kind == CTV_ENC_MODE)
715*1a646342SBen Skeggs 				nv17_ctv_update_rescaler(encoder);
716*1a646342SBen Skeggs 			else
717*1a646342SBen Skeggs 				nv17_tv_update_rescaler(encoder);
718*1a646342SBen Skeggs 		}
719*1a646342SBen Skeggs 
720*1a646342SBen Skeggs 	} else if (property == conf->tv_saturation_property) {
721*1a646342SBen Skeggs 		if (tv_norm->kind != TV_ENC_MODE)
722*1a646342SBen Skeggs 			return -EINVAL;
723*1a646342SBen Skeggs 
724*1a646342SBen Skeggs 		tv_enc->saturation = val;
725*1a646342SBen Skeggs 		nv17_tv_update_properties(encoder);
726*1a646342SBen Skeggs 
727*1a646342SBen Skeggs 	} else if (property == conf->tv_hue_property) {
728*1a646342SBen Skeggs 		if (tv_norm->kind != TV_ENC_MODE)
729*1a646342SBen Skeggs 			return -EINVAL;
730*1a646342SBen Skeggs 
731*1a646342SBen Skeggs 		tv_enc->hue = val;
732*1a646342SBen Skeggs 		nv17_tv_update_properties(encoder);
733*1a646342SBen Skeggs 
734*1a646342SBen Skeggs 	} else if (property == conf->tv_flicker_reduction_property) {
735*1a646342SBen Skeggs 		if (tv_norm->kind != TV_ENC_MODE)
736*1a646342SBen Skeggs 			return -EINVAL;
737*1a646342SBen Skeggs 
738*1a646342SBen Skeggs 		tv_enc->flicker = val;
739*1a646342SBen Skeggs 		if (encoder->crtc)
740*1a646342SBen Skeggs 			nv17_tv_update_rescaler(encoder);
741*1a646342SBen Skeggs 
742*1a646342SBen Skeggs 	} else if (property == conf->tv_mode_property) {
743*1a646342SBen Skeggs 		if (connector->dpms != DRM_MODE_DPMS_OFF)
744*1a646342SBen Skeggs 			return -EINVAL;
745*1a646342SBen Skeggs 
746*1a646342SBen Skeggs 		tv_enc->tv_norm = val;
747*1a646342SBen Skeggs 
748*1a646342SBen Skeggs 		modes_changed = true;
749*1a646342SBen Skeggs 
750*1a646342SBen Skeggs 	} else if (property == conf->tv_select_subconnector_property) {
751*1a646342SBen Skeggs 		if (tv_norm->kind != TV_ENC_MODE)
752*1a646342SBen Skeggs 			return -EINVAL;
753*1a646342SBen Skeggs 
754*1a646342SBen Skeggs 		tv_enc->select_subconnector = val;
755*1a646342SBen Skeggs 		nv17_tv_update_properties(encoder);
756*1a646342SBen Skeggs 
757*1a646342SBen Skeggs 	} else {
758*1a646342SBen Skeggs 		return -EINVAL;
759*1a646342SBen Skeggs 	}
760*1a646342SBen Skeggs 
761*1a646342SBen Skeggs 	if (modes_changed) {
762*1a646342SBen Skeggs 		drm_helper_probe_single_connector_modes(connector, 0, 0);
763*1a646342SBen Skeggs 
764*1a646342SBen Skeggs 		/* Disable the crtc to ensure a full modeset is
765*1a646342SBen Skeggs 		 * performed whenever it's turned on again. */
766*1a646342SBen Skeggs 		if (crtc) {
767*1a646342SBen Skeggs 			struct drm_mode_set modeset = {
768*1a646342SBen Skeggs 				.crtc = crtc,
769*1a646342SBen Skeggs 			};
770*1a646342SBen Skeggs 
771*1a646342SBen Skeggs 			drm_mode_set_config_internal(&modeset);
772*1a646342SBen Skeggs 		}
773*1a646342SBen Skeggs 	}
774*1a646342SBen Skeggs 
775*1a646342SBen Skeggs 	return 0;
776*1a646342SBen Skeggs }
777*1a646342SBen Skeggs 
778*1a646342SBen Skeggs static void nv17_tv_destroy(struct drm_encoder *encoder)
779*1a646342SBen Skeggs {
780*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
781*1a646342SBen Skeggs 
782*1a646342SBen Skeggs 	drm_encoder_cleanup(encoder);
783*1a646342SBen Skeggs 	kfree(tv_enc);
784*1a646342SBen Skeggs }
785*1a646342SBen Skeggs 
786*1a646342SBen Skeggs static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
787*1a646342SBen Skeggs 	.dpms = nv17_tv_dpms,
788*1a646342SBen Skeggs 	.save = nv17_tv_save,
789*1a646342SBen Skeggs 	.restore = nv17_tv_restore,
790*1a646342SBen Skeggs 	.mode_fixup = nv17_tv_mode_fixup,
791*1a646342SBen Skeggs 	.prepare = nv17_tv_prepare,
792*1a646342SBen Skeggs 	.commit = nv17_tv_commit,
793*1a646342SBen Skeggs 	.mode_set = nv17_tv_mode_set,
794*1a646342SBen Skeggs 	.detect = nv17_tv_detect,
795*1a646342SBen Skeggs };
796*1a646342SBen Skeggs 
797*1a646342SBen Skeggs static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
798*1a646342SBen Skeggs 	.get_modes = nv17_tv_get_modes,
799*1a646342SBen Skeggs 	.mode_valid = nv17_tv_mode_valid,
800*1a646342SBen Skeggs 	.create_resources = nv17_tv_create_resources,
801*1a646342SBen Skeggs 	.set_property = nv17_tv_set_property,
802*1a646342SBen Skeggs };
803*1a646342SBen Skeggs 
804*1a646342SBen Skeggs static struct drm_encoder_funcs nv17_tv_funcs = {
805*1a646342SBen Skeggs 	.destroy = nv17_tv_destroy,
806*1a646342SBen Skeggs };
807*1a646342SBen Skeggs 
808*1a646342SBen Skeggs int
809*1a646342SBen Skeggs nv17_tv_create(struct drm_connector *connector, struct dcb_output *entry)
810*1a646342SBen Skeggs {
811*1a646342SBen Skeggs 	struct drm_device *dev = connector->dev;
812*1a646342SBen Skeggs 	struct drm_encoder *encoder;
813*1a646342SBen Skeggs 	struct nv17_tv_encoder *tv_enc = NULL;
814*1a646342SBen Skeggs 
815*1a646342SBen Skeggs 	tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
816*1a646342SBen Skeggs 	if (!tv_enc)
817*1a646342SBen Skeggs 		return -ENOMEM;
818*1a646342SBen Skeggs 
819*1a646342SBen Skeggs 	tv_enc->overscan = 50;
820*1a646342SBen Skeggs 	tv_enc->flicker = 50;
821*1a646342SBen Skeggs 	tv_enc->saturation = 50;
822*1a646342SBen Skeggs 	tv_enc->hue = 0;
823*1a646342SBen Skeggs 	tv_enc->tv_norm = TV_NORM_PAL;
824*1a646342SBen Skeggs 	tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
825*1a646342SBen Skeggs 	tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
826*1a646342SBen Skeggs 	tv_enc->pin_mask = 0;
827*1a646342SBen Skeggs 
828*1a646342SBen Skeggs 	encoder = to_drm_encoder(&tv_enc->base);
829*1a646342SBen Skeggs 
830*1a646342SBen Skeggs 	tv_enc->base.dcb = entry;
831*1a646342SBen Skeggs 	tv_enc->base.or = ffs(entry->or) - 1;
832*1a646342SBen Skeggs 
833*1a646342SBen Skeggs 	drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
834*1a646342SBen Skeggs 	drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
835*1a646342SBen Skeggs 	to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
836*1a646342SBen Skeggs 
837*1a646342SBen Skeggs 	encoder->possible_crtcs = entry->heads;
838*1a646342SBen Skeggs 	encoder->possible_clones = 0;
839*1a646342SBen Skeggs 
840*1a646342SBen Skeggs 	nv17_tv_create_resources(encoder, connector);
841*1a646342SBen Skeggs 	drm_mode_connector_attach_encoder(connector, encoder);
842*1a646342SBen Skeggs 	return 0;
843*1a646342SBen Skeggs }
844