xref: /dragonfly/sys/dev/drm/i915/intel_tv.c (revision 3f2dd94a)
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  */
28e3adcf8fSFrançois Tigeot 
29e3adcf8fSFrançois Tigeot /** @file
30e3adcf8fSFrançois Tigeot  * Integrated TV-out support for the 915GM and 945GM.
31e3adcf8fSFrançois Tigeot  */
32e3adcf8fSFrançois Tigeot 
3318e26a6dSFrançois Tigeot #include <drm/drmP.h>
342c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h>
3518e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
3718e26a6dSFrançois Tigeot #include "intel_drv.h"
385c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
39e3adcf8fSFrançois Tigeot #include "i915_drv.h"
40e3adcf8fSFrançois Tigeot 
41e3adcf8fSFrançois Tigeot enum tv_margin {
42e3adcf8fSFrançois Tigeot 	TV_MARGIN_LEFT, TV_MARGIN_TOP,
43e3adcf8fSFrançois Tigeot 	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
44e3adcf8fSFrançois Tigeot };
45e3adcf8fSFrançois Tigeot 
46e3adcf8fSFrançois Tigeot /** Private structure for the integrated TV support */
47e3adcf8fSFrançois Tigeot struct intel_tv {
48e3adcf8fSFrançois Tigeot 	struct intel_encoder base;
49e3adcf8fSFrançois Tigeot 
50e3adcf8fSFrançois Tigeot 	int type;
51e3adcf8fSFrançois Tigeot };
52e3adcf8fSFrançois Tigeot 
53e3adcf8fSFrançois Tigeot struct video_levels {
541e12ee3bSFrançois Tigeot 	u16 blank, black;
551e12ee3bSFrançois Tigeot 	u8 burst;
56e3adcf8fSFrançois Tigeot };
57e3adcf8fSFrançois Tigeot 
58e3adcf8fSFrançois Tigeot struct color_conversion {
59e3adcf8fSFrançois Tigeot 	u16 ry, gy, by, ay;
60e3adcf8fSFrançois Tigeot 	u16 ru, gu, bu, au;
61e3adcf8fSFrançois Tigeot 	u16 rv, gv, bv, av;
62e3adcf8fSFrançois Tigeot };
63e3adcf8fSFrançois Tigeot 
64e3adcf8fSFrançois Tigeot static const u32 filter_table[] = {
65e3adcf8fSFrançois Tigeot 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
66e3adcf8fSFrançois Tigeot 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
67e3adcf8fSFrançois Tigeot 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
68e3adcf8fSFrançois Tigeot 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
69e3adcf8fSFrançois Tigeot 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
70e3adcf8fSFrançois Tigeot 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
71e3adcf8fSFrançois Tigeot 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
72e3adcf8fSFrançois Tigeot 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
73e3adcf8fSFrançois Tigeot 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
74e3adcf8fSFrançois Tigeot 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
75e3adcf8fSFrançois Tigeot 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
76e3adcf8fSFrançois Tigeot 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
77e3adcf8fSFrançois Tigeot 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
78e3adcf8fSFrançois Tigeot 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
79e3adcf8fSFrançois Tigeot 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
80e3adcf8fSFrançois Tigeot 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
81e3adcf8fSFrançois Tigeot 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
82e3adcf8fSFrançois Tigeot 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
83e3adcf8fSFrançois Tigeot 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
84e3adcf8fSFrançois Tigeot 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
85e3adcf8fSFrançois Tigeot 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
86e3adcf8fSFrançois Tigeot 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
87e3adcf8fSFrançois Tigeot 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
88e3adcf8fSFrançois Tigeot 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
89e3adcf8fSFrançois Tigeot 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
90e3adcf8fSFrançois Tigeot 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
91e3adcf8fSFrançois Tigeot 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
92e3adcf8fSFrançois Tigeot 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
93e3adcf8fSFrançois Tigeot 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
94e3adcf8fSFrançois Tigeot 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
95e3adcf8fSFrançois Tigeot 	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
96e3adcf8fSFrançois Tigeot 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
97e3adcf8fSFrançois Tigeot 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
98e3adcf8fSFrançois Tigeot 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
99e3adcf8fSFrançois Tigeot 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
100e3adcf8fSFrançois Tigeot 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
101e3adcf8fSFrançois Tigeot 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
102e3adcf8fSFrançois Tigeot 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
103e3adcf8fSFrançois Tigeot 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
104e3adcf8fSFrançois Tigeot 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
105e3adcf8fSFrançois Tigeot 	0x28003100, 0x28002F00, 0x00003100, 0x36403000,
106e3adcf8fSFrançois Tigeot 	0x2D002CC0, 0x30003640, 0x2D0036C0,
107e3adcf8fSFrançois Tigeot 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
108e3adcf8fSFrançois Tigeot 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
109e3adcf8fSFrançois Tigeot 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
110e3adcf8fSFrançois Tigeot 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
111e3adcf8fSFrançois Tigeot 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
112e3adcf8fSFrançois Tigeot 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
113e3adcf8fSFrançois Tigeot 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
114e3adcf8fSFrançois Tigeot 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
115e3adcf8fSFrançois Tigeot 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
116e3adcf8fSFrançois Tigeot 	0x28003100, 0x28002F00, 0x00003100,
117e3adcf8fSFrançois Tigeot };
118e3adcf8fSFrançois Tigeot 
119e3adcf8fSFrançois Tigeot /*
120e3adcf8fSFrançois Tigeot  * Color conversion values have 3 separate fixed point formats:
121e3adcf8fSFrançois Tigeot  *
122e3adcf8fSFrançois Tigeot  * 10 bit fields (ay, au)
123e3adcf8fSFrançois Tigeot  *   1.9 fixed point (b.bbbbbbbbb)
124e3adcf8fSFrançois Tigeot  * 11 bit fields (ry, by, ru, gu, gv)
125e3adcf8fSFrançois Tigeot  *   exp.mantissa (ee.mmmmmmmmm)
126e3adcf8fSFrançois Tigeot  *   ee = 00 = 10^-1 (0.mmmmmmmmm)
127e3adcf8fSFrançois Tigeot  *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
128e3adcf8fSFrançois Tigeot  *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
129e3adcf8fSFrançois Tigeot  *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
130e3adcf8fSFrançois Tigeot  * 12 bit fields (gy, rv, bu)
131e3adcf8fSFrançois Tigeot  *   exp.mantissa (eee.mmmmmmmmm)
132e3adcf8fSFrançois Tigeot  *   eee = 000 = 10^-1 (0.mmmmmmmmm)
133e3adcf8fSFrançois Tigeot  *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
134e3adcf8fSFrançois Tigeot  *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
135e3adcf8fSFrançois Tigeot  *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
136e3adcf8fSFrançois Tigeot  *   eee = 100 = reserved
137e3adcf8fSFrançois Tigeot  *   eee = 101 = reserved
138e3adcf8fSFrançois Tigeot  *   eee = 110 = reserved
139e3adcf8fSFrançois Tigeot  *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
140e3adcf8fSFrançois Tigeot  *
141e3adcf8fSFrançois Tigeot  * Saturation and contrast are 8 bits, with their own representation:
142e3adcf8fSFrançois Tigeot  * 8 bit field (saturation, contrast)
143e3adcf8fSFrançois Tigeot  *   exp.mantissa (ee.mmmmmm)
144e3adcf8fSFrançois Tigeot  *   ee = 00 = 10^-1 (0.mmmmmm)
145e3adcf8fSFrançois Tigeot  *   ee = 01 = 10^0 (m.mmmmm)
146e3adcf8fSFrançois Tigeot  *   ee = 10 = 10^1 (mm.mmmm)
147e3adcf8fSFrançois Tigeot  *   ee = 11 = 10^2 (mmm.mmm)
148e3adcf8fSFrançois Tigeot  *
149e3adcf8fSFrançois Tigeot  * Simple conversion function:
150e3adcf8fSFrançois Tigeot  *
151e3adcf8fSFrançois Tigeot  * static u32
152e3adcf8fSFrançois Tigeot  * float_to_csc_11(float f)
153e3adcf8fSFrançois Tigeot  * {
154e3adcf8fSFrançois Tigeot  *     u32 exp;
155e3adcf8fSFrançois Tigeot  *     u32 mant;
156e3adcf8fSFrançois Tigeot  *     u32 ret;
157e3adcf8fSFrançois Tigeot  *
158e3adcf8fSFrançois Tigeot  *     if (f < 0)
159e3adcf8fSFrançois Tigeot  *         f = -f;
160e3adcf8fSFrançois Tigeot  *
161e3adcf8fSFrançois Tigeot  *     if (f >= 1) {
162e3adcf8fSFrançois Tigeot  *         exp = 0x7;
163e3adcf8fSFrançois Tigeot  *	   mant = 1 << 8;
164e3adcf8fSFrançois Tigeot  *     } else {
165e3adcf8fSFrançois Tigeot  *         for (exp = 0; exp < 3 && f < 0.5; exp++)
166e3adcf8fSFrançois Tigeot  *	   f *= 2.0;
167e3adcf8fSFrançois Tigeot  *         mant = (f * (1 << 9) + 0.5);
168e3adcf8fSFrançois Tigeot  *         if (mant >= (1 << 9))
169e3adcf8fSFrançois Tigeot  *             mant = (1 << 9) - 1;
170e3adcf8fSFrançois Tigeot  *     }
171e3adcf8fSFrançois Tigeot  *     ret = (exp << 9) | mant;
172e3adcf8fSFrançois Tigeot  *     return ret;
173e3adcf8fSFrançois Tigeot  * }
174e3adcf8fSFrançois Tigeot  */
175e3adcf8fSFrançois Tigeot 
176e3adcf8fSFrançois Tigeot /*
177e3adcf8fSFrançois Tigeot  * Behold, magic numbers!  If we plant them they might grow a big
178e3adcf8fSFrançois Tigeot  * s-video cable to the sky... or something.
179e3adcf8fSFrançois Tigeot  *
180e3adcf8fSFrançois Tigeot  * Pre-converted to appropriate hex value.
181e3adcf8fSFrançois Tigeot  */
182e3adcf8fSFrançois Tigeot 
183e3adcf8fSFrançois Tigeot /*
184e3adcf8fSFrançois Tigeot  * PAL & NTSC values for composite & s-video connections
185e3adcf8fSFrançois Tigeot  */
186e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_composite = {
187e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
188e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
189e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
190e3adcf8fSFrançois Tigeot };
191e3adcf8fSFrançois Tigeot 
192e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_composite = {
193e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 113,
194e3adcf8fSFrançois Tigeot };
195e3adcf8fSFrançois Tigeot 
196e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_svideo = {
197e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
198e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
199e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
200e3adcf8fSFrançois Tigeot };
201e3adcf8fSFrançois Tigeot 
202e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_svideo = {
203e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 133,
204e3adcf8fSFrançois Tigeot };
205e3adcf8fSFrançois Tigeot 
206e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_composite = {
207e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
208e3adcf8fSFrançois Tigeot 	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
209e3adcf8fSFrançois Tigeot 	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
210e3adcf8fSFrançois Tigeot };
211e3adcf8fSFrançois Tigeot 
212e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_composite = {
213e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 225, .burst = 113,
214e3adcf8fSFrançois Tigeot };
215e3adcf8fSFrançois Tigeot 
216e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_svideo = {
217e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
218e3adcf8fSFrançois Tigeot 	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
219e3adcf8fSFrançois Tigeot 	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
220e3adcf8fSFrançois Tigeot };
221e3adcf8fSFrançois Tigeot 
222e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_svideo = {
223e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 266, .burst = 133,
224e3adcf8fSFrançois Tigeot };
225e3adcf8fSFrançois Tigeot 
226e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_composite = {
227e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
228e3adcf8fSFrançois Tigeot 	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
229e3adcf8fSFrançois Tigeot 	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
230e3adcf8fSFrançois Tigeot };
231e3adcf8fSFrançois Tigeot 
232e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_composite = {
233e3adcf8fSFrançois Tigeot 	.blank = 237, .black = 237, .burst = 118,
234e3adcf8fSFrançois Tigeot };
235e3adcf8fSFrançois Tigeot 
236e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_svideo = {
237e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
238e3adcf8fSFrançois Tigeot 	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
239e3adcf8fSFrançois Tigeot 	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
240e3adcf8fSFrançois Tigeot };
241e3adcf8fSFrançois Tigeot 
242e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_svideo = {
243e3adcf8fSFrançois Tigeot 	.blank = 280, .black = 280, .burst = 139,
244e3adcf8fSFrançois Tigeot };
245e3adcf8fSFrançois Tigeot 
246e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_composite = {
247e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
248e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
249e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
250e3adcf8fSFrançois Tigeot };
251e3adcf8fSFrançois Tigeot 
252e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_composite = {
253e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 113,
254e3adcf8fSFrançois Tigeot };
255e3adcf8fSFrançois Tigeot 
256e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_svideo = {
257e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
258e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
259e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
260e3adcf8fSFrançois Tigeot };
261e3adcf8fSFrançois Tigeot 
262e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_svideo = {
263e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 133,
264e3adcf8fSFrançois Tigeot };
265e3adcf8fSFrançois Tigeot 
266e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_composite = {
267e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
268e3adcf8fSFrançois Tigeot 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
269e3adcf8fSFrançois Tigeot 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
270e3adcf8fSFrançois Tigeot };
271e3adcf8fSFrançois Tigeot 
272e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_composite = {
273e3adcf8fSFrançois Tigeot 	.blank = 225, .black = 267, .burst = 118,
274e3adcf8fSFrançois Tigeot };
275e3adcf8fSFrançois Tigeot 
276e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_svideo = {
277e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
278e3adcf8fSFrançois Tigeot 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
279e3adcf8fSFrançois Tigeot 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
280e3adcf8fSFrançois Tigeot };
281e3adcf8fSFrançois Tigeot 
282e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_svideo = {
283e3adcf8fSFrançois Tigeot 	.blank = 266, .black = 316, .burst = 139,
284e3adcf8fSFrançois Tigeot };
285e3adcf8fSFrançois Tigeot 
286e3adcf8fSFrançois Tigeot /*
287e3adcf8fSFrançois Tigeot  * Component connections
288e3adcf8fSFrançois Tigeot  */
289e3adcf8fSFrançois Tigeot static const struct color_conversion sdtv_csc_yprpb = {
290e3adcf8fSFrançois Tigeot 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
291e3adcf8fSFrançois Tigeot 	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
292e3adcf8fSFrançois Tigeot 	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
293e3adcf8fSFrançois Tigeot };
294e3adcf8fSFrançois Tigeot 
295e3adcf8fSFrançois Tigeot static const struct color_conversion hdtv_csc_yprpb = {
296e3adcf8fSFrançois Tigeot 	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
297e3adcf8fSFrançois Tigeot 	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
298e3adcf8fSFrançois Tigeot 	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
299e3adcf8fSFrançois Tigeot };
300e3adcf8fSFrançois Tigeot 
301e3adcf8fSFrançois Tigeot static const struct video_levels component_levels = {
302e3adcf8fSFrançois Tigeot 	.blank = 279, .black = 279, .burst = 0,
303e3adcf8fSFrançois Tigeot };
304e3adcf8fSFrançois Tigeot 
305e3adcf8fSFrançois Tigeot 
306e3adcf8fSFrançois Tigeot struct tv_mode {
307e3adcf8fSFrançois Tigeot 	const char *name;
3081e12ee3bSFrançois Tigeot 
3091e12ee3bSFrançois Tigeot 	u32 clock;
3101e12ee3bSFrançois Tigeot 	u16 refresh; /* in millihertz (for precision) */
311e3adcf8fSFrançois Tigeot 	u32 oversample;
3121e12ee3bSFrançois Tigeot 	u8 hsync_end;
3131e12ee3bSFrançois Tigeot 	u16 hblank_start, hblank_end, htotal;
3141e12ee3bSFrançois Tigeot 	bool progressive : 1, trilevel_sync : 1, component_only : 1;
3151e12ee3bSFrançois Tigeot 	u8 vsync_start_f1, vsync_start_f2, vsync_len;
3161e12ee3bSFrançois Tigeot 	bool veq_ena : 1;
3171e12ee3bSFrançois Tigeot 	u8 veq_start_f1, veq_start_f2, veq_len;
3181e12ee3bSFrançois Tigeot 	u8 vi_end_f1, vi_end_f2;
3191e12ee3bSFrançois Tigeot 	u16 nbr_end;
3201e12ee3bSFrançois Tigeot 	bool burst_ena : 1;
3211e12ee3bSFrançois Tigeot 	u8 hburst_start, hburst_len;
3221e12ee3bSFrançois Tigeot 	u8 vburst_start_f1;
3231e12ee3bSFrançois Tigeot 	u16 vburst_end_f1;
3241e12ee3bSFrançois Tigeot 	u8 vburst_start_f2;
3251e12ee3bSFrançois Tigeot 	u16 vburst_end_f2;
3261e12ee3bSFrançois Tigeot 	u8 vburst_start_f3;
3271e12ee3bSFrançois Tigeot 	u16 vburst_end_f3;
3281e12ee3bSFrançois Tigeot 	u8 vburst_start_f4;
3291e12ee3bSFrançois Tigeot 	u16 vburst_end_f4;
330e3adcf8fSFrançois Tigeot 	/*
331e3adcf8fSFrançois Tigeot 	 * subcarrier programming
332e3adcf8fSFrançois Tigeot 	 */
3331e12ee3bSFrançois Tigeot 	u16 dda2_size, dda3_size;
3341e12ee3bSFrançois Tigeot 	u8 dda1_inc;
3351e12ee3bSFrançois Tigeot 	u16 dda2_inc, dda3_inc;
336e3adcf8fSFrançois Tigeot 	u32 sc_reset;
3371e12ee3bSFrançois Tigeot 	bool pal_burst : 1;
338e3adcf8fSFrançois Tigeot 	/*
339e3adcf8fSFrançois Tigeot 	 * blank/black levels
340e3adcf8fSFrançois Tigeot 	 */
341e3adcf8fSFrançois Tigeot 	const struct video_levels *composite_levels, *svideo_levels;
342e3adcf8fSFrançois Tigeot 	const struct color_conversion *composite_color, *svideo_color;
343e3adcf8fSFrançois Tigeot 	const u32 *filter_table;
3441e12ee3bSFrançois Tigeot 	u16 max_srcw;
345e3adcf8fSFrançois Tigeot };
346e3adcf8fSFrançois Tigeot 
347e3adcf8fSFrançois Tigeot 
348e3adcf8fSFrançois Tigeot /*
349e3adcf8fSFrançois Tigeot  * Sub carrier DDA
350e3adcf8fSFrançois Tigeot  *
351e3adcf8fSFrançois Tigeot  *  I think this works as follows:
352e3adcf8fSFrançois Tigeot  *
353e3adcf8fSFrançois Tigeot  *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
354e3adcf8fSFrançois Tigeot  *
355e3adcf8fSFrançois Tigeot  * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
356e3adcf8fSFrançois Tigeot  *
357e3adcf8fSFrançois Tigeot  * So,
358e3adcf8fSFrançois Tigeot  *  dda1_ideal = subcarrier/pixel * 4096
359e3adcf8fSFrançois Tigeot  *  dda1_inc = floor (dda1_ideal)
360e3adcf8fSFrançois Tigeot  *  dda2 = dda1_ideal - dda1_inc
361e3adcf8fSFrançois Tigeot  *
362e3adcf8fSFrançois Tigeot  *  then pick a ratio for dda2 that gives the closest approximation. If
363e3adcf8fSFrançois Tigeot  *  you can't get close enough, you can play with dda3 as well. This
364e3adcf8fSFrançois Tigeot  *  seems likely to happen when dda2 is small as the jumps would be larger
365e3adcf8fSFrançois Tigeot  *
366e3adcf8fSFrançois Tigeot  * To invert this,
367e3adcf8fSFrançois Tigeot  *
368e3adcf8fSFrançois Tigeot  *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
369e3adcf8fSFrançois Tigeot  *
370e3adcf8fSFrançois Tigeot  * The constants below were all computed using a 107.520MHz clock
371e3adcf8fSFrançois Tigeot  */
372e3adcf8fSFrançois Tigeot 
373e3adcf8fSFrançois Tigeot /**
374e3adcf8fSFrançois Tigeot  * Register programming values for TV modes.
375e3adcf8fSFrançois Tigeot  *
376e3adcf8fSFrançois Tigeot  * These values account for -1s required.
377e3adcf8fSFrançois Tigeot  */
378e3adcf8fSFrançois Tigeot 
379e3adcf8fSFrançois Tigeot static const struct tv_mode tv_modes[] = {
380e3adcf8fSFrançois Tigeot 	{
381e3adcf8fSFrançois Tigeot 		.name		= "NTSC-M",
382e3adcf8fSFrançois Tigeot 		.clock		= 108000,
383e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
384e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
385e3adcf8fSFrançois Tigeot 		.component_only = 0,
386e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
387e3adcf8fSFrançois Tigeot 
388e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
389e3adcf8fSFrançois Tigeot 		.hblank_start	= 836,		    .htotal		= 857,
390e3adcf8fSFrançois Tigeot 
391e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
392e3adcf8fSFrançois Tigeot 
393e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
394e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
395e3adcf8fSFrançois Tigeot 
396e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
397e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
398e3adcf8fSFrançois Tigeot 
399e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
400e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
401e3adcf8fSFrançois Tigeot 
402e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
403e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
404e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
405e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
406e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
407e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
408e3adcf8fSFrançois Tigeot 
409e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
410e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
411e3adcf8fSFrançois Tigeot 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
412e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
413e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_4,
414e3adcf8fSFrançois Tigeot 		.pal_burst	= false,
415e3adcf8fSFrançois Tigeot 
416e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_m_levels_composite,
417e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_m_csc_composite,
418e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_m_levels_svideo,
419e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_m_csc_svideo,
420e3adcf8fSFrançois Tigeot 
421e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
422e3adcf8fSFrançois Tigeot 	},
423e3adcf8fSFrançois Tigeot 	{
424e3adcf8fSFrançois Tigeot 		.name		= "NTSC-443",
425e3adcf8fSFrançois Tigeot 		.clock		= 108000,
426e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
427e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
428e3adcf8fSFrançois Tigeot 		.component_only = 0,
429e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
430e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
431e3adcf8fSFrançois Tigeot 		.hblank_start	= 836,		    .htotal		= 857,
432e3adcf8fSFrançois Tigeot 
433e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
434e3adcf8fSFrançois Tigeot 
435e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
436e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
437e3adcf8fSFrançois Tigeot 
438e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
439e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
440e3adcf8fSFrançois Tigeot 
441e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
442e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
443e3adcf8fSFrançois Tigeot 
444e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
445e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
446e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
447e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
448e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
449e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
450e3adcf8fSFrançois Tigeot 
451e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
452e3adcf8fSFrançois Tigeot 		.dda1_inc       =    168,
453e3adcf8fSFrançois Tigeot 		.dda2_inc       =   4093,       .dda2_size      =  27456,
454e3adcf8fSFrançois Tigeot 		.dda3_inc       =    310,       .dda3_size      =    525,
455e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_NEVER,
456e3adcf8fSFrançois Tigeot 		.pal_burst  = false,
457e3adcf8fSFrançois Tigeot 
458e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_m_levels_composite,
459e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_m_csc_composite,
460e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_m_levels_svideo,
461e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_m_csc_svideo,
462e3adcf8fSFrançois Tigeot 
463e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
464e3adcf8fSFrançois Tigeot 	},
465e3adcf8fSFrançois Tigeot 	{
466e3adcf8fSFrançois Tigeot 		.name		= "NTSC-J",
467e3adcf8fSFrançois Tigeot 		.clock		= 108000,
468e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
469e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
470e3adcf8fSFrançois Tigeot 		.component_only = 0,
471e3adcf8fSFrançois Tigeot 
472e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
473e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 124,
474e3adcf8fSFrançois Tigeot 		.hblank_start = 836,	    .htotal		= 857,
475e3adcf8fSFrançois Tigeot 
476e3adcf8fSFrançois Tigeot 		.progressive	= false,    .trilevel_sync = false,
477e3adcf8fSFrançois Tigeot 
478e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
479e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
480e3adcf8fSFrançois Tigeot 
481e3adcf8fSFrançois Tigeot 		.veq_ena      = true,	    .veq_start_f1	= 0,
482e3adcf8fSFrançois Tigeot 		.veq_start_f2 = 1,	    .veq_len		= 18,
483e3adcf8fSFrançois Tigeot 
484e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
485e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
486e3adcf8fSFrançois Tigeot 
487e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
488e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
489e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
490e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
491e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
492e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
493e3adcf8fSFrançois Tigeot 
494e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
495e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
496e3adcf8fSFrançois Tigeot 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
497e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
498e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_4,
499e3adcf8fSFrançois Tigeot 		.pal_burst	= false,
500e3adcf8fSFrançois Tigeot 
501e3adcf8fSFrançois Tigeot 		.composite_levels = &ntsc_j_levels_composite,
502e3adcf8fSFrançois Tigeot 		.composite_color = &ntsc_j_csc_composite,
503e3adcf8fSFrançois Tigeot 		.svideo_levels  = &ntsc_j_levels_svideo,
504e3adcf8fSFrançois Tigeot 		.svideo_color = &ntsc_j_csc_svideo,
505e3adcf8fSFrançois Tigeot 
506e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
507e3adcf8fSFrançois Tigeot 	},
508e3adcf8fSFrançois Tigeot 	{
509e3adcf8fSFrançois Tigeot 		.name		= "PAL-M",
510e3adcf8fSFrançois Tigeot 		.clock		= 108000,
511e3adcf8fSFrançois Tigeot 		.refresh	= 59940,
512e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
513e3adcf8fSFrançois Tigeot 		.component_only = 0,
514e3adcf8fSFrançois Tigeot 
515e3adcf8fSFrançois Tigeot 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
516e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		  .hblank_end		= 124,
517e3adcf8fSFrançois Tigeot 		.hblank_start = 836,	  .htotal		= 857,
518e3adcf8fSFrançois Tigeot 
519e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = false,
520e3adcf8fSFrançois Tigeot 
521e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
522e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
523e3adcf8fSFrançois Tigeot 
524e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
525e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
526e3adcf8fSFrançois Tigeot 
527e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
528e3adcf8fSFrançois Tigeot 		.nbr_end	= 240,
529e3adcf8fSFrançois Tigeot 
530e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
531e3adcf8fSFrançois Tigeot 		.hburst_start	= 72,		    .hburst_len		= 34,
532e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
533e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
534e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
535e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
536e3adcf8fSFrançois Tigeot 
537e3adcf8fSFrançois Tigeot 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
538e3adcf8fSFrançois Tigeot 		.dda1_inc	=    135,
539e3adcf8fSFrançois Tigeot 		.dda2_inc	=  16704,	    .dda2_size		=  27456,
540e3adcf8fSFrançois Tigeot 		.dda3_inc	=      0,	    .dda3_size		=      0,
541e3adcf8fSFrançois Tigeot 		.sc_reset	= TV_SC_RESET_EVERY_8,
542e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
543e3adcf8fSFrançois Tigeot 
544e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_m_levels_composite,
545e3adcf8fSFrançois Tigeot 		.composite_color = &pal_m_csc_composite,
546e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_m_levels_svideo,
547e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_m_csc_svideo,
548e3adcf8fSFrançois Tigeot 
549e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
550e3adcf8fSFrançois Tigeot 	},
551e3adcf8fSFrançois Tigeot 	{
552e3adcf8fSFrançois Tigeot 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
553e3adcf8fSFrançois Tigeot 		.name	    = "PAL-N",
554e3adcf8fSFrançois Tigeot 		.clock		= 108000,
555e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
556e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
557e3adcf8fSFrançois Tigeot 		.component_only = 0,
558e3adcf8fSFrançois Tigeot 
559e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 128,
560e3adcf8fSFrançois Tigeot 		.hblank_start = 844,	    .htotal		= 863,
561e3adcf8fSFrançois Tigeot 
562e3adcf8fSFrançois Tigeot 		.progressive  = false,    .trilevel_sync = false,
563e3adcf8fSFrançois Tigeot 
564e3adcf8fSFrançois Tigeot 
565e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
566e3adcf8fSFrançois Tigeot 		.vsync_len	= 6,
567e3adcf8fSFrançois Tigeot 
568e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 0,
569e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,		    .veq_len		= 18,
570e3adcf8fSFrançois Tigeot 
571e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
572e3adcf8fSFrançois Tigeot 		.nbr_end	= 286,
573e3adcf8fSFrançois Tigeot 
574e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
575e3adcf8fSFrançois Tigeot 		.hburst_start = 73,	    .hburst_len		= 34,
576e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
577e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
578e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
579e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
580e3adcf8fSFrançois Tigeot 
581e3adcf8fSFrançois Tigeot 
582e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
583e3adcf8fSFrançois Tigeot 		.dda1_inc       =    135,
584e3adcf8fSFrançois Tigeot 		.dda2_inc       =  23578,       .dda2_size      =  27648,
585e3adcf8fSFrançois Tigeot 		.dda3_inc       =    134,       .dda3_size      =    625,
586e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_EVERY_8,
587e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
588e3adcf8fSFrançois Tigeot 
589e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_n_levels_composite,
590e3adcf8fSFrançois Tigeot 		.composite_color = &pal_n_csc_composite,
591e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_n_levels_svideo,
592e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_n_csc_svideo,
593e3adcf8fSFrançois Tigeot 
594e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
595e3adcf8fSFrançois Tigeot 	},
596e3adcf8fSFrançois Tigeot 	{
597e3adcf8fSFrançois Tigeot 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
598e3adcf8fSFrançois Tigeot 		.name	    = "PAL",
599e3adcf8fSFrançois Tigeot 		.clock		= 108000,
600e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
601e3adcf8fSFrançois Tigeot 		.oversample	= TV_OVERSAMPLE_8X,
602e3adcf8fSFrançois Tigeot 		.component_only = 0,
603e3adcf8fSFrançois Tigeot 
604e3adcf8fSFrançois Tigeot 		.hsync_end	= 64,		    .hblank_end		= 142,
605e3adcf8fSFrançois Tigeot 		.hblank_start	= 844,	    .htotal		= 863,
606e3adcf8fSFrançois Tigeot 
607e3adcf8fSFrançois Tigeot 		.progressive	= false,    .trilevel_sync = false,
608e3adcf8fSFrançois Tigeot 
609e3adcf8fSFrançois Tigeot 		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
610e3adcf8fSFrançois Tigeot 		.vsync_len	= 5,
611e3adcf8fSFrançois Tigeot 
612e3adcf8fSFrançois Tigeot 		.veq_ena	= true,	    .veq_start_f1	= 0,
613e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 1,	    .veq_len		= 15,
614e3adcf8fSFrançois Tigeot 
615e3adcf8fSFrançois Tigeot 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
616e3adcf8fSFrançois Tigeot 		.nbr_end	= 286,
617e3adcf8fSFrançois Tigeot 
618e3adcf8fSFrançois Tigeot 		.burst_ena	= true,
619e3adcf8fSFrançois Tigeot 		.hburst_start	= 73,		    .hburst_len		= 32,
620e3adcf8fSFrançois Tigeot 		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
621e3adcf8fSFrançois Tigeot 		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
622e3adcf8fSFrançois Tigeot 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
623e3adcf8fSFrançois Tigeot 		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
624e3adcf8fSFrançois Tigeot 
625e3adcf8fSFrançois Tigeot 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
626e3adcf8fSFrançois Tigeot 		.dda1_inc       =    168,
627e3adcf8fSFrançois Tigeot 		.dda2_inc       =   4122,       .dda2_size      =  27648,
628e3adcf8fSFrançois Tigeot 		.dda3_inc       =     67,       .dda3_size      =    625,
629e3adcf8fSFrançois Tigeot 		.sc_reset   = TV_SC_RESET_EVERY_8,
630e3adcf8fSFrançois Tigeot 		.pal_burst  = true,
631e3adcf8fSFrançois Tigeot 
632e3adcf8fSFrançois Tigeot 		.composite_levels = &pal_levels_composite,
633e3adcf8fSFrançois Tigeot 		.composite_color = &pal_csc_composite,
634e3adcf8fSFrançois Tigeot 		.svideo_levels  = &pal_levels_svideo,
635e3adcf8fSFrançois Tigeot 		.svideo_color = &pal_csc_svideo,
636e3adcf8fSFrançois Tigeot 
637e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
638e3adcf8fSFrançois Tigeot 	},
639e3adcf8fSFrançois Tigeot 	{
64019df918dSFrançois Tigeot 		.name       = "480p",
64119df918dSFrançois Tigeot 		.clock		= 107520,
64219df918dSFrançois Tigeot 		.refresh	= 59940,
64319df918dSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_4X,
64419df918dSFrançois Tigeot 		.component_only = 1,
64519df918dSFrançois Tigeot 
64619df918dSFrançois Tigeot 		.hsync_end      = 64,               .hblank_end         = 122,
64719df918dSFrançois Tigeot 		.hblank_start   = 842,              .htotal             = 857,
64819df918dSFrançois Tigeot 
64919df918dSFrançois Tigeot 		.progressive    = true,		    .trilevel_sync = false,
65019df918dSFrançois Tigeot 
65119df918dSFrançois Tigeot 		.vsync_start_f1 = 12,               .vsync_start_f2     = 12,
65219df918dSFrançois Tigeot 		.vsync_len      = 12,
65319df918dSFrançois Tigeot 
65419df918dSFrançois Tigeot 		.veq_ena        = false,
65519df918dSFrançois Tigeot 
65619df918dSFrançois Tigeot 		.vi_end_f1      = 44,               .vi_end_f2          = 44,
65719df918dSFrançois Tigeot 		.nbr_end        = 479,
65819df918dSFrançois Tigeot 
65919df918dSFrançois Tigeot 		.burst_ena      = false,
66019df918dSFrançois Tigeot 
66119df918dSFrançois Tigeot 		.filter_table = filter_table,
66219df918dSFrançois Tigeot 	},
66319df918dSFrançois Tigeot 	{
66419df918dSFrançois Tigeot 		.name       = "576p",
66519df918dSFrançois Tigeot 		.clock		= 107520,
66619df918dSFrançois Tigeot 		.refresh	= 50000,
66719df918dSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_4X,
66819df918dSFrançois Tigeot 		.component_only = 1,
66919df918dSFrançois Tigeot 
67019df918dSFrançois Tigeot 		.hsync_end      = 64,               .hblank_end         = 139,
67119df918dSFrançois Tigeot 		.hblank_start   = 859,              .htotal             = 863,
67219df918dSFrançois Tigeot 
67319df918dSFrançois Tigeot 		.progressive    = true,		    .trilevel_sync = false,
67419df918dSFrançois Tigeot 
67519df918dSFrançois Tigeot 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
67619df918dSFrançois Tigeot 		.vsync_len      = 10,
67719df918dSFrançois Tigeot 
67819df918dSFrançois Tigeot 		.veq_ena        = false,
67919df918dSFrançois Tigeot 
68019df918dSFrançois Tigeot 		.vi_end_f1      = 48,               .vi_end_f2          = 48,
68119df918dSFrançois Tigeot 		.nbr_end        = 575,
68219df918dSFrançois Tigeot 
68319df918dSFrançois Tigeot 		.burst_ena      = false,
68419df918dSFrançois Tigeot 
68519df918dSFrançois Tigeot 		.filter_table = filter_table,
68619df918dSFrançois Tigeot 	},
68719df918dSFrançois Tigeot 	{
688e3adcf8fSFrançois Tigeot 		.name       = "720p@60Hz",
689e3adcf8fSFrançois Tigeot 		.clock		= 148800,
690e3adcf8fSFrançois Tigeot 		.refresh	= 60000,
691e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
692e3adcf8fSFrançois Tigeot 		.component_only = 1,
693e3adcf8fSFrançois Tigeot 
694e3adcf8fSFrançois Tigeot 		.hsync_end      = 80,               .hblank_end         = 300,
695e3adcf8fSFrançois Tigeot 		.hblank_start   = 1580,             .htotal             = 1649,
696e3adcf8fSFrançois Tigeot 
697e3adcf8fSFrançois Tigeot 		.progressive	= true,		    .trilevel_sync = true,
698e3adcf8fSFrançois Tigeot 
699e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
700e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
701e3adcf8fSFrançois Tigeot 
702e3adcf8fSFrançois Tigeot 		.veq_ena        = false,
703e3adcf8fSFrançois Tigeot 
704e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
705e3adcf8fSFrançois Tigeot 		.nbr_end        = 719,
706e3adcf8fSFrançois Tigeot 
707e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
708e3adcf8fSFrançois Tigeot 
709e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
710e3adcf8fSFrançois Tigeot 	},
711e3adcf8fSFrançois Tigeot 	{
712e3adcf8fSFrançois Tigeot 		.name       = "720p@50Hz",
713e3adcf8fSFrançois Tigeot 		.clock		= 148800,
714e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
715e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
716e3adcf8fSFrançois Tigeot 		.component_only = 1,
717e3adcf8fSFrançois Tigeot 
718e3adcf8fSFrançois Tigeot 		.hsync_end      = 80,               .hblank_end         = 300,
719e3adcf8fSFrançois Tigeot 		.hblank_start   = 1580,             .htotal             = 1979,
720e3adcf8fSFrançois Tigeot 
721e3adcf8fSFrançois Tigeot 		.progressive	= true,		    .trilevel_sync = true,
722e3adcf8fSFrançois Tigeot 
723e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
724e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
725e3adcf8fSFrançois Tigeot 
726e3adcf8fSFrançois Tigeot 		.veq_ena        = false,
727e3adcf8fSFrançois Tigeot 
728e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
729e3adcf8fSFrançois Tigeot 		.nbr_end        = 719,
730e3adcf8fSFrançois Tigeot 
731e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
732e3adcf8fSFrançois Tigeot 
733e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
734e3adcf8fSFrançois Tigeot 		.max_srcw = 800
735e3adcf8fSFrançois Tigeot 	},
736e3adcf8fSFrançois Tigeot 	{
737e3adcf8fSFrançois Tigeot 		.name       = "1080i@50Hz",
738e3adcf8fSFrançois Tigeot 		.clock		= 148800,
739e3adcf8fSFrançois Tigeot 		.refresh	= 50000,
740e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
741e3adcf8fSFrançois Tigeot 		.component_only = 1,
742e3adcf8fSFrançois Tigeot 
743e3adcf8fSFrançois Tigeot 		.hsync_end      = 88,               .hblank_end         = 235,
744e3adcf8fSFrançois Tigeot 		.hblank_start   = 2155,             .htotal             = 2639,
745e3adcf8fSFrançois Tigeot 
746e3adcf8fSFrançois Tigeot 		.progressive	= false,	  .trilevel_sync = true,
747e3adcf8fSFrançois Tigeot 
748e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 4,              .vsync_start_f2     = 5,
749e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
750e3adcf8fSFrançois Tigeot 
751e3adcf8fSFrançois Tigeot 		.veq_ena	= true,	    .veq_start_f1	= 4,
752e3adcf8fSFrançois Tigeot 		.veq_start_f2   = 4,	    .veq_len		= 10,
753e3adcf8fSFrançois Tigeot 
754e3adcf8fSFrançois Tigeot 
755e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 21,           .vi_end_f2          = 22,
756e3adcf8fSFrançois Tigeot 		.nbr_end        = 539,
757e3adcf8fSFrançois Tigeot 
758e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
759e3adcf8fSFrançois Tigeot 
760e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
761e3adcf8fSFrançois Tigeot 	},
762e3adcf8fSFrançois Tigeot 	{
763e3adcf8fSFrançois Tigeot 		.name       = "1080i@60Hz",
764e3adcf8fSFrançois Tigeot 		.clock		= 148800,
765e3adcf8fSFrançois Tigeot 		.refresh	= 60000,
766e3adcf8fSFrançois Tigeot 		.oversample     = TV_OVERSAMPLE_2X,
767e3adcf8fSFrançois Tigeot 		.component_only = 1,
768e3adcf8fSFrançois Tigeot 
769e3adcf8fSFrançois Tigeot 		.hsync_end      = 88,               .hblank_end         = 235,
770e3adcf8fSFrançois Tigeot 		.hblank_start   = 2155,             .htotal             = 2199,
771e3adcf8fSFrançois Tigeot 
772e3adcf8fSFrançois Tigeot 		.progressive	= false,	    .trilevel_sync = true,
773e3adcf8fSFrançois Tigeot 
774e3adcf8fSFrançois Tigeot 		.vsync_start_f1 = 4,               .vsync_start_f2     = 5,
775e3adcf8fSFrançois Tigeot 		.vsync_len      = 10,
776e3adcf8fSFrançois Tigeot 
777e3adcf8fSFrançois Tigeot 		.veq_ena	= true,		    .veq_start_f1	= 4,
778e3adcf8fSFrançois Tigeot 		.veq_start_f2	= 4,		    .veq_len		= 10,
779e3adcf8fSFrançois Tigeot 
780e3adcf8fSFrançois Tigeot 
781e3adcf8fSFrançois Tigeot 		.vi_end_f1      = 21,               .vi_end_f2          = 22,
782e3adcf8fSFrançois Tigeot 		.nbr_end        = 539,
783e3adcf8fSFrançois Tigeot 
784e3adcf8fSFrançois Tigeot 		.burst_ena      = false,
785e3adcf8fSFrançois Tigeot 
786e3adcf8fSFrançois Tigeot 		.filter_table = filter_table,
787e3adcf8fSFrançois Tigeot 	},
788e3adcf8fSFrançois Tigeot };
789e3adcf8fSFrançois Tigeot 
enc_to_tv(struct intel_encoder * encoder)7909edbd4a0SFrançois Tigeot static struct intel_tv *enc_to_tv(struct intel_encoder *encoder)
791e3adcf8fSFrançois Tigeot {
7929edbd4a0SFrançois Tigeot 	return container_of(encoder, struct intel_tv, base);
793e3adcf8fSFrançois Tigeot }
794e3adcf8fSFrançois Tigeot 
intel_attached_tv(struct drm_connector * connector)795e3adcf8fSFrançois Tigeot static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
796e3adcf8fSFrançois Tigeot {
7979edbd4a0SFrançois Tigeot 	return enc_to_tv(intel_attached_encoder(connector));
798e3adcf8fSFrançois Tigeot }
799e3adcf8fSFrançois Tigeot 
80019df918dSFrançois Tigeot static bool
intel_tv_get_hw_state(struct intel_encoder * encoder,enum i915_pipe * pipe)80119df918dSFrançois Tigeot intel_tv_get_hw_state(struct intel_encoder *encoder, enum i915_pipe *pipe)
802e3adcf8fSFrançois Tigeot {
80319df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
804bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
80519df918dSFrançois Tigeot 	u32 tmp = I915_READ(TV_CTL);
80619df918dSFrançois Tigeot 
80719df918dSFrançois Tigeot 	if (!(tmp & TV_ENC_ENABLE))
80819df918dSFrançois Tigeot 		return false;
80919df918dSFrançois Tigeot 
81019df918dSFrançois Tigeot 	*pipe = PORT_TO_PIPE(tmp);
81119df918dSFrançois Tigeot 
81219df918dSFrançois Tigeot 	return true;
81319df918dSFrançois Tigeot }
81419df918dSFrançois Tigeot 
81519df918dSFrançois Tigeot static void
intel_enable_tv(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)8161e12ee3bSFrançois Tigeot intel_enable_tv(struct intel_encoder *encoder,
817*3f2dd94aSFrançois Tigeot 		const struct intel_crtc_state *pipe_config,
818*3f2dd94aSFrançois Tigeot 		const struct drm_connector_state *conn_state)
81919df918dSFrançois Tigeot {
82019df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
821bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
822e3adcf8fSFrançois Tigeot 
82324edb884SFrançois Tigeot 	/* Prevents vblank waits from timing out in intel_tv_detect_type() */
8244be47400SFrançois Tigeot 	intel_wait_for_vblank(dev_priv,
82524edb884SFrançois Tigeot 			      to_intel_crtc(encoder->base.crtc)->pipe);
82624edb884SFrançois Tigeot 
827e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
828e3adcf8fSFrançois Tigeot }
82919df918dSFrançois Tigeot 
83019df918dSFrançois Tigeot static void
intel_disable_tv(struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)8311e12ee3bSFrançois Tigeot intel_disable_tv(struct intel_encoder *encoder,
832*3f2dd94aSFrançois Tigeot 		 const struct intel_crtc_state *old_crtc_state,
833*3f2dd94aSFrançois Tigeot 		 const struct drm_connector_state *old_conn_state)
83419df918dSFrançois Tigeot {
83519df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
836bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
83719df918dSFrançois Tigeot 
83819df918dSFrançois Tigeot 	I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
839e3adcf8fSFrançois Tigeot }
840e3adcf8fSFrançois Tigeot 
intel_tv_mode_find(const struct drm_connector_state * conn_state)841*3f2dd94aSFrançois Tigeot static const struct tv_mode *intel_tv_mode_find(const struct drm_connector_state *conn_state)
842e3adcf8fSFrançois Tigeot {
843*3f2dd94aSFrançois Tigeot 	int format = conn_state->tv.mode;
844e3adcf8fSFrançois Tigeot 
845*3f2dd94aSFrançois Tigeot 	return &tv_modes[format];
846e3adcf8fSFrançois Tigeot }
847e3adcf8fSFrançois Tigeot 
848e3adcf8fSFrançois Tigeot static enum drm_mode_status
intel_tv_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)849e3adcf8fSFrançois Tigeot intel_tv_mode_valid(struct drm_connector *connector,
850e3adcf8fSFrançois Tigeot 		    struct drm_display_mode *mode)
851e3adcf8fSFrançois Tigeot {
852*3f2dd94aSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
853c0e85e96SFrançois Tigeot 	int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
854c0e85e96SFrançois Tigeot 
855c0e85e96SFrançois Tigeot 	if (mode->clock > max_dotclk)
856c0e85e96SFrançois Tigeot 		return MODE_CLOCK_HIGH;
857e3adcf8fSFrançois Tigeot 
858e3adcf8fSFrançois Tigeot 	/* Ensure TV refresh is close to desired refresh */
859e3adcf8fSFrançois Tigeot 	if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
860e3adcf8fSFrançois Tigeot 				< 1000)
861e3adcf8fSFrançois Tigeot 		return MODE_OK;
862e3adcf8fSFrançois Tigeot 
863e3adcf8fSFrançois Tigeot 	return MODE_CLOCK_RANGE;
864e3adcf8fSFrançois Tigeot }
865e3adcf8fSFrançois Tigeot 
866e3adcf8fSFrançois Tigeot 
8679edbd4a0SFrançois Tigeot static void
intel_tv_get_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config)8689edbd4a0SFrançois Tigeot intel_tv_get_config(struct intel_encoder *encoder,
8692c9916cdSFrançois Tigeot 		    struct intel_crtc_state *pipe_config)
8709edbd4a0SFrançois Tigeot {
8712c9916cdSFrançois Tigeot 	pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
8729edbd4a0SFrançois Tigeot }
8739edbd4a0SFrançois Tigeot 
874e3adcf8fSFrançois Tigeot static bool
intel_tv_compute_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config,struct drm_connector_state * conn_state)8758e26cdf6SFrançois Tigeot intel_tv_compute_config(struct intel_encoder *encoder,
8761e12ee3bSFrançois Tigeot 			struct intel_crtc_state *pipe_config,
8771e12ee3bSFrançois Tigeot 			struct drm_connector_state *conn_state)
878e3adcf8fSFrançois Tigeot {
879*3f2dd94aSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
880e3adcf8fSFrançois Tigeot 
881e3adcf8fSFrançois Tigeot 	if (!tv_mode)
882e3adcf8fSFrançois Tigeot 		return false;
883e3adcf8fSFrançois Tigeot 
8842c9916cdSFrançois Tigeot 	pipe_config->base.adjusted_mode.crtc_clock = tv_mode->clock;
8858e26cdf6SFrançois Tigeot 	DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
8868e26cdf6SFrançois Tigeot 	pipe_config->pipe_bpp = 8*3;
8878e26cdf6SFrançois Tigeot 
8889edbd4a0SFrançois Tigeot 	/* TV has it's own notion of sync and other mode flags, so clear them. */
8892c9916cdSFrançois Tigeot 	pipe_config->base.adjusted_mode.flags = 0;
8909edbd4a0SFrançois Tigeot 
8919edbd4a0SFrançois Tigeot 	/*
8929edbd4a0SFrançois Tigeot 	 * FIXME: We don't check whether the input mode is actually what we want
8939edbd4a0SFrançois Tigeot 	 * or whether userspace is doing something stupid.
8949edbd4a0SFrançois Tigeot 	 */
8959edbd4a0SFrançois Tigeot 
896e3adcf8fSFrançois Tigeot 	return true;
897e3adcf8fSFrançois Tigeot }
898e3adcf8fSFrançois Tigeot 
899ba55f2f5SFrançois Tigeot static void
set_tv_mode_timings(struct drm_i915_private * dev_priv,const struct tv_mode * tv_mode,bool burst_ena)900ba55f2f5SFrançois Tigeot set_tv_mode_timings(struct drm_i915_private *dev_priv,
901ba55f2f5SFrançois Tigeot 		    const struct tv_mode *tv_mode,
902ba55f2f5SFrançois Tigeot 		    bool burst_ena)
903e3adcf8fSFrançois Tigeot {
904e3adcf8fSFrançois Tigeot 	u32 hctl1, hctl2, hctl3;
905e3adcf8fSFrançois Tigeot 	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
906e3adcf8fSFrançois Tigeot 
907e3adcf8fSFrançois Tigeot 	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
908e3adcf8fSFrançois Tigeot 		(tv_mode->htotal << TV_HTOTAL_SHIFT);
909e3adcf8fSFrançois Tigeot 
910e3adcf8fSFrançois Tigeot 	hctl2 = (tv_mode->hburst_start << 16) |
911e3adcf8fSFrançois Tigeot 		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
912e3adcf8fSFrançois Tigeot 
913e3adcf8fSFrançois Tigeot 	if (burst_ena)
914e3adcf8fSFrançois Tigeot 		hctl2 |= TV_BURST_ENA;
915e3adcf8fSFrançois Tigeot 
916e3adcf8fSFrançois Tigeot 	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
917e3adcf8fSFrançois Tigeot 		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
918e3adcf8fSFrançois Tigeot 
919e3adcf8fSFrançois Tigeot 	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
920e3adcf8fSFrançois Tigeot 		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
921e3adcf8fSFrançois Tigeot 		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
922e3adcf8fSFrançois Tigeot 
923e3adcf8fSFrançois Tigeot 	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
924e3adcf8fSFrançois Tigeot 		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
925e3adcf8fSFrançois Tigeot 		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
926e3adcf8fSFrançois Tigeot 
927e3adcf8fSFrançois Tigeot 	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
928e3adcf8fSFrançois Tigeot 		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
929e3adcf8fSFrançois Tigeot 		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
930e3adcf8fSFrançois Tigeot 
931e3adcf8fSFrançois Tigeot 	if (tv_mode->veq_ena)
932e3adcf8fSFrançois Tigeot 		vctl3 |= TV_EQUAL_ENA;
933e3adcf8fSFrançois Tigeot 
934e3adcf8fSFrançois Tigeot 	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
935e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
936e3adcf8fSFrançois Tigeot 
937e3adcf8fSFrançois Tigeot 	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
938e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
939e3adcf8fSFrançois Tigeot 
940e3adcf8fSFrançois Tigeot 	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
941e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
942e3adcf8fSFrançois Tigeot 
943e3adcf8fSFrançois Tigeot 	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
944e3adcf8fSFrançois Tigeot 		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
945e3adcf8fSFrançois Tigeot 
946ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_H_CTL_1, hctl1);
947ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_H_CTL_2, hctl2);
948ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_H_CTL_3, hctl3);
949ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_1, vctl1);
950ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_2, vctl2);
951ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_3, vctl3);
952ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_4, vctl4);
953ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_5, vctl5);
954ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_6, vctl6);
955ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_V_CTL_7, vctl7);
956ba55f2f5SFrançois Tigeot }
957ba55f2f5SFrançois Tigeot 
set_color_conversion(struct drm_i915_private * dev_priv,const struct color_conversion * color_conversion)958ba55f2f5SFrançois Tigeot static void set_color_conversion(struct drm_i915_private *dev_priv,
959ba55f2f5SFrançois Tigeot 				 const struct color_conversion *color_conversion)
960ba55f2f5SFrançois Tigeot {
961ba55f2f5SFrançois Tigeot 	if (!color_conversion)
962ba55f2f5SFrançois Tigeot 		return;
963ba55f2f5SFrançois Tigeot 
964ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
965ba55f2f5SFrançois Tigeot 		   color_conversion->gy);
966ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
967ba55f2f5SFrançois Tigeot 		   color_conversion->ay);
968ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
969ba55f2f5SFrançois Tigeot 		   color_conversion->gu);
970ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
971ba55f2f5SFrançois Tigeot 		   color_conversion->au);
972ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
973ba55f2f5SFrançois Tigeot 		   color_conversion->gv);
974ba55f2f5SFrançois Tigeot 	I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
975ba55f2f5SFrançois Tigeot 		   color_conversion->av);
976ba55f2f5SFrançois Tigeot }
977ba55f2f5SFrançois Tigeot 
intel_tv_pre_enable(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)9781e12ee3bSFrançois Tigeot static void intel_tv_pre_enable(struct intel_encoder *encoder,
979*3f2dd94aSFrançois Tigeot 				const struct intel_crtc_state *pipe_config,
980*3f2dd94aSFrançois Tigeot 				const struct drm_connector_state *conn_state)
981ba55f2f5SFrançois Tigeot {
9824be47400SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
983ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
984ba55f2f5SFrançois Tigeot 	struct intel_tv *intel_tv = enc_to_tv(encoder);
985*3f2dd94aSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
986ba55f2f5SFrançois Tigeot 	u32 tv_ctl;
987ba55f2f5SFrançois Tigeot 	u32 scctl1, scctl2, scctl3;
988ba55f2f5SFrançois Tigeot 	int i, j;
989ba55f2f5SFrançois Tigeot 	const struct video_levels *video_levels;
990ba55f2f5SFrançois Tigeot 	const struct color_conversion *color_conversion;
991ba55f2f5SFrançois Tigeot 	bool burst_ena;
992ba55f2f5SFrançois Tigeot 	int xpos = 0x0, ypos = 0x0;
993ba55f2f5SFrançois Tigeot 	unsigned int xsize, ysize;
994ba55f2f5SFrançois Tigeot 
995ba55f2f5SFrançois Tigeot 	if (!tv_mode)
996ba55f2f5SFrançois Tigeot 		return;	/* can't happen (mode_prepare prevents this) */
997ba55f2f5SFrançois Tigeot 
998ba55f2f5SFrançois Tigeot 	tv_ctl = I915_READ(TV_CTL);
999ba55f2f5SFrançois Tigeot 	tv_ctl &= TV_CTL_SAVE;
1000ba55f2f5SFrançois Tigeot 
1001ba55f2f5SFrançois Tigeot 	switch (intel_tv->type) {
1002ba55f2f5SFrançois Tigeot 	default:
1003ba55f2f5SFrançois Tigeot 	case DRM_MODE_CONNECTOR_Unknown:
1004ba55f2f5SFrançois Tigeot 	case DRM_MODE_CONNECTOR_Composite:
1005ba55f2f5SFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
1006ba55f2f5SFrançois Tigeot 		video_levels = tv_mode->composite_levels;
1007ba55f2f5SFrançois Tigeot 		color_conversion = tv_mode->composite_color;
1008ba55f2f5SFrançois Tigeot 		burst_ena = tv_mode->burst_ena;
1009ba55f2f5SFrançois Tigeot 		break;
1010ba55f2f5SFrançois Tigeot 	case DRM_MODE_CONNECTOR_Component:
1011ba55f2f5SFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
1012ba55f2f5SFrançois Tigeot 		video_levels = &component_levels;
1013ba55f2f5SFrançois Tigeot 		if (tv_mode->burst_ena)
1014ba55f2f5SFrançois Tigeot 			color_conversion = &sdtv_csc_yprpb;
1015ba55f2f5SFrançois Tigeot 		else
1016ba55f2f5SFrançois Tigeot 			color_conversion = &hdtv_csc_yprpb;
1017ba55f2f5SFrançois Tigeot 		burst_ena = false;
1018ba55f2f5SFrançois Tigeot 		break;
1019ba55f2f5SFrançois Tigeot 	case DRM_MODE_CONNECTOR_SVIDEO:
1020ba55f2f5SFrançois Tigeot 		tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
1021ba55f2f5SFrançois Tigeot 		video_levels = tv_mode->svideo_levels;
1022ba55f2f5SFrançois Tigeot 		color_conversion = tv_mode->svideo_color;
1023ba55f2f5SFrançois Tigeot 		burst_ena = tv_mode->burst_ena;
1024ba55f2f5SFrançois Tigeot 		break;
1025ba55f2f5SFrançois Tigeot 	}
1026ba55f2f5SFrançois Tigeot 
1027e3adcf8fSFrançois Tigeot 	if (intel_crtc->pipe == 1)
1028e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_PIPEB_SELECT;
1029e3adcf8fSFrançois Tigeot 	tv_ctl |= tv_mode->oversample;
1030e3adcf8fSFrançois Tigeot 
1031e3adcf8fSFrançois Tigeot 	if (tv_mode->progressive)
1032e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_PROGRESSIVE;
1033e3adcf8fSFrançois Tigeot 	if (tv_mode->trilevel_sync)
1034e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_TRILEVEL_SYNC;
1035e3adcf8fSFrançois Tigeot 	if (tv_mode->pal_burst)
1036e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_PAL_BURST;
1037e3adcf8fSFrançois Tigeot 
1038e3adcf8fSFrançois Tigeot 	scctl1 = 0;
1039e3adcf8fSFrançois Tigeot 	if (tv_mode->dda1_inc)
1040e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA1_EN;
1041e3adcf8fSFrançois Tigeot 	if (tv_mode->dda2_inc)
1042e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA2_EN;
1043e3adcf8fSFrançois Tigeot 	if (tv_mode->dda3_inc)
1044e3adcf8fSFrançois Tigeot 		scctl1 |= TV_SC_DDA3_EN;
1045e3adcf8fSFrançois Tigeot 	scctl1 |= tv_mode->sc_reset;
1046e3adcf8fSFrançois Tigeot 	if (video_levels)
1047e3adcf8fSFrançois Tigeot 		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
1048e3adcf8fSFrançois Tigeot 	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1049e3adcf8fSFrançois Tigeot 
1050e3adcf8fSFrançois Tigeot 	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1051e3adcf8fSFrançois Tigeot 		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1052e3adcf8fSFrançois Tigeot 
1053e3adcf8fSFrançois Tigeot 	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1054e3adcf8fSFrançois Tigeot 		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1055e3adcf8fSFrançois Tigeot 
1056e3adcf8fSFrançois Tigeot 	/* Enable two fixes for the chips that need them. */
10571e12ee3bSFrançois Tigeot 	if (IS_I915GM(dev_priv))
1058e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1059e3adcf8fSFrançois Tigeot 
1060ba55f2f5SFrançois Tigeot 	set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
1061ba55f2f5SFrançois Tigeot 
1062e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_1, scctl1);
1063e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_2, scctl2);
1064e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_SC_CTL_3, scctl3);
1065e3adcf8fSFrançois Tigeot 
1066ba55f2f5SFrançois Tigeot 	set_color_conversion(dev_priv, color_conversion);
1067e3adcf8fSFrançois Tigeot 
10684be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 4)
1069e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_KNOBS, 0x00404000);
1070e3adcf8fSFrançois Tigeot 	else
1071e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_KNOBS, 0x00606000);
1072e3adcf8fSFrançois Tigeot 
1073e3adcf8fSFrançois Tigeot 	if (video_levels)
1074e3adcf8fSFrançois Tigeot 		I915_WRITE(TV_CLR_LEVEL,
1075e3adcf8fSFrançois Tigeot 			   ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1076e3adcf8fSFrançois Tigeot 			    (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1077e3adcf8fSFrançois Tigeot 
1078ba55f2f5SFrançois Tigeot 	assert_pipe_disabled(dev_priv, intel_crtc->pipe);
1079e3adcf8fSFrançois Tigeot 
1080e3adcf8fSFrançois Tigeot 	/* Filter ctl must be set before TV_WIN_SIZE */
1081e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1082e3adcf8fSFrançois Tigeot 	xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1083e3adcf8fSFrançois Tigeot 	if (tv_mode->progressive)
1084e3adcf8fSFrançois Tigeot 		ysize = tv_mode->nbr_end + 1;
1085e3adcf8fSFrançois Tigeot 	else
1086e3adcf8fSFrançois Tigeot 		ysize = 2*tv_mode->nbr_end + 1;
1087e3adcf8fSFrançois Tigeot 
1088*3f2dd94aSFrançois Tigeot 	xpos += conn_state->tv.margins.left;
1089*3f2dd94aSFrançois Tigeot 	ypos += conn_state->tv.margins.top;
1090*3f2dd94aSFrançois Tigeot 	xsize -= (conn_state->tv.margins.left +
1091*3f2dd94aSFrançois Tigeot 		  conn_state->tv.margins.right);
1092*3f2dd94aSFrançois Tigeot 	ysize -= (conn_state->tv.margins.top +
1093*3f2dd94aSFrançois Tigeot 		  conn_state->tv.margins.bottom);
1094e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
1095e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
1096e3adcf8fSFrançois Tigeot 
1097e3adcf8fSFrançois Tigeot 	j = 0;
1098e3adcf8fSFrançois Tigeot 	for (i = 0; i < 60; i++)
1099352ff8bdSFrançois Tigeot 		I915_WRITE(TV_H_LUMA(i), tv_mode->filter_table[j++]);
1100e3adcf8fSFrançois Tigeot 	for (i = 0; i < 60; i++)
1101352ff8bdSFrançois Tigeot 		I915_WRITE(TV_H_CHROMA(i), tv_mode->filter_table[j++]);
1102e3adcf8fSFrançois Tigeot 	for (i = 0; i < 43; i++)
1103352ff8bdSFrançois Tigeot 		I915_WRITE(TV_V_LUMA(i), tv_mode->filter_table[j++]);
1104e3adcf8fSFrançois Tigeot 	for (i = 0; i < 43; i++)
1105352ff8bdSFrançois Tigeot 		I915_WRITE(TV_V_CHROMA(i), tv_mode->filter_table[j++]);
1106e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
1107e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, tv_ctl);
1108e3adcf8fSFrançois Tigeot }
1109e3adcf8fSFrançois Tigeot 
1110e3adcf8fSFrançois Tigeot static const struct drm_display_mode reported_modes[] = {
1111e3adcf8fSFrançois Tigeot 	{
1112e3adcf8fSFrançois Tigeot 		.name = "NTSC 480i",
1113e3adcf8fSFrançois Tigeot 		.clock = 107520,
1114e3adcf8fSFrançois Tigeot 		.hdisplay = 1280,
1115e3adcf8fSFrançois Tigeot 		.hsync_start = 1368,
1116e3adcf8fSFrançois Tigeot 		.hsync_end = 1496,
1117e3adcf8fSFrançois Tigeot 		.htotal = 1712,
1118e3adcf8fSFrançois Tigeot 
1119e3adcf8fSFrançois Tigeot 		.vdisplay = 1024,
1120e3adcf8fSFrançois Tigeot 		.vsync_start = 1027,
1121e3adcf8fSFrançois Tigeot 		.vsync_end = 1034,
1122e3adcf8fSFrançois Tigeot 		.vtotal = 1104,
1123e3adcf8fSFrançois Tigeot 		.type = DRM_MODE_TYPE_DRIVER,
1124e3adcf8fSFrançois Tigeot 	},
1125e3adcf8fSFrançois Tigeot };
1126e3adcf8fSFrançois Tigeot 
1127e3adcf8fSFrançois Tigeot /**
1128e3adcf8fSFrançois Tigeot  * Detects TV presence by checking for load.
1129e3adcf8fSFrançois Tigeot  *
1130e3adcf8fSFrançois Tigeot  * Requires that the current pipe's DPLL is active.
1131e3adcf8fSFrançois Tigeot 
1132e3adcf8fSFrançois Tigeot  * \return true if TV is connected.
1133e3adcf8fSFrançois Tigeot  * \return false if TV is disconnected.
1134e3adcf8fSFrançois Tigeot  */
1135e3adcf8fSFrançois Tigeot static int
intel_tv_detect_type(struct intel_tv * intel_tv,struct drm_connector * connector)1136e3adcf8fSFrançois Tigeot intel_tv_detect_type(struct intel_tv *intel_tv,
1137e3adcf8fSFrançois Tigeot 		      struct drm_connector *connector)
1138e3adcf8fSFrançois Tigeot {
1139c0e85e96SFrançois Tigeot 	struct drm_crtc *crtc = connector->state->crtc;
1140e3adcf8fSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1141c0e85e96SFrançois Tigeot 	struct drm_device *dev = connector->dev;
1142bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
1143e3adcf8fSFrançois Tigeot 	u32 tv_ctl, save_tv_ctl;
1144e3adcf8fSFrançois Tigeot 	u32 tv_dac, save_tv_dac;
1145e3adcf8fSFrançois Tigeot 	int type;
1146e3adcf8fSFrançois Tigeot 
1147e3adcf8fSFrançois Tigeot 	/* Disable TV interrupts around load detect or we'll recurse */
1148e3adcf8fSFrançois Tigeot 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
11495e269720SFrançois Tigeot 		spin_lock_irq(&dev_priv->irq_lock);
1150e3adcf8fSFrançois Tigeot 		i915_disable_pipestat(dev_priv, 0,
1151ba55f2f5SFrançois Tigeot 				      PIPE_HOTPLUG_INTERRUPT_STATUS |
1152ba55f2f5SFrançois Tigeot 				      PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
11535e269720SFrançois Tigeot 		spin_unlock_irq(&dev_priv->irq_lock);
1154e3adcf8fSFrançois Tigeot 	}
1155e3adcf8fSFrançois Tigeot 
1156e3adcf8fSFrançois Tigeot 	save_tv_dac = tv_dac = I915_READ(TV_DAC);
1157e3adcf8fSFrançois Tigeot 	save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
1158e3adcf8fSFrançois Tigeot 
1159e3adcf8fSFrançois Tigeot 	/* Poll for TV detection */
1160e3adcf8fSFrançois Tigeot 	tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
1161e3adcf8fSFrançois Tigeot 	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1162e3adcf8fSFrançois Tigeot 	if (intel_crtc->pipe == 1)
1163e3adcf8fSFrançois Tigeot 		tv_ctl |= TV_ENC_PIPEB_SELECT;
1164e3adcf8fSFrançois Tigeot 	else
1165e3adcf8fSFrançois Tigeot 		tv_ctl &= ~TV_ENC_PIPEB_SELECT;
1166e3adcf8fSFrançois Tigeot 
1167e3adcf8fSFrançois Tigeot 	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1168e3adcf8fSFrançois Tigeot 	tv_dac |= (TVDAC_STATE_CHG_EN |
1169e3adcf8fSFrançois Tigeot 		   TVDAC_A_SENSE_CTL |
1170e3adcf8fSFrançois Tigeot 		   TVDAC_B_SENSE_CTL |
1171e3adcf8fSFrançois Tigeot 		   TVDAC_C_SENSE_CTL |
1172e3adcf8fSFrançois Tigeot 		   DAC_CTL_OVERRIDE |
1173e3adcf8fSFrançois Tigeot 		   DAC_A_0_7_V |
1174e3adcf8fSFrançois Tigeot 		   DAC_B_0_7_V |
1175e3adcf8fSFrançois Tigeot 		   DAC_C_0_7_V);
1176e3adcf8fSFrançois Tigeot 
117719df918dSFrançois Tigeot 
117819df918dSFrançois Tigeot 	/*
117919df918dSFrançois Tigeot 	 * The TV sense state should be cleared to zero on cantiga platform. Otherwise
118019df918dSFrançois Tigeot 	 * the TV is misdetected. This is hardware requirement.
118119df918dSFrançois Tigeot 	 */
11821e12ee3bSFrançois Tigeot 	if (IS_GM45(dev_priv))
118319df918dSFrançois Tigeot 		tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
118419df918dSFrançois Tigeot 			    TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
118519df918dSFrançois Tigeot 
1186e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, tv_ctl);
1187e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, tv_dac);
1188e3adcf8fSFrançois Tigeot 	POSTING_READ(TV_DAC);
1189e3adcf8fSFrançois Tigeot 
11904be47400SFrançois Tigeot 	intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
1191e3adcf8fSFrançois Tigeot 
1192e3adcf8fSFrançois Tigeot 	type = -1;
1193e3adcf8fSFrançois Tigeot 	tv_dac = I915_READ(TV_DAC);
1194e3adcf8fSFrançois Tigeot 	DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
1195e3adcf8fSFrançois Tigeot 	/*
1196e3adcf8fSFrançois Tigeot 	 *  A B C
1197e3adcf8fSFrançois Tigeot 	 *  0 1 1 Composite
1198e3adcf8fSFrançois Tigeot 	 *  1 0 X svideo
1199e3adcf8fSFrançois Tigeot 	 *  0 0 0 Component
1200e3adcf8fSFrançois Tigeot 	 */
1201e3adcf8fSFrançois Tigeot 	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1202e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected Composite TV connection\n");
1203e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_Composite;
1204e3adcf8fSFrançois Tigeot 	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1205e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected S-Video TV connection\n");
1206e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_SVIDEO;
1207e3adcf8fSFrançois Tigeot 	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1208e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Detected Component TV connection\n");
1209e3adcf8fSFrançois Tigeot 		type = DRM_MODE_CONNECTOR_Component;
1210e3adcf8fSFrançois Tigeot 	} else {
1211e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Unrecognised TV connection\n");
1212e3adcf8fSFrançois Tigeot 		type = -1;
1213e3adcf8fSFrançois Tigeot 	}
1214e3adcf8fSFrançois Tigeot 
1215e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1216e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_CTL, save_tv_ctl);
121719df918dSFrançois Tigeot 	POSTING_READ(TV_CTL);
121819df918dSFrançois Tigeot 
121919df918dSFrançois Tigeot 	/* For unknown reasons the hw barfs if we don't do this vblank wait. */
12204be47400SFrançois Tigeot 	intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
1221e3adcf8fSFrançois Tigeot 
1222e3adcf8fSFrançois Tigeot 	/* Restore interrupt config */
1223e3adcf8fSFrançois Tigeot 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
12245e269720SFrançois Tigeot 		spin_lock_irq(&dev_priv->irq_lock);
1225e3adcf8fSFrançois Tigeot 		i915_enable_pipestat(dev_priv, 0,
1226ba55f2f5SFrançois Tigeot 				     PIPE_HOTPLUG_INTERRUPT_STATUS |
1227ba55f2f5SFrançois Tigeot 				     PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
12285e269720SFrançois Tigeot 		spin_unlock_irq(&dev_priv->irq_lock);
1229e3adcf8fSFrançois Tigeot 	}
1230e3adcf8fSFrançois Tigeot 
1231e3adcf8fSFrançois Tigeot 	return type;
1232e3adcf8fSFrançois Tigeot }
1233e3adcf8fSFrançois Tigeot 
1234e3adcf8fSFrançois Tigeot /*
1235e3adcf8fSFrançois Tigeot  * Here we set accurate tv format according to connector type
1236e3adcf8fSFrançois Tigeot  * i.e Component TV should not be assigned by NTSC or PAL
1237e3adcf8fSFrançois Tigeot  */
intel_tv_find_better_format(struct drm_connector * connector)1238e3adcf8fSFrançois Tigeot static void intel_tv_find_better_format(struct drm_connector *connector)
1239e3adcf8fSFrançois Tigeot {
1240e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
1241*3f2dd94aSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1242e3adcf8fSFrançois Tigeot 	int i;
1243e3adcf8fSFrançois Tigeot 
1244e3adcf8fSFrançois Tigeot 	if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1245e3adcf8fSFrançois Tigeot 		tv_mode->component_only)
1246e3adcf8fSFrançois Tigeot 		return;
1247e3adcf8fSFrançois Tigeot 
1248e3adcf8fSFrançois Tigeot 
1249352ff8bdSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
1250e3adcf8fSFrançois Tigeot 		tv_mode = tv_modes + i;
1251e3adcf8fSFrançois Tigeot 
1252e3adcf8fSFrançois Tigeot 		if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1253e3adcf8fSFrançois Tigeot 			tv_mode->component_only)
1254e3adcf8fSFrançois Tigeot 			break;
1255e3adcf8fSFrançois Tigeot 	}
1256e3adcf8fSFrançois Tigeot 
1257*3f2dd94aSFrançois Tigeot 	connector->state->tv.mode = i;
1258e3adcf8fSFrançois Tigeot }
1259e3adcf8fSFrançois Tigeot 
1260e3adcf8fSFrançois Tigeot /**
1261e3adcf8fSFrançois Tigeot  * Detect the TV connection.
1262e3adcf8fSFrançois Tigeot  *
1263e3adcf8fSFrançois Tigeot  * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
1264e3adcf8fSFrançois Tigeot  * we have a pipe programmed in order to probe the TV.
1265e3adcf8fSFrançois Tigeot  */
1266a85cb24fSFrançois Tigeot static int
intel_tv_detect(struct drm_connector * connector,struct drm_modeset_acquire_ctx * ctx,bool force)1267a85cb24fSFrançois Tigeot intel_tv_detect(struct drm_connector *connector,
1268a85cb24fSFrançois Tigeot 		struct drm_modeset_acquire_ctx *ctx,
1269a85cb24fSFrançois Tigeot 		bool force)
1270e3adcf8fSFrançois Tigeot {
1271e3adcf8fSFrançois Tigeot 	struct drm_display_mode mode;
1272e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv = intel_attached_tv(connector);
127324edb884SFrançois Tigeot 	enum drm_connector_status status;
1274e3adcf8fSFrançois Tigeot 	int type;
1275e3adcf8fSFrançois Tigeot 
12769edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
1277b4efbf42Szrj 		      connector->base.id, connector->name,
12789edbd4a0SFrançois Tigeot 		      force);
12799edbd4a0SFrançois Tigeot 
1280e3adcf8fSFrançois Tigeot 	mode = reported_modes[0];
1281e3adcf8fSFrançois Tigeot 
128219df918dSFrançois Tigeot 	if (force) {
1283e3adcf8fSFrançois Tigeot 		struct intel_load_detect_pipe tmp;
1284a85cb24fSFrançois Tigeot 		int ret;
1285e3adcf8fSFrançois Tigeot 
1286a85cb24fSFrançois Tigeot 		ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx);
1287a85cb24fSFrançois Tigeot 		if (ret < 0)
1288a85cb24fSFrançois Tigeot 			return ret;
128924edb884SFrançois Tigeot 
1290a85cb24fSFrançois Tigeot 		if (ret > 0) {
1291e3adcf8fSFrançois Tigeot 			type = intel_tv_detect_type(intel_tv, connector);
1292a85cb24fSFrançois Tigeot 			intel_release_load_detect_pipe(connector, &tmp, ctx);
129324edb884SFrançois Tigeot 			status = type < 0 ?
129424edb884SFrançois Tigeot 				connector_status_disconnected :
129524edb884SFrançois Tigeot 				connector_status_connected;
1296e3adcf8fSFrançois Tigeot 		} else
129724edb884SFrançois Tigeot 			status = connector_status_unknown;
1298e3adcf8fSFrançois Tigeot 
1299*3f2dd94aSFrançois Tigeot 		if (status == connector_status_connected) {
1300e3adcf8fSFrançois Tigeot 			intel_tv->type = type;
1301e3adcf8fSFrançois Tigeot 			intel_tv_find_better_format(connector);
1302*3f2dd94aSFrançois Tigeot 		}
1303e3adcf8fSFrançois Tigeot 
1304*3f2dd94aSFrançois Tigeot 		return status;
1305*3f2dd94aSFrançois Tigeot 	} else
1306*3f2dd94aSFrançois Tigeot 		return connector->status;
1307e3adcf8fSFrançois Tigeot }
1308e3adcf8fSFrançois Tigeot 
1309e3adcf8fSFrançois Tigeot static const struct input_res {
1310e3adcf8fSFrançois Tigeot 	const char *name;
1311e3adcf8fSFrançois Tigeot 	int w, h;
1312e3adcf8fSFrançois Tigeot } input_res_table[] = {
1313e3adcf8fSFrançois Tigeot 	{"640x480", 640, 480},
1314e3adcf8fSFrançois Tigeot 	{"800x600", 800, 600},
1315e3adcf8fSFrançois Tigeot 	{"1024x768", 1024, 768},
1316e3adcf8fSFrançois Tigeot 	{"1280x1024", 1280, 1024},
1317e3adcf8fSFrançois Tigeot 	{"848x480", 848, 480},
1318e3adcf8fSFrançois Tigeot 	{"1280x720", 1280, 720},
1319e3adcf8fSFrançois Tigeot 	{"1920x1080", 1920, 1080},
1320e3adcf8fSFrançois Tigeot };
1321e3adcf8fSFrançois Tigeot 
1322e3adcf8fSFrançois Tigeot /*
1323e3adcf8fSFrançois Tigeot  * Chose preferred mode  according to line number of TV format
1324e3adcf8fSFrançois Tigeot  */
1325e3adcf8fSFrançois Tigeot static void
intel_tv_choose_preferred_modes(const struct tv_mode * tv_mode,struct drm_display_mode * mode_ptr)1326*3f2dd94aSFrançois Tigeot intel_tv_choose_preferred_modes(const struct tv_mode *tv_mode,
1327e3adcf8fSFrançois Tigeot 			       struct drm_display_mode *mode_ptr)
1328e3adcf8fSFrançois Tigeot {
1329e3adcf8fSFrançois Tigeot 	if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
1330e3adcf8fSFrançois Tigeot 		mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1331e3adcf8fSFrançois Tigeot 	else if (tv_mode->nbr_end > 480) {
1332e3adcf8fSFrançois Tigeot 		if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
1333e3adcf8fSFrançois Tigeot 			if (mode_ptr->vdisplay == 720)
1334e3adcf8fSFrançois Tigeot 				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1335e3adcf8fSFrançois Tigeot 		} else if (mode_ptr->vdisplay == 1080)
1336e3adcf8fSFrançois Tigeot 				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1337e3adcf8fSFrançois Tigeot 	}
1338e3adcf8fSFrançois Tigeot }
1339e3adcf8fSFrançois Tigeot 
1340e3adcf8fSFrançois Tigeot /**
1341e3adcf8fSFrançois Tigeot  * Stub get_modes function.
1342e3adcf8fSFrançois Tigeot  *
1343e3adcf8fSFrançois Tigeot  * This should probably return a set of fixed modes, unless we can figure out
1344e3adcf8fSFrançois Tigeot  * how to probe modes off of TV connections.
1345e3adcf8fSFrançois Tigeot  */
1346e3adcf8fSFrançois Tigeot 
1347e3adcf8fSFrançois Tigeot static int
intel_tv_get_modes(struct drm_connector * connector)1348e3adcf8fSFrançois Tigeot intel_tv_get_modes(struct drm_connector *connector)
1349e3adcf8fSFrançois Tigeot {
1350e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode_ptr;
1351*3f2dd94aSFrançois Tigeot 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1352e3adcf8fSFrançois Tigeot 	int j, count = 0;
1353e3adcf8fSFrançois Tigeot 	u64 tmp;
1354e3adcf8fSFrançois Tigeot 
135519df918dSFrançois Tigeot 	for (j = 0; j < ARRAY_SIZE(input_res_table);
1356e3adcf8fSFrançois Tigeot 	     j++) {
1357e3adcf8fSFrançois Tigeot 		const struct input_res *input = &input_res_table[j];
1358e3adcf8fSFrançois Tigeot 		unsigned int hactive_s = input->w;
1359e3adcf8fSFrançois Tigeot 		unsigned int vactive_s = input->h;
1360e3adcf8fSFrançois Tigeot 
1361e3adcf8fSFrançois Tigeot 		if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1362e3adcf8fSFrançois Tigeot 			continue;
1363e3adcf8fSFrançois Tigeot 
1364e3adcf8fSFrançois Tigeot 		if (input->w > 1024 && (!tv_mode->progressive
1365e3adcf8fSFrançois Tigeot 					&& !tv_mode->component_only))
1366e3adcf8fSFrançois Tigeot 			continue;
1367e3adcf8fSFrançois Tigeot 
1368e3adcf8fSFrançois Tigeot 		mode_ptr = drm_mode_create(connector->dev);
1369e3adcf8fSFrançois Tigeot 		if (!mode_ptr)
1370e3adcf8fSFrançois Tigeot 			continue;
1371e3adcf8fSFrançois Tigeot 		strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
1372c0e85e96SFrançois Tigeot 		mode_ptr->name[DRM_DISPLAY_MODE_LEN - 1] = '\0';
1373e3adcf8fSFrançois Tigeot 
1374e3adcf8fSFrançois Tigeot 		mode_ptr->hdisplay = hactive_s;
1375e3adcf8fSFrançois Tigeot 		mode_ptr->hsync_start = hactive_s + 1;
1376e3adcf8fSFrançois Tigeot 		mode_ptr->hsync_end = hactive_s + 64;
1377e3adcf8fSFrançois Tigeot 		if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
1378e3adcf8fSFrançois Tigeot 			mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
1379e3adcf8fSFrançois Tigeot 		mode_ptr->htotal = hactive_s + 96;
1380e3adcf8fSFrançois Tigeot 
1381e3adcf8fSFrançois Tigeot 		mode_ptr->vdisplay = vactive_s;
1382e3adcf8fSFrançois Tigeot 		mode_ptr->vsync_start = vactive_s + 1;
1383e3adcf8fSFrançois Tigeot 		mode_ptr->vsync_end = vactive_s + 32;
1384e3adcf8fSFrançois Tigeot 		if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
1385e3adcf8fSFrançois Tigeot 			mode_ptr->vsync_end = mode_ptr->vsync_start  + 1;
1386e3adcf8fSFrançois Tigeot 		mode_ptr->vtotal = vactive_s + 33;
1387e3adcf8fSFrançois Tigeot 
1388*3f2dd94aSFrançois Tigeot 		tmp = mul_u32_u32(tv_mode->refresh, mode_ptr->vtotal);
1389e3adcf8fSFrançois Tigeot 		tmp *= mode_ptr->htotal;
1390a2fdbec6SFrançois Tigeot 		tmp = div_u64(tmp, 1000000);
1391e3adcf8fSFrançois Tigeot 		mode_ptr->clock = (int) tmp;
1392e3adcf8fSFrançois Tigeot 
1393e3adcf8fSFrançois Tigeot 		mode_ptr->type = DRM_MODE_TYPE_DRIVER;
1394*3f2dd94aSFrançois Tigeot 		intel_tv_choose_preferred_modes(tv_mode, mode_ptr);
1395e3adcf8fSFrançois Tigeot 		drm_mode_probed_add(connector, mode_ptr);
1396e3adcf8fSFrançois Tigeot 		count++;
1397e3adcf8fSFrançois Tigeot 	}
1398e3adcf8fSFrançois Tigeot 
1399e3adcf8fSFrançois Tigeot 	return count;
1400e3adcf8fSFrançois Tigeot }
1401e3adcf8fSFrançois Tigeot 
1402e3adcf8fSFrançois Tigeot static void
intel_tv_destroy(struct drm_connector * connector)1403e3adcf8fSFrançois Tigeot intel_tv_destroy(struct drm_connector *connector)
1404e3adcf8fSFrançois Tigeot {
1405e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
1406158486a6SFrançois Tigeot 	kfree(connector);
1407e3adcf8fSFrançois Tigeot }
1408e3adcf8fSFrançois Tigeot 
1409e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_tv_connector_funcs = {
14101487f786SFrançois Tigeot 	.late_register = intel_connector_register,
14111487f786SFrançois Tigeot 	.early_unregister = intel_connector_unregister,
1412e3adcf8fSFrançois Tigeot 	.destroy = intel_tv_destroy,
1413e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
14142c9916cdSFrançois Tigeot 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
1415477eb7f9SFrançois Tigeot 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
1416e3adcf8fSFrançois Tigeot };
1417e3adcf8fSFrançois Tigeot 
intel_tv_atomic_check(struct drm_connector * connector,struct drm_connector_state * new_state)1418*3f2dd94aSFrançois Tigeot static int intel_tv_atomic_check(struct drm_connector *connector,
1419*3f2dd94aSFrançois Tigeot 				 struct drm_connector_state *new_state)
1420*3f2dd94aSFrançois Tigeot {
1421*3f2dd94aSFrançois Tigeot 	struct drm_crtc_state *new_crtc_state;
1422*3f2dd94aSFrançois Tigeot 	struct drm_connector_state *old_state;
1423*3f2dd94aSFrançois Tigeot 
1424*3f2dd94aSFrançois Tigeot 	if (!new_state->crtc)
1425*3f2dd94aSFrançois Tigeot 		return 0;
1426*3f2dd94aSFrançois Tigeot 
1427*3f2dd94aSFrançois Tigeot 	old_state = drm_atomic_get_old_connector_state(new_state->state, connector);
1428*3f2dd94aSFrançois Tigeot 	new_crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
1429*3f2dd94aSFrançois Tigeot 
1430*3f2dd94aSFrançois Tigeot 	if (old_state->tv.mode != new_state->tv.mode ||
1431*3f2dd94aSFrançois Tigeot 	    old_state->tv.margins.left != new_state->tv.margins.left ||
1432*3f2dd94aSFrançois Tigeot 	    old_state->tv.margins.right != new_state->tv.margins.right ||
1433*3f2dd94aSFrançois Tigeot 	    old_state->tv.margins.top != new_state->tv.margins.top ||
1434*3f2dd94aSFrançois Tigeot 	    old_state->tv.margins.bottom != new_state->tv.margins.bottom) {
1435*3f2dd94aSFrançois Tigeot 		/* Force a modeset. */
1436*3f2dd94aSFrançois Tigeot 
1437*3f2dd94aSFrançois Tigeot 		new_crtc_state->connectors_changed = true;
1438*3f2dd94aSFrançois Tigeot 	}
1439*3f2dd94aSFrançois Tigeot 
1440*3f2dd94aSFrançois Tigeot 	return 0;
1441*3f2dd94aSFrançois Tigeot }
1442*3f2dd94aSFrançois Tigeot 
1443e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1444a85cb24fSFrançois Tigeot 	.detect_ctx = intel_tv_detect,
1445e3adcf8fSFrançois Tigeot 	.mode_valid = intel_tv_mode_valid,
1446e3adcf8fSFrançois Tigeot 	.get_modes = intel_tv_get_modes,
1447*3f2dd94aSFrançois Tigeot 	.atomic_check = intel_tv_atomic_check,
1448e3adcf8fSFrançois Tigeot };
1449e3adcf8fSFrançois Tigeot 
1450e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_tv_enc_funcs = {
1451e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
1452e3adcf8fSFrançois Tigeot };
1453e3adcf8fSFrançois Tigeot 
1454e3adcf8fSFrançois Tigeot void
intel_tv_init(struct drm_i915_private * dev_priv)1455a85cb24fSFrançois Tigeot intel_tv_init(struct drm_i915_private *dev_priv)
1456e3adcf8fSFrançois Tigeot {
1457a85cb24fSFrançois Tigeot 	struct drm_device *dev = &dev_priv->drm;
1458e3adcf8fSFrançois Tigeot 	struct drm_connector *connector;
1459e3adcf8fSFrançois Tigeot 	struct intel_tv *intel_tv;
1460e3adcf8fSFrançois Tigeot 	struct intel_encoder *intel_encoder;
1461e3adcf8fSFrançois Tigeot 	struct intel_connector *intel_connector;
1462e3adcf8fSFrançois Tigeot 	u32 tv_dac_on, tv_dac_off, save_tv_dac;
1463352ff8bdSFrançois Tigeot 	const char *tv_format_names[ARRAY_SIZE(tv_modes)];
1464e3adcf8fSFrançois Tigeot 	int i, initial_mode = 0;
1465*3f2dd94aSFrançois Tigeot 	struct drm_connector_state *state;
1466e3adcf8fSFrançois Tigeot 
1467e3adcf8fSFrançois Tigeot 	if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1468e3adcf8fSFrançois Tigeot 		return;
1469e3adcf8fSFrançois Tigeot 
14708621f407SFrançois Tigeot 	if (!intel_bios_is_tv_present(dev_priv)) {
1471e3adcf8fSFrançois Tigeot 		DRM_DEBUG_KMS("Integrated TV is not present.\n");
1472e3adcf8fSFrançois Tigeot 		return;
1473e3adcf8fSFrançois Tigeot 	}
1474e3adcf8fSFrançois Tigeot 
1475e3adcf8fSFrançois Tigeot 	/*
1476e3adcf8fSFrançois Tigeot 	 * Sanity check the TV output by checking to see if the
1477e3adcf8fSFrançois Tigeot 	 * DAC register holds a value
1478e3adcf8fSFrançois Tigeot 	 */
1479e3adcf8fSFrançois Tigeot 	save_tv_dac = I915_READ(TV_DAC);
1480e3adcf8fSFrançois Tigeot 
1481e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1482e3adcf8fSFrançois Tigeot 	tv_dac_on = I915_READ(TV_DAC);
1483e3adcf8fSFrançois Tigeot 
1484e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1485e3adcf8fSFrançois Tigeot 	tv_dac_off = I915_READ(TV_DAC);
1486e3adcf8fSFrançois Tigeot 
1487e3adcf8fSFrançois Tigeot 	I915_WRITE(TV_DAC, save_tv_dac);
1488e3adcf8fSFrançois Tigeot 
1489e3adcf8fSFrançois Tigeot 	/*
1490e3adcf8fSFrançois Tigeot 	 * If the register does not hold the state change enable
1491e3adcf8fSFrançois Tigeot 	 * bit, (either as a 0 or a 1), assume it doesn't really
1492e3adcf8fSFrançois Tigeot 	 * exist
1493e3adcf8fSFrançois Tigeot 	 */
1494e3adcf8fSFrançois Tigeot 	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1495e3adcf8fSFrançois Tigeot 	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1496e3adcf8fSFrançois Tigeot 		return;
1497e3adcf8fSFrançois Tigeot 
14989edbd4a0SFrançois Tigeot 	intel_tv = kzalloc(sizeof(*intel_tv), GFP_KERNEL);
149919df918dSFrançois Tigeot 	if (!intel_tv) {
150019df918dSFrançois Tigeot 		return;
150119df918dSFrançois Tigeot 	}
150219df918dSFrançois Tigeot 
1503477eb7f9SFrançois Tigeot 	intel_connector = intel_connector_alloc();
150419df918dSFrançois Tigeot 	if (!intel_connector) {
1505158486a6SFrançois Tigeot 		kfree(intel_tv);
150619df918dSFrançois Tigeot 		return;
150719df918dSFrançois Tigeot 	}
1508e3adcf8fSFrançois Tigeot 
1509e3adcf8fSFrançois Tigeot 	intel_encoder = &intel_tv->base;
1510e3adcf8fSFrançois Tigeot 	connector = &intel_connector->base;
1511*3f2dd94aSFrançois Tigeot 	state = connector->state;
1512e3adcf8fSFrançois Tigeot 
1513e3adcf8fSFrançois Tigeot 	/* The documentation, for the older chipsets at least, recommend
1514e3adcf8fSFrançois Tigeot 	 * using a polling method rather than hotplug detection for TVs.
1515e3adcf8fSFrançois Tigeot 	 * This is because in order to perform the hotplug detection, the PLLs
1516e3adcf8fSFrançois Tigeot 	 * for the TV must be kept alive increasing power drain and starving
1517e3adcf8fSFrançois Tigeot 	 * bandwidth from other encoders. Notably for instance, it causes
1518e3adcf8fSFrançois Tigeot 	 * pipe underruns on Crestline when this encoder is supposedly idle.
1519e3adcf8fSFrançois Tigeot 	 *
1520e3adcf8fSFrançois Tigeot 	 * More recent chipsets favour HDMI rather than integrated S-Video.
1521e3adcf8fSFrançois Tigeot 	 */
15228e26cdf6SFrançois Tigeot 	intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1523e3adcf8fSFrançois Tigeot 
1524e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1525e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_SVIDEO);
1526e3adcf8fSFrançois Tigeot 
1527e3adcf8fSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
15281487f786SFrançois Tigeot 			 DRM_MODE_ENCODER_TVDAC, "TV");
1529e3adcf8fSFrançois Tigeot 
15308e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_tv_compute_config;
15319edbd4a0SFrançois Tigeot 	intel_encoder->get_config = intel_tv_get_config;
1532ba55f2f5SFrançois Tigeot 	intel_encoder->pre_enable = intel_tv_pre_enable;
153319df918dSFrançois Tigeot 	intel_encoder->enable = intel_enable_tv;
153419df918dSFrançois Tigeot 	intel_encoder->disable = intel_disable_tv;
153519df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_tv_get_hw_state;
153619df918dSFrançois Tigeot 	intel_connector->get_hw_state = intel_connector_get_hw_state;
153719df918dSFrançois Tigeot 
1538e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
15391e12ee3bSFrançois Tigeot 
1540e3adcf8fSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_TVOUT;
1541a85cb24fSFrançois Tigeot 	intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
15421e12ee3bSFrançois Tigeot 	intel_encoder->port = PORT_NONE;
1543e3adcf8fSFrançois Tigeot 	intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1544ba55f2f5SFrançois Tigeot 	intel_encoder->cloneable = 0;
1545e3adcf8fSFrançois Tigeot 	intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
1546e3adcf8fSFrançois Tigeot 	intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
1547e3adcf8fSFrançois Tigeot 
1548e3adcf8fSFrançois Tigeot 	/* BIOS margin values */
1549*3f2dd94aSFrançois Tigeot 	state->tv.margins.left = 54;
1550*3f2dd94aSFrançois Tigeot 	state->tv.margins.top = 36;
1551*3f2dd94aSFrançois Tigeot 	state->tv.margins.right = 46;
1552*3f2dd94aSFrançois Tigeot 	state->tv.margins.bottom = 37;
1553e3adcf8fSFrançois Tigeot 
1554*3f2dd94aSFrançois Tigeot 	state->tv.mode = initial_mode;
1555e3adcf8fSFrançois Tigeot 
1556e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1557e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = false;
1558e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = false;
1559e3adcf8fSFrançois Tigeot 
1560e3adcf8fSFrançois Tigeot 	/* Create TV properties then attach current values */
156119df918dSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
1562352ff8bdSFrançois Tigeot 		tv_format_names[i] = tv_modes[i].name;
1563e3adcf8fSFrançois Tigeot 	drm_mode_create_tv_properties(dev,
156419df918dSFrançois Tigeot 				      ARRAY_SIZE(tv_modes),
1565e3adcf8fSFrançois Tigeot 				      tv_format_names);
1566e3adcf8fSFrançois Tigeot 
1567b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
1568*3f2dd94aSFrançois Tigeot 				   state->tv.mode);
1569b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1570e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_left_margin_property,
1571*3f2dd94aSFrançois Tigeot 				   state->tv.margins.left);
1572b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1573e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_top_margin_property,
1574*3f2dd94aSFrançois Tigeot 				   state->tv.margins.top);
1575b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1576e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_right_margin_property,
1577*3f2dd94aSFrançois Tigeot 				   state->tv.margins.right);
1578b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1579e3adcf8fSFrançois Tigeot 				   dev->mode_config.tv_bottom_margin_property,
1580*3f2dd94aSFrançois Tigeot 				   state->tv.margins.bottom);
1581e3adcf8fSFrançois Tigeot }
1582