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