• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/gpu/drm/nouveau/
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 "drmP.h"
28#include "drm_crtc_helper.h"
29#include "nouveau_drv.h"
30#include "nouveau_encoder.h"
31#include "nouveau_crtc.h"
32#include "nouveau_hw.h"
33#include "nv17_tv.h"
34
35char *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
52struct 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
257static 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
316static 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 + p->ki3*i*i*i)
340					+ (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
341					+ (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
342					+ (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
343
344				(*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
345			}
346		}
347	}
348}
349
350/* Hardware state saving/restoring */
351
352static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
353{
354	int i, j;
355	uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
356
357	for (i = 0; i < 4; i++) {
358		for (j = 0; j < 7; j++)
359			regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
360	}
361}
362
363static void tv_load_filter(struct drm_device *dev, uint32_t base, 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			nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
371	}
372}
373
374void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
375{
376	int i;
377
378	for (i = 0; i < 0x40; i++)
379		state->tv_enc[i] = nv_read_tv_enc(dev, i);
380
381	tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
382	tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
383	tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
384
385	nv_save_ptv(dev, state, 200);
386	nv_save_ptv(dev, state, 204);
387	nv_save_ptv(dev, state, 208);
388	nv_save_ptv(dev, state, 20c);
389	nv_save_ptv(dev, state, 304);
390	nv_save_ptv(dev, state, 500);
391	nv_save_ptv(dev, state, 504);
392	nv_save_ptv(dev, state, 508);
393	nv_save_ptv(dev, state, 600);
394	nv_save_ptv(dev, state, 604);
395	nv_save_ptv(dev, state, 608);
396	nv_save_ptv(dev, state, 60c);
397	nv_save_ptv(dev, state, 610);
398	nv_save_ptv(dev, state, 614);
399}
400
401void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
402{
403	int i;
404
405	for (i = 0; i < 0x40; i++)
406		nv_write_tv_enc(dev, i, state->tv_enc[i]);
407
408	tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
409	tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
410	tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
411
412	nv_load_ptv(dev, state, 200);
413	nv_load_ptv(dev, state, 204);
414	nv_load_ptv(dev, state, 208);
415	nv_load_ptv(dev, state, 20c);
416	nv_load_ptv(dev, state, 304);
417	nv_load_ptv(dev, state, 500);
418	nv_load_ptv(dev, state, 504);
419	nv_load_ptv(dev, state, 508);
420	nv_load_ptv(dev, state, 600);
421	nv_load_ptv(dev, state, 604);
422	nv_load_ptv(dev, state, 608);
423	nv_load_ptv(dev, state, 60c);
424	nv_load_ptv(dev, state, 610);
425	nv_load_ptv(dev, state, 614);
426
427	/* This is required for some settings to kick in. */
428	nv_write_tv_enc(dev, 0x3e, 1);
429	nv_write_tv_enc(dev, 0x3e, 0);
430}
431
432/* Timings similar to the ones the blob sets */
433
434struct drm_display_mode nv17_tv_modes[] = {
435	{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
436		   320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
437		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
438		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
439	{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
440		   320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
441		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
442		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
443	{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
444		   400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
445		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
446		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
447	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
448		   640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
449		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
450	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
451		   720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
452		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
453	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
454		   720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
455		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
456	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
457		   800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
458		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
459	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
460		   1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
461		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
462	{}
463};
464
465void nv17_tv_update_properties(struct drm_encoder *encoder)
466{
467	struct drm_device *dev = encoder->dev;
468	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
469	struct nv17_tv_state *regs = &tv_enc->state;
470	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
471	int subconnector = tv_enc->select_subconnector ?
472						tv_enc->select_subconnector :
473						tv_enc->subconnector;
474
475	switch (subconnector) {
476	case DRM_MODE_SUBCONNECTOR_Composite:
477	{
478		regs->ptv_204 = 0x2;
479
480		/* The composite connector may be found on either pin. */
481		if (tv_enc->pin_mask & 0x4)
482			regs->ptv_204 |= 0x010000;
483		else if (tv_enc->pin_mask & 0x2)
484			regs->ptv_204 |= 0x100000;
485		else
486			regs->ptv_204 |= 0x110000;
487
488		regs->tv_enc[0x7] = 0x10;
489		break;
490	}
491	case DRM_MODE_SUBCONNECTOR_SVIDEO:
492		regs->ptv_204 = 0x11012;
493		regs->tv_enc[0x7] = 0x18;
494		break;
495
496	case DRM_MODE_SUBCONNECTOR_Component:
497		regs->ptv_204 = 0x111333;
498		regs->tv_enc[0x7] = 0x14;
499		break;
500
501	case DRM_MODE_SUBCONNECTOR_SCART:
502		regs->ptv_204 = 0x111012;
503		regs->tv_enc[0x7] = 0x18;
504		break;
505	}
506
507	regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
508					 tv_enc->saturation);
509	regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
510					 tv_enc->saturation);
511	regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
512
513	nv_load_ptv(dev, regs, 204);
514	nv_load_tv_enc(dev, regs, 7);
515	nv_load_tv_enc(dev, regs, 20);
516	nv_load_tv_enc(dev, regs, 22);
517	nv_load_tv_enc(dev, regs, 25);
518}
519
520void nv17_tv_update_rescaler(struct drm_encoder *encoder)
521{
522	struct drm_device *dev = encoder->dev;
523	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
524	struct nv17_tv_state *regs = &tv_enc->state;
525
526	regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
527
528	tv_setup_filter(encoder);
529
530	nv_load_ptv(dev, regs, 208);
531	tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
532	tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
533	tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
534}
535
536void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
537{
538	struct drm_device *dev = encoder->dev;
539	struct drm_nouveau_private *dev_priv = dev->dev_private;
540	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
541	int head = nouveau_crtc(encoder->crtc)->index;
542	struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
543	struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
544	struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
545	int overscan, hmargin, vmargin, hratio, vratio;
546
547	/* The rescaler doesn't do the right thing for interlaced modes. */
548	if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
549		overscan = 100;
550	else
551		overscan = tv_enc->overscan;
552
553	hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
554	vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
555
556	hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
557			      overscan);
558	vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
559			      overscan);
560
561	hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
562	vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
563
564	regs->fp_horiz_regs[FP_VALID_START] = hmargin;
565	regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
566	regs->fp_vert_regs[FP_VALID_START] = vmargin;
567	regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
568
569	regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
570		XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
571		NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
572		XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
573
574	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
575		      regs->fp_horiz_regs[FP_VALID_START]);
576	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
577		      regs->fp_horiz_regs[FP_VALID_END]);
578	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
579		      regs->fp_vert_regs[FP_VALID_START]);
580	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
581		      regs->fp_vert_regs[FP_VALID_END]);
582	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
583}
584