1/*
2 * Copyright © 2006 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 *    Eric Anholt <eric@anholt.net>
25 *
26 */
27
28/** @file
29 * Integrated TV-out support for the 915GM and 945GM.
30 */
31
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
36#include "xf86.h"
37#include "i830.h"
38#include "i830_display.h"
39#include "i830_bios.h"
40#include "X11/Xatom.h"
41#include <string.h>
42
43enum tv_type {
44    TV_TYPE_NONE,
45    TV_TYPE_UNKNOWN,
46    TV_TYPE_COMPOSITE,
47    TV_TYPE_SVIDEO,
48    TV_TYPE_COMPONENT
49};
50
51enum tv_margin {
52    TV_MARGIN_LEFT, TV_MARGIN_TOP,
53    TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
54};
55
56/** Private structure for the integrated TV support */
57struct i830_tv_priv {
58    int type;
59    Bool force_type;
60    char *tv_format;
61    int margin[4];
62    uint8_t brightness;
63    uint8_t contrast;
64    uint8_t saturation;
65    uint8_t hue;
66    uint32_t save_TV_H_CTL_1;
67    uint32_t save_TV_H_CTL_2;
68    uint32_t save_TV_H_CTL_3;
69    uint32_t save_TV_V_CTL_1;
70    uint32_t save_TV_V_CTL_2;
71    uint32_t save_TV_V_CTL_3;
72    uint32_t save_TV_V_CTL_4;
73    uint32_t save_TV_V_CTL_5;
74    uint32_t save_TV_V_CTL_6;
75    uint32_t save_TV_V_CTL_7;
76    uint32_t save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
77
78    uint32_t save_TV_CSC_Y;
79    uint32_t save_TV_CSC_Y2;
80    uint32_t save_TV_CSC_U;
81    uint32_t save_TV_CSC_U2;
82    uint32_t save_TV_CSC_V;
83    uint32_t save_TV_CSC_V2;
84    uint32_t save_TV_CLR_KNOBS;
85    uint32_t save_TV_CLR_LEVEL;
86    uint32_t save_TV_WIN_POS;
87    uint32_t save_TV_WIN_SIZE;
88    uint32_t save_TV_FILTER_CTL_1;
89    uint32_t save_TV_FILTER_CTL_2;
90    uint32_t save_TV_FILTER_CTL_3;
91
92    uint32_t save_TV_H_LUMA[60];
93    uint32_t save_TV_H_CHROMA[60];
94    uint32_t save_TV_V_LUMA[43];
95    uint32_t save_TV_V_CHROMA[43];
96
97    uint32_t save_TV_DAC;
98    uint32_t save_TV_CTL;
99};
100
101typedef struct {
102    int	blank, black, burst;
103} video_levels_t;
104
105typedef struct {
106    float   ry, gy, by, ay;
107    float   ru, gu, bu, au;
108    float   rv, gv, bv, av;
109} color_conversion_t;
110
111static const uint32_t filter_table[] = {
112    0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
113    0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
114    0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
115    0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
116    0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
117    0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
118    0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
119    0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
120    0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
121    0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
122    0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
123    0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
124    0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
125    0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
126    0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
127    0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
128    0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
129    0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
130    0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
131    0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
132    0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
133    0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
134    0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
135    0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
136    0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
137    0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
138    0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
139    0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
140    0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
141    0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
142    0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
143    0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
144    0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
145    0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
146    0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
147    0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
148    0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
149    0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
150    0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
151    0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
152    0x28003100, 0x28002F00, 0x00003100, 0x36403000,
153    0x2D002CC0, 0x30003640, 0x2D0036C0,
154    0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
155    0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
156    0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
157    0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
158    0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
159    0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
160    0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
161    0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
162    0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
163    0x28003100, 0x28002F00, 0x00003100,
164};
165
166typedef struct {
167    char *name;
168    int	clock;
169    double refresh;
170    uint32_t oversample;
171    int hsync_end, hblank_start, hblank_end, htotal;
172    Bool progressive, trilevel_sync, component_only;
173    int vsync_start_f1, vsync_start_f2, vsync_len;
174    Bool veq_ena;
175    int veq_start_f1, veq_start_f2, veq_len;
176    int vi_end_f1, vi_end_f2, nbr_end;
177    Bool burst_ena;
178    int hburst_start, hburst_len;
179    int vburst_start_f1, vburst_end_f1;
180    int vburst_start_f2, vburst_end_f2;
181    int vburst_start_f3, vburst_end_f3;
182    int vburst_start_f4, vburst_end_f4;
183    /*
184     * subcarrier programming
185     */
186    int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
187    uint32_t sc_reset;
188    Bool pal_burst;
189    /*
190     * blank/black levels
191     */
192    video_levels_t	composite_levels, svideo_levels;
193    color_conversion_t	composite_color, svideo_color;
194    const uint32_t *filter_table;
195    int max_srcw;
196} tv_mode_t;
197
198
199/*
200 * Sub carrier DDA
201 *
202 *  I think this works as follows:
203 *
204 *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
205 *
206 * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
207 *
208 * So,
209 *  dda1_ideal = subcarrier/pixel * 4096
210 *  dda1_inc = floor (dda1_ideal)
211 *  dda2 = dda1_ideal - dda1_inc
212 *
213 *  then pick a ratio for dda2 that gives the closest approximation. If
214 *  you can't get close enough, you can play with dda3 as well. This
215 *  seems likely to happen when dda2 is small as the jumps would be larger
216 *
217 * To invert this,
218 *
219 *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
220 *
221 * The constants below were all computed using a 107.520MHz clock
222 */
223
224/**
225 * Register programming values for TV modes.
226 *
227 * These values account for -1s required.
228 */
229
230const static tv_mode_t tv_modes[] = {
231    {
232	.name		= "NTSC-M",
233	.clock		= 108000,
234	.refresh	= 29.97,
235	.oversample	= TV_OVERSAMPLE_8X,
236	.component_only = 0,
237	/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
238
239	.hsync_end	= 64,		    .hblank_end		= 124,
240	.hblank_start	= 836,		    .htotal		= 857,
241
242	.progressive	= FALSE,	    .trilevel_sync = FALSE,
243
244	.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
245	.vsync_len	= 6,
246
247	.veq_ena	= TRUE,		    .veq_start_f1	= 0,
248	.veq_start_f2	= 1,		    .veq_len		= 18,
249
250	.vi_end_f1	= 20,		    .vi_end_f2		= 21,
251	.nbr_end	= 240,
252
253	.burst_ena	= TRUE,
254	.hburst_start	= 72,		    .hburst_len		= 34,
255	.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
256	.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
257	.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
258	.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
259
260	/* desired 3.5800000 actual 3.5800000 clock 107.52 */
261	.dda1_inc	=    135,
262	.dda2_inc	=  20800,	    .dda2_size		=  27456,
263	.dda3_inc	=      0,	    .dda3_size		=      0,
264	.sc_reset	= TV_SC_RESET_EVERY_4,
265	.pal_burst	= FALSE,
266
267	.composite_levels = { .blank = 225, .black = 267, .burst = 113 },
268	.composite_color = {
269	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082,
270	    .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000,
271	    .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000,
272	},
273
274	.svideo_levels    = { .blank = 266, .black = 316, .burst = 133 },
275	.svideo_color = {
276	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006,
277	    .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000,
278	    .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000,
279	},
280	.filter_table = filter_table,
281    },
282    {
283	.name		= "NTSC-443",
284	.clock		= 108000,
285	.refresh	= 29.97,
286	.oversample	= TV_OVERSAMPLE_8X,
287	.component_only = 0,
288	/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
289	.hsync_end	= 64,		    .hblank_end		= 124,
290	.hblank_start	= 836,		    .htotal		= 857,
291
292	.progressive	= FALSE,	    .trilevel_sync = FALSE,
293
294	.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
295	.vsync_len	= 6,
296
297	.veq_ena	= TRUE,		    .veq_start_f1	= 0,
298	.veq_start_f2	= 1,		    .veq_len		= 18,
299
300	.vi_end_f1	= 20,		    .vi_end_f2		= 21,
301	.nbr_end	= 240,
302
303	.burst_ena	= 8,
304	.hburst_start	= 72,		    .hburst_len		= 34,
305	.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
306	.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
307	.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
308	.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
309
310	/* desired 4.4336180 actual 4.4336180 clock 107.52 */
311	.dda1_inc       =    168,
312	.dda2_inc       =   4093,       .dda2_size      =  27456,
313	.dda3_inc       =    310,       .dda3_size      =    525,
314	.sc_reset   = TV_SC_RESET_NEVER,
315	.pal_burst  = FALSE,
316
317	.composite_levels = { .blank = 225, .black = 267, .burst = 113 },
318	.composite_color = {
319	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082,
320	    .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000,
321	    .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000,
322	},
323
324	.svideo_levels    = { .blank = 266, .black = 316, .burst = 133 },
325	.svideo_color = {
326	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006,
327	    .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000,
328	    .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000,
329	},
330	.filter_table = filter_table,
331    },
332    {
333	.name		= "NTSC-J",
334	.clock		= 108000,
335	.refresh	= 29.97,
336	.oversample	= TV_OVERSAMPLE_8X,
337	.component_only = 0,
338
339	/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
340	.hsync_end	= 64,		    .hblank_end		= 124,
341	.hblank_start = 836,	    .htotal		= 857,
342
343	.progressive	= FALSE,    .trilevel_sync = FALSE,
344
345	.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
346	.vsync_len	= 6,
347
348	.veq_ena	= TRUE,	    .veq_start_f1	= 0,
349	.veq_start_f2 = 1,	    .veq_len		= 18,
350
351	.vi_end_f1	= 20,		    .vi_end_f2		= 21,
352	.nbr_end	= 240,
353
354	.burst_ena	= TRUE,
355	.hburst_start	= 72,		    .hburst_len		= 34,
356	.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
357	.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
358	.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
359	.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
360
361	/* desired 3.5800000 actual 3.5800000 clock 107.52 */
362	.dda1_inc	=    135,
363	.dda2_inc	=  20800,	    .dda2_size		=  27456,
364	.dda3_inc	=      0,	    .dda3_size		=      0,
365	.sc_reset	= TV_SC_RESET_EVERY_4,
366	.pal_burst	= FALSE,
367
368	.composite_levels = { .blank = 225, .black = 225, .burst = 113 },
369	.composite_color = {
370	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5495,
371	    .ru =-0.0810, .gu =-0.1590, .bu = 0.2400, .au = 1.0000,
372	    .rv = 0.3378, .gv =-0.2829, .bv =-0.0549, .av = 1.0000,
373	},
374
375	.svideo_levels    = { .blank = 266, .black = 266, .burst = 133 },
376	.svideo_color = {
377	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6494,
378	    .ru =-0.0957, .gu =-0.1879, .bu = 0.2836, .au = 1.0000,
379	    .rv = 0.3992, .gv =-0.3343, .bv =-0.0649, .av = 1.0000,
380	},
381	.filter_table = filter_table,
382    },
383    {
384	.name		= "PAL-M",
385	.clock		= 108000,
386	.refresh	= 29.97,
387	.oversample	= TV_OVERSAMPLE_8X,
388	.component_only = 0,
389
390	/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
391	.hsync_end	= 64,		  .hblank_end		= 124,
392	.hblank_start = 836,	  .htotal		= 857,
393
394	.progressive	= FALSE,	    .trilevel_sync = FALSE,
395
396	.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
397	.vsync_len	= 6,
398
399	.veq_ena	= TRUE,		    .veq_start_f1	= 0,
400	.veq_start_f2	= 1,		    .veq_len		= 18,
401
402	.vi_end_f1	= 20,		    .vi_end_f2		= 21,
403	.nbr_end	= 240,
404
405	.burst_ena	= TRUE,
406	.hburst_start	= 72,		    .hburst_len		= 34,
407	.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
408	.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
409	.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
410	.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
411
412	/* desired 3.5800000 actual 3.5800000 clock 107.52 */
413	.dda1_inc	=    135,
414	.dda2_inc	=  16704,	    .dda2_size		=  27456,
415	.dda3_inc	=      0,	    .dda3_size		=      0,
416	.sc_reset	= TV_SC_RESET_EVERY_8,
417	.pal_burst  = TRUE,
418
419	.composite_levels = { .blank = 225, .black = 267, .burst = 113 },
420	.composite_color = {
421	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082,
422	    .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000,
423	    .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000,
424	},
425
426	.svideo_levels    = { .blank = 266, .black = 316, .burst = 133 },
427	.svideo_color = {
428	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006,
429	    .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000,
430	    .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000,
431	},
432	.filter_table = filter_table,
433    },
434    {
435	/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
436	.name	    = "PAL-N",
437	.clock		= 108000,
438	.refresh	= 25.0,
439	.oversample	= TV_OVERSAMPLE_8X,
440	.component_only = 0,
441
442	.hsync_end	= 64,		    .hblank_end		= 128,
443	.hblank_start = 844,	    .htotal		= 863,
444
445	.progressive  = FALSE,    .trilevel_sync = FALSE,
446
447
448	.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
449	.vsync_len	= 6,
450
451	.veq_ena	= TRUE,		    .veq_start_f1	= 0,
452	.veq_start_f2	= 1,		    .veq_len		= 18,
453
454	.vi_end_f1	= 24,		    .vi_end_f2		= 25,
455	.nbr_end	= 286,
456
457	.burst_ena	= TRUE,
458	.hburst_start = 73,		    .hburst_len		= 34,
459	.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
460	.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
461	.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
462	.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
463
464
465	/* desired 4.4336180 actual 4.4336180 clock 107.52 */
466	.dda1_inc       =    135,
467	.dda2_inc       =  23578,       .dda2_size      =  27648,
468	.dda3_inc       =    134,       .dda3_size      =    625,
469	.sc_reset   = TV_SC_RESET_EVERY_8,
470	.pal_burst  = TRUE,
471
472	.composite_levels = { .blank = 225, .black = 267, .burst = 118 },
473	.composite_color = {
474	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082,
475	    .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000,
476	    .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000,
477	},
478
479	.svideo_levels    = { .blank = 266, .black = 316, .burst = 139 },
480	.svideo_color = {
481	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006,
482	    .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000,
483	    .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000,
484	},
485	.filter_table = filter_table,
486    },
487    {
488	/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
489	.name	    = "PAL",
490	.clock		= 108000,
491	.refresh	= 25.0,
492	.oversample	= TV_OVERSAMPLE_8X,
493	.component_only = 0,
494
495	.hsync_end	= 64,		    .hblank_end		= 142,
496	.hblank_start	= 844,	    .htotal		= 863,
497
498	.progressive	= FALSE,    .trilevel_sync = FALSE,
499
500	.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
501	.vsync_len	= 5,
502
503	.veq_ena	= TRUE,	    .veq_start_f1	= 0,
504	.veq_start_f2	= 1,	    .veq_len		= 15,
505
506	.vi_end_f1	= 24,		    .vi_end_f2		= 25,
507	.nbr_end	= 286,
508
509	.burst_ena	= TRUE,
510	.hburst_start	= 73,		    .hburst_len		= 32,
511	.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
512	.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
513	.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
514	.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
515
516	/* desired 4.4336180 actual 4.4336180 clock 107.52 */
517	.dda1_inc       =    168,
518	.dda2_inc       =   4122,       .dda2_size      =  27648,
519	.dda3_inc       =     67,       .dda3_size      =    625,
520	.sc_reset   = TV_SC_RESET_EVERY_8,
521	.pal_burst  = TRUE,
522
523	.composite_levels = { .blank = 237, .black = 237, .burst = 118 },
524	.composite_color = {
525	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5379,
526	    .ru =-0.0793, .gu =-0.1557, .bu = 0.2350, .au = 1.0000,
527	    .rv = 0.3307, .gv =-0.2769, .bv =-0.0538, .av = 1.0000,
528	},
529
530	.svideo_levels    = { .blank = 280, .black = 280, .burst = 139 },
531	.svideo_color = {
532	    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6357,
533	    .ru =-0.0937, .gu =-0.1840, .bu = 0.2777, .au = 1.0000,
534	    .rv = 0.3908, .gv =-0.3273, .bv =-0.0636, .av = 1.0000,
535	},
536	.filter_table = filter_table,
537    },
538    {
539	.name       = "480p@59.94Hz",
540	.clock		= 107520,
541	.refresh	= 59.94,
542	.oversample     = TV_OVERSAMPLE_4X,
543	.component_only = 1,
544
545	.hsync_end      = 64,               .hblank_end         = 122,
546	.hblank_start   = 842,              .htotal             = 857,
547
548	.progressive    = TRUE,		    .trilevel_sync = FALSE,
549
550	.vsync_start_f1 = 12,               .vsync_start_f2     = 12,
551	.vsync_len      = 12,
552
553	.veq_ena        = FALSE,
554
555	.vi_end_f1      = 44,               .vi_end_f2          = 44,
556	.nbr_end        = 479,
557
558	.burst_ena      = FALSE,
559
560	.filter_table = filter_table,
561    },
562    {
563	.name       = "480p@60Hz",
564	.clock		= 107520,
565	.refresh	= 60.0,
566	.oversample     = TV_OVERSAMPLE_4X,
567	.component_only = 1,
568
569	.hsync_end      = 64,               .hblank_end         = 122,
570	.hblank_start   = 842,              .htotal             = 856,
571
572	.progressive    = TRUE,		    .trilevel_sync = FALSE,
573
574	.vsync_start_f1 = 12,               .vsync_start_f2     = 12,
575	.vsync_len      = 12,
576
577	.veq_ena        = FALSE,
578
579	.vi_end_f1      = 44,               .vi_end_f2          = 44,
580	.nbr_end        = 479,
581
582	.burst_ena      = FALSE,
583
584	.filter_table = filter_table,
585    },
586    {
587	.name       = "576p",
588	.clock		= 107520,
589	.refresh	= 50.0,
590	.oversample     = TV_OVERSAMPLE_4X,
591	.component_only = 1,
592
593	.hsync_end      = 64,               .hblank_end         = 139,
594	.hblank_start   = 859,              .htotal             = 863,
595
596	.progressive    = TRUE,		    .trilevel_sync = FALSE,
597
598	.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
599	.vsync_len      = 10,
600
601	.veq_ena        = FALSE,
602
603	.vi_end_f1      = 48,               .vi_end_f2          = 48,
604	.nbr_end        = 575,
605
606	.burst_ena      = FALSE,
607
608	.filter_table = filter_table,
609    },
610    {
611	.name       = "720p@60Hz",
612	.clock		= 148800,
613	.refresh	= 60.0,
614	.oversample     = TV_OVERSAMPLE_2X,
615	.component_only = 1,
616
617	.hsync_end      = 80,               .hblank_end         = 300,
618	.hblank_start   = 1580,             .htotal             = 1649,
619
620	.progressive    = TRUE,		    .trilevel_sync = TRUE,
621
622	.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
623	.vsync_len      = 10,
624
625	.veq_ena        = FALSE,
626
627	.vi_end_f1      = 29,               .vi_end_f2          = 29,
628	.nbr_end        = 719,
629
630	.burst_ena      = FALSE,
631
632	.filter_table = filter_table,
633    },
634    {
635	.name       = "720p@59.94Hz",
636	.clock		= 148800,
637	.refresh	= 59.94,
638	.oversample     = TV_OVERSAMPLE_2X,
639	.component_only = 1,
640
641	.hsync_end      = 80,               .hblank_end         = 300,
642	.hblank_start   = 1580,             .htotal             = 1651,
643
644	.progressive    = TRUE,		    .trilevel_sync = TRUE,
645
646	.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
647	.vsync_len      = 10,
648
649	.veq_ena        = FALSE,
650
651	.vi_end_f1      = 29,               .vi_end_f2          = 29,
652	.nbr_end        = 719,
653
654	.burst_ena      = FALSE,
655
656	.filter_table = filter_table,
657    },
658    {
659	.name       = "720p@50Hz",
660	.clock		= 148800,
661	.refresh	= 50.0,
662	.oversample     = TV_OVERSAMPLE_2X,
663	.component_only = 1,
664
665	.hsync_end      = 80,               .hblank_end         = 300,
666	.hblank_start   = 1580,             .htotal             = 1979,
667
668	.progressive    = TRUE,	            .trilevel_sync = TRUE,
669
670	.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
671	.vsync_len      = 10,
672
673	.veq_ena        = FALSE,
674
675	.vi_end_f1      = 29,               .vi_end_f2          = 29,
676	.nbr_end        = 719,
677
678	.burst_ena      = FALSE,
679
680	.filter_table = filter_table,
681	.max_srcw = 800
682    },
683    {
684	.name       = "1080i@50Hz",
685	.clock		= 148800,
686	.refresh	= 25.0,
687	.oversample     = TV_OVERSAMPLE_2X,
688	.component_only = 1,
689
690	.hsync_end      = 88,               .hblank_end         = 235,
691	.hblank_start   = 2155,             .htotal             = 2639,
692
693	.progressive    = FALSE,	    .trilevel_sync = TRUE,
694
695	.vsync_start_f1 = 4,                .vsync_start_f2     = 5,
696	.vsync_len      = 10,
697
698	.veq_ena	= TRUE,		    .veq_start_f1	= 4,
699	.veq_start_f2   = 4,		    .veq_len		= 10,
700
701	.vi_end_f1      = 21,           .vi_end_f2          = 22,
702	.nbr_end        = 539,
703
704	.burst_ena      = FALSE,
705
706	.filter_table = filter_table,
707    },
708    {
709	.name       = "1080i@60Hz",
710	.clock		= 148800,
711	.refresh	= 30.0,
712	.oversample     = TV_OVERSAMPLE_2X,
713	.component_only = 1,
714
715	.hsync_end      = 88,               .hblank_end         = 235,
716	.hblank_start   = 2155,             .htotal             = 2199,
717
718	.progressive    = FALSE,	    .trilevel_sync = TRUE,
719
720	.vsync_start_f1 = 4,                .vsync_start_f2     = 5,
721	.vsync_len      = 10,
722
723	.veq_ena	= TRUE,		    .veq_start_f1	= 4,
724	.veq_start_f2	= 4,		    .veq_len		= 10,
725
726	.vi_end_f1      = 21,               .vi_end_f2          = 22,
727	.nbr_end        = 539,
728
729	.burst_ena      = FALSE,
730
731	.filter_table = filter_table,
732    },
733    {
734	.name       = "1080i@59.94Hz",
735	.clock		= 148800,
736	.refresh	= 29.97,
737	.oversample     = TV_OVERSAMPLE_2X,
738	.component_only = 1,
739
740	.hsync_end      = 88,               .hblank_end         = 235,
741	.hblank_start   = 2155,             .htotal             = 2201,
742
743	.progressive    = FALSE,	    .trilevel_sync = TRUE,
744
745	.vsync_start_f1 = 4,                .vsync_start_f2     = 5,
746	.vsync_len      = 10,
747
748	.veq_ena	= TRUE,		    .veq_start_f1	= 4,
749	.veq_start_f2 = 4,		    .veq_len = 10,
750
751
752	.vi_end_f1      = 21,               .vi_end_f2		= 22,
753	.nbr_end        = 539,
754
755	.burst_ena      = FALSE,
756
757	.filter_table = filter_table,
758    },
759};
760
761#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0])
762
763static const video_levels_t component_level = {
764	.blank = 279, .black = 279, .burst = 0,
765};
766
767static const color_conversion_t sdtv_component_color = {
768    .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6364,
769    .ru =-0.1687, .gu =-0.3313, .bu = 0.5000, .au = 1.0000,
770    .rv = 0.5000, .gv =-0.4187, .bv =-0.0813, .av = 1.0000,
771};
772
773static const color_conversion_t hdtv_component_color = {
774    .ry = 0.2126, .gy = 0.7152, .by = 0.0722, .ay = 0.6364,
775    .ru =-0.1146, .gu =-0.3854, .bu = 0.5000, .au = 1.0000,
776    .rv = 0.5000, .gv =-0.4542, .bv =-0.0458, .av = 1.0000,
777};
778
779static void
780i830_tv_dpms(xf86OutputPtr output, int mode)
781{
782    ScrnInfoPtr pScrn = output->scrn;
783    I830Ptr pI830 = I830PTR(pScrn);
784
785    switch(mode) {
786	case DPMSModeOn:
787	    OUTREG(TV_CTL, INREG(TV_CTL) | TV_ENC_ENABLE);
788	    break;
789	case DPMSModeStandby:
790	case DPMSModeSuspend:
791	case DPMSModeOff:
792	    OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE);
793	    break;
794    }
795    i830WaitForVblank(pScrn);
796}
797
798static void
799i830_tv_save(xf86OutputPtr output)
800{
801    ScrnInfoPtr		    pScrn = output->scrn;
802    I830Ptr		    pI830 = I830PTR(pScrn);
803    I830OutputPrivatePtr    intel_output = output->driver_private;
804    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
805    int			    i;
806
807    dev_priv->save_TV_H_CTL_1 = INREG(TV_H_CTL_1);
808    dev_priv->save_TV_H_CTL_2 = INREG(TV_H_CTL_2);
809    dev_priv->save_TV_H_CTL_3 = INREG(TV_H_CTL_3);
810    dev_priv->save_TV_V_CTL_1 = INREG(TV_V_CTL_1);
811    dev_priv->save_TV_V_CTL_2 = INREG(TV_V_CTL_2);
812    dev_priv->save_TV_V_CTL_3 = INREG(TV_V_CTL_3);
813    dev_priv->save_TV_V_CTL_4 = INREG(TV_V_CTL_4);
814    dev_priv->save_TV_V_CTL_5 = INREG(TV_V_CTL_5);
815    dev_priv->save_TV_V_CTL_6 = INREG(TV_V_CTL_6);
816    dev_priv->save_TV_V_CTL_7 = INREG(TV_V_CTL_7);
817    dev_priv->save_TV_SC_CTL_1 = INREG(TV_SC_CTL_1);
818    dev_priv->save_TV_SC_CTL_2 = INREG(TV_SC_CTL_2);
819    dev_priv->save_TV_SC_CTL_3 = INREG(TV_SC_CTL_3);
820
821    dev_priv->save_TV_CSC_Y = INREG(TV_CSC_Y);
822    dev_priv->save_TV_CSC_Y2 = INREG(TV_CSC_Y2);
823    dev_priv->save_TV_CSC_U = INREG(TV_CSC_U);
824    dev_priv->save_TV_CSC_U2 = INREG(TV_CSC_U2);
825    dev_priv->save_TV_CSC_V = INREG(TV_CSC_V);
826    dev_priv->save_TV_CSC_V2 = INREG(TV_CSC_V2);
827    dev_priv->save_TV_CLR_KNOBS = INREG(TV_CLR_KNOBS);
828    dev_priv->save_TV_CLR_LEVEL = INREG(TV_CLR_LEVEL);
829    dev_priv->save_TV_WIN_POS = INREG(TV_WIN_POS);
830    dev_priv->save_TV_WIN_SIZE = INREG(TV_WIN_SIZE);
831    dev_priv->save_TV_FILTER_CTL_1 = INREG(TV_FILTER_CTL_1);
832    dev_priv->save_TV_FILTER_CTL_2 = INREG(TV_FILTER_CTL_2);
833    dev_priv->save_TV_FILTER_CTL_3 = INREG(TV_FILTER_CTL_3);
834
835    for (i = 0; i < 60; i++)
836	dev_priv->save_TV_H_LUMA[i] = INREG(TV_H_LUMA_0 + (i <<2));
837    for (i = 0; i < 60; i++)
838	dev_priv->save_TV_H_CHROMA[i] = INREG(TV_H_CHROMA_0 + (i <<2));
839    for (i = 0; i < 43; i++)
840	dev_priv->save_TV_V_LUMA[i] = INREG(TV_V_LUMA_0 + (i <<2));
841    for (i = 0; i < 43; i++)
842	dev_priv->save_TV_V_CHROMA[i] = INREG(TV_V_CHROMA_0 + (i <<2));
843
844    dev_priv->save_TV_DAC = INREG(TV_DAC);
845    dev_priv->save_TV_CTL = INREG(TV_CTL);
846}
847
848static void
849i830_tv_restore(xf86OutputPtr output)
850{
851    ScrnInfoPtr		    pScrn = output->scrn;
852    I830Ptr		    pI830 = I830PTR(pScrn);
853    I830OutputPrivatePtr    intel_output = output->driver_private;
854    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
855    int			    i;
856
857    xf86CrtcPtr	    crtc = output->crtc;
858    I830CrtcPrivatePtr  intel_crtc;
859    if (!crtc)
860	return;
861    intel_crtc = crtc->driver_private;
862    OUTREG(TV_H_CTL_1, dev_priv->save_TV_H_CTL_1);
863    OUTREG(TV_H_CTL_2, dev_priv->save_TV_H_CTL_2);
864    OUTREG(TV_H_CTL_3, dev_priv->save_TV_H_CTL_3);
865    OUTREG(TV_V_CTL_1, dev_priv->save_TV_V_CTL_1);
866    OUTREG(TV_V_CTL_2, dev_priv->save_TV_V_CTL_2);
867    OUTREG(TV_V_CTL_3, dev_priv->save_TV_V_CTL_3);
868    OUTREG(TV_V_CTL_4, dev_priv->save_TV_V_CTL_4);
869    OUTREG(TV_V_CTL_5, dev_priv->save_TV_V_CTL_5);
870    OUTREG(TV_V_CTL_6, dev_priv->save_TV_V_CTL_6);
871    OUTREG(TV_V_CTL_7, dev_priv->save_TV_V_CTL_7);
872    OUTREG(TV_SC_CTL_1, dev_priv->save_TV_SC_CTL_1);
873    OUTREG(TV_SC_CTL_2, dev_priv->save_TV_SC_CTL_2);
874    OUTREG(TV_SC_CTL_3, dev_priv->save_TV_SC_CTL_3);
875
876    OUTREG(TV_CSC_Y, dev_priv->save_TV_CSC_Y);
877    OUTREG(TV_CSC_Y2, dev_priv->save_TV_CSC_Y2);
878    OUTREG(TV_CSC_U, dev_priv->save_TV_CSC_U);
879    OUTREG(TV_CSC_U2, dev_priv->save_TV_CSC_U2);
880    OUTREG(TV_CSC_V, dev_priv->save_TV_CSC_V);
881    OUTREG(TV_CSC_V2, dev_priv->save_TV_CSC_V2);
882    OUTREG(TV_CLR_KNOBS, dev_priv->save_TV_CLR_KNOBS);
883    OUTREG(TV_CLR_LEVEL, dev_priv->save_TV_CLR_LEVEL);
884
885    {
886	int pipeconf_reg = (intel_crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
887	int dspcntr_reg = (intel_crtc->plane == 0) ? DSPACNTR : DSPBCNTR;
888	int pipeconf = INREG(pipeconf_reg);
889	int dspcntr = INREG(dspcntr_reg);
890	int dspbase_reg = (intel_crtc->plane == 0) ? DSPABASE : DSPBBASE;
891	/* Pipe must be off here */
892	OUTREG(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
893	/* Flush the plane changes */
894	OUTREG(dspbase_reg, INREG(dspbase_reg));
895
896	if (!IS_I9XX(pI830)) {
897	    /* Wait for vblank for the disable to take effect */
898	    i830WaitForVblank(pScrn);
899	}
900
901	OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
902	/* Wait for vblank for the disable to take effect. */
903	i830WaitForVblank(pScrn);
904
905	/* Filter ctl must be set before TV_WIN_SIZE */
906	OUTREG(TV_FILTER_CTL_1, dev_priv->save_TV_FILTER_CTL_1);
907	OUTREG(TV_FILTER_CTL_2, dev_priv->save_TV_FILTER_CTL_2);
908	OUTREG(TV_FILTER_CTL_3, dev_priv->save_TV_FILTER_CTL_3);
909	OUTREG(TV_WIN_POS, dev_priv->save_TV_WIN_POS);
910	OUTREG(TV_WIN_SIZE, dev_priv->save_TV_WIN_SIZE);
911	OUTREG(pipeconf_reg, pipeconf);
912	OUTREG(dspcntr_reg, dspcntr);
913	/* Flush the plane changes */
914	OUTREG(dspbase_reg, INREG(dspbase_reg));
915    }
916
917    for (i = 0; i < 60; i++)
918	OUTREG(TV_H_LUMA_0 + (i <<2), dev_priv->save_TV_H_LUMA[i]);
919    for (i = 0; i < 60; i++)
920	OUTREG(TV_H_CHROMA_0 + (i <<2), dev_priv->save_TV_H_CHROMA[i]);
921    for (i = 0; i < 43; i++)
922	OUTREG(TV_V_LUMA_0 + (i <<2), dev_priv->save_TV_V_LUMA[i]);
923    for (i = 0; i < 43; i++)
924	OUTREG(TV_V_CHROMA_0 + (i <<2), dev_priv->save_TV_V_CHROMA[i]);
925
926    OUTREG(TV_DAC, dev_priv->save_TV_DAC);
927    OUTREG(TV_CTL, dev_priv->save_TV_CTL);
928    i830WaitForVblank(pScrn);
929}
930
931static const tv_mode_t *
932i830_tv_mode_lookup (char *tv_format)
933{
934    int			    i;
935
936    for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++)
937    {
938	const tv_mode_t	*tv_mode = &tv_modes[i];
939
940	if (xf86nameCompare (tv_format, tv_mode->name) == 0)
941	    return tv_mode;
942    }
943    return NULL;
944}
945
946static const tv_mode_t *
947i830_tv_mode_find (xf86OutputPtr output)
948{
949    I830OutputPrivatePtr    intel_output = output->driver_private;
950    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
951
952    return i830_tv_mode_lookup (dev_priv->tv_format);
953}
954
955static int
956i830_tv_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
957{
958    const tv_mode_t	*tv_mode = i830_tv_mode_find (output);
959
960    if (tv_mode && fabs (tv_mode->refresh - xf86ModeVRefresh (mode)) < 1.0)
961	return MODE_OK;
962    return MODE_CLOCK_RANGE;
963}
964
965
966static Bool
967i830_tv_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
968		DisplayModePtr adjusted_mode)
969{
970    ScrnInfoPtr		pScrn = output->scrn;
971    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
972    int			i;
973    const tv_mode_t	*tv_mode = i830_tv_mode_find (output);
974
975    if (!tv_mode)
976	return FALSE;
977
978    for (i = 0; i < xf86_config->num_output; i++)
979    {
980	xf86OutputPtr other_output = xf86_config->output[i];
981
982	if (other_output != output && other_output->crtc == output->crtc)
983	    return FALSE;
984    }
985
986    adjusted_mode->Clock = tv_mode->clock;
987    return TRUE;
988}
989
990static uint32_t
991i830_float_to_csc (float fin)
992{
993    uint32_t exp;
994    uint32_t mant;
995    uint32_t ret;
996    float f = fin;
997
998    /* somehow the color conversion knows the signs of all the values */
999    if (f < 0) f = -f;
1000
1001    if (f >= 1)
1002    {
1003	exp = 0x7;
1004	mant = 1 << 8;
1005    }
1006    else
1007    {
1008	for (exp = 0; exp < 3 && f < 0.5; exp++)
1009	    f *= 2.0;
1010	mant = (f * (1 << 9) + 0.5);
1011	if (mant >= (1 << 9))
1012	    mant = (1 << 9) - 1;
1013    }
1014    ret = (exp << 9) | mant;
1015    return ret;
1016}
1017
1018static uint16_t
1019i830_float_to_luma (float f)
1020{
1021    uint16_t ret;
1022
1023    ret = (f * (1 << 9));
1024    return ret;
1025}
1026
1027static uint8_t
1028float_to_float_2_6(float fin)
1029{
1030    uint8_t exp;
1031    uint8_t mant;
1032    float f = fin;
1033    uint32_t tmp;
1034
1035    if (f < 0) f = -f;
1036
1037    tmp = f;
1038    for (exp = 0; exp <= 3 && tmp > 0; exp++)
1039	tmp /= 2;
1040
1041    mant = (f * (1 << 6) + 0.5);
1042    mant >>= exp;
1043    if (mant > (1 << 6))
1044	mant = (1 << 6) - 1;
1045
1046    return (exp << 6) | mant;
1047}
1048
1049static uint8_t
1050float_to_fix_2_6(float f)
1051{
1052    uint8_t ret;
1053
1054    ret = f * (1 << 6);
1055    return ret;
1056}
1057
1058static void
1059i830_tv_update_brightness(I830Ptr pI830, uint8_t brightness)
1060{
1061    /* brightness in 2's comp value */
1062    uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_BRIGHTNESS_MASK;
1063    int8_t bri = brightness - 128; /* remove bias */
1064
1065    val |= (bri << TV_BRIGHTNESS_SHIFT) & TV_BRIGHTNESS_MASK;
1066    OUTREG(TV_CLR_KNOBS, val);
1067}
1068
1069static void
1070i830_tv_update_contrast(I830Ptr pI830, uint8_t contrast)
1071{
1072    uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_CONTRAST_MASK;;
1073    float con;
1074    uint8_t c;
1075
1076    if (IS_I965G(pI830)) {
1077	/* 2.6 fixed point */
1078	con = 3.0 * ((float) contrast / 255);
1079	c = float_to_fix_2_6(con);
1080    } else {
1081	/* 2.6 floating point */
1082	con = 2.65625 * ((float) contrast / 255);
1083	c = float_to_float_2_6(con);
1084    }
1085    val |= (c << TV_CONTRAST_SHIFT) & TV_CONTRAST_MASK;
1086    OUTREG(TV_CLR_KNOBS, val);
1087}
1088
1089static void
1090i830_tv_update_saturation(I830Ptr pI830, uint8_t saturation)
1091{
1092    uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_SATURATION_MASK;
1093    float sat;
1094    uint8_t s;
1095
1096    /* same as contrast */
1097    if (IS_I965G(pI830)) {
1098	sat = 3.0 * ((float) saturation / 255);
1099	s = float_to_fix_2_6(sat);
1100    } else {
1101	sat = 2.65625 * ((float) saturation / 255);
1102	s = float_to_float_2_6(sat);
1103    }
1104    val |= (s << TV_SATURATION_SHIFT) & TV_SATURATION_MASK;
1105    OUTREG(TV_CLR_KNOBS, val);
1106}
1107
1108static void
1109i830_tv_update_hue(I830Ptr pI830, uint8_t hue)
1110{
1111    uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_HUE_MASK;
1112
1113    val |= (hue << TV_HUE_SHIFT) & TV_HUE_MASK;
1114    OUTREG(TV_CLR_KNOBS, val);
1115}
1116
1117static void
1118i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode,
1119		DisplayModePtr adjusted_mode)
1120{
1121    ScrnInfoPtr		    pScrn = output->scrn;
1122    I830Ptr		    pI830 = I830PTR(pScrn);
1123    xf86CrtcPtr	    crtc = output->crtc;
1124    I830OutputPrivatePtr    intel_output = output->driver_private;
1125    I830CrtcPrivatePtr	    intel_crtc = crtc->driver_private;
1126    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1127    const tv_mode_t	    *tv_mode = i830_tv_mode_find (output);
1128    uint32_t		    tv_ctl;
1129    uint32_t		    hctl1, hctl2, hctl3;
1130    uint32_t		    vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
1131    uint32_t		    scctl1, scctl2, scctl3;
1132    int			    i, j;
1133    const video_levels_t	*video_levels;
1134    const color_conversion_t	*color_conversion;
1135    Bool burst_ena;
1136
1137    if (!tv_mode)
1138	return;	/* can't happen (mode_prepare prevents this) */
1139
1140    tv_ctl = INREG(TV_CTL);
1141    tv_ctl &= TV_CTL_SAVE;
1142
1143    switch (dev_priv->type) {
1144	default:
1145	case TV_TYPE_UNKNOWN:
1146	case TV_TYPE_COMPOSITE:
1147	    tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
1148	    video_levels = &tv_mode->composite_levels;
1149	    color_conversion = &tv_mode->composite_color;
1150	    burst_ena = tv_mode->burst_ena;
1151	    break;
1152	case TV_TYPE_COMPONENT:
1153	    tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
1154	    video_levels = &component_level;
1155	    if (tv_mode->burst_ena)
1156		color_conversion = &sdtv_component_color;
1157	    else
1158		color_conversion = &hdtv_component_color;
1159	    burst_ena = FALSE;
1160	    break;
1161	case TV_TYPE_SVIDEO:
1162	    tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
1163	    video_levels = &tv_mode->svideo_levels;
1164	    color_conversion = &tv_mode->svideo_color;
1165	    burst_ena = tv_mode->burst_ena;
1166	    break;
1167    }
1168    hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
1169	(tv_mode->htotal << TV_HTOTAL_SHIFT);
1170
1171    hctl2 = (tv_mode->hburst_start << 16) |
1172	(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
1173
1174    if (burst_ena)
1175	hctl2 |= TV_BURST_ENA;
1176
1177    hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
1178	(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
1179
1180    vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
1181	(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
1182	(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
1183
1184    vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
1185	(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
1186	(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
1187
1188    vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
1189	(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
1190	(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
1191
1192    if (tv_mode->veq_ena)
1193	vctl3 |= TV_EQUAL_ENA;
1194
1195    vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
1196	(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
1197
1198    vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
1199	(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
1200
1201    vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
1202	(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
1203
1204    vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
1205	(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
1206
1207    if (intel_crtc->pipe == 1)
1208	tv_ctl |= TV_ENC_PIPEB_SELECT;
1209    tv_ctl |= tv_mode->oversample;
1210
1211    if (tv_mode->progressive)
1212	tv_ctl |= TV_PROGRESSIVE;
1213    if (tv_mode->trilevel_sync)
1214	tv_ctl |= TV_TRILEVEL_SYNC;
1215    if (tv_mode->pal_burst)
1216	tv_ctl |= TV_PAL_BURST;
1217    scctl1 = 0;
1218    if (tv_mode->dda1_inc)
1219	scctl1 |= TV_SC_DDA1_EN;
1220
1221    if (tv_mode->dda2_inc)
1222	scctl1 |= TV_SC_DDA2_EN;
1223
1224    if (tv_mode->dda3_inc)
1225	scctl1 |= TV_SC_DDA3_EN;
1226
1227    scctl1 |= tv_mode->sc_reset;
1228    scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
1229    scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1230
1231    scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1232	tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1233
1234    scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1235	tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1236
1237    /* Enable two fixes for the chips that need them. */
1238    if (DEVICE_ID(pI830->PciInfo) < PCI_CHIP_I945_G)
1239	tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1240
1241    OUTREG(TV_H_CTL_1, hctl1);
1242    OUTREG(TV_H_CTL_2, hctl2);
1243    OUTREG(TV_H_CTL_3, hctl3);
1244    OUTREG(TV_V_CTL_1, vctl1);
1245    OUTREG(TV_V_CTL_2, vctl2);
1246    OUTREG(TV_V_CTL_3, vctl3);
1247    OUTREG(TV_V_CTL_4, vctl4);
1248    OUTREG(TV_V_CTL_5, vctl5);
1249    OUTREG(TV_V_CTL_6, vctl6);
1250    OUTREG(TV_V_CTL_7, vctl7);
1251    OUTREG(TV_SC_CTL_1, scctl1);
1252    OUTREG(TV_SC_CTL_2, scctl2);
1253    OUTREG(TV_SC_CTL_3, scctl3);
1254
1255    OUTREG(TV_CSC_Y,
1256	    (i830_float_to_csc(color_conversion->ry) << 16) |
1257	    (i830_float_to_csc(color_conversion->gy)));
1258    OUTREG(TV_CSC_Y2,
1259	    (i830_float_to_csc(color_conversion->by) << 16) |
1260	    (i830_float_to_luma(color_conversion->ay)));
1261
1262    OUTREG(TV_CSC_U,
1263	    (i830_float_to_csc(color_conversion->ru) << 16) |
1264	    (i830_float_to_csc(color_conversion->gu)));
1265
1266    OUTREG(TV_CSC_U2,
1267	    (i830_float_to_csc(color_conversion->bu) << 16) |
1268	    (i830_float_to_luma(color_conversion->au)));
1269
1270    OUTREG(TV_CSC_V,
1271	    (i830_float_to_csc(color_conversion->rv) << 16) |
1272	    (i830_float_to_csc(color_conversion->gv)));
1273
1274    OUTREG(TV_CSC_V2,
1275	    (i830_float_to_csc(color_conversion->bv) << 16) |
1276	    (i830_float_to_luma(color_conversion->av)));
1277
1278    OUTREG(TV_CLR_LEVEL, ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1279		(video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1280    {
1281	int pipeconf_reg = (intel_crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
1282	int dspcntr_reg = (intel_crtc->plane == 0) ? DSPACNTR : DSPBCNTR;
1283	int pipeconf = INREG(pipeconf_reg);
1284	int dspcntr = INREG(dspcntr_reg);
1285	int dspbase_reg = (intel_crtc->plane == 0) ? DSPABASE : DSPBBASE;
1286	int xpos = 0x0, ypos = 0x0;
1287	unsigned int xsize, ysize;
1288	/* Pipe must be off here */
1289	OUTREG(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
1290	/* Flush the plane changes */
1291	OUTREG(dspbase_reg, INREG(dspbase_reg));
1292
1293	if (!IS_I9XX(pI830)) {
1294	    /* Wait for vblank for the disable to take effect */
1295	    i830WaitForVblank(pScrn);
1296	}
1297
1298	OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
1299	/* Wait for vblank for the disable to take effect. */
1300	i830WaitForVblank(pScrn);
1301
1302	/* Filter ctl must be set before TV_WIN_SIZE */
1303	OUTREG(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1304	xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1305	if (tv_mode->progressive)
1306	    ysize = tv_mode->nbr_end + 1;
1307	else
1308	    ysize = 2*tv_mode->nbr_end + 1;
1309
1310	xpos += dev_priv->margin[TV_MARGIN_LEFT];
1311	ypos += dev_priv->margin[TV_MARGIN_TOP];
1312	xsize -= (dev_priv->margin[TV_MARGIN_LEFT] +
1313		  dev_priv->margin[TV_MARGIN_RIGHT]);
1314	ysize -= (dev_priv->margin[TV_MARGIN_TOP] +
1315		  dev_priv->margin[TV_MARGIN_BOTTOM]);
1316	OUTREG(TV_WIN_POS, (xpos<<16)|ypos);
1317	OUTREG(TV_WIN_SIZE, (xsize<<16)|ysize);
1318
1319	OUTREG(pipeconf_reg, pipeconf);
1320	OUTREG(dspcntr_reg, dspcntr);
1321	/* Flush the plane changes */
1322	OUTREG(dspbase_reg, INREG(dspbase_reg));
1323    }
1324
1325    j = 0;
1326    for (i = 0; i < 60; i++)
1327	OUTREG(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1328    for (i = 0; i < 60; i++)
1329	OUTREG(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1330    for (i = 0; i < 43; i++)
1331	OUTREG(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1332    for (i = 0; i < 43; i++)
1333	OUTREG(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1334    OUTREG(TV_DAC, 0);
1335    OUTREG(TV_CTL, tv_ctl);
1336    i830WaitForVblank(pScrn);
1337}
1338
1339static const DisplayModeRec reported_modes[] = {
1340    {
1341	.name = "NTSC 480i",
1342	.Clock = 107520,
1343	.HDisplay   = 1280,
1344	.HSyncStart = 1368,
1345	.HSyncEnd   = 1496,
1346	.HTotal     = 1712,
1347
1348	.VDisplay   = 1024,
1349	.VSyncStart = 1027,
1350	.VSyncEnd   = 1034,
1351	.VTotal     = 1104,
1352	.type       = M_T_DRIVER
1353    },
1354};
1355
1356/**
1357 * Detects TV presence by checking for load.
1358 *
1359 * Requires that the current pipe's DPLL is active.
1360
1361 * \return TRUE if TV is connected.
1362 * \return FALSE if TV is disconnected.
1363 */
1364static int
1365i830_tv_detect_type (xf86CrtcPtr    crtc,
1366		xf86OutputPtr  output)
1367{
1368    ScrnInfoPtr		    pScrn = output->scrn;
1369    I830Ptr		    pI830 = I830PTR(pScrn);
1370    I830OutputPrivatePtr    intel_output = output->driver_private;
1371    uint32_t		    tv_ctl, save_tv_ctl;
1372    uint32_t		    tv_dac, save_tv_dac;
1373    int			    type = TV_TYPE_UNKNOWN;
1374
1375    tv_dac = INREG(TV_DAC);
1376    /*
1377     * Detect TV by polling)
1378     */
1379    if (intel_output->load_detect_temp)
1380    {
1381	/* TV not currently running, prod it with destructive detect */
1382	save_tv_dac = tv_dac;
1383	tv_ctl = INREG(TV_CTL);
1384	save_tv_ctl = tv_ctl;
1385	tv_ctl &= ~TV_ENC_ENABLE;
1386	tv_ctl &= ~TV_TEST_MODE_MASK;
1387	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1388	tv_dac &= ~TVDAC_SENSE_MASK;
1389        tv_dac &= ~DAC_A_MASK;
1390        tv_dac &= ~DAC_B_MASK;
1391        tv_dac &= ~DAC_C_MASK;
1392	tv_dac |= (TVDAC_STATE_CHG_EN |
1393		TVDAC_A_SENSE_CTL |
1394		TVDAC_B_SENSE_CTL |
1395		TVDAC_C_SENSE_CTL |
1396		DAC_CTL_OVERRIDE |
1397		DAC_A_0_7_V |
1398		DAC_B_0_7_V |
1399		DAC_C_0_7_V);
1400	OUTREG(TV_CTL, tv_ctl);
1401	OUTREG(TV_DAC, tv_dac);
1402	i830WaitForVblank(pScrn);
1403	tv_dac = INREG(TV_DAC);
1404	OUTREG(TV_DAC, save_tv_dac);
1405	OUTREG(TV_CTL, save_tv_ctl);
1406	i830WaitForVblank(pScrn);
1407    }
1408    /*
1409     *  A B C
1410     *  0 1 1 Composite
1411     *  1 0 X svideo
1412     *  0 0 0 Component
1413     */
1414    if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1415	if (pI830->debug_modes) {
1416	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1417		       "Detected Composite TV connection\n");
1418	}
1419	type = TV_TYPE_COMPOSITE;
1420    } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1421	if (pI830->debug_modes) {
1422	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1423		       "Detected S-Video TV connection\n");
1424	}
1425	type = TV_TYPE_SVIDEO;
1426    } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1427	if (pI830->debug_modes) {
1428	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1429		       "Detected Component TV connection\n");
1430	}
1431	type = TV_TYPE_COMPONENT;
1432    } else {
1433	if (pI830->debug_modes) {
1434	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1435		       "No TV connection detected\n");
1436	}
1437	type = TV_TYPE_NONE;
1438    }
1439
1440    return type;
1441}
1442
1443#ifdef RANDR_12_INTERFACE
1444static int
1445i830_tv_format_configure_property (xf86OutputPtr output);
1446#endif
1447
1448/**
1449 * Detect the TV connection.
1450 *
1451 * Currently this always returns OUTPUT_STATUS_UNKNOWN, as we need to be sure
1452 * we have a pipe programmed in order to probe the TV.
1453 */
1454static xf86OutputStatus
1455i830_tv_detect(xf86OutputPtr output)
1456{
1457    xf86CrtcPtr		    crtc;
1458    DisplayModeRec	    mode;
1459    I830OutputPrivatePtr    intel_output = output->driver_private;
1460    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1461    int			    dpms_mode;
1462    int			    type = dev_priv->type;
1463
1464    /* If TV connector type set by user, always return connected */
1465    if (dev_priv->force_type)
1466	return XF86OutputStatusConnected;
1467
1468    mode = reported_modes[0];
1469    xf86SetModeCrtc (&mode, INTERLACE_HALVE_V);
1470    crtc = i830GetLoadDetectPipe (output, &mode, &dpms_mode);
1471    if (crtc)
1472    {
1473        type = i830_tv_detect_type (crtc, output);
1474        i830ReleaseLoadDetectPipe (output, dpms_mode);
1475    }
1476
1477    if (type != dev_priv->type)
1478    {
1479	dev_priv->type = type;
1480#ifdef RANDR_12_INTERFACE
1481	i830_tv_format_configure_property (output);
1482#endif
1483    }
1484
1485    switch (type) {
1486    case TV_TYPE_NONE:
1487        return XF86OutputStatusDisconnected;
1488    case TV_TYPE_UNKNOWN:
1489        return XF86OutputStatusUnknown;
1490    default:
1491        return XF86OutputStatusConnected;
1492    }
1493}
1494
1495static struct input_res {
1496    char *name;
1497    int w, h;
1498} input_res_table[] =
1499{
1500	{"640x480", 640, 480},
1501	{"800x600", 800, 600},
1502	{"848x480", 848, 480},
1503	{"1024x768", 1024, 768},
1504	{"1280x720", 1280, 720},
1505	{"1280x1024", 1280, 1024},
1506	{"1920x1080", 1920, 1080},
1507};
1508
1509/**
1510 * Stub get_modes function.
1511 *
1512 * This should probably return a set of fixed modes, unless we can figure out
1513 * how to probe modes off of TV connections.
1514 */
1515
1516static DisplayModePtr
1517i830_tv_get_modes(xf86OutputPtr output)
1518{
1519    DisplayModePtr	ret = NULL, mode_ptr;
1520    int			j;
1521    const tv_mode_t	*tv_mode = i830_tv_mode_find (output);
1522
1523    for (j = 0; j < sizeof(input_res_table)/sizeof(input_res_table[0]); j++)
1524    {
1525	struct input_res *input = &input_res_table[j];
1526	unsigned int hactive_s = input->w;
1527	unsigned int vactive_s = input->h;
1528
1529	if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1530	    continue;
1531
1532	if (input->w > 1024 && (!tv_mode->progressive
1533				&& !tv_mode->component_only))
1534	    continue;
1535
1536	mode_ptr = xnfcalloc(1, sizeof(DisplayModeRec));
1537	mode_ptr->name = xnfalloc(strlen(input->name) + 1);
1538	strcpy (mode_ptr->name, input->name);
1539
1540	mode_ptr->HDisplay = hactive_s;
1541	mode_ptr->HSyncStart = hactive_s + 1;
1542	mode_ptr->HSyncEnd = hactive_s + 64;
1543	if ( mode_ptr->HSyncEnd <= mode_ptr->HSyncStart)
1544	    mode_ptr->HSyncEnd = mode_ptr->HSyncStart  + 1;
1545	mode_ptr->HTotal = hactive_s + 96;
1546
1547	mode_ptr->VDisplay = vactive_s;
1548	mode_ptr->VSyncStart = vactive_s + 1;
1549	mode_ptr->VSyncEnd = vactive_s + 32;
1550	if ( mode_ptr->VSyncEnd <= mode_ptr->VSyncStart)
1551	    mode_ptr->VSyncEnd = mode_ptr->VSyncStart  + 1;
1552	mode_ptr->VTotal = vactive_s + 33;
1553
1554	mode_ptr->Clock = (int) (tv_mode->refresh *
1555				 mode_ptr->VTotal *
1556				 mode_ptr->HTotal / 1000.0);
1557
1558	mode_ptr->type = M_T_DRIVER;
1559	mode_ptr->next = ret;
1560	mode_ptr->prev = NULL;
1561	if (ret != NULL)
1562	    ret->prev = mode_ptr;
1563	ret = mode_ptr;
1564    }
1565
1566    return ret;
1567}
1568
1569static void
1570i830_tv_destroy (xf86OutputPtr output)
1571{
1572    if (output->driver_private)
1573	xfree (output->driver_private);
1574}
1575
1576#ifdef RANDR_12_INTERFACE
1577#define TV_FORMAT_NAME	"TV_FORMAT"
1578static Atom tv_format_atom;
1579static Atom tv_format_name_atoms[NUM_TV_MODES];
1580static Atom margin_atoms[4];
1581static char *margin_names[4] = {
1582    "LEFT", "TOP", "RIGHT", "BOTTOM"
1583};
1584
1585/**
1586 *  contrast and saturation has different format on 915/945 with 965.
1587 *  On 915/945, it's 2.6 floating point number.
1588 *  On 965, it's 2.6 fixed point number.
1589 */
1590#define TV_BRIGHTNESS_NAME "BRIGHTNESS"
1591#define TV_BRIGHTNESS_DEFAULT 128	/* bias */
1592static Atom brightness_atom;
1593#define TV_CONTRAST_NAME "CONTRAST"
1594#define TV_CONTRAST_DEFAULT 0x40
1595#define TV_CONTRAST_DEFAULT_945G 0x60
1596static Atom contrast_atom;
1597#define TV_SATURATION_NAME "SATURATION"
1598#define TV_SATURATION_DEFAULT 0x40
1599#define TV_SATURATION_DEFAULT_945G 0x60
1600static Atom saturation_atom;
1601#define TV_HUE_NAME "HUE"
1602#define TV_HUE_DEFAULT 0
1603static Atom hue_atom;
1604
1605static Bool
1606i830_tv_format_set_property (xf86OutputPtr output)
1607{
1608    I830OutputPrivatePtr    intel_output = output->driver_private;
1609    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1610    const tv_mode_t	    *tv_mode = i830_tv_mode_lookup (dev_priv->tv_format);
1611    int			    err;
1612
1613    if (!tv_mode)
1614	tv_mode = &tv_modes[0];
1615    err = RRChangeOutputProperty (output->randr_output, tv_format_atom,
1616				  XA_ATOM, 32, PropModeReplace, 1,
1617				  &tv_format_name_atoms[tv_mode - tv_modes],
1618				  FALSE, TRUE);
1619    return err == Success;
1620}
1621
1622/**
1623 * Configure the TV_FORMAT property to list only supported formats
1624 *
1625 * Unless the connector is component, list only the formats supported by
1626 * svideo and composite
1627 */
1628
1629static int
1630i830_tv_format_configure_property (xf86OutputPtr output)
1631{
1632    I830OutputPrivatePtr    intel_output = output->driver_private;
1633    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1634    Atom		    current_atoms[NUM_TV_MODES];
1635    int			    num_atoms = 0;
1636    int			    i;
1637
1638    if (!output->randr_output)
1639	return Success;
1640
1641    for (i = 0; i < NUM_TV_MODES; i++)
1642	if (!tv_modes[i].component_only || dev_priv->type == TV_TYPE_COMPONENT)
1643	    current_atoms[num_atoms++] = tv_format_name_atoms[i];
1644
1645    return RRConfigureOutputProperty(output->randr_output, tv_format_atom,
1646				     TRUE, FALSE, FALSE,
1647				     num_atoms, (INT32 *) current_atoms);
1648}
1649
1650static void
1651i830_tv_color_set_property(xf86OutputPtr output, Atom property,
1652			   uint8_t val)
1653{
1654    ScrnInfoPtr		    pScrn = output->scrn;
1655    I830Ptr		    pI830 = I830PTR(pScrn);
1656    I830OutputPrivatePtr    intel_output = output->driver_private;
1657    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1658
1659    if (property == brightness_atom) {
1660	dev_priv->brightness = val;
1661	i830_tv_update_brightness(pI830, val);
1662    } else if (property == contrast_atom) {
1663	dev_priv->contrast = val;
1664	i830_tv_update_contrast(pI830, val);
1665    } else if (property == saturation_atom) {
1666	dev_priv->saturation = val;
1667	i830_tv_update_saturation(pI830, val);
1668    } else if (property == hue_atom) {
1669	dev_priv->hue = val;
1670	i830_tv_update_hue(pI830, val);
1671    }
1672}
1673
1674static void
1675i830_tv_color_create_property(xf86OutputPtr output, Atom *property,
1676			      char *name, int name_len, uint8_t val)
1677{
1678    ScrnInfoPtr	pScrn = output->scrn;
1679    INT32 range[2];
1680    int err = 0;
1681
1682    *property = MakeAtom(name, name_len - 1, TRUE);
1683    range[0] = 0;
1684    range[1] = 255;
1685    err = RRConfigureOutputProperty(output->randr_output, *property,
1686				    FALSE, TRUE, FALSE, 2, range);
1687    if (err != 0) {
1688	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1689		   "RRConfigureOutputProperty error, %d\n", err);
1690	goto out;
1691    }
1692    /* Set the current value */
1693    i830_tv_color_set_property(output, *property, val);
1694
1695    err = RRChangeOutputProperty(output->randr_output, *property,
1696				 XA_INTEGER, 32, PropModeReplace, 1, &val,
1697				 FALSE, FALSE);
1698    if (err != 0) {
1699	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1700		   "RRChangeOutputProperty error, %d\n", err);
1701    }
1702out:
1703    return;
1704}
1705
1706#endif /* RANDR_12_INTERFACE */
1707
1708static void
1709i830_tv_create_resources(xf86OutputPtr output)
1710{
1711#ifdef RANDR_12_INTERFACE
1712    ScrnInfoPtr		    pScrn = output->scrn;
1713    I830Ptr		    pI830 = I830PTR(pScrn);
1714    I830OutputPrivatePtr    intel_output = output->driver_private;
1715    struct i830_tv_priv	    *dev_priv = intel_output->dev_priv;
1716    int			    err, i;
1717
1718    /* Set up the tv_format property, which takes effect on mode set
1719     * and accepts strings that match exactly
1720     */
1721    tv_format_atom = MakeAtom(TV_FORMAT_NAME, sizeof(TV_FORMAT_NAME) - 1,
1722	TRUE);
1723
1724    for (i = 0; i < NUM_TV_MODES; i++)
1725	tv_format_name_atoms[i] = MakeAtom (tv_modes[i].name,
1726					    strlen (tv_modes[i].name),
1727					    TRUE);
1728
1729    err = i830_tv_format_configure_property (output);
1730
1731    if (err != 0) {
1732	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1733		   "RRConfigureOutputProperty error, %d\n", err);
1734    }
1735
1736    /* Set the current value of the tv_format property */
1737    if (!i830_tv_format_set_property (output))
1738	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1739		   "RRChangeOutputProperty error, %d\n", err);
1740
1741    for (i = 0; i < 4; i++)
1742    {
1743	INT32	range[2];
1744	margin_atoms[i] = MakeAtom(margin_names[i], strlen (margin_names[i]),
1745				   TRUE);
1746
1747	range[0] = 0;
1748	range[1] = 100;
1749	err = RRConfigureOutputProperty(output->randr_output, margin_atoms[i],
1750				    TRUE, TRUE, FALSE, 2, range);
1751
1752	if (err != 0)
1753	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1754		       "RRConfigureOutputProperty error, %d\n", err);
1755
1756	err = RRChangeOutputProperty(output->randr_output, margin_atoms[i],
1757				     XA_INTEGER, 32, PropModeReplace,
1758				     1, &dev_priv->margin[i],
1759				     FALSE, TRUE);
1760	if (err != 0)
1761	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1762		       "RRChangeOutputProperty error, %d\n", err);
1763    }
1764
1765    i830_tv_color_create_property(output, &brightness_atom,
1766				  TV_BRIGHTNESS_NAME,
1767				  sizeof(TV_BRIGHTNESS_NAME),
1768				  TV_BRIGHTNESS_DEFAULT);
1769    i830_tv_color_create_property(output, &contrast_atom,
1770				  TV_CONTRAST_NAME,
1771				  sizeof(TV_CONTRAST_NAME),
1772				  IS_I965G(pI830) ? TV_CONTRAST_DEFAULT :
1773						TV_CONTRAST_DEFAULT_945G);
1774    i830_tv_color_create_property(output, &saturation_atom,
1775				  TV_SATURATION_NAME,
1776				  sizeof(TV_SATURATION_NAME),
1777				  IS_I965G(pI830) ? TV_SATURATION_DEFAULT :
1778						TV_SATURATION_DEFAULT_945G);
1779    i830_tv_color_create_property(output, &hue_atom, TV_HUE_NAME,
1780				  sizeof(TV_HUE_NAME), TV_HUE_DEFAULT);
1781#endif /* RANDR_12_INTERFACE */
1782}
1783
1784#ifdef RANDR_12_INTERFACE
1785static Bool
1786i830_tv_set_property(xf86OutputPtr output, Atom property,
1787		       RRPropertyValuePtr value)
1788{
1789    int	i;
1790
1791    if (property == tv_format_atom)
1792    {
1793	I830OutputPrivatePtr    intel_output = output->driver_private;
1794	struct i830_tv_priv	*dev_priv = intel_output->dev_priv;
1795	I830Ptr			pI830 = I830PTR(output->scrn);
1796	Atom			atom;
1797	const char		*name;
1798	char			*val;
1799	RRCrtcPtr		randr_crtc;
1800	xRRModeInfo		modeinfo;
1801	RRModePtr		mode;
1802	DisplayModePtr		crtc_mode;
1803
1804	if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
1805	    return FALSE;
1806
1807	memcpy (&atom, value->data, 4);
1808	name = NameForAtom (atom);
1809
1810	val = xalloc (strlen (name) + 1);
1811	if (!val)
1812	    return FALSE;
1813	strcpy (val, name);
1814	if (!i830_tv_mode_lookup (val))
1815	{
1816	    xfree (val);
1817	    return FALSE;
1818	}
1819	xfree (dev_priv->tv_format);
1820	dev_priv->tv_format = val;
1821
1822	if (pI830->starting || output->crtc == NULL)
1823	    return TRUE;
1824
1825	/* TV format change will generate new modelines, try
1826	   to probe them and update outputs. */
1827	xf86ProbeOutputModes(output->scrn, 0, 0);
1828	 /* Mirror output modes to scrn mode list */
1829	xf86SetScrnInfoModes (output->scrn);
1830
1831	for (crtc_mode = output->probed_modes; crtc_mode;
1832		crtc_mode = crtc_mode->next)
1833	{
1834	    if (output->crtc->mode.HDisplay == crtc_mode->HDisplay &&
1835		    output->crtc->mode.VDisplay == crtc_mode->VDisplay)
1836		break;
1837	}
1838	if (!crtc_mode)
1839	    crtc_mode = output->probed_modes;
1840
1841	xf86CrtcSetMode(output->crtc, crtc_mode, output->crtc->rotation,
1842		output->crtc->x, output->crtc->y);
1843
1844	xf86RandR12TellChanged(output->scrn->pScreen);
1845
1846	modeinfo.width = crtc_mode->HDisplay;
1847	modeinfo.height = crtc_mode->VDisplay;
1848	modeinfo.dotClock = crtc_mode->Clock * 1000;
1849	modeinfo.hSyncStart = crtc_mode->HSyncStart;
1850	modeinfo.hSyncEnd = crtc_mode->HSyncEnd;
1851	modeinfo.hTotal = crtc_mode->HTotal;
1852	modeinfo.hSkew = crtc_mode->HSkew;
1853	modeinfo.vSyncStart = crtc_mode->VSyncStart;
1854	modeinfo.vSyncEnd = crtc_mode->VSyncEnd;
1855	modeinfo.vTotal = crtc_mode->VTotal;
1856	modeinfo.nameLength = strlen(crtc_mode->name);
1857	modeinfo.modeFlags = crtc_mode->Flags;
1858
1859	mode = RRModeGet(&modeinfo, crtc_mode->name);
1860	randr_crtc = output->crtc->randr_crtc;
1861	if (mode != randr_crtc->mode) {
1862	    if (randr_crtc->mode)
1863		RRModeDestroy(randr_crtc->mode);
1864	    randr_crtc->mode = mode;
1865	}
1866	return TRUE;
1867    }
1868    for (i = 0; i < 4; i++)
1869    {
1870	if (property == margin_atoms[i])
1871	{
1872	    I830OutputPrivatePtr    intel_output = output->driver_private;
1873	    struct i830_tv_priv	*dev_priv = intel_output->dev_priv;
1874	    INT32		val;
1875
1876	    if (value->type != XA_INTEGER || value->format != 32 ||
1877		value->size != 1)
1878		return FALSE;
1879
1880	    memcpy (&val, value->data, 4);
1881	    dev_priv->margin[i] = val;
1882	    return TRUE;
1883	}
1884    }
1885    if (property == brightness_atom || property == contrast_atom ||
1886	property == saturation_atom || property == hue_atom) {
1887	uint8_t val;
1888
1889	/* Make sure value is sane */
1890	if (value->type != XA_INTEGER || value->format != 32 ||
1891	    value->size != 1)
1892	    return FALSE;
1893
1894	memcpy (&val, value->data, 1);
1895	i830_tv_color_set_property(output, property, val);
1896    }
1897
1898    return TRUE;
1899}
1900#endif /* RANDR_12_INTERFACE */
1901
1902#ifdef RANDR_GET_CRTC_INTERFACE
1903static xf86CrtcPtr
1904i830_tv_get_crtc(xf86OutputPtr output)
1905{
1906    ScrnInfoPtr	pScrn = output->scrn;
1907    I830Ptr pI830 = I830PTR(pScrn);
1908    int pipe = !!(INREG(TV_CTL) & TV_ENC_PIPEB_SELECT);
1909
1910    return i830_pipe_to_crtc(pScrn, pipe);
1911}
1912#endif
1913
1914static const xf86OutputFuncsRec i830_tv_output_funcs = {
1915    .create_resources = i830_tv_create_resources,
1916    .dpms = i830_tv_dpms,
1917    .save = i830_tv_save,
1918    .restore = i830_tv_restore,
1919    .mode_valid = i830_tv_mode_valid,
1920    .mode_fixup = i830_tv_mode_fixup,
1921    .prepare = i830_output_prepare,
1922    .mode_set = i830_tv_mode_set,
1923    .commit = i830_output_commit,
1924    .detect = i830_tv_detect,
1925    .get_modes = i830_tv_get_modes,
1926    .destroy = i830_tv_destroy,
1927#ifdef RANDR_12_INTERFACE
1928    .set_property = i830_tv_set_property,
1929#endif
1930#ifdef RANDR_GET_CRTC_INTERFACE
1931    .get_crtc = i830_tv_get_crtc,
1932#endif
1933};
1934
1935void
1936i830_tv_init(ScrnInfoPtr pScrn)
1937{
1938    I830Ptr		    pI830 = I830PTR(pScrn);
1939    xf86OutputPtr	    output;
1940    I830OutputPrivatePtr    intel_output;
1941    struct i830_tv_priv	    *dev_priv;
1942    uint32_t		    tv_dac_on, tv_dac_off, save_tv_dac;
1943    XF86OptionPtr	    mon_option_lst = NULL;
1944    char		    *tv_format = NULL;
1945    char		    *tv_type = NULL;
1946
1947    if (pI830->quirk_flag & QUIRK_IGNORE_TV)
1948	return;
1949
1950    if ((INREG(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1951	return;
1952
1953    /*
1954     * Sanity check the TV output by checking to see if the
1955     * DAC register holds a value
1956     */
1957    save_tv_dac = INREG(TV_DAC);
1958
1959    OUTREG(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1960    tv_dac_on = INREG(TV_DAC);
1961
1962    OUTREG(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1963    tv_dac_off = INREG(TV_DAC);
1964
1965    OUTREG(TV_DAC, save_tv_dac);
1966
1967    /*
1968     * If the register does not hold the state change enable
1969     * bit, (either as a 0 or a 1), assume it doesn't really
1970     * exist
1971     */
1972    if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1973	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1974	return;
1975
1976    if (!pI830->tv_present) /* VBIOS claims no TV connector */
1977	return;
1978
1979    output = xf86OutputCreate (pScrn, &i830_tv_output_funcs, "TV");
1980
1981    if (!output)
1982	return;
1983
1984    intel_output = xnfcalloc (sizeof (I830OutputPrivateRec) +
1985	    sizeof (struct i830_tv_priv), 1);
1986    if (!intel_output)
1987    {
1988	xf86OutputDestroy (output);
1989	return;
1990    }
1991    dev_priv = (struct i830_tv_priv *) (intel_output + 1);
1992    intel_output->type = I830_OUTPUT_TVOUT;
1993    intel_output->pipe_mask = ((1 << 0) | (1 << 1));
1994    intel_output->clone_mask = (1 << I830_OUTPUT_TVOUT);
1995    intel_output->dev_priv = dev_priv;
1996    dev_priv->type = TV_TYPE_UNKNOWN;
1997
1998    dev_priv->tv_format = NULL;
1999
2000    if (output->conf_monitor)
2001	mon_option_lst = output->conf_monitor->mon_option_lst;
2002
2003     /* BIOS margin values */
2004    dev_priv->margin[TV_MARGIN_LEFT] = xf86SetIntOption (mon_option_lst,
2005	    "Left", 54);
2006    dev_priv->margin[TV_MARGIN_TOP] = xf86SetIntOption (mon_option_lst,
2007	    "Top", 36);
2008    dev_priv->margin[TV_MARGIN_RIGHT] = xf86SetIntOption (mon_option_lst,
2009	    "Right", 46);
2010    dev_priv->margin[TV_MARGIN_BOTTOM] = xf86SetIntOption (mon_option_lst,
2011	    "Bottom", 37);
2012
2013    tv_format = xf86findOptionValue (mon_option_lst, "TV_Format");
2014    if (tv_format)
2015	dev_priv->tv_format = xstrdup (tv_format);
2016    else
2017	dev_priv->tv_format = xstrdup (tv_modes[0].name);
2018
2019    tv_type = xf86findOptionValue (mon_option_lst, "TV_Connector");
2020    if (tv_type) {
2021	dev_priv->force_type = TRUE;
2022	if (strcasecmp(tv_type, "S-Video") == 0)
2023	    dev_priv->type = TV_TYPE_SVIDEO;
2024	else if (strcasecmp(tv_type, "Composite") == 0)
2025	    dev_priv->type = TV_TYPE_COMPOSITE;
2026	else if (strcasecmp(tv_type, "Component") == 0)
2027	    dev_priv->type = TV_TYPE_COMPONENT;
2028	else {
2029	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
2030		    "Unknown TV Connector type %s\n", tv_type);
2031	    dev_priv->force_type = FALSE;
2032	}
2033    }
2034
2035    if (dev_priv->force_type)
2036	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
2037		"Force TV Connector type as %s\n", tv_type);
2038
2039    output->driver_private = intel_output;
2040    output->interlaceAllowed = FALSE;
2041    output->doubleScanAllowed = FALSE;
2042}
2043