intel_tv.c revision 280183
1193323Sed/*
2193323Sed * Copyright �� 2006-2008 Intel Corporation
3193323Sed *   Jesse Barnes <jesse.barnes@intel.com>
4193323Sed *
5193323Sed * Permission is hereby granted, free of charge, to any person obtaining a
6193323Sed * copy of this software and associated documentation files (the "Software"),
7193323Sed * to deal in the Software without restriction, including without limitation
8193323Sed * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9193323Sed * and/or sell copies of the Software, and to permit persons to whom the
10193323Sed * Software is furnished to do so, subject to the following conditions:
11193323Sed *
12193323Sed * The above copyright notice and this permission notice (including the next
13193323Sed * paragraph) shall be included in all copies or substantial portions of the
14193323Sed * Software.
15193323Sed *
16193323Sed * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17193323Sed * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18193323Sed * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19193323Sed * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20193323Sed * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21193323Sed * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22193323Sed * DEALINGS IN THE SOFTWARE.
23193323Sed *
24193323Sed * Authors:
25193323Sed *    Eric Anholt <eric@anholt.net>
26193323Sed *
27193323Sed */
28193323Sed
29193323Sed/** @file
30193323Sed * Integrated TV-out support for the 915GM and 945GM.
31193323Sed */
32193323Sed
33193323Sed#include <sys/cdefs.h>
34193323Sed__FBSDID("$FreeBSD: head/sys/dev/drm2/i915/intel_tv.c 280183 2015-03-17 18:50:33Z dumbbell $");
35193323Sed
36193323Sed#include <dev/drm2/drmP.h>
37198090Srdivacky#include <dev/drm2/drm.h>
38193323Sed#include <dev/drm2/drm_crtc.h>
39193323Sed#include <dev/drm2/drm_edid.h>
40193323Sed#include <dev/drm2/i915/i915_drm.h>
41193323Sed#include <dev/drm2/i915/i915_drv.h>
42193323Sed#include <dev/drm2/i915/intel_drv.h>
43193323Sed
44193323Sedenum tv_margin {
45193323Sed	TV_MARGIN_LEFT, TV_MARGIN_TOP,
46193323Sed	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
47193323Sed};
48193323Sed
49193323Sed/** Private structure for the integrated TV support */
50193323Sedstruct intel_tv {
51193323Sed	struct intel_encoder base;
52193323Sed
53193323Sed	int type;
54193323Sed	const char *tv_format;
55193323Sed	int margin[4];
56193323Sed	u32 save_TV_H_CTL_1;
57193323Sed	u32 save_TV_H_CTL_2;
58193323Sed	u32 save_TV_H_CTL_3;
59198892Srdivacky	u32 save_TV_V_CTL_1;
60193323Sed	u32 save_TV_V_CTL_2;
61193323Sed	u32 save_TV_V_CTL_3;
62193323Sed	u32 save_TV_V_CTL_4;
63193323Sed	u32 save_TV_V_CTL_5;
64198090Srdivacky	u32 save_TV_V_CTL_6;
65193323Sed	u32 save_TV_V_CTL_7;
66193323Sed	u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
67193323Sed
68193323Sed	u32 save_TV_CSC_Y;
69193323Sed	u32 save_TV_CSC_Y2;
70193323Sed	u32 save_TV_CSC_U;
71193323Sed	u32 save_TV_CSC_U2;
72193323Sed	u32 save_TV_CSC_V;
73193323Sed	u32 save_TV_CSC_V2;
74193323Sed	u32 save_TV_CLR_KNOBS;
75193323Sed	u32 save_TV_CLR_LEVEL;
76193323Sed	u32 save_TV_WIN_POS;
77193323Sed	u32 save_TV_WIN_SIZE;
78193323Sed	u32 save_TV_FILTER_CTL_1;
79193323Sed	u32 save_TV_FILTER_CTL_2;
80193323Sed	u32 save_TV_FILTER_CTL_3;
81193323Sed
82193323Sed	u32 save_TV_H_LUMA[60];
83193323Sed	u32 save_TV_H_CHROMA[60];
84193323Sed	u32 save_TV_V_LUMA[43];
85193323Sed	u32 save_TV_V_CHROMA[43];
86193323Sed
87193323Sed	u32 save_TV_DAC;
88193323Sed	u32 save_TV_CTL;
89193323Sed};
90193323Sed
91193323Sedstruct video_levels {
92193323Sed	int blank, black, burst;
93193323Sed};
94193323Sed
95193323Sedstruct color_conversion {
96193323Sed	u16 ry, gy, by, ay;
97193323Sed	u16 ru, gu, bu, au;
98193323Sed	u16 rv, gv, bv, av;
99193323Sed};
100193323Sed
101193323Sedstatic const u32 filter_table[] = {
102193323Sed	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
103193323Sed	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
104193323Sed	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
105193323Sed	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
106193323Sed	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
107193323Sed	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
108193323Sed	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
109198090Srdivacky	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
110198090Srdivacky	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
111198090Srdivacky	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
112198090Srdivacky	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
113198090Srdivacky	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
114198090Srdivacky	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
115199511Srdivacky	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
116198090Srdivacky	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
117198090Srdivacky	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
118198090Srdivacky	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
119198090Srdivacky	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
120198090Srdivacky	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
121198090Srdivacky	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
122198090Srdivacky	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
123193323Sed	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
124193323Sed	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
125198090Srdivacky	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
126193323Sed	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
127193323Sed	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
128193323Sed	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
129193323Sed	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
130193323Sed	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
131198090Srdivacky	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
132198090Srdivacky	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
133193323Sed	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
134193323Sed	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
135193323Sed	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
136193323Sed	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
137193323Sed	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
138193323Sed	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
139193323Sed	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
140193323Sed	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
141193323Sed	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
142193323Sed	0x28003100, 0x28002F00, 0x00003100, 0x36403000,
143193323Sed	0x2D002CC0, 0x30003640, 0x2D0036C0,
144193323Sed	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
145193323Sed	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
146193323Sed	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
147193323Sed	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
148193323Sed	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
149193323Sed	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
150193323Sed	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
151193323Sed	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
152193323Sed	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
153193323Sed	0x28003100, 0x28002F00, 0x00003100,
154193323Sed};
155193323Sed
156193323Sed/*
157193323Sed * Color conversion values have 3 separate fixed point formats:
158193323Sed *
159193323Sed * 10 bit fields (ay, au)
160193323Sed *   1.9 fixed point (b.bbbbbbbbb)
161193323Sed * 11 bit fields (ry, by, ru, gu, gv)
162193323Sed *   exp.mantissa (ee.mmmmmmmmm)
163204642Srdivacky *   ee = 00 = 10^-1 (0.mmmmmmmmm)
164193323Sed *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
165193323Sed *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
166193323Sed *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
167193323Sed * 12 bit fields (gy, rv, bu)
168193323Sed *   exp.mantissa (eee.mmmmmmmmm)
169193323Sed *   eee = 000 = 10^-1 (0.mmmmmmmmm)
170193323Sed *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
171193323Sed *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
172193323Sed *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
173193323Sed *   eee = 100 = reserved
174193323Sed *   eee = 101 = reserved
175193323Sed *   eee = 110 = reserved
176193323Sed *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
177193323Sed *
178193323Sed * Saturation and contrast are 8 bits, with their own representation:
179193323Sed * 8 bit field (saturation, contrast)
180193323Sed *   exp.mantissa (ee.mmmmmm)
181193323Sed *   ee = 00 = 10^-1 (0.mmmmmm)
182193323Sed *   ee = 01 = 10^0 (m.mmmmm)
183193323Sed *   ee = 10 = 10^1 (mm.mmmm)
184193323Sed *   ee = 11 = 10^2 (mmm.mmm)
185193323Sed *
186193323Sed * Simple conversion function:
187193323Sed *
188193323Sed * static u32
189193323Sed * float_to_csc_11(float f)
190193323Sed * {
191193323Sed *     u32 exp;
192193323Sed *     u32 mant;
193193323Sed *     u32 ret;
194193323Sed *
195193323Sed *     if (f < 0)
196193323Sed *         f = -f;
197193323Sed *
198193323Sed *     if (f >= 1) {
199193323Sed *         exp = 0x7;
200193323Sed *	   mant = 1 << 8;
201193323Sed *     } else {
202193323Sed *         for (exp = 0; exp < 3 && f < 0.5; exp++)
203193323Sed *	   f *= 2.0;
204193323Sed *         mant = (f * (1 << 9) + 0.5);
205193323Sed *         if (mant >= (1 << 9))
206193323Sed *             mant = (1 << 9) - 1;
207193323Sed *     }
208193323Sed *     ret = (exp << 9) | mant;
209193323Sed *     return ret;
210193323Sed * }
211193323Sed */
212193323Sed
213193323Sed/*
214200581Srdivacky * Behold, magic numbers!  If we plant them they might grow a big
215193323Sed * s-video cable to the sky... or something.
216203954Srdivacky *
217203954Srdivacky * Pre-converted to appropriate hex value.
218203954Srdivacky */
219193323Sed
220193323Sed/*
221193323Sed * PAL & NTSC values for composite & s-video connections
222193323Sed */
223193323Sedstatic const struct color_conversion ntsc_m_csc_composite = {
224193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
225193323Sed	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
226193323Sed	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
227193323Sed};
228193323Sed
229193323Sedstatic const struct video_levels ntsc_m_levels_composite = {
230193323Sed	.blank = 225, .black = 267, .burst = 113,
231193323Sed};
232193323Sed
233193323Sedstatic const struct color_conversion ntsc_m_csc_svideo = {
234193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
235193323Sed	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
236193323Sed	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
237193323Sed};
238193323Sed
239193323Sedstatic const struct video_levels ntsc_m_levels_svideo = {
240193323Sed	.blank = 266, .black = 316, .burst = 133,
241193323Sed};
242193323Sed
243193323Sedstatic const struct color_conversion ntsc_j_csc_composite = {
244193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
245193323Sed	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
246193323Sed	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
247193323Sed};
248193323Sed
249193323Sedstatic const struct video_levels ntsc_j_levels_composite = {
250193323Sed	.blank = 225, .black = 225, .burst = 113,
251193323Sed};
252193323Sed
253193323Sedstatic const struct color_conversion ntsc_j_csc_svideo = {
254193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
255193323Sed	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
256193323Sed	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
257193323Sed};
258193323Sed
259193323Sedstatic const struct video_levels ntsc_j_levels_svideo = {
260193323Sed	.blank = 266, .black = 266, .burst = 133,
261193323Sed};
262193323Sed
263193323Sedstatic const struct color_conversion pal_csc_composite = {
264193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
265193323Sed	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
266193323Sed	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
267193323Sed};
268193323Sed
269193323Sedstatic const struct video_levels pal_levels_composite = {
270193323Sed	.blank = 237, .black = 237, .burst = 118,
271193323Sed};
272193323Sed
273193323Sedstatic const struct color_conversion pal_csc_svideo = {
274193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
275193323Sed	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
276193323Sed	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
277193323Sed};
278193323Sed
279193323Sedstatic const struct video_levels pal_levels_svideo = {
280193323Sed	.blank = 280, .black = 280, .burst = 139,
281193323Sed};
282193323Sed
283193323Sedstatic const struct color_conversion pal_m_csc_composite = {
284193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
285193323Sed	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
286193323Sed	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
287193323Sed};
288193323Sed
289193323Sedstatic const struct video_levels pal_m_levels_composite = {
290193323Sed	.blank = 225, .black = 267, .burst = 113,
291193323Sed};
292193323Sed
293193323Sedstatic const struct color_conversion pal_m_csc_svideo = {
294193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
295193323Sed	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
296193323Sed	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
297193323Sed};
298193323Sed
299193323Sedstatic const struct video_levels pal_m_levels_svideo = {
300193323Sed	.blank = 266, .black = 316, .burst = 133,
301193323Sed};
302193323Sed
303193323Sedstatic const struct color_conversion pal_n_csc_composite = {
304193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
305193323Sed	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
306193323Sed	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
307193323Sed};
308193323Sed
309193323Sedstatic const struct video_levels pal_n_levels_composite = {
310193323Sed	.blank = 225, .black = 267, .burst = 118,
311193323Sed};
312193323Sed
313193323Sedstatic const struct color_conversion pal_n_csc_svideo = {
314193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
315193323Sed	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
316193323Sed	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
317193323Sed};
318193323Sed
319193323Sedstatic const struct video_levels pal_n_levels_svideo = {
320193323Sed	.blank = 266, .black = 316, .burst = 139,
321193323Sed};
322203954Srdivacky
323193323Sed/*
324193323Sed * Component connections
325193323Sed */
326193323Sedstatic const struct color_conversion sdtv_csc_yprpb = {
327193323Sed	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
328193323Sed	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
329193323Sed	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
330193323Sed};
331193323Sed
332193323Sedstatic const struct color_conversion sdtv_csc_rgb = {
333193323Sed	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
334193323Sed	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
335193323Sed	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
336193323Sed};
337193323Sed
338193323Sedstatic const struct color_conversion hdtv_csc_yprpb = {
339193323Sed	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
340193323Sed	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
341193323Sed	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
342193323Sed};
343193323Sed
344193323Sedstatic const struct color_conversion hdtv_csc_rgb = {
345203954Srdivacky	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
346193323Sed	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
347193323Sed	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
348193323Sed};
349193323Sed
350193323Sedstatic const struct video_levels component_levels = {
351193323Sed	.blank = 279, .black = 279, .burst = 0,
352193323Sed};
353193323Sed
354193323Sed
355193323Sedstruct tv_mode {
356193323Sed	const char *name;
357193323Sed	int clock;
358193323Sed	int refresh; /* in millihertz (for precision) */
359193323Sed	u32 oversample;
360193323Sed	int hsync_end, hblank_start, hblank_end, htotal;
361193323Sed	bool progressive, trilevel_sync, component_only;
362193323Sed	int vsync_start_f1, vsync_start_f2, vsync_len;
363193323Sed	bool veq_ena;
364193323Sed	int veq_start_f1, veq_start_f2, veq_len;
365193323Sed	int vi_end_f1, vi_end_f2, nbr_end;
366193323Sed	bool burst_ena;
367193323Sed	int hburst_start, hburst_len;
368193323Sed	int vburst_start_f1, vburst_end_f1;
369193323Sed	int vburst_start_f2, vburst_end_f2;
370193323Sed	int vburst_start_f3, vburst_end_f3;
371203954Srdivacky	int vburst_start_f4, vburst_end_f4;
372193323Sed	/*
373193323Sed	 * subcarrier programming
374203954Srdivacky	 */
375193323Sed	int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
376193323Sed	u32 sc_reset;
377203954Srdivacky	bool pal_burst;
378193323Sed	/*
379193323Sed	 * blank/black levels
380193323Sed	 */
381193323Sed	const struct video_levels *composite_levels, *svideo_levels;
382193323Sed	const struct color_conversion *composite_color, *svideo_color;
383193323Sed	const u32 *filter_table;
384193323Sed	int max_srcw;
385193323Sed};
386193323Sed
387193323Sed
388193323Sed/*
389193323Sed * Sub carrier DDA
390193323Sed *
391193323Sed *  I think this works as follows:
392193323Sed *
393193323Sed *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
394193323Sed *
395193323Sed * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
396193323Sed *
397193323Sed * So,
398193323Sed *  dda1_ideal = subcarrier/pixel * 4096
399193323Sed *  dda1_inc = floor (dda1_ideal)
400193323Sed *  dda2 = dda1_ideal - dda1_inc
401193323Sed *
402193323Sed *  then pick a ratio for dda2 that gives the closest approximation. If
403193323Sed *  you can't get close enough, you can play with dda3 as well. This
404193323Sed *  seems likely to happen when dda2 is small as the jumps would be larger
405193323Sed *
406193323Sed * To invert this,
407193323Sed *
408193323Sed *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
409193323Sed *
410193323Sed * The constants below were all computed using a 107.520MHz clock
411193323Sed */
412193323Sed
413193323Sed/**
414193323Sed * Register programming values for TV modes.
415193323Sed *
416193323Sed * These values account for -1s required.
417193323Sed */
418200581Srdivacky
419193323Sedstatic const struct tv_mode tv_modes[] = {
420193323Sed	{
421193323Sed		.name		= "NTSC-M",
422193323Sed		.clock		= 108000,
423193323Sed		.refresh	= 59940,
424193323Sed		.oversample	= TV_OVERSAMPLE_8X,
425193323Sed		.component_only = 0,
426193323Sed		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
427193323Sed
428193323Sed		.hsync_end	= 64,		    .hblank_end		= 124,
429193323Sed		.hblank_start	= 836,		    .htotal		= 857,
430193323Sed
431193323Sed		.progressive	= false,	    .trilevel_sync = false,
432193323Sed
433193323Sed		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
434193323Sed		.vsync_len	= 6,
435203954Srdivacky
436193323Sed		.veq_ena	= true,		    .veq_start_f1	= 0,
437193323Sed		.veq_start_f2	= 1,		    .veq_len		= 18,
438193323Sed
439193323Sed		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
440193323Sed		.nbr_end	= 240,
441193323Sed
442193323Sed		.burst_ena	= true,
443193323Sed		.hburst_start	= 72,		    .hburst_len		= 34,
444193323Sed		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
445193323Sed		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
446193323Sed		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
447193323Sed		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
448193323Sed
449193323Sed		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
450193323Sed		.dda1_inc	=    135,
451193323Sed		.dda2_inc	=  20800,	    .dda2_size		=  27456,
452193323Sed		.dda3_inc	=      0,	    .dda3_size		=      0,
453193323Sed		.sc_reset	= TV_SC_RESET_EVERY_4,
454193323Sed		.pal_burst	= false,
455193323Sed
456193323Sed		.composite_levels = &ntsc_m_levels_composite,
457204792Srdivacky		.composite_color = &ntsc_m_csc_composite,
458204792Srdivacky		.svideo_levels  = &ntsc_m_levels_svideo,
459193323Sed		.svideo_color = &ntsc_m_csc_svideo,
460204792Srdivacky
461193323Sed		.filter_table = filter_table,
462193323Sed	},
463193323Sed	{
464193323Sed		.name		= "NTSC-443",
465193323Sed		.clock		= 108000,
466193323Sed		.refresh	= 59940,
467193323Sed		.oversample	= TV_OVERSAMPLE_8X,
468193323Sed		.component_only = 0,
469193323Sed		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
470193323Sed		.hsync_end	= 64,		    .hblank_end		= 124,
471193323Sed		.hblank_start	= 836,		    .htotal		= 857,
472193323Sed
473193323Sed		.progressive	= false,	    .trilevel_sync = false,
474193323Sed
475193323Sed		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7,
476193323Sed		.vsync_len	= 6,
477193323Sed
478193323Sed		.veq_ena	= true,		    .veq_start_f1	= 0,
479193323Sed		.veq_start_f2	= 1,		    .veq_len		= 18,
480193323Sed
481193323Sed		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
482193323Sed		.nbr_end	= 240,
483193323Sed
484193323Sed		.burst_ena	= true,
485193323Sed		.hburst_start	= 72,		    .hburst_len		= 34,
486193323Sed		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
487193323Sed		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
488193323Sed		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
489193323Sed		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
490193323Sed
491193323Sed		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
492193323Sed		.dda1_inc       =    168,
493193323Sed		.dda2_inc       =   4093,       .dda2_size      =  27456,
494193323Sed		.dda3_inc       =    310,       .dda3_size      =    525,
495193323Sed		.sc_reset   = TV_SC_RESET_NEVER,
496193323Sed		.pal_burst  = false,
497193323Sed
498193323Sed		.composite_levels = &ntsc_m_levels_composite,
499193323Sed		.composite_color = &ntsc_m_csc_composite,
500193323Sed		.svideo_levels  = &ntsc_m_levels_svideo,
501193323Sed		.svideo_color = &ntsc_m_csc_svideo,
502193323Sed
503193323Sed		.filter_table = filter_table,
504193323Sed	},
505193323Sed	{
506193323Sed		.name		= "NTSC-J",
507193323Sed		.clock		= 108000,
508193323Sed		.refresh	= 59940,
509193323Sed		.oversample	= TV_OVERSAMPLE_8X,
510193323Sed		.component_only = 0,
511193323Sed
512193323Sed		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
513193323Sed		.hsync_end	= 64,		    .hblank_end		= 124,
514193323Sed		.hblank_start = 836,	    .htotal		= 857,
515193323Sed
516193323Sed		.progressive	= false,    .trilevel_sync = false,
517193323Sed
518193323Sed		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7,
519193323Sed		.vsync_len	= 6,
520193323Sed
521193323Sed		.veq_ena      = true,	    .veq_start_f1	= 0,
522193323Sed		.veq_start_f2 = 1,	    .veq_len		= 18,
523193323Sed
524193323Sed		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
525193323Sed		.nbr_end	= 240,
526193323Sed
527193323Sed		.burst_ena	= true,
528193323Sed		.hburst_start	= 72,		    .hburst_len		= 34,
529193323Sed		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
530193323Sed		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
531193323Sed		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
532193323Sed		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
533193323Sed
534193323Sed		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
535193323Sed		.dda1_inc	=    135,
536193323Sed		.dda2_inc	=  20800,	    .dda2_size		=  27456,
537193323Sed		.dda3_inc	=      0,	    .dda3_size		=      0,
538193323Sed		.sc_reset	= TV_SC_RESET_EVERY_4,
539193323Sed		.pal_burst	= false,
540193323Sed
541193323Sed		.composite_levels = &ntsc_j_levels_composite,
542193323Sed		.composite_color = &ntsc_j_csc_composite,
543193323Sed		.svideo_levels  = &ntsc_j_levels_svideo,
544193323Sed		.svideo_color = &ntsc_j_csc_svideo,
545193323Sed
546193323Sed		.filter_table = filter_table,
547193323Sed	},
548193323Sed	{
549193323Sed		.name		= "PAL-M",
550193323Sed		.clock		= 108000,
551193323Sed		.refresh	= 59940,
552193323Sed		.oversample	= TV_OVERSAMPLE_8X,
553193323Sed		.component_only = 0,
554193323Sed
555193323Sed		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
556193323Sed		.hsync_end	= 64,		  .hblank_end		= 124,
557193323Sed		.hblank_start = 836,	  .htotal		= 857,
558193323Sed
559193323Sed		.progressive	= false,	    .trilevel_sync = false,
560193323Sed
561193323Sed		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7,
562193323Sed		.vsync_len	= 6,
563193323Sed
564193323Sed		.veq_ena	= true,		    .veq_start_f1	= 0,
565193323Sed		.veq_start_f2	= 1,		    .veq_len		= 18,
566193323Sed
567193323Sed		.vi_end_f1	= 20,		    .vi_end_f2		= 21,
568193323Sed		.nbr_end	= 240,
569193323Sed
570193323Sed		.burst_ena	= true,
571193323Sed		.hburst_start	= 72,		    .hburst_len		= 34,
572193323Sed		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240,
573193323Sed		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240,
574193323Sed		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,
575202375Srdivacky		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240,
576193323Sed
577193323Sed		/* desired 3.5800000 actual 3.5800000 clock 107.52 */
578193323Sed		.dda1_inc	=    135,
579202375Srdivacky		.dda2_inc	=  16704,	    .dda2_size		=  27456,
580193323Sed		.dda3_inc	=      0,	    .dda3_size		=      0,
581193323Sed		.sc_reset	= TV_SC_RESET_EVERY_8,
582193323Sed		.pal_burst  = true,
583202375Srdivacky
584193323Sed		.composite_levels = &pal_m_levels_composite,
585193323Sed		.composite_color = &pal_m_csc_composite,
586193323Sed		.svideo_levels  = &pal_m_levels_svideo,
587193323Sed		.svideo_color = &pal_m_csc_svideo,
588193323Sed
589193323Sed		.filter_table = filter_table,
590193323Sed	},
591193323Sed	{
592193323Sed		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
593193323Sed		.name	    = "PAL-N",
594193323Sed		.clock		= 108000,
595193323Sed		.refresh	= 50000,
596193323Sed		.oversample	= TV_OVERSAMPLE_8X,
597193323Sed		.component_only = 0,
598193323Sed
599193323Sed		.hsync_end	= 64,		    .hblank_end		= 128,
600193323Sed		.hblank_start = 844,	    .htotal		= 863,
601193323Sed
602193323Sed		.progressive  = false,    .trilevel_sync = false,
603193323Sed
604193323Sed
605193323Sed		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7,
606193323Sed		.vsync_len	= 6,
607193323Sed
608193323Sed		.veq_ena	= true,		    .veq_start_f1	= 0,
609193323Sed		.veq_start_f2	= 1,		    .veq_len		= 18,
610193323Sed
611193323Sed		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
612193323Sed		.nbr_end	= 286,
613193323Sed
614193323Sed		.burst_ena	= true,
615193323Sed		.hburst_start = 73,	    .hburst_len		= 34,
616193323Sed		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285,
617193323Sed		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286,
618193323Sed		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,
619193323Sed		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285,
620193323Sed
621193323Sed
622193323Sed		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
623193323Sed		.dda1_inc       =    135,
624193323Sed		.dda2_inc       =  23578,       .dda2_size      =  27648,
625193323Sed		.dda3_inc       =    134,       .dda3_size      =    625,
626193323Sed		.sc_reset   = TV_SC_RESET_EVERY_8,
627193323Sed		.pal_burst  = true,
628193323Sed
629193323Sed		.composite_levels = &pal_n_levels_composite,
630202375Srdivacky		.composite_color = &pal_n_csc_composite,
631202375Srdivacky		.svideo_levels  = &pal_n_levels_svideo,
632193323Sed		.svideo_color = &pal_n_csc_svideo,
633193323Sed
634193323Sed		.filter_table = filter_table,
635193323Sed	},
636193323Sed	{
637193323Sed		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
638193323Sed		.name	    = "PAL",
639193323Sed		.clock		= 108000,
640193323Sed		.refresh	= 50000,
641193323Sed		.oversample	= TV_OVERSAMPLE_8X,
642193323Sed		.component_only = 0,
643193323Sed
644193323Sed		.hsync_end	= 64,		    .hblank_end		= 142,
645200581Srdivacky		.hblank_start	= 844,	    .htotal		= 863,
646193323Sed
647193323Sed		.progressive	= false,    .trilevel_sync = false,
648193323Sed
649193323Sed		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6,
650193323Sed		.vsync_len	= 5,
651193323Sed
652193323Sed		.veq_ena	= true,	    .veq_start_f1	= 0,
653193323Sed		.veq_start_f2	= 1,	    .veq_len		= 15,
654193323Sed
655193323Sed		.vi_end_f1	= 24,		    .vi_end_f2		= 25,
656193323Sed		.nbr_end	= 286,
657193323Sed
658193323Sed		.burst_ena	= true,
659193323Sed		.hburst_start	= 73,		    .hburst_len		= 32,
660193323Sed		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285,
661193323Sed		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286,
662193323Sed		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,
663193323Sed		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285,
664193323Sed
665193323Sed		/* desired 4.4336180 actual 4.4336180 clock 107.52 */
666193323Sed		.dda1_inc       =    168,
667193323Sed		.dda2_inc       =   4122,       .dda2_size      =  27648,
668193323Sed		.dda3_inc       =     67,       .dda3_size      =    625,
669193323Sed		.sc_reset   = TV_SC_RESET_EVERY_8,
670193323Sed		.pal_burst  = true,
671193323Sed
672193323Sed		.composite_levels = &pal_levels_composite,
673193323Sed		.composite_color = &pal_csc_composite,
674193323Sed		.svideo_levels  = &pal_levels_svideo,
675193323Sed		.svideo_color = &pal_csc_svideo,
676193323Sed
677193323Sed		.filter_table = filter_table,
678193323Sed	},
679193323Sed	{
680193323Sed		.name       = "720p@60Hz",
681193323Sed		.clock		= 148800,
682193323Sed		.refresh	= 60000,
683193323Sed		.oversample     = TV_OVERSAMPLE_2X,
684193323Sed		.component_only = 1,
685193323Sed
686193323Sed		.hsync_end      = 80,               .hblank_end         = 300,
687193323Sed		.hblank_start   = 1580,             .htotal             = 1649,
688193323Sed
689193323Sed		.progressive	= true,		    .trilevel_sync = true,
690193323Sed
691193323Sed		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
692193323Sed		.vsync_len      = 10,
693193323Sed
694193323Sed		.veq_ena        = false,
695193323Sed
696193323Sed		.vi_end_f1      = 29,               .vi_end_f2          = 29,
697193323Sed		.nbr_end        = 719,
698193323Sed
699193323Sed		.burst_ena      = false,
700193323Sed
701193323Sed		.filter_table = filter_table,
702193323Sed	},
703193323Sed	{
704193323Sed		.name       = "720p@50Hz",
705193323Sed		.clock		= 148800,
706193323Sed		.refresh	= 50000,
707193323Sed		.oversample     = TV_OVERSAMPLE_2X,
708193323Sed		.component_only = 1,
709193323Sed
710193323Sed		.hsync_end      = 80,               .hblank_end         = 300,
711193323Sed		.hblank_start   = 1580,             .htotal             = 1979,
712193323Sed
713193323Sed		.progressive	= true,		    .trilevel_sync = true,
714193323Sed
715193323Sed		.vsync_start_f1 = 10,               .vsync_start_f2     = 10,
716193323Sed		.vsync_len      = 10,
717193323Sed
718193323Sed		.veq_ena        = false,
719193323Sed
720193323Sed		.vi_end_f1      = 29,               .vi_end_f2          = 29,
721193323Sed		.nbr_end        = 719,
722193323Sed
723193323Sed		.burst_ena      = false,
724193323Sed
725193323Sed		.filter_table = filter_table,
726193323Sed		.max_srcw = 800
727193323Sed	},
728193323Sed	{
729193323Sed		.name       = "1080i@50Hz",
730193323Sed		.clock		= 148800,
731199511Srdivacky		.refresh	= 50000,
732193323Sed		.oversample     = TV_OVERSAMPLE_2X,
733193323Sed		.component_only = 1,
734193323Sed
735193323Sed		.hsync_end      = 88,               .hblank_end         = 235,
736193323Sed		.hblank_start   = 2155,             .htotal             = 2639,
737193323Sed
738193323Sed		.progressive	= false,	  .trilevel_sync = true,
739193323Sed
740193323Sed		.vsync_start_f1 = 4,              .vsync_start_f2     = 5,
741193323Sed		.vsync_len      = 10,
742193323Sed
743193323Sed		.veq_ena	= true,	    .veq_start_f1	= 4,
744193323Sed		.veq_start_f2   = 4,	    .veq_len		= 10,
745193323Sed
746199511Srdivacky
747193323Sed		.vi_end_f1      = 21,           .vi_end_f2          = 22,
748193323Sed		.nbr_end        = 539,
749193323Sed
750193323Sed		.burst_ena      = false,
751193323Sed
752198090Srdivacky		.filter_table = filter_table,
753198090Srdivacky	},
754198090Srdivacky	{
755198090Srdivacky		.name       = "1080i@60Hz",
756198090Srdivacky		.clock		= 148800,
757198090Srdivacky		.refresh	= 60000,
758198090Srdivacky		.oversample     = TV_OVERSAMPLE_2X,
759198090Srdivacky		.component_only = 1,
760198090Srdivacky
761198090Srdivacky		.hsync_end      = 88,               .hblank_end         = 235,
762198090Srdivacky		.hblank_start   = 2155,             .htotal             = 2199,
763198090Srdivacky
764198090Srdivacky		.progressive	= false,	    .trilevel_sync = true,
765198090Srdivacky
766198090Srdivacky		.vsync_start_f1 = 4,               .vsync_start_f2     = 5,
767198090Srdivacky		.vsync_len      = 10,
768198090Srdivacky
769198090Srdivacky		.veq_ena	= true,		    .veq_start_f1	= 4,
770198090Srdivacky		.veq_start_f2	= 4,		    .veq_len		= 10,
771198090Srdivacky
772198090Srdivacky
773198090Srdivacky		.vi_end_f1      = 21,               .vi_end_f2          = 22,
774198090Srdivacky		.nbr_end        = 539,
775198090Srdivacky
776198090Srdivacky		.burst_ena      = false,
777198090Srdivacky
778198090Srdivacky		.filter_table = filter_table,
779198090Srdivacky	},
780198090Srdivacky};
781198090Srdivacky
782198090Srdivackystatic struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder)
783198090Srdivacky{
784198090Srdivacky	return container_of(encoder, struct intel_tv, base.base);
785198090Srdivacky}
786199511Srdivacky
787198090Srdivackystatic struct intel_tv *intel_attached_tv(struct drm_connector *connector)
788198090Srdivacky{
789198090Srdivacky	return container_of(intel_attached_encoder(connector),
790198090Srdivacky			    struct intel_tv,
791198090Srdivacky			    base);
792198090Srdivacky}
793198090Srdivacky
794198090Srdivackystatic void
795198090Srdivackyintel_tv_dpms(struct drm_encoder *encoder, int mode)
796198090Srdivacky{
797198090Srdivacky	struct drm_device *dev = encoder->dev;
798198090Srdivacky	struct drm_i915_private *dev_priv = dev->dev_private;
799198090Srdivacky
800198090Srdivacky	switch (mode) {
801198090Srdivacky	case DRM_MODE_DPMS_ON:
802198090Srdivacky		I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
803198090Srdivacky		break;
804198090Srdivacky	case DRM_MODE_DPMS_STANDBY:
805198090Srdivacky	case DRM_MODE_DPMS_SUSPEND:
806198090Srdivacky	case DRM_MODE_DPMS_OFF:
807198090Srdivacky		I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
808198090Srdivacky		break;
809198090Srdivacky	}
810198090Srdivacky}
811198090Srdivacky
812198090Srdivackystatic const struct tv_mode *
813198090Srdivackyintel_tv_mode_lookup(const char *tv_format)
814198090Srdivacky{
815198090Srdivacky	int i;
816198090Srdivacky
817198090Srdivacky	for (i = 0; i < DRM_ARRAY_SIZE(tv_modes); i++) {
818198090Srdivacky		const struct tv_mode *tv_mode = &tv_modes[i];
819198090Srdivacky
820198090Srdivacky		if (!strcmp(tv_format, tv_mode->name))
821198090Srdivacky			return tv_mode;
822198090Srdivacky	}
823198090Srdivacky	return NULL;
824198090Srdivacky}
825198090Srdivacky
826198090Srdivackystatic const struct tv_mode *
827198090Srdivackyintel_tv_mode_find(struct intel_tv *intel_tv)
828198090Srdivacky{
829198090Srdivacky	return intel_tv_mode_lookup(intel_tv->tv_format);
830198090Srdivacky}
831198090Srdivacky
832198090Srdivackystatic enum drm_mode_status
833198090Srdivackyintel_tv_mode_valid(struct drm_connector *connector,
834198090Srdivacky		    struct drm_display_mode *mode)
835198090Srdivacky{
836198090Srdivacky	struct intel_tv *intel_tv = intel_attached_tv(connector);
837199511Srdivacky	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
838198090Srdivacky
839198090Srdivacky	/* Ensure TV refresh is close to desired refresh */
840198090Srdivacky	if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
841198090Srdivacky				< 1000)
842198090Srdivacky		return MODE_OK;
843198090Srdivacky
844198090Srdivacky	return MODE_CLOCK_RANGE;
845198090Srdivacky}
846198090Srdivacky
847198090Srdivacky
848198090Srdivackystatic bool
849198090Srdivackyintel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode,
850198090Srdivacky		    struct drm_display_mode *adjusted_mode)
851198090Srdivacky{
852198090Srdivacky	struct drm_device *dev = encoder->dev;
853198090Srdivacky	struct drm_mode_config *drm_config = &dev->mode_config;
854198090Srdivacky	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
855198090Srdivacky	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
856198090Srdivacky	struct drm_encoder *other_encoder;
857198090Srdivacky
858198090Srdivacky	if (!tv_mode)
859198090Srdivacky		return false;
860198090Srdivacky
861198090Srdivacky	/* FIXME: lock encoder list */
862198090Srdivacky	list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
863198090Srdivacky		if (other_encoder != encoder &&
864198090Srdivacky		    other_encoder->crtc == encoder->crtc)
865198090Srdivacky			return false;
866198090Srdivacky	}
867198090Srdivacky
868198090Srdivacky	adjusted_mode->clock = tv_mode->clock;
869198090Srdivacky	return true;
870198090Srdivacky}
871198090Srdivacky
872198090Srdivackystatic void
873198090Srdivackyintel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
874198090Srdivacky		  struct drm_display_mode *adjusted_mode)
875198090Srdivacky{
876198090Srdivacky	struct drm_device *dev = encoder->dev;
877198090Srdivacky	struct drm_i915_private *dev_priv = dev->dev_private;
878198090Srdivacky	struct drm_crtc *crtc = encoder->crtc;
879198090Srdivacky	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
880198090Srdivacky	struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
881198090Srdivacky	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
882198090Srdivacky	u32 tv_ctl;
883198090Srdivacky	u32 hctl1, hctl2, hctl3;
884198090Srdivacky	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
885198090Srdivacky	u32 scctl1, scctl2, scctl3;
886198090Srdivacky	int i, j;
887198090Srdivacky	const struct video_levels *video_levels;
888198090Srdivacky	const struct color_conversion *color_conversion;
889198090Srdivacky	bool burst_ena;
890193323Sed	int pipe = intel_crtc->pipe;
891193323Sed
892193323Sed	if (!tv_mode)
893202375Srdivacky		return;	/* can't happen (mode_prepare prevents this) */
894193323Sed
895193323Sed	tv_ctl = I915_READ(TV_CTL);
896193323Sed	tv_ctl &= TV_CTL_SAVE;
897193323Sed
898193323Sed	switch (intel_tv->type) {
899198090Srdivacky	default:
900193323Sed	case DRM_MODE_CONNECTOR_Unknown:
901193323Sed	case DRM_MODE_CONNECTOR_Composite:
902193323Sed		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
903202375Srdivacky		video_levels = tv_mode->composite_levels;
904202375Srdivacky		color_conversion = tv_mode->composite_color;
905198090Srdivacky		burst_ena = tv_mode->burst_ena;
906193323Sed		break;
907193323Sed	case DRM_MODE_CONNECTOR_Component:
908193323Sed		tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
909193323Sed		video_levels = &component_levels;
910193323Sed		if (tv_mode->burst_ena)
911198090Srdivacky			color_conversion = &sdtv_csc_yprpb;
912198090Srdivacky		else
913198090Srdivacky			color_conversion = &hdtv_csc_yprpb;
914198090Srdivacky		burst_ena = false;
915193323Sed		break;
916193323Sed	case DRM_MODE_CONNECTOR_SVIDEO:
917193323Sed		tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
918193323Sed		video_levels = tv_mode->svideo_levels;
919193323Sed		color_conversion = tv_mode->svideo_color;
920193323Sed		burst_ena = tv_mode->burst_ena;
921193323Sed		break;
922193323Sed	}
923193323Sed	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
924193323Sed		(tv_mode->htotal << TV_HTOTAL_SHIFT);
925200581Srdivacky
926203954Srdivacky	hctl2 = (tv_mode->hburst_start << 16) |
927203954Srdivacky		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
928203954Srdivacky
929203954Srdivacky	if (burst_ena)
930193323Sed		hctl2 |= TV_BURST_ENA;
931193323Sed
932193323Sed	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
933193323Sed		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
934193323Sed
935193323Sed	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
936193323Sed		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
937198090Srdivacky		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
938198090Srdivacky
939203954Srdivacky	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
940193323Sed		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
941198090Srdivacky		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
942198090Srdivacky
943198090Srdivacky	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
944193323Sed		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
945193323Sed		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
946193323Sed
947198090Srdivacky	if (tv_mode->veq_ena)
948193323Sed		vctl3 |= TV_EQUAL_ENA;
949202375Srdivacky
950193323Sed	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
951193323Sed		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
952198090Srdivacky
953198090Srdivacky	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
954198090Srdivacky		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
955198090Srdivacky
956193323Sed	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
957198090Srdivacky		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
958198090Srdivacky
959198090Srdivacky	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
960198090Srdivacky		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
961198090Srdivacky
962198090Srdivacky	if (intel_crtc->pipe == 1)
963198090Srdivacky		tv_ctl |= TV_ENC_PIPEB_SELECT;
964198090Srdivacky	tv_ctl |= tv_mode->oversample;
965193323Sed
966198090Srdivacky	if (tv_mode->progressive)
967198090Srdivacky		tv_ctl |= TV_PROGRESSIVE;
968198090Srdivacky	if (tv_mode->trilevel_sync)
969198090Srdivacky		tv_ctl |= TV_TRILEVEL_SYNC;
970193323Sed	if (tv_mode->pal_burst)
971198090Srdivacky		tv_ctl |= TV_PAL_BURST;
972198090Srdivacky
973198090Srdivacky	scctl1 = 0;
974198090Srdivacky	if (tv_mode->dda1_inc)
975198090Srdivacky		scctl1 |= TV_SC_DDA1_EN;
976198090Srdivacky	if (tv_mode->dda2_inc)
977193323Sed		scctl1 |= TV_SC_DDA2_EN;
978198090Srdivacky	if (tv_mode->dda3_inc)
979198090Srdivacky		scctl1 |= TV_SC_DDA3_EN;
980198090Srdivacky	scctl1 |= tv_mode->sc_reset;
981198090Srdivacky	if (video_levels)
982193323Sed		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
983198090Srdivacky	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
984198090Srdivacky
985198090Srdivacky	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
986193323Sed		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
987198090Srdivacky
988198090Srdivacky	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
989198090Srdivacky		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
990198090Srdivacky
991198090Srdivacky	/* Enable two fixes for the chips that need them. */
992198090Srdivacky	if (dev->pci_device < 0x2772)
993198090Srdivacky		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
994198090Srdivacky
995198090Srdivacky	I915_WRITE(TV_H_CTL_1, hctl1);
996198090Srdivacky	I915_WRITE(TV_H_CTL_2, hctl2);
997198090Srdivacky	I915_WRITE(TV_H_CTL_3, hctl3);
998193323Sed	I915_WRITE(TV_V_CTL_1, vctl1);
999198090Srdivacky	I915_WRITE(TV_V_CTL_2, vctl2);
1000198090Srdivacky	I915_WRITE(TV_V_CTL_3, vctl3);
1001198090Srdivacky	I915_WRITE(TV_V_CTL_4, vctl4);
1002198090Srdivacky	I915_WRITE(TV_V_CTL_5, vctl5);
1003198090Srdivacky	I915_WRITE(TV_V_CTL_6, vctl6);
1004198090Srdivacky	I915_WRITE(TV_V_CTL_7, vctl7);
1005198090Srdivacky	I915_WRITE(TV_SC_CTL_1, scctl1);
1006198090Srdivacky	I915_WRITE(TV_SC_CTL_2, scctl2);
1007193323Sed	I915_WRITE(TV_SC_CTL_3, scctl3);
1008198090Srdivacky
1009198090Srdivacky	if (color_conversion) {
1010193323Sed		I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
1011193323Sed			   color_conversion->gy);
1012198090Srdivacky		I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
1013198090Srdivacky			   color_conversion->ay);
1014198090Srdivacky		I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
1015198090Srdivacky			   color_conversion->gu);
1016198090Srdivacky		I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
1017198090Srdivacky			   color_conversion->au);
1018198090Srdivacky		I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
1019193323Sed			   color_conversion->gv);
1020193323Sed		I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
1021198090Srdivacky			   color_conversion->av);
1022198090Srdivacky	}
1023193323Sed
1024193323Sed	if (INTEL_INFO(dev)->gen >= 4)
1025193323Sed		I915_WRITE(TV_CLR_KNOBS, 0x00404000);
1026193323Sed	else
1027193323Sed		I915_WRITE(TV_CLR_KNOBS, 0x00606000);
1028204642Srdivacky
1029193323Sed	if (video_levels)
1030202375Srdivacky		I915_WRITE(TV_CLR_LEVEL,
1031198090Srdivacky			   ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1032199481Srdivacky			    (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1033193323Sed	{
1034193323Sed		int pipeconf_reg = PIPECONF(pipe);
1035193323Sed		int dspcntr_reg = DSPCNTR(intel_crtc->plane);
1036193323Sed		int pipeconf = I915_READ(pipeconf_reg);
1037193323Sed		int dspcntr = I915_READ(dspcntr_reg);
1038193323Sed		int dspbase_reg = DSPADDR(intel_crtc->plane);
1039193323Sed		int xpos = 0x0, ypos = 0x0;
1040193323Sed		unsigned int xsize, ysize;
1041193323Sed		/* Pipe must be off here */
1042193323Sed		I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
1043193323Sed		/* Flush the plane changes */
1044193323Sed		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1045193323Sed
1046202375Srdivacky		/* Wait for vblank for the disable to take effect */
1047193323Sed		if (IS_GEN2(dev))
1048198090Srdivacky			intel_wait_for_vblank(dev, intel_crtc->pipe);
1049198090Srdivacky
1050198090Srdivacky		I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
1051198090Srdivacky		/* Wait for vblank for the disable to take effect. */
1052198090Srdivacky		intel_wait_for_pipe_off(dev, intel_crtc->pipe);
1053198090Srdivacky
1054193323Sed		/* Filter ctl must be set before TV_WIN_SIZE */
1055198090Srdivacky		I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1056198090Srdivacky		xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1057193323Sed		if (tv_mode->progressive)
1058198090Srdivacky			ysize = tv_mode->nbr_end + 1;
1059198090Srdivacky		else
1060193323Sed			ysize = 2*tv_mode->nbr_end + 1;
1061198090Srdivacky
1062198090Srdivacky		xpos += intel_tv->margin[TV_MARGIN_LEFT];
1063198090Srdivacky		ypos += intel_tv->margin[TV_MARGIN_TOP];
1064198090Srdivacky		xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
1065198090Srdivacky			  intel_tv->margin[TV_MARGIN_RIGHT]);
1066198090Srdivacky		ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
1067198090Srdivacky			  intel_tv->margin[TV_MARGIN_BOTTOM]);
1068198090Srdivacky		I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
1069193323Sed		I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
1070198090Srdivacky
1071198090Srdivacky		I915_WRITE(pipeconf_reg, pipeconf);
1072198090Srdivacky		I915_WRITE(dspcntr_reg, dspcntr);
1073198090Srdivacky		/* Flush the plane changes */
1074198090Srdivacky		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1075198090Srdivacky	}
1076198090Srdivacky
1077198090Srdivacky	j = 0;
1078198090Srdivacky	for (i = 0; i < 60; i++)
1079198090Srdivacky		I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1080198090Srdivacky	for (i = 0; i < 60; i++)
1081198090Srdivacky		I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1082198090Srdivacky	for (i = 0; i < 43; i++)
1083198090Srdivacky		I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1084198090Srdivacky	for (i = 0; i < 43; i++)
1085198090Srdivacky		I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1086198090Srdivacky	I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
1087193323Sed	I915_WRITE(TV_CTL, tv_ctl);
1088198090Srdivacky}
1089193323Sed
1090193323Sedstatic const struct drm_display_mode reported_modes[] = {
1091202375Srdivacky	{
1092193323Sed		.name = "NTSC 480i",
1093193323Sed		.clock = 107520,
1094198090Srdivacky		.hdisplay = 1280,
1095198090Srdivacky		.hsync_start = 1368,
1096198090Srdivacky		.hsync_end = 1496,
1097193323Sed		.htotal = 1712,
1098193323Sed
1099193323Sed		.vdisplay = 1024,
1100193323Sed		.vsync_start = 1027,
1101193323Sed		.vsync_end = 1034,
1102193323Sed		.vtotal = 1104,
1103193323Sed		.type = DRM_MODE_TYPE_DRIVER,
1104193323Sed	},
1105193323Sed};
1106193323Sed
1107193323Sed/**
1108193323Sed * Detects TV presence by checking for load.
1109193323Sed *
1110193323Sed * Requires that the current pipe's DPLL is active.
1111193323Sed
1112193323Sed * \return true if TV is connected.
1113 * \return false if TV is disconnected.
1114 */
1115static int
1116intel_tv_detect_type(struct intel_tv *intel_tv,
1117		      struct drm_connector *connector)
1118{
1119	struct drm_encoder *encoder = &intel_tv->base.base;
1120	struct drm_crtc *crtc = encoder->crtc;
1121	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1122	struct drm_device *dev = encoder->dev;
1123	struct drm_i915_private *dev_priv = dev->dev_private;
1124	u32 tv_ctl, save_tv_ctl;
1125	u32 tv_dac, save_tv_dac;
1126	int type;
1127
1128	/* Disable TV interrupts around load detect or we'll recurse */
1129	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1130		mtx_lock(&dev_priv->irq_lock);
1131		i915_disable_pipestat(dev_priv, 0,
1132				      PIPE_HOTPLUG_INTERRUPT_ENABLE |
1133				      PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1134		mtx_unlock(&dev_priv->irq_lock);
1135	}
1136
1137	save_tv_dac = tv_dac = I915_READ(TV_DAC);
1138	save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
1139
1140	/* Poll for TV detection */
1141	tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
1142	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1143	if (intel_crtc->pipe == 1)
1144		tv_ctl |= TV_ENC_PIPEB_SELECT;
1145	else
1146		tv_ctl &= ~TV_ENC_PIPEB_SELECT;
1147
1148	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1149	tv_dac |= (TVDAC_STATE_CHG_EN |
1150		   TVDAC_A_SENSE_CTL |
1151		   TVDAC_B_SENSE_CTL |
1152		   TVDAC_C_SENSE_CTL |
1153		   DAC_CTL_OVERRIDE |
1154		   DAC_A_0_7_V |
1155		   DAC_B_0_7_V |
1156		   DAC_C_0_7_V);
1157
1158
1159	/*
1160	 * The TV sense state should be cleared to zero on cantiga platform. Otherwise
1161	 * the TV is misdetected. This is hardware requirement.
1162	 */
1163	if (IS_GM45(dev))
1164		tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
1165			    TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
1166
1167	I915_WRITE(TV_CTL, tv_ctl);
1168	I915_WRITE(TV_DAC, tv_dac);
1169	POSTING_READ(TV_DAC);
1170
1171	intel_wait_for_vblank(intel_tv->base.base.dev,
1172			      to_intel_crtc(intel_tv->base.base.crtc)->pipe);
1173
1174	type = -1;
1175	tv_dac = I915_READ(TV_DAC);
1176	DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
1177	/*
1178	 *  A B C
1179	 *  0 1 1 Composite
1180	 *  1 0 X svideo
1181	 *  0 0 0 Component
1182	 */
1183	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1184		DRM_DEBUG_KMS("Detected Composite TV connection\n");
1185		type = DRM_MODE_CONNECTOR_Composite;
1186	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1187		DRM_DEBUG_KMS("Detected S-Video TV connection\n");
1188		type = DRM_MODE_CONNECTOR_SVIDEO;
1189	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1190		DRM_DEBUG_KMS("Detected Component TV connection\n");
1191		type = DRM_MODE_CONNECTOR_Component;
1192	} else {
1193		DRM_DEBUG_KMS("Unrecognised TV connection\n");
1194		type = -1;
1195	}
1196
1197	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1198	I915_WRITE(TV_CTL, save_tv_ctl);
1199
1200	/* Restore interrupt config */
1201	if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1202		mtx_lock(&dev_priv->irq_lock);
1203		i915_enable_pipestat(dev_priv, 0,
1204				     PIPE_HOTPLUG_INTERRUPT_ENABLE |
1205				     PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1206		mtx_unlock(&dev_priv->irq_lock);
1207	}
1208
1209	return type;
1210}
1211
1212/*
1213 * Here we set accurate tv format according to connector type
1214 * i.e Component TV should not be assigned by NTSC or PAL
1215 */
1216static void intel_tv_find_better_format(struct drm_connector *connector)
1217{
1218	struct intel_tv *intel_tv = intel_attached_tv(connector);
1219	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1220	int i;
1221
1222	if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1223		tv_mode->component_only)
1224		return;
1225
1226
1227	for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) {
1228		tv_mode = tv_modes + i;
1229
1230		if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
1231			tv_mode->component_only)
1232			break;
1233	}
1234
1235	intel_tv->tv_format = tv_mode->name;
1236	drm_object_property_set_value(&connector->base,
1237		connector->dev->mode_config.tv_mode_property, i);
1238}
1239
1240/**
1241 * Detect the TV connection.
1242 *
1243 * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
1244 * we have a pipe programmed in order to probe the TV.
1245 */
1246static enum drm_connector_status
1247intel_tv_detect(struct drm_connector *connector, bool force)
1248{
1249	struct drm_display_mode mode;
1250	struct intel_tv *intel_tv = intel_attached_tv(connector);
1251	int type;
1252
1253	mode = reported_modes[0];
1254	drm_mode_set_crtcinfo(&mode, 0);
1255
1256	if (force) {
1257		struct intel_load_detect_pipe tmp;
1258
1259		if (intel_get_load_detect_pipe(&intel_tv->base, connector,
1260					       &mode, &tmp)) {
1261			type = intel_tv_detect_type(intel_tv, connector);
1262			intel_release_load_detect_pipe(&intel_tv->base,
1263						       connector,
1264						       &tmp);
1265		} else
1266			return connector_status_unknown;
1267	} else
1268		return connector->status;
1269
1270	if (type < 0)
1271		return connector_status_disconnected;
1272
1273	intel_tv->type = type;
1274	intel_tv_find_better_format(connector);
1275
1276	return connector_status_connected;
1277}
1278
1279static const struct input_res {
1280	const char *name;
1281	int w, h;
1282} input_res_table[] = {
1283	{"640x480", 640, 480},
1284	{"800x600", 800, 600},
1285	{"1024x768", 1024, 768},
1286	{"1280x1024", 1280, 1024},
1287	{"848x480", 848, 480},
1288	{"1280x720", 1280, 720},
1289	{"1920x1080", 1920, 1080},
1290};
1291
1292/*
1293 * Chose preferred mode  according to line number of TV format
1294 */
1295static void
1296intel_tv_chose_preferred_modes(struct drm_connector *connector,
1297			       struct drm_display_mode *mode_ptr)
1298{
1299	struct intel_tv *intel_tv = intel_attached_tv(connector);
1300	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1301
1302	if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
1303		mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1304	else if (tv_mode->nbr_end > 480) {
1305		if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
1306			if (mode_ptr->vdisplay == 720)
1307				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1308		} else if (mode_ptr->vdisplay == 1080)
1309				mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1310	}
1311}
1312
1313/**
1314 * Stub get_modes function.
1315 *
1316 * This should probably return a set of fixed modes, unless we can figure out
1317 * how to probe modes off of TV connections.
1318 */
1319
1320static int
1321intel_tv_get_modes(struct drm_connector *connector)
1322{
1323	struct drm_display_mode *mode_ptr;
1324	struct intel_tv *intel_tv = intel_attached_tv(connector);
1325	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
1326	int j, count = 0;
1327	u64 tmp;
1328
1329	for (j = 0; j < ARRAY_SIZE(input_res_table);
1330	     j++) {
1331		const struct input_res *input = &input_res_table[j];
1332		unsigned int hactive_s = input->w;
1333		unsigned int vactive_s = input->h;
1334
1335		if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1336			continue;
1337
1338		if (input->w > 1024 && (!tv_mode->progressive
1339					&& !tv_mode->component_only))
1340			continue;
1341
1342		mode_ptr = drm_mode_create(connector->dev);
1343		if (!mode_ptr)
1344			continue;
1345		strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
1346
1347		mode_ptr->hdisplay = hactive_s;
1348		mode_ptr->hsync_start = hactive_s + 1;
1349		mode_ptr->hsync_end = hactive_s + 64;
1350		if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
1351			mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
1352		mode_ptr->htotal = hactive_s + 96;
1353
1354		mode_ptr->vdisplay = vactive_s;
1355		mode_ptr->vsync_start = vactive_s + 1;
1356		mode_ptr->vsync_end = vactive_s + 32;
1357		if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
1358			mode_ptr->vsync_end = mode_ptr->vsync_start  + 1;
1359		mode_ptr->vtotal = vactive_s + 33;
1360
1361		tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
1362		tmp *= mode_ptr->htotal;
1363		tmp = tmp / 1000000;
1364		mode_ptr->clock = (int) tmp;
1365
1366		mode_ptr->type = DRM_MODE_TYPE_DRIVER;
1367		intel_tv_chose_preferred_modes(connector, mode_ptr);
1368		drm_mode_probed_add(connector, mode_ptr);
1369		count++;
1370	}
1371
1372	return count;
1373}
1374
1375static void
1376intel_tv_destroy(struct drm_connector *connector)
1377{
1378#if 0
1379	drm_sysfs_connector_remove(connector);
1380#endif
1381	drm_connector_cleanup(connector);
1382	free(connector, DRM_MEM_KMS);
1383}
1384
1385
1386static int
1387intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
1388		      uint64_t val)
1389{
1390	struct drm_device *dev = connector->dev;
1391	struct intel_tv *intel_tv = intel_attached_tv(connector);
1392	struct drm_crtc *crtc = intel_tv->base.base.crtc;
1393	int ret = 0;
1394	bool changed = false;
1395
1396	ret = drm_object_property_set_value(&connector->base, property, val);
1397	if (ret < 0)
1398		goto out;
1399
1400	if (property == dev->mode_config.tv_left_margin_property &&
1401		intel_tv->margin[TV_MARGIN_LEFT] != val) {
1402		intel_tv->margin[TV_MARGIN_LEFT] = val;
1403		changed = true;
1404	} else if (property == dev->mode_config.tv_right_margin_property &&
1405		intel_tv->margin[TV_MARGIN_RIGHT] != val) {
1406		intel_tv->margin[TV_MARGIN_RIGHT] = val;
1407		changed = true;
1408	} else if (property == dev->mode_config.tv_top_margin_property &&
1409		intel_tv->margin[TV_MARGIN_TOP] != val) {
1410		intel_tv->margin[TV_MARGIN_TOP] = val;
1411		changed = true;
1412	} else if (property == dev->mode_config.tv_bottom_margin_property &&
1413		intel_tv->margin[TV_MARGIN_BOTTOM] != val) {
1414		intel_tv->margin[TV_MARGIN_BOTTOM] = val;
1415		changed = true;
1416	} else if (property == dev->mode_config.tv_mode_property) {
1417		if (val >= ARRAY_SIZE(tv_modes)) {
1418			ret = -EINVAL;
1419			goto out;
1420		}
1421		if (!strcmp(intel_tv->tv_format, tv_modes[val].name))
1422			goto out;
1423
1424		intel_tv->tv_format = tv_modes[val].name;
1425		changed = true;
1426	} else {
1427		ret = -EINVAL;
1428		goto out;
1429	}
1430
1431	if (changed && crtc)
1432		drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
1433				crtc->y, crtc->fb);
1434out:
1435	return ret;
1436}
1437
1438static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
1439	.dpms = intel_tv_dpms,
1440	.mode_fixup = intel_tv_mode_fixup,
1441	.prepare = intel_encoder_prepare,
1442	.mode_set = intel_tv_mode_set,
1443	.commit = intel_encoder_commit,
1444};
1445
1446static const struct drm_connector_funcs intel_tv_connector_funcs = {
1447	.dpms = drm_helper_connector_dpms,
1448	.detect = intel_tv_detect,
1449	.destroy = intel_tv_destroy,
1450	.set_property = intel_tv_set_property,
1451	.fill_modes = drm_helper_probe_single_connector_modes,
1452};
1453
1454static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1455	.mode_valid = intel_tv_mode_valid,
1456	.get_modes = intel_tv_get_modes,
1457	.best_encoder = intel_best_encoder,
1458};
1459
1460static const struct drm_encoder_funcs intel_tv_enc_funcs = {
1461	.destroy = intel_encoder_destroy,
1462};
1463
1464/*
1465 * Enumerate the child dev array parsed from VBT to check whether
1466 * the integrated TV is present.
1467 * If it is present, return 1.
1468 * If it is not present, return false.
1469 * If no child dev is parsed from VBT, it assumes that the TV is present.
1470 */
1471static int tv_is_present_in_vbt(struct drm_device *dev)
1472{
1473	struct drm_i915_private *dev_priv = dev->dev_private;
1474	struct child_device_config *p_child;
1475	int i, ret;
1476
1477	if (!dev_priv->child_dev_num)
1478		return 1;
1479
1480	ret = 0;
1481	for (i = 0; i < dev_priv->child_dev_num; i++) {
1482		p_child = dev_priv->child_dev + i;
1483		/*
1484		 * If the device type is not TV, continue.
1485		 */
1486		if (p_child->device_type != DEVICE_TYPE_INT_TV &&
1487			p_child->device_type != DEVICE_TYPE_TV)
1488			continue;
1489		/* Only when the addin_offset is non-zero, it is regarded
1490		 * as present.
1491		 */
1492		if (p_child->addin_offset) {
1493			ret = 1;
1494			break;
1495		}
1496	}
1497	return ret;
1498}
1499
1500void
1501intel_tv_init(struct drm_device *dev)
1502{
1503	struct drm_i915_private *dev_priv = dev->dev_private;
1504	struct drm_connector *connector;
1505	struct intel_tv *intel_tv;
1506	struct intel_encoder *intel_encoder;
1507	struct intel_connector *intel_connector;
1508	u32 tv_dac_on, tv_dac_off, save_tv_dac;
1509	char *tv_format_names[ARRAY_SIZE(tv_modes)];
1510	int i, initial_mode = 0;
1511
1512	if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1513		return;
1514
1515	if (!tv_is_present_in_vbt(dev)) {
1516		DRM_DEBUG_KMS("Integrated TV is not present.\n");
1517		return;
1518	}
1519	/* Even if we have an encoder we may not have a connector */
1520	if (!dev_priv->int_tv_support)
1521		return;
1522
1523	/*
1524	 * Sanity check the TV output by checking to see if the
1525	 * DAC register holds a value
1526	 */
1527	save_tv_dac = I915_READ(TV_DAC);
1528
1529	I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1530	tv_dac_on = I915_READ(TV_DAC);
1531
1532	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1533	tv_dac_off = I915_READ(TV_DAC);
1534
1535	I915_WRITE(TV_DAC, save_tv_dac);
1536
1537	/*
1538	 * If the register does not hold the state change enable
1539	 * bit, (either as a 0 or a 1), assume it doesn't really
1540	 * exist
1541	 */
1542	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1543	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1544		return;
1545
1546	intel_tv = malloc(sizeof(struct intel_tv), DRM_MEM_KMS,
1547	    M_WAITOK | M_ZERO);
1548	intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS,
1549	    M_WAITOK | M_ZERO);
1550
1551	intel_encoder = &intel_tv->base;
1552	connector = &intel_connector->base;
1553
1554	/* The documentation, for the older chipsets at least, recommend
1555	 * using a polling method rather than hotplug detection for TVs.
1556	 * This is because in order to perform the hotplug detection, the PLLs
1557	 * for the TV must be kept alive increasing power drain and starving
1558	 * bandwidth from other encoders. Notably for instance, it causes
1559	 * pipe underruns on Crestline when this encoder is supposedly idle.
1560	 *
1561	 * More recent chipsets favour HDMI rather than integrated S-Video.
1562	 */
1563	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1564
1565	drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1566			   DRM_MODE_CONNECTOR_SVIDEO);
1567
1568	drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
1569			 DRM_MODE_ENCODER_TVDAC);
1570
1571	intel_connector_attach_encoder(intel_connector, intel_encoder);
1572	intel_encoder->type = INTEL_OUTPUT_TVOUT;
1573	intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1574	intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
1575	intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
1576	intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
1577	intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
1578
1579	/* BIOS margin values */
1580	intel_tv->margin[TV_MARGIN_LEFT] = 54;
1581	intel_tv->margin[TV_MARGIN_TOP] = 36;
1582	intel_tv->margin[TV_MARGIN_RIGHT] = 46;
1583	intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
1584
1585	intel_tv->tv_format = tv_modes[initial_mode].name;
1586
1587	drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs);
1588	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1589	connector->interlace_allowed = false;
1590	connector->doublescan_allowed = false;
1591
1592	/* Create TV properties then attach current values */
1593	for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
1594		tv_format_names[i] = __DECONST(char *, tv_modes[i].name);
1595	drm_mode_create_tv_properties(dev,
1596				      ARRAY_SIZE(tv_modes),
1597				      tv_format_names);
1598
1599	drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
1600				   initial_mode);
1601	drm_object_attach_property(&connector->base,
1602				   dev->mode_config.tv_left_margin_property,
1603				   intel_tv->margin[TV_MARGIN_LEFT]);
1604	drm_object_attach_property(&connector->base,
1605				   dev->mode_config.tv_top_margin_property,
1606				   intel_tv->margin[TV_MARGIN_TOP]);
1607	drm_object_attach_property(&connector->base,
1608				   dev->mode_config.tv_right_margin_property,
1609				   intel_tv->margin[TV_MARGIN_RIGHT]);
1610	drm_object_attach_property(&connector->base,
1611				   dev->mode_config.tv_bottom_margin_property,
1612				   intel_tv->margin[TV_MARGIN_BOTTOM]);
1613#if 0
1614	drm_sysfs_connector_add(connector);
1615#endif
1616}
1617