xref: /openbmc/linux/drivers/gpu/drm/i915/display/intel_tv.c (revision 96d3c5a7d20ec546e44695983fe0508c6f904248)
1379bc100SJani Nikula /*
2379bc100SJani Nikula  * Copyright © 2006-2008 Intel Corporation
3379bc100SJani Nikula  *   Jesse Barnes <jesse.barnes@intel.com>
4379bc100SJani Nikula  *
5379bc100SJani Nikula  * Permission is hereby granted, free of charge, to any person obtaining a
6379bc100SJani Nikula  * copy of this software and associated documentation files (the "Software"),
7379bc100SJani Nikula  * to deal in the Software without restriction, including without limitation
8379bc100SJani Nikula  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9379bc100SJani Nikula  * and/or sell copies of the Software, and to permit persons to whom the
10379bc100SJani Nikula  * Software is furnished to do so, subject to the following conditions:
11379bc100SJani Nikula  *
12379bc100SJani Nikula  * The above copyright notice and this permission notice (including the next
13379bc100SJani Nikula  * paragraph) shall be included in all copies or substantial portions of the
14379bc100SJani Nikula  * Software.
15379bc100SJani Nikula  *
16379bc100SJani Nikula  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17379bc100SJani Nikula  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18379bc100SJani Nikula  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19379bc100SJani Nikula  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20379bc100SJani Nikula  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21379bc100SJani Nikula  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22379bc100SJani Nikula  * DEALINGS IN THE SOFTWARE.
23379bc100SJani Nikula  *
24379bc100SJani Nikula  * Authors:
25379bc100SJani Nikula  *    Eric Anholt <eric@anholt.net>
26379bc100SJani Nikula  *
27379bc100SJani Nikula  */
28379bc100SJani Nikula 
29379bc100SJani Nikula /** @file
30379bc100SJani Nikula  * Integrated TV-out support for the 915GM and 945GM.
31379bc100SJani Nikula  */
32379bc100SJani Nikula 
33379bc100SJani Nikula #include <drm/drm_atomic_helper.h>
34379bc100SJani Nikula #include <drm/drm_crtc.h>
35379bc100SJani Nikula #include <drm/drm_edid.h>
36379bc100SJani Nikula 
37379bc100SJani Nikula #include "i915_drv.h"
38f84a27f9SJani Nikula #include "i915_reg.h"
39379bc100SJani Nikula #include "intel_connector.h"
40fd2b94a5SJani Nikula #include "intel_crtc.h"
417785ae0bSVille Syrjälä #include "intel_de.h"
422b874a02SJani Nikula #include "intel_display_irq.h"
431d455f8dSJani Nikula #include "intel_display_types.h"
441bba5543SVille Syrjälä #include "intel_dpll.h"
45379bc100SJani Nikula #include "intel_hotplug.h"
46b13604c0SJani Nikula #include "intel_load_detect.h"
47379bc100SJani Nikula #include "intel_tv.h"
48f84a27f9SJani Nikula #include "intel_tv_regs.h"
49379bc100SJani Nikula 
50379bc100SJani Nikula enum tv_margin {
51379bc100SJani Nikula 	TV_MARGIN_LEFT, TV_MARGIN_TOP,
52379bc100SJani Nikula 	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
53379bc100SJani Nikula };
54379bc100SJani Nikula 
55379bc100SJani Nikula struct intel_tv {
56379bc100SJani Nikula 	struct intel_encoder base;
57379bc100SJani Nikula 
58379bc100SJani Nikula 	int type;
59379bc100SJani Nikula };
60379bc100SJani Nikula 
61379bc100SJani Nikula struct video_levels {
62379bc100SJani Nikula 	u16 blank, black;
63379bc100SJani Nikula 	u8 burst;
64379bc100SJani Nikula };
65379bc100SJani Nikula 
66379bc100SJani Nikula struct color_conversion {
67379bc100SJani Nikula 	u16 ry, gy, by, ay;
68379bc100SJani Nikula 	u16 ru, gu, bu, au;
69379bc100SJani Nikula 	u16 rv, gv, bv, av;
70379bc100SJani Nikula };
71379bc100SJani Nikula 
72379bc100SJani Nikula static const u32 filter_table[] = {
73379bc100SJani Nikula 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
74379bc100SJani Nikula 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
75379bc100SJani Nikula 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
76379bc100SJani Nikula 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
77379bc100SJani Nikula 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
78379bc100SJani Nikula 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
79379bc100SJani Nikula 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
80379bc100SJani Nikula 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
81379bc100SJani Nikula 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
82379bc100SJani Nikula 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
83379bc100SJani Nikula 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
84379bc100SJani Nikula 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
85379bc100SJani Nikula 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
86379bc100SJani Nikula 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
87379bc100SJani Nikula 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
88379bc100SJani Nikula 	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
89379bc100SJani Nikula 	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
90379bc100SJani Nikula 	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
91379bc100SJani Nikula 	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
92379bc100SJani Nikula 	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
93379bc100SJani Nikula 	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
94379bc100SJani Nikula 	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
95379bc100SJani Nikula 	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
96379bc100SJani Nikula 	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
97379bc100SJani Nikula 	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
98379bc100SJani Nikula 	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
99379bc100SJani Nikula 	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
100379bc100SJani Nikula 	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
101379bc100SJani Nikula 	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
102379bc100SJani Nikula 	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
103379bc100SJani Nikula 	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
104379bc100SJani Nikula 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
105379bc100SJani Nikula 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
106379bc100SJani Nikula 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
107379bc100SJani Nikula 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
108379bc100SJani Nikula 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
109379bc100SJani Nikula 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
110379bc100SJani Nikula 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
111379bc100SJani Nikula 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
112379bc100SJani Nikula 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
113379bc100SJani Nikula 	0x28003100, 0x28002F00, 0x00003100, 0x36403000,
114379bc100SJani Nikula 	0x2D002CC0, 0x30003640, 0x2D0036C0,
115379bc100SJani Nikula 	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
116379bc100SJani Nikula 	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
117379bc100SJani Nikula 	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
118379bc100SJani Nikula 	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
119379bc100SJani Nikula 	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
120379bc100SJani Nikula 	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
121379bc100SJani Nikula 	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
122379bc100SJani Nikula 	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
123379bc100SJani Nikula 	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
124379bc100SJani Nikula 	0x28003100, 0x28002F00, 0x00003100,
125379bc100SJani Nikula };
126379bc100SJani Nikula 
127379bc100SJani Nikula /*
128379bc100SJani Nikula  * Color conversion values have 3 separate fixed point formats:
129379bc100SJani Nikula  *
130379bc100SJani Nikula  * 10 bit fields (ay, au)
131379bc100SJani Nikula  *   1.9 fixed point (b.bbbbbbbbb)
132379bc100SJani Nikula  * 11 bit fields (ry, by, ru, gu, gv)
133379bc100SJani Nikula  *   exp.mantissa (ee.mmmmmmmmm)
134379bc100SJani Nikula  *   ee = 00 = 10^-1 (0.mmmmmmmmm)
135379bc100SJani Nikula  *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
136379bc100SJani Nikula  *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
137379bc100SJani Nikula  *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
138379bc100SJani Nikula  * 12 bit fields (gy, rv, bu)
139379bc100SJani Nikula  *   exp.mantissa (eee.mmmmmmmmm)
140379bc100SJani Nikula  *   eee = 000 = 10^-1 (0.mmmmmmmmm)
141379bc100SJani Nikula  *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
142379bc100SJani Nikula  *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
143379bc100SJani Nikula  *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
144379bc100SJani Nikula  *   eee = 100 = reserved
145379bc100SJani Nikula  *   eee = 101 = reserved
146379bc100SJani Nikula  *   eee = 110 = reserved
147379bc100SJani Nikula  *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
148379bc100SJani Nikula  *
149379bc100SJani Nikula  * Saturation and contrast are 8 bits, with their own representation:
150379bc100SJani Nikula  * 8 bit field (saturation, contrast)
151379bc100SJani Nikula  *   exp.mantissa (ee.mmmmmm)
152379bc100SJani Nikula  *   ee = 00 = 10^-1 (0.mmmmmm)
153379bc100SJani Nikula  *   ee = 01 = 10^0 (m.mmmmm)
154379bc100SJani Nikula  *   ee = 10 = 10^1 (mm.mmmm)
155379bc100SJani Nikula  *   ee = 11 = 10^2 (mmm.mmm)
156379bc100SJani Nikula  *
157379bc100SJani Nikula  * Simple conversion function:
158379bc100SJani Nikula  *
159379bc100SJani Nikula  * static u32
160379bc100SJani Nikula  * float_to_csc_11(float f)
161379bc100SJani Nikula  * {
162379bc100SJani Nikula  *     u32 exp;
163379bc100SJani Nikula  *     u32 mant;
164379bc100SJani Nikula  *     u32 ret;
165379bc100SJani Nikula  *
166379bc100SJani Nikula  *     if (f < 0)
167379bc100SJani Nikula  *         f = -f;
168379bc100SJani Nikula  *
169379bc100SJani Nikula  *     if (f >= 1) {
170379bc100SJani Nikula  *         exp = 0x7;
171379bc100SJani Nikula  *	   mant = 1 << 8;
172379bc100SJani Nikula  *     } else {
173379bc100SJani Nikula  *         for (exp = 0; exp < 3 && f < 0.5; exp++)
174379bc100SJani Nikula  *	   f *= 2.0;
175379bc100SJani Nikula  *         mant = (f * (1 << 9) + 0.5);
176379bc100SJani Nikula  *         if (mant >= (1 << 9))
177379bc100SJani Nikula  *             mant = (1 << 9) - 1;
178379bc100SJani Nikula  *     }
179379bc100SJani Nikula  *     ret = (exp << 9) | mant;
180379bc100SJani Nikula  *     return ret;
181379bc100SJani Nikula  * }
182379bc100SJani Nikula  */
183379bc100SJani Nikula 
184379bc100SJani Nikula /*
185379bc100SJani Nikula  * Behold, magic numbers!  If we plant them they might grow a big
186379bc100SJani Nikula  * s-video cable to the sky... or something.
187379bc100SJani Nikula  *
188379bc100SJani Nikula  * Pre-converted to appropriate hex value.
189379bc100SJani Nikula  */
190379bc100SJani Nikula 
191379bc100SJani Nikula /*
192379bc100SJani Nikula  * PAL & NTSC values for composite & s-video connections
193379bc100SJani Nikula  */
194379bc100SJani Nikula static const struct color_conversion ntsc_m_csc_composite = {
195379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
196379bc100SJani Nikula 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
197379bc100SJani Nikula 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
198379bc100SJani Nikula };
199379bc100SJani Nikula 
200379bc100SJani Nikula static const struct video_levels ntsc_m_levels_composite = {
201379bc100SJani Nikula 	.blank = 225, .black = 267, .burst = 113,
202379bc100SJani Nikula };
203379bc100SJani Nikula 
204379bc100SJani Nikula static const struct color_conversion ntsc_m_csc_svideo = {
205379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
206379bc100SJani Nikula 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
207379bc100SJani Nikula 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
208379bc100SJani Nikula };
209379bc100SJani Nikula 
210379bc100SJani Nikula static const struct video_levels ntsc_m_levels_svideo = {
211379bc100SJani Nikula 	.blank = 266, .black = 316, .burst = 133,
212379bc100SJani Nikula };
213379bc100SJani Nikula 
214379bc100SJani Nikula static const struct color_conversion ntsc_j_csc_composite = {
215379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
216379bc100SJani Nikula 	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
217379bc100SJani Nikula 	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
218379bc100SJani Nikula };
219379bc100SJani Nikula 
220379bc100SJani Nikula static const struct video_levels ntsc_j_levels_composite = {
221379bc100SJani Nikula 	.blank = 225, .black = 225, .burst = 113,
222379bc100SJani Nikula };
223379bc100SJani Nikula 
224379bc100SJani Nikula static const struct color_conversion ntsc_j_csc_svideo = {
225379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
226379bc100SJani Nikula 	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
227379bc100SJani Nikula 	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
228379bc100SJani Nikula };
229379bc100SJani Nikula 
230379bc100SJani Nikula static const struct video_levels ntsc_j_levels_svideo = {
231379bc100SJani Nikula 	.blank = 266, .black = 266, .burst = 133,
232379bc100SJani Nikula };
233379bc100SJani Nikula 
234379bc100SJani Nikula static const struct color_conversion pal_csc_composite = {
235379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
236379bc100SJani Nikula 	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
237379bc100SJani Nikula 	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
238379bc100SJani Nikula };
239379bc100SJani Nikula 
240379bc100SJani Nikula static const struct video_levels pal_levels_composite = {
241379bc100SJani Nikula 	.blank = 237, .black = 237, .burst = 118,
242379bc100SJani Nikula };
243379bc100SJani Nikula 
244379bc100SJani Nikula static const struct color_conversion pal_csc_svideo = {
245379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
246379bc100SJani Nikula 	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
247379bc100SJani Nikula 	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
248379bc100SJani Nikula };
249379bc100SJani Nikula 
250379bc100SJani Nikula static const struct video_levels pal_levels_svideo = {
251379bc100SJani Nikula 	.blank = 280, .black = 280, .burst = 139,
252379bc100SJani Nikula };
253379bc100SJani Nikula 
254379bc100SJani Nikula static const struct color_conversion pal_m_csc_composite = {
255379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
256379bc100SJani Nikula 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
257379bc100SJani Nikula 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
258379bc100SJani Nikula };
259379bc100SJani Nikula 
260379bc100SJani Nikula static const struct video_levels pal_m_levels_composite = {
261379bc100SJani Nikula 	.blank = 225, .black = 267, .burst = 113,
262379bc100SJani Nikula };
263379bc100SJani Nikula 
264379bc100SJani Nikula static const struct color_conversion pal_m_csc_svideo = {
265379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
266379bc100SJani Nikula 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
267379bc100SJani Nikula 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
268379bc100SJani Nikula };
269379bc100SJani Nikula 
270379bc100SJani Nikula static const struct video_levels pal_m_levels_svideo = {
271379bc100SJani Nikula 	.blank = 266, .black = 316, .burst = 133,
272379bc100SJani Nikula };
273379bc100SJani Nikula 
274379bc100SJani Nikula static const struct color_conversion pal_n_csc_composite = {
275379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
276379bc100SJani Nikula 	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
277379bc100SJani Nikula 	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
278379bc100SJani Nikula };
279379bc100SJani Nikula 
280379bc100SJani Nikula static const struct video_levels pal_n_levels_composite = {
281379bc100SJani Nikula 	.blank = 225, .black = 267, .burst = 118,
282379bc100SJani Nikula };
283379bc100SJani Nikula 
284379bc100SJani Nikula static const struct color_conversion pal_n_csc_svideo = {
285379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
286379bc100SJani Nikula 	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
287379bc100SJani Nikula 	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
288379bc100SJani Nikula };
289379bc100SJani Nikula 
290379bc100SJani Nikula static const struct video_levels pal_n_levels_svideo = {
291379bc100SJani Nikula 	.blank = 266, .black = 316, .burst = 139,
292379bc100SJani Nikula };
293379bc100SJani Nikula 
294379bc100SJani Nikula /*
295379bc100SJani Nikula  * Component connections
296379bc100SJani Nikula  */
297379bc100SJani Nikula static const struct color_conversion sdtv_csc_yprpb = {
298379bc100SJani Nikula 	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
299379bc100SJani Nikula 	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
300379bc100SJani Nikula 	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
301379bc100SJani Nikula };
302379bc100SJani Nikula 
303379bc100SJani Nikula static const struct color_conversion hdtv_csc_yprpb = {
304379bc100SJani Nikula 	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
305379bc100SJani Nikula 	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
306379bc100SJani Nikula 	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
307379bc100SJani Nikula };
308379bc100SJani Nikula 
309379bc100SJani Nikula static const struct video_levels component_levels = {
310379bc100SJani Nikula 	.blank = 279, .black = 279, .burst = 0,
311379bc100SJani Nikula };
312379bc100SJani Nikula 
313379bc100SJani Nikula 
314379bc100SJani Nikula struct tv_mode {
315379bc100SJani Nikula 	const char *name;
316379bc100SJani Nikula 
317379bc100SJani Nikula 	u32 clock;
318379bc100SJani Nikula 	u16 refresh; /* in millihertz (for precision) */
319379bc100SJani Nikula 	u8 oversample;
320379bc100SJani Nikula 	u8 hsync_end;
321379bc100SJani Nikula 	u16 hblank_start, hblank_end, htotal;
322379bc100SJani Nikula 	bool progressive : 1, trilevel_sync : 1, component_only : 1;
323379bc100SJani Nikula 	u8 vsync_start_f1, vsync_start_f2, vsync_len;
324379bc100SJani Nikula 	bool veq_ena : 1;
325379bc100SJani Nikula 	u8 veq_start_f1, veq_start_f2, veq_len;
326379bc100SJani Nikula 	u8 vi_end_f1, vi_end_f2;
327379bc100SJani Nikula 	u16 nbr_end;
328379bc100SJani Nikula 	bool burst_ena : 1;
329379bc100SJani Nikula 	u8 hburst_start, hburst_len;
330379bc100SJani Nikula 	u8 vburst_start_f1;
331379bc100SJani Nikula 	u16 vburst_end_f1;
332379bc100SJani Nikula 	u8 vburst_start_f2;
333379bc100SJani Nikula 	u16 vburst_end_f2;
334379bc100SJani Nikula 	u8 vburst_start_f3;
335379bc100SJani Nikula 	u16 vburst_end_f3;
336379bc100SJani Nikula 	u8 vburst_start_f4;
337379bc100SJani Nikula 	u16 vburst_end_f4;
338379bc100SJani Nikula 	/*
339379bc100SJani Nikula 	 * subcarrier programming
340379bc100SJani Nikula 	 */
341379bc100SJani Nikula 	u16 dda2_size, dda3_size;
342379bc100SJani Nikula 	u8 dda1_inc;
343379bc100SJani Nikula 	u16 dda2_inc, dda3_inc;
344379bc100SJani Nikula 	u32 sc_reset;
345379bc100SJani Nikula 	bool pal_burst : 1;
346379bc100SJani Nikula 	/*
347379bc100SJani Nikula 	 * blank/black levels
348379bc100SJani Nikula 	 */
349379bc100SJani Nikula 	const struct video_levels *composite_levels, *svideo_levels;
350379bc100SJani Nikula 	const struct color_conversion *composite_color, *svideo_color;
351379bc100SJani Nikula 	const u32 *filter_table;
352379bc100SJani Nikula };
353379bc100SJani Nikula 
354379bc100SJani Nikula 
355379bc100SJani Nikula /*
356379bc100SJani Nikula  * Sub carrier DDA
357379bc100SJani Nikula  *
358379bc100SJani Nikula  *  I think this works as follows:
359379bc100SJani Nikula  *
360379bc100SJani Nikula  *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
361379bc100SJani Nikula  *
362379bc100SJani Nikula  * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
363379bc100SJani Nikula  *
364379bc100SJani Nikula  * So,
365379bc100SJani Nikula  *  dda1_ideal = subcarrier/pixel * 4096
366379bc100SJani Nikula  *  dda1_inc = floor (dda1_ideal)
367379bc100SJani Nikula  *  dda2 = dda1_ideal - dda1_inc
368379bc100SJani Nikula  *
369379bc100SJani Nikula  *  then pick a ratio for dda2 that gives the closest approximation. If
370379bc100SJani Nikula  *  you can't get close enough, you can play with dda3 as well. This
371379bc100SJani Nikula  *  seems likely to happen when dda2 is small as the jumps would be larger
372379bc100SJani Nikula  *
373379bc100SJani Nikula  * To invert this,
374379bc100SJani Nikula  *
375379bc100SJani Nikula  *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
376379bc100SJani Nikula  *
377379bc100SJani Nikula  * The constants below were all computed using a 107.520MHz clock
378379bc100SJani Nikula  */
379379bc100SJani Nikula 
380379bc100SJani Nikula /*
381379bc100SJani Nikula  * Register programming values for TV modes.
382379bc100SJani Nikula  *
383379bc100SJani Nikula  * These values account for -1s required.
384379bc100SJani Nikula  */
385379bc100SJani Nikula static const struct tv_mode tv_modes[] = {
386379bc100SJani Nikula 	{
387379bc100SJani Nikula 		.name		= "NTSC-M",
388379bc100SJani Nikula 		.clock		= 108000,
389379bc100SJani Nikula 		.refresh	= 59940,
390379bc100SJani Nikula 		.oversample	= 8,
391379bc100SJani Nikula 		.component_only = false,
392379bc100SJani Nikula 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
393379bc100SJani Nikula 
394379bc100SJani Nikula 		.hsync_end	= 64,		    .hblank_end		= 124,
395379bc100SJani Nikula 		.hblank_start	= 836,		    .htotal		= 857,
396379bc100SJani Nikula 
397379bc100SJani Nikula 		.progressive	= false,	    .trilevel_sync = false,
398379bc100SJani Nikula 
399379bc100SJani Nikula 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
400379bc100SJani Nikula 		.vsync_len	= 6,
401379bc100SJani Nikula 
402379bc100SJani Nikula 		.veq_ena	= true,		    .veq_start_f1	= 0,
403379bc100SJani Nikula 		.veq_start_f2	= 1,		    .veq_len		= 18,
404379bc100SJani Nikula 
405379bc100SJani Nikula 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
406379bc100SJani Nikula 		.nbr_end	= 240,
407379bc100SJani Nikula 
408379bc100SJani Nikula 		.burst_ena	= true,
409379bc100SJani Nikula 		.hburst_start	= 72,		    .hburst_len		= 34,
410379bc100SJani Nikula 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
411379bc100SJani Nikula 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
412379bc100SJani Nikula 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
413379bc100SJani Nikula 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
414379bc100SJani Nikula 
415379bc100SJani Nikula 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
416379bc100SJani Nikula 		.dda1_inc	=    135,
417379bc100SJani Nikula 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
418379bc100SJani Nikula 		.dda3_inc	=      0,	    .dda3_size		=      0,
419379bc100SJani Nikula 		.sc_reset	= TV_SC_RESET_EVERY_4,
420379bc100SJani Nikula 		.pal_burst	= false,
421379bc100SJani Nikula 
422379bc100SJani Nikula 		.composite_levels = &ntsc_m_levels_composite,
423379bc100SJani Nikula 		.composite_color = &ntsc_m_csc_composite,
424379bc100SJani Nikula 		.svideo_levels  = &ntsc_m_levels_svideo,
425379bc100SJani Nikula 		.svideo_color = &ntsc_m_csc_svideo,
426379bc100SJani Nikula 
427379bc100SJani Nikula 		.filter_table = filter_table,
428379bc100SJani Nikula 	},
429379bc100SJani Nikula 	{
430379bc100SJani Nikula 		.name		= "NTSC-443",
431379bc100SJani Nikula 		.clock		= 108000,
432379bc100SJani Nikula 		.refresh	= 59940,
433379bc100SJani Nikula 		.oversample	= 8,
434379bc100SJani Nikula 		.component_only = false,
435379bc100SJani Nikula 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
436379bc100SJani Nikula 		.hsync_end	= 64,		    .hblank_end		= 124,
437379bc100SJani Nikula 		.hblank_start	= 836,		    .htotal		= 857,
438379bc100SJani Nikula 
439379bc100SJani Nikula 		.progressive	= false,	    .trilevel_sync = false,
440379bc100SJani Nikula 
441379bc100SJani Nikula 		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
442379bc100SJani Nikula 		.vsync_len	= 6,
443379bc100SJani Nikula 
444379bc100SJani Nikula 		.veq_ena	= true,		    .veq_start_f1	= 0,
445379bc100SJani Nikula 		.veq_start_f2	= 1,		    .veq_len		= 18,
446379bc100SJani Nikula 
447379bc100SJani Nikula 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
448379bc100SJani Nikula 		.nbr_end	= 240,
449379bc100SJani Nikula 
450379bc100SJani Nikula 		.burst_ena	= true,
451379bc100SJani Nikula 		.hburst_start	= 72,		    .hburst_len		= 34,
452379bc100SJani Nikula 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
453379bc100SJani Nikula 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
454379bc100SJani Nikula 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
455379bc100SJani Nikula 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
456379bc100SJani Nikula 
457379bc100SJani Nikula 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
458379bc100SJani Nikula 		.dda1_inc       =    168,
459379bc100SJani Nikula 		.dda2_inc       =   4093,       .dda2_size      =  27456,
460379bc100SJani Nikula 		.dda3_inc       =    310,       .dda3_size      =    525,
461379bc100SJani Nikula 		.sc_reset   = TV_SC_RESET_NEVER,
462379bc100SJani Nikula 		.pal_burst  = false,
463379bc100SJani Nikula 
464379bc100SJani Nikula 		.composite_levels = &ntsc_m_levels_composite,
465379bc100SJani Nikula 		.composite_color = &ntsc_m_csc_composite,
466379bc100SJani Nikula 		.svideo_levels  = &ntsc_m_levels_svideo,
467379bc100SJani Nikula 		.svideo_color = &ntsc_m_csc_svideo,
468379bc100SJani Nikula 
469379bc100SJani Nikula 		.filter_table = filter_table,
470379bc100SJani Nikula 	},
471379bc100SJani Nikula 	{
472379bc100SJani Nikula 		.name		= "NTSC-J",
473379bc100SJani Nikula 		.clock		= 108000,
474379bc100SJani Nikula 		.refresh	= 59940,
475379bc100SJani Nikula 		.oversample	= 8,
476379bc100SJani Nikula 		.component_only = false,
477379bc100SJani Nikula 
478379bc100SJani Nikula 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
479379bc100SJani Nikula 		.hsync_end	= 64,		    .hblank_end		= 124,
480379bc100SJani Nikula 		.hblank_start = 836,	    .htotal		= 857,
481379bc100SJani Nikula 
482379bc100SJani Nikula 		.progressive	= false,    .trilevel_sync = false,
483379bc100SJani Nikula 
484379bc100SJani Nikula 		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
485379bc100SJani Nikula 		.vsync_len	= 6,
486379bc100SJani Nikula 
487379bc100SJani Nikula 		.veq_ena      = true,	    .veq_start_f1	= 0,
488379bc100SJani Nikula 		.veq_start_f2 = 1,	    .veq_len		= 18,
489379bc100SJani Nikula 
490379bc100SJani Nikula 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
491379bc100SJani Nikula 		.nbr_end	= 240,
492379bc100SJani Nikula 
493379bc100SJani Nikula 		.burst_ena	= true,
494379bc100SJani Nikula 		.hburst_start	= 72,		    .hburst_len		= 34,
495379bc100SJani Nikula 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
496379bc100SJani Nikula 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
497379bc100SJani Nikula 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
498379bc100SJani Nikula 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
499379bc100SJani Nikula 
500379bc100SJani Nikula 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
501379bc100SJani Nikula 		.dda1_inc	=    135,
502379bc100SJani Nikula 		.dda2_inc	=  20800,	    .dda2_size		=  27456,
503379bc100SJani Nikula 		.dda3_inc	=      0,	    .dda3_size		=      0,
504379bc100SJani Nikula 		.sc_reset	= TV_SC_RESET_EVERY_4,
505379bc100SJani Nikula 		.pal_burst	= false,
506379bc100SJani Nikula 
507379bc100SJani Nikula 		.composite_levels = &ntsc_j_levels_composite,
508379bc100SJani Nikula 		.composite_color = &ntsc_j_csc_composite,
509379bc100SJani Nikula 		.svideo_levels  = &ntsc_j_levels_svideo,
510379bc100SJani Nikula 		.svideo_color = &ntsc_j_csc_svideo,
511379bc100SJani Nikula 
512379bc100SJani Nikula 		.filter_table = filter_table,
513379bc100SJani Nikula 	},
514379bc100SJani Nikula 	{
515379bc100SJani Nikula 		.name		= "PAL-M",
516379bc100SJani Nikula 		.clock		= 108000,
517379bc100SJani Nikula 		.refresh	= 59940,
518379bc100SJani Nikula 		.oversample	= 8,
519379bc100SJani Nikula 		.component_only = false,
520379bc100SJani Nikula 
521379bc100SJani Nikula 		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
522379bc100SJani Nikula 		.hsync_end	= 64,		  .hblank_end		= 124,
523379bc100SJani Nikula 		.hblank_start = 836,	  .htotal		= 857,
524379bc100SJani Nikula 
525379bc100SJani Nikula 		.progressive	= false,	    .trilevel_sync = false,
526379bc100SJani Nikula 
527379bc100SJani Nikula 		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
528379bc100SJani Nikula 		.vsync_len	= 6,
529379bc100SJani Nikula 
530379bc100SJani Nikula 		.veq_ena	= true,		    .veq_start_f1	= 0,
531379bc100SJani Nikula 		.veq_start_f2	= 1,		    .veq_len		= 18,
532379bc100SJani Nikula 
533379bc100SJani Nikula 		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
534379bc100SJani Nikula 		.nbr_end	= 240,
535379bc100SJani Nikula 
536379bc100SJani Nikula 		.burst_ena	= true,
537379bc100SJani Nikula 		.hburst_start	= 72,		    .hburst_len		= 34,
538379bc100SJani Nikula 		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
539379bc100SJani Nikula 		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
540379bc100SJani Nikula 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
541379bc100SJani Nikula 		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
542379bc100SJani Nikula 
543379bc100SJani Nikula 		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
544379bc100SJani Nikula 		.dda1_inc	=    135,
545379bc100SJani Nikula 		.dda2_inc	=  16704,	    .dda2_size		=  27456,
546379bc100SJani Nikula 		.dda3_inc	=      0,	    .dda3_size		=      0,
547379bc100SJani Nikula 		.sc_reset	= TV_SC_RESET_EVERY_8,
548379bc100SJani Nikula 		.pal_burst  = true,
549379bc100SJani Nikula 
550379bc100SJani Nikula 		.composite_levels = &pal_m_levels_composite,
551379bc100SJani Nikula 		.composite_color = &pal_m_csc_composite,
552379bc100SJani Nikula 		.svideo_levels  = &pal_m_levels_svideo,
553379bc100SJani Nikula 		.svideo_color = &pal_m_csc_svideo,
554379bc100SJani Nikula 
555379bc100SJani Nikula 		.filter_table = filter_table,
556379bc100SJani Nikula 	},
557379bc100SJani Nikula 	{
558379bc100SJani Nikula 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
559379bc100SJani Nikula 		.name	    = "PAL-N",
560379bc100SJani Nikula 		.clock		= 108000,
561379bc100SJani Nikula 		.refresh	= 50000,
562379bc100SJani Nikula 		.oversample	= 8,
563379bc100SJani Nikula 		.component_only = false,
564379bc100SJani Nikula 
565379bc100SJani Nikula 		.hsync_end	= 64,		    .hblank_end		= 128,
566379bc100SJani Nikula 		.hblank_start = 844,	    .htotal		= 863,
567379bc100SJani Nikula 
568379bc100SJani Nikula 		.progressive  = false,    .trilevel_sync = false,
569379bc100SJani Nikula 
570379bc100SJani Nikula 
571379bc100SJani Nikula 		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
572379bc100SJani Nikula 		.vsync_len	= 6,
573379bc100SJani Nikula 
574379bc100SJani Nikula 		.veq_ena	= true,		    .veq_start_f1	= 0,
575379bc100SJani Nikula 		.veq_start_f2	= 1,		    .veq_len		= 18,
576379bc100SJani Nikula 
577379bc100SJani Nikula 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
578379bc100SJani Nikula 		.nbr_end	= 286,
579379bc100SJani Nikula 
580379bc100SJani Nikula 		.burst_ena	= true,
581379bc100SJani Nikula 		.hburst_start = 73,	    .hburst_len		= 34,
582379bc100SJani Nikula 		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
583379bc100SJani Nikula 		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
584379bc100SJani Nikula 		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
585379bc100SJani Nikula 		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
586379bc100SJani Nikula 
587379bc100SJani Nikula 
588379bc100SJani Nikula 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
589379bc100SJani Nikula 		.dda1_inc       =    135,
590379bc100SJani Nikula 		.dda2_inc       =  23578,       .dda2_size      =  27648,
591379bc100SJani Nikula 		.dda3_inc       =    134,       .dda3_size      =    625,
592379bc100SJani Nikula 		.sc_reset   = TV_SC_RESET_EVERY_8,
593379bc100SJani Nikula 		.pal_burst  = true,
594379bc100SJani Nikula 
595379bc100SJani Nikula 		.composite_levels = &pal_n_levels_composite,
596379bc100SJani Nikula 		.composite_color = &pal_n_csc_composite,
597379bc100SJani Nikula 		.svideo_levels  = &pal_n_levels_svideo,
598379bc100SJani Nikula 		.svideo_color = &pal_n_csc_svideo,
599379bc100SJani Nikula 
600379bc100SJani Nikula 		.filter_table = filter_table,
601379bc100SJani Nikula 	},
602379bc100SJani Nikula 	{
603379bc100SJani Nikula 		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
604379bc100SJani Nikula 		.name	    = "PAL",
605379bc100SJani Nikula 		.clock		= 108000,
606379bc100SJani Nikula 		.refresh	= 50000,
607379bc100SJani Nikula 		.oversample	= 8,
608379bc100SJani Nikula 		.component_only = false,
609379bc100SJani Nikula 
610379bc100SJani Nikula 		.hsync_end	= 64,		    .hblank_end		= 142,
611379bc100SJani Nikula 		.hblank_start	= 844,	    .htotal		= 863,
612379bc100SJani Nikula 
613379bc100SJani Nikula 		.progressive	= false,    .trilevel_sync = false,
614379bc100SJani Nikula 
615379bc100SJani Nikula 		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
616379bc100SJani Nikula 		.vsync_len	= 5,
617379bc100SJani Nikula 
618379bc100SJani Nikula 		.veq_ena	= true,	    .veq_start_f1	= 0,
619379bc100SJani Nikula 		.veq_start_f2	= 1,	    .veq_len		= 15,
620379bc100SJani Nikula 
621379bc100SJani Nikula 		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
622379bc100SJani Nikula 		.nbr_end	= 286,
623379bc100SJani Nikula 
624379bc100SJani Nikula 		.burst_ena	= true,
625379bc100SJani Nikula 		.hburst_start	= 73,		    .hburst_len		= 32,
626379bc100SJani Nikula 		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
627379bc100SJani Nikula 		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
628379bc100SJani Nikula 		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
629379bc100SJani Nikula 		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
630379bc100SJani Nikula 
631379bc100SJani Nikula 		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
632379bc100SJani Nikula 		.dda1_inc       =    168,
633379bc100SJani Nikula 		.dda2_inc       =   4122,       .dda2_size      =  27648,
634379bc100SJani Nikula 		.dda3_inc       =     67,       .dda3_size      =    625,
635379bc100SJani Nikula 		.sc_reset   = TV_SC_RESET_EVERY_8,
636379bc100SJani Nikula 		.pal_burst  = true,
637379bc100SJani Nikula 
638379bc100SJani Nikula 		.composite_levels = &pal_levels_composite,
639379bc100SJani Nikula 		.composite_color = &pal_csc_composite,
640379bc100SJani Nikula 		.svideo_levels  = &pal_levels_svideo,
641379bc100SJani Nikula 		.svideo_color = &pal_csc_svideo,
642379bc100SJani Nikula 
643379bc100SJani Nikula 		.filter_table = filter_table,
644379bc100SJani Nikula 	},
645379bc100SJani Nikula 	{
646379bc100SJani Nikula 		.name       = "480p",
647379bc100SJani Nikula 		.clock		= 108000,
648379bc100SJani Nikula 		.refresh	= 59940,
649379bc100SJani Nikula 		.oversample     = 4,
650379bc100SJani Nikula 		.component_only = true,
651379bc100SJani Nikula 
652379bc100SJani Nikula 		.hsync_end      = 64,               .hblank_end         = 122,
653379bc100SJani Nikula 		.hblank_start   = 842,              .htotal             = 857,
654379bc100SJani Nikula 
655379bc100SJani Nikula 		.progressive    = true,		    .trilevel_sync = false,
656379bc100SJani Nikula 
657379bc100SJani Nikula 		.vsync_start_f1 = 12,               .vsync_start_f2     = 12,
658379bc100SJani Nikula 		.vsync_len      = 12,
659379bc100SJani Nikula 
660379bc100SJani Nikula 		.veq_ena        = false,
661379bc100SJani Nikula 
662379bc100SJani Nikula 		.vi_end_f1      = 44,               .vi_end_f2          = 44,
663379bc100SJani Nikula 		.nbr_end        = 479,
664379bc100SJani Nikula 
665379bc100SJani Nikula 		.burst_ena      = false,
666379bc100SJani Nikula 
667379bc100SJani Nikula 		.filter_table = filter_table,
668379bc100SJani Nikula 	},
669379bc100SJani Nikula 	{
670379bc100SJani Nikula 		.name       = "576p",
671379bc100SJani Nikula 		.clock		= 108000,
672379bc100SJani Nikula 		.refresh	= 50000,
673379bc100SJani Nikula 		.oversample     = 4,
674379bc100SJani Nikula 		.component_only = true,
675379bc100SJani Nikula 
676379bc100SJani Nikula 		.hsync_end      = 64,               .hblank_end         = 139,
677379bc100SJani Nikula 		.hblank_start   = 859,              .htotal             = 863,
678379bc100SJani Nikula 
679379bc100SJani Nikula 		.progressive    = true,		    .trilevel_sync = false,
680379bc100SJani Nikula 
681379bc100SJani Nikula 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
682379bc100SJani Nikula 		.vsync_len      = 10,
683379bc100SJani Nikula 
684379bc100SJani Nikula 		.veq_ena        = false,
685379bc100SJani Nikula 
686379bc100SJani Nikula 		.vi_end_f1      = 48,               .vi_end_f2          = 48,
687379bc100SJani Nikula 		.nbr_end        = 575,
688379bc100SJani Nikula 
689379bc100SJani Nikula 		.burst_ena      = false,
690379bc100SJani Nikula 
691379bc100SJani Nikula 		.filter_table = filter_table,
692379bc100SJani Nikula 	},
693379bc100SJani Nikula 	{
694379bc100SJani Nikula 		.name       = "720p@60Hz",
695379bc100SJani Nikula 		.clock		= 148500,
696379bc100SJani Nikula 		.refresh	= 60000,
697379bc100SJani Nikula 		.oversample     = 2,
698379bc100SJani Nikula 		.component_only = true,
699379bc100SJani Nikula 
700379bc100SJani Nikula 		.hsync_end      = 80,               .hblank_end         = 300,
701379bc100SJani Nikula 		.hblank_start   = 1580,             .htotal             = 1649,
702379bc100SJani Nikula 
703379bc100SJani Nikula 		.progressive	= true,		    .trilevel_sync = true,
704379bc100SJani Nikula 
705379bc100SJani Nikula 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
706379bc100SJani Nikula 		.vsync_len      = 10,
707379bc100SJani Nikula 
708379bc100SJani Nikula 		.veq_ena        = false,
709379bc100SJani Nikula 
710379bc100SJani Nikula 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
711379bc100SJani Nikula 		.nbr_end        = 719,
712379bc100SJani Nikula 
713379bc100SJani Nikula 		.burst_ena      = false,
714379bc100SJani Nikula 
715379bc100SJani Nikula 		.filter_table = filter_table,
716379bc100SJani Nikula 	},
717379bc100SJani Nikula 	{
718379bc100SJani Nikula 		.name       = "720p@50Hz",
719379bc100SJani Nikula 		.clock		= 148500,
720379bc100SJani Nikula 		.refresh	= 50000,
721379bc100SJani Nikula 		.oversample     = 2,
722379bc100SJani Nikula 		.component_only = true,
723379bc100SJani Nikula 
724379bc100SJani Nikula 		.hsync_end      = 80,               .hblank_end         = 300,
725379bc100SJani Nikula 		.hblank_start   = 1580,             .htotal             = 1979,
726379bc100SJani Nikula 
727379bc100SJani Nikula 		.progressive	= true,		    .trilevel_sync = true,
728379bc100SJani Nikula 
729379bc100SJani Nikula 		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
730379bc100SJani Nikula 		.vsync_len      = 10,
731379bc100SJani Nikula 
732379bc100SJani Nikula 		.veq_ena        = false,
733379bc100SJani Nikula 
734379bc100SJani Nikula 		.vi_end_f1      = 29,               .vi_end_f2          = 29,
735379bc100SJani Nikula 		.nbr_end        = 719,
736379bc100SJani Nikula 
737379bc100SJani Nikula 		.burst_ena      = false,
738379bc100SJani Nikula 
739379bc100SJani Nikula 		.filter_table = filter_table,
740379bc100SJani Nikula 	},
741379bc100SJani Nikula 	{
742379bc100SJani Nikula 		.name       = "1080i@50Hz",
743379bc100SJani Nikula 		.clock		= 148500,
744379bc100SJani Nikula 		.refresh	= 50000,
745379bc100SJani Nikula 		.oversample     = 2,
746379bc100SJani Nikula 		.component_only = true,
747379bc100SJani Nikula 
748379bc100SJani Nikula 		.hsync_end      = 88,               .hblank_end         = 235,
749379bc100SJani Nikula 		.hblank_start   = 2155,             .htotal             = 2639,
750379bc100SJani Nikula 
751379bc100SJani Nikula 		.progressive	= false,	  .trilevel_sync = true,
752379bc100SJani Nikula 
753379bc100SJani Nikula 		.vsync_start_f1 = 4,              .vsync_start_f2     = 5,
754379bc100SJani Nikula 		.vsync_len      = 10,
755379bc100SJani Nikula 
756379bc100SJani Nikula 		.veq_ena	= true,	    .veq_start_f1	= 4,
757379bc100SJani Nikula 		.veq_start_f2   = 4,	    .veq_len		= 10,
758379bc100SJani Nikula 
759379bc100SJani Nikula 
760379bc100SJani Nikula 		.vi_end_f1      = 21,           .vi_end_f2          = 22,
761379bc100SJani Nikula 		.nbr_end        = 539,
762379bc100SJani Nikula 
763379bc100SJani Nikula 		.burst_ena      = false,
764379bc100SJani Nikula 
765379bc100SJani Nikula 		.filter_table = filter_table,
766379bc100SJani Nikula 	},
767379bc100SJani Nikula 	{
768379bc100SJani Nikula 		.name       = "1080i@60Hz",
769379bc100SJani Nikula 		.clock		= 148500,
770379bc100SJani Nikula 		.refresh	= 60000,
771379bc100SJani Nikula 		.oversample     = 2,
772379bc100SJani Nikula 		.component_only = true,
773379bc100SJani Nikula 
774379bc100SJani Nikula 		.hsync_end      = 88,               .hblank_end         = 235,
775379bc100SJani Nikula 		.hblank_start   = 2155,             .htotal             = 2199,
776379bc100SJani Nikula 
777379bc100SJani Nikula 		.progressive	= false,	    .trilevel_sync = true,
778379bc100SJani Nikula 
779379bc100SJani Nikula 		.vsync_start_f1 = 4,               .vsync_start_f2     = 5,
780379bc100SJani Nikula 		.vsync_len      = 10,
781379bc100SJani Nikula 
782379bc100SJani Nikula 		.veq_ena	= true,		    .veq_start_f1	= 4,
783379bc100SJani Nikula 		.veq_start_f2	= 4,		    .veq_len		= 10,
784379bc100SJani Nikula 
785379bc100SJani Nikula 
786379bc100SJani Nikula 		.vi_end_f1      = 21,               .vi_end_f2          = 22,
787379bc100SJani Nikula 		.nbr_end        = 539,
788379bc100SJani Nikula 
789379bc100SJani Nikula 		.burst_ena      = false,
790379bc100SJani Nikula 
791379bc100SJani Nikula 		.filter_table = filter_table,
792379bc100SJani Nikula 	},
793379bc100SJani Nikula 
794379bc100SJani Nikula 	{
795379bc100SJani Nikula 		.name       = "1080p@30Hz",
796379bc100SJani Nikula 		.clock		= 148500,
797379bc100SJani Nikula 		.refresh	= 30000,
798379bc100SJani Nikula 		.oversample     = 2,
799379bc100SJani Nikula 		.component_only = true,
800379bc100SJani Nikula 
801379bc100SJani Nikula 		.hsync_end      = 88,               .hblank_end         = 235,
802379bc100SJani Nikula 		.hblank_start   = 2155,             .htotal             = 2199,
803379bc100SJani Nikula 
804379bc100SJani Nikula 		.progressive	= true,		    .trilevel_sync = true,
805379bc100SJani Nikula 
806379bc100SJani Nikula 		.vsync_start_f1 = 8,               .vsync_start_f2     = 8,
807379bc100SJani Nikula 		.vsync_len      = 10,
808379bc100SJani Nikula 
809379bc100SJani Nikula 		.veq_ena	= false,	.veq_start_f1	= 0,
810379bc100SJani Nikula 		.veq_start_f2	= 0,		    .veq_len		= 0,
811379bc100SJani Nikula 
812379bc100SJani Nikula 		.vi_end_f1      = 44,               .vi_end_f2          = 44,
813379bc100SJani Nikula 		.nbr_end        = 1079,
814379bc100SJani Nikula 
815379bc100SJani Nikula 		.burst_ena      = false,
816379bc100SJani Nikula 
817379bc100SJani Nikula 		.filter_table = filter_table,
818379bc100SJani Nikula 	},
819379bc100SJani Nikula 
820379bc100SJani Nikula 	{
821379bc100SJani Nikula 		.name       = "1080p@50Hz",
822379bc100SJani Nikula 		.clock		= 148500,
823379bc100SJani Nikula 		.refresh	= 50000,
824379bc100SJani Nikula 		.oversample     = 1,
825379bc100SJani Nikula 		.component_only = true,
826379bc100SJani Nikula 
827379bc100SJani Nikula 		.hsync_end      = 88,               .hblank_end         = 235,
828379bc100SJani Nikula 		.hblank_start   = 2155,             .htotal             = 2639,
829379bc100SJani Nikula 
830379bc100SJani Nikula 		.progressive	= true,		    .trilevel_sync = true,
831379bc100SJani Nikula 
832379bc100SJani Nikula 		.vsync_start_f1 = 8,               .vsync_start_f2     = 8,
833379bc100SJani Nikula 		.vsync_len      = 10,
834379bc100SJani Nikula 
835379bc100SJani Nikula 		.veq_ena	= false,	.veq_start_f1	= 0,
836379bc100SJani Nikula 		.veq_start_f2	= 0,		    .veq_len		= 0,
837379bc100SJani Nikula 
838379bc100SJani Nikula 		.vi_end_f1      = 44,               .vi_end_f2          = 44,
839379bc100SJani Nikula 		.nbr_end        = 1079,
840379bc100SJani Nikula 
841379bc100SJani Nikula 		.burst_ena      = false,
842379bc100SJani Nikula 
843379bc100SJani Nikula 		.filter_table = filter_table,
844379bc100SJani Nikula 	},
845379bc100SJani Nikula 
846379bc100SJani Nikula 	{
847379bc100SJani Nikula 		.name       = "1080p@60Hz",
848379bc100SJani Nikula 		.clock		= 148500,
849379bc100SJani Nikula 		.refresh	= 60000,
850379bc100SJani Nikula 		.oversample     = 1,
851379bc100SJani Nikula 		.component_only = true,
852379bc100SJani Nikula 
853379bc100SJani Nikula 		.hsync_end      = 88,               .hblank_end         = 235,
854379bc100SJani Nikula 		.hblank_start   = 2155,             .htotal             = 2199,
855379bc100SJani Nikula 
856379bc100SJani Nikula 		.progressive	= true,		    .trilevel_sync = true,
857379bc100SJani Nikula 
858379bc100SJani Nikula 		.vsync_start_f1 = 8,               .vsync_start_f2     = 8,
859379bc100SJani Nikula 		.vsync_len      = 10,
860379bc100SJani Nikula 
861379bc100SJani Nikula 		.veq_ena	= false,		    .veq_start_f1	= 0,
862379bc100SJani Nikula 		.veq_start_f2	= 0,		    .veq_len		= 0,
863379bc100SJani Nikula 
864379bc100SJani Nikula 		.vi_end_f1      = 44,               .vi_end_f2          = 44,
865379bc100SJani Nikula 		.nbr_end        = 1079,
866379bc100SJani Nikula 
867379bc100SJani Nikula 		.burst_ena      = false,
868379bc100SJani Nikula 
869379bc100SJani Nikula 		.filter_table = filter_table,
870379bc100SJani Nikula 	},
871379bc100SJani Nikula };
872379bc100SJani Nikula 
873379bc100SJani Nikula struct intel_tv_connector_state {
874379bc100SJani Nikula 	struct drm_connector_state base;
875379bc100SJani Nikula 
876379bc100SJani Nikula 	/*
877379bc100SJani Nikula 	 * May need to override the user margins for
878379bc100SJani Nikula 	 * gen3 >1024 wide source vertical centering.
879379bc100SJani Nikula 	 */
880379bc100SJani Nikula 	struct {
881379bc100SJani Nikula 		u16 top, bottom;
882379bc100SJani Nikula 	} margins;
883379bc100SJani Nikula 
884379bc100SJani Nikula 	bool bypass_vfilter;
885379bc100SJani Nikula };
886379bc100SJani Nikula 
887379bc100SJani Nikula #define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
888379bc100SJani Nikula 
889379bc100SJani Nikula static struct drm_connector_state *
intel_tv_connector_duplicate_state(struct drm_connector * connector)890379bc100SJani Nikula intel_tv_connector_duplicate_state(struct drm_connector *connector)
891379bc100SJani Nikula {
892379bc100SJani Nikula 	struct intel_tv_connector_state *state;
893379bc100SJani Nikula 
894379bc100SJani Nikula 	state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
895379bc100SJani Nikula 	if (!state)
896379bc100SJani Nikula 		return NULL;
897379bc100SJani Nikula 
898379bc100SJani Nikula 	__drm_atomic_helper_connector_duplicate_state(connector, &state->base);
899379bc100SJani Nikula 	return &state->base;
900379bc100SJani Nikula }
901379bc100SJani Nikula 
enc_to_tv(struct intel_encoder * encoder)902379bc100SJani Nikula static struct intel_tv *enc_to_tv(struct intel_encoder *encoder)
903379bc100SJani Nikula {
904379bc100SJani Nikula 	return container_of(encoder, struct intel_tv, base);
905379bc100SJani Nikula }
906379bc100SJani Nikula 
intel_attached_tv(struct intel_connector * connector)90743a6d19cSVille Syrjälä static struct intel_tv *intel_attached_tv(struct intel_connector *connector)
908379bc100SJani Nikula {
909379bc100SJani Nikula 	return enc_to_tv(intel_attached_encoder(connector));
910379bc100SJani Nikula }
911379bc100SJani Nikula 
912379bc100SJani Nikula static bool
intel_tv_get_hw_state(struct intel_encoder * encoder,enum pipe * pipe)913379bc100SJani Nikula intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
914379bc100SJani Nikula {
915379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
916988ff27bSJani Nikula 	u32 tmp = intel_de_read(dev_priv, TV_CTL);
917379bc100SJani Nikula 
918379bc100SJani Nikula 	*pipe = (tmp & TV_ENC_PIPE_SEL_MASK) >> TV_ENC_PIPE_SEL_SHIFT;
919379bc100SJani Nikula 
920379bc100SJani Nikula 	return tmp & TV_ENC_ENABLE;
921379bc100SJani Nikula }
922379bc100SJani Nikula 
923379bc100SJani Nikula static void
intel_enable_tv(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)924ede9771dSVille Syrjälä intel_enable_tv(struct intel_atomic_state *state,
925ede9771dSVille Syrjälä 		struct intel_encoder *encoder,
926379bc100SJani Nikula 		const struct intel_crtc_state *pipe_config,
927379bc100SJani Nikula 		const struct drm_connector_state *conn_state)
928379bc100SJani Nikula {
929379bc100SJani Nikula 	struct drm_device *dev = encoder->base.dev;
930379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(dev);
931379bc100SJani Nikula 
932379bc100SJani Nikula 	/* Prevents vblank waits from timing out in intel_tv_detect_type() */
9337b06894bSJani Nikula 	intel_crtc_wait_for_next_vblank(to_intel_crtc(pipe_config->uapi.crtc));
934379bc100SJani Nikula 
93559ea2887SAndrzej Hajda 	intel_de_rmw(dev_priv, TV_CTL, 0, TV_ENC_ENABLE);
936379bc100SJani Nikula }
937379bc100SJani Nikula 
938379bc100SJani Nikula static void
intel_disable_tv(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)939ede9771dSVille Syrjälä intel_disable_tv(struct intel_atomic_state *state,
940ede9771dSVille Syrjälä 		 struct intel_encoder *encoder,
941379bc100SJani Nikula 		 const struct intel_crtc_state *old_crtc_state,
942379bc100SJani Nikula 		 const struct drm_connector_state *old_conn_state)
943379bc100SJani Nikula {
944379bc100SJani Nikula 	struct drm_device *dev = encoder->base.dev;
945379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(dev);
946379bc100SJani Nikula 
94759ea2887SAndrzej Hajda 	intel_de_rmw(dev_priv, TV_CTL, TV_ENC_ENABLE, 0);
948379bc100SJani Nikula }
949379bc100SJani Nikula 
intel_tv_mode_find(const struct drm_connector_state * conn_state)950379bc100SJani Nikula static const struct tv_mode *intel_tv_mode_find(const struct drm_connector_state *conn_state)
951379bc100SJani Nikula {
952*16bc939fSMaxime Ripard 	int format = conn_state->tv.legacy_mode;
953379bc100SJani Nikula 
954379bc100SJani Nikula 	return &tv_modes[format];
955379bc100SJani Nikula }
956379bc100SJani Nikula 
957379bc100SJani Nikula static enum drm_mode_status
intel_tv_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)958379bc100SJani Nikula intel_tv_mode_valid(struct drm_connector *connector,
959379bc100SJani Nikula 		    struct drm_display_mode *mode)
960379bc100SJani Nikula {
9618e1e489cSVille Syrjälä 	struct drm_i915_private *i915 = to_i915(connector->dev);
962379bc100SJani Nikula 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
9638e1e489cSVille Syrjälä 	int max_dotclk = i915->max_dotclk_freq;
9648e1e489cSVille Syrjälä 	enum drm_mode_status status;
9658e1e489cSVille Syrjälä 
9668e1e489cSVille Syrjälä 	status = intel_cpu_transcoder_mode_valid(i915, mode);
9678e1e489cSVille Syrjälä 	if (status != MODE_OK)
9688e1e489cSVille Syrjälä 		return status;
969379bc100SJani Nikula 
970379bc100SJani Nikula 	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
971379bc100SJani Nikula 		return MODE_NO_DBLESCAN;
972379bc100SJani Nikula 
973379bc100SJani Nikula 	if (mode->clock > max_dotclk)
974379bc100SJani Nikula 		return MODE_CLOCK_HIGH;
975379bc100SJani Nikula 
976379bc100SJani Nikula 	/* Ensure TV refresh is close to desired refresh */
97715de0889SVille Syrjälä 	if (abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) >= 1000)
978379bc100SJani Nikula 		return MODE_CLOCK_RANGE;
97915de0889SVille Syrjälä 
98015de0889SVille Syrjälä 	return MODE_OK;
981379bc100SJani Nikula }
982379bc100SJani Nikula 
983379bc100SJani Nikula static int
intel_tv_mode_vdisplay(const struct tv_mode * tv_mode)984379bc100SJani Nikula intel_tv_mode_vdisplay(const struct tv_mode *tv_mode)
985379bc100SJani Nikula {
986379bc100SJani Nikula 	if (tv_mode->progressive)
987379bc100SJani Nikula 		return tv_mode->nbr_end + 1;
988379bc100SJani Nikula 	else
989379bc100SJani Nikula 		return 2 * (tv_mode->nbr_end + 1);
990379bc100SJani Nikula }
991379bc100SJani Nikula 
992379bc100SJani Nikula static void
intel_tv_mode_to_mode(struct drm_display_mode * mode,const struct tv_mode * tv_mode,int clock)993379bc100SJani Nikula intel_tv_mode_to_mode(struct drm_display_mode *mode,
9941bba5543SVille Syrjälä 		      const struct tv_mode *tv_mode,
9951bba5543SVille Syrjälä 		      int clock)
996379bc100SJani Nikula {
9971bba5543SVille Syrjälä 	mode->clock = clock / (tv_mode->oversample >> !tv_mode->progressive);
998379bc100SJani Nikula 
999379bc100SJani Nikula 	/*
1000379bc100SJani Nikula 	 * tv_mode horizontal timings:
1001379bc100SJani Nikula 	 *
1002379bc100SJani Nikula 	 * hsync_end
1003379bc100SJani Nikula 	 *    | hblank_end
1004379bc100SJani Nikula 	 *    |    | hblank_start
1005379bc100SJani Nikula 	 *    |    |       | htotal
1006379bc100SJani Nikula 	 *    |     _______    |
1007379bc100SJani Nikula 	 *     ____/       \___
1008379bc100SJani Nikula 	 * \__/                \
1009379bc100SJani Nikula 	 */
1010379bc100SJani Nikula 	mode->hdisplay =
1011379bc100SJani Nikula 		tv_mode->hblank_start - tv_mode->hblank_end;
1012379bc100SJani Nikula 	mode->hsync_start = mode->hdisplay +
1013379bc100SJani Nikula 		tv_mode->htotal - tv_mode->hblank_start;
1014379bc100SJani Nikula 	mode->hsync_end = mode->hsync_start +
1015379bc100SJani Nikula 		tv_mode->hsync_end;
1016379bc100SJani Nikula 	mode->htotal = tv_mode->htotal + 1;
1017379bc100SJani Nikula 
1018379bc100SJani Nikula 	/*
1019379bc100SJani Nikula 	 * tv_mode vertical timings:
1020379bc100SJani Nikula 	 *
1021379bc100SJani Nikula 	 * vsync_start
1022379bc100SJani Nikula 	 *    | vsync_end
1023379bc100SJani Nikula 	 *    |  | vi_end nbr_end
1024379bc100SJani Nikula 	 *    |  |    |       |
1025379bc100SJani Nikula 	 *    |  |     _______
1026379bc100SJani Nikula 	 * \__    ____/       \
1027379bc100SJani Nikula 	 *    \__/
1028379bc100SJani Nikula 	 */
1029379bc100SJani Nikula 	mode->vdisplay = intel_tv_mode_vdisplay(tv_mode);
1030379bc100SJani Nikula 	if (tv_mode->progressive) {
1031379bc100SJani Nikula 		mode->vsync_start = mode->vdisplay +
1032379bc100SJani Nikula 			tv_mode->vsync_start_f1 + 1;
1033379bc100SJani Nikula 		mode->vsync_end = mode->vsync_start +
1034379bc100SJani Nikula 			tv_mode->vsync_len;
1035379bc100SJani Nikula 		mode->vtotal = mode->vdisplay +
1036379bc100SJani Nikula 			tv_mode->vi_end_f1 + 1;
1037379bc100SJani Nikula 	} else {
1038379bc100SJani Nikula 		mode->vsync_start = mode->vdisplay +
1039379bc100SJani Nikula 			tv_mode->vsync_start_f1 + 1 +
1040379bc100SJani Nikula 			tv_mode->vsync_start_f2 + 1;
1041379bc100SJani Nikula 		mode->vsync_end = mode->vsync_start +
1042379bc100SJani Nikula 			2 * tv_mode->vsync_len;
1043379bc100SJani Nikula 		mode->vtotal = mode->vdisplay +
1044379bc100SJani Nikula 			tv_mode->vi_end_f1 + 1 +
1045379bc100SJani Nikula 			tv_mode->vi_end_f2 + 1;
1046379bc100SJani Nikula 	}
1047379bc100SJani Nikula 
1048379bc100SJani Nikula 	/* TV has it's own notion of sync and other mode flags, so clear them. */
1049379bc100SJani Nikula 	mode->flags = 0;
1050379bc100SJani Nikula 
1051379bc100SJani Nikula 	snprintf(mode->name, sizeof(mode->name),
1052379bc100SJani Nikula 		 "%dx%d%c (%s)",
1053379bc100SJani Nikula 		 mode->hdisplay, mode->vdisplay,
1054379bc100SJani Nikula 		 tv_mode->progressive ? 'p' : 'i',
1055379bc100SJani Nikula 		 tv_mode->name);
1056379bc100SJani Nikula }
1057379bc100SJani Nikula 
intel_tv_scale_mode_horiz(struct drm_display_mode * mode,int hdisplay,int left_margin,int right_margin)1058379bc100SJani Nikula static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode,
1059379bc100SJani Nikula 				      int hdisplay, int left_margin,
1060379bc100SJani Nikula 				      int right_margin)
1061379bc100SJani Nikula {
1062379bc100SJani Nikula 	int hsync_start = mode->hsync_start - mode->hdisplay + right_margin;
1063379bc100SJani Nikula 	int hsync_end = mode->hsync_end - mode->hdisplay + right_margin;
1064379bc100SJani Nikula 	int new_htotal = mode->htotal * hdisplay /
1065379bc100SJani Nikula 		(mode->hdisplay - left_margin - right_margin);
1066379bc100SJani Nikula 
1067379bc100SJani Nikula 	mode->clock = mode->clock * new_htotal / mode->htotal;
1068379bc100SJani Nikula 
1069379bc100SJani Nikula 	mode->hdisplay = hdisplay;
1070379bc100SJani Nikula 	mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal;
1071379bc100SJani Nikula 	mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal;
1072379bc100SJani Nikula 	mode->htotal = new_htotal;
1073379bc100SJani Nikula }
1074379bc100SJani Nikula 
intel_tv_scale_mode_vert(struct drm_display_mode * mode,int vdisplay,int top_margin,int bottom_margin)1075379bc100SJani Nikula static void intel_tv_scale_mode_vert(struct drm_display_mode *mode,
1076379bc100SJani Nikula 				     int vdisplay, int top_margin,
1077379bc100SJani Nikula 				     int bottom_margin)
1078379bc100SJani Nikula {
1079379bc100SJani Nikula 	int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin;
1080379bc100SJani Nikula 	int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin;
1081379bc100SJani Nikula 	int new_vtotal = mode->vtotal * vdisplay /
1082379bc100SJani Nikula 		(mode->vdisplay - top_margin - bottom_margin);
1083379bc100SJani Nikula 
1084379bc100SJani Nikula 	mode->clock = mode->clock * new_vtotal / mode->vtotal;
1085379bc100SJani Nikula 
1086379bc100SJani Nikula 	mode->vdisplay = vdisplay;
1087379bc100SJani Nikula 	mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal;
1088379bc100SJani Nikula 	mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal;
1089379bc100SJani Nikula 	mode->vtotal = new_vtotal;
1090379bc100SJani Nikula }
1091379bc100SJani Nikula 
1092379bc100SJani Nikula static void
intel_tv_get_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config)1093379bc100SJani Nikula intel_tv_get_config(struct intel_encoder *encoder,
1094379bc100SJani Nikula 		    struct intel_crtc_state *pipe_config)
1095379bc100SJani Nikula {
1096379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1097379bc100SJani Nikula 	struct drm_display_mode *adjusted_mode =
10981326a92cSMaarten Lankhorst 		&pipe_config->hw.adjusted_mode;
1099379bc100SJani Nikula 	struct drm_display_mode mode = {};
1100379bc100SJani Nikula 	u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp;
1101379bc100SJani Nikula 	struct tv_mode tv_mode = {};
1102379bc100SJani Nikula 	int hdisplay = adjusted_mode->crtc_hdisplay;
1103379bc100SJani Nikula 	int vdisplay = adjusted_mode->crtc_vdisplay;
1104379bc100SJani Nikula 	int xsize, ysize, xpos, ypos;
1105379bc100SJani Nikula 
1106379bc100SJani Nikula 	pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
1107379bc100SJani Nikula 
1108988ff27bSJani Nikula 	tv_ctl = intel_de_read(dev_priv, TV_CTL);
1109988ff27bSJani Nikula 	hctl1 = intel_de_read(dev_priv, TV_H_CTL_1);
1110988ff27bSJani Nikula 	hctl3 = intel_de_read(dev_priv, TV_H_CTL_3);
1111988ff27bSJani Nikula 	vctl1 = intel_de_read(dev_priv, TV_V_CTL_1);
1112988ff27bSJani Nikula 	vctl2 = intel_de_read(dev_priv, TV_V_CTL_2);
1113379bc100SJani Nikula 
1114379bc100SJani Nikula 	tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT;
1115379bc100SJani Nikula 	tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT;
1116379bc100SJani Nikula 
1117379bc100SJani Nikula 	tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT;
1118379bc100SJani Nikula 	tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT;
1119379bc100SJani Nikula 
1120379bc100SJani Nikula 	tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT;
1121379bc100SJani Nikula 	tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT;
1122379bc100SJani Nikula 	tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT;
1123379bc100SJani Nikula 
1124379bc100SJani Nikula 	tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT;
1125379bc100SJani Nikula 	tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT;
1126379bc100SJani Nikula 	tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT;
1127379bc100SJani Nikula 
1128379bc100SJani Nikula 	tv_mode.clock = pipe_config->port_clock;
1129379bc100SJani Nikula 
1130379bc100SJani Nikula 	tv_mode.progressive = tv_ctl & TV_PROGRESSIVE;
1131379bc100SJani Nikula 
1132379bc100SJani Nikula 	switch (tv_ctl & TV_OVERSAMPLE_MASK) {
1133379bc100SJani Nikula 	case TV_OVERSAMPLE_8X:
1134379bc100SJani Nikula 		tv_mode.oversample = 8;
1135379bc100SJani Nikula 		break;
1136379bc100SJani Nikula 	case TV_OVERSAMPLE_4X:
1137379bc100SJani Nikula 		tv_mode.oversample = 4;
1138379bc100SJani Nikula 		break;
1139379bc100SJani Nikula 	case TV_OVERSAMPLE_2X:
1140379bc100SJani Nikula 		tv_mode.oversample = 2;
1141379bc100SJani Nikula 		break;
1142379bc100SJani Nikula 	default:
1143379bc100SJani Nikula 		tv_mode.oversample = 1;
1144379bc100SJani Nikula 		break;
1145379bc100SJani Nikula 	}
1146379bc100SJani Nikula 
1147988ff27bSJani Nikula 	tmp = intel_de_read(dev_priv, TV_WIN_POS);
1148379bc100SJani Nikula 	xpos = tmp >> 16;
1149379bc100SJani Nikula 	ypos = tmp & 0xffff;
1150379bc100SJani Nikula 
1151988ff27bSJani Nikula 	tmp = intel_de_read(dev_priv, TV_WIN_SIZE);
1152379bc100SJani Nikula 	xsize = tmp >> 16;
1153379bc100SJani Nikula 	ysize = tmp & 0xffff;
1154379bc100SJani Nikula 
11551bba5543SVille Syrjälä 	intel_tv_mode_to_mode(&mode, &tv_mode, pipe_config->port_clock);
1156379bc100SJani Nikula 
1157f01bae2dSVille Syrjälä 	drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n",
1158f01bae2dSVille Syrjälä 		    DRM_MODE_ARG(&mode));
1159379bc100SJani Nikula 
1160379bc100SJani Nikula 	intel_tv_scale_mode_horiz(&mode, hdisplay,
1161379bc100SJani Nikula 				  xpos, mode.hdisplay - xsize - xpos);
1162379bc100SJani Nikula 	intel_tv_scale_mode_vert(&mode, vdisplay,
1163379bc100SJani Nikula 				 ypos, mode.vdisplay - ysize - ypos);
1164379bc100SJani Nikula 
1165379bc100SJani Nikula 	adjusted_mode->crtc_clock = mode.clock;
1166379bc100SJani Nikula 	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
1167379bc100SJani Nikula 		adjusted_mode->crtc_clock /= 2;
1168379bc100SJani Nikula 
1169379bc100SJani Nikula 	/* pixel counter doesn't work on i965gm TV output */
1170379bc100SJani Nikula 	if (IS_I965GM(dev_priv))
1171af157b76SVille Syrjälä 		pipe_config->mode_flags |=
1172379bc100SJani Nikula 			I915_MODE_FLAG_USE_SCANLINE_COUNTER;
1173379bc100SJani Nikula }
1174379bc100SJani Nikula 
intel_tv_source_too_wide(struct drm_i915_private * dev_priv,int hdisplay)1175379bc100SJani Nikula static bool intel_tv_source_too_wide(struct drm_i915_private *dev_priv,
1176379bc100SJani Nikula 				     int hdisplay)
1177379bc100SJani Nikula {
117893e7e61eSLucas De Marchi 	return DISPLAY_VER(dev_priv) == 3 && hdisplay > 1024;
1179379bc100SJani Nikula }
1180379bc100SJani Nikula 
intel_tv_vert_scaling(const struct drm_display_mode * tv_mode,const struct drm_connector_state * conn_state,int vdisplay)1181379bc100SJani Nikula static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode,
1182379bc100SJani Nikula 				  const struct drm_connector_state *conn_state,
1183379bc100SJani Nikula 				  int vdisplay)
1184379bc100SJani Nikula {
1185379bc100SJani Nikula 	return tv_mode->crtc_vdisplay -
1186379bc100SJani Nikula 		conn_state->tv.margins.top -
1187379bc100SJani Nikula 		conn_state->tv.margins.bottom !=
1188379bc100SJani Nikula 		vdisplay;
1189379bc100SJani Nikula }
1190379bc100SJani Nikula 
1191379bc100SJani Nikula static int
intel_tv_compute_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config,struct drm_connector_state * conn_state)1192379bc100SJani Nikula intel_tv_compute_config(struct intel_encoder *encoder,
1193379bc100SJani Nikula 			struct intel_crtc_state *pipe_config,
1194379bc100SJani Nikula 			struct drm_connector_state *conn_state)
1195379bc100SJani Nikula {
11961bba5543SVille Syrjälä 	struct intel_atomic_state *state =
11971bba5543SVille Syrjälä 		to_intel_atomic_state(pipe_config->uapi.state);
11981bba5543SVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
1199379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1200379bc100SJani Nikula 	struct intel_tv_connector_state *tv_conn_state =
1201379bc100SJani Nikula 		to_intel_tv_connector_state(conn_state);
1202379bc100SJani Nikula 	const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
1203379bc100SJani Nikula 	struct drm_display_mode *adjusted_mode =
12041326a92cSMaarten Lankhorst 		&pipe_config->hw.adjusted_mode;
1205379bc100SJani Nikula 	int hdisplay = adjusted_mode->crtc_hdisplay;
1206379bc100SJani Nikula 	int vdisplay = adjusted_mode->crtc_vdisplay;
12071bba5543SVille Syrjälä 	int ret;
1208379bc100SJani Nikula 
1209379bc100SJani Nikula 	if (!tv_mode)
1210379bc100SJani Nikula 		return -EINVAL;
1211379bc100SJani Nikula 
1212379bc100SJani Nikula 	if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
1213379bc100SJani Nikula 		return -EINVAL;
1214379bc100SJani Nikula 
1215a04d27cdSAnkit Nautiyal 	pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
1216379bc100SJani Nikula 	pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
1217379bc100SJani Nikula 
1218025c2e19SWambui Karuga 	drm_dbg_kms(&dev_priv->drm, "forcing bpc to 8 for TV\n");
1219379bc100SJani Nikula 	pipe_config->pipe_bpp = 8*3;
1220379bc100SJani Nikula 
1221379bc100SJani Nikula 	pipe_config->port_clock = tv_mode->clock;
1222379bc100SJani Nikula 
12231bba5543SVille Syrjälä 	ret = intel_dpll_crtc_compute_clock(state, crtc);
12241bba5543SVille Syrjälä 	if (ret)
12251bba5543SVille Syrjälä 		return ret;
12261bba5543SVille Syrjälä 
12271bba5543SVille Syrjälä 	pipe_config->clock_set = true;
12281bba5543SVille Syrjälä 
12291bba5543SVille Syrjälä 	intel_tv_mode_to_mode(adjusted_mode, tv_mode, pipe_config->port_clock);
1230379bc100SJani Nikula 	drm_mode_set_crtcinfo(adjusted_mode, 0);
1231379bc100SJani Nikula 
1232379bc100SJani Nikula 	if (intel_tv_source_too_wide(dev_priv, hdisplay) ||
1233379bc100SJani Nikula 	    !intel_tv_vert_scaling(adjusted_mode, conn_state, vdisplay)) {
1234379bc100SJani Nikula 		int extra, top, bottom;
1235379bc100SJani Nikula 
1236379bc100SJani Nikula 		extra = adjusted_mode->crtc_vdisplay - vdisplay;
1237379bc100SJani Nikula 
1238379bc100SJani Nikula 		if (extra < 0) {
1239025c2e19SWambui Karuga 			drm_dbg_kms(&dev_priv->drm,
1240025c2e19SWambui Karuga 				    "No vertical scaling for >1024 pixel wide modes\n");
1241379bc100SJani Nikula 			return -EINVAL;
1242379bc100SJani Nikula 		}
1243379bc100SJani Nikula 
1244379bc100SJani Nikula 		/* Need to turn off the vertical filter and center the image */
1245379bc100SJani Nikula 
1246379bc100SJani Nikula 		/* Attempt to maintain the relative sizes of the margins */
1247379bc100SJani Nikula 		top = conn_state->tv.margins.top;
1248379bc100SJani Nikula 		bottom = conn_state->tv.margins.bottom;
1249379bc100SJani Nikula 
1250379bc100SJani Nikula 		if (top + bottom)
1251379bc100SJani Nikula 			top = extra * top / (top + bottom);
1252379bc100SJani Nikula 		else
1253379bc100SJani Nikula 			top = extra / 2;
1254379bc100SJani Nikula 		bottom = extra - top;
1255379bc100SJani Nikula 
1256379bc100SJani Nikula 		tv_conn_state->margins.top = top;
1257379bc100SJani Nikula 		tv_conn_state->margins.bottom = bottom;
1258379bc100SJani Nikula 
1259379bc100SJani Nikula 		tv_conn_state->bypass_vfilter = true;
1260379bc100SJani Nikula 
1261379bc100SJani Nikula 		if (!tv_mode->progressive) {
1262379bc100SJani Nikula 			adjusted_mode->clock /= 2;
1263379bc100SJani Nikula 			adjusted_mode->crtc_clock /= 2;
1264379bc100SJani Nikula 			adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
1265379bc100SJani Nikula 		}
1266379bc100SJani Nikula 	} else {
1267379bc100SJani Nikula 		tv_conn_state->margins.top = conn_state->tv.margins.top;
1268379bc100SJani Nikula 		tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
1269379bc100SJani Nikula 
1270379bc100SJani Nikula 		tv_conn_state->bypass_vfilter = false;
1271379bc100SJani Nikula 	}
1272379bc100SJani Nikula 
1273f01bae2dSVille Syrjälä 	drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n",
1274f01bae2dSVille Syrjälä 		    DRM_MODE_ARG(adjusted_mode));
1275379bc100SJani Nikula 
1276379bc100SJani Nikula 	/*
1277379bc100SJani Nikula 	 * The pipe scanline counter behaviour looks as follows when
1278379bc100SJani Nikula 	 * using the TV encoder:
1279379bc100SJani Nikula 	 *
1280379bc100SJani Nikula 	 * time ->
1281379bc100SJani Nikula 	 *
1282379bc100SJani Nikula 	 * dsl=vtotal-1       |             |
1283379bc100SJani Nikula 	 *                   ||            ||
1284379bc100SJani Nikula 	 *               ___| |        ___| |
1285379bc100SJani Nikula 	 *              /     |       /     |
1286379bc100SJani Nikula 	 *             /      |      /      |
1287379bc100SJani Nikula 	 * dsl=0   ___/       |_____/       |
1288379bc100SJani Nikula 	 *        | | |  |  | |
1289379bc100SJani Nikula 	 *         ^ ^ ^   ^ ^
1290379bc100SJani Nikula 	 *         | | |   | pipe vblank/first part of tv vblank
1291379bc100SJani Nikula 	 *         | | |   bottom margin
1292379bc100SJani Nikula 	 *         | | active
1293379bc100SJani Nikula 	 *         | top margin
1294379bc100SJani Nikula 	 *         remainder of tv vblank
1295379bc100SJani Nikula 	 *
1296379bc100SJani Nikula 	 * When the TV encoder is used the pipe wants to run faster
1297379bc100SJani Nikula 	 * than expected rate. During the active portion the TV
1298379bc100SJani Nikula 	 * encoder stalls the pipe every few lines to keep it in
1299379bc100SJani Nikula 	 * check. When the TV encoder reaches the bottom margin the
1300379bc100SJani Nikula 	 * pipe simply stops. Once we reach the TV vblank the pipe is
1301379bc100SJani Nikula 	 * no longer stalled and it runs at the max rate (apparently
1302379bc100SJani Nikula 	 * oversample clock on gen3, cdclk on gen4). Once the pipe
1303379bc100SJani Nikula 	 * reaches the pipe vtotal the pipe stops for the remainder
1304379bc100SJani Nikula 	 * of the TV vblank/top margin. The pipe starts up again when
1305379bc100SJani Nikula 	 * the TV encoder exits the top margin.
1306379bc100SJani Nikula 	 *
1307379bc100SJani Nikula 	 * To avoid huge hassles for vblank timestamping we scale
1308379bc100SJani Nikula 	 * the pipe timings as if the pipe always runs at the average
1309379bc100SJani Nikula 	 * rate it maintains during the active period. This also
1310379bc100SJani Nikula 	 * gives us a reasonable guesstimate as to the pixel rate.
1311379bc100SJani Nikula 	 * Due to the variation in the actual pipe speed the scanline
1312379bc100SJani Nikula 	 * counter will give us slightly erroneous results during the
1313379bc100SJani Nikula 	 * TV vblank/margins. But since vtotal was selected such that
1314379bc100SJani Nikula 	 * it matches the average rate of the pipe during the active
1315379bc100SJani Nikula 	 * portion the error shouldn't cause any serious grief to
1316379bc100SJani Nikula 	 * vblank timestamps.
1317379bc100SJani Nikula 	 *
1318379bc100SJani Nikula 	 * For posterity here is the empirically derived formula
1319379bc100SJani Nikula 	 * that gives us the maximum length of the pipe vblank
1320379bc100SJani Nikula 	 * we can use without causing display corruption. Following
1321379bc100SJani Nikula 	 * this would allow us to have a ticking scanline counter
1322379bc100SJani Nikula 	 * everywhere except during the bottom margin (there the
1323379bc100SJani Nikula 	 * pipe always stops). Ie. this would eliminate the second
1324379bc100SJani Nikula 	 * flat portion of the above graph. However this would also
1325379bc100SJani Nikula 	 * complicate vblank timestamping as the pipe vtotal would
1326379bc100SJani Nikula 	 * no longer match the average rate the pipe runs at during
1327379bc100SJani Nikula 	 * the active portion. Hence following this formula seems
1328379bc100SJani Nikula 	 * more trouble that it's worth.
1329379bc100SJani Nikula 	 *
133007960a4cSLucas De Marchi 	 * if (GRAPHICS_VER(dev_priv) == 4) {
1331379bc100SJani Nikula 	 *	num = cdclk * (tv_mode->oversample >> !tv_mode->progressive);
1332379bc100SJani Nikula 	 *	den = tv_mode->clock;
1333379bc100SJani Nikula 	 * } else {
1334379bc100SJani Nikula 	 *	num = tv_mode->oversample >> !tv_mode->progressive;
1335379bc100SJani Nikula 	 *	den = 1;
1336379bc100SJani Nikula 	 * }
1337379bc100SJani Nikula 	 * max_pipe_vblank_len ~=
1338379bc100SJani Nikula 	 *	(num * tv_htotal * (tv_vblank_len + top_margin)) /
1339379bc100SJani Nikula 	 *	(den * pipe_htotal);
1340379bc100SJani Nikula 	 */
1341379bc100SJani Nikula 	intel_tv_scale_mode_horiz(adjusted_mode, hdisplay,
1342379bc100SJani Nikula 				  conn_state->tv.margins.left,
1343379bc100SJani Nikula 				  conn_state->tv.margins.right);
1344379bc100SJani Nikula 	intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
1345379bc100SJani Nikula 				 tv_conn_state->margins.top,
1346379bc100SJani Nikula 				 tv_conn_state->margins.bottom);
1347379bc100SJani Nikula 	drm_mode_set_crtcinfo(adjusted_mode, 0);
1348379bc100SJani Nikula 	adjusted_mode->name[0] = '\0';
1349379bc100SJani Nikula 
1350379bc100SJani Nikula 	/* pixel counter doesn't work on i965gm TV output */
1351379bc100SJani Nikula 	if (IS_I965GM(dev_priv))
1352af157b76SVille Syrjälä 		pipe_config->mode_flags |=
1353379bc100SJani Nikula 			I915_MODE_FLAG_USE_SCANLINE_COUNTER;
1354379bc100SJani Nikula 
1355379bc100SJani Nikula 	return 0;
1356379bc100SJani Nikula }
1357379bc100SJani Nikula 
1358379bc100SJani Nikula static void
set_tv_mode_timings(struct drm_i915_private * dev_priv,const struct tv_mode * tv_mode,bool burst_ena)1359379bc100SJani Nikula set_tv_mode_timings(struct drm_i915_private *dev_priv,
1360379bc100SJani Nikula 		    const struct tv_mode *tv_mode,
1361379bc100SJani Nikula 		    bool burst_ena)
1362379bc100SJani Nikula {
1363379bc100SJani Nikula 	u32 hctl1, hctl2, hctl3;
1364379bc100SJani Nikula 	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
1365379bc100SJani Nikula 
1366379bc100SJani Nikula 	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
1367379bc100SJani Nikula 		(tv_mode->htotal << TV_HTOTAL_SHIFT);
1368379bc100SJani Nikula 
1369379bc100SJani Nikula 	hctl2 = (tv_mode->hburst_start << 16) |
1370379bc100SJani Nikula 		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
1371379bc100SJani Nikula 
1372379bc100SJani Nikula 	if (burst_ena)
1373379bc100SJani Nikula 		hctl2 |= TV_BURST_ENA;
1374379bc100SJani Nikula 
1375379bc100SJani Nikula 	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
1376379bc100SJani Nikula 		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
1377379bc100SJani Nikula 
1378379bc100SJani Nikula 	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
1379379bc100SJani Nikula 		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
1380379bc100SJani Nikula 		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
1381379bc100SJani Nikula 
1382379bc100SJani Nikula 	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
1383379bc100SJani Nikula 		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
1384379bc100SJani Nikula 		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
1385379bc100SJani Nikula 
1386379bc100SJani Nikula 	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
1387379bc100SJani Nikula 		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
1388379bc100SJani Nikula 		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
1389379bc100SJani Nikula 
1390379bc100SJani Nikula 	if (tv_mode->veq_ena)
1391379bc100SJani Nikula 		vctl3 |= TV_EQUAL_ENA;
1392379bc100SJani Nikula 
1393379bc100SJani Nikula 	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
1394379bc100SJani Nikula 		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
1395379bc100SJani Nikula 
1396379bc100SJani Nikula 	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
1397379bc100SJani Nikula 		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
1398379bc100SJani Nikula 
1399379bc100SJani Nikula 	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
1400379bc100SJani Nikula 		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
1401379bc100SJani Nikula 
1402379bc100SJani Nikula 	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
1403379bc100SJani Nikula 		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
1404379bc100SJani Nikula 
1405988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_H_CTL_1, hctl1);
1406988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_H_CTL_2, hctl2);
1407988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_H_CTL_3, hctl3);
1408988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_1, vctl1);
1409988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_2, vctl2);
1410988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_3, vctl3);
1411988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_4, vctl4);
1412988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_5, vctl5);
1413988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_6, vctl6);
1414988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_V_CTL_7, vctl7);
1415379bc100SJani Nikula }
1416379bc100SJani Nikula 
set_color_conversion(struct drm_i915_private * dev_priv,const struct color_conversion * color_conversion)1417379bc100SJani Nikula static void set_color_conversion(struct drm_i915_private *dev_priv,
1418379bc100SJani Nikula 				 const struct color_conversion *color_conversion)
1419379bc100SJani Nikula {
1420379bc100SJani Nikula 	if (!color_conversion)
1421379bc100SJani Nikula 		return;
1422379bc100SJani Nikula 
1423988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_Y,
1424988ff27bSJani Nikula 		       (color_conversion->ry << 16) | color_conversion->gy);
1425988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_Y2,
1426988ff27bSJani Nikula 		       (color_conversion->by << 16) | color_conversion->ay);
1427988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_U,
1428988ff27bSJani Nikula 		       (color_conversion->ru << 16) | color_conversion->gu);
1429988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_U2,
1430988ff27bSJani Nikula 		       (color_conversion->bu << 16) | color_conversion->au);
1431988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_V,
1432988ff27bSJani Nikula 		       (color_conversion->rv << 16) | color_conversion->gv);
1433988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CSC_V2,
1434988ff27bSJani Nikula 		       (color_conversion->bv << 16) | color_conversion->av);
1435379bc100SJani Nikula }
1436379bc100SJani Nikula 
intel_tv_pre_enable(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)1437ede9771dSVille Syrjälä static void intel_tv_pre_enable(struct intel_atomic_state *state,
1438ede9771dSVille Syrjälä 				struct intel_encoder *encoder,
1439379bc100SJani Nikula 				const struct intel_crtc_state *pipe_config,
1440379bc100SJani Nikula 				const struct drm_connector_state *conn_state)
1441379bc100SJani Nikula {
1442379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1443f15f01a7SVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
1444379bc100SJani Nikula 	struct intel_tv *intel_tv = enc_to_tv(encoder);
1445379bc100SJani Nikula 	const struct intel_tv_connector_state *tv_conn_state =
1446379bc100SJani Nikula 		to_intel_tv_connector_state(conn_state);
1447379bc100SJani Nikula 	const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
1448379bc100SJani Nikula 	u32 tv_ctl, tv_filter_ctl;
1449379bc100SJani Nikula 	u32 scctl1, scctl2, scctl3;
1450379bc100SJani Nikula 	int i, j;
1451379bc100SJani Nikula 	const struct video_levels *video_levels;
1452379bc100SJani Nikula 	const struct color_conversion *color_conversion;
1453379bc100SJani Nikula 	bool burst_ena;
1454379bc100SJani Nikula 	int xpos, ypos;
1455379bc100SJani Nikula 	unsigned int xsize, ysize;
1456379bc100SJani Nikula 
1457379bc100SJani Nikula 	if (!tv_mode)
1458379bc100SJani Nikula 		return;	/* can't happen (mode_prepare prevents this) */
1459379bc100SJani Nikula 
1460988ff27bSJani Nikula 	tv_ctl = intel_de_read(dev_priv, TV_CTL);
1461379bc100SJani Nikula 	tv_ctl &= TV_CTL_SAVE;
1462379bc100SJani Nikula 
1463379bc100SJani Nikula 	switch (intel_tv->type) {
1464379bc100SJani Nikula 	default:
1465379bc100SJani Nikula 	case DRM_MODE_CONNECTOR_Unknown:
1466379bc100SJani Nikula 	case DRM_MODE_CONNECTOR_Composite:
1467379bc100SJani Nikula 		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
1468379bc100SJani Nikula 		video_levels = tv_mode->composite_levels;
1469379bc100SJani Nikula 		color_conversion = tv_mode->composite_color;
1470379bc100SJani Nikula 		burst_ena = tv_mode->burst_ena;
1471379bc100SJani Nikula 		break;
1472379bc100SJani Nikula 	case DRM_MODE_CONNECTOR_Component:
1473379bc100SJani Nikula 		tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
1474379bc100SJani Nikula 		video_levels = &component_levels;
1475379bc100SJani Nikula 		if (tv_mode->burst_ena)
1476379bc100SJani Nikula 			color_conversion = &sdtv_csc_yprpb;
1477379bc100SJani Nikula 		else
1478379bc100SJani Nikula 			color_conversion = &hdtv_csc_yprpb;
1479379bc100SJani Nikula 		burst_ena = false;
1480379bc100SJani Nikula 		break;
1481379bc100SJani Nikula 	case DRM_MODE_CONNECTOR_SVIDEO:
1482379bc100SJani Nikula 		tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
1483379bc100SJani Nikula 		video_levels = tv_mode->svideo_levels;
1484379bc100SJani Nikula 		color_conversion = tv_mode->svideo_color;
1485379bc100SJani Nikula 		burst_ena = tv_mode->burst_ena;
1486379bc100SJani Nikula 		break;
1487379bc100SJani Nikula 	}
1488379bc100SJani Nikula 
1489f15f01a7SVille Syrjälä 	tv_ctl |= TV_ENC_PIPE_SEL(crtc->pipe);
1490379bc100SJani Nikula 
1491379bc100SJani Nikula 	switch (tv_mode->oversample) {
1492379bc100SJani Nikula 	case 8:
1493379bc100SJani Nikula 		tv_ctl |= TV_OVERSAMPLE_8X;
1494379bc100SJani Nikula 		break;
1495379bc100SJani Nikula 	case 4:
1496379bc100SJani Nikula 		tv_ctl |= TV_OVERSAMPLE_4X;
1497379bc100SJani Nikula 		break;
1498379bc100SJani Nikula 	case 2:
1499379bc100SJani Nikula 		tv_ctl |= TV_OVERSAMPLE_2X;
1500379bc100SJani Nikula 		break;
1501379bc100SJani Nikula 	default:
1502379bc100SJani Nikula 		tv_ctl |= TV_OVERSAMPLE_NONE;
1503379bc100SJani Nikula 		break;
1504379bc100SJani Nikula 	}
1505379bc100SJani Nikula 
1506379bc100SJani Nikula 	if (tv_mode->progressive)
1507379bc100SJani Nikula 		tv_ctl |= TV_PROGRESSIVE;
1508379bc100SJani Nikula 	if (tv_mode->trilevel_sync)
1509379bc100SJani Nikula 		tv_ctl |= TV_TRILEVEL_SYNC;
1510379bc100SJani Nikula 	if (tv_mode->pal_burst)
1511379bc100SJani Nikula 		tv_ctl |= TV_PAL_BURST;
1512379bc100SJani Nikula 
1513379bc100SJani Nikula 	scctl1 = 0;
1514379bc100SJani Nikula 	if (tv_mode->dda1_inc)
1515379bc100SJani Nikula 		scctl1 |= TV_SC_DDA1_EN;
1516379bc100SJani Nikula 	if (tv_mode->dda2_inc)
1517379bc100SJani Nikula 		scctl1 |= TV_SC_DDA2_EN;
1518379bc100SJani Nikula 	if (tv_mode->dda3_inc)
1519379bc100SJani Nikula 		scctl1 |= TV_SC_DDA3_EN;
1520379bc100SJani Nikula 	scctl1 |= tv_mode->sc_reset;
1521379bc100SJani Nikula 	if (video_levels)
1522379bc100SJani Nikula 		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
1523379bc100SJani Nikula 	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1524379bc100SJani Nikula 
1525379bc100SJani Nikula 	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1526379bc100SJani Nikula 		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1527379bc100SJani Nikula 
1528379bc100SJani Nikula 	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1529379bc100SJani Nikula 		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1530379bc100SJani Nikula 
1531379bc100SJani Nikula 	/* Enable two fixes for the chips that need them. */
1532379bc100SJani Nikula 	if (IS_I915GM(dev_priv))
1533379bc100SJani Nikula 		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1534379bc100SJani Nikula 
1535379bc100SJani Nikula 	set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
1536379bc100SJani Nikula 
1537988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_SC_CTL_1, scctl1);
1538988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_SC_CTL_2, scctl2);
1539988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_SC_CTL_3, scctl3);
1540379bc100SJani Nikula 
1541379bc100SJani Nikula 	set_color_conversion(dev_priv, color_conversion);
1542379bc100SJani Nikula 
1543005e9537SMatt Roper 	if (DISPLAY_VER(dev_priv) >= 4)
1544988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_CLR_KNOBS, 0x00404000);
1545379bc100SJani Nikula 	else
1546988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_CLR_KNOBS, 0x00606000);
1547379bc100SJani Nikula 
1548379bc100SJani Nikula 	if (video_levels)
1549988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_CLR_LEVEL,
1550988ff27bSJani Nikula 			       ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1551379bc100SJani Nikula 
15528c66081bSVille Syrjälä 	assert_transcoder_disabled(dev_priv, pipe_config->cpu_transcoder);
1553379bc100SJani Nikula 
1554379bc100SJani Nikula 	/* Filter ctl must be set before TV_WIN_SIZE */
1555379bc100SJani Nikula 	tv_filter_ctl = TV_AUTO_SCALE;
1556379bc100SJani Nikula 	if (tv_conn_state->bypass_vfilter)
1557379bc100SJani Nikula 		tv_filter_ctl |= TV_V_FILTER_BYPASS;
1558988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_FILTER_CTL_1, tv_filter_ctl);
1559379bc100SJani Nikula 
1560379bc100SJani Nikula 	xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1561379bc100SJani Nikula 	ysize = intel_tv_mode_vdisplay(tv_mode);
1562379bc100SJani Nikula 
1563379bc100SJani Nikula 	xpos = conn_state->tv.margins.left;
1564379bc100SJani Nikula 	ypos = tv_conn_state->margins.top;
1565379bc100SJani Nikula 	xsize -= (conn_state->tv.margins.left +
1566379bc100SJani Nikula 		  conn_state->tv.margins.right);
1567379bc100SJani Nikula 	ysize -= (tv_conn_state->margins.top +
1568379bc100SJani Nikula 		  tv_conn_state->margins.bottom);
1569988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_WIN_POS, (xpos << 16) | ypos);
1570988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_WIN_SIZE, (xsize << 16) | ysize);
1571379bc100SJani Nikula 
1572379bc100SJani Nikula 	j = 0;
1573379bc100SJani Nikula 	for (i = 0; i < 60; i++)
1574988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_H_LUMA(i),
1575988ff27bSJani Nikula 			       tv_mode->filter_table[j++]);
1576379bc100SJani Nikula 	for (i = 0; i < 60; i++)
1577988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_H_CHROMA(i),
1578988ff27bSJani Nikula 			       tv_mode->filter_table[j++]);
1579379bc100SJani Nikula 	for (i = 0; i < 43; i++)
1580988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_V_LUMA(i),
1581988ff27bSJani Nikula 			       tv_mode->filter_table[j++]);
1582379bc100SJani Nikula 	for (i = 0; i < 43; i++)
1583988ff27bSJani Nikula 		intel_de_write(dev_priv, TV_V_CHROMA(i),
1584988ff27bSJani Nikula 			       tv_mode->filter_table[j++]);
1585988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC,
1586988ff27bSJani Nikula 		       intel_de_read(dev_priv, TV_DAC) & TV_DAC_SAVE);
1587988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CTL, tv_ctl);
1588379bc100SJani Nikula }
1589379bc100SJani Nikula 
1590379bc100SJani Nikula static int
intel_tv_detect_type(struct intel_tv * intel_tv,struct drm_connector * connector)1591379bc100SJani Nikula intel_tv_detect_type(struct intel_tv *intel_tv,
1592379bc100SJani Nikula 		      struct drm_connector *connector)
1593379bc100SJani Nikula {
1594f15f01a7SVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(connector->state->crtc);
1595379bc100SJani Nikula 	struct drm_device *dev = connector->dev;
1596379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(dev);
1597379bc100SJani Nikula 	u32 tv_ctl, save_tv_ctl;
1598379bc100SJani Nikula 	u32 tv_dac, save_tv_dac;
1599379bc100SJani Nikula 	int type;
1600379bc100SJani Nikula 
1601379bc100SJani Nikula 	/* Disable TV interrupts around load detect or we'll recurse */
1602379bc100SJani Nikula 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1603379bc100SJani Nikula 		spin_lock_irq(&dev_priv->irq_lock);
1604379bc100SJani Nikula 		i915_disable_pipestat(dev_priv, 0,
1605379bc100SJani Nikula 				      PIPE_HOTPLUG_INTERRUPT_STATUS |
1606379bc100SJani Nikula 				      PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
1607379bc100SJani Nikula 		spin_unlock_irq(&dev_priv->irq_lock);
1608379bc100SJani Nikula 	}
1609379bc100SJani Nikula 
1610988ff27bSJani Nikula 	save_tv_dac = tv_dac = intel_de_read(dev_priv, TV_DAC);
1611988ff27bSJani Nikula 	save_tv_ctl = tv_ctl = intel_de_read(dev_priv, TV_CTL);
1612379bc100SJani Nikula 
1613379bc100SJani Nikula 	/* Poll for TV detection */
1614379bc100SJani Nikula 	tv_ctl &= ~(TV_ENC_ENABLE | TV_ENC_PIPE_SEL_MASK | TV_TEST_MODE_MASK);
1615379bc100SJani Nikula 	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1616f15f01a7SVille Syrjälä 	tv_ctl |= TV_ENC_PIPE_SEL(crtc->pipe);
1617379bc100SJani Nikula 
1618379bc100SJani Nikula 	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1619379bc100SJani Nikula 	tv_dac |= (TVDAC_STATE_CHG_EN |
1620379bc100SJani Nikula 		   TVDAC_A_SENSE_CTL |
1621379bc100SJani Nikula 		   TVDAC_B_SENSE_CTL |
1622379bc100SJani Nikula 		   TVDAC_C_SENSE_CTL |
1623379bc100SJani Nikula 		   DAC_CTL_OVERRIDE |
1624379bc100SJani Nikula 		   DAC_A_0_7_V |
1625379bc100SJani Nikula 		   DAC_B_0_7_V |
1626379bc100SJani Nikula 		   DAC_C_0_7_V);
1627379bc100SJani Nikula 
1628379bc100SJani Nikula 
1629379bc100SJani Nikula 	/*
1630379bc100SJani Nikula 	 * The TV sense state should be cleared to zero on cantiga platform. Otherwise
1631379bc100SJani Nikula 	 * the TV is misdetected. This is hardware requirement.
1632379bc100SJani Nikula 	 */
1633379bc100SJani Nikula 	if (IS_GM45(dev_priv))
1634379bc100SJani Nikula 		tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
1635379bc100SJani Nikula 			    TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
1636379bc100SJani Nikula 
1637988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CTL, tv_ctl);
1638988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC, tv_dac);
1639988ff27bSJani Nikula 	intel_de_posting_read(dev_priv, TV_DAC);
1640379bc100SJani Nikula 
16417b06894bSJani Nikula 	intel_crtc_wait_for_next_vblank(crtc);
1642379bc100SJani Nikula 
1643379bc100SJani Nikula 	type = -1;
1644988ff27bSJani Nikula 	tv_dac = intel_de_read(dev_priv, TV_DAC);
1645025c2e19SWambui Karuga 	drm_dbg_kms(&dev_priv->drm, "TV detected: %x, %x\n", tv_ctl, tv_dac);
1646379bc100SJani Nikula 	/*
1647379bc100SJani Nikula 	 *  A B C
1648379bc100SJani Nikula 	 *  0 1 1 Composite
1649379bc100SJani Nikula 	 *  1 0 X svideo
1650379bc100SJani Nikula 	 *  0 0 0 Component
1651379bc100SJani Nikula 	 */
1652379bc100SJani Nikula 	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1653025c2e19SWambui Karuga 		drm_dbg_kms(&dev_priv->drm,
1654025c2e19SWambui Karuga 			    "Detected Composite TV connection\n");
1655379bc100SJani Nikula 		type = DRM_MODE_CONNECTOR_Composite;
1656379bc100SJani Nikula 	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1657025c2e19SWambui Karuga 		drm_dbg_kms(&dev_priv->drm,
1658025c2e19SWambui Karuga 			    "Detected S-Video TV connection\n");
1659379bc100SJani Nikula 		type = DRM_MODE_CONNECTOR_SVIDEO;
1660379bc100SJani Nikula 	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1661025c2e19SWambui Karuga 		drm_dbg_kms(&dev_priv->drm,
1662025c2e19SWambui Karuga 			    "Detected Component TV connection\n");
1663379bc100SJani Nikula 		type = DRM_MODE_CONNECTOR_Component;
1664379bc100SJani Nikula 	} else {
1665025c2e19SWambui Karuga 		drm_dbg_kms(&dev_priv->drm, "Unrecognised TV connection\n");
1666379bc100SJani Nikula 		type = -1;
1667379bc100SJani Nikula 	}
1668379bc100SJani Nikula 
1669988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1670988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_CTL, save_tv_ctl);
1671988ff27bSJani Nikula 	intel_de_posting_read(dev_priv, TV_CTL);
1672379bc100SJani Nikula 
1673379bc100SJani Nikula 	/* For unknown reasons the hw barfs if we don't do this vblank wait. */
16747b06894bSJani Nikula 	intel_crtc_wait_for_next_vblank(crtc);
1675379bc100SJani Nikula 
1676379bc100SJani Nikula 	/* Restore interrupt config */
1677379bc100SJani Nikula 	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1678379bc100SJani Nikula 		spin_lock_irq(&dev_priv->irq_lock);
1679379bc100SJani Nikula 		i915_enable_pipestat(dev_priv, 0,
1680379bc100SJani Nikula 				     PIPE_HOTPLUG_INTERRUPT_STATUS |
1681379bc100SJani Nikula 				     PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
1682379bc100SJani Nikula 		spin_unlock_irq(&dev_priv->irq_lock);
1683379bc100SJani Nikula 	}
1684379bc100SJani Nikula 
1685379bc100SJani Nikula 	return type;
1686379bc100SJani Nikula }
1687379bc100SJani Nikula 
1688379bc100SJani Nikula /*
1689379bc100SJani Nikula  * Here we set accurate tv format according to connector type
1690379bc100SJani Nikula  * i.e Component TV should not be assigned by NTSC or PAL
1691379bc100SJani Nikula  */
intel_tv_find_better_format(struct drm_connector * connector)1692379bc100SJani Nikula static void intel_tv_find_better_format(struct drm_connector *connector)
1693379bc100SJani Nikula {
169443a6d19cSVille Syrjälä 	struct intel_tv *intel_tv = intel_attached_tv(to_intel_connector(connector));
1695379bc100SJani Nikula 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1696379bc100SJani Nikula 	int i;
1697379bc100SJani Nikula 
1698379bc100SJani Nikula 	/* Component supports everything so we can keep the current mode */
1699379bc100SJani Nikula 	if (intel_tv->type == DRM_MODE_CONNECTOR_Component)
1700379bc100SJani Nikula 		return;
1701379bc100SJani Nikula 
1702379bc100SJani Nikula 	/* If the current mode is fine don't change it */
1703379bc100SJani Nikula 	if (!tv_mode->component_only)
1704379bc100SJani Nikula 		return;
1705379bc100SJani Nikula 
1706379bc100SJani Nikula 	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
1707379bc100SJani Nikula 		tv_mode = &tv_modes[i];
1708379bc100SJani Nikula 
1709379bc100SJani Nikula 		if (!tv_mode->component_only)
1710379bc100SJani Nikula 			break;
1711379bc100SJani Nikula 	}
1712379bc100SJani Nikula 
1713*16bc939fSMaxime Ripard 	connector->state->tv.legacy_mode = i;
1714379bc100SJani Nikula }
1715379bc100SJani Nikula 
1716379bc100SJani Nikula static int
intel_tv_detect(struct drm_connector * connector,struct drm_modeset_acquire_ctx * ctx,bool force)1717379bc100SJani Nikula intel_tv_detect(struct drm_connector *connector,
1718379bc100SJani Nikula 		struct drm_modeset_acquire_ctx *ctx,
1719379bc100SJani Nikula 		bool force)
1720379bc100SJani Nikula {
17217bee031dSJani Nikula 	struct drm_i915_private *i915 = to_i915(connector->dev);
172243a6d19cSVille Syrjälä 	struct intel_tv *intel_tv = intel_attached_tv(to_intel_connector(connector));
1723379bc100SJani Nikula 	enum drm_connector_status status;
1724379bc100SJani Nikula 	int type;
1725379bc100SJani Nikula 
17267bee031dSJani Nikula 	drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] force=%d\n",
17277bee031dSJani Nikula 		    connector->base.id, connector->name, force);
1728379bc100SJani Nikula 
1729b81dddb9SVille Syrjälä 	if (!INTEL_DISPLAY_ENABLED(i915))
1730b81dddb9SVille Syrjälä 		return connector_status_disconnected;
1731b81dddb9SVille Syrjälä 
1732379bc100SJani Nikula 	if (force) {
17338902a55dSJani Nikula 		struct drm_atomic_state *state;
1734379bc100SJani Nikula 
17358902a55dSJani Nikula 		state = intel_load_detect_get_pipe(connector, ctx);
17368902a55dSJani Nikula 		if (IS_ERR(state))
17378902a55dSJani Nikula 			return PTR_ERR(state);
1738379bc100SJani Nikula 
17398902a55dSJani Nikula 		if (state) {
1740379bc100SJani Nikula 			type = intel_tv_detect_type(intel_tv, connector);
17418902a55dSJani Nikula 			intel_load_detect_release_pipe(connector, state, ctx);
1742379bc100SJani Nikula 			status = type < 0 ?
1743379bc100SJani Nikula 				connector_status_disconnected :
1744379bc100SJani Nikula 				connector_status_connected;
17458902a55dSJani Nikula 		} else {
1746379bc100SJani Nikula 			status = connector_status_unknown;
17478902a55dSJani Nikula 		}
1748379bc100SJani Nikula 
1749379bc100SJani Nikula 		if (status == connector_status_connected) {
1750379bc100SJani Nikula 			intel_tv->type = type;
1751379bc100SJani Nikula 			intel_tv_find_better_format(connector);
1752379bc100SJani Nikula 		}
1753379bc100SJani Nikula 
1754379bc100SJani Nikula 		return status;
1755379bc100SJani Nikula 	} else
1756379bc100SJani Nikula 		return connector->status;
1757379bc100SJani Nikula }
1758379bc100SJani Nikula 
1759379bc100SJani Nikula static const struct input_res {
1760379bc100SJani Nikula 	u16 w, h;
1761379bc100SJani Nikula } input_res_table[] = {
1762379bc100SJani Nikula 	{ 640, 480 },
1763379bc100SJani Nikula 	{ 800, 600 },
1764379bc100SJani Nikula 	{ 1024, 768 },
1765379bc100SJani Nikula 	{ 1280, 1024 },
1766379bc100SJani Nikula 	{ 848, 480 },
1767379bc100SJani Nikula 	{ 1280, 720 },
1768379bc100SJani Nikula 	{ 1920, 1080 },
1769379bc100SJani Nikula };
1770379bc100SJani Nikula 
1771379bc100SJani Nikula /* Choose preferred mode according to line number of TV format */
1772379bc100SJani Nikula static bool
intel_tv_is_preferred_mode(const struct drm_display_mode * mode,const struct tv_mode * tv_mode)1773379bc100SJani Nikula intel_tv_is_preferred_mode(const struct drm_display_mode *mode,
1774379bc100SJani Nikula 			   const struct tv_mode *tv_mode)
1775379bc100SJani Nikula {
1776379bc100SJani Nikula 	int vdisplay = intel_tv_mode_vdisplay(tv_mode);
1777379bc100SJani Nikula 
1778379bc100SJani Nikula 	/* prefer 480 line modes for all SD TV modes */
1779379bc100SJani Nikula 	if (vdisplay <= 576)
1780379bc100SJani Nikula 		vdisplay = 480;
1781379bc100SJani Nikula 
1782379bc100SJani Nikula 	return vdisplay == mode->vdisplay;
1783379bc100SJani Nikula }
1784379bc100SJani Nikula 
1785379bc100SJani Nikula static void
intel_tv_set_mode_type(struct drm_display_mode * mode,const struct tv_mode * tv_mode)1786379bc100SJani Nikula intel_tv_set_mode_type(struct drm_display_mode *mode,
1787379bc100SJani Nikula 		       const struct tv_mode *tv_mode)
1788379bc100SJani Nikula {
1789379bc100SJani Nikula 	mode->type = DRM_MODE_TYPE_DRIVER;
1790379bc100SJani Nikula 
1791379bc100SJani Nikula 	if (intel_tv_is_preferred_mode(mode, tv_mode))
1792379bc100SJani Nikula 		mode->type |= DRM_MODE_TYPE_PREFERRED;
1793379bc100SJani Nikula }
1794379bc100SJani Nikula 
1795379bc100SJani Nikula static int
intel_tv_get_modes(struct drm_connector * connector)1796379bc100SJani Nikula intel_tv_get_modes(struct drm_connector *connector)
1797379bc100SJani Nikula {
1798379bc100SJani Nikula 	struct drm_i915_private *dev_priv = to_i915(connector->dev);
1799379bc100SJani Nikula 	const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1800379bc100SJani Nikula 	int i, count = 0;
1801379bc100SJani Nikula 
1802379bc100SJani Nikula 	for (i = 0; i < ARRAY_SIZE(input_res_table); i++) {
1803379bc100SJani Nikula 		const struct input_res *input = &input_res_table[i];
1804379bc100SJani Nikula 		struct drm_display_mode *mode;
1805379bc100SJani Nikula 
1806379bc100SJani Nikula 		if (input->w > 1024 &&
1807379bc100SJani Nikula 		    !tv_mode->progressive &&
1808379bc100SJani Nikula 		    !tv_mode->component_only)
1809379bc100SJani Nikula 			continue;
1810379bc100SJani Nikula 
1811379bc100SJani Nikula 		/* no vertical scaling with wide sources on gen3 */
181293e7e61eSLucas De Marchi 		if (DISPLAY_VER(dev_priv) == 3 && input->w > 1024 &&
1813379bc100SJani Nikula 		    input->h > intel_tv_mode_vdisplay(tv_mode))
1814379bc100SJani Nikula 			continue;
1815379bc100SJani Nikula 
1816379bc100SJani Nikula 		mode = drm_mode_create(connector->dev);
1817379bc100SJani Nikula 		if (!mode)
1818379bc100SJani Nikula 			continue;
1819379bc100SJani Nikula 
1820379bc100SJani Nikula 		/*
1821379bc100SJani Nikula 		 * We take the TV mode and scale it to look
1822379bc100SJani Nikula 		 * like it had the expected h/vdisplay. This
1823379bc100SJani Nikula 		 * provides the most information to userspace
1824379bc100SJani Nikula 		 * about the actual timings of the mode. We
1825379bc100SJani Nikula 		 * do ignore the margins though.
1826379bc100SJani Nikula 		 */
18271bba5543SVille Syrjälä 		intel_tv_mode_to_mode(mode, tv_mode, tv_mode->clock);
1828379bc100SJani Nikula 		if (count == 0) {
1829f01bae2dSVille Syrjälä 			drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n",
1830f01bae2dSVille Syrjälä 				    DRM_MODE_ARG(mode));
1831379bc100SJani Nikula 		}
1832379bc100SJani Nikula 		intel_tv_scale_mode_horiz(mode, input->w, 0, 0);
1833379bc100SJani Nikula 		intel_tv_scale_mode_vert(mode, input->h, 0, 0);
1834379bc100SJani Nikula 		intel_tv_set_mode_type(mode, tv_mode);
1835379bc100SJani Nikula 
1836379bc100SJani Nikula 		drm_mode_set_name(mode);
1837379bc100SJani Nikula 
1838379bc100SJani Nikula 		drm_mode_probed_add(connector, mode);
1839379bc100SJani Nikula 		count++;
1840379bc100SJani Nikula 	}
1841379bc100SJani Nikula 
1842379bc100SJani Nikula 	return count;
1843379bc100SJani Nikula }
1844379bc100SJani Nikula 
1845379bc100SJani Nikula static const struct drm_connector_funcs intel_tv_connector_funcs = {
1846379bc100SJani Nikula 	.late_register = intel_connector_register,
1847379bc100SJani Nikula 	.early_unregister = intel_connector_unregister,
1848379bc100SJani Nikula 	.destroy = intel_connector_destroy,
1849379bc100SJani Nikula 	.fill_modes = drm_helper_probe_single_connector_modes,
1850379bc100SJani Nikula 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
1851379bc100SJani Nikula 	.atomic_duplicate_state = intel_tv_connector_duplicate_state,
1852379bc100SJani Nikula };
1853379bc100SJani Nikula 
intel_tv_atomic_check(struct drm_connector * connector,struct drm_atomic_state * state)1854379bc100SJani Nikula static int intel_tv_atomic_check(struct drm_connector *connector,
1855417f2544SDave Airlie 				 struct drm_atomic_state *state)
1856379bc100SJani Nikula {
1857417f2544SDave Airlie 	struct drm_connector_state *new_state;
1858379bc100SJani Nikula 	struct drm_crtc_state *new_crtc_state;
1859379bc100SJani Nikula 	struct drm_connector_state *old_state;
1860379bc100SJani Nikula 
1861417f2544SDave Airlie 	new_state = drm_atomic_get_new_connector_state(state, connector);
1862379bc100SJani Nikula 	if (!new_state->crtc)
1863379bc100SJani Nikula 		return 0;
1864379bc100SJani Nikula 
1865417f2544SDave Airlie 	old_state = drm_atomic_get_old_connector_state(state, connector);
1866417f2544SDave Airlie 	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
1867379bc100SJani Nikula 
1868*16bc939fSMaxime Ripard 	if (old_state->tv.legacy_mode != new_state->tv.legacy_mode ||
1869379bc100SJani Nikula 	    old_state->tv.margins.left != new_state->tv.margins.left ||
1870379bc100SJani Nikula 	    old_state->tv.margins.right != new_state->tv.margins.right ||
1871379bc100SJani Nikula 	    old_state->tv.margins.top != new_state->tv.margins.top ||
1872379bc100SJani Nikula 	    old_state->tv.margins.bottom != new_state->tv.margins.bottom) {
1873379bc100SJani Nikula 		/* Force a modeset. */
1874379bc100SJani Nikula 
1875379bc100SJani Nikula 		new_crtc_state->connectors_changed = true;
1876379bc100SJani Nikula 	}
1877379bc100SJani Nikula 
1878379bc100SJani Nikula 	return 0;
1879379bc100SJani Nikula }
1880379bc100SJani Nikula 
1881379bc100SJani Nikula static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1882379bc100SJani Nikula 	.detect_ctx = intel_tv_detect,
1883379bc100SJani Nikula 	.mode_valid = intel_tv_mode_valid,
1884379bc100SJani Nikula 	.get_modes = intel_tv_get_modes,
1885379bc100SJani Nikula 	.atomic_check = intel_tv_atomic_check,
1886379bc100SJani Nikula };
1887379bc100SJani Nikula 
1888379bc100SJani Nikula static const struct drm_encoder_funcs intel_tv_enc_funcs = {
1889379bc100SJani Nikula 	.destroy = intel_encoder_destroy,
1890379bc100SJani Nikula };
1891379bc100SJani Nikula 
intel_tv_add_properties(struct drm_connector * connector)1892d7e4a257SVille Syrjälä static void intel_tv_add_properties(struct drm_connector *connector)
1893d7e4a257SVille Syrjälä {
1894d7e4a257SVille Syrjälä 	struct drm_i915_private *i915 = to_i915(connector->dev);
1895d7e4a257SVille Syrjälä 	struct drm_connector_state *conn_state = connector->state;
1896d7e4a257SVille Syrjälä 	const char *tv_format_names[ARRAY_SIZE(tv_modes)];
1897d7e4a257SVille Syrjälä 	int i;
1898d7e4a257SVille Syrjälä 
1899d7e4a257SVille Syrjälä 	/* BIOS margin values */
1900d7e4a257SVille Syrjälä 	conn_state->tv.margins.left = 54;
1901d7e4a257SVille Syrjälä 	conn_state->tv.margins.top = 36;
1902d7e4a257SVille Syrjälä 	conn_state->tv.margins.right = 46;
1903d7e4a257SVille Syrjälä 	conn_state->tv.margins.bottom = 37;
1904d7e4a257SVille Syrjälä 
1905*16bc939fSMaxime Ripard 	conn_state->tv.legacy_mode = 0;
1906d7e4a257SVille Syrjälä 
1907d7e4a257SVille Syrjälä 	/* Create TV properties then attach current values */
1908d7e4a257SVille Syrjälä 	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
1909d7e4a257SVille Syrjälä 		/* 1080p50/1080p60 not supported on gen3 */
1910d7e4a257SVille Syrjälä 		if (DISPLAY_VER(i915) == 3 && tv_modes[i].oversample == 1)
1911d7e4a257SVille Syrjälä 			break;
1912d7e4a257SVille Syrjälä 
1913d7e4a257SVille Syrjälä 		tv_format_names[i] = tv_modes[i].name;
1914d7e4a257SVille Syrjälä 	}
191580ed86d4SMaxime Ripard 	drm_mode_create_tv_properties_legacy(&i915->drm, i, tv_format_names);
1916d7e4a257SVille Syrjälä 
1917d7e4a257SVille Syrjälä 	drm_object_attach_property(&connector->base,
19181fd4a5a3SMaxime Ripard 				   i915->drm.mode_config.legacy_tv_mode_property,
1919*16bc939fSMaxime Ripard 				   conn_state->tv.legacy_mode);
1920d7e4a257SVille Syrjälä 	drm_object_attach_property(&connector->base,
1921d7e4a257SVille Syrjälä 				   i915->drm.mode_config.tv_left_margin_property,
1922d7e4a257SVille Syrjälä 				   conn_state->tv.margins.left);
1923d7e4a257SVille Syrjälä 	drm_object_attach_property(&connector->base,
1924d7e4a257SVille Syrjälä 				   i915->drm.mode_config.tv_top_margin_property,
1925d7e4a257SVille Syrjälä 				   conn_state->tv.margins.top);
1926d7e4a257SVille Syrjälä 	drm_object_attach_property(&connector->base,
1927d7e4a257SVille Syrjälä 				   i915->drm.mode_config.tv_right_margin_property,
1928d7e4a257SVille Syrjälä 				   conn_state->tv.margins.right);
1929d7e4a257SVille Syrjälä 	drm_object_attach_property(&connector->base,
1930d7e4a257SVille Syrjälä 				   i915->drm.mode_config.tv_bottom_margin_property,
1931d7e4a257SVille Syrjälä 				   conn_state->tv.margins.bottom);
1932d7e4a257SVille Syrjälä }
1933d7e4a257SVille Syrjälä 
1934379bc100SJani Nikula void
intel_tv_init(struct drm_i915_private * dev_priv)1935379bc100SJani Nikula intel_tv_init(struct drm_i915_private *dev_priv)
1936379bc100SJani Nikula {
1937379bc100SJani Nikula 	struct drm_connector *connector;
1938379bc100SJani Nikula 	struct intel_tv *intel_tv;
1939379bc100SJani Nikula 	struct intel_encoder *intel_encoder;
1940379bc100SJani Nikula 	struct intel_connector *intel_connector;
1941379bc100SJani Nikula 	u32 tv_dac_on, tv_dac_off, save_tv_dac;
1942379bc100SJani Nikula 
1943988ff27bSJani Nikula 	if ((intel_de_read(dev_priv, TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1944379bc100SJani Nikula 		return;
1945379bc100SJani Nikula 
1946379bc100SJani Nikula 	if (!intel_bios_is_tv_present(dev_priv)) {
1947025c2e19SWambui Karuga 		drm_dbg_kms(&dev_priv->drm, "Integrated TV is not present.\n");
1948379bc100SJani Nikula 		return;
1949379bc100SJani Nikula 	}
1950379bc100SJani Nikula 
1951379bc100SJani Nikula 	/*
1952379bc100SJani Nikula 	 * Sanity check the TV output by checking to see if the
1953379bc100SJani Nikula 	 * DAC register holds a value
1954379bc100SJani Nikula 	 */
1955988ff27bSJani Nikula 	save_tv_dac = intel_de_read(dev_priv, TV_DAC);
1956379bc100SJani Nikula 
1957988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1958988ff27bSJani Nikula 	tv_dac_on = intel_de_read(dev_priv, TV_DAC);
1959379bc100SJani Nikula 
1960988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1961988ff27bSJani Nikula 	tv_dac_off = intel_de_read(dev_priv, TV_DAC);
1962379bc100SJani Nikula 
1963988ff27bSJani Nikula 	intel_de_write(dev_priv, TV_DAC, save_tv_dac);
1964379bc100SJani Nikula 
1965379bc100SJani Nikula 	/*
1966379bc100SJani Nikula 	 * If the register does not hold the state change enable
1967379bc100SJani Nikula 	 * bit, (either as a 0 or a 1), assume it doesn't really
1968379bc100SJani Nikula 	 * exist
1969379bc100SJani Nikula 	 */
1970379bc100SJani Nikula 	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1971379bc100SJani Nikula 	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1972379bc100SJani Nikula 		return;
1973379bc100SJani Nikula 
1974379bc100SJani Nikula 	intel_tv = kzalloc(sizeof(*intel_tv), GFP_KERNEL);
1975379bc100SJani Nikula 	if (!intel_tv) {
1976379bc100SJani Nikula 		return;
1977379bc100SJani Nikula 	}
1978379bc100SJani Nikula 
1979379bc100SJani Nikula 	intel_connector = intel_connector_alloc();
1980379bc100SJani Nikula 	if (!intel_connector) {
1981379bc100SJani Nikula 		kfree(intel_tv);
1982379bc100SJani Nikula 		return;
1983379bc100SJani Nikula 	}
1984379bc100SJani Nikula 
1985379bc100SJani Nikula 	intel_encoder = &intel_tv->base;
1986379bc100SJani Nikula 	connector = &intel_connector->base;
1987379bc100SJani Nikula 
1988379bc100SJani Nikula 	/*
1989379bc100SJani Nikula 	 * The documentation, for the older chipsets at least, recommend
1990379bc100SJani Nikula 	 * using a polling method rather than hotplug detection for TVs.
1991379bc100SJani Nikula 	 * This is because in order to perform the hotplug detection, the PLLs
1992379bc100SJani Nikula 	 * for the TV must be kept alive increasing power drain and starving
1993379bc100SJani Nikula 	 * bandwidth from other encoders. Notably for instance, it causes
1994379bc100SJani Nikula 	 * pipe underruns on Crestline when this encoder is supposedly idle.
1995379bc100SJani Nikula 	 *
1996379bc100SJani Nikula 	 * More recent chipsets favour HDMI rather than integrated S-Video.
1997379bc100SJani Nikula 	 */
1998379bc100SJani Nikula 	intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1999379bc100SJani Nikula 
20003703060dSAndrzej Hajda 	drm_connector_init(&dev_priv->drm, connector, &intel_tv_connector_funcs,
2001379bc100SJani Nikula 			   DRM_MODE_CONNECTOR_SVIDEO);
2002379bc100SJani Nikula 
20033703060dSAndrzej Hajda 	drm_encoder_init(&dev_priv->drm, &intel_encoder->base, &intel_tv_enc_funcs,
2004379bc100SJani Nikula 			 DRM_MODE_ENCODER_TVDAC, "TV");
2005379bc100SJani Nikula 
2006379bc100SJani Nikula 	intel_encoder->compute_config = intel_tv_compute_config;
2007379bc100SJani Nikula 	intel_encoder->get_config = intel_tv_get_config;
2008379bc100SJani Nikula 	intel_encoder->pre_enable = intel_tv_pre_enable;
2009379bc100SJani Nikula 	intel_encoder->enable = intel_enable_tv;
2010379bc100SJani Nikula 	intel_encoder->disable = intel_disable_tv;
2011379bc100SJani Nikula 	intel_encoder->get_hw_state = intel_tv_get_hw_state;
2012379bc100SJani Nikula 	intel_connector->get_hw_state = intel_connector_get_hw_state;
2013379bc100SJani Nikula 
2014379bc100SJani Nikula 	intel_connector_attach_encoder(intel_connector, intel_encoder);
2015379bc100SJani Nikula 
2016379bc100SJani Nikula 	intel_encoder->type = INTEL_OUTPUT_TVOUT;
2017379bc100SJani Nikula 	intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
2018379bc100SJani Nikula 	intel_encoder->port = PORT_NONE;
201934053ee1SVille Syrjälä 	intel_encoder->pipe_mask = ~0;
2020379bc100SJani Nikula 	intel_encoder->cloneable = 0;
2021379bc100SJani Nikula 	intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
2022379bc100SJani Nikula 
2023379bc100SJani Nikula 	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
2024379bc100SJani Nikula 
2025d7e4a257SVille Syrjälä 	intel_tv_add_properties(connector);
2026379bc100SJani Nikula }
2027