1235783Skib/*
2235783Skib * Copyright �� 2006-2008 Intel Corporation
3235783Skib *   Jesse Barnes <jesse.barnes@intel.com>
4235783Skib *
5235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
6235783Skib * copy of this software and associated documentation files (the "Software"),
7235783Skib * to deal in the Software without restriction, including without limitation
8235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9235783Skib * and/or sell copies of the Software, and to permit persons to whom the
10235783Skib * Software is furnished to do so, subject to the following conditions:
11235783Skib *
12235783Skib * The above copyright notice and this permission notice (including the next
13235783Skib * paragraph) shall be included in all copies or substantial portions of the
14235783Skib * Software.
15235783Skib *
16235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19235783Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20235783Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21235783Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22235783Skib * DEALINGS IN THE SOFTWARE.
23235783Skib *
24235783Skib * Authors:
25235783Skib *    Eric Anholt <eric@anholt.net>
26235783Skib *
27235783Skib */
28235783Skib
29235783Skib/** @file
30235783Skib * Integrated TV-out support for the 915GM and 945GM.
31235783Skib */
32235783Skib
33235783Skib#include <sys/cdefs.h>
34235783Skib__FBSDID("$FreeBSD$");
35235783Skib
36235783Skib#include <dev/drm2/drmP.h>
37235783Skib#include <dev/drm2/drm_crtc.h>
38235783Skib#include <dev/drm2/drm_edid.h>
39296548Sdumbbell#include <dev/drm2/i915/intel_drv.h>
40235783Skib#include <dev/drm2/i915/i915_drm.h>
41235783Skib#include <dev/drm2/i915/i915_drv.h>
42235783Skib
43235783Skibenum tv_margin {
44235783Skib	TV_MARGIN_LEFT, TV_MARGIN_TOP,
45235783Skib	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
46235783Skib};
47235783Skib
48235783Skib/** Private structure for the integrated TV support */
49235783Skibstruct intel_tv {
50235783Skib	struct intel_encoder base;
51235783Skib
52235783Skib	int type;
53235783Skib	const char *tv_format;
54235783Skib	int margin[4];
55235783Skib	u32 save_TV_H_CTL_1;
56235783Skib	u32 save_TV_H_CTL_2;
57235783Skib	u32 save_TV_H_CTL_3;
58235783Skib	u32 save_TV_V_CTL_1;
59235783Skib	u32 save_TV_V_CTL_2;
60235783Skib	u32 save_TV_V_CTL_3;
61235783Skib	u32 save_TV_V_CTL_4;
62235783Skib	u32 save_TV_V_CTL_5;
63235783Skib	u32 save_TV_V_CTL_6;
64235783Skib	u32 save_TV_V_CTL_7;
65235783Skib	u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
66235783Skib
67235783Skib	u32 save_TV_CSC_Y;
68235783Skib	u32 save_TV_CSC_Y2;
69235783Skib	u32 save_TV_CSC_U;
70235783Skib	u32 save_TV_CSC_U2;
71235783Skib	u32 save_TV_CSC_V;
72235783Skib	u32 save_TV_CSC_V2;
73235783Skib	u32 save_TV_CLR_KNOBS;
74235783Skib	u32 save_TV_CLR_LEVEL;
75235783Skib	u32 save_TV_WIN_POS;
76235783Skib	u32 save_TV_WIN_SIZE;
77235783Skib	u32 save_TV_FILTER_CTL_1;
78235783Skib	u32 save_TV_FILTER_CTL_2;
79235783Skib	u32 save_TV_FILTER_CTL_3;
80235783Skib
81235783Skib	u32 save_TV_H_LUMA[60];
82235783Skib	u32 save_TV_H_CHROMA[60];
83235783Skib	u32 save_TV_V_LUMA[43];
84235783Skib	u32 save_TV_V_CHROMA[43];
85235783Skib
86235783Skib	u32 save_TV_DAC;
87235783Skib	u32 save_TV_CTL;
88235783Skib};
89235783Skib
90235783Skibstruct video_levels {
91235783Skib	int blank, black, burst;
92235783Skib};
93235783Skib
94235783Skibstruct color_conversion {
95235783Skib	u16 ry, gy, by, ay;
96235783Skib	u16 ru, gu, bu, au;
97235783Skib	u16 rv, gv, bv, av;
98235783Skib};
99235783Skib
100235783Skibstatic const u32 filter_table[] = {
101235783Skib	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
102235783Skib	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
103235783Skib	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
104235783Skib	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
105235783Skib	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
106235783Skib	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
107235783Skib	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
108235783Skib	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
109235783Skib	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
110235783Skib	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
111235783Skib	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
112235783Skib	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
113235783Skib	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
114235783Skib	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
115235783Skib	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
116235783Skib	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
117235783Skib	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
118235783Skib	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
119235783Skib	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
120235783Skib	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
121235783Skib	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
122235783Skib	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
123235783Skib	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
124235783Skib	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
125235783Skib	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
126235783Skib	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
127235783Skib	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
128235783Skib	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
129235783Skib	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
130235783Skib	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
131235783Skib	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
132235783Skib	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
133235783Skib	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
134235783Skib	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
135235783Skib	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
136235783Skib	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
137235783Skib	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
138235783Skib	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
139235783Skib	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
140235783Skib	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
141235783Skib	0x28003100, 0x28002F00, 0x00003100, 0x36403000,
142235783Skib	0x2D002CC0, 0x30003640, 0x2D0036C0,
143235783Skib	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
144235783Skib	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
145235783Skib	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
146235783Skib	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
147235783Skib	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
148235783Skib	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
149235783Skib	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
150235783Skib	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
151235783Skib	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
152235783Skib	0x28003100, 0x28002F00, 0x00003100,
153235783Skib};
154235783Skib
155235783Skib/*
156235783Skib * Color conversion values have 3 separate fixed point formats:
157235783Skib *
158235783Skib * 10 bit fields (ay, au)
159235783Skib *   1.9 fixed point (b.bbbbbbbbb)
160235783Skib * 11 bit fields (ry, by, ru, gu, gv)
161235783Skib *   exp.mantissa (ee.mmmmmmmmm)
162235783Skib *   ee = 00 = 10^-1 (0.mmmmmmmmm)
163235783Skib *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
164235783Skib *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
165235783Skib *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
166235783Skib * 12 bit fields (gy, rv, bu)
167235783Skib *   exp.mantissa (eee.mmmmmmmmm)
168235783Skib *   eee = 000 = 10^-1 (0.mmmmmmmmm)
169235783Skib *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
170235783Skib *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
171235783Skib *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
172235783Skib *   eee = 100 = reserved
173235783Skib *   eee = 101 = reserved
174235783Skib *   eee = 110 = reserved
175235783Skib *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
176235783Skib *
177235783Skib * Saturation and contrast are 8 bits, with their own representation:
178235783Skib * 8 bit field (saturation, contrast)
179235783Skib *   exp.mantissa (ee.mmmmmm)
180235783Skib *   ee = 00 = 10^-1 (0.mmmmmm)
181235783Skib *   ee = 01 = 10^0 (m.mmmmm)
182235783Skib *   ee = 10 = 10^1 (mm.mmmm)
183235783Skib *   ee = 11 = 10^2 (mmm.mmm)
184235783Skib *
185235783Skib * Simple conversion function:
186235783Skib *
187235783Skib * static u32
188235783Skib * float_to_csc_11(float f)
189235783Skib * {
190235783Skib *     u32 exp;
191235783Skib *     u32 mant;
192235783Skib *     u32 ret;
193235783Skib *
194235783Skib *     if (f < 0)
195235783Skib *         f = -f;
196235783Skib *
197235783Skib *     if (f >= 1) {
198235783Skib *         exp = 0x7;
199235783Skib *	   mant = 1 << 8;
200235783Skib *     } else {
201235783Skib *         for (exp = 0; exp < 3 && f < 0.5; exp++)
202235783Skib *	   f *= 2.0;
203235783Skib *         mant = (f * (1 << 9) + 0.5);
204235783Skib *         if (mant >= (1 << 9))
205235783Skib *             mant = (1 << 9) - 1;
206235783Skib *     }
207235783Skib *     ret = (exp << 9) | mant;
208235783Skib *     return ret;
209235783Skib * }
210235783Skib */
211235783Skib
212235783Skib/*
213235783Skib * Behold, magic numbers!  If we plant them they might grow a big
214235783Skib * s-video cable to the sky... or something.
215235783Skib *
216235783Skib * Pre-converted to appropriate hex value.
217235783Skib */
218235783Skib
219235783Skib/*
220235783Skib * PAL & NTSC values for composite & s-video connections
221235783Skib */
222235783Skibstatic const struct color_conversion ntsc_m_csc_composite = {
223235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
224235783Skib	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
225235783Skib	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
226235783Skib};
227235783Skib
228235783Skibstatic const struct video_levels ntsc_m_levels_composite = {
229235783Skib	.blank = 225, .black = 267, .burst = 113,
230235783Skib};
231235783Skib
232235783Skibstatic const struct color_conversion ntsc_m_csc_svideo = {
233235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
234235783Skib	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
235235783Skib	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
236235783Skib};
237235783Skib
238235783Skibstatic const struct video_levels ntsc_m_levels_svideo = {
239235783Skib	.blank = 266, .black = 316, .burst = 133,
240235783Skib};
241235783Skib
242235783Skibstatic const struct color_conversion ntsc_j_csc_composite = {
243235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
244235783Skib	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
245235783Skib	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
246235783Skib};
247235783Skib
248235783Skibstatic const struct video_levels ntsc_j_levels_composite = {
249235783Skib	.blank = 225, .black = 225, .burst = 113,
250235783Skib};
251235783Skib
252235783Skibstatic const struct color_conversion ntsc_j_csc_svideo = {
253235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
254235783Skib	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
255235783Skib	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
256235783Skib};
257235783Skib
258235783Skibstatic const struct video_levels ntsc_j_levels_svideo = {
259235783Skib	.blank = 266, .black = 266, .burst = 133,
260235783Skib};
261235783Skib
262235783Skibstatic const struct color_conversion pal_csc_composite = {
263235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
264235783Skib	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
265235783Skib	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
266235783Skib};
267235783Skib
268235783Skibstatic const struct video_levels pal_levels_composite = {
269235783Skib	.blank = 237, .black = 237, .burst = 118,
270235783Skib};
271235783Skib
272235783Skibstatic const struct color_conversion pal_csc_svideo = {
273235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
274235783Skib	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
275235783Skib	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
276235783Skib};
277235783Skib
278235783Skibstatic const struct video_levels pal_levels_svideo = {
279235783Skib	.blank = 280, .black = 280, .burst = 139,
280235783Skib};
281235783Skib
282235783Skibstatic const struct color_conversion pal_m_csc_composite = {
283235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
284235783Skib	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
285235783Skib	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
286235783Skib};
287235783Skib
288235783Skibstatic const struct video_levels pal_m_levels_composite = {
289235783Skib	.blank = 225, .black = 267, .burst = 113,
290235783Skib};
291235783Skib
292235783Skibstatic const struct color_conversion pal_m_csc_svideo = {
293235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
294235783Skib	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
295235783Skib	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
296235783Skib};
297235783Skib
298235783Skibstatic const struct video_levels pal_m_levels_svideo = {
299235783Skib	.blank = 266, .black = 316, .burst = 133,
300235783Skib};
301235783Skib
302235783Skibstatic const struct color_conversion pal_n_csc_composite = {
303235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
304235783Skib	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
305235783Skib	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
306235783Skib};
307235783Skib
308235783Skibstatic const struct video_levels pal_n_levels_composite = {
309235783Skib	.blank = 225, .black = 267, .burst = 118,
310235783Skib};
311235783Skib
312235783Skibstatic const struct color_conversion pal_n_csc_svideo = {
313235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
314235783Skib	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
315235783Skib	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
316235783Skib};
317235783Skib
318235783Skibstatic const struct video_levels pal_n_levels_svideo = {
319235783Skib	.blank = 266, .black = 316, .burst = 139,
320235783Skib};
321235783Skib
322235783Skib/*
323235783Skib * Component connections
324235783Skib */
325235783Skibstatic const struct color_conversion sdtv_csc_yprpb = {
326235783Skib	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
327235783Skib	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
328235783Skib	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
329235783Skib};
330235783Skib
331235783Skibstatic const struct color_conversion sdtv_csc_rgb = {
332235783Skib	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
333235783Skib	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
334235783Skib	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
335235783Skib};
336235783Skib
337235783Skibstatic const struct color_conversion hdtv_csc_yprpb = {
338235783Skib	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
339235783Skib	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
340235783Skib	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
341235783Skib};
342235783Skib
343235783Skibstatic const struct color_conversion hdtv_csc_rgb = {
344235783Skib	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
345235783Skib	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
346235783Skib	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
347235783Skib};
348235783Skib
349235783Skibstatic const struct video_levels component_levels = {
350235783Skib	.blank = 279, .black = 279, .burst = 0,
351235783Skib};
352235783Skib
353235783Skib
354235783Skibstruct tv_mode {
355235783Skib	const char *name;
356235783Skib	int clock;
357235783Skib	int refresh; /* in millihertz (for precision) */
358235783Skib	u32 oversample;
359235783Skib	int hsync_end, hblank_start, hblank_end, htotal;
360235783Skib	bool progressive, trilevel_sync, component_only;
361235783Skib	int vsync_start_f1, vsync_start_f2, vsync_len;
362235783Skib	bool veq_ena;
363235783Skib	int veq_start_f1, veq_start_f2, veq_len;
364235783Skib	int vi_end_f1, vi_end_f2, nbr_end;
365235783Skib	bool burst_ena;
366235783Skib	int hburst_start, hburst_len;
367235783Skib	int vburst_start_f1, vburst_end_f1;
368235783Skib	int vburst_start_f2, vburst_end_f2;
369235783Skib	int vburst_start_f3, vburst_end_f3;
370235783Skib	int vburst_start_f4, vburst_end_f4;
371235783Skib	/*
372235783Skib	 * subcarrier programming
373235783Skib	 */
374235783Skib	int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
375235783Skib	u32 sc_reset;
376235783Skib	bool pal_burst;
377235783Skib	/*
378235783Skib	 * blank/black levels
379235783Skib	 */
380235783Skib	const struct video_levels *composite_levels, *svideo_levels;
381235783Skib	const struct color_conversion *composite_color, *svideo_color;
382235783Skib	const u32 *filter_table;
383235783Skib	int max_srcw;
384235783Skib};
385235783Skib
386235783Skib
387235783Skib/*
388235783Skib * Sub carrier DDA
389235783Skib *
390235783Skib *  I think this works as follows:
391235783Skib *
392235783Skib *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
393235783Skib *
394235783Skib * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
395235783Skib *
396235783Skib * So,
397235783Skib *  dda1_ideal = subcarrier/pixel * 4096
398235783Skib *  dda1_inc = floor (dda1_ideal)
399235783Skib *  dda2 = dda1_ideal - dda1_inc
400235783Skib *
401235783Skib *  then pick a ratio for dda2 that gives the closest approximation. If
402235783Skib *  you can't get close enough, you can play with dda3 as well. This
403235783Skib *  seems likely to happen when dda2 is small as the jumps would be larger
404235783Skib *
405235783Skib * To invert this,
406235783Skib *
407235783Skib *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
408235783Skib *
409235783Skib * The constants below were all computed using a 107.520MHz clock
410235783Skib */
411235783Skib
412235783Skib/**
413235783Skib * Register programming values for TV modes.
414235783Skib *
415235783Skib * These values account for -1s required.
416235783Skib */
417235783Skib
418235783Skibstatic const struct tv_mode tv_modes[] = {
419235783Skib	{
420235783Skib		.name		= "NTSC-M",
421235783Skib		.clock		= 108000,
422235783Skib		.refresh	= 59940,
423235783Skib		.oversample	= TV_OVERSAMPLE_8X,
424235783Skib		.component_only = 0,
425235783Skib		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
426235783Skib
427235783Skib		.hsync_end	= 64,		    .hblank_end		= 124,
428235783Skib		.hblank_start	= 836,		    .htotal		= 857,
429235783Skib
430235783Skib		.progressive	= false,	    .trilevel_sync = false,
431235783Skib
432235783Skib		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
433235783Skib		.vsync_len	= 6,
434235783Skib
435235783Skib		.veq_ena	= true,		    .veq_start_f1	= 0,
436235783Skib		.veq_start_f2	= 1,		    .veq_len		= 18,
437235783Skib
438235783Skib		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
439235783Skib		.nbr_end	= 240,
440235783Skib
441235783Skib		.burst_ena	= true,
442235783Skib		.hburst_start	= 72,		    .hburst_len		= 34,
443235783Skib		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
444235783Skib		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
445235783Skib		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
446235783Skib		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
447235783Skib
448235783Skib		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
449235783Skib		.dda1_inc	=    135,
450235783Skib		.dda2_inc	=  20800,	    .dda2_size		=  27456,
451235783Skib		.dda3_inc	=      0,	    .dda3_size		=      0,
452235783Skib		.sc_reset	= TV_SC_RESET_EVERY_4,
453235783Skib		.pal_burst	= false,
454235783Skib
455235783Skib		.composite_levels = &ntsc_m_levels_composite,
456235783Skib		.composite_color = &ntsc_m_csc_composite,
457235783Skib		.svideo_levels  = &ntsc_m_levels_svideo,
458235783Skib		.svideo_color = &ntsc_m_csc_svideo,
459235783Skib
460235783Skib		.filter_table = filter_table,
461235783Skib	},
462235783Skib	{
463235783Skib		.name		= "NTSC-443",
464235783Skib		.clock		= 108000,
465235783Skib		.refresh	= 59940,
466235783Skib		.oversample	= TV_OVERSAMPLE_8X,
467235783Skib		.component_only = 0,
468235783Skib		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
469235783Skib		.hsync_end	= 64,		    .hblank_end		= 124,
470235783Skib		.hblank_start	= 836,		    .htotal		= 857,
471235783Skib
472235783Skib		.progressive	= false,	    .trilevel_sync = false,
473235783Skib
474235783Skib		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
475235783Skib		.vsync_len	= 6,
476235783Skib
477235783Skib		.veq_ena	= true,		    .veq_start_f1	= 0,
478235783Skib		.veq_start_f2	= 1,		    .veq_len		= 18,
479235783Skib
480235783Skib		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
481235783Skib		.nbr_end	= 240,
482235783Skib
483235783Skib		.burst_ena	= true,
484235783Skib		.hburst_start	= 72,		    .hburst_len		= 34,
485235783Skib		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
486235783Skib		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
487235783Skib		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
488235783Skib		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
489235783Skib
490235783Skib		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
491235783Skib		.dda1_inc       =    168,
492235783Skib		.dda2_inc       =   4093,       .dda2_size      =  27456,
493235783Skib		.dda3_inc       =    310,       .dda3_size      =    525,
494235783Skib		.sc_reset   = TV_SC_RESET_NEVER,
495235783Skib		.pal_burst  = false,
496235783Skib
497235783Skib		.composite_levels = &ntsc_m_levels_composite,
498235783Skib		.composite_color = &ntsc_m_csc_composite,
499235783Skib		.svideo_levels  = &ntsc_m_levels_svideo,
500235783Skib		.svideo_color = &ntsc_m_csc_svideo,
501235783Skib
502235783Skib		.filter_table = filter_table,
503235783Skib	},
504235783Skib	{
505235783Skib		.name		= "NTSC-J",
506235783Skib		.clock		= 108000,
507235783Skib		.refresh	= 59940,
508235783Skib		.oversample	= TV_OVERSAMPLE_8X,
509235783Skib		.component_only = 0,
510235783Skib
511235783Skib		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
512235783Skib		.hsync_end	= 64,		    .hblank_end		= 124,
513235783Skib		.hblank_start = 836,	    .htotal		= 857,
514235783Skib
515235783Skib		.progressive	= false,    .trilevel_sync = false,
516235783Skib
517235783Skib		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
518235783Skib		.vsync_len	= 6,
519235783Skib
520235783Skib		.veq_ena      = true,	    .veq_start_f1	= 0,
521235783Skib		.veq_start_f2 = 1,	    .veq_len		= 18,
522235783Skib
523235783Skib		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
524235783Skib		.nbr_end	= 240,
525235783Skib
526235783Skib		.burst_ena	= true,
527235783Skib		.hburst_start	= 72,		    .hburst_len		= 34,
528235783Skib		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
529235783Skib		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
530235783Skib		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
531235783Skib		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
532235783Skib
533235783Skib		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
534235783Skib		.dda1_inc	=    135,
535235783Skib		.dda2_inc	=  20800,	    .dda2_size		=  27456,
536235783Skib		.dda3_inc	=      0,	    .dda3_size		=      0,
537235783Skib		.sc_reset	= TV_SC_RESET_EVERY_4,
538235783Skib		.pal_burst	= false,
539235783Skib
540235783Skib		.composite_levels = &ntsc_j_levels_composite,
541235783Skib		.composite_color = &ntsc_j_csc_composite,
542235783Skib		.svideo_levels  = &ntsc_j_levels_svideo,
543235783Skib		.svideo_color = &ntsc_j_csc_svideo,
544235783Skib
545235783Skib		.filter_table = filter_table,
546235783Skib	},
547235783Skib	{
548235783Skib		.name		= "PAL-M",
549235783Skib		.clock		= 108000,
550235783Skib		.refresh	= 59940,
551235783Skib		.oversample	= TV_OVERSAMPLE_8X,
552235783Skib		.component_only = 0,
553235783Skib
554235783Skib		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
555235783Skib		.hsync_end	= 64,		  .hblank_end		= 124,
556235783Skib		.hblank_start = 836,	  .htotal		= 857,
557235783Skib
558235783Skib		.progressive	= false,	    .trilevel_sync = false,
559235783Skib
560235783Skib		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
561235783Skib		.vsync_len	= 6,
562235783Skib
563235783Skib		.veq_ena	= true,		    .veq_start_f1	= 0,
564235783Skib		.veq_start_f2	= 1,		    .veq_len		= 18,
565235783Skib
566235783Skib		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
567235783Skib		.nbr_end	= 240,
568235783Skib
569235783Skib		.burst_ena	= true,
570235783Skib		.hburst_start	= 72,		    .hburst_len		= 34,
571235783Skib		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
572235783Skib		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
573235783Skib		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
574235783Skib		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
575235783Skib
576235783Skib		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
577235783Skib		.dda1_inc	=    135,
578235783Skib		.dda2_inc	=  16704,	    .dda2_size		=  27456,
579235783Skib		.dda3_inc	=      0,	    .dda3_size		=      0,
580235783Skib		.sc_reset	= TV_SC_RESET_EVERY_8,
581235783Skib		.pal_burst  = true,
582235783Skib
583235783Skib		.composite_levels = &pal_m_levels_composite,
584235783Skib		.composite_color = &pal_m_csc_composite,
585235783Skib		.svideo_levels  = &pal_m_levels_svideo,
586235783Skib		.svideo_color = &pal_m_csc_svideo,
587235783Skib
588235783Skib		.filter_table = filter_table,
589235783Skib	},
590235783Skib	{
591235783Skib		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
592235783Skib		.name	    = "PAL-N",
593235783Skib		.clock		= 108000,
594235783Skib		.refresh	= 50000,
595235783Skib		.oversample	= TV_OVERSAMPLE_8X,
596235783Skib		.component_only = 0,
597235783Skib
598235783Skib		.hsync_end	= 64,		    .hblank_end		= 128,
599235783Skib		.hblank_start = 844,	    .htotal		= 863,
600235783Skib
601235783Skib		.progressive  = false,    .trilevel_sync = false,
602235783Skib
603235783Skib
604235783Skib		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
605235783Skib		.vsync_len	= 6,
606235783Skib
607235783Skib		.veq_ena	= true,		    .veq_start_f1	= 0,
608235783Skib		.veq_start_f2	= 1,		    .veq_len		= 18,
609235783Skib
610235783Skib		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
611235783Skib		.nbr_end	= 286,
612235783Skib
613235783Skib		.burst_ena	= true,
614235783Skib		.hburst_start = 73,	    .hburst_len		= 34,
615235783Skib		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
616235783Skib		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
617235783Skib		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
618235783Skib		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
619235783Skib
620235783Skib
621235783Skib		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
622235783Skib		.dda1_inc       =    135,
623235783Skib		.dda2_inc       =  23578,       .dda2_size      =  27648,
624235783Skib		.dda3_inc       =    134,       .dda3_size      =    625,
625235783Skib		.sc_reset   = TV_SC_RESET_EVERY_8,
626235783Skib		.pal_burst  = true,
627235783Skib
628235783Skib		.composite_levels = &pal_n_levels_composite,
629235783Skib		.composite_color = &pal_n_csc_composite,
630235783Skib		.svideo_levels  = &pal_n_levels_svideo,
631235783Skib		.svideo_color = &pal_n_csc_svideo,
632235783Skib
633235783Skib		.filter_table = filter_table,
634235783Skib	},
635235783Skib	{
636235783Skib		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
637235783Skib		.name	    = "PAL",
638235783Skib		.clock		= 108000,
639235783Skib		.refresh	= 50000,
640235783Skib		.oversample	= TV_OVERSAMPLE_8X,
641235783Skib		.component_only = 0,
642235783Skib
643235783Skib		.hsync_end	= 64,		    .hblank_end		= 142,
644235783Skib		.hblank_start	= 844,	    .htotal		= 863,
645235783Skib
646235783Skib		.progressive	= false,    .trilevel_sync = false,
647235783Skib
648235783Skib		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
649235783Skib		.vsync_len	= 5,
650235783Skib
651235783Skib		.veq_ena	= true,	    .veq_start_f1	= 0,
652235783Skib		.veq_start_f2	= 1,	    .veq_len		= 15,
653235783Skib
654235783Skib		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
655235783Skib		.nbr_end	= 286,
656235783Skib
657235783Skib		.burst_ena	= true,
658235783Skib		.hburst_start	= 73,		    .hburst_len		= 32,
659235783Skib		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
660235783Skib		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
661235783Skib		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
662235783Skib		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
663235783Skib
664235783Skib		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
665235783Skib		.dda1_inc       =    168,
666235783Skib		.dda2_inc       =   4122,       .dda2_size      =  27648,
667235783Skib		.dda3_inc       =     67,       .dda3_size      =    625,
668235783Skib		.sc_reset   = TV_SC_RESET_EVERY_8,
669235783Skib		.pal_burst  = true,
670235783Skib
671235783Skib		.composite_levels = &pal_levels_composite,
672235783Skib		.composite_color = &pal_csc_composite,
673235783Skib		.svideo_levels  = &pal_levels_svideo,
674235783Skib		.svideo_color = &pal_csc_svideo,
675235783Skib
676235783Skib		.filter_table = filter_table,
677235783Skib	},
678235783Skib	{
679296548Sdumbbell		.name       = "480p",
680296548Sdumbbell		.clock		= 107520,
681296548Sdumbbell		.refresh	= 59940,
682296548Sdumbbell		.oversample     = TV_OVERSAMPLE_4X,
683296548Sdumbbell		.component_only = 1,
684296548Sdumbbell
685296548Sdumbbell		.hsync_end      = 64,               .hblank_end         = 122,
686296548Sdumbbell		.hblank_start   = 842,              .htotal             = 857,
687296548Sdumbbell
688296548Sdumbbell		.progressive    = true,		    .trilevel_sync = false,
689296548Sdumbbell
690296548Sdumbbell		.vsync_start_f1 = 12,               .vsync_start_f2     = 12,
691296548Sdumbbell		.vsync_len      = 12,
692296548Sdumbbell
693296548Sdumbbell		.veq_ena        = false,
694296548Sdumbbell
695296548Sdumbbell		.vi_end_f1      = 44,               .vi_end_f2          = 44,
696296548Sdumbbell		.nbr_end        = 479,
697296548Sdumbbell
698296548Sdumbbell		.burst_ena      = false,
699296548Sdumbbell
700296548Sdumbbell		.filter_table = filter_table,
701296548Sdumbbell	},
702296548Sdumbbell	{
703296548Sdumbbell		.name       = "576p",
704296548Sdumbbell		.clock		= 107520,
705296548Sdumbbell		.refresh	= 50000,
706296548Sdumbbell		.oversample     = TV_OVERSAMPLE_4X,
707296548Sdumbbell		.component_only = 1,
708296548Sdumbbell
709296548Sdumbbell		.hsync_end      = 64,               .hblank_end         = 139,
710296548Sdumbbell		.hblank_start   = 859,              .htotal             = 863,
711296548Sdumbbell
712296548Sdumbbell		.progressive    = true,		    .trilevel_sync = false,
713296548Sdumbbell
714296548Sdumbbell		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
715296548Sdumbbell		.vsync_len      = 10,
716296548Sdumbbell
717296548Sdumbbell		.veq_ena        = false,
718296548Sdumbbell
719296548Sdumbbell		.vi_end_f1      = 48,               .vi_end_f2          = 48,
720296548Sdumbbell		.nbr_end        = 575,
721296548Sdumbbell
722296548Sdumbbell		.burst_ena      = false,
723296548Sdumbbell
724296548Sdumbbell		.filter_table = filter_table,
725296548Sdumbbell	},
726296548Sdumbbell	{
727235783Skib		.name       = "720p@60Hz",
728235783Skib		.clock		= 148800,
729235783Skib		.refresh	= 60000,
730235783Skib		.oversample     = TV_OVERSAMPLE_2X,
731235783Skib		.component_only = 1,
732235783Skib
733235783Skib		.hsync_end      = 80,               .hblank_end         = 300,
734235783Skib		.hblank_start   = 1580,             .htotal             = 1649,
735235783Skib
736235783Skib		.progressive	= true,		    .trilevel_sync = true,
737235783Skib
738235783Skib		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
739235783Skib		.vsync_len      = 10,
740235783Skib
741235783Skib		.veq_ena        = false,
742235783Skib
743235783Skib		.vi_end_f1      = 29,               .vi_end_f2          = 29,
744235783Skib		.nbr_end        = 719,
745235783Skib
746235783Skib		.burst_ena      = false,
747235783Skib
748235783Skib		.filter_table = filter_table,
749235783Skib	},
750235783Skib	{
751235783Skib		.name       = "720p@50Hz",
752235783Skib		.clock		= 148800,
753235783Skib		.refresh	= 50000,
754235783Skib		.oversample     = TV_OVERSAMPLE_2X,
755235783Skib		.component_only = 1,
756235783Skib
757235783Skib		.hsync_end      = 80,               .hblank_end         = 300,
758235783Skib		.hblank_start   = 1580,             .htotal             = 1979,
759235783Skib
760235783Skib		.progressive	= true,		    .trilevel_sync = true,
761235783Skib
762235783Skib		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
763235783Skib		.vsync_len      = 10,
764235783Skib
765235783Skib		.veq_ena        = false,
766235783Skib
767235783Skib		.vi_end_f1      = 29,               .vi_end_f2          = 29,
768235783Skib		.nbr_end        = 719,
769235783Skib
770235783Skib		.burst_ena      = false,
771235783Skib
772235783Skib		.filter_table = filter_table,
773235783Skib		.max_srcw = 800
774235783Skib	},
775235783Skib	{
776235783Skib		.name       = "1080i@50Hz",
777235783Skib		.clock		= 148800,
778235783Skib		.refresh	= 50000,
779235783Skib		.oversample     = TV_OVERSAMPLE_2X,
780235783Skib		.component_only = 1,
781235783Skib
782235783Skib		.hsync_end      = 88,               .hblank_end         = 235,
783235783Skib		.hblank_start   = 2155,             .htotal             = 2639,
784235783Skib
785235783Skib		.progressive	= false,	  .trilevel_sync = true,
786235783Skib
787235783Skib		.vsync_start_f1 = 4,              .vsync_start_f2     = 5,
788235783Skib		.vsync_len      = 10,
789235783Skib
790235783Skib		.veq_ena	= true,	    .veq_start_f1	= 4,
791235783Skib		.veq_start_f2   = 4,	    .veq_len		= 10,
792235783Skib
793235783Skib
794235783Skib		.vi_end_f1      = 21,           .vi_end_f2          = 22,
795235783Skib		.nbr_end        = 539,
796235783Skib
797235783Skib		.burst_ena      = false,
798235783Skib
799235783Skib		.filter_table = filter_table,
800235783Skib	},
801235783Skib	{
802235783Skib		.name       = "1080i@60Hz",
803235783Skib		.clock		= 148800,
804235783Skib		.refresh	= 60000,
805235783Skib		.oversample     = TV_OVERSAMPLE_2X,
806235783Skib		.component_only = 1,
807235783Skib
808235783Skib		.hsync_end      = 88,               .hblank_end         = 235,
809235783Skib		.hblank_start   = 2155,             .htotal             = 2199,
810235783Skib
811235783Skib		.progressive	= false,	    .trilevel_sync = true,
812235783Skib
813235783Skib		.vsync_start_f1 = 4,               .vsync_start_f2     = 5,
814235783Skib		.vsync_len      = 10,
815235783Skib
816235783Skib		.veq_ena	= true,		    .veq_start_f1	= 4,
817235783Skib		.veq_start_f2	= 4,		    .veq_len		= 10,
818235783Skib
819235783Skib
820235783Skib		.vi_end_f1      = 21,               .vi_end_f2          = 22,
821235783Skib		.nbr_end        = 539,
822235783Skib
823235783Skib		.burst_ena      = false,
824235783Skib
825235783Skib		.filter_table = filter_table,
826235783Skib	},
827235783Skib};
828235783Skib
829235783Skibstatic struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder)
830235783Skib{
831235783Skib	return container_of(encoder, struct intel_tv, base.base);
832235783Skib}
833235783Skib
834235783Skibstatic struct intel_tv *intel_attached_tv(struct drm_connector *connector)
835235783Skib{
836235783Skib	return container_of(intel_attached_encoder(connector),
837235783Skib			    struct intel_tv,
838235783Skib			    base);
839235783Skib}
840235783Skib
841296548Sdumbbellstatic bool
842296548Sdumbbellintel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
843296548Sdumbbell{
844296548Sdumbbell	struct drm_device *dev = encoder->base.dev;
845296548Sdumbbell	struct drm_i915_private *dev_priv = dev->dev_private;
846296548Sdumbbell	u32 tmp = I915_READ(TV_CTL);
847296548Sdumbbell
848296548Sdumbbell	if (!(tmp & TV_ENC_ENABLE))
849296548Sdumbbell		return false;
850296548Sdumbbell
851296548Sdumbbell	*pipe = PORT_TO_PIPE(tmp);
852296548Sdumbbell
853296548Sdumbbell	return true;
854296548Sdumbbell}
855296548Sdumbbell
856235783Skibstatic void
857296548Sdumbbellintel_enable_tv(struct intel_encoder *encoder)
858235783Skib{
859296548Sdumbbell	struct drm_device *dev = encoder->base.dev;
860235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
861235783Skib
862296548Sdumbbell	I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
863235783Skib}
864235783Skib
865296548Sdumbbellstatic void
866296548Sdumbbellintel_disable_tv(struct intel_encoder *encoder)
867296548Sdumbbell{
868296548Sdumbbell	struct drm_device *dev = encoder->base.dev;
869296548Sdumbbell	struct drm_i915_private *dev_priv = dev->dev_private;
870296548Sdumbbell
871296548Sdumbbell	I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
872296548Sdumbbell}
873296548Sdumbbell
874235783Skibstatic const struct tv_mode *
875235783Skibintel_tv_mode_lookup(const char *tv_format)
876235783Skib{
877235783Skib	int i;
878235783Skib
879296548Sdumbbell	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
880235783Skib		const struct tv_mode *tv_mode = &tv_modes[i];
881235783Skib
882235783Skib		if (!strcmp(tv_format, tv_mode->name))
883235783Skib			return tv_mode;
884235783Skib	}
885235783Skib	return NULL;
886235783Skib}
887235783Skib
888235783Skibstatic const struct tv_mode *
889235783Skibintel_tv_mode_find(struct intel_tv *intel_tv)
890235783Skib{
891235783Skib	return intel_tv_mode_lookup(intel_tv->tv_format);
892235783Skib}
893235783Skib
894235783Skibstatic enum drm_mode_status
895235783Skibintel_tv_mode_valid(struct drm_connector *connector,
896235783Skib		    struct drm_display_mode *mode)
897235783Skib{
898235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
899235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
900235783Skib
901235783Skib	/* Ensure TV refresh is close to desired refresh */
902235783Skib	if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
903235783Skib				< 1000)
904235783Skib		return MODE_OK;
905235783Skib
906235783Skib	return MODE_CLOCK_RANGE;
907235783Skib}
908235783Skib
909235783Skib
910235783Skibstatic bool
911296548Sdumbbellintel_tv_mode_fixup(struct drm_encoder *encoder,
912296548Sdumbbell		    const struct drm_display_mode *mode,
913235783Skib		    struct drm_display_mode *adjusted_mode)
914235783Skib{
915235783Skib	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
916235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
917235783Skib
918235783Skib	if (!tv_mode)
919235783Skib		return false;
920235783Skib
921296548Sdumbbell	if (intel_encoder_check_is_cloned(&intel_tv->base))
922296548Sdumbbell		return false;
923235783Skib
924235783Skib	adjusted_mode->clock = tv_mode->clock;
925235783Skib	return true;
926235783Skib}
927235783Skib
928235783Skibstatic void
929235783Skibintel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
930235783Skib		  struct drm_display_mode *adjusted_mode)
931235783Skib{
932235783Skib	struct drm_device *dev = encoder->dev;
933235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
934235783Skib	struct drm_crtc *crtc = encoder->crtc;
935235783Skib	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
936235783Skib	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
937235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
938235783Skib	u32 tv_ctl;
939235783Skib	u32 hctl1, hctl2, hctl3;
940235783Skib	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
941235783Skib	u32 scctl1, scctl2, scctl3;
942235783Skib	int i, j;
943235783Skib	const struct video_levels *video_levels;
944235783Skib	const struct color_conversion *color_conversion;
945235783Skib	bool burst_ena;
946235783Skib	int pipe = intel_crtc->pipe;
947235783Skib
948235783Skib	if (!tv_mode)
949235783Skib		return;	/* can't happen (mode_prepare prevents this) */
950235783Skib
951235783Skib	tv_ctl = I915_READ(TV_CTL);
952235783Skib	tv_ctl &= TV_CTL_SAVE;
953235783Skib
954235783Skib	switch (intel_tv->type) {
955235783Skib	default:
956235783Skib	case DRM_MODE_CONNECTOR_Unknown:
957235783Skib	case DRM_MODE_CONNECTOR_Composite:
958235783Skib		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
959235783Skib		video_levels = tv_mode->composite_levels;
960235783Skib		color_conversion = tv_mode->composite_color;
961235783Skib		burst_ena = tv_mode->burst_ena;
962235783Skib		break;
963235783Skib	case DRM_MODE_CONNECTOR_Component:
964235783Skib		tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
965235783Skib		video_levels = &component_levels;
966235783Skib		if (tv_mode->burst_ena)
967235783Skib			color_conversion = &sdtv_csc_yprpb;
968235783Skib		else
969235783Skib			color_conversion = &hdtv_csc_yprpb;
970235783Skib		burst_ena = false;
971235783Skib		break;
972235783Skib	case DRM_MODE_CONNECTOR_SVIDEO:
973235783Skib		tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
974235783Skib		video_levels = tv_mode->svideo_levels;
975235783Skib		color_conversion = tv_mode->svideo_color;
976235783Skib		burst_ena = tv_mode->burst_ena;
977235783Skib		break;
978235783Skib	}
979235783Skib	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
980235783Skib		(tv_mode->htotal << TV_HTOTAL_SHIFT);
981235783Skib
982235783Skib	hctl2 = (tv_mode->hburst_start << 16) |
983235783Skib		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
984235783Skib
985235783Skib	if (burst_ena)
986235783Skib		hctl2 |= TV_BURST_ENA;
987235783Skib
988235783Skib	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
989235783Skib		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
990235783Skib
991235783Skib	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
992235783Skib		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
993235783Skib		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
994235783Skib
995235783Skib	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
996235783Skib		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
997235783Skib		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
998235783Skib
999235783Skib	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
1000235783Skib		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
1001235783Skib		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
1002235783Skib
1003235783Skib	if (tv_mode->veq_ena)
1004235783Skib		vctl3 |= TV_EQUAL_ENA;
1005235783Skib
1006235783Skib	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
1007235783Skib		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
1008235783Skib
1009235783Skib	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
1010235783Skib		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
1011235783Skib
1012235783Skib	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
1013235783Skib		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
1014235783Skib
1015235783Skib	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
1016235783Skib		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
1017235783Skib
1018235783Skib	if (intel_crtc->pipe == 1)
1019235783Skib		tv_ctl |= TV_ENC_PIPEB_SELECT;
1020235783Skib	tv_ctl |= tv_mode->oversample;
1021235783Skib
1022235783Skib	if (tv_mode->progressive)
1023235783Skib		tv_ctl |= TV_PROGRESSIVE;
1024235783Skib	if (tv_mode->trilevel_sync)
1025235783Skib		tv_ctl |= TV_TRILEVEL_SYNC;
1026235783Skib	if (tv_mode->pal_burst)
1027235783Skib		tv_ctl |= TV_PAL_BURST;
1028235783Skib
1029235783Skib	scctl1 = 0;
1030235783Skib	if (tv_mode->dda1_inc)
1031235783Skib		scctl1 |= TV_SC_DDA1_EN;
1032235783Skib	if (tv_mode->dda2_inc)
1033235783Skib		scctl1 |= TV_SC_DDA2_EN;
1034235783Skib	if (tv_mode->dda3_inc)
1035235783Skib		scctl1 |= TV_SC_DDA3_EN;
1036235783Skib	scctl1 |= tv_mode->sc_reset;
1037235783Skib	if (video_levels)
1038235783Skib		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
1039235783Skib	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1040235783Skib
1041235783Skib	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1042235783Skib		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1043235783Skib
1044235783Skib	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1045235783Skib		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1046235783Skib
1047235783Skib	/* Enable two fixes for the chips that need them. */
1048235783Skib	if (dev->pci_device < 0x2772)
1049235783Skib		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1050235783Skib
1051235783Skib	I915_WRITE(TV_H_CTL_1, hctl1);
1052235783Skib	I915_WRITE(TV_H_CTL_2, hctl2);
1053235783Skib	I915_WRITE(TV_H_CTL_3, hctl3);
1054235783Skib	I915_WRITE(TV_V_CTL_1, vctl1);
1055235783Skib	I915_WRITE(TV_V_CTL_2, vctl2);
1056235783Skib	I915_WRITE(TV_V_CTL_3, vctl3);
1057235783Skib	I915_WRITE(TV_V_CTL_4, vctl4);
1058235783Skib	I915_WRITE(TV_V_CTL_5, vctl5);
1059235783Skib	I915_WRITE(TV_V_CTL_6, vctl6);
1060235783Skib	I915_WRITE(TV_V_CTL_7, vctl7);
1061235783Skib	I915_WRITE(TV_SC_CTL_1, scctl1);
1062235783Skib	I915_WRITE(TV_SC_CTL_2, scctl2);
1063235783Skib	I915_WRITE(TV_SC_CTL_3, scctl3);
1064235783Skib
1065235783Skib	if (color_conversion) {
1066235783Skib		I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
1067235783Skib			   color_conversion->gy);
1068235783Skib		I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
1069235783Skib			   color_conversion->ay);
1070235783Skib		I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
1071235783Skib			   color_conversion->gu);
1072235783Skib		I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
1073235783Skib			   color_conversion->au);
1074235783Skib		I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
1075235783Skib			   color_conversion->gv);
1076235783Skib		I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
1077235783Skib			   color_conversion->av);
1078235783Skib	}
1079235783Skib
1080235783Skib	if (INTEL_INFO(dev)->gen >= 4)
1081235783Skib		I915_WRITE(TV_CLR_KNOBS, 0x00404000);
1082235783Skib	else
1083235783Skib		I915_WRITE(TV_CLR_KNOBS, 0x00606000);
1084235783Skib
1085235783Skib	if (video_levels)
1086235783Skib		I915_WRITE(TV_CLR_LEVEL,
1087235783Skib			   ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1088235783Skib			    (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1089235783Skib	{
1090235783Skib		int pipeconf_reg = PIPECONF(pipe);
1091235783Skib		int dspcntr_reg = DSPCNTR(intel_crtc->plane);
1092235783Skib		int pipeconf = I915_READ(pipeconf_reg);
1093235783Skib		int dspcntr = I915_READ(dspcntr_reg);
1094235783Skib		int xpos = 0x0, ypos = 0x0;
1095235783Skib		unsigned int xsize, ysize;
1096235783Skib		/* Pipe must be off here */
1097235783Skib		I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
1098296548Sdumbbell		intel_flush_display_plane(dev_priv, intel_crtc->plane);
1099235783Skib
1100235783Skib		/* Wait for vblank for the disable to take effect */
1101235783Skib		if (IS_GEN2(dev))
1102235783Skib			intel_wait_for_vblank(dev, intel_crtc->pipe);
1103235783Skib
1104235783Skib		I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
1105235783Skib		/* Wait for vblank for the disable to take effect. */
1106235783Skib		intel_wait_for_pipe_off(dev, intel_crtc->pipe);
1107235783Skib
1108235783Skib		/* Filter ctl must be set before TV_WIN_SIZE */
1109235783Skib		I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1110235783Skib		xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1111235783Skib		if (tv_mode->progressive)
1112235783Skib			ysize = tv_mode->nbr_end + 1;
1113235783Skib		else
1114235783Skib			ysize = 2*tv_mode->nbr_end + 1;
1115235783Skib
1116235783Skib		xpos += intel_tv->margin[TV_MARGIN_LEFT];
1117235783Skib		ypos += intel_tv->margin[TV_MARGIN_TOP];
1118235783Skib		xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
1119235783Skib			  intel_tv->margin[TV_MARGIN_RIGHT]);
1120235783Skib		ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
1121235783Skib			  intel_tv->margin[TV_MARGIN_BOTTOM]);
1122235783Skib		I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
1123235783Skib		I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
1124235783Skib
1125235783Skib		I915_WRITE(pipeconf_reg, pipeconf);
1126235783Skib		I915_WRITE(dspcntr_reg, dspcntr);
1127296548Sdumbbell		intel_flush_display_plane(dev_priv, intel_crtc->plane);
1128235783Skib	}
1129235783Skib
1130235783Skib	j = 0;
1131235783Skib	for (i = 0; i < 60; i++)
1132235783Skib		I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1133235783Skib	for (i = 0; i < 60; i++)
1134235783Skib		I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1135235783Skib	for (i = 0; i < 43; i++)
1136235783Skib		I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1137235783Skib	for (i = 0; i < 43; i++)
1138235783Skib		I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1139235783Skib	I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
1140235783Skib	I915_WRITE(TV_CTL, tv_ctl);
1141235783Skib}
1142235783Skib
1143235783Skibstatic const struct drm_display_mode reported_modes[] = {
1144235783Skib	{
1145235783Skib		.name = "NTSC 480i",
1146235783Skib		.clock = 107520,
1147235783Skib		.hdisplay = 1280,
1148235783Skib		.hsync_start = 1368,
1149235783Skib		.hsync_end = 1496,
1150235783Skib		.htotal = 1712,
1151235783Skib
1152235783Skib		.vdisplay = 1024,
1153235783Skib		.vsync_start = 1027,
1154235783Skib		.vsync_end = 1034,
1155235783Skib		.vtotal = 1104,
1156235783Skib		.type = DRM_MODE_TYPE_DRIVER,
1157235783Skib	},
1158235783Skib};
1159235783Skib
1160235783Skib/**
1161235783Skib * Detects TV presence by checking for load.
1162235783Skib *
1163235783Skib * Requires that the current pipe's DPLL is active.
1164235783Skib
1165235783Skib * \return true if TV is connected.
1166235783Skib * \return false if TV is disconnected.
1167235783Skib */
1168235783Skibstatic int
1169235783Skibintel_tv_detect_type(struct intel_tv *intel_tv,
1170235783Skib		      struct drm_connector *connector)
1171235783Skib{
1172235783Skib	struct drm_encoder *encoder = &intel_tv->base.base;
1173235783Skib	struct drm_crtc *crtc = encoder->crtc;
1174235783Skib	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1175235783Skib	struct drm_device *dev = encoder->dev;
1176235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
1177235783Skib	u32 tv_ctl, save_tv_ctl;
1178235783Skib	u32 tv_dac, save_tv_dac;
1179235783Skib	int type;
1180235783Skib
1181235783Skib	/* Disable TV interrupts around load detect or we'll recurse */
1182235783Skib	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1183235783Skib		mtx_lock(&dev_priv->irq_lock);
1184235783Skib		i915_disable_pipestat(dev_priv, 0,
1185235783Skib				      PIPE_HOTPLUG_INTERRUPT_ENABLE |
1186235783Skib				      PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1187235783Skib		mtx_unlock(&dev_priv->irq_lock);
1188235783Skib	}
1189235783Skib
1190235783Skib	save_tv_dac = tv_dac = I915_READ(TV_DAC);
1191235783Skib	save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
1192235783Skib
1193235783Skib	/* Poll for TV detection */
1194235783Skib	tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
1195235783Skib	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1196235783Skib	if (intel_crtc->pipe == 1)
1197235783Skib		tv_ctl |= TV_ENC_PIPEB_SELECT;
1198235783Skib	else
1199235783Skib		tv_ctl &= ~TV_ENC_PIPEB_SELECT;
1200235783Skib
1201235783Skib	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1202235783Skib	tv_dac |= (TVDAC_STATE_CHG_EN |
1203235783Skib		   TVDAC_A_SENSE_CTL |
1204235783Skib		   TVDAC_B_SENSE_CTL |
1205235783Skib		   TVDAC_C_SENSE_CTL |
1206235783Skib		   DAC_CTL_OVERRIDE |
1207235783Skib		   DAC_A_0_7_V |
1208235783Skib		   DAC_B_0_7_V |
1209235783Skib		   DAC_C_0_7_V);
1210235783Skib
1211277487Skib
1212277487Skib	/*
1213277487Skib	 * The TV sense state should be cleared to zero on cantiga platform. Otherwise
1214277487Skib	 * the TV is misdetected. This is hardware requirement.
1215277487Skib	 */
1216277487Skib	if (IS_GM45(dev))
1217277487Skib		tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
1218277487Skib			    TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
1219277487Skib
1220235783Skib	I915_WRITE(TV_CTL, tv_ctl);
1221235783Skib	I915_WRITE(TV_DAC, tv_dac);
1222235783Skib	POSTING_READ(TV_DAC);
1223235783Skib
1224235783Skib	intel_wait_for_vblank(intel_tv->base.base.dev,
1225235783Skib			      to_intel_crtc(intel_tv->base.base.crtc)->pipe);
1226235783Skib
1227235783Skib	type = -1;
1228235783Skib	tv_dac = I915_READ(TV_DAC);
1229235783Skib	DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
1230235783Skib	/*
1231235783Skib	 *  A B C
1232235783Skib	 *  0 1 1 Composite
1233235783Skib	 *  1 0 X svideo
1234235783Skib	 *  0 0 0 Component
1235235783Skib	 */
1236235783Skib	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1237235783Skib		DRM_DEBUG_KMS("Detected Composite TV connection\n");
1238235783Skib		type = DRM_MODE_CONNECTOR_Composite;
1239235783Skib	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1240235783Skib		DRM_DEBUG_KMS("Detected S-Video TV connection\n");
1241235783Skib		type = DRM_MODE_CONNECTOR_SVIDEO;
1242235783Skib	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1243235783Skib		DRM_DEBUG_KMS("Detected Component TV connection\n");
1244235783Skib		type = DRM_MODE_CONNECTOR_Component;
1245235783Skib	} else {
1246235783Skib		DRM_DEBUG_KMS("Unrecognised TV connection\n");
1247235783Skib		type = -1;
1248235783Skib	}
1249235783Skib
1250235783Skib	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1251235783Skib	I915_WRITE(TV_CTL, save_tv_ctl);
1252296548Sdumbbell	POSTING_READ(TV_CTL);
1253235783Skib
1254296548Sdumbbell	/* For unknown reasons the hw barfs if we don't do this vblank wait. */
1255296548Sdumbbell	intel_wait_for_vblank(intel_tv->base.base.dev,
1256296548Sdumbbell			      to_intel_crtc(intel_tv->base.base.crtc)->pipe);
1257296548Sdumbbell
1258235783Skib	/* Restore interrupt config */
1259235783Skib	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1260235783Skib		mtx_lock(&dev_priv->irq_lock);
1261235783Skib		i915_enable_pipestat(dev_priv, 0,
1262235783Skib				     PIPE_HOTPLUG_INTERRUPT_ENABLE |
1263235783Skib				     PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1264235783Skib		mtx_unlock(&dev_priv->irq_lock);
1265235783Skib	}
1266235783Skib
1267235783Skib	return type;
1268235783Skib}
1269235783Skib
1270235783Skib/*
1271235783Skib * Here we set accurate tv format according to connector type
1272235783Skib * i.e Component TV should not be assigned by NTSC or PAL
1273235783Skib */
1274235783Skibstatic void intel_tv_find_better_format(struct drm_connector *connector)
1275235783Skib{
1276235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
1277235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1278235783Skib	int i;
1279235783Skib
1280235783Skib	if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1281235783Skib		tv_mode->component_only)
1282235783Skib		return;
1283235783Skib
1284235783Skib
1285235783Skib	for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) {
1286235783Skib		tv_mode = tv_modes + i;
1287235783Skib
1288235783Skib		if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1289235783Skib			tv_mode->component_only)
1290235783Skib			break;
1291235783Skib	}
1292235783Skib
1293235783Skib	intel_tv->tv_format = tv_mode->name;
1294280183Sdumbbell	drm_object_property_set_value(&connector->base,
1295235783Skib		connector->dev->mode_config.tv_mode_property, i);
1296235783Skib}
1297235783Skib
1298235783Skib/**
1299235783Skib * Detect the TV connection.
1300235783Skib *
1301235783Skib * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
1302235783Skib * we have a pipe programmed in order to probe the TV.
1303235783Skib */
1304235783Skibstatic enum drm_connector_status
1305235783Skibintel_tv_detect(struct drm_connector *connector, bool force)
1306235783Skib{
1307235783Skib	struct drm_display_mode mode;
1308235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
1309235783Skib	int type;
1310235783Skib
1311235783Skib	mode = reported_modes[0];
1312235783Skib
1313277487Skib	if (force) {
1314235783Skib		struct intel_load_detect_pipe tmp;
1315235783Skib
1316296548Sdumbbell		if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
1317235783Skib			type = intel_tv_detect_type(intel_tv, connector);
1318296548Sdumbbell			intel_release_load_detect_pipe(connector, &tmp);
1319235783Skib		} else
1320235783Skib			return connector_status_unknown;
1321235783Skib	} else
1322235783Skib		return connector->status;
1323235783Skib
1324235783Skib	if (type < 0)
1325235783Skib		return connector_status_disconnected;
1326235783Skib
1327235783Skib	intel_tv->type = type;
1328235783Skib	intel_tv_find_better_format(connector);
1329235783Skib
1330235783Skib	return connector_status_connected;
1331235783Skib}
1332235783Skib
1333235783Skibstatic const struct input_res {
1334235783Skib	const char *name;
1335235783Skib	int w, h;
1336235783Skib} input_res_table[] = {
1337235783Skib	{"640x480", 640, 480},
1338235783Skib	{"800x600", 800, 600},
1339235783Skib	{"1024x768", 1024, 768},
1340235783Skib	{"1280x1024", 1280, 1024},
1341235783Skib	{"848x480", 848, 480},
1342235783Skib	{"1280x720", 1280, 720},
1343235783Skib	{"1920x1080", 1920, 1080},
1344235783Skib};
1345235783Skib
1346235783Skib/*
1347235783Skib * Chose preferred mode  according to line number of TV format
1348235783Skib */
1349235783Skibstatic void
1350235783Skibintel_tv_chose_preferred_modes(struct drm_connector *connector,
1351235783Skib			       struct drm_display_mode *mode_ptr)
1352235783Skib{
1353235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
1354235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1355235783Skib
1356235783Skib	if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
1357235783Skib		mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1358235783Skib	else if (tv_mode->nbr_end > 480) {
1359235783Skib		if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
1360235783Skib			if (mode_ptr->vdisplay == 720)
1361235783Skib				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1362235783Skib		} else if (mode_ptr->vdisplay == 1080)
1363235783Skib				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1364235783Skib	}
1365235783Skib}
1366235783Skib
1367235783Skib/**
1368235783Skib * Stub get_modes function.
1369235783Skib *
1370235783Skib * This should probably return a set of fixed modes, unless we can figure out
1371235783Skib * how to probe modes off of TV connections.
1372235783Skib */
1373235783Skib
1374235783Skibstatic int
1375235783Skibintel_tv_get_modes(struct drm_connector *connector)
1376235783Skib{
1377235783Skib	struct drm_display_mode *mode_ptr;
1378235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
1379235783Skib	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1380235783Skib	int j, count = 0;
1381235783Skib	u64 tmp;
1382235783Skib
1383280183Sdumbbell	for (j = 0; j < ARRAY_SIZE(input_res_table);
1384235783Skib	     j++) {
1385235783Skib		const struct input_res *input = &input_res_table[j];
1386235783Skib		unsigned int hactive_s = input->w;
1387235783Skib		unsigned int vactive_s = input->h;
1388235783Skib
1389235783Skib		if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1390235783Skib			continue;
1391235783Skib
1392235783Skib		if (input->w > 1024 && (!tv_mode->progressive
1393235783Skib					&& !tv_mode->component_only))
1394235783Skib			continue;
1395235783Skib
1396235783Skib		mode_ptr = drm_mode_create(connector->dev);
1397235783Skib		if (!mode_ptr)
1398235783Skib			continue;
1399235783Skib		strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
1400235783Skib
1401235783Skib		mode_ptr->hdisplay = hactive_s;
1402235783Skib		mode_ptr->hsync_start = hactive_s + 1;
1403235783Skib		mode_ptr->hsync_end = hactive_s + 64;
1404235783Skib		if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
1405235783Skib			mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
1406235783Skib		mode_ptr->htotal = hactive_s + 96;
1407235783Skib
1408235783Skib		mode_ptr->vdisplay = vactive_s;
1409235783Skib		mode_ptr->vsync_start = vactive_s + 1;
1410235783Skib		mode_ptr->vsync_end = vactive_s + 32;
1411235783Skib		if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
1412235783Skib			mode_ptr->vsync_end = mode_ptr->vsync_start  + 1;
1413235783Skib		mode_ptr->vtotal = vactive_s + 33;
1414235783Skib
1415235783Skib		tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
1416235783Skib		tmp *= mode_ptr->htotal;
1417296548Sdumbbell		tmp = div_u64(tmp, 1000000);
1418235783Skib		mode_ptr->clock = (int) tmp;
1419235783Skib
1420235783Skib		mode_ptr->type = DRM_MODE_TYPE_DRIVER;
1421235783Skib		intel_tv_chose_preferred_modes(connector, mode_ptr);
1422235783Skib		drm_mode_probed_add(connector, mode_ptr);
1423235783Skib		count++;
1424235783Skib	}
1425235783Skib
1426235783Skib	return count;
1427235783Skib}
1428235783Skib
1429235783Skibstatic void
1430235783Skibintel_tv_destroy(struct drm_connector *connector)
1431235783Skib{
1432235783Skib	drm_connector_cleanup(connector);
1433235783Skib	free(connector, DRM_MEM_KMS);
1434235783Skib}
1435235783Skib
1436235783Skib
1437235783Skibstatic int
1438235783Skibintel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
1439235783Skib		      uint64_t val)
1440235783Skib{
1441235783Skib	struct drm_device *dev = connector->dev;
1442235783Skib	struct intel_tv *intel_tv = intel_attached_tv(connector);
1443235783Skib	struct drm_crtc *crtc = intel_tv->base.base.crtc;
1444235783Skib	int ret = 0;
1445235783Skib	bool changed = false;
1446235783Skib
1447280183Sdumbbell	ret = drm_object_property_set_value(&connector->base, property, val);
1448235783Skib	if (ret < 0)
1449235783Skib		goto out;
1450235783Skib
1451235783Skib	if (property == dev->mode_config.tv_left_margin_property &&
1452235783Skib		intel_tv->margin[TV_MARGIN_LEFT] != val) {
1453235783Skib		intel_tv->margin[TV_MARGIN_LEFT] = val;
1454235783Skib		changed = true;
1455235783Skib	} else if (property == dev->mode_config.tv_right_margin_property &&
1456235783Skib		intel_tv->margin[TV_MARGIN_RIGHT] != val) {
1457235783Skib		intel_tv->margin[TV_MARGIN_RIGHT] = val;
1458235783Skib		changed = true;
1459235783Skib	} else if (property == dev->mode_config.tv_top_margin_property &&
1460235783Skib		intel_tv->margin[TV_MARGIN_TOP] != val) {
1461235783Skib		intel_tv->margin[TV_MARGIN_TOP] = val;
1462235783Skib		changed = true;
1463235783Skib	} else if (property == dev->mode_config.tv_bottom_margin_property &&
1464235783Skib		intel_tv->margin[TV_MARGIN_BOTTOM] != val) {
1465235783Skib		intel_tv->margin[TV_MARGIN_BOTTOM] = val;
1466235783Skib		changed = true;
1467235783Skib	} else if (property == dev->mode_config.tv_mode_property) {
1468280183Sdumbbell		if (val >= ARRAY_SIZE(tv_modes)) {
1469235783Skib			ret = -EINVAL;
1470235783Skib			goto out;
1471235783Skib		}
1472235783Skib		if (!strcmp(intel_tv->tv_format, tv_modes[val].name))
1473235783Skib			goto out;
1474235783Skib
1475235783Skib		intel_tv->tv_format = tv_modes[val].name;
1476235783Skib		changed = true;
1477235783Skib	} else {
1478235783Skib		ret = -EINVAL;
1479235783Skib		goto out;
1480235783Skib	}
1481235783Skib
1482235783Skib	if (changed && crtc)
1483296548Sdumbbell		intel_set_mode(crtc, &crtc->mode,
1484296548Sdumbbell			       crtc->x, crtc->y, crtc->fb);
1485235783Skibout:
1486235783Skib	return ret;
1487235783Skib}
1488235783Skib
1489235783Skibstatic const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
1490235783Skib	.mode_fixup = intel_tv_mode_fixup,
1491235783Skib	.mode_set = intel_tv_mode_set,
1492296548Sdumbbell	.disable = intel_encoder_noop,
1493235783Skib};
1494235783Skib
1495235783Skibstatic const struct drm_connector_funcs intel_tv_connector_funcs = {
1496296548Sdumbbell	.dpms = intel_connector_dpms,
1497235783Skib	.detect = intel_tv_detect,
1498235783Skib	.destroy = intel_tv_destroy,
1499235783Skib	.set_property = intel_tv_set_property,
1500235783Skib	.fill_modes = drm_helper_probe_single_connector_modes,
1501235783Skib};
1502235783Skib
1503235783Skibstatic const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1504235783Skib	.mode_valid = intel_tv_mode_valid,
1505235783Skib	.get_modes = intel_tv_get_modes,
1506235783Skib	.best_encoder = intel_best_encoder,
1507235783Skib};
1508235783Skib
1509235783Skibstatic const struct drm_encoder_funcs intel_tv_enc_funcs = {
1510235783Skib	.destroy = intel_encoder_destroy,
1511235783Skib};
1512235783Skib
1513235783Skib/*
1514235783Skib * Enumerate the child dev array parsed from VBT to check whether
1515235783Skib * the integrated TV is present.
1516235783Skib * If it is present, return 1.
1517235783Skib * If it is not present, return false.
1518235783Skib * If no child dev is parsed from VBT, it assumes that the TV is present.
1519235783Skib */
1520235783Skibstatic int tv_is_present_in_vbt(struct drm_device *dev)
1521235783Skib{
1522235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
1523235783Skib	struct child_device_config *p_child;
1524235783Skib	int i, ret;
1525235783Skib
1526235783Skib	if (!dev_priv->child_dev_num)
1527235783Skib		return 1;
1528235783Skib
1529235783Skib	ret = 0;
1530235783Skib	for (i = 0; i < dev_priv->child_dev_num; i++) {
1531235783Skib		p_child = dev_priv->child_dev + i;
1532235783Skib		/*
1533235783Skib		 * If the device type is not TV, continue.
1534235783Skib		 */
1535235783Skib		if (p_child->device_type != DEVICE_TYPE_INT_TV &&
1536235783Skib			p_child->device_type != DEVICE_TYPE_TV)
1537235783Skib			continue;
1538235783Skib		/* Only when the addin_offset is non-zero, it is regarded
1539235783Skib		 * as present.
1540235783Skib		 */
1541235783Skib		if (p_child->addin_offset) {
1542235783Skib			ret = 1;
1543235783Skib			break;
1544235783Skib		}
1545235783Skib	}
1546235783Skib	return ret;
1547235783Skib}
1548235783Skib
1549235783Skibvoid
1550235783Skibintel_tv_init(struct drm_device *dev)
1551235783Skib{
1552235783Skib	struct drm_i915_private *dev_priv = dev->dev_private;
1553235783Skib	struct drm_connector *connector;
1554235783Skib	struct intel_tv *intel_tv;
1555235783Skib	struct intel_encoder *intel_encoder;
1556235783Skib	struct intel_connector *intel_connector;
1557235783Skib	u32 tv_dac_on, tv_dac_off, save_tv_dac;
1558280183Sdumbbell	char *tv_format_names[ARRAY_SIZE(tv_modes)];
1559235783Skib	int i, initial_mode = 0;
1560235783Skib
1561235783Skib	if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1562235783Skib		return;
1563235783Skib
1564235783Skib	if (!tv_is_present_in_vbt(dev)) {
1565235783Skib		DRM_DEBUG_KMS("Integrated TV is not present.\n");
1566235783Skib		return;
1567235783Skib	}
1568235783Skib	/* Even if we have an encoder we may not have a connector */
1569235783Skib	if (!dev_priv->int_tv_support)
1570235783Skib		return;
1571235783Skib
1572235783Skib	/*
1573235783Skib	 * Sanity check the TV output by checking to see if the
1574235783Skib	 * DAC register holds a value
1575235783Skib	 */
1576235783Skib	save_tv_dac = I915_READ(TV_DAC);
1577235783Skib
1578235783Skib	I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1579235783Skib	tv_dac_on = I915_READ(TV_DAC);
1580235783Skib
1581235783Skib	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1582235783Skib	tv_dac_off = I915_READ(TV_DAC);
1583235783Skib
1584235783Skib	I915_WRITE(TV_DAC, save_tv_dac);
1585235783Skib
1586235783Skib	/*
1587235783Skib	 * If the register does not hold the state change enable
1588235783Skib	 * bit, (either as a 0 or a 1), assume it doesn't really
1589235783Skib	 * exist
1590235783Skib	 */
1591235783Skib	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1592235783Skib	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1593235783Skib		return;
1594235783Skib
1595296548Sdumbbell	intel_tv = malloc(sizeof(struct intel_tv), DRM_MEM_KMS, M_WAITOK | M_ZERO);
1596296548Sdumbbell	if (!intel_tv) {
1597296548Sdumbbell		return;
1598296548Sdumbbell	}
1599235783Skib
1600296548Sdumbbell	intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO);
1601296548Sdumbbell	if (!intel_connector) {
1602296548Sdumbbell		free(intel_tv, DRM_MEM_KMS);
1603296548Sdumbbell		return;
1604296548Sdumbbell	}
1605296548Sdumbbell
1606235783Skib	intel_encoder = &intel_tv->base;
1607235783Skib	connector = &intel_connector->base;
1608235783Skib
1609235783Skib	/* The documentation, for the older chipsets at least, recommend
1610235783Skib	 * using a polling method rather than hotplug detection for TVs.
1611235783Skib	 * This is because in order to perform the hotplug detection, the PLLs
1612235783Skib	 * for the TV must be kept alive increasing power drain and starving
1613235783Skib	 * bandwidth from other encoders. Notably for instance, it causes
1614235783Skib	 * pipe underruns on Crestline when this encoder is supposedly idle.
1615235783Skib	 *
1616235783Skib	 * More recent chipsets favour HDMI rather than integrated S-Video.
1617235783Skib	 */
1618235783Skib	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1619235783Skib
1620235783Skib	drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1621235783Skib			   DRM_MODE_CONNECTOR_SVIDEO);
1622235783Skib
1623235783Skib	drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
1624235783Skib			 DRM_MODE_ENCODER_TVDAC);
1625235783Skib
1626296548Sdumbbell	intel_encoder->enable = intel_enable_tv;
1627296548Sdumbbell	intel_encoder->disable = intel_disable_tv;
1628296548Sdumbbell	intel_encoder->get_hw_state = intel_tv_get_hw_state;
1629296548Sdumbbell	intel_connector->get_hw_state = intel_connector_get_hw_state;
1630296548Sdumbbell
1631235783Skib	intel_connector_attach_encoder(intel_connector, intel_encoder);
1632235783Skib	intel_encoder->type = INTEL_OUTPUT_TVOUT;
1633235783Skib	intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1634296548Sdumbbell	intel_encoder->cloneable = false;
1635235783Skib	intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
1636235783Skib	intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
1637235783Skib	intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
1638235783Skib
1639235783Skib	/* BIOS margin values */
1640235783Skib	intel_tv->margin[TV_MARGIN_LEFT] = 54;
1641235783Skib	intel_tv->margin[TV_MARGIN_TOP] = 36;
1642235783Skib	intel_tv->margin[TV_MARGIN_RIGHT] = 46;
1643235783Skib	intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
1644235783Skib
1645235783Skib	intel_tv->tv_format = tv_modes[initial_mode].name;
1646235783Skib
1647235783Skib	drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs);
1648235783Skib	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1649235783Skib	connector->interlace_allowed = false;
1650235783Skib	connector->doublescan_allowed = false;
1651235783Skib
1652235783Skib	/* Create TV properties then attach current values */
1653280183Sdumbbell	for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
1654235783Skib		tv_format_names[i] = __DECONST(char *, tv_modes[i].name);
1655235783Skib	drm_mode_create_tv_properties(dev,
1656280183Sdumbbell				      ARRAY_SIZE(tv_modes),
1657235783Skib				      tv_format_names);
1658235783Skib
1659280183Sdumbbell	drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
1660235783Skib				   initial_mode);
1661280183Sdumbbell	drm_object_attach_property(&connector->base,
1662235783Skib				   dev->mode_config.tv_left_margin_property,
1663235783Skib				   intel_tv->margin[TV_MARGIN_LEFT]);
1664280183Sdumbbell	drm_object_attach_property(&connector->base,
1665235783Skib				   dev->mode_config.tv_top_margin_property,
1666235783Skib				   intel_tv->margin[TV_MARGIN_TOP]);
1667280183Sdumbbell	drm_object_attach_property(&connector->base,
1668235783Skib				   dev->mode_config.tv_right_margin_property,
1669235783Skib				   intel_tv->margin[TV_MARGIN_RIGHT]);
1670280183Sdumbbell	drm_object_attach_property(&connector->base,
1671235783Skib				   dev->mode_config.tv_bottom_margin_property,
1672235783Skib				   intel_tv->margin[TV_MARGIN_BOTTOM]);
1673235783Skib}
1674