xref: /linux/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c (revision e58e871becec2d3b04ed91c0c16fe8deac9c9dfa)
1 /*
2  * Copyright (C) 2009 Francisco Jerez.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26 
27 #include <drm/drmP.h>
28 #include <drm/drm_crtc_helper.h>
29 #include "nouveau_drv.h"
30 #include "nouveau_encoder.h"
31 #include "nouveau_crtc.h"
32 #include "hw.h"
33 #include "tvnv17.h"
34 
35 const char * const nv17_tv_norm_names[NUM_TV_NORMS] = {
36 	[TV_NORM_PAL] = "PAL",
37 	[TV_NORM_PAL_M] = "PAL-M",
38 	[TV_NORM_PAL_N] = "PAL-N",
39 	[TV_NORM_PAL_NC] = "PAL-Nc",
40 	[TV_NORM_NTSC_M] = "NTSC-M",
41 	[TV_NORM_NTSC_J] = "NTSC-J",
42 	[TV_NORM_HD480I] = "hd480i",
43 	[TV_NORM_HD480P] = "hd480p",
44 	[TV_NORM_HD576I] = "hd576i",
45 	[TV_NORM_HD576P] = "hd576p",
46 	[TV_NORM_HD720P] = "hd720p",
47 	[TV_NORM_HD1080I] = "hd1080i"
48 };
49 
50 /* TV standard specific parameters */
51 
52 struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
53 	[TV_NORM_PAL] = { TV_ENC_MODE, {
54 			.tv_enc_mode = { 720, 576, 50000, {
55 					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
56 					0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
57 					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
58 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
59 					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
60 					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
61 					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
62 					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
63 				} } } },
64 
65 	[TV_NORM_PAL_M] = { TV_ENC_MODE, {
66 			.tv_enc_mode = { 720, 480, 59940, {
67 					0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
68 					0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
69 					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
70 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
71 					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
72 					0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
73 					0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
74 					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
75 				} } } },
76 
77 	[TV_NORM_PAL_N] = { TV_ENC_MODE, {
78 			.tv_enc_mode = { 720, 576, 50000, {
79 					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
80 					0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
81 					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
82 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
83 					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
84 					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
85 					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
86 					0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
87 				} } } },
88 
89 	[TV_NORM_PAL_NC] = { TV_ENC_MODE, {
90 			.tv_enc_mode = { 720, 576, 50000, {
91 					0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
92 					0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
93 					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
94 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
95 					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
96 					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
97 					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
98 					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
99 				} } } },
100 
101 	[TV_NORM_NTSC_M] = { TV_ENC_MODE, {
102 			.tv_enc_mode = { 720, 480, 59940, {
103 					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
104 					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
105 					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
106 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
107 					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
108 					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
109 					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
110 					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
111 				} } } },
112 
113 	[TV_NORM_NTSC_J] = { TV_ENC_MODE, {
114 			.tv_enc_mode = { 720, 480, 59940, {
115 					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
116 					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
117 					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
118 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
119 					0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
120 					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
121 					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
122 					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
123 				} } } },
124 
125 	[TV_NORM_HD480I] = { TV_ENC_MODE, {
126 			.tv_enc_mode = { 720, 480, 59940, {
127 					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
128 					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
129 					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
130 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
131 					0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
132 					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
133 					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
134 					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
135 				} } } },
136 
137 	[TV_NORM_HD576I] = { TV_ENC_MODE, {
138 			.tv_enc_mode = { 720, 576, 50000, {
139 					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
140 					0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
141 					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
142 					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
143 					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
144 					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
145 					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
146 					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
147 				} } } },
148 
149 
150 	[TV_NORM_HD480P] = { CTV_ENC_MODE, {
151 			.ctv_enc_mode = {
152 				.mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000,
153 						   720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
154 						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
155 				.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
156 					      0x354003a, 0x40000, 0x6f0344, 0x18100000,
157 					      0x10160004, 0x10060005, 0x1006000c, 0x10060020,
158 					      0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
159 					      0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
160 					      0x10000fff, 0x10000fff, 0x10000fff, 0x70,
161 					      0x3ff0000, 0x57, 0x2e001e, 0x258012c,
162 					      0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
163 					      0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
164 				} } } },
165 
166 	[TV_NORM_HD576P] = { CTV_ENC_MODE, {
167 			.ctv_enc_mode = {
168 				.mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000,
169 						   720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
170 						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
171 				.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
172 					      0x354003a, 0x40000, 0x6f0344, 0x18100000,
173 					      0x10060001, 0x10060009, 0x10060026, 0x10060027,
174 					      0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
175 					      0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
176 					      0x10000fff, 0x10000fff, 0x10000fff, 0x69,
177 					      0x3ff0000, 0x57, 0x2e001e, 0x258012c,
178 					      0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
179 					      0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
180 				} } } },
181 
182 	[TV_NORM_HD720P] = { CTV_ENC_MODE, {
183 			.ctv_enc_mode = {
184 				.mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
185 						   1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
186 						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
187 				.ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
188 					      0x66b0021, 0x6004a, 0x1210626, 0x8170000,
189 					      0x70004, 0x70016, 0x70017, 0x40f0018,
190 					      0x702e8, 0x81702ed, 0xfff, 0xfff,
191 					      0xfff, 0xfff, 0xfff, 0xfff,
192 					      0xfff, 0xfff, 0xfff, 0x0,
193 					      0x2e40001, 0x58, 0x2e001e, 0x258012c,
194 					      0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
195 					      0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
196 				} } } },
197 
198 	[TV_NORM_HD1080I] = { CTV_ENC_MODE, {
199 			.ctv_enc_mode = {
200 				.mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250,
201 						   1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
202 						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
203 						   | DRM_MODE_FLAG_INTERLACE) },
204 				.ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
205 					      0x8940028, 0x60054, 0xe80870, 0xbf70000,
206 					      0xbc70004, 0x70005, 0x70012, 0x70013,
207 					      0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
208 					      0x1c70237, 0x70238, 0x70244, 0x70245,
209 					      0x40f0246, 0x70462, 0x1f70464, 0x0,
210 					      0x2e40001, 0x58, 0x2e001e, 0x258012c,
211 					      0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
212 					      0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
213 				} } } }
214 };
215 
216 /*
217  * The following is some guesswork on how the TV encoder flicker
218  * filter/rescaler works:
219  *
220  * It seems to use some sort of resampling filter, it is controlled
221  * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
222  * control the horizontal and vertical stage respectively, there is
223  * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
224  * but they seem to do nothing. A rough guess might be that they could
225  * be used to independently control the filtering of each interlaced
226  * field, but I don't know how they are enabled. The whole filtering
227  * process seems to be disabled with bits 26:27 of PTV_200, but we
228  * aren't doing that.
229  *
230  * The layout of both register sets is the same:
231  *
232  * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
233  * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
234  *
235  * Each coefficient is stored in bits [31],[15:9] in two's complement
236  * format. They seem to be some kind of weights used in a low-pass
237  * filter. Both A and B coefficients are applied to the 14 nearest
238  * samples on each side (Listed from nearest to furthermost.  They
239  * roughly cover 2 framebuffer pixels on each side).  They are
240  * probably multiplied with some more hardwired weights before being
241  * used: B-coefficients are applied the same on both sides,
242  * A-coefficients are inverted before being applied to the opposite
243  * side.
244  *
245  * After all the hassle, I got the following formula by empirical
246  * means...
247  */
248 
249 #define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
250 
251 #define id1 (1LL << 8)
252 #define id2 (1LL << 16)
253 #define id3 (1LL << 24)
254 #define id4 (1LL << 32)
255 #define id5 (1LL << 48)
256 
257 static struct filter_params{
258 	int64_t k1;
259 	int64_t ki;
260 	int64_t ki2;
261 	int64_t ki3;
262 	int64_t kr;
263 	int64_t kir;
264 	int64_t ki2r;
265 	int64_t ki3r;
266 	int64_t kf;
267 	int64_t kif;
268 	int64_t ki2f;
269 	int64_t ki3f;
270 	int64_t krf;
271 	int64_t kirf;
272 	int64_t ki2rf;
273 	int64_t ki3rf;
274 } fparams[2][4] = {
275 	/* Horizontal filter parameters */
276 	{
277 		{64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
278 		 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
279 		 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
280 		 -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
281 		{-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
282 		 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
283 		 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
284 		 -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
285 		{-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
286 		 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
287 		 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
288 		 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
289 		{51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
290 		 -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
291 		 -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
292 		 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
293 	},
294 
295 	/* Vertical filter parameters */
296 	{
297 		{67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
298 		 -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
299 		 -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
300 		 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
301 		{6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
302 		 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
303 		 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
304 		 -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
305 		{-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
306 		 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
307 		 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
308 		 -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
309 		{-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
310 		 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
311 		 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
312 		 -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
313 	}
314 };
315 
316 static void tv_setup_filter(struct drm_encoder *encoder)
317 {
318 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
319 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
320 	struct drm_display_mode *mode = &encoder->crtc->mode;
321 	uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
322 				       &tv_enc->state.vfilter};
323 	int i, j, k;
324 	int32_t overscan = calc_overscan(tv_enc->overscan);
325 	int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100);
326 	uint64_t rs[] = {mode->hdisplay * id3,
327 			 mode->vdisplay * id3};
328 
329 	do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay);
330 	do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay);
331 
332 	for (k = 0; k < 2; k++) {
333 		rs[k] = max((int64_t)rs[k], id2);
334 
335 		for (j = 0; j < 4; j++) {
336 			struct filter_params *p = &fparams[k][j];
337 
338 			for (i = 0; i < 7; i++) {
339 				int64_t c = (p->k1 + p->ki*i + p->ki2*i*i +
340 					     p->ki3*i*i*i)
341 					+ (p->kr + p->kir*i + p->ki2r*i*i +
342 					   p->ki3r*i*i*i) * rs[k]
343 					+ (p->kf + p->kif*i + p->ki2f*i*i +
344 					   p->ki3f*i*i*i) * flicker
345 					+ (p->krf + p->kirf*i + p->ki2rf*i*i +
346 					   p->ki3rf*i*i*i) * flicker * rs[k];
347 
348 				(*filters[k])[j][i] = (c + id5/2) >> 39
349 					& (0x1 << 31 | 0x7f << 9);
350 			}
351 		}
352 	}
353 }
354 
355 /* Hardware state saving/restoring */
356 
357 static void tv_save_filter(struct drm_device *dev, uint32_t base,
358 			   uint32_t regs[4][7])
359 {
360 	int i, j;
361 	uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
362 
363 	for (i = 0; i < 4; i++) {
364 		for (j = 0; j < 7; j++)
365 			regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
366 	}
367 }
368 
369 static void tv_load_filter(struct drm_device *dev, uint32_t base,
370 			   uint32_t regs[4][7])
371 {
372 	int i, j;
373 	uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
374 
375 	for (i = 0; i < 4; i++) {
376 		for (j = 0; j < 7; j++)
377 			nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
378 	}
379 }
380 
381 void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
382 {
383 	int i;
384 
385 	for (i = 0; i < 0x40; i++)
386 		state->tv_enc[i] = nv_read_tv_enc(dev, i);
387 
388 	tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
389 	tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
390 	tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
391 
392 	nv_save_ptv(dev, state, 200);
393 	nv_save_ptv(dev, state, 204);
394 	nv_save_ptv(dev, state, 208);
395 	nv_save_ptv(dev, state, 20c);
396 	nv_save_ptv(dev, state, 304);
397 	nv_save_ptv(dev, state, 500);
398 	nv_save_ptv(dev, state, 504);
399 	nv_save_ptv(dev, state, 508);
400 	nv_save_ptv(dev, state, 600);
401 	nv_save_ptv(dev, state, 604);
402 	nv_save_ptv(dev, state, 608);
403 	nv_save_ptv(dev, state, 60c);
404 	nv_save_ptv(dev, state, 610);
405 	nv_save_ptv(dev, state, 614);
406 }
407 
408 void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
409 {
410 	int i;
411 
412 	for (i = 0; i < 0x40; i++)
413 		nv_write_tv_enc(dev, i, state->tv_enc[i]);
414 
415 	tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
416 	tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
417 	tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
418 
419 	nv_load_ptv(dev, state, 200);
420 	nv_load_ptv(dev, state, 204);
421 	nv_load_ptv(dev, state, 208);
422 	nv_load_ptv(dev, state, 20c);
423 	nv_load_ptv(dev, state, 304);
424 	nv_load_ptv(dev, state, 500);
425 	nv_load_ptv(dev, state, 504);
426 	nv_load_ptv(dev, state, 508);
427 	nv_load_ptv(dev, state, 600);
428 	nv_load_ptv(dev, state, 604);
429 	nv_load_ptv(dev, state, 608);
430 	nv_load_ptv(dev, state, 60c);
431 	nv_load_ptv(dev, state, 610);
432 	nv_load_ptv(dev, state, 614);
433 
434 	/* This is required for some settings to kick in. */
435 	nv_write_tv_enc(dev, 0x3e, 1);
436 	nv_write_tv_enc(dev, 0x3e, 0);
437 }
438 
439 /* Timings similar to the ones the blob sets */
440 
441 const struct drm_display_mode nv17_tv_modes[] = {
442 	{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
443 		   320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
444 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
445 		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
446 	{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
447 		   320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
448 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
449 		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
450 	{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
451 		   400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
452 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
453 		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
454 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
455 		   640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
456 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
457 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
458 		   720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
459 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
460 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
461 		   720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
462 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
463 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
464 		   800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
465 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
466 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
467 		   1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
468 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
469 	{}
470 };
471 
472 void nv17_tv_update_properties(struct drm_encoder *encoder)
473 {
474 	struct drm_device *dev = encoder->dev;
475 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
476 	struct nv17_tv_state *regs = &tv_enc->state;
477 	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
478 	int subconnector = tv_enc->select_subconnector ?
479 						tv_enc->select_subconnector :
480 						tv_enc->subconnector;
481 
482 	switch (subconnector) {
483 	case DRM_MODE_SUBCONNECTOR_Composite:
484 	{
485 		regs->ptv_204 = 0x2;
486 
487 		/* The composite connector may be found on either pin. */
488 		if (tv_enc->pin_mask & 0x4)
489 			regs->ptv_204 |= 0x010000;
490 		else if (tv_enc->pin_mask & 0x2)
491 			regs->ptv_204 |= 0x100000;
492 		else
493 			regs->ptv_204 |= 0x110000;
494 
495 		regs->tv_enc[0x7] = 0x10;
496 		break;
497 	}
498 	case DRM_MODE_SUBCONNECTOR_SVIDEO:
499 		regs->ptv_204 = 0x11012;
500 		regs->tv_enc[0x7] = 0x18;
501 		break;
502 
503 	case DRM_MODE_SUBCONNECTOR_Component:
504 		regs->ptv_204 = 0x111333;
505 		regs->tv_enc[0x7] = 0x14;
506 		break;
507 
508 	case DRM_MODE_SUBCONNECTOR_SCART:
509 		regs->ptv_204 = 0x111012;
510 		regs->tv_enc[0x7] = 0x18;
511 		break;
512 	}
513 
514 	regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20],
515 					 255, tv_enc->saturation);
516 	regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22],
517 					 255, tv_enc->saturation);
518 	regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
519 
520 	nv_load_ptv(dev, regs, 204);
521 	nv_load_tv_enc(dev, regs, 7);
522 	nv_load_tv_enc(dev, regs, 20);
523 	nv_load_tv_enc(dev, regs, 22);
524 	nv_load_tv_enc(dev, regs, 25);
525 }
526 
527 void nv17_tv_update_rescaler(struct drm_encoder *encoder)
528 {
529 	struct drm_device *dev = encoder->dev;
530 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
531 	struct nv17_tv_state *regs = &tv_enc->state;
532 
533 	regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
534 
535 	tv_setup_filter(encoder);
536 
537 	nv_load_ptv(dev, regs, 208);
538 	tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
539 	tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
540 	tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
541 }
542 
543 void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
544 {
545 	struct drm_device *dev = encoder->dev;
546 	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
547 	int head = nouveau_crtc(encoder->crtc)->index;
548 	struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head];
549 	struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
550 	struct drm_display_mode *output_mode =
551 		&get_tv_norm(encoder)->ctv_enc_mode.mode;
552 	int overscan, hmargin, vmargin, hratio, vratio;
553 
554 	/* The rescaler doesn't do the right thing for interlaced modes. */
555 	if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
556 		overscan = 100;
557 	else
558 		overscan = tv_enc->overscan;
559 
560 	hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
561 	vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
562 
563 	hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20),
564 			      hmargin, overscan);
565 	vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20),
566 			      vmargin, overscan);
567 
568 	hratio = crtc_mode->hdisplay * 0x800 /
569 		(output_mode->hdisplay - 2*hmargin);
570 	vratio = crtc_mode->vdisplay * 0x800 /
571 		(output_mode->vdisplay - 2*vmargin) & ~3;
572 
573 	regs->fp_horiz_regs[FP_VALID_START] = hmargin;
574 	regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
575 	regs->fp_vert_regs[FP_VALID_START] = vmargin;
576 	regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
577 
578 	regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
579 		XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
580 		NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
581 		XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
582 
583 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
584 		      regs->fp_horiz_regs[FP_VALID_START]);
585 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
586 		      regs->fp_horiz_regs[FP_VALID_END]);
587 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
588 		      regs->fp_vert_regs[FP_VALID_START]);
589 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
590 		      regs->fp_vert_regs[FP_VALID_END]);
591 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
592 }
593