xref: /dflybsd-src/sys/dev/drm/i915/intel_tv.c (revision b5162e19595c53a775b64d13116edf69b4734416)
1e3adcf8fSFrançois Tigeot /*
2e3adcf8fSFrançois Tigeot  * Copyright © 2006-2008 Intel Corporation
3e3adcf8fSFrançois Tigeot  *   Jesse Barnes <jesse.barnes@intel.com>
4e3adcf8fSFrançois Tigeot  *
5e3adcf8fSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
6e3adcf8fSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
7e3adcf8fSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
8e3adcf8fSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9e3adcf8fSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
10e3adcf8fSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11e3adcf8fSFrançois Tigeot  *
12e3adcf8fSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
13e3adcf8fSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
14e3adcf8fSFrançois Tigeot  * Software.
15e3adcf8fSFrançois Tigeot  *
16e3adcf8fSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17e3adcf8fSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18e3adcf8fSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19e3adcf8fSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20e3adcf8fSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21e3adcf8fSFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22e3adcf8fSFrançois Tigeot  * DEALINGS IN THE SOFTWARE.
23e3adcf8fSFrançois Tigeot  *
24e3adcf8fSFrançois Tigeot  * Authors:
25e3adcf8fSFrançois Tigeot  *    Eric Anholt <eric@anholt.net>
26e3adcf8fSFrançois Tigeot  *
27e3adcf8fSFrançois Tigeot  * $FreeBSD: src/sys/dev/drm2/i915/intel_tv.c,v 1.1 2012/05/22 11:07:44 kib Exp $
28e3adcf8fSFrançois Tigeot  */
29e3adcf8fSFrançois Tigeot 
30e3adcf8fSFrançois Tigeot /** @file
31e3adcf8fSFrançois Tigeot  * Integrated TV-out support for the 915GM and 945GM.
32e3adcf8fSFrançois Tigeot  */
33e3adcf8fSFrançois Tigeot 
34e3adcf8fSFrançois Tigeot 
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
3818e26a6dSFrançois Tigeot #include "intel_drv.h"
395c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
40e3adcf8fSFrançois Tigeot #include "i915_drv.h"
41e3adcf8fSFrançois Tigeot 
42e3adcf8fSFrançois Tigeot enum tv_margin {
43e3adcf8fSFrançois Tigeot 	TV_MARGIN_LEFT, TV_MARGIN_TOP,
44e3adcf8fSFrançois Tigeot 	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
45e3adcf8fSFrançois Tigeot };
46e3adcf8fSFrançois Tigeot 
47e3adcf8fSFrançois Tigeot /** Private structure for the integrated TV support */
48e3adcf8fSFrançois Tigeot struct intel_tv {
49e3adcf8fSFrançois Tigeot 	struct intel_encoder base;
50e3adcf8fSFrançois Tigeot 
51e3adcf8fSFrançois Tigeot 	int type;
52e3adcf8fSFrançois Tigeot 	const char *tv_format;
53e3adcf8fSFrançois Tigeot 	int margin[4];
54e3adcf8fSFrançois Tigeot 	u32 save_TV_H_CTL_1;
55e3adcf8fSFrançois Tigeot 	u32 save_TV_H_CTL_2;
56e3adcf8fSFrançois Tigeot 	u32 save_TV_H_CTL_3;
57e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_1;
58e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_2;
59e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_3;
60e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_4;
61e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_5;
62e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_6;
63e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CTL_7;
64e3adcf8fSFrançois Tigeot 	u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
65e3adcf8fSFrançois Tigeot 
66e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_Y;
67e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_Y2;
68e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_U;
69e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_U2;
70e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_V;
71e3adcf8fSFrançois Tigeot 	u32 save_TV_CSC_V2;
72e3adcf8fSFrançois Tigeot 	u32 save_TV_CLR_KNOBS;
73e3adcf8fSFrançois Tigeot 	u32 save_TV_CLR_LEVEL;
74e3adcf8fSFrançois Tigeot 	u32 save_TV_WIN_POS;
75e3adcf8fSFrançois Tigeot 	u32 save_TV_WIN_SIZE;
76e3adcf8fSFrançois Tigeot 	u32 save_TV_FILTER_CTL_1;
77e3adcf8fSFrançois Tigeot 	u32 save_TV_FILTER_CTL_2;
78e3adcf8fSFrançois Tigeot 	u32 save_TV_FILTER_CTL_3;
79e3adcf8fSFrançois Tigeot 
80e3adcf8fSFrançois Tigeot 	u32 save_TV_H_LUMA[60];
81e3adcf8fSFrançois Tigeot 	u32 save_TV_H_CHROMA[60];
82e3adcf8fSFrançois Tigeot 	u32 save_TV_V_LUMA[43];
83e3adcf8fSFrançois Tigeot 	u32 save_TV_V_CHROMA[43];
84e3adcf8fSFrançois Tigeot 
85e3adcf8fSFrançois Tigeot 	u32 save_TV_DAC;
86e3adcf8fSFrançois Tigeot 	u32 save_TV_CTL;
87e3adcf8fSFrançois Tigeot };
88e3adcf8fSFrançois Tigeot 
89e3adcf8fSFrançois Tigeot struct video_levels {
90e3adcf8fSFrançois Tigeot 	int blank, black, burst;
91e3adcf8fSFrançois Tigeot };
92e3adcf8fSFrançois Tigeot 
93e3adcf8fSFrançois Tigeot struct color_conversion {
94e3adcf8fSFrançois Tigeot 	u16 ry, gy, by, ay;
95e3adcf8fSFrançois Tigeot 	u16 ru, gu, bu, au;
96e3adcf8fSFrançois Tigeot 	u16 rv, gv, bv, av;
97e3adcf8fSFrançois Tigeot };
98e3adcf8fSFrançois Tigeot 
99e3adcf8fSFrançois Tigeot static const u32 filter_table[] = {
100e3adcf8fSFrançois Tigeot 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
101e3adcf8fSFrançois Tigeot 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
102e3adcf8fSFrançois Tigeot 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
103e3adcf8fSFrançois Tigeot 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
104e3adcf8fSFrançois Tigeot 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
105e3adcf8fSFrançois Tigeot 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
106e3adcf8fSFrançois Tigeot 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
107e3adcf8fSFrançois Tigeot 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
108e3adcf8fSFrançois Tigeot 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
109e3adcf8fSFrançois Tigeot 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
110e3adcf8fSFrançois Tigeot 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
111e3adcf8fSFrançois Tigeot 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
112e3adcf8fSFrançois Tigeot 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
113e3adcf8fSFrançois Tigeot 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
114e3adcf8fSFrançois Tigeot 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
115e3adcf8fSFrançois Tigeot 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
116e3adcf8fSFrançois Tigeot 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
117e3adcf8fSFrançois Tigeot 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
118e3adcf8fSFrançois Tigeot 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
119e3adcf8fSFrançois Tigeot 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
120e3adcf8fSFrançois Tigeot 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
121e3adcf8fSFrançois Tigeot 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
122e3adcf8fSFrançois Tigeot 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
123e3adcf8fSFrançois Tigeot 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
124e3adcf8fSFrançois Tigeot 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
125e3adcf8fSFrançois Tigeot 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
126e3adcf8fSFrançois Tigeot 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
127e3adcf8fSFrançois Tigeot 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
128e3adcf8fSFrançois Tigeot 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
129e3adcf8fSFrançois Tigeot 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
130e3adcf8fSFrançois Tigeot 	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
131e3adcf8fSFrançois Tigeot 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
132e3adcf8fSFrançois Tigeot 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
133e3adcf8fSFrançois Tigeot 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
134e3adcf8fSFrançois Tigeot 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
135e3adcf8fSFrançois Tigeot 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
136e3adcf8fSFrançois Tigeot 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
137e3adcf8fSFrançois Tigeot 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
138e3adcf8fSFrançois Tigeot 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
139e3adcf8fSFrançois Tigeot 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
140e3adcf8fSFrançois Tigeot 	0x28003100, 0x28002F00, 0x00003100, 0x36403000,
141e3adcf8fSFrançois Tigeot 	0x2D002CC0, 0x30003640, 0x2D0036C0,
142e3adcf8fSFrançois Tigeot 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
143e3adcf8fSFrançois Tigeot 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
144e3adcf8fSFrançois Tigeot 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
145e3adcf8fSFrançois Tigeot 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
146e3adcf8fSFrançois Tigeot 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
147e3adcf8fSFrançois Tigeot 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
148e3adcf8fSFrançois Tigeot 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
149e3adcf8fSFrançois Tigeot 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
150e3adcf8fSFrançois Tigeot 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
151e3adcf8fSFrançois Tigeot 	0x28003100, 0x28002F00, 0x00003100,
152e3adcf8fSFrançois Tigeot };
153e3adcf8fSFrançois Tigeot 
154e3adcf8fSFrançois Tigeot /*
155e3adcf8fSFrançois Tigeot  * Color conversion values have 3 separate fixed point formats:
156e3adcf8fSFrançois Tigeot  *
157e3adcf8fSFrançois Tigeot  * 10 bit fields (ay, au)
158e3adcf8fSFrançois Tigeot  *   1.9 fixed point (b.bbbbbbbbb)
159e3adcf8fSFrançois Tigeot  * 11 bit fields (ry, by, ru, gu, gv)
160e3adcf8fSFrançois Tigeot  *   exp.mantissa (ee.mmmmmmmmm)
161e3adcf8fSFrançois Tigeot  *   ee = 00 = 10^-1 (0.mmmmmmmmm)
162e3adcf8fSFrançois Tigeot  *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
163e3adcf8fSFrançois Tigeot  *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
164e3adcf8fSFrançois Tigeot  *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
165e3adcf8fSFrançois Tigeot  * 12 bit fields (gy, rv, bu)
166e3adcf8fSFrançois Tigeot  *   exp.mantissa (eee.mmmmmmmmm)
167e3adcf8fSFrançois Tigeot  *   eee = 000 = 10^-1 (0.mmmmmmmmm)
168e3adcf8fSFrançois Tigeot  *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
169e3adcf8fSFrançois Tigeot  *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
170e3adcf8fSFrançois Tigeot  *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
171e3adcf8fSFrançois Tigeot  *   eee = 100 = reserved
172e3adcf8fSFrançois Tigeot  *   eee = 101 = reserved
173e3adcf8fSFrançois Tigeot  *   eee = 110 = reserved
174e3adcf8fSFrançois Tigeot  *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
175e3adcf8fSFrançois Tigeot  *
176e3adcf8fSFrançois Tigeot  * Saturation and contrast are 8 bits, with their own representation:
177e3adcf8fSFrançois Tigeot  * 8 bit field (saturation, contrast)
178e3adcf8fSFrançois Tigeot  *   exp.mantissa (ee.mmmmmm)
179e3adcf8fSFrançois Tigeot  *   ee = 00 = 10^-1 (0.mmmmmm)
180e3adcf8fSFrançois Tigeot  *   ee = 01 = 10^0 (m.mmmmm)
181e3adcf8fSFrançois Tigeot  *   ee = 10 = 10^1 (mm.mmmm)
182e3adcf8fSFrançois Tigeot  *   ee = 11 = 10^2 (mmm.mmm)
183e3adcf8fSFrançois Tigeot  *
184e3adcf8fSFrançois Tigeot  * Simple conversion function:
185e3adcf8fSFrançois Tigeot  *
186e3adcf8fSFrançois Tigeot  * static u32
187e3adcf8fSFrançois Tigeot  * float_to_csc_11(float f)
188e3adcf8fSFrançois Tigeot  * {
189e3adcf8fSFrançois Tigeot  *     u32 exp;
190e3adcf8fSFrançois Tigeot  *     u32 mant;
191e3adcf8fSFrançois Tigeot  *     u32 ret;
192e3adcf8fSFrançois Tigeot  *
193e3adcf8fSFrançois Tigeot  *     if (f < 0)
194e3adcf8fSFrançois Tigeot  *         f = -f;
195e3adcf8fSFrançois Tigeot  *
196e3adcf8fSFrançois Tigeot  *     if (f >= 1) {
197e3adcf8fSFrançois Tigeot  *         exp = 0x7;
198e3adcf8fSFrançois Tigeot  *	   mant = 1 << 8;
199e3adcf8fSFrançois Tigeot  *     } else {
200e3adcf8fSFrançois Tigeot  *         for (exp = 0; exp < 3 && f < 0.5; exp++)
201e3adcf8fSFrançois Tigeot  *	   f *= 2.0;
202e3adcf8fSFrançois Tigeot  *         mant = (f * (1 << 9) + 0.5);
203e3adcf8fSFrançois Tigeot  *         if (mant >= (1 << 9))
204e3adcf8fSFrançois Tigeot  *             mant = (1 << 9) - 1;
205e3adcf8fSFrançois Tigeot  *     }
206e3adcf8fSFrançois Tigeot  *     ret = (exp << 9) | mant;
207e3adcf8fSFrançois Tigeot  *     return ret;
208e3adcf8fSFrançois Tigeot  * }
209e3adcf8fSFrançois Tigeot  */
210e3adcf8fSFrançois Tigeot 
211e3adcf8fSFrançois Tigeot /*
212e3adcf8fSFrançois Tigeot  * Behold, magic numbers!  If we plant them they might grow a big
213e3adcf8fSFrançois Tigeot  * s-video cable to the sky... or something.
214e3adcf8fSFrançois Tigeot  *
215e3adcf8fSFrançois Tigeot  * Pre-converted to appropriate hex value.
216e3adcf8fSFrançois Tigeot  */
217e3adcf8fSFrançois Tigeot 
218e3adcf8fSFrançois Tigeot /*
219e3adcf8fSFrançois Tigeot  * PAL & NTSC values for composite & s-video connections
220e3adcf8fSFrançois Tigeot  */
221e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_composite = {
222e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
223e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
224e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
225e3adcf8fSFrançois Tigeot };
226e3adcf8fSFrançois Tigeot 
227e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_composite = {
228e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 113,
229e3adcf8fSFrançois Tigeot };
230e3adcf8fSFrançois Tigeot 
231e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_svideo = {
232e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
233e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
234e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
235e3adcf8fSFrançois Tigeot };
236e3adcf8fSFrançois Tigeot 
237e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_svideo = {
238e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 133,
239e3adcf8fSFrançois Tigeot };
240e3adcf8fSFrançois Tigeot 
241e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_composite = {
242e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
243e3adcf8fSFrançois Tigeot 	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
244e3adcf8fSFrançois Tigeot 	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
245e3adcf8fSFrançois Tigeot };
246e3adcf8fSFrançois Tigeot 
247e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_composite = {
248e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 225, .burst = 113,
249e3adcf8fSFrançois Tigeot };
250e3adcf8fSFrançois Tigeot 
251e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_svideo = {
252e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
253e3adcf8fSFrançois Tigeot 	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
254e3adcf8fSFrançois Tigeot 	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
255e3adcf8fSFrançois Tigeot };
256e3adcf8fSFrançois Tigeot 
257e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_svideo = {
258e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 266, .burst = 133,
259e3adcf8fSFrançois Tigeot };
260e3adcf8fSFrançois Tigeot 
261e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_composite = {
262e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
263e3adcf8fSFrançois Tigeot 	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
264e3adcf8fSFrançois Tigeot 	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
265e3adcf8fSFrançois Tigeot };
266e3adcf8fSFrançois Tigeot 
267e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_composite = {
268e3adcf8fSFrançois Tigeot 	.blank = 237, .black = 237, .burst = 118,
269e3adcf8fSFrançois Tigeot };
270e3adcf8fSFrançois Tigeot 
271e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_svideo = {
272e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
273e3adcf8fSFrançois Tigeot 	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
274e3adcf8fSFrançois Tigeot 	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
275e3adcf8fSFrançois Tigeot };
276e3adcf8fSFrançois Tigeot 
277e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_svideo = {
278e3adcf8fSFrançois Tigeot 	.blank = 280, .black = 280, .burst = 139,
279e3adcf8fSFrançois Tigeot };
280e3adcf8fSFrançois Tigeot 
281e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_composite = {
282e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
283e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
284e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
285e3adcf8fSFrançois Tigeot };
286e3adcf8fSFrançois Tigeot 
287e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_composite = {
288e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 113,
289e3adcf8fSFrançois Tigeot };
290e3adcf8fSFrançois Tigeot 
291e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_svideo = {
292e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
293e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
294e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
295e3adcf8fSFrançois Tigeot };
296e3adcf8fSFrançois Tigeot 
297e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_svideo = {
298e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 133,
299e3adcf8fSFrançois Tigeot };
300e3adcf8fSFrançois Tigeot 
301e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_composite = {
302e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
303e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
304e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
305e3adcf8fSFrançois Tigeot };
306e3adcf8fSFrançois Tigeot 
307e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_composite = {
308e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 118,
309e3adcf8fSFrançois Tigeot };
310e3adcf8fSFrançois Tigeot 
311e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_svideo = {
312e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
313e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
314e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
315e3adcf8fSFrançois Tigeot };
316e3adcf8fSFrançois Tigeot 
317e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_svideo = {
318e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 139,
319e3adcf8fSFrançois Tigeot };
320e3adcf8fSFrançois Tigeot 
321e3adcf8fSFrançois Tigeot /*
322e3adcf8fSFrançois Tigeot  * Component connections
323e3adcf8fSFrançois Tigeot  */
324e3adcf8fSFrançois Tigeot static const struct color_conversion sdtv_csc_yprpb = {
325e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
326e3adcf8fSFrançois Tigeot 	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
327e3adcf8fSFrançois Tigeot 	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
328e3adcf8fSFrançois Tigeot };
329e3adcf8fSFrançois Tigeot 
330e3adcf8fSFrançois Tigeot static const struct color_conversion sdtv_csc_rgb = {
331e3adcf8fSFrançois Tigeot 	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
332e3adcf8fSFrançois Tigeot 	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
333e3adcf8fSFrançois Tigeot 	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
334e3adcf8fSFrançois Tigeot };
335e3adcf8fSFrançois Tigeot 
336e3adcf8fSFrançois Tigeot static const struct color_conversion hdtv_csc_yprpb = {
337e3adcf8fSFrançois Tigeot 	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
338e3adcf8fSFrançois Tigeot 	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
339e3adcf8fSFrançois Tigeot 	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
340e3adcf8fSFrançois Tigeot };
341e3adcf8fSFrançois Tigeot 
342e3adcf8fSFrançois Tigeot static const struct color_conversion hdtv_csc_rgb = {
343e3adcf8fSFrançois Tigeot 	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
344e3adcf8fSFrançois Tigeot 	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
345e3adcf8fSFrançois Tigeot 	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
346e3adcf8fSFrançois Tigeot };
347e3adcf8fSFrançois Tigeot 
348e3adcf8fSFrançois Tigeot static const struct video_levels component_levels = {
349e3adcf8fSFrançois Tigeot 	.blank = 279, .black = 279, .burst = 0,
350e3adcf8fSFrançois Tigeot };
351e3adcf8fSFrançois Tigeot 
352e3adcf8fSFrançois Tigeot 
353e3adcf8fSFrançois Tigeot struct tv_mode {
354e3adcf8fSFrançois Tigeot 	const char *name;
355e3adcf8fSFrançois Tigeot 	int clock;
356e3adcf8fSFrançois Tigeot 	int refresh; /* in millihertz (for precision) */
357e3adcf8fSFrançois Tigeot 	u32 oversample;
358e3adcf8fSFrançois Tigeot 	int hsync_end, hblank_start, hblank_end, htotal;
359e3adcf8fSFrançois Tigeot 	bool progressive, trilevel_sync, component_only;
360e3adcf8fSFrançois Tigeot 	int vsync_start_f1, vsync_start_f2, vsync_len;
361e3adcf8fSFrançois Tigeot 	bool veq_ena;
362e3adcf8fSFrançois Tigeot 	int veq_start_f1, veq_start_f2, veq_len;
363e3adcf8fSFrançois Tigeot 	int vi_end_f1, vi_end_f2, nbr_end;
364e3adcf8fSFrançois Tigeot 	bool burst_ena;
365e3adcf8fSFrançois Tigeot 	int hburst_start, hburst_len;
366e3adcf8fSFrançois Tigeot 	int vburst_start_f1, vburst_end_f1;
367e3adcf8fSFrançois Tigeot 	int vburst_start_f2, vburst_end_f2;
368e3adcf8fSFrançois Tigeot 	int vburst_start_f3, vburst_end_f3;
369e3adcf8fSFrançois Tigeot 	int vburst_start_f4, vburst_end_f4;
370e3adcf8fSFrançois Tigeot 	/*
371e3adcf8fSFrançois Tigeot 	 * subcarrier programming
372e3adcf8fSFrançois Tigeot 	 */
373e3adcf8fSFrançois Tigeot 	int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
374e3adcf8fSFrançois Tigeot 	u32 sc_reset;
375e3adcf8fSFrançois Tigeot 	bool pal_burst;
376e3adcf8fSFrançois Tigeot 	/*
377e3adcf8fSFrançois Tigeot 	 * blank/black levels
378e3adcf8fSFrançois Tigeot 	 */
379e3adcf8fSFrançois Tigeot 	const struct video_levels *composite_levels, *svideo_levels;
380e3adcf8fSFrançois Tigeot 	const struct color_conversion *composite_color, *svideo_color;
381e3adcf8fSFrançois Tigeot 	const u32 *filter_table;
382e3adcf8fSFrançois Tigeot 	int max_srcw;
383e3adcf8fSFrançois Tigeot };
384e3adcf8fSFrançois Tigeot 
385e3adcf8fSFrançois Tigeot 
386e3adcf8fSFrançois Tigeot /*
387e3adcf8fSFrançois Tigeot  * Sub carrier DDA
388e3adcf8fSFrançois Tigeot  *
389e3adcf8fSFrançois Tigeot  *  I think this works as follows:
390e3adcf8fSFrançois Tigeot  *
391e3adcf8fSFrançois Tigeot  *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
392e3adcf8fSFrançois Tigeot  *
393e3adcf8fSFrançois Tigeot  * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
394e3adcf8fSFrançois Tigeot  *
395e3adcf8fSFrançois Tigeot  * So,
396e3adcf8fSFrançois Tigeot  *  dda1_ideal = subcarrier/pixel * 4096
397e3adcf8fSFrançois Tigeot  *  dda1_inc = floor (dda1_ideal)
398e3adcf8fSFrançois Tigeot  *  dda2 = dda1_ideal - dda1_inc
399e3adcf8fSFrançois Tigeot  *
400e3adcf8fSFrançois Tigeot  *  then pick a ratio for dda2 that gives the closest approximation. If
401e3adcf8fSFrançois Tigeot  *  you can't get close enough, you can play with dda3 as well. This
402e3adcf8fSFrançois Tigeot  *  seems likely to happen when dda2 is small as the jumps would be larger
403e3adcf8fSFrançois Tigeot  *
404e3adcf8fSFrançois Tigeot  * To invert this,
405e3adcf8fSFrançois Tigeot  *
406e3adcf8fSFrançois Tigeot  *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
407e3adcf8fSFrançois Tigeot  *
408e3adcf8fSFrançois Tigeot  * The constants below were all computed using a 107.520MHz clock
409e3adcf8fSFrançois Tigeot  */
410e3adcf8fSFrançois Tigeot 
411e3adcf8fSFrançois Tigeot /**
412e3adcf8fSFrançois Tigeot  * Register programming values for TV modes.
413e3adcf8fSFrançois Tigeot  *
414e3adcf8fSFrançois Tigeot  * These values account for -1s required.
415e3adcf8fSFrançois Tigeot  */
416e3adcf8fSFrançois Tigeot 
417e3adcf8fSFrançois Tigeot static const struct tv_mode tv_modes[] = {
418e3adcf8fSFrançois Tigeot 	{
419e3adcf8fSFrançois Tigeot 		.name		= "NTSC-M",
420e3adcf8fSFrançois Tigeot 		.clock		= 108000,
421e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
422e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
423e3adcf8fSFrançois Tigeot 		.component_only = 0,
424e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
425e3adcf8fSFrançois Tigeot 
426e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
427e3adcf8fSFrançois Tigeot 		.hblank_start	= 836,		    .htotal		= 857,
428e3adcf8fSFrançois Tigeot 
429e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
430e3adcf8fSFrançois Tigeot 
431e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
432e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
433e3adcf8fSFrançois Tigeot 
434e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
435e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
436e3adcf8fSFrançois Tigeot 
437e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
438e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
439e3adcf8fSFrançois Tigeot 
440e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
441e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
442e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
443e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
444e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
445e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
446e3adcf8fSFrançois Tigeot 
447e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
448e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
449e3adcf8fSFrançois Tigeot 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
450e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
451e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_4,
452e3adcf8fSFrançois Tigeot 		.pal_burst	= false,
453e3adcf8fSFrançois Tigeot 
454e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_m_levels_composite,
455e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_m_csc_composite,
456e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_m_levels_svideo,
457e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_m_csc_svideo,
458e3adcf8fSFrançois Tigeot 
459e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
460e3adcf8fSFrançois Tigeot 	},
461e3adcf8fSFrançois Tigeot 	{
462e3adcf8fSFrançois Tigeot 		.name		= "NTSC-443",
463e3adcf8fSFrançois Tigeot 		.clock		= 108000,
464e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
465e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
466e3adcf8fSFrançois Tigeot 		.component_only = 0,
467e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
468e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
469e3adcf8fSFrançois Tigeot 		.hblank_start	= 836,		    .htotal		= 857,
470e3adcf8fSFrançois Tigeot 
471e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
472e3adcf8fSFrançois Tigeot 
473e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
474e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
475e3adcf8fSFrançois Tigeot 
476e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
477e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
478e3adcf8fSFrançois Tigeot 
479e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
480e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
481e3adcf8fSFrançois Tigeot 
482e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
483e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
484e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
485e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
486e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
487e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
488e3adcf8fSFrançois Tigeot 
489e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
490e3adcf8fSFrançois Tigeot 		.dda1_inc       =    168,
491e3adcf8fSFrançois Tigeot 		.dda2_inc       =   4093,       .dda2_size      =  27456,
492e3adcf8fSFrançois Tigeot 		.dda3_inc       =    310,       .dda3_size      =    525,
493e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_NEVER,
494e3adcf8fSFrançois Tigeot 		.pal_burst  = false,
495e3adcf8fSFrançois Tigeot 
496e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_m_levels_composite,
497e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_m_csc_composite,
498e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_m_levels_svideo,
499e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_m_csc_svideo,
500e3adcf8fSFrançois Tigeot 
501e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
502e3adcf8fSFrançois Tigeot 	},
503e3adcf8fSFrançois Tigeot 	{
504e3adcf8fSFrançois Tigeot 		.name		= "NTSC-J",
505e3adcf8fSFrançois Tigeot 		.clock		= 108000,
506e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
507e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
508e3adcf8fSFrançois Tigeot 		.component_only = 0,
509e3adcf8fSFrançois Tigeot 
510e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
511e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
512e3adcf8fSFrançois Tigeot 		.hblank_start = 836,	    .htotal		= 857,
513e3adcf8fSFrançois Tigeot 
514e3adcf8fSFrançois Tigeot 		.progressive	= false,    .trilevel_sync = false,
515e3adcf8fSFrançois Tigeot 
516e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
517e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
518e3adcf8fSFrançois Tigeot 
519e3adcf8fSFrançois Tigeot 		.veq_ena      = true,	    .veq_start_f1	= 0,
520e3adcf8fSFrançois Tigeot 		.veq_start_f2 = 1,	    .veq_len		= 18,
521e3adcf8fSFrançois Tigeot 
522e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
523e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
524e3adcf8fSFrançois Tigeot 
525e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
526e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
527e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
528e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
529e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
530e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
531e3adcf8fSFrançois Tigeot 
532e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
533e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
534e3adcf8fSFrançois Tigeot 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
535e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
536e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_4,
537e3adcf8fSFrançois Tigeot 		.pal_burst	= false,
538e3adcf8fSFrançois Tigeot 
539e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_j_levels_composite,
540e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_j_csc_composite,
541e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_j_levels_svideo,
542e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_j_csc_svideo,
543e3adcf8fSFrançois Tigeot 
544e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
545e3adcf8fSFrançois Tigeot 	},
546e3adcf8fSFrançois Tigeot 	{
547e3adcf8fSFrançois Tigeot 		.name		= "PAL-M",
548e3adcf8fSFrançois Tigeot 		.clock		= 108000,
549e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
550e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
551e3adcf8fSFrançois Tigeot 		.component_only = 0,
552e3adcf8fSFrançois Tigeot 
553e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
554e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		  .hblank_end		= 124,
555e3adcf8fSFrançois Tigeot 		.hblank_start = 836,	  .htotal		= 857,
556e3adcf8fSFrançois Tigeot 
557e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
558e3adcf8fSFrançois Tigeot 
559e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
560e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
561e3adcf8fSFrançois Tigeot 
562e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
563e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
564e3adcf8fSFrançois Tigeot 
565e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
566e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
567e3adcf8fSFrançois Tigeot 
568e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
569e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
570e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
571e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
572e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
573e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
574e3adcf8fSFrançois Tigeot 
575e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
576e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
577e3adcf8fSFrançois Tigeot 		.dda2_inc	=  16704,	    .dda2_size		=  27456,
578e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
579e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_8,
580e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
581e3adcf8fSFrançois Tigeot 
582e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_m_levels_composite,
583e3adcf8fSFrançois Tigeot 		.composite_color = &pal_m_csc_composite,
584e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_m_levels_svideo,
585e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_m_csc_svideo,
586e3adcf8fSFrançois Tigeot 
587e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
588e3adcf8fSFrançois Tigeot 	},
589e3adcf8fSFrançois Tigeot 	{
590e3adcf8fSFrançois Tigeot 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
591e3adcf8fSFrançois Tigeot 		.name	    = "PAL-N",
592e3adcf8fSFrançois Tigeot 		.clock		= 108000,
593e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
594e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
595e3adcf8fSFrançois Tigeot 		.component_only = 0,
596e3adcf8fSFrançois Tigeot 
597e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 128,
598e3adcf8fSFrançois Tigeot 		.hblank_start = 844,	    .htotal		= 863,
599e3adcf8fSFrançois Tigeot 
600e3adcf8fSFrançois Tigeot 		.progressive  = false,    .trilevel_sync = false,
601e3adcf8fSFrançois Tigeot 
602e3adcf8fSFrançois Tigeot 
603e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
604e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
605e3adcf8fSFrançois Tigeot 
606e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
607e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
608e3adcf8fSFrançois Tigeot 
609e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
610e3adcf8fSFrançois Tigeot 		.nbr_end	= 286,
611e3adcf8fSFrançois Tigeot 
612e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
613e3adcf8fSFrançois Tigeot 		.hburst_start = 73,	    .hburst_len		= 34,
614e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
615e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
616e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
617e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
618e3adcf8fSFrançois Tigeot 
619e3adcf8fSFrançois Tigeot 
620e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
621e3adcf8fSFrançois Tigeot 		.dda1_inc       =    135,
622e3adcf8fSFrançois Tigeot 		.dda2_inc       =  23578,       .dda2_size      =  27648,
623e3adcf8fSFrançois Tigeot 		.dda3_inc       =    134,       .dda3_size      =    625,
624e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_EVERY_8,
625e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
626e3adcf8fSFrançois Tigeot 
627e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_n_levels_composite,
628e3adcf8fSFrançois Tigeot 		.composite_color = &pal_n_csc_composite,
629e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_n_levels_svideo,
630e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_n_csc_svideo,
631e3adcf8fSFrançois Tigeot 
632e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
633e3adcf8fSFrançois Tigeot 	},
634e3adcf8fSFrançois Tigeot 	{
635e3adcf8fSFrançois Tigeot 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
636e3adcf8fSFrançois Tigeot 		.name	    = "PAL",
637e3adcf8fSFrançois Tigeot 		.clock		= 108000,
638e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
639e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
640e3adcf8fSFrançois Tigeot 		.component_only = 0,
641e3adcf8fSFrançois Tigeot 
642e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 142,
643e3adcf8fSFrançois Tigeot 		.hblank_start	= 844,	    .htotal		= 863,
644e3adcf8fSFrançois Tigeot 
645e3adcf8fSFrançois Tigeot 		.progressive	= false,    .trilevel_sync = false,
646e3adcf8fSFrançois Tigeot 
647e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
648e3adcf8fSFrançois Tigeot 		.vsync_len	= 5,
649e3adcf8fSFrançois Tigeot 
650e3adcf8fSFrançois Tigeot 		.veq_ena	= true,	    .veq_start_f1	= 0,
651e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,	    .veq_len		= 15,
652e3adcf8fSFrançois Tigeot 
653e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
654e3adcf8fSFrançois Tigeot 		.nbr_end	= 286,
655e3adcf8fSFrançois Tigeot 
656e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
657e3adcf8fSFrançois Tigeot 		.hburst_start	= 73,		    .hburst_len		= 32,
658e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
659e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
660e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
661e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
662e3adcf8fSFrançois Tigeot 
663e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
664e3adcf8fSFrançois Tigeot 		.dda1_inc       =    168,
665e3adcf8fSFrançois Tigeot 		.dda2_inc       =   4122,       .dda2_size      =  27648,
666e3adcf8fSFrançois Tigeot 		.dda3_inc       =     67,       .dda3_size      =    625,
667e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_EVERY_8,
668e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
669e3adcf8fSFrançois Tigeot 
670e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_levels_composite,
671e3adcf8fSFrançois Tigeot 		.composite_color = &pal_csc_composite,
672e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_levels_svideo,
673e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_csc_svideo,
674e3adcf8fSFrançois Tigeot 
675e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
676e3adcf8fSFrançois Tigeot 	},
677e3adcf8fSFrançois Tigeot 	{
678e3adcf8fSFrançois Tigeot 		.name       = "720p@60Hz",
679e3adcf8fSFrançois Tigeot 		.clock		= 148800,
680e3adcf8fSFrançois Tigeot 		.refresh	= 60000,
681e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
682e3adcf8fSFrançois Tigeot 		.component_only = 1,
683e3adcf8fSFrançois Tigeot 
684e3adcf8fSFrançois Tigeot 		.hsync_end      = 80,               .hblank_end         = 300,
685e3adcf8fSFrançois Tigeot 		.hblank_start   = 1580,             .htotal             = 1649,
686e3adcf8fSFrançois Tigeot 
687e3adcf8fSFrançois Tigeot 		.progressive	= true,		    .trilevel_sync = true,
688e3adcf8fSFrançois Tigeot 
689e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
690e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
691e3adcf8fSFrançois Tigeot 
692e3adcf8fSFrançois Tigeot 		.veq_ena        = false,
693e3adcf8fSFrançois Tigeot 
694e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
695e3adcf8fSFrançois Tigeot 		.nbr_end        = 719,
696e3adcf8fSFrançois Tigeot 
697e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
698e3adcf8fSFrançois Tigeot 
699e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
700e3adcf8fSFrançois Tigeot 	},
701e3adcf8fSFrançois Tigeot 	{
702e3adcf8fSFrançois Tigeot 		.name       = "720p@50Hz",
703e3adcf8fSFrançois Tigeot 		.clock		= 148800,
704e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
705e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
706e3adcf8fSFrançois Tigeot 		.component_only = 1,
707e3adcf8fSFrançois Tigeot 
708e3adcf8fSFrançois Tigeot 		.hsync_end      = 80,               .hblank_end         = 300,
709e3adcf8fSFrançois Tigeot 		.hblank_start   = 1580,             .htotal             = 1979,
710e3adcf8fSFrançois Tigeot 
711e3adcf8fSFrançois Tigeot 		.progressive	= true,		    .trilevel_sync = true,
712e3adcf8fSFrançois Tigeot 
713e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
714e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
715e3adcf8fSFrançois Tigeot 
716e3adcf8fSFrançois Tigeot 		.veq_ena        = false,
717e3adcf8fSFrançois Tigeot 
718e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
719e3adcf8fSFrançois Tigeot 		.nbr_end        = 719,
720e3adcf8fSFrançois Tigeot 
721e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
722e3adcf8fSFrançois Tigeot 
723e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
724e3adcf8fSFrançois Tigeot 		.max_srcw = 800
725e3adcf8fSFrançois Tigeot 	},
726e3adcf8fSFrançois Tigeot 	{
727e3adcf8fSFrançois Tigeot 		.name       = "1080i@50Hz",
728e3adcf8fSFrançois Tigeot 		.clock		= 148800,
729e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
730e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
731e3adcf8fSFrançois Tigeot 		.component_only = 1,
732e3adcf8fSFrançois Tigeot 
733e3adcf8fSFrançois Tigeot 		.hsync_end      = 88,               .hblank_end         = 235,
734e3adcf8fSFrançois Tigeot 		.hblank_start   = 2155,             .htotal             = 2639,
735e3adcf8fSFrançois Tigeot 
736e3adcf8fSFrançois Tigeot 		.progressive	= false,	  .trilevel_sync = true,
737e3adcf8fSFrançois Tigeot 
738e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 4,              .vsync_start_f2     = 5,
739e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
740e3adcf8fSFrançois Tigeot 
741e3adcf8fSFrançois Tigeot 		.veq_ena	= true,	    .veq_start_f1	= 4,
742e3adcf8fSFrançois Tigeot 		.veq_start_f2   = 4,	    .veq_len		= 10,
743e3adcf8fSFrançois Tigeot 
744e3adcf8fSFrançois Tigeot 
745e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 21,           .vi_end_f2          = 22,
746e3adcf8fSFrançois Tigeot 		.nbr_end        = 539,
747e3adcf8fSFrançois Tigeot 
748e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
749e3adcf8fSFrançois Tigeot 
750e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
751e3adcf8fSFrançois Tigeot 	},
752e3adcf8fSFrançois Tigeot 	{
753e3adcf8fSFrançois Tigeot 		.name       = "1080i@60Hz",
754e3adcf8fSFrançois Tigeot 		.clock		= 148800,
755e3adcf8fSFrançois Tigeot 		.refresh	= 60000,
756e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
757e3adcf8fSFrançois Tigeot 		.component_only = 1,
758e3adcf8fSFrançois Tigeot 
759e3adcf8fSFrançois Tigeot 		.hsync_end      = 88,               .hblank_end         = 235,
760e3adcf8fSFrançois Tigeot 		.hblank_start   = 2155,             .htotal             = 2199,
761e3adcf8fSFrançois Tigeot 
762e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = true,
763e3adcf8fSFrançois Tigeot 
764e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 4,               .vsync_start_f2     = 5,
765e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
766e3adcf8fSFrançois Tigeot 
767e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 4,
768e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 4,		    .veq_len		= 10,
769e3adcf8fSFrançois Tigeot 
770e3adcf8fSFrançois Tigeot 
771e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 21,               .vi_end_f2          = 22,
772e3adcf8fSFrançois Tigeot 		.nbr_end        = 539,
773e3adcf8fSFrançois Tigeot 
774e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
775e3adcf8fSFrançois Tigeot 
776e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
777e3adcf8fSFrançois Tigeot 	},
778e3adcf8fSFrançois Tigeot };
779e3adcf8fSFrançois Tigeot 
780e3adcf8fSFrançois Tigeot static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder)
781e3adcf8fSFrançois Tigeot {
782e3adcf8fSFrançois Tigeot 	return container_of(encoder, struct intel_tv, base.base);
783e3adcf8fSFrançois Tigeot }
784e3adcf8fSFrançois Tigeot 
785e3adcf8fSFrançois Tigeot static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
786e3adcf8fSFrançois Tigeot {
787e3adcf8fSFrançois Tigeot 	return container_of(intel_attached_encoder(connector),
788e3adcf8fSFrançois Tigeot 			    struct intel_tv,
789e3adcf8fSFrançois Tigeot 			    base);
790e3adcf8fSFrançois Tigeot }
791e3adcf8fSFrançois Tigeot 
792e3adcf8fSFrançois Tigeot static void
793e3adcf8fSFrançois Tigeot intel_tv_dpms(struct drm_encoder *encoder, int mode)
794e3adcf8fSFrançois Tigeot {
795e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
796e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
797e3adcf8fSFrançois Tigeot 
798e3adcf8fSFrançois Tigeot 	switch (mode) {
799e3adcf8fSFrançois Tigeot 	case DRM_MODE_DPMS_ON:
800e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
801e3adcf8fSFrançois Tigeot 		break;
802e3adcf8fSFrançois Tigeot 	case DRM_MODE_DPMS_STANDBY:
803e3adcf8fSFrançois Tigeot 	case DRM_MODE_DPMS_SUSPEND:
804e3adcf8fSFrançois Tigeot 	case DRM_MODE_DPMS_OFF:
805e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
806e3adcf8fSFrançois Tigeot 		break;
807e3adcf8fSFrançois Tigeot 	}
808e3adcf8fSFrançois Tigeot }
809e3adcf8fSFrançois Tigeot 
810e3adcf8fSFrançois Tigeot static const struct tv_mode *
811e3adcf8fSFrançois Tigeot intel_tv_mode_lookup(const char *tv_format)
812e3adcf8fSFrançois Tigeot {
813e3adcf8fSFrançois Tigeot 	int i;
814e3adcf8fSFrançois Tigeot 
815e3adcf8fSFrançois Tigeot 	for (i = 0; i < sizeof(tv_modes) / sizeof(tv_modes[0]); i++) {
816e3adcf8fSFrançois Tigeot 		const struct tv_mode *tv_mode = &tv_modes[i];
817e3adcf8fSFrançois Tigeot 
818e3adcf8fSFrançois Tigeot 		if (!strcmp(tv_format, tv_mode->name))
819e3adcf8fSFrançois Tigeot 			return tv_mode;
820e3adcf8fSFrançois Tigeot 	}
821e3adcf8fSFrançois Tigeot 	return NULL;
822e3adcf8fSFrançois Tigeot }
823e3adcf8fSFrançois Tigeot 
824e3adcf8fSFrançois Tigeot static const struct tv_mode *
825e3adcf8fSFrançois Tigeot intel_tv_mode_find(struct intel_tv *intel_tv)
826e3adcf8fSFrançois Tigeot {
827e3adcf8fSFrançois Tigeot 	return intel_tv_mode_lookup(intel_tv->tv_format);
828e3adcf8fSFrançois Tigeot }
829e3adcf8fSFrançois Tigeot 
830e3adcf8fSFrançois Tigeot static enum drm_mode_status
831e3adcf8fSFrançois Tigeot intel_tv_mode_valid(struct drm_connector *connector,
832e3adcf8fSFrançois Tigeot 		    struct drm_display_mode *mode)
833e3adcf8fSFrançois Tigeot {
834e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
835e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
836e3adcf8fSFrançois Tigeot 
837e3adcf8fSFrançois Tigeot 	/* Ensure TV refresh is close to desired refresh */
838e3adcf8fSFrançois Tigeot 	if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
839e3adcf8fSFrançois Tigeot 				< 1000)
840e3adcf8fSFrançois Tigeot 		return MODE_OK;
841e3adcf8fSFrançois Tigeot 
842e3adcf8fSFrançois Tigeot 	return MODE_CLOCK_RANGE;
843e3adcf8fSFrançois Tigeot }
844e3adcf8fSFrançois Tigeot 
845e3adcf8fSFrançois Tigeot 
846e3adcf8fSFrançois Tigeot static bool
8476f486c69SFrançois Tigeot intel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode,
848e3adcf8fSFrançois Tigeot 		    struct drm_display_mode *adjusted_mode)
849e3adcf8fSFrançois Tigeot {
850e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
851e3adcf8fSFrançois Tigeot 	struct drm_mode_config *drm_config = &dev->mode_config;
852e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
853e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
854e3adcf8fSFrançois Tigeot 	struct drm_encoder *other_encoder;
855e3adcf8fSFrançois Tigeot 
856e3adcf8fSFrançois Tigeot 	if (!tv_mode)
857e3adcf8fSFrançois Tigeot 		return false;
858e3adcf8fSFrançois Tigeot 
859e3adcf8fSFrançois Tigeot 	/* FIXME: lock encoder list */
860e3adcf8fSFrançois Tigeot 	list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
861e3adcf8fSFrançois Tigeot 		if (other_encoder != encoder &&
862e3adcf8fSFrançois Tigeot 		    other_encoder->crtc == encoder->crtc)
863e3adcf8fSFrançois Tigeot 			return false;
864e3adcf8fSFrançois Tigeot 	}
865e3adcf8fSFrançois Tigeot 
866e3adcf8fSFrançois Tigeot 	adjusted_mode->clock = tv_mode->clock;
867e3adcf8fSFrançois Tigeot 	return true;
868e3adcf8fSFrançois Tigeot }
869e3adcf8fSFrançois Tigeot 
870e3adcf8fSFrançois Tigeot static void
871e3adcf8fSFrançois Tigeot intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
872e3adcf8fSFrançois Tigeot 		  struct drm_display_mode *adjusted_mode)
873e3adcf8fSFrançois Tigeot {
874e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
875e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
876e3adcf8fSFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
877e3adcf8fSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
878e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
879e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
880e3adcf8fSFrançois Tigeot 	u32 tv_ctl;
881e3adcf8fSFrançois Tigeot 	u32 hctl1, hctl2, hctl3;
882e3adcf8fSFrançois Tigeot 	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
883e3adcf8fSFrançois Tigeot 	u32 scctl1, scctl2, scctl3;
884e3adcf8fSFrançois Tigeot 	int i, j;
885e3adcf8fSFrançois Tigeot 	const struct video_levels *video_levels;
886e3adcf8fSFrançois Tigeot 	const struct color_conversion *color_conversion;
887e3adcf8fSFrançois Tigeot 	bool burst_ena;
888e3adcf8fSFrançois Tigeot 	int pipe = intel_crtc->pipe;
889e3adcf8fSFrançois Tigeot 
890e3adcf8fSFrançois Tigeot 	if (!tv_mode)
891e3adcf8fSFrançois Tigeot 		return;	/* can't happen (mode_prepare prevents this) */
892e3adcf8fSFrançois Tigeot 
893e3adcf8fSFrançois Tigeot 	tv_ctl = I915_READ(TV_CTL);
894e3adcf8fSFrançois Tigeot 	tv_ctl &= TV_CTL_SAVE;
895e3adcf8fSFrançois Tigeot 
896e3adcf8fSFrançois Tigeot 	switch (intel_tv->type) {
897e3adcf8fSFrançois Tigeot 	default:
898e3adcf8fSFrançois Tigeot 	case DRM_MODE_CONNECTOR_Unknown:
899e3adcf8fSFrançois Tigeot 	case DRM_MODE_CONNECTOR_Composite:
900e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
901e3adcf8fSFrançois Tigeot 		video_levels = tv_mode->composite_levels;
902e3adcf8fSFrançois Tigeot 		color_conversion = tv_mode->composite_color;
903e3adcf8fSFrançois Tigeot 		burst_ena = tv_mode->burst_ena;
904e3adcf8fSFrançois Tigeot 		break;
905e3adcf8fSFrançois Tigeot 	case DRM_MODE_CONNECTOR_Component:
906e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
907e3adcf8fSFrançois Tigeot 		video_levels = &component_levels;
908e3adcf8fSFrançois Tigeot 		if (tv_mode->burst_ena)
909e3adcf8fSFrançois Tigeot 			color_conversion = &sdtv_csc_yprpb;
910e3adcf8fSFrançois Tigeot 		else
911e3adcf8fSFrançois Tigeot 			color_conversion = &hdtv_csc_yprpb;
912e3adcf8fSFrançois Tigeot 		burst_ena = false;
913e3adcf8fSFrançois Tigeot 		break;
914e3adcf8fSFrançois Tigeot 	case DRM_MODE_CONNECTOR_SVIDEO:
915e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
916e3adcf8fSFrançois Tigeot 		video_levels = tv_mode->svideo_levels;
917e3adcf8fSFrançois Tigeot 		color_conversion = tv_mode->svideo_color;
918e3adcf8fSFrançois Tigeot 		burst_ena = tv_mode->burst_ena;
919e3adcf8fSFrançois Tigeot 		break;
920e3adcf8fSFrançois Tigeot 	}
921e3adcf8fSFrançois Tigeot 	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
922e3adcf8fSFrançois Tigeot 		(tv_mode->htotal << TV_HTOTAL_SHIFT);
923e3adcf8fSFrançois Tigeot 
924e3adcf8fSFrançois Tigeot 	hctl2 = (tv_mode->hburst_start << 16) |
925e3adcf8fSFrançois Tigeot 		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
926e3adcf8fSFrançois Tigeot 
927e3adcf8fSFrançois Tigeot 	if (burst_ena)
928e3adcf8fSFrançois Tigeot 		hctl2 |= TV_BURST_ENA;
929e3adcf8fSFrançois Tigeot 
930e3adcf8fSFrançois Tigeot 	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
931e3adcf8fSFrançois Tigeot 		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
932e3adcf8fSFrançois Tigeot 
933e3adcf8fSFrançois Tigeot 	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
934e3adcf8fSFrançois Tigeot 		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
935e3adcf8fSFrançois Tigeot 		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
936e3adcf8fSFrançois Tigeot 
937e3adcf8fSFrançois Tigeot 	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
938e3adcf8fSFrançois Tigeot 		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
939e3adcf8fSFrançois Tigeot 		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
940e3adcf8fSFrançois Tigeot 
941e3adcf8fSFrançois Tigeot 	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
942e3adcf8fSFrançois Tigeot 		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
943e3adcf8fSFrançois Tigeot 		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
944e3adcf8fSFrançois Tigeot 
945e3adcf8fSFrançois Tigeot 	if (tv_mode->veq_ena)
946e3adcf8fSFrançois Tigeot 		vctl3 |= TV_EQUAL_ENA;
947e3adcf8fSFrançois Tigeot 
948e3adcf8fSFrançois Tigeot 	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
949e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
950e3adcf8fSFrançois Tigeot 
951e3adcf8fSFrançois Tigeot 	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
952e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
953e3adcf8fSFrançois Tigeot 
954e3adcf8fSFrançois Tigeot 	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
955e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
956e3adcf8fSFrançois Tigeot 
957e3adcf8fSFrançois Tigeot 	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
958e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
959e3adcf8fSFrançois Tigeot 
960e3adcf8fSFrançois Tigeot 	if (intel_crtc->pipe == 1)
961e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_PIPEB_SELECT;
962e3adcf8fSFrançois Tigeot 	tv_ctl |= tv_mode->oversample;
963e3adcf8fSFrançois Tigeot 
964e3adcf8fSFrançois Tigeot 	if (tv_mode->progressive)
965e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_PROGRESSIVE;
966e3adcf8fSFrançois Tigeot 	if (tv_mode->trilevel_sync)
967e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_TRILEVEL_SYNC;
968e3adcf8fSFrançois Tigeot 	if (tv_mode->pal_burst)
969e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_PAL_BURST;
970e3adcf8fSFrançois Tigeot 
971e3adcf8fSFrançois Tigeot 	scctl1 = 0;
972e3adcf8fSFrançois Tigeot 	if (tv_mode->dda1_inc)
973e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA1_EN;
974e3adcf8fSFrançois Tigeot 	if (tv_mode->dda2_inc)
975e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA2_EN;
976e3adcf8fSFrançois Tigeot 	if (tv_mode->dda3_inc)
977e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA3_EN;
978e3adcf8fSFrançois Tigeot 	scctl1 |= tv_mode->sc_reset;
979e3adcf8fSFrançois Tigeot 	if (video_levels)
980e3adcf8fSFrançois Tigeot 		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
981e3adcf8fSFrançois Tigeot 	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
982e3adcf8fSFrançois Tigeot 
983e3adcf8fSFrançois Tigeot 	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
984e3adcf8fSFrançois Tigeot 		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
985e3adcf8fSFrançois Tigeot 
986e3adcf8fSFrançois Tigeot 	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
987e3adcf8fSFrançois Tigeot 		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
988e3adcf8fSFrançois Tigeot 
989e3adcf8fSFrançois Tigeot 	/* Enable two fixes for the chips that need them. */
990e3adcf8fSFrançois Tigeot 	if (dev->pci_device < 0x2772)
991e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
992e3adcf8fSFrançois Tigeot 
993e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_H_CTL_1, hctl1);
994e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_H_CTL_2, hctl2);
995e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_H_CTL_3, hctl3);
996e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_1, vctl1);
997e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_2, vctl2);
998e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_3, vctl3);
999e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_4, vctl4);
1000e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_5, vctl5);
1001e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_6, vctl6);
1002e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_V_CTL_7, vctl7);
1003e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_1, scctl1);
1004e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_2, scctl2);
1005e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_3, scctl3);
1006e3adcf8fSFrançois Tigeot 
1007e3adcf8fSFrançois Tigeot 	if (color_conversion) {
1008e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
1009e3adcf8fSFrançois Tigeot 			   color_conversion->gy);
1010e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
1011e3adcf8fSFrançois Tigeot 			   color_conversion->ay);
1012e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
1013e3adcf8fSFrançois Tigeot 			   color_conversion->gu);
1014e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
1015e3adcf8fSFrançois Tigeot 			   color_conversion->au);
1016e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
1017e3adcf8fSFrançois Tigeot 			   color_conversion->gv);
1018e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
1019e3adcf8fSFrançois Tigeot 			   color_conversion->av);
1020e3adcf8fSFrançois Tigeot 	}
1021e3adcf8fSFrançois Tigeot 
1022e3adcf8fSFrançois Tigeot 	if (INTEL_INFO(dev)->gen >= 4)
1023e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_KNOBS, 0x00404000);
1024e3adcf8fSFrançois Tigeot 	else
1025e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_KNOBS, 0x00606000);
1026e3adcf8fSFrançois Tigeot 
1027e3adcf8fSFrançois Tigeot 	if (video_levels)
1028e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_LEVEL,
1029e3adcf8fSFrançois Tigeot 			   ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1030e3adcf8fSFrançois Tigeot 			    (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1031e3adcf8fSFrançois Tigeot 	{
1032e3adcf8fSFrançois Tigeot 		int pipeconf_reg = PIPECONF(pipe);
1033e3adcf8fSFrançois Tigeot 		int dspcntr_reg = DSPCNTR(intel_crtc->plane);
1034e3adcf8fSFrançois Tigeot 		int pipeconf = I915_READ(pipeconf_reg);
1035e3adcf8fSFrançois Tigeot 		int dspcntr = I915_READ(dspcntr_reg);
1036e3adcf8fSFrançois Tigeot 		int dspbase_reg = DSPADDR(intel_crtc->plane);
1037e3adcf8fSFrançois Tigeot 		int xpos = 0x0, ypos = 0x0;
1038e3adcf8fSFrançois Tigeot 		unsigned int xsize, ysize;
1039e3adcf8fSFrançois Tigeot 		/* Pipe must be off here */
1040e3adcf8fSFrançois Tigeot 		I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
1041e3adcf8fSFrançois Tigeot 		/* Flush the plane changes */
1042e3adcf8fSFrançois Tigeot 		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1043e3adcf8fSFrançois Tigeot 
1044e3adcf8fSFrançois Tigeot 		/* Wait for vblank for the disable to take effect */
1045e3adcf8fSFrançois Tigeot 		if (IS_GEN2(dev))
1046e3adcf8fSFrançois Tigeot 			intel_wait_for_vblank(dev, intel_crtc->pipe);
1047e3adcf8fSFrançois Tigeot 
1048e3adcf8fSFrançois Tigeot 		I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
1049e3adcf8fSFrançois Tigeot 		/* Wait for vblank for the disable to take effect. */
1050e3adcf8fSFrançois Tigeot 		intel_wait_for_pipe_off(dev, intel_crtc->pipe);
1051e3adcf8fSFrançois Tigeot 
1052e3adcf8fSFrançois Tigeot 		/* Filter ctl must be set before TV_WIN_SIZE */
1053e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1054e3adcf8fSFrançois Tigeot 		xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1055e3adcf8fSFrançois Tigeot 		if (tv_mode->progressive)
1056e3adcf8fSFrançois Tigeot 			ysize = tv_mode->nbr_end + 1;
1057e3adcf8fSFrançois Tigeot 		else
1058e3adcf8fSFrançois Tigeot 			ysize = 2*tv_mode->nbr_end + 1;
1059e3adcf8fSFrançois Tigeot 
1060e3adcf8fSFrançois Tigeot 		xpos += intel_tv->margin[TV_MARGIN_LEFT];
1061e3adcf8fSFrançois Tigeot 		ypos += intel_tv->margin[TV_MARGIN_TOP];
1062e3adcf8fSFrançois Tigeot 		xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
1063e3adcf8fSFrançois Tigeot 			  intel_tv->margin[TV_MARGIN_RIGHT]);
1064e3adcf8fSFrançois Tigeot 		ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
1065e3adcf8fSFrançois Tigeot 			  intel_tv->margin[TV_MARGIN_BOTTOM]);
1066e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
1067e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
1068e3adcf8fSFrançois Tigeot 
1069e3adcf8fSFrançois Tigeot 		I915_WRITE(pipeconf_reg, pipeconf);
1070e3adcf8fSFrançois Tigeot 		I915_WRITE(dspcntr_reg, dspcntr);
1071e3adcf8fSFrançois Tigeot 		/* Flush the plane changes */
1072e3adcf8fSFrançois Tigeot 		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1073e3adcf8fSFrançois Tigeot 	}
1074e3adcf8fSFrançois Tigeot 
1075e3adcf8fSFrançois Tigeot 	j = 0;
1076e3adcf8fSFrançois Tigeot 	for (i = 0; i < 60; i++)
1077e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1078e3adcf8fSFrançois Tigeot 	for (i = 0; i < 60; i++)
1079e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1080e3adcf8fSFrançois Tigeot 	for (i = 0; i < 43; i++)
1081e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1082e3adcf8fSFrançois Tigeot 	for (i = 0; i < 43; i++)
1083e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1084e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
1085e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, tv_ctl);
1086e3adcf8fSFrançois Tigeot }
1087e3adcf8fSFrançois Tigeot 
1088e3adcf8fSFrançois Tigeot static const struct drm_display_mode reported_modes[] = {
1089e3adcf8fSFrançois Tigeot 	{
1090e3adcf8fSFrançois Tigeot 		.name = "NTSC 480i",
1091e3adcf8fSFrançois Tigeot 		.clock = 107520,
1092e3adcf8fSFrançois Tigeot 		.hdisplay = 1280,
1093e3adcf8fSFrançois Tigeot 		.hsync_start = 1368,
1094e3adcf8fSFrançois Tigeot 		.hsync_end = 1496,
1095e3adcf8fSFrançois Tigeot 		.htotal = 1712,
1096e3adcf8fSFrançois Tigeot 
1097e3adcf8fSFrançois Tigeot 		.vdisplay = 1024,
1098e3adcf8fSFrançois Tigeot 		.vsync_start = 1027,
1099e3adcf8fSFrançois Tigeot 		.vsync_end = 1034,
1100e3adcf8fSFrançois Tigeot 		.vtotal = 1104,
1101e3adcf8fSFrançois Tigeot 		.type = DRM_MODE_TYPE_DRIVER,
1102e3adcf8fSFrançois Tigeot 	},
1103e3adcf8fSFrançois Tigeot };
1104e3adcf8fSFrançois Tigeot 
1105e3adcf8fSFrançois Tigeot /**
1106e3adcf8fSFrançois Tigeot  * Detects TV presence by checking for load.
1107e3adcf8fSFrançois Tigeot  *
1108e3adcf8fSFrançois Tigeot  * Requires that the current pipe's DPLL is active.
1109e3adcf8fSFrançois Tigeot 
1110e3adcf8fSFrançois Tigeot  * \return true if TV is connected.
1111e3adcf8fSFrançois Tigeot  * \return false if TV is disconnected.
1112e3adcf8fSFrançois Tigeot  */
1113e3adcf8fSFrançois Tigeot static int
1114e3adcf8fSFrançois Tigeot intel_tv_detect_type(struct intel_tv *intel_tv,
1115e3adcf8fSFrançois Tigeot 		      struct drm_connector *connector)
1116e3adcf8fSFrançois Tigeot {
1117e3adcf8fSFrançois Tigeot 	struct drm_encoder *encoder = &intel_tv->base.base;
1118e3adcf8fSFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
1119e3adcf8fSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1120e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
1121e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1122e3adcf8fSFrançois Tigeot 	u32 tv_ctl, save_tv_ctl;
1123e3adcf8fSFrançois Tigeot 	u32 tv_dac, save_tv_dac;
1124e3adcf8fSFrançois Tigeot 	int type;
1125e3adcf8fSFrançois Tigeot 
1126e3adcf8fSFrançois Tigeot 	/* Disable TV interrupts around load detect or we'll recurse */
1127e3adcf8fSFrançois Tigeot 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1128e3adcf8fSFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
1129e3adcf8fSFrançois Tigeot 		i915_disable_pipestat(dev_priv, 0,
1130e3adcf8fSFrançois Tigeot 				      PIPE_HOTPLUG_INTERRUPT_ENABLE |
1131e3adcf8fSFrançois Tigeot 				      PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1132e3adcf8fSFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
1133e3adcf8fSFrançois Tigeot 	}
1134e3adcf8fSFrançois Tigeot 
1135e3adcf8fSFrançois Tigeot 	save_tv_dac = tv_dac = I915_READ(TV_DAC);
1136e3adcf8fSFrançois Tigeot 	save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
1137e3adcf8fSFrançois Tigeot 
1138e3adcf8fSFrançois Tigeot 	/* Poll for TV detection */
1139e3adcf8fSFrançois Tigeot 	tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
1140e3adcf8fSFrançois Tigeot 	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1141e3adcf8fSFrançois Tigeot 	if (intel_crtc->pipe == 1)
1142e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_PIPEB_SELECT;
1143e3adcf8fSFrançois Tigeot 	else
1144e3adcf8fSFrançois Tigeot 		tv_ctl &= ~TV_ENC_PIPEB_SELECT;
1145e3adcf8fSFrançois Tigeot 
1146e3adcf8fSFrançois Tigeot 	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1147e3adcf8fSFrançois Tigeot 	tv_dac |= (TVDAC_STATE_CHG_EN |
1148e3adcf8fSFrançois Tigeot 		   TVDAC_A_SENSE_CTL |
1149e3adcf8fSFrançois Tigeot 		   TVDAC_B_SENSE_CTL |
1150e3adcf8fSFrançois Tigeot 		   TVDAC_C_SENSE_CTL |
1151e3adcf8fSFrançois Tigeot 		   DAC_CTL_OVERRIDE |
1152e3adcf8fSFrançois Tigeot 		   DAC_A_0_7_V |
1153e3adcf8fSFrançois Tigeot 		   DAC_B_0_7_V |
1154e3adcf8fSFrançois Tigeot 		   DAC_C_0_7_V);
1155e3adcf8fSFrançois Tigeot 
1156e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, tv_ctl);
1157e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, tv_dac);
1158e3adcf8fSFrançois Tigeot 	POSTING_READ(TV_DAC);
1159e3adcf8fSFrançois Tigeot 
1160e3adcf8fSFrançois Tigeot 	intel_wait_for_vblank(intel_tv->base.base.dev,
1161e3adcf8fSFrançois Tigeot 			      to_intel_crtc(intel_tv->base.base.crtc)->pipe);
1162e3adcf8fSFrançois Tigeot 
1163e3adcf8fSFrançois Tigeot 	type = -1;
1164e3adcf8fSFrançois Tigeot 	tv_dac = I915_READ(TV_DAC);
1165e3adcf8fSFrançois Tigeot 	DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
1166e3adcf8fSFrançois Tigeot 	/*
1167e3adcf8fSFrançois Tigeot 	 *  A B C
1168e3adcf8fSFrançois Tigeot 	 *  0 1 1 Composite
1169e3adcf8fSFrançois Tigeot 	 *  1 0 X svideo
1170e3adcf8fSFrançois Tigeot 	 *  0 0 0 Component
1171e3adcf8fSFrançois Tigeot 	 */
1172e3adcf8fSFrançois Tigeot 	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1173e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected Composite TV connection\n");
1174e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_Composite;
1175e3adcf8fSFrançois Tigeot 	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1176e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected S-Video TV connection\n");
1177e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_SVIDEO;
1178e3adcf8fSFrançois Tigeot 	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1179e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected Component TV connection\n");
1180e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_Component;
1181e3adcf8fSFrançois Tigeot 	} else {
1182e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Unrecognised TV connection\n");
1183e3adcf8fSFrançois Tigeot 		type = -1;
1184e3adcf8fSFrançois Tigeot 	}
1185e3adcf8fSFrançois Tigeot 
1186e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1187e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, save_tv_ctl);
1188e3adcf8fSFrançois Tigeot 
1189e3adcf8fSFrançois Tigeot 	/* Restore interrupt config */
1190e3adcf8fSFrançois Tigeot 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1191e3adcf8fSFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
1192e3adcf8fSFrançois Tigeot 		i915_enable_pipestat(dev_priv, 0,
1193e3adcf8fSFrançois Tigeot 				     PIPE_HOTPLUG_INTERRUPT_ENABLE |
1194e3adcf8fSFrançois Tigeot 				     PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1195e3adcf8fSFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
1196e3adcf8fSFrançois Tigeot 	}
1197e3adcf8fSFrançois Tigeot 
1198e3adcf8fSFrançois Tigeot 	return type;
1199e3adcf8fSFrançois Tigeot }
1200e3adcf8fSFrançois Tigeot 
1201e3adcf8fSFrançois Tigeot /*
1202e3adcf8fSFrançois Tigeot  * Here we set accurate tv format according to connector type
1203e3adcf8fSFrançois Tigeot  * i.e Component TV should not be assigned by NTSC or PAL
1204e3adcf8fSFrançois Tigeot  */
1205e3adcf8fSFrançois Tigeot static void intel_tv_find_better_format(struct drm_connector *connector)
1206e3adcf8fSFrançois Tigeot {
1207e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1208e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1209e3adcf8fSFrançois Tigeot 	int i;
1210e3adcf8fSFrançois Tigeot 
1211e3adcf8fSFrançois Tigeot 	if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1212e3adcf8fSFrançois Tigeot 		tv_mode->component_only)
1213e3adcf8fSFrançois Tigeot 		return;
1214e3adcf8fSFrançois Tigeot 
1215e3adcf8fSFrançois Tigeot 
1216e3adcf8fSFrançois Tigeot 	for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) {
1217e3adcf8fSFrançois Tigeot 		tv_mode = tv_modes + i;
1218e3adcf8fSFrançois Tigeot 
1219e3adcf8fSFrançois Tigeot 		if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1220e3adcf8fSFrançois Tigeot 			tv_mode->component_only)
1221e3adcf8fSFrançois Tigeot 			break;
1222e3adcf8fSFrançois Tigeot 	}
1223e3adcf8fSFrançois Tigeot 
1224e3adcf8fSFrançois Tigeot 	intel_tv->tv_format = tv_mode->name;
1225*b5162e19SFrançois Tigeot 	drm_object_property_set_value(&connector->base,
1226e3adcf8fSFrançois Tigeot 		connector->dev->mode_config.tv_mode_property, i);
1227e3adcf8fSFrançois Tigeot }
1228e3adcf8fSFrançois Tigeot 
1229e3adcf8fSFrançois Tigeot /**
1230e3adcf8fSFrançois Tigeot  * Detect the TV connection.
1231e3adcf8fSFrançois Tigeot  *
1232e3adcf8fSFrançois Tigeot  * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
1233e3adcf8fSFrançois Tigeot  * we have a pipe programmed in order to probe the TV.
1234e3adcf8fSFrançois Tigeot  */
1235e3adcf8fSFrançois Tigeot static enum drm_connector_status
1236e3adcf8fSFrançois Tigeot intel_tv_detect(struct drm_connector *connector, bool force)
1237e3adcf8fSFrançois Tigeot {
1238e3adcf8fSFrançois Tigeot 	struct drm_display_mode mode;
1239e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1240e3adcf8fSFrançois Tigeot 	int type;
1241e3adcf8fSFrançois Tigeot 
1242e3adcf8fSFrançois Tigeot 	mode = reported_modes[0];
1243e3adcf8fSFrançois Tigeot 	drm_mode_set_crtcinfo(&mode, 0);
1244e3adcf8fSFrançois Tigeot 
1245e3adcf8fSFrançois Tigeot 	if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) {
1246e3adcf8fSFrançois Tigeot 		type = intel_tv_detect_type(intel_tv, connector);
1247e3adcf8fSFrançois Tigeot 	} else if (force) {
1248e3adcf8fSFrançois Tigeot 		struct intel_load_detect_pipe tmp;
1249e3adcf8fSFrançois Tigeot 
1250e3adcf8fSFrançois Tigeot 		if (intel_get_load_detect_pipe(&intel_tv->base, connector,
1251e3adcf8fSFrançois Tigeot 					       &mode, &tmp)) {
1252e3adcf8fSFrançois Tigeot 			type = intel_tv_detect_type(intel_tv, connector);
1253e3adcf8fSFrançois Tigeot 			intel_release_load_detect_pipe(&intel_tv->base,
1254e3adcf8fSFrançois Tigeot 						       connector,
1255e3adcf8fSFrançois Tigeot 						       &tmp);
1256e3adcf8fSFrançois Tigeot 		} else
1257e3adcf8fSFrançois Tigeot 			return connector_status_unknown;
1258e3adcf8fSFrançois Tigeot 	} else
1259e3adcf8fSFrançois Tigeot 		return connector->status;
1260e3adcf8fSFrançois Tigeot 
1261e3adcf8fSFrançois Tigeot 	if (type < 0)
1262e3adcf8fSFrançois Tigeot 		return connector_status_disconnected;
1263e3adcf8fSFrançois Tigeot 
1264e3adcf8fSFrançois Tigeot 	intel_tv->type = type;
1265e3adcf8fSFrançois Tigeot 	intel_tv_find_better_format(connector);
1266e3adcf8fSFrançois Tigeot 
1267e3adcf8fSFrançois Tigeot 	return connector_status_connected;
1268e3adcf8fSFrançois Tigeot }
1269e3adcf8fSFrançois Tigeot 
1270e3adcf8fSFrançois Tigeot static const struct input_res {
1271e3adcf8fSFrançois Tigeot 	const char *name;
1272e3adcf8fSFrançois Tigeot 	int w, h;
1273e3adcf8fSFrançois Tigeot } input_res_table[] = {
1274e3adcf8fSFrançois Tigeot 	{"640x480", 640, 480},
1275e3adcf8fSFrançois Tigeot 	{"800x600", 800, 600},
1276e3adcf8fSFrançois Tigeot 	{"1024x768", 1024, 768},
1277e3adcf8fSFrançois Tigeot 	{"1280x1024", 1280, 1024},
1278e3adcf8fSFrançois Tigeot 	{"848x480", 848, 480},
1279e3adcf8fSFrançois Tigeot 	{"1280x720", 1280, 720},
1280e3adcf8fSFrançois Tigeot 	{"1920x1080", 1920, 1080},
1281e3adcf8fSFrançois Tigeot };
1282e3adcf8fSFrançois Tigeot 
1283e3adcf8fSFrançois Tigeot /*
1284e3adcf8fSFrançois Tigeot  * Chose preferred mode  according to line number of TV format
1285e3adcf8fSFrançois Tigeot  */
1286e3adcf8fSFrançois Tigeot static void
1287e3adcf8fSFrançois Tigeot intel_tv_chose_preferred_modes(struct drm_connector *connector,
1288e3adcf8fSFrançois Tigeot 			       struct drm_display_mode *mode_ptr)
1289e3adcf8fSFrançois Tigeot {
1290e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1291e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1292e3adcf8fSFrançois Tigeot 
1293e3adcf8fSFrançois Tigeot 	if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
1294e3adcf8fSFrançois Tigeot 		mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1295e3adcf8fSFrançois Tigeot 	else if (tv_mode->nbr_end > 480) {
1296e3adcf8fSFrançois Tigeot 		if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
1297e3adcf8fSFrançois Tigeot 			if (mode_ptr->vdisplay == 720)
1298e3adcf8fSFrançois Tigeot 				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1299e3adcf8fSFrançois Tigeot 		} else if (mode_ptr->vdisplay == 1080)
1300e3adcf8fSFrançois Tigeot 				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1301e3adcf8fSFrançois Tigeot 	}
1302e3adcf8fSFrançois Tigeot }
1303e3adcf8fSFrançois Tigeot 
1304e3adcf8fSFrançois Tigeot /**
1305e3adcf8fSFrançois Tigeot  * Stub get_modes function.
1306e3adcf8fSFrançois Tigeot  *
1307e3adcf8fSFrançois Tigeot  * This should probably return a set of fixed modes, unless we can figure out
1308e3adcf8fSFrançois Tigeot  * how to probe modes off of TV connections.
1309e3adcf8fSFrançois Tigeot  */
1310e3adcf8fSFrançois Tigeot 
1311e3adcf8fSFrançois Tigeot static int
1312e3adcf8fSFrançois Tigeot intel_tv_get_modes(struct drm_connector *connector)
1313e3adcf8fSFrançois Tigeot {
1314e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode_ptr;
1315e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1316e3adcf8fSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1317e3adcf8fSFrançois Tigeot 	int j, count = 0;
1318e3adcf8fSFrançois Tigeot 	u64 tmp;
1319e3adcf8fSFrançois Tigeot 
1320e3adcf8fSFrançois Tigeot 	for (j = 0; j < DRM_ARRAY_SIZE(input_res_table);
1321e3adcf8fSFrançois Tigeot 	     j++) {
1322e3adcf8fSFrançois Tigeot 		const struct input_res *input = &input_res_table[j];
1323e3adcf8fSFrançois Tigeot 		unsigned int hactive_s = input->w;
1324e3adcf8fSFrançois Tigeot 		unsigned int vactive_s = input->h;
1325e3adcf8fSFrançois Tigeot 
1326e3adcf8fSFrançois Tigeot 		if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1327e3adcf8fSFrançois Tigeot 			continue;
1328e3adcf8fSFrançois Tigeot 
1329e3adcf8fSFrançois Tigeot 		if (input->w > 1024 && (!tv_mode->progressive
1330e3adcf8fSFrançois Tigeot 					&& !tv_mode->component_only))
1331e3adcf8fSFrançois Tigeot 			continue;
1332e3adcf8fSFrançois Tigeot 
1333e3adcf8fSFrançois Tigeot 		mode_ptr = drm_mode_create(connector->dev);
1334e3adcf8fSFrançois Tigeot 		if (!mode_ptr)
1335e3adcf8fSFrançois Tigeot 			continue;
1336e3adcf8fSFrançois Tigeot 		strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
1337e3adcf8fSFrançois Tigeot 
1338e3adcf8fSFrançois Tigeot 		mode_ptr->hdisplay = hactive_s;
1339e3adcf8fSFrançois Tigeot 		mode_ptr->hsync_start = hactive_s + 1;
1340e3adcf8fSFrançois Tigeot 		mode_ptr->hsync_end = hactive_s + 64;
1341e3adcf8fSFrançois Tigeot 		if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
1342e3adcf8fSFrançois Tigeot 			mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
1343e3adcf8fSFrançois Tigeot 		mode_ptr->htotal = hactive_s + 96;
1344e3adcf8fSFrançois Tigeot 
1345e3adcf8fSFrançois Tigeot 		mode_ptr->vdisplay = vactive_s;
1346e3adcf8fSFrançois Tigeot 		mode_ptr->vsync_start = vactive_s + 1;
1347e3adcf8fSFrançois Tigeot 		mode_ptr->vsync_end = vactive_s + 32;
1348e3adcf8fSFrançois Tigeot 		if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
1349e3adcf8fSFrançois Tigeot 			mode_ptr->vsync_end = mode_ptr->vsync_start  + 1;
1350e3adcf8fSFrançois Tigeot 		mode_ptr->vtotal = vactive_s + 33;
1351e3adcf8fSFrançois Tigeot 
1352e3adcf8fSFrançois Tigeot 		tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
1353e3adcf8fSFrançois Tigeot 		tmp *= mode_ptr->htotal;
1354e3adcf8fSFrançois Tigeot 		tmp = tmp / 1000000;
1355e3adcf8fSFrançois Tigeot 		mode_ptr->clock = (int) tmp;
1356e3adcf8fSFrançois Tigeot 
1357e3adcf8fSFrançois Tigeot 		mode_ptr->type = DRM_MODE_TYPE_DRIVER;
1358e3adcf8fSFrançois Tigeot 		intel_tv_chose_preferred_modes(connector, mode_ptr);
1359e3adcf8fSFrançois Tigeot 		drm_mode_probed_add(connector, mode_ptr);
1360e3adcf8fSFrançois Tigeot 		count++;
1361e3adcf8fSFrançois Tigeot 	}
1362e3adcf8fSFrançois Tigeot 
1363e3adcf8fSFrançois Tigeot 	return count;
1364e3adcf8fSFrançois Tigeot }
1365e3adcf8fSFrançois Tigeot 
1366e3adcf8fSFrançois Tigeot static void
1367e3adcf8fSFrançois Tigeot intel_tv_destroy(struct drm_connector *connector)
1368e3adcf8fSFrançois Tigeot {
1369e3adcf8fSFrançois Tigeot #if 0
1370e3adcf8fSFrançois Tigeot 	drm_sysfs_connector_remove(connector);
1371e3adcf8fSFrançois Tigeot #endif
1372e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
1373e3adcf8fSFrançois Tigeot 	drm_free(connector, DRM_MEM_KMS);
1374e3adcf8fSFrançois Tigeot }
1375e3adcf8fSFrançois Tigeot 
1376e3adcf8fSFrançois Tigeot 
1377e3adcf8fSFrançois Tigeot static int
1378e3adcf8fSFrançois Tigeot intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
1379e3adcf8fSFrançois Tigeot 		      uint64_t val)
1380e3adcf8fSFrançois Tigeot {
1381e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
1382e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1383e3adcf8fSFrançois Tigeot 	struct drm_crtc *crtc = intel_tv->base.base.crtc;
1384e3adcf8fSFrançois Tigeot 	int ret = 0;
1385e3adcf8fSFrançois Tigeot 	bool changed = false;
1386e3adcf8fSFrançois Tigeot 
1387*b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base, property, val);
1388e3adcf8fSFrançois Tigeot 	if (ret < 0)
1389e3adcf8fSFrançois Tigeot 		goto out;
1390e3adcf8fSFrançois Tigeot 
1391e3adcf8fSFrançois Tigeot 	if (property == dev->mode_config.tv_left_margin_property &&
1392e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_LEFT] != val) {
1393e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_LEFT] = val;
1394e3adcf8fSFrançois Tigeot 		changed = true;
1395e3adcf8fSFrançois Tigeot 	} else if (property == dev->mode_config.tv_right_margin_property &&
1396e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_RIGHT] != val) {
1397e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_RIGHT] = val;
1398e3adcf8fSFrançois Tigeot 		changed = true;
1399e3adcf8fSFrançois Tigeot 	} else if (property == dev->mode_config.tv_top_margin_property &&
1400e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_TOP] != val) {
1401e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_TOP] = val;
1402e3adcf8fSFrançois Tigeot 		changed = true;
1403e3adcf8fSFrançois Tigeot 	} else if (property == dev->mode_config.tv_bottom_margin_property &&
1404e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_BOTTOM] != val) {
1405e3adcf8fSFrançois Tigeot 		intel_tv->margin[TV_MARGIN_BOTTOM] = val;
1406e3adcf8fSFrançois Tigeot 		changed = true;
1407e3adcf8fSFrançois Tigeot 	} else if (property == dev->mode_config.tv_mode_property) {
1408e3adcf8fSFrançois Tigeot 		if (val >= DRM_ARRAY_SIZE(tv_modes)) {
1409e3adcf8fSFrançois Tigeot 			ret = -EINVAL;
1410e3adcf8fSFrançois Tigeot 			goto out;
1411e3adcf8fSFrançois Tigeot 		}
1412e3adcf8fSFrançois Tigeot 		if (!strcmp(intel_tv->tv_format, tv_modes[val].name))
1413e3adcf8fSFrançois Tigeot 			goto out;
1414e3adcf8fSFrançois Tigeot 
1415e3adcf8fSFrançois Tigeot 		intel_tv->tv_format = tv_modes[val].name;
1416e3adcf8fSFrançois Tigeot 		changed = true;
1417e3adcf8fSFrançois Tigeot 	} else {
1418e3adcf8fSFrançois Tigeot 		ret = -EINVAL;
1419e3adcf8fSFrançois Tigeot 		goto out;
1420e3adcf8fSFrançois Tigeot 	}
1421e3adcf8fSFrançois Tigeot 
1422e3adcf8fSFrançois Tigeot 	if (changed && crtc)
1423e3adcf8fSFrançois Tigeot 		drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
1424e3adcf8fSFrançois Tigeot 				crtc->y, crtc->fb);
1425e3adcf8fSFrançois Tigeot out:
1426e3adcf8fSFrançois Tigeot 	return ret;
1427e3adcf8fSFrançois Tigeot }
1428e3adcf8fSFrançois Tigeot 
1429e3adcf8fSFrançois Tigeot static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
1430e3adcf8fSFrançois Tigeot 	.dpms = intel_tv_dpms,
1431e3adcf8fSFrançois Tigeot 	.mode_fixup = intel_tv_mode_fixup,
1432e3adcf8fSFrançois Tigeot 	.prepare = intel_encoder_prepare,
1433e3adcf8fSFrançois Tigeot 	.mode_set = intel_tv_mode_set,
1434e3adcf8fSFrançois Tigeot 	.commit = intel_encoder_commit,
1435e3adcf8fSFrançois Tigeot };
1436e3adcf8fSFrançois Tigeot 
1437e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_tv_connector_funcs = {
1438e3adcf8fSFrançois Tigeot 	.dpms = drm_helper_connector_dpms,
1439e3adcf8fSFrançois Tigeot 	.detect = intel_tv_detect,
1440e3adcf8fSFrançois Tigeot 	.destroy = intel_tv_destroy,
1441e3adcf8fSFrançois Tigeot 	.set_property = intel_tv_set_property,
1442e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
1443e3adcf8fSFrançois Tigeot };
1444e3adcf8fSFrançois Tigeot 
1445e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1446e3adcf8fSFrançois Tigeot 	.mode_valid = intel_tv_mode_valid,
1447e3adcf8fSFrançois Tigeot 	.get_modes = intel_tv_get_modes,
1448e3adcf8fSFrançois Tigeot 	.best_encoder = intel_best_encoder,
1449e3adcf8fSFrançois Tigeot };
1450e3adcf8fSFrançois Tigeot 
1451e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_tv_enc_funcs = {
1452e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
1453e3adcf8fSFrançois Tigeot };
1454e3adcf8fSFrançois Tigeot 
1455e3adcf8fSFrançois Tigeot /*
1456e3adcf8fSFrançois Tigeot  * Enumerate the child dev array parsed from VBT to check whether
1457e3adcf8fSFrançois Tigeot  * the integrated TV is present.
1458e3adcf8fSFrançois Tigeot  * If it is present, return 1.
1459e3adcf8fSFrançois Tigeot  * If it is not present, return false.
1460e3adcf8fSFrançois Tigeot  * If no child dev is parsed from VBT, it assumes that the TV is present.
1461e3adcf8fSFrançois Tigeot  */
1462e3adcf8fSFrançois Tigeot static int tv_is_present_in_vbt(struct drm_device *dev)
1463e3adcf8fSFrançois Tigeot {
1464e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1465e3adcf8fSFrançois Tigeot 	struct child_device_config *p_child;
1466e3adcf8fSFrançois Tigeot 	int i, ret;
1467e3adcf8fSFrançois Tigeot 
1468e3adcf8fSFrançois Tigeot 	if (!dev_priv->child_dev_num)
1469e3adcf8fSFrançois Tigeot 		return 1;
1470e3adcf8fSFrançois Tigeot 
1471e3adcf8fSFrançois Tigeot 	ret = 0;
1472e3adcf8fSFrançois Tigeot 	for (i = 0; i < dev_priv->child_dev_num; i++) {
1473e3adcf8fSFrançois Tigeot 		p_child = dev_priv->child_dev + i;
1474e3adcf8fSFrançois Tigeot 		/*
1475e3adcf8fSFrançois Tigeot 		 * If the device type is not TV, continue.
1476e3adcf8fSFrançois Tigeot 		 */
1477e3adcf8fSFrançois Tigeot 		if (p_child->device_type != DEVICE_TYPE_INT_TV &&
1478e3adcf8fSFrançois Tigeot 			p_child->device_type != DEVICE_TYPE_TV)
1479e3adcf8fSFrançois Tigeot 			continue;
1480e3adcf8fSFrançois Tigeot 		/* Only when the addin_offset is non-zero, it is regarded
1481e3adcf8fSFrançois Tigeot 		 * as present.
1482e3adcf8fSFrançois Tigeot 		 */
1483e3adcf8fSFrançois Tigeot 		if (p_child->addin_offset) {
1484e3adcf8fSFrançois Tigeot 			ret = 1;
1485e3adcf8fSFrançois Tigeot 			break;
1486e3adcf8fSFrançois Tigeot 		}
1487e3adcf8fSFrançois Tigeot 	}
1488e3adcf8fSFrançois Tigeot 	return ret;
1489e3adcf8fSFrançois Tigeot }
1490e3adcf8fSFrançois Tigeot 
1491e3adcf8fSFrançois Tigeot void
1492e3adcf8fSFrançois Tigeot intel_tv_init(struct drm_device *dev)
1493e3adcf8fSFrançois Tigeot {
1494e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1495e3adcf8fSFrançois Tigeot 	struct drm_connector *connector;
1496e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv;
1497e3adcf8fSFrançois Tigeot 	struct intel_encoder *intel_encoder;
1498e3adcf8fSFrançois Tigeot 	struct intel_connector *intel_connector;
1499e3adcf8fSFrançois Tigeot 	u32 tv_dac_on, tv_dac_off, save_tv_dac;
1500e3adcf8fSFrançois Tigeot 	char *tv_format_names[DRM_ARRAY_SIZE(tv_modes)];
1501e3adcf8fSFrançois Tigeot 	int i, initial_mode = 0;
1502e3adcf8fSFrançois Tigeot 
1503e3adcf8fSFrançois Tigeot 	if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1504e3adcf8fSFrançois Tigeot 		return;
1505e3adcf8fSFrançois Tigeot 
1506e3adcf8fSFrançois Tigeot 	if (!tv_is_present_in_vbt(dev)) {
1507e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Integrated TV is not present.\n");
1508e3adcf8fSFrançois Tigeot 		return;
1509e3adcf8fSFrançois Tigeot 	}
1510e3adcf8fSFrançois Tigeot 	/* Even if we have an encoder we may not have a connector */
1511e3adcf8fSFrançois Tigeot 	if (!dev_priv->int_tv_support)
1512e3adcf8fSFrançois Tigeot 		return;
1513e3adcf8fSFrançois Tigeot 
1514e3adcf8fSFrançois Tigeot 	/*
1515e3adcf8fSFrançois Tigeot 	 * Sanity check the TV output by checking to see if the
1516e3adcf8fSFrançois Tigeot 	 * DAC register holds a value
1517e3adcf8fSFrançois Tigeot 	 */
1518e3adcf8fSFrançois Tigeot 	save_tv_dac = I915_READ(TV_DAC);
1519e3adcf8fSFrançois Tigeot 
1520e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1521e3adcf8fSFrançois Tigeot 	tv_dac_on = I915_READ(TV_DAC);
1522e3adcf8fSFrançois Tigeot 
1523e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1524e3adcf8fSFrançois Tigeot 	tv_dac_off = I915_READ(TV_DAC);
1525e3adcf8fSFrançois Tigeot 
1526e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac);
1527e3adcf8fSFrançois Tigeot 
1528e3adcf8fSFrançois Tigeot 	/*
1529e3adcf8fSFrançois Tigeot 	 * If the register does not hold the state change enable
1530e3adcf8fSFrançois Tigeot 	 * bit, (either as a 0 or a 1), assume it doesn't really
1531e3adcf8fSFrançois Tigeot 	 * exist
1532e3adcf8fSFrançois Tigeot 	 */
1533e3adcf8fSFrançois Tigeot 	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1534e3adcf8fSFrançois Tigeot 	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1535e3adcf8fSFrançois Tigeot 		return;
1536e3adcf8fSFrançois Tigeot 
1537e3adcf8fSFrançois Tigeot 	intel_tv = kmalloc(sizeof(struct intel_tv), DRM_MEM_KMS,
1538e3adcf8fSFrançois Tigeot 	    M_WAITOK | M_ZERO);
1539e3adcf8fSFrançois Tigeot 	intel_connector = kmalloc(sizeof(struct intel_connector), DRM_MEM_KMS,
1540e3adcf8fSFrançois Tigeot 	    M_WAITOK | M_ZERO);
1541e3adcf8fSFrançois Tigeot 
1542e3adcf8fSFrançois Tigeot 	intel_encoder = &intel_tv->base;
1543e3adcf8fSFrançois Tigeot 	connector = &intel_connector->base;
1544e3adcf8fSFrançois Tigeot 
1545e3adcf8fSFrançois Tigeot 	/* The documentation, for the older chipsets at least, recommend
1546e3adcf8fSFrançois Tigeot 	 * using a polling method rather than hotplug detection for TVs.
1547e3adcf8fSFrançois Tigeot 	 * This is because in order to perform the hotplug detection, the PLLs
1548e3adcf8fSFrançois Tigeot 	 * for the TV must be kept alive increasing power drain and starving
1549e3adcf8fSFrançois Tigeot 	 * bandwidth from other encoders. Notably for instance, it causes
1550e3adcf8fSFrançois Tigeot 	 * pipe underruns on Crestline when this encoder is supposedly idle.
1551e3adcf8fSFrançois Tigeot 	 *
1552e3adcf8fSFrançois Tigeot 	 * More recent chipsets favour HDMI rather than integrated S-Video.
1553e3adcf8fSFrançois Tigeot 	 */
1554e3adcf8fSFrançois Tigeot 	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1555e3adcf8fSFrançois Tigeot 
1556e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1557e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_SVIDEO);
1558e3adcf8fSFrançois Tigeot 
1559e3adcf8fSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
1560e3adcf8fSFrançois Tigeot 			 DRM_MODE_ENCODER_TVDAC);
1561e3adcf8fSFrançois Tigeot 
1562e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1563e3adcf8fSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_TVOUT;
1564e3adcf8fSFrançois Tigeot 	intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1565e3adcf8fSFrançois Tigeot 	intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
1566e3adcf8fSFrançois Tigeot 	intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
1567e3adcf8fSFrançois Tigeot 	intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
1568e3adcf8fSFrançois Tigeot 	intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
1569e3adcf8fSFrançois Tigeot 
1570e3adcf8fSFrançois Tigeot 	/* BIOS margin values */
1571e3adcf8fSFrançois Tigeot 	intel_tv->margin[TV_MARGIN_LEFT] = 54;
1572e3adcf8fSFrançois Tigeot 	intel_tv->margin[TV_MARGIN_TOP] = 36;
1573e3adcf8fSFrançois Tigeot 	intel_tv->margin[TV_MARGIN_RIGHT] = 46;
1574e3adcf8fSFrançois Tigeot 	intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
1575e3adcf8fSFrançois Tigeot 
1576e3adcf8fSFrançois Tigeot 	intel_tv->tv_format = tv_modes[initial_mode].name;
1577e3adcf8fSFrançois Tigeot 
1578e3adcf8fSFrançois Tigeot 	drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs);
1579e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1580e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = false;
1581e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = false;
1582e3adcf8fSFrançois Tigeot 
1583e3adcf8fSFrançois Tigeot 	/* Create TV properties then attach current values */
1584e3adcf8fSFrançois Tigeot 	for (i = 0; i < DRM_ARRAY_SIZE(tv_modes); i++)
1585e3adcf8fSFrançois Tigeot 		tv_format_names[i] = __DECONST(char *, tv_modes[i].name);
1586e3adcf8fSFrançois Tigeot 	drm_mode_create_tv_properties(dev,
1587e3adcf8fSFrançois Tigeot 				      DRM_ARRAY_SIZE(tv_modes),
1588e3adcf8fSFrançois Tigeot 				      tv_format_names);
1589e3adcf8fSFrançois Tigeot 
1590*b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
1591e3adcf8fSFrançois Tigeot 				   initial_mode);
1592*b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1593e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_left_margin_property,
1594e3adcf8fSFrançois Tigeot 				   intel_tv->margin[TV_MARGIN_LEFT]);
1595*b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1596e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_top_margin_property,
1597e3adcf8fSFrançois Tigeot 				   intel_tv->margin[TV_MARGIN_TOP]);
1598*b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1599e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_right_margin_property,
1600e3adcf8fSFrançois Tigeot 				   intel_tv->margin[TV_MARGIN_RIGHT]);
1601*b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1602e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_bottom_margin_property,
1603e3adcf8fSFrançois Tigeot 				   intel_tv->margin[TV_MARGIN_BOTTOM]);
1604e3adcf8fSFrançois Tigeot #if 0
1605e3adcf8fSFrançois Tigeot 	drm_sysfs_connector_add(connector);
1606e3adcf8fSFrançois Tigeot #endif
1607e3adcf8fSFrançois Tigeot }
1608