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