1209ff23fSmrg/*
2209ff23fSmrg * Integrated TV out support based on the GATOS code by
3209ff23fSmrg * Federico Ulivi <fulivi@lycos.com>
4209ff23fSmrg */
5209ff23fSmrg
6209ff23fSmrg#ifdef HAVE_CONFIG_H
7209ff23fSmrg#include "config.h"
8209ff23fSmrg#endif
9209ff23fSmrg
10209ff23fSmrg#include <string.h>
11209ff23fSmrg#include <stdio.h>
12209ff23fSmrg
13209ff23fSmrg/* X and server generic header files */
14209ff23fSmrg#include "xf86.h"
15209ff23fSmrg#include "xf86_OSproc.h"
16209ff23fSmrg#include "vgaHW.h"
17209ff23fSmrg#include "xf86Modes.h"
18209ff23fSmrg
19209ff23fSmrg/* Driver data structures */
20209ff23fSmrg#include "radeon.h"
21209ff23fSmrg#include "radeon_reg.h"
22209ff23fSmrg#include "radeon_macros.h"
23209ff23fSmrg#include "radeon_probe.h"
24209ff23fSmrg#include "radeon_version.h"
25209ff23fSmrg#include "radeon_tv.h"
26b7e1c893Smrg#include "radeon_atombios.h"
27209ff23fSmrg
28209ff23fSmrg/**********************************************************************
29209ff23fSmrg *
30209ff23fSmrg * ModeConstants
31209ff23fSmrg *
32209ff23fSmrg * Storage of constants related to a single video mode
33209ff23fSmrg *
34209ff23fSmrg **********************************************************************/
35209ff23fSmrg
36209ff23fSmrgtypedef struct
37209ff23fSmrg{
38209ff23fSmrg    uint16_t horResolution;
39209ff23fSmrg    uint16_t verResolution;
40209ff23fSmrg    TVStd  standard;
41209ff23fSmrg    uint16_t horTotal;
42209ff23fSmrg    uint16_t verTotal;
43209ff23fSmrg    uint16_t horStart;
44209ff23fSmrg    uint16_t horSyncStart;
45209ff23fSmrg    uint16_t verSyncStart;
46209ff23fSmrg    unsigned defRestart;
47209ff23fSmrg    uint16_t crtcPLL_N;
48209ff23fSmrg    uint8_t  crtcPLL_M;
49209ff23fSmrg    uint8_t  crtcPLL_postDiv;
50209ff23fSmrg    unsigned pixToTV;
51209ff23fSmrg} TVModeConstants;
52209ff23fSmrg
53209ff23fSmrgstatic const uint16_t hor_timing_NTSC[] =
54209ff23fSmrg{
55209ff23fSmrg    0x0007,
56209ff23fSmrg    0x003f,
57209ff23fSmrg    0x0263,
58209ff23fSmrg    0x0a24,
59209ff23fSmrg    0x2a6b,
60209ff23fSmrg    0x0a36,
61209ff23fSmrg    0x126d, /* H_TABLE_POS1 */
62209ff23fSmrg    0x1bfe,
63209ff23fSmrg    0x1a8f, /* H_TABLE_POS2 */
64209ff23fSmrg    0x1ec7,
65209ff23fSmrg    0x3863,
66209ff23fSmrg    0x1bfe,
67209ff23fSmrg    0x1bfe,
68209ff23fSmrg    0x1a2a,
69209ff23fSmrg    0x1e95,
70209ff23fSmrg    0x0e31,
71209ff23fSmrg    0x201b,
72209ff23fSmrg    0
73209ff23fSmrg};
74209ff23fSmrg
75209ff23fSmrgstatic const uint16_t vert_timing_NTSC[] =
76209ff23fSmrg{
77209ff23fSmrg    0x2001,
78209ff23fSmrg    0x200d,
79209ff23fSmrg    0x1006,
80209ff23fSmrg    0x0c06,
81209ff23fSmrg    0x1006,
82209ff23fSmrg    0x1818,
83209ff23fSmrg    0x21e3,
84209ff23fSmrg    0x1006,
85209ff23fSmrg    0x0c06,
86209ff23fSmrg    0x1006,
87209ff23fSmrg    0x1817,
88209ff23fSmrg    0x21d4,
89209ff23fSmrg    0x0002,
90209ff23fSmrg    0
91209ff23fSmrg};
92209ff23fSmrg
93209ff23fSmrgstatic const uint16_t hor_timing_PAL[] =
94209ff23fSmrg{
95209ff23fSmrg    0x0007,
96209ff23fSmrg    0x0058,
97209ff23fSmrg    0x027c,
98209ff23fSmrg    0x0a31,
99209ff23fSmrg    0x2a77,
100209ff23fSmrg    0x0a95,
101209ff23fSmrg    0x124f, /* H_TABLE_POS1 */
102209ff23fSmrg    0x1bfe,
103209ff23fSmrg    0x1b22, /* H_TABLE_POS2 */
104209ff23fSmrg    0x1ef9,
105209ff23fSmrg    0x387c,
106209ff23fSmrg    0x1bfe,
107209ff23fSmrg    0x1bfe,
108209ff23fSmrg    0x1b31,
109209ff23fSmrg    0x1eb5,
110209ff23fSmrg    0x0e43,
111209ff23fSmrg    0x201b,
112209ff23fSmrg    0
113209ff23fSmrg};
114209ff23fSmrg
115209ff23fSmrgstatic const uint16_t vert_timing_PAL[] =
116209ff23fSmrg{
117209ff23fSmrg    0x2001,
118209ff23fSmrg    0x200c,
119209ff23fSmrg    0x1005,
120209ff23fSmrg    0x0c05,
121209ff23fSmrg    0x1005,
122209ff23fSmrg    0x1401,
123209ff23fSmrg    0x1821,
124209ff23fSmrg    0x2240,
125209ff23fSmrg    0x1005,
126209ff23fSmrg    0x0c05,
127209ff23fSmrg    0x1005,
128209ff23fSmrg    0x1401,
129209ff23fSmrg    0x1822,
130209ff23fSmrg    0x2230,
131209ff23fSmrg    0x0002,
132209ff23fSmrg    0
133209ff23fSmrg};
134209ff23fSmrg
135209ff23fSmrg/**********************************************************************
136209ff23fSmrg *
137209ff23fSmrg * availableModes
138209ff23fSmrg *
139209ff23fSmrg * Table of all allowed modes for tv output
140209ff23fSmrg *
141209ff23fSmrg **********************************************************************/
142209ff23fSmrgstatic const TVModeConstants availableTVModes[] =
143209ff23fSmrg{
144b7e1c893Smrg    {   /* NTSC timing for 27 Mhz ref clk */
145209ff23fSmrg	800,                /* horResolution */
146209ff23fSmrg	600,                /* verResolution */
147209ff23fSmrg	TV_STD_NTSC,        /* standard */
148209ff23fSmrg	990,                /* horTotal */
149209ff23fSmrg	740,                /* verTotal */
150209ff23fSmrg	813,                /* horStart */
151209ff23fSmrg	824,                /* horSyncStart */
152209ff23fSmrg	632,                /* verSyncStart */
153209ff23fSmrg	625592,             /* defRestart */
154209ff23fSmrg	592,                /* crtcPLL_N */
155209ff23fSmrg	91,                 /* crtcPLL_M */
156209ff23fSmrg	4,                  /* crtcPLL_postDiv */
157209ff23fSmrg	1022,               /* pixToTV */
158209ff23fSmrg    },
159b7e1c893Smrg    {   /* PAL timing for 27 Mhz ref clk */
160209ff23fSmrg	800,               /* horResolution */
161209ff23fSmrg	600,               /* verResolution */
162209ff23fSmrg	TV_STD_PAL,        /* standard */
163209ff23fSmrg	1144,              /* horTotal */
164209ff23fSmrg	706,               /* verTotal */
165209ff23fSmrg	812,               /* horStart */
166209ff23fSmrg	824,               /* horSyncStart */
167209ff23fSmrg	669,               /* verSyncStart */
168209ff23fSmrg	696700,            /* defRestart */
169209ff23fSmrg	1382,              /* crtcPLL_N */
170209ff23fSmrg	231,               /* crtcPLL_M */
171209ff23fSmrg	4,                 /* crtcPLL_postDiv */
172209ff23fSmrg	759,               /* pixToTV */
173b7e1c893Smrg    },
174b7e1c893Smrg    {   /* NTSC timing for 14 Mhz ref clk */
175b7e1c893Smrg	800,                /* horResolution */
176b7e1c893Smrg	600,                /* verResolution */
177b7e1c893Smrg	TV_STD_NTSC,        /* standard */
178b7e1c893Smrg	1018,               /* horTotal */
179b7e1c893Smrg	727,                /* verTotal */
180b7e1c893Smrg	813,                /* horStart */
181b7e1c893Smrg	840,                /* horSyncStart */
182b7e1c893Smrg	633,                /* verSyncStart */
183b7e1c893Smrg	630627,             /* defRestart */
184b7e1c893Smrg	347,                /* crtcPLL_N */
185b7e1c893Smrg	14,                 /* crtcPLL_M */
186b7e1c893Smrg	8,                  /* crtcPLL_postDiv */
187b7e1c893Smrg	1022,               /* pixToTV */
188b7e1c893Smrg    },
189ad43ddacSmrg    {   /* PAL timing for 14 Mhz ref clk */
190ad43ddacSmrg	800,                /* horResolution */
191ad43ddacSmrg	600,                /* verResolution */
192ad43ddacSmrg	TV_STD_PAL,         /* standard */
193ad43ddacSmrg	1131,               /* horTotal */
194ad43ddacSmrg	742,                /* verTotal */
195ad43ddacSmrg	813,                /* horStart */
196ad43ddacSmrg	840,                /* horSyncStart */
197ad43ddacSmrg	633,                /* verSyncStart */
198ad43ddacSmrg	708369,             /* defRestart */
199ad43ddacSmrg	211,                /* crtcPLL_N */
200ad43ddacSmrg	9,                  /* crtcPLL_M */
201ad43ddacSmrg	8,                  /* crtcPLL_postDiv */
202ad43ddacSmrg	759,                /* pixToTV */
203ad43ddacSmrg    },
204209ff23fSmrg};
205209ff23fSmrg
206209ff23fSmrg#define N_AVAILABLE_MODES (sizeof(availableModes) / sizeof(availableModes[ 0 ]))
207209ff23fSmrg
208209ff23fSmrgstatic long YCOEF_value[5] = { 2, 2, 0, 4, 0 };
209209ff23fSmrgstatic long YCOEF_EN_value[5] = { 1, 1, 0, 1, 0 };
210209ff23fSmrgstatic long SLOPE_value[5] = { 1, 2, 2, 4, 8 };
211209ff23fSmrgstatic long SLOPE_limit[5] = { 6, 5, 4, 3, 2 };
212209ff23fSmrg
213209ff23fSmrg
214209ff23fSmrgstatic void
215209ff23fSmrgRADEONWaitPLLLock(ScrnInfoPtr pScrn, unsigned nTests,
216209ff23fSmrg		  unsigned nWaitLoops, unsigned cntThreshold)
217209ff23fSmrg{
218209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
219209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
220209ff23fSmrg    uint32_t savePLLTest;
221209ff23fSmrg    unsigned i;
222209ff23fSmrg    unsigned j;
223209ff23fSmrg
224209ff23fSmrg    OUTREG(RADEON_TEST_DEBUG_MUX, (INREG(RADEON_TEST_DEBUG_MUX) & 0xffff60ff) | 0x100);
225209ff23fSmrg
226209ff23fSmrg    savePLLTest = INPLL(pScrn, RADEON_PLL_TEST_CNTL);
227209ff23fSmrg
228209ff23fSmrg    OUTPLL(pScrn, RADEON_PLL_TEST_CNTL, savePLLTest & ~RADEON_PLL_MASK_READ_B);
229209ff23fSmrg
230209ff23fSmrg    /* XXX: these should probably be OUTPLL to avoid various PLL errata */
231209ff23fSmrg
232209ff23fSmrg    OUTREG8(RADEON_CLOCK_CNTL_INDEX, RADEON_PLL_TEST_CNTL);
233209ff23fSmrg
234209ff23fSmrg    for (i = 0; i < nTests; i++) {
235209ff23fSmrg	OUTREG8(RADEON_CLOCK_CNTL_DATA + 3, 0);
236209ff23fSmrg
237209ff23fSmrg	for (j = 0; j < nWaitLoops; j++)
238209ff23fSmrg	    if (INREG8(RADEON_CLOCK_CNTL_DATA + 3) >= cntThreshold)
239209ff23fSmrg		break;
240209ff23fSmrg    }
241209ff23fSmrg
242209ff23fSmrg    OUTPLL(pScrn, RADEON_PLL_TEST_CNTL, savePLLTest);
243209ff23fSmrg
244209ff23fSmrg    OUTREG(RADEON_TEST_DEBUG_MUX, INREG(RADEON_TEST_DEBUG_MUX) & 0xffffe0ff);
245209ff23fSmrg}
246209ff23fSmrg
247209ff23fSmrg/* Write to TV FIFO RAM */
248209ff23fSmrgstatic void
249209ff23fSmrgRADEONWriteTVFIFO(ScrnInfoPtr pScrn, uint16_t addr,
250209ff23fSmrg		  uint32_t value)
251209ff23fSmrg{
252209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
253209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
254209ff23fSmrg    uint32_t tmp;
255209ff23fSmrg    int i = 0;
256209ff23fSmrg
257209ff23fSmrg    OUTREG(RADEON_TV_HOST_WRITE_DATA, value);
258209ff23fSmrg
259209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, addr);
260209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_WT);
261209ff23fSmrg
262209ff23fSmrg    do {
263209ff23fSmrg	tmp = INREG(RADEON_TV_HOST_RD_WT_CNTL);
264209ff23fSmrg	if ((tmp & RADEON_HOST_FIFO_WT_ACK) == 0)
265209ff23fSmrg	    break;
266209ff23fSmrg	i++;
267209ff23fSmrg    }
268209ff23fSmrg    while (i < 10000);
269209ff23fSmrg    /*while ((tmp & RADEON_HOST_FIFO_WT_ACK) == 0);*/
270209ff23fSmrg
271209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, 0);
272209ff23fSmrg}
273209ff23fSmrg
274209ff23fSmrg/* Read from TV FIFO RAM */
275209ff23fSmrgstatic uint32_t
276209ff23fSmrgRADEONReadTVFIFO(ScrnInfoPtr pScrn, uint16_t addr)
277209ff23fSmrg{
278209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
279209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
280209ff23fSmrg    uint32_t tmp;
281209ff23fSmrg    int i = 0;
282209ff23fSmrg
283209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, addr);
284209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, addr | RADEON_HOST_FIFO_RD);
285209ff23fSmrg
286209ff23fSmrg    do {
287209ff23fSmrg	tmp = INREG(RADEON_TV_HOST_RD_WT_CNTL);
288209ff23fSmrg	if ((tmp & RADEON_HOST_FIFO_RD_ACK) == 0)
289209ff23fSmrg	    break;
290209ff23fSmrg	i++;
291209ff23fSmrg    }
292209ff23fSmrg    while (i < 10000);
293209ff23fSmrg    /*while ((tmp & RADEON_HOST_FIFO_RD_ACK) == 0);*/
294209ff23fSmrg
295209ff23fSmrg    OUTREG(RADEON_TV_HOST_RD_WT_CNTL, 0);
296209ff23fSmrg
297209ff23fSmrg    return INREG(RADEON_TV_HOST_READ_DATA);
298209ff23fSmrg}
299209ff23fSmrg
300209ff23fSmrg/* Get FIFO addresses of horizontal & vertical code timing tables from
301209ff23fSmrg * settings of uv_adr register.
302209ff23fSmrg */
303209ff23fSmrgstatic uint16_t
304209ff23fSmrgRADEONGetHTimingTablesAddr(uint32_t tv_uv_adr)
305209ff23fSmrg{
306209ff23fSmrg    uint16_t hTable;
307209ff23fSmrg
308209ff23fSmrg    switch ((tv_uv_adr & RADEON_HCODE_TABLE_SEL_MASK) >> RADEON_HCODE_TABLE_SEL_SHIFT) {
309209ff23fSmrg    case 0:
310209ff23fSmrg	hTable = RADEON_TV_MAX_FIFO_ADDR_INTERNAL;
311209ff23fSmrg	break;
312209ff23fSmrg    case 1:
313209ff23fSmrg	hTable = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2;
314209ff23fSmrg	break;
315209ff23fSmrg    case 2:
316209ff23fSmrg	hTable = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2;
317209ff23fSmrg	break;
318209ff23fSmrg    default:
319209ff23fSmrg	/* Of course, this should never happen */
320209ff23fSmrg	hTable = 0;
321209ff23fSmrg	break;
322209ff23fSmrg    }
323209ff23fSmrg    return hTable;
324209ff23fSmrg}
325209ff23fSmrg
326209ff23fSmrgstatic uint16_t
327209ff23fSmrgRADEONGetVTimingTablesAddr(uint32_t tv_uv_adr)
328209ff23fSmrg{
329209ff23fSmrg    uint16_t vTable;
330209ff23fSmrg
331209ff23fSmrg    switch ((tv_uv_adr & RADEON_VCODE_TABLE_SEL_MASK) >> RADEON_VCODE_TABLE_SEL_SHIFT) {
332209ff23fSmrg    case 0:
333209ff23fSmrg	vTable = ((tv_uv_adr & RADEON_MAX_UV_ADR_MASK) >> RADEON_MAX_UV_ADR_SHIFT) * 2 + 1;
334209ff23fSmrg	break;
335209ff23fSmrg    case 1:
336209ff23fSmrg	vTable = ((tv_uv_adr & RADEON_TABLE1_BOT_ADR_MASK) >> RADEON_TABLE1_BOT_ADR_SHIFT) * 2 + 1;
337209ff23fSmrg	break;
338209ff23fSmrg    case 2:
339209ff23fSmrg	vTable = ((tv_uv_adr & RADEON_TABLE3_TOP_ADR_MASK) >> RADEON_TABLE3_TOP_ADR_SHIFT) * 2 + 1;
340209ff23fSmrg	break;
341209ff23fSmrg    default:
342209ff23fSmrg	/* Of course, this should never happen */
343209ff23fSmrg	vTable = 0;
344209ff23fSmrg	break;
345209ff23fSmrg    }
346209ff23fSmrg    return vTable;
347209ff23fSmrg}
348209ff23fSmrg
349209ff23fSmrg/* Restore horizontal/vertical timing code tables */
350209ff23fSmrgstatic void
351209ff23fSmrgRADEONRestoreTVTimingTables(ScrnInfoPtr pScrn, RADEONSavePtr restore)
352209ff23fSmrg{
353209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
354209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
355209ff23fSmrg    uint16_t hTable;
356209ff23fSmrg    uint16_t vTable;
357209ff23fSmrg    uint32_t tmp;
358209ff23fSmrg    unsigned i;
359209ff23fSmrg
360209ff23fSmrg    OUTREG(RADEON_TV_UV_ADR, restore->tv_uv_adr);
361209ff23fSmrg    hTable = RADEONGetHTimingTablesAddr(restore->tv_uv_adr);
362209ff23fSmrg    vTable = RADEONGetVTimingTablesAddr(restore->tv_uv_adr);
363209ff23fSmrg
364209ff23fSmrg    for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2, hTable--) {
365209ff23fSmrg	tmp = ((uint32_t)restore->h_code_timing[ i ] << 14) | ((uint32_t)restore->h_code_timing[ i + 1 ]);
366209ff23fSmrg	RADEONWriteTVFIFO(pScrn, hTable, tmp);
367209ff23fSmrg	if (restore->h_code_timing[ i ] == 0 || restore->h_code_timing[ i + 1 ] == 0)
368209ff23fSmrg	    break;
369209ff23fSmrg    }
370209ff23fSmrg
371209ff23fSmrg    for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2, vTable++) {
372209ff23fSmrg	tmp = ((uint32_t)restore->v_code_timing[ i + 1 ] << 14) | ((uint32_t)restore->v_code_timing[ i ]);
373209ff23fSmrg	RADEONWriteTVFIFO(pScrn, vTable, tmp);
374209ff23fSmrg	if (restore->v_code_timing[ i ] == 0 || restore->v_code_timing[ i + 1 ] == 0)
375209ff23fSmrg	    break;
376209ff23fSmrg    }
377209ff23fSmrg}
378209ff23fSmrg
379209ff23fSmrg/* restore TV PLLs */
380209ff23fSmrgstatic void
381209ff23fSmrgRADEONRestoreTVPLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr restore)
382209ff23fSmrg{
383209ff23fSmrg
384209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVCLK_SRC_SEL_TVPLL);
385209ff23fSmrg    OUTPLL(pScrn, RADEON_TV_PLL_CNTL, restore->tv_pll_cntl);
386209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, RADEON_TVPLL_RESET, ~RADEON_TVPLL_RESET);
387209ff23fSmrg
388209ff23fSmrg    RADEONWaitPLLLock(pScrn, 200, 800, 135);
389209ff23fSmrg
390209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_RESET);
391209ff23fSmrg
392209ff23fSmrg    RADEONWaitPLLLock(pScrn, 300, 160, 27);
393209ff23fSmrg    RADEONWaitPLLLock(pScrn, 200, 800, 135);
394209ff23fSmrg
395209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, 0, ~0xf);
396209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, RADEON_TVCLK_SRC_SEL_TVPLL, ~RADEON_TVCLK_SRC_SEL_TVPLL);
397209ff23fSmrg
398209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, (1 << RADEON_TVPDC_SHIFT), ~RADEON_TVPDC_MASK);
399209ff23fSmrg    OUTPLLP(pScrn, RADEON_TV_PLL_CNTL1, 0, ~RADEON_TVPLL_SLEEP);
400209ff23fSmrg}
401209ff23fSmrg
402209ff23fSmrg/* Restore TV horizontal/vertical settings */
403209ff23fSmrgstatic void
404209ff23fSmrgRADEONRestoreTVHVRegisters(ScrnInfoPtr pScrn, RADEONSavePtr restore)
405209ff23fSmrg{
406209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
407209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
408209ff23fSmrg
409209ff23fSmrg    OUTREG(RADEON_TV_RGB_CNTL, restore->tv_rgb_cntl);
410209ff23fSmrg
411209ff23fSmrg    OUTREG(RADEON_TV_HTOTAL, restore->tv_htotal);
412209ff23fSmrg    OUTREG(RADEON_TV_HDISP, restore->tv_hdisp);
413209ff23fSmrg    OUTREG(RADEON_TV_HSTART, restore->tv_hstart);
414209ff23fSmrg
415209ff23fSmrg    OUTREG(RADEON_TV_VTOTAL, restore->tv_vtotal);
416209ff23fSmrg    OUTREG(RADEON_TV_VDISP, restore->tv_vdisp);
417209ff23fSmrg
418209ff23fSmrg    OUTREG(RADEON_TV_FTOTAL, restore->tv_ftotal);
419209ff23fSmrg
420209ff23fSmrg    OUTREG(RADEON_TV_VSCALER_CNTL1, restore->tv_vscaler_cntl1);
421209ff23fSmrg    OUTREG(RADEON_TV_VSCALER_CNTL2, restore->tv_vscaler_cntl2);
422209ff23fSmrg
423209ff23fSmrg    OUTREG(RADEON_TV_Y_FALL_CNTL, restore->tv_y_fall_cntl);
424209ff23fSmrg    OUTREG(RADEON_TV_Y_RISE_CNTL, restore->tv_y_rise_cntl);
425209ff23fSmrg    OUTREG(RADEON_TV_Y_SAW_TOOTH_CNTL, restore->tv_y_saw_tooth_cntl);
426209ff23fSmrg}
427209ff23fSmrg
428209ff23fSmrg/* restore TV RESTART registers */
429209ff23fSmrgstatic void
430209ff23fSmrgRADEONRestoreTVRestarts(ScrnInfoPtr pScrn, RADEONSavePtr restore)
431209ff23fSmrg{
432209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
433209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
434209ff23fSmrg
435209ff23fSmrg    OUTREG(RADEON_TV_FRESTART, restore->tv_frestart);
436209ff23fSmrg    OUTREG(RADEON_TV_HRESTART, restore->tv_hrestart);
437209ff23fSmrg    OUTREG(RADEON_TV_VRESTART, restore->tv_vrestart);
438209ff23fSmrg}
439209ff23fSmrg
440209ff23fSmrg/* restore tv standard & output muxes */
441209ff23fSmrgstatic void
442209ff23fSmrgRADEONRestoreTVOutputStd(ScrnInfoPtr pScrn, RADEONSavePtr restore)
443209ff23fSmrg{
444209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
445209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
446209ff23fSmrg
447209ff23fSmrg    OUTREG(RADEON_TV_SYNC_CNTL, restore->tv_sync_cntl);
448209ff23fSmrg
449209ff23fSmrg    OUTREG(RADEON_TV_TIMING_CNTL, restore->tv_timing_cntl);
450209ff23fSmrg
451209ff23fSmrg    OUTREG(RADEON_TV_MODULATOR_CNTL1, restore->tv_modulator_cntl1);
452209ff23fSmrg    OUTREG(RADEON_TV_MODULATOR_CNTL2, restore->tv_modulator_cntl2);
453209ff23fSmrg
454209ff23fSmrg    OUTREG(RADEON_TV_PRE_DAC_MUX_CNTL, restore->tv_pre_dac_mux_cntl);
455209ff23fSmrg
456209ff23fSmrg    OUTREG(RADEON_TV_CRC_CNTL, restore->tv_crc_cntl);
457209ff23fSmrg}
458209ff23fSmrg
459209ff23fSmrg/* Restore TV out regs */
460209ff23fSmrgvoid
461209ff23fSmrgRADEONRestoreTVRegisters(ScrnInfoPtr pScrn, RADEONSavePtr restore)
462209ff23fSmrg{
463209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
464209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
465209ff23fSmrg
466209ff23fSmrg    ErrorF("Entering Restore TV\n");
467209ff23fSmrg
468209ff23fSmrg    OUTREG(RADEON_TV_MASTER_CNTL, (restore->tv_master_cntl
469209ff23fSmrg				   | RADEON_TV_ASYNC_RST
470209ff23fSmrg				   | RADEON_CRT_ASYNC_RST
471209ff23fSmrg				   | RADEON_TV_FIFO_ASYNC_RST));
472209ff23fSmrg
473209ff23fSmrg    /* Temporarily turn the TV DAC off */
474209ff23fSmrg    OUTREG(RADEON_TV_DAC_CNTL, ((restore->tv_dac_cntl & ~RADEON_TV_DAC_NBLANK)
475209ff23fSmrg				| RADEON_TV_DAC_BGSLEEP
476209ff23fSmrg				| RADEON_TV_DAC_RDACPD
477209ff23fSmrg				| RADEON_TV_DAC_GDACPD
478209ff23fSmrg				| RADEON_TV_DAC_BDACPD));
479209ff23fSmrg
480209ff23fSmrg    ErrorF("Restore TV PLL\n");
481209ff23fSmrg    RADEONRestoreTVPLLRegisters(pScrn, restore);
482209ff23fSmrg
483209ff23fSmrg    ErrorF("Restore TVHV\n");
484209ff23fSmrg    RADEONRestoreTVHVRegisters(pScrn, restore);
485209ff23fSmrg
486209ff23fSmrg    OUTREG(RADEON_TV_MASTER_CNTL, (restore->tv_master_cntl
487209ff23fSmrg				   | RADEON_TV_ASYNC_RST
488209ff23fSmrg				   | RADEON_CRT_ASYNC_RST));
489209ff23fSmrg
490209ff23fSmrg    ErrorF("Restore TV Restarts\n");
491209ff23fSmrg    RADEONRestoreTVRestarts(pScrn, restore);
492209ff23fSmrg
493209ff23fSmrg    ErrorF("Restore Timing Tables\n");
494209ff23fSmrg    RADEONRestoreTVTimingTables(pScrn, restore);
495209ff23fSmrg
496209ff23fSmrg
497209ff23fSmrg    OUTREG(RADEON_TV_MASTER_CNTL, (restore->tv_master_cntl
498209ff23fSmrg				   | RADEON_TV_ASYNC_RST));
499209ff23fSmrg
500209ff23fSmrg    ErrorF("Restore TV standard\n");
501209ff23fSmrg    RADEONRestoreTVOutputStd(pScrn, restore);
502209ff23fSmrg
503209ff23fSmrg    OUTREG(RADEON_TV_MASTER_CNTL, restore->tv_master_cntl);
504209ff23fSmrg
505209ff23fSmrg    OUTREG(RADEON_TV_GAIN_LIMIT_SETTINGS, restore->tv_gain_limit_settings);
506209ff23fSmrg    OUTREG(RADEON_TV_LINEAR_GAIN_SETTINGS, restore->tv_linear_gain_settings);
507209ff23fSmrg
508209ff23fSmrg    OUTREG(RADEON_TV_DAC_CNTL, restore->tv_dac_cntl);
509209ff23fSmrg
510209ff23fSmrg    ErrorF("Leaving Restore TV\n");
511209ff23fSmrg}
512209ff23fSmrg
513209ff23fSmrg/* Save horizontal/vertical timing code tables */
514209ff23fSmrgstatic void
515209ff23fSmrgRADEONSaveTVTimingTables(ScrnInfoPtr pScrn, RADEONSavePtr save)
516209ff23fSmrg{
517209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
518209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
519209ff23fSmrg    uint16_t hTable;
520209ff23fSmrg    uint16_t vTable;
521209ff23fSmrg    uint32_t tmp;
522209ff23fSmrg    unsigned i;
523209ff23fSmrg
524209ff23fSmrg    save->tv_uv_adr = INREG(RADEON_TV_UV_ADR);
525209ff23fSmrg    hTable = RADEONGetHTimingTablesAddr(save->tv_uv_adr);
526209ff23fSmrg    vTable = RADEONGetVTimingTablesAddr(save->tv_uv_adr);
527209ff23fSmrg
528209ff23fSmrg    /*
529209ff23fSmrg     * Reset FIFO arbiter in order to be able to access FIFO RAM
530209ff23fSmrg     */
531209ff23fSmrg
532209ff23fSmrg    OUTREG(RADEON_TV_MASTER_CNTL, (RADEON_TV_ASYNC_RST
533209ff23fSmrg				   | RADEON_CRT_ASYNC_RST
534209ff23fSmrg				   | RADEON_RESTART_PHASE_FIX
535209ff23fSmrg				   | RADEON_CRT_FIFO_CE_EN
536209ff23fSmrg				   | RADEON_TV_FIFO_CE_EN
537209ff23fSmrg				   | RADEON_TV_ON));
538209ff23fSmrg
539209ff23fSmrg    /*OUTREG(RADEON_TV_MASTER_CNTL, save->tv_master_cntl | RADEON_TV_ON);*/
540209ff23fSmrg
541209ff23fSmrg    ErrorF("saveTimingTables: reading timing tables\n");
542209ff23fSmrg
543209ff23fSmrg    for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2) {
544209ff23fSmrg	tmp = RADEONReadTVFIFO(pScrn, hTable--);
545209ff23fSmrg	save->h_code_timing[ i     ] = (uint16_t)((tmp >> 14) & 0x3fff);
546209ff23fSmrg	save->h_code_timing[ i + 1 ] = (uint16_t)(tmp & 0x3fff);
547209ff23fSmrg
548209ff23fSmrg	if (save->h_code_timing[ i ] == 0 || save->h_code_timing[ i + 1 ] == 0)
549209ff23fSmrg	    break;
550209ff23fSmrg    }
551209ff23fSmrg
552209ff23fSmrg    for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2) {
553209ff23fSmrg	tmp = RADEONReadTVFIFO(pScrn, vTable++);
554209ff23fSmrg	save->v_code_timing[ i     ] = (uint16_t)(tmp & 0x3fff);
555209ff23fSmrg	save->v_code_timing[ i + 1 ] = (uint16_t)((tmp >> 14) & 0x3fff);
556209ff23fSmrg
557209ff23fSmrg	if (save->v_code_timing[ i ] == 0 || save->v_code_timing[ i + 1 ] == 0)
558209ff23fSmrg	    break;
559209ff23fSmrg    }
560209ff23fSmrg}
561209ff23fSmrg
562209ff23fSmrg/* read TV regs */
563209ff23fSmrgvoid
564209ff23fSmrgRADEONSaveTVRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
565209ff23fSmrg{
566209ff23fSmrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
567209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
568209ff23fSmrg
569209ff23fSmrg    ErrorF("Entering TV Save\n");
570209ff23fSmrg
571209ff23fSmrg    save->tv_crc_cntl = INREG(RADEON_TV_CRC_CNTL);
572209ff23fSmrg    save->tv_frestart = INREG(RADEON_TV_FRESTART);
573209ff23fSmrg    save->tv_hrestart = INREG(RADEON_TV_HRESTART);
574209ff23fSmrg    save->tv_vrestart = INREG(RADEON_TV_VRESTART);
575209ff23fSmrg    save->tv_gain_limit_settings = INREG(RADEON_TV_GAIN_LIMIT_SETTINGS);
576209ff23fSmrg    save->tv_hdisp = INREG(RADEON_TV_HDISP);
577209ff23fSmrg    save->tv_hstart = INREG(RADEON_TV_HSTART);
578209ff23fSmrg    save->tv_htotal = INREG(RADEON_TV_HTOTAL);
579209ff23fSmrg    save->tv_linear_gain_settings = INREG(RADEON_TV_LINEAR_GAIN_SETTINGS);
580209ff23fSmrg    save->tv_master_cntl = INREG(RADEON_TV_MASTER_CNTL);
581209ff23fSmrg    save->tv_rgb_cntl = INREG(RADEON_TV_RGB_CNTL);
582209ff23fSmrg    save->tv_modulator_cntl1 = INREG(RADEON_TV_MODULATOR_CNTL1);
583209ff23fSmrg    save->tv_modulator_cntl2 = INREG(RADEON_TV_MODULATOR_CNTL2);
584209ff23fSmrg    save->tv_pre_dac_mux_cntl = INREG(RADEON_TV_PRE_DAC_MUX_CNTL);
585209ff23fSmrg    save->tv_sync_cntl = INREG(RADEON_TV_SYNC_CNTL);
586209ff23fSmrg    save->tv_timing_cntl = INREG(RADEON_TV_TIMING_CNTL);
587209ff23fSmrg    save->tv_dac_cntl = INREG(RADEON_TV_DAC_CNTL);
588209ff23fSmrg    save->tv_upsamp_and_gain_cntl = INREG(RADEON_TV_UPSAMP_AND_GAIN_CNTL);
589209ff23fSmrg    save->tv_vdisp = INREG(RADEON_TV_VDISP);
590209ff23fSmrg    save->tv_ftotal = INREG(RADEON_TV_FTOTAL);
591209ff23fSmrg    save->tv_vscaler_cntl1 = INREG(RADEON_TV_VSCALER_CNTL1);
592209ff23fSmrg    save->tv_vscaler_cntl2 = INREG(RADEON_TV_VSCALER_CNTL2);
593209ff23fSmrg    save->tv_vtotal = INREG(RADEON_TV_VTOTAL);
594209ff23fSmrg    save->tv_y_fall_cntl = INREG(RADEON_TV_Y_FALL_CNTL);
595209ff23fSmrg    save->tv_y_rise_cntl = INREG(RADEON_TV_Y_RISE_CNTL);
596209ff23fSmrg    save->tv_y_saw_tooth_cntl = INREG(RADEON_TV_Y_SAW_TOOTH_CNTL);
597209ff23fSmrg
598209ff23fSmrg    save->tv_pll_cntl = INPLL(pScrn, RADEON_TV_PLL_CNTL);
599209ff23fSmrg    save->tv_pll_cntl1 = INPLL(pScrn, RADEON_TV_PLL_CNTL1);
600209ff23fSmrg
601209ff23fSmrg    ErrorF("Save TV timing tables\n");
602209ff23fSmrg
603209ff23fSmrg    RADEONSaveTVTimingTables(pScrn, save);
604209ff23fSmrg
605209ff23fSmrg    ErrorF("TV Save done\n");
606209ff23fSmrg}
607209ff23fSmrg
608209ff23fSmrg
609209ff23fSmrg/* Compute F,V,H restarts from default restart position and hPos & vPos
610209ff23fSmrg * Return TRUE when code timing table was changed
611209ff23fSmrg */
612209ff23fSmrgstatic Bool RADEONInitTVRestarts(xf86OutputPtr output, RADEONSavePtr save,
613209ff23fSmrg				 DisplayModePtr mode)
614209ff23fSmrg{
615209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
616b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
617b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(output->scrn);
618b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
619209ff23fSmrg    int restart;
620209ff23fSmrg    unsigned hTotal;
621209ff23fSmrg    unsigned vTotal;
622209ff23fSmrg    unsigned fTotal;
623209ff23fSmrg    int vOffset;
624209ff23fSmrg    int hOffset;
625209ff23fSmrg    uint16_t p1;
626209ff23fSmrg    uint16_t p2;
627209ff23fSmrg    Bool hChanged;
628209ff23fSmrg    uint16_t hInc;
629209ff23fSmrg    const TVModeConstants *constPtr;
630209ff23fSmrg
631209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
632b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
633b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
634b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
635b7e1c893Smrg	if (pll->reference_freq == 2700)
636b7e1c893Smrg	    constPtr = &availableTVModes[0];
637b7e1c893Smrg	else
638b7e1c893Smrg	    constPtr = &availableTVModes[2];
639b7e1c893Smrg    } else {
640b7e1c893Smrg	if (pll->reference_freq == 2700)
641b7e1c893Smrg	    constPtr = &availableTVModes[1];
642b7e1c893Smrg	else
643ad43ddacSmrg	    constPtr = &availableTVModes[3];
644b7e1c893Smrg    }
645209ff23fSmrg
646209ff23fSmrg    hTotal = constPtr->horTotal;
647209ff23fSmrg    vTotal = constPtr->verTotal;
648b7e1c893Smrg
649b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
650b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
651b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_M ||
652b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_60)
653209ff23fSmrg	fTotal = NTSC_TV_VFTOTAL + 1;
654209ff23fSmrg    else
655209ff23fSmrg	fTotal = PAL_TV_VFTOTAL + 1;
656209ff23fSmrg
657209ff23fSmrg    /* Adjust positions 1&2 in hor. code timing table */
658b7e1c893Smrg    hOffset = tvout->hPos * H_POS_UNIT;
659209ff23fSmrg
660b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
661b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
662b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
663209ff23fSmrg	/* improve image centering */
664209ff23fSmrg	hOffset -= 50;
665209ff23fSmrg	p1 = hor_timing_NTSC[ H_TABLE_POS1 ];
666209ff23fSmrg	p2 = hor_timing_NTSC[ H_TABLE_POS2 ];
667209ff23fSmrg    } else {
668209ff23fSmrg	p1 = hor_timing_PAL[ H_TABLE_POS1 ];
669209ff23fSmrg	p2 = hor_timing_PAL[ H_TABLE_POS2 ];
670209ff23fSmrg    }
671209ff23fSmrg
672209ff23fSmrg
673209ff23fSmrg    p1 = (uint16_t)((int)p1 + hOffset);
674209ff23fSmrg    p2 = (uint16_t)((int)p2 - hOffset);
675209ff23fSmrg
676209ff23fSmrg    hChanged = (p1 != save->h_code_timing[ H_TABLE_POS1 ] ||
677209ff23fSmrg		p2 != save->h_code_timing[ H_TABLE_POS2 ]);
678209ff23fSmrg
679209ff23fSmrg    save->h_code_timing[ H_TABLE_POS1 ] = p1;
680209ff23fSmrg    save->h_code_timing[ H_TABLE_POS2 ] = p2;
681209ff23fSmrg
682209ff23fSmrg    /* Convert hOffset from n. of TV clock periods to n. of CRTC clock periods (CRTC pixels) */
683209ff23fSmrg    hOffset = (hOffset * (int)(constPtr->pixToTV)) / 1000;
684209ff23fSmrg
685209ff23fSmrg    /* Adjust restart */
686209ff23fSmrg    restart = constPtr->defRestart;
687209ff23fSmrg
688209ff23fSmrg    /*
689209ff23fSmrg     * Convert vPos TV lines to n. of CRTC pixels
690209ff23fSmrg     * Be verrrrry careful when mixing signed & unsigned values in C..
691209ff23fSmrg     */
692b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
693b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
694b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M ||
695b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_60)
696b7e1c893Smrg	vOffset = ((int)(vTotal * hTotal) * 2 * tvout->vPos) / (int)(NTSC_TV_LINES_PER_FRAME);
697209ff23fSmrg    else
698b7e1c893Smrg	vOffset = ((int)(vTotal * hTotal) * 2 * tvout->vPos) / (int)(PAL_TV_LINES_PER_FRAME);
699209ff23fSmrg
700209ff23fSmrg    restart -= vOffset + hOffset;
701209ff23fSmrg
702209ff23fSmrg    ErrorF("computeRestarts: def = %u, h = %d, v = %d, p1=%04x, p2=%04x, restart = %d\n",
703b7e1c893Smrg	   constPtr->defRestart , tvout->hPos , tvout->vPos , p1 , p2 , restart);
704209ff23fSmrg
705209ff23fSmrg    save->tv_hrestart = restart % hTotal;
706209ff23fSmrg    restart /= hTotal;
707209ff23fSmrg    save->tv_vrestart = restart % vTotal;
708209ff23fSmrg    restart /= vTotal;
709209ff23fSmrg    save->tv_frestart = restart % fTotal;
710209ff23fSmrg
711209ff23fSmrg    ErrorF("computeRestarts: F/H/V=%u,%u,%u\n",
712209ff23fSmrg	   (unsigned)save->tv_frestart, (unsigned)save->tv_vrestart,
713209ff23fSmrg	   (unsigned)save->tv_hrestart);
714209ff23fSmrg
715209ff23fSmrg    /* Compute H_INC from hSize */
716b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
717b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
718b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M)
719209ff23fSmrg	hInc = (uint16_t)((int)(constPtr->horResolution * 4096 * NTSC_TV_CLOCK_T) /
720b7e1c893Smrg			(tvout->hSize * (int)(NTSC_TV_H_SIZE_UNIT) + (int)(NTSC_TV_ZERO_H_SIZE)));
721209ff23fSmrg    else
722209ff23fSmrg	hInc = (uint16_t)((int)(constPtr->horResolution * 4096 * PAL_TV_CLOCK_T) /
723b7e1c893Smrg			(tvout->hSize * (int)(PAL_TV_H_SIZE_UNIT) + (int)(PAL_TV_ZERO_H_SIZE)));
724209ff23fSmrg
725209ff23fSmrg    save->tv_timing_cntl = (save->tv_timing_cntl & ~RADEON_H_INC_MASK) |
726209ff23fSmrg	((uint32_t)hInc << RADEON_H_INC_SHIFT);
727209ff23fSmrg
728b7e1c893Smrg    ErrorF("computeRestarts: hSize=%d,hInc=%u\n" , tvout->hSize , hInc);
729209ff23fSmrg
730209ff23fSmrg    return hChanged;
731209ff23fSmrg}
732209ff23fSmrg
733209ff23fSmrg/* intit TV-out regs */
734209ff23fSmrgvoid RADEONInitTVRegisters(xf86OutputPtr output, RADEONSavePtr save,
735209ff23fSmrg                                  DisplayModePtr mode, BOOL IsPrimary)
736209ff23fSmrg{
737209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
738209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
739b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
740209ff23fSmrg    RADEONInfoPtr  info = RADEONPTR(pScrn);
741b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
742b7e1c893Smrg    unsigned m, n, p;
743209ff23fSmrg    unsigned i;
744209ff23fSmrg    unsigned long vert_space, flicker_removal;
745209ff23fSmrg    uint32_t tmp;
746209ff23fSmrg    const TVModeConstants *constPtr;
747209ff23fSmrg    const uint16_t *hor_timing;
748209ff23fSmrg    const uint16_t *vert_timing;
749b7e1c893Smrg    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
750b7e1c893Smrg    radeon_tvdac_ptr tvdac = NULL;
751b7e1c893Smrg
752b7e1c893Smrg    if (radeon_encoder == NULL)
753b7e1c893Smrg	return;
754b7e1c893Smrg
755b7e1c893Smrg    tvdac = (radeon_tvdac_ptr)radeon_encoder->dev_priv;
756209ff23fSmrg
757b7e1c893Smrg    if (tvdac == NULL)
758b7e1c893Smrg	return;
759209ff23fSmrg
760209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
761b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
762b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
763b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
764b7e1c893Smrg	if (pll->reference_freq == 2700)
765b7e1c893Smrg	    constPtr = &availableTVModes[0];
766b7e1c893Smrg	else
767b7e1c893Smrg	    constPtr = &availableTVModes[2];
768b7e1c893Smrg    } else {
769b7e1c893Smrg	if (pll->reference_freq == 2700)
770b7e1c893Smrg	    constPtr = &availableTVModes[1];
771b7e1c893Smrg	else
772ad43ddacSmrg	    constPtr = &availableTVModes[3];
773b7e1c893Smrg    }
774209ff23fSmrg
775209ff23fSmrg    save->tv_crc_cntl = 0;
776209ff23fSmrg
777209ff23fSmrg    save->tv_gain_limit_settings = (0x17f << RADEON_UV_GAIN_LIMIT_SHIFT) |
778209ff23fSmrg	                           (0x5ff << RADEON_Y_GAIN_LIMIT_SHIFT);
779209ff23fSmrg
780209ff23fSmrg    save->tv_hdisp = constPtr->horResolution - 1;
781209ff23fSmrg    save->tv_hstart = constPtr->horStart;
782209ff23fSmrg    save->tv_htotal = constPtr->horTotal - 1;
783209ff23fSmrg
784209ff23fSmrg    save->tv_linear_gain_settings = (0x100 << RADEON_UV_GAIN_SHIFT) |
785209ff23fSmrg	                            (0x100 << RADEON_Y_GAIN_SHIFT);
786209ff23fSmrg
787209ff23fSmrg    save->tv_master_cntl = (RADEON_VIN_ASYNC_RST
788209ff23fSmrg			    | RADEON_CRT_FIFO_CE_EN
789209ff23fSmrg			    | RADEON_TV_FIFO_CE_EN
790209ff23fSmrg			    | RADEON_TV_ON);
791209ff23fSmrg
792209ff23fSmrg    if (!IS_R300_VARIANT)
793209ff23fSmrg	save->tv_master_cntl |= RADEON_TVCLK_ALWAYS_ONb;
794209ff23fSmrg
795b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
796b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J)
797209ff23fSmrg	save->tv_master_cntl |= RADEON_RESTART_PHASE_FIX;
798209ff23fSmrg
799209ff23fSmrg    save->tv_modulator_cntl1 = RADEON_SLEW_RATE_LIMIT
800209ff23fSmrg	                       | RADEON_SYNC_TIP_LEVEL
801209ff23fSmrg	                       | RADEON_YFLT_EN
802209ff23fSmrg	                       | RADEON_UVFLT_EN
803209ff23fSmrg	                       | (6 << RADEON_CY_FILT_BLEND_SHIFT);
804209ff23fSmrg
805b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
806b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J) {
807209ff23fSmrg	save->tv_modulator_cntl1 |= (0x46 << RADEON_SET_UP_LEVEL_SHIFT)
808209ff23fSmrg	                            | (0x3b << RADEON_BLANK_LEVEL_SHIFT);
809209ff23fSmrg	save->tv_modulator_cntl2 = (-111 & RADEON_TV_U_BURST_LEVEL_MASK) |
810209ff23fSmrg	    ((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
811b7e1c893Smrg    } else if (tvout->tvStd == TV_STD_SCART_PAL) {
812209ff23fSmrg	save->tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN;
813209ff23fSmrg	save->tv_modulator_cntl2 = (0 & RADEON_TV_U_BURST_LEVEL_MASK) |
814209ff23fSmrg	    ((0 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
815209ff23fSmrg    } else {
816209ff23fSmrg	save->tv_modulator_cntl1 |= RADEON_ALT_PHASE_EN
817209ff23fSmrg	                            | (0x3b << RADEON_SET_UP_LEVEL_SHIFT)
818209ff23fSmrg	                            | (0x3b << RADEON_BLANK_LEVEL_SHIFT);
819209ff23fSmrg	save->tv_modulator_cntl2 = (-78 & RADEON_TV_U_BURST_LEVEL_MASK) |
820209ff23fSmrg	    ((62 & RADEON_TV_V_BURST_LEVEL_MASK) << RADEON_TV_V_BURST_LEVEL_SHIFT);
821209ff23fSmrg    }
822209ff23fSmrg
823209ff23fSmrg    save->pll_test_cntl = 0;
824209ff23fSmrg
825209ff23fSmrg    save->tv_pre_dac_mux_cntl = (RADEON_Y_RED_EN
826209ff23fSmrg				 | RADEON_C_GRN_EN
827209ff23fSmrg				 | RADEON_CMP_BLU_EN
828209ff23fSmrg				 | RADEON_DAC_DITHER_EN);
829209ff23fSmrg
830209ff23fSmrg    save->tv_rgb_cntl = (RADEON_RGB_DITHER_EN
831209ff23fSmrg			 | RADEON_TVOUT_SCALE_EN
832209ff23fSmrg			 | (0x0b << RADEON_UVRAM_READ_MARGIN_SHIFT)
833b7e1c893Smrg			 | (0x07 << RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT)
834b7e1c893Smrg			 | RADEON_RGB_ATTEN_SEL(0x3)
835b7e1c893Smrg			 | RADEON_RGB_ATTEN_VAL(0xc));
836209ff23fSmrg
837209ff23fSmrg    if (IsPrimary) {
838209ff23fSmrg	if (radeon_output->Flags & RADEON_USE_RMX)
839209ff23fSmrg	    save->tv_rgb_cntl |= RADEON_RGB_SRC_SEL_RMX;
840209ff23fSmrg	else
841209ff23fSmrg	    save->tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC1;
842209ff23fSmrg    } else {
843209ff23fSmrg	save->tv_rgb_cntl |= RADEON_RGB_SRC_SEL_CRTC2;
844209ff23fSmrg    }
845209ff23fSmrg
846209ff23fSmrg    save->tv_sync_cntl = RADEON_SYNC_PUB | RADEON_TV_SYNC_IO_DRIVE;
847209ff23fSmrg
848209ff23fSmrg    save->tv_sync_size = constPtr->horResolution + 8;
849209ff23fSmrg
850b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
851b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
852b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M ||
853b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_60)
854209ff23fSmrg	vert_space = constPtr->verTotal * 2 * 10000 / NTSC_TV_LINES_PER_FRAME;
855209ff23fSmrg    else
856209ff23fSmrg	vert_space = constPtr->verTotal * 2 * 10000 / PAL_TV_LINES_PER_FRAME;
857209ff23fSmrg
858209ff23fSmrg    save->tv_vscaler_cntl1 = RADEON_Y_W_EN;
859209ff23fSmrg    save->tv_vscaler_cntl1 =
860209ff23fSmrg	(save->tv_vscaler_cntl1 & 0xe3ff0000) | (vert_space * (1 << FRAC_BITS) / 10000);
861b7e1c893Smrg
862b7e1c893Smrg    if (pll->reference_freq == 2700)
863b7e1c893Smrg	save->tv_vscaler_cntl1 |= RADEON_RESTART_FIELD;
864b7e1c893Smrg
865209ff23fSmrg    if (constPtr->horResolution == 1024)
866209ff23fSmrg	save->tv_vscaler_cntl1 |= (4 << RADEON_Y_DEL_W_SIG_SHIFT);
867209ff23fSmrg    else
868209ff23fSmrg	save->tv_vscaler_cntl1 |= (2 << RADEON_Y_DEL_W_SIG_SHIFT);
869209ff23fSmrg
870b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
871b7e1c893Smrg        tvout->tvStd == TV_STD_NTSC_J ||
872b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_M ||
873b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_60)
874209ff23fSmrg	flicker_removal =
875209ff23fSmrg	    (float) constPtr->verTotal * 2.0 / NTSC_TV_LINES_PER_FRAME + 0.5;
876209ff23fSmrg    else
877209ff23fSmrg	flicker_removal =
878209ff23fSmrg	    (float) constPtr->verTotal * 2.0 / PAL_TV_LINES_PER_FRAME + 0.5;
879209ff23fSmrg
880209ff23fSmrg    if (flicker_removal < 3)
881209ff23fSmrg	flicker_removal = 3;
882209ff23fSmrg    for (i = 0; i < 6; ++i) {
883209ff23fSmrg	if (flicker_removal == SLOPE_limit[i])
884209ff23fSmrg	    break;
885209ff23fSmrg    }
886209ff23fSmrg    save->tv_y_saw_tooth_cntl =
887209ff23fSmrg	(vert_space * SLOPE_value[i] * (1 << (FRAC_BITS - 1)) + 5001) / 10000 / 8
888209ff23fSmrg	| ((SLOPE_value[i] * (1 << (FRAC_BITS - 1)) / 8) << 16);
889209ff23fSmrg    save->tv_y_fall_cntl =
890209ff23fSmrg	(YCOEF_EN_value[i] << 17) | ((YCOEF_value[i] * (1 << 8) / 8) << 24) |
891209ff23fSmrg	RADEON_Y_FALL_PING_PONG | (272 * SLOPE_value[i] / 8) * (1 << (FRAC_BITS - 1)) /
892209ff23fSmrg	1024;
893209ff23fSmrg    save->tv_y_rise_cntl =
894209ff23fSmrg	RADEON_Y_RISE_PING_PONG
895209ff23fSmrg	| (flicker_removal * 1024 - 272) * SLOPE_value[i] / 8 * (1 << (FRAC_BITS - 1)) / 1024;
896209ff23fSmrg
897209ff23fSmrg    save->tv_vscaler_cntl2 = ((save->tv_vscaler_cntl2 & 0x00fffff0)
898209ff23fSmrg			      | (0x10 << 24)
899209ff23fSmrg			      | RADEON_DITHER_MODE
900209ff23fSmrg			      | RADEON_Y_OUTPUT_DITHER_EN
901209ff23fSmrg			      | RADEON_UV_OUTPUT_DITHER_EN
902209ff23fSmrg			      | RADEON_UV_TO_BUF_DITHER_EN);
903209ff23fSmrg
904209ff23fSmrg    tmp = (save->tv_vscaler_cntl1 >> RADEON_UV_INC_SHIFT) & RADEON_UV_INC_MASK;
905209ff23fSmrg    tmp = ((16384 * 256 * 10) / tmp + 5) / 10;
906209ff23fSmrg    tmp = (tmp << RADEON_UV_OUTPUT_POST_SCALE_SHIFT) | 0x000b0000;
907209ff23fSmrg    save->tv_timing_cntl = tmp;
908209ff23fSmrg
909b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
910b7e1c893Smrg        tvout->tvStd == TV_STD_NTSC_J ||
911b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_M ||
912b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_60)
913b7e1c893Smrg	save->tv_dac_cntl = tvdac->ntsc_tvdac_adj;
914209ff23fSmrg    else
915b7e1c893Smrg	save->tv_dac_cntl = tvdac->pal_tvdac_adj;
916209ff23fSmrg
917209ff23fSmrg    save->tv_dac_cntl |= (RADEON_TV_DAC_NBLANK | RADEON_TV_DAC_NHOLD);
918209ff23fSmrg
919b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
920b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J)
921209ff23fSmrg	save->tv_dac_cntl |= RADEON_TV_DAC_STD_NTSC;
922209ff23fSmrg    else
923209ff23fSmrg	save->tv_dac_cntl |= RADEON_TV_DAC_STD_PAL;
924209ff23fSmrg
925209ff23fSmrg#if 0
926209ff23fSmrg    /* needs fixes for r4xx */
927209ff23fSmrg    save->tv_dac_cntl |= (RADEON_TV_DAC_RDACPD | RADEON_TV_DAC_GDACPD
928209ff23fSmrg	                 | RADEON_TV_DAC_BDACPD);
929209ff23fSmrg
930209ff23fSmrg    if (radeon_output->MonType == MT_CTV) {
931209ff23fSmrg	save->tv_dac_cntl &= ~RADEON_TV_DAC_BDACPD;
932209ff23fSmrg    }
933209ff23fSmrg
934209ff23fSmrg    if (radeon_output->MonType == MT_STV) {
935209ff23fSmrg	save->tv_dac_cntl &= ~(RADEON_TV_DAC_RDACPD |
936209ff23fSmrg			       RADEON_TV_DAC_GDACPD);
937209ff23fSmrg    }
938209ff23fSmrg#endif
939209ff23fSmrg
940b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
941b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J) {
942b7e1c893Smrg	if (pll->reference_freq == 2700) {
943b7e1c893Smrg	    m = NTSC_TV_PLL_M_27;
944b7e1c893Smrg	    n = NTSC_TV_PLL_N_27;
945b7e1c893Smrg	    p = NTSC_TV_PLL_P_27;
946b7e1c893Smrg	} else {
947b7e1c893Smrg	    m = NTSC_TV_PLL_M_14;
948b7e1c893Smrg	    n = NTSC_TV_PLL_N_14;
949b7e1c893Smrg	    p = NTSC_TV_PLL_P_14;
950b7e1c893Smrg	}
951b7e1c893Smrg    } else {
952b7e1c893Smrg	if (pll->reference_freq == 2700) {
953b7e1c893Smrg	    m = PAL_TV_PLL_M_27;
954b7e1c893Smrg	    n = PAL_TV_PLL_N_27;
955b7e1c893Smrg	    p = PAL_TV_PLL_P_27;
956b7e1c893Smrg	} else {
957ad43ddacSmrg	    m = PAL_TV_PLL_M_14;
958ad43ddacSmrg	    n = PAL_TV_PLL_N_14;
959ad43ddacSmrg	    p = PAL_TV_PLL_P_14;
960b7e1c893Smrg	}
961b7e1c893Smrg    }
962b7e1c893Smrg    save->tv_pll_cntl = (m & RADEON_TV_M0LO_MASK) |
963b7e1c893Smrg	(((m >> 8) & RADEON_TV_M0HI_MASK) << RADEON_TV_M0HI_SHIFT) |
964b7e1c893Smrg	((n & RADEON_TV_N0LO_MASK) << RADEON_TV_N0LO_SHIFT) |
965b7e1c893Smrg	(((n >> 9) & RADEON_TV_N0HI_MASK) << RADEON_TV_N0HI_SHIFT) |
966b7e1c893Smrg	((p & RADEON_TV_P_MASK) << RADEON_TV_P_SHIFT);
967209ff23fSmrg
968209ff23fSmrg    save->tv_pll_cntl1 =  (((4 & RADEON_TVPCP_MASK)<< RADEON_TVPCP_SHIFT) |
969209ff23fSmrg			   ((4 & RADEON_TVPVG_MASK) << RADEON_TVPVG_SHIFT) |
970209ff23fSmrg			   ((1 & RADEON_TVPDC_MASK)<< RADEON_TVPDC_SHIFT) |
971209ff23fSmrg			   RADEON_TVCLK_SRC_SEL_TVPLL |
972209ff23fSmrg			   RADEON_TVPLL_TEST_DIS);
973209ff23fSmrg
974209ff23fSmrg    save->tv_upsamp_and_gain_cntl = RADEON_YUPSAMP_EN | RADEON_UVUPSAMP_EN;
975209ff23fSmrg
976209ff23fSmrg    save->tv_uv_adr = 0xc8;
977209ff23fSmrg
978209ff23fSmrg    save->tv_vdisp = constPtr->verResolution - 1;
979209ff23fSmrg
980b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
981b7e1c893Smrg        tvout->tvStd == TV_STD_NTSC_J ||
982b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_M ||
983b7e1c893Smrg        tvout->tvStd == TV_STD_PAL_60)
984209ff23fSmrg	save->tv_ftotal = NTSC_TV_VFTOTAL;
985209ff23fSmrg    else
986209ff23fSmrg	save->tv_ftotal = PAL_TV_VFTOTAL;
987209ff23fSmrg
988209ff23fSmrg    save->tv_vtotal = constPtr->verTotal - 1;
989209ff23fSmrg
990b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
991b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
992b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
993209ff23fSmrg	hor_timing = hor_timing_NTSC;
994209ff23fSmrg    } else {
995209ff23fSmrg	hor_timing = hor_timing_PAL;
996209ff23fSmrg    }
997209ff23fSmrg
998b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
999b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
1000b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M ||
1001b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_60) {
1002209ff23fSmrg	vert_timing = vert_timing_NTSC;
1003209ff23fSmrg    } else {
1004209ff23fSmrg	vert_timing = vert_timing_PAL;
1005209ff23fSmrg    }
1006209ff23fSmrg
1007209ff23fSmrg    for (i = 0; i < MAX_H_CODE_TIMING_LEN; i++) {
1008209ff23fSmrg	if ((save->h_code_timing[ i ] = hor_timing[ i ]) == 0)
1009209ff23fSmrg	    break;
1010209ff23fSmrg    }
1011209ff23fSmrg
1012209ff23fSmrg    for (i = 0; i < MAX_V_CODE_TIMING_LEN; i++) {
1013209ff23fSmrg	if ((save->v_code_timing[ i ] = vert_timing[ i ]) == 0)
1014209ff23fSmrg	    break;
1015209ff23fSmrg    }
1016209ff23fSmrg
1017209ff23fSmrg    /*
1018209ff23fSmrg     * This must be called AFTER loading timing tables as they are modified by this function
1019209ff23fSmrg     */
1020209ff23fSmrg    RADEONInitTVRestarts(output, save, mode);
1021209ff23fSmrg
1022209ff23fSmrg    save->dac_cntl &= ~RADEON_DAC_TVO_EN;
1023209ff23fSmrg
1024209ff23fSmrg    if (IS_R300_VARIANT)
1025209ff23fSmrg        save->gpiopad_a = info->SavedReg->gpiopad_a & ~1;
1026209ff23fSmrg
1027209ff23fSmrg    if (IsPrimary) {
1028209ff23fSmrg	save->disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK;
1029209ff23fSmrg	save->disp_output_cntl |= (RADEON_DISP_TVDAC_SOURCE_CRTC
1030209ff23fSmrg				   | RADEON_DISP_TV_SOURCE_CRTC);
1031209ff23fSmrg    	if (info->ChipFamily >= CHIP_FAMILY_R200) {
1032209ff23fSmrg	    save->disp_tv_out_cntl &= ~RADEON_DISP_TV_PATH_SRC_CRTC2;
1033209ff23fSmrg    	} else {
1034209ff23fSmrg            save->disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
1035209ff23fSmrg    	}
1036209ff23fSmrg    } else {
1037209ff23fSmrg	save->disp_output_cntl &= ~RADEON_DISP_DAC_SOURCE_MASK;
1038209ff23fSmrg	save->disp_output_cntl |= RADEON_DISP_TV_SOURCE_CRTC;
1039209ff23fSmrg
1040209ff23fSmrg    	if (info->ChipFamily >= CHIP_FAMILY_R200) {
1041209ff23fSmrg	    save->disp_tv_out_cntl |= RADEON_DISP_TV_PATH_SRC_CRTC2;
1042209ff23fSmrg    	} else {
1043209ff23fSmrg            save->disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL;
1044209ff23fSmrg    	}
1045209ff23fSmrg    }
1046209ff23fSmrg}
1047209ff23fSmrg
1048209ff23fSmrg
1049209ff23fSmrg/* Set hw registers for a new h/v position & h size */
1050209ff23fSmrgvoid RADEONUpdateHVPosition(xf86OutputPtr output, DisplayModePtr mode)
1051209ff23fSmrg{
1052209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
1053209ff23fSmrg    RADEONInfoPtr  info = RADEONPTR(pScrn);
1054209ff23fSmrg    unsigned char *RADEONMMIO = info->MMIO;
1055209ff23fSmrg    Bool reloadTable;
1056209ff23fSmrg    RADEONSavePtr restore = info->ModeReg;
1057209ff23fSmrg
1058209ff23fSmrg    reloadTable = RADEONInitTVRestarts(output, restore, mode);
1059209ff23fSmrg
1060209ff23fSmrg    RADEONRestoreTVRestarts(pScrn, restore);
1061209ff23fSmrg
1062209ff23fSmrg    OUTREG(RADEON_TV_TIMING_CNTL, restore->tv_timing_cntl);
1063209ff23fSmrg
1064209ff23fSmrg    if (reloadTable) {
1065209ff23fSmrg	 OUTREG(RADEON_TV_MASTER_CNTL, restore->tv_master_cntl
1066209ff23fSmrg		                       | RADEON_TV_ASYNC_RST
1067209ff23fSmrg		                       | RADEON_CRT_ASYNC_RST
1068209ff23fSmrg		                       | RADEON_RESTART_PHASE_FIX);
1069209ff23fSmrg
1070209ff23fSmrg	RADEONRestoreTVTimingTables(pScrn, restore);
1071209ff23fSmrg
1072209ff23fSmrg	OUTREG(RADEON_TV_MASTER_CNTL, restore->tv_master_cntl);
1073209ff23fSmrg    }
1074209ff23fSmrg}
1075209ff23fSmrg
1076209ff23fSmrgvoid RADEONAdjustCrtcRegistersForTV(ScrnInfoPtr pScrn, RADEONSavePtr save,
1077209ff23fSmrg				    DisplayModePtr mode, xf86OutputPtr output)
1078209ff23fSmrg{
1079209ff23fSmrg    const TVModeConstants *constPtr;
1080209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
1081b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
1082b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
1083b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
1084209ff23fSmrg
1085209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
1086b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
1087b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
1088b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
1089b7e1c893Smrg	if (pll->reference_freq == 2700)
1090b7e1c893Smrg	    constPtr = &availableTVModes[0];
1091b7e1c893Smrg	else
1092b7e1c893Smrg	    constPtr = &availableTVModes[2];
1093b7e1c893Smrg    } else {
1094b7e1c893Smrg	if (pll->reference_freq == 2700)
1095b7e1c893Smrg	    constPtr = &availableTVModes[1];
1096b7e1c893Smrg	else
1097ad43ddacSmrg	    constPtr = &availableTVModes[3];
1098b7e1c893Smrg    }
1099209ff23fSmrg
1100209ff23fSmrg    save->crtc_h_total_disp = (((constPtr->horResolution / 8) - 1) << RADEON_CRTC_H_DISP_SHIFT) |
1101209ff23fSmrg	(((constPtr->horTotal / 8) - 1) << RADEON_CRTC_H_TOTAL_SHIFT);
1102209ff23fSmrg
1103209ff23fSmrg    save->crtc_h_sync_strt_wid = (save->crtc_h_sync_strt_wid
1104209ff23fSmrg				  & ~(RADEON_CRTC_H_SYNC_STRT_PIX | RADEON_CRTC_H_SYNC_STRT_CHAR)) |
1105209ff23fSmrg	(((constPtr->horSyncStart / 8) - 1) << RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) |
1106209ff23fSmrg	(constPtr->horSyncStart & 7);
1107209ff23fSmrg
1108209ff23fSmrg    save->crtc_v_total_disp = ((constPtr->verResolution - 1) << RADEON_CRTC_V_DISP_SHIFT) |
1109209ff23fSmrg	((constPtr->verTotal - 1) << RADEON_CRTC_V_TOTAL_SHIFT);
1110209ff23fSmrg
1111209ff23fSmrg    save->crtc_v_sync_strt_wid = (save->crtc_v_sync_strt_wid & ~RADEON_CRTC_V_SYNC_STRT) |
1112209ff23fSmrg	((constPtr->verSyncStart - 1) << RADEON_CRTC_V_SYNC_STRT_SHIFT);
1113209ff23fSmrg
1114209ff23fSmrg}
1115209ff23fSmrg
1116209ff23fSmrgvoid RADEONAdjustPLLRegistersForTV(ScrnInfoPtr pScrn, RADEONSavePtr save,
1117209ff23fSmrg				   DisplayModePtr mode, xf86OutputPtr output)
1118209ff23fSmrg{
1119209ff23fSmrg    unsigned postDiv;
1120209ff23fSmrg    const TVModeConstants *constPtr;
1121209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
1122b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
1123b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
1124b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
1125209ff23fSmrg
1126209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
1127b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
1128b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
1129b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
1130b7e1c893Smrg	if (pll->reference_freq == 2700)
1131b7e1c893Smrg	    constPtr = &availableTVModes[0];
1132b7e1c893Smrg	else
1133b7e1c893Smrg	    constPtr = &availableTVModes[2];
1134b7e1c893Smrg    } else {
1135b7e1c893Smrg	if (pll->reference_freq == 2700)
1136b7e1c893Smrg	    constPtr = &availableTVModes[1];
1137b7e1c893Smrg	else
1138ad43ddacSmrg	    constPtr = &availableTVModes[3];
1139b7e1c893Smrg    }
1140209ff23fSmrg
1141209ff23fSmrg    save->htotal_cntl = (constPtr->horTotal & 0x7 /*0xf*/) | RADEON_HTOT_CNTL_VGA_EN;
1142209ff23fSmrg
1143209ff23fSmrg    save->ppll_ref_div = constPtr->crtcPLL_M;
1144209ff23fSmrg
1145209ff23fSmrg    switch (constPtr->crtcPLL_postDiv) {
1146209ff23fSmrg    case 1:
1147209ff23fSmrg	postDiv = 0;
1148209ff23fSmrg	break;
1149209ff23fSmrg    case 2:
1150209ff23fSmrg	postDiv = 1;
1151209ff23fSmrg	break;
1152209ff23fSmrg    case 3:
1153209ff23fSmrg	postDiv = 4;
1154209ff23fSmrg	break;
1155209ff23fSmrg    case 4:
1156209ff23fSmrg	postDiv = 2;
1157209ff23fSmrg	break;
1158209ff23fSmrg    case 6:
1159209ff23fSmrg	postDiv = 6;
1160209ff23fSmrg	break;
1161209ff23fSmrg    case 8:
1162209ff23fSmrg	postDiv = 3;
1163209ff23fSmrg	break;
1164209ff23fSmrg    case 12:
1165209ff23fSmrg	postDiv = 7;
1166209ff23fSmrg	break;
1167209ff23fSmrg    case 16:
1168209ff23fSmrg    default:
1169209ff23fSmrg	postDiv = 5;
1170209ff23fSmrg	break;
1171209ff23fSmrg    }
1172209ff23fSmrg
1173209ff23fSmrg    save->ppll_div_3 = (constPtr->crtcPLL_N & 0x7ff) | (postDiv << 16);
1174209ff23fSmrg
1175209ff23fSmrg    save->pixclks_cntl &= ~(RADEON_PIX2CLK_SRC_SEL_MASK | RADEON_PIXCLK_TV_SRC_SEL);
1176209ff23fSmrg    save->pixclks_cntl |= RADEON_PIX2CLK_SRC_SEL_P2PLLCLK;
1177209ff23fSmrg
1178209ff23fSmrg}
1179209ff23fSmrg
1180209ff23fSmrgvoid RADEONAdjustCrtc2RegistersForTV(ScrnInfoPtr pScrn, RADEONSavePtr save,
1181209ff23fSmrg				     DisplayModePtr mode, xf86OutputPtr output)
1182209ff23fSmrg{
1183209ff23fSmrg    const TVModeConstants *constPtr;
1184209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
1185b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
1186b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
1187b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
1188209ff23fSmrg
1189209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
1190b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
1191b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
1192b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
1193b7e1c893Smrg	if (pll->reference_freq == 2700)
1194b7e1c893Smrg	    constPtr = &availableTVModes[0];
1195b7e1c893Smrg	else
1196b7e1c893Smrg	    constPtr = &availableTVModes[2];
1197b7e1c893Smrg    } else {
1198b7e1c893Smrg	if (pll->reference_freq == 2700)
1199b7e1c893Smrg	    constPtr = &availableTVModes[1];
1200b7e1c893Smrg	else
1201ad43ddacSmrg	    constPtr = &availableTVModes[3];
1202b7e1c893Smrg    }
1203209ff23fSmrg
1204209ff23fSmrg    save->crtc2_h_total_disp = (((constPtr->horResolution / 8) - 1) << RADEON_CRTC_H_DISP_SHIFT) |
1205209ff23fSmrg	(((constPtr->horTotal / 8) - 1) << RADEON_CRTC_H_TOTAL_SHIFT);
1206209ff23fSmrg
1207209ff23fSmrg    save->crtc2_h_sync_strt_wid = (save->crtc2_h_sync_strt_wid
1208209ff23fSmrg				  & ~(RADEON_CRTC_H_SYNC_STRT_PIX | RADEON_CRTC_H_SYNC_STRT_CHAR)) |
1209209ff23fSmrg	(((constPtr->horSyncStart / 8) - 1) << RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) |
1210209ff23fSmrg	(constPtr->horSyncStart & 7);
1211209ff23fSmrg
1212209ff23fSmrg    save->crtc2_v_total_disp = ((constPtr->verResolution - 1) << RADEON_CRTC_V_DISP_SHIFT) |
1213209ff23fSmrg	((constPtr->verTotal - 1) << RADEON_CRTC_V_TOTAL_SHIFT);
1214209ff23fSmrg
1215c503f109Smrg    save->crtc2_v_sync_strt_wid = (save->crtc2_v_sync_strt_wid & ~RADEON_CRTC_V_SYNC_STRT) |
1216209ff23fSmrg	((constPtr->verSyncStart - 1) << RADEON_CRTC_V_SYNC_STRT_SHIFT);
1217209ff23fSmrg
1218209ff23fSmrg}
1219209ff23fSmrg
1220209ff23fSmrgvoid RADEONAdjustPLL2RegistersForTV(ScrnInfoPtr pScrn, RADEONSavePtr save,
1221209ff23fSmrg				    DisplayModePtr mode, xf86OutputPtr output)
1222209ff23fSmrg{
1223209ff23fSmrg    unsigned postDiv;
1224209ff23fSmrg    const TVModeConstants *constPtr;
1225209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
1226b7e1c893Smrg    radeon_tvout_ptr tvout = &radeon_output->tvout;
1227b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
1228b7e1c893Smrg    RADEONPLLPtr pll = &info->pll;
1229209ff23fSmrg
1230209ff23fSmrg    /* FIXME: need to revisit this when we add more modes */
1231b7e1c893Smrg    if (tvout->tvStd == TV_STD_NTSC ||
1232b7e1c893Smrg	tvout->tvStd == TV_STD_NTSC_J ||
1233b7e1c893Smrg	tvout->tvStd == TV_STD_PAL_M) {
1234b7e1c893Smrg	if (pll->reference_freq == 2700)
1235b7e1c893Smrg	    constPtr = &availableTVModes[0];
1236b7e1c893Smrg	else
1237b7e1c893Smrg	    constPtr = &availableTVModes[2];
1238b7e1c893Smrg    } else {
1239b7e1c893Smrg	if (pll->reference_freq == 2700)
1240b7e1c893Smrg	    constPtr = &availableTVModes[1];
1241b7e1c893Smrg	else
1242ad43ddacSmrg	    constPtr = &availableTVModes[3];
1243b7e1c893Smrg    }
1244209ff23fSmrg
1245209ff23fSmrg    save->htotal_cntl2 = (constPtr->horTotal & 0x7); /* 0xf */
1246209ff23fSmrg
1247209ff23fSmrg    save->p2pll_ref_div = constPtr->crtcPLL_M;
1248209ff23fSmrg
1249209ff23fSmrg    switch (constPtr->crtcPLL_postDiv) {
1250209ff23fSmrg    case 1:
1251209ff23fSmrg	postDiv = 0;
1252209ff23fSmrg	break;
1253209ff23fSmrg    case 2:
1254209ff23fSmrg	postDiv = 1;
1255209ff23fSmrg	break;
1256209ff23fSmrg    case 3:
1257209ff23fSmrg	postDiv = 4;
1258209ff23fSmrg	break;
1259209ff23fSmrg    case 4:
1260209ff23fSmrg	postDiv = 2;
1261209ff23fSmrg	break;
1262209ff23fSmrg    case 6:
1263209ff23fSmrg	postDiv = 6;
1264209ff23fSmrg	break;
1265209ff23fSmrg    case 8:
1266209ff23fSmrg	postDiv = 3;
1267209ff23fSmrg	break;
1268209ff23fSmrg    case 12:
1269209ff23fSmrg	postDiv = 7;
1270209ff23fSmrg	break;
1271209ff23fSmrg    case 16:
1272209ff23fSmrg    default:
1273209ff23fSmrg	postDiv = 5;
1274209ff23fSmrg	break;
1275209ff23fSmrg    }
1276209ff23fSmrg
1277209ff23fSmrg    save->p2pll_div_0 = (constPtr->crtcPLL_N & 0x7ff) | (postDiv << 16);
1278209ff23fSmrg
1279209ff23fSmrg    save->pixclks_cntl &= ~RADEON_PIX2CLK_SRC_SEL_MASK;
1280209ff23fSmrg    save->pixclks_cntl |= (RADEON_PIX2CLK_SRC_SEL_P2PLLCLK
1281209ff23fSmrg			   | RADEON_PIXCLK_TV_SRC_SEL);
1282209ff23fSmrg
1283209ff23fSmrg}
1284