cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tvmodesnv17.c (21850B)


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