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
33const 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
50struct 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
255static 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
314static 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
355static 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
367static 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
379void 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
406void 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
439const 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
470void 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
525void 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
541void 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