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