1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright © 2006-2008 Intel Corporation
3*4882a593Smuzhiyun * Jesse Barnes <jesse.barnes@intel.com>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
6*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
7*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
8*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
10*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * The above copyright notice and this permission notice (including the next
13*4882a593Smuzhiyun * paragraph) shall be included in all copies or substantial portions of the
14*4882a593Smuzhiyun * Software.
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19*4882a593Smuzhiyun * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*4882a593Smuzhiyun * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*4882a593Smuzhiyun * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22*4882a593Smuzhiyun * DEALINGS IN THE SOFTWARE.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * Authors:
25*4882a593Smuzhiyun * Eric Anholt <eric@anholt.net>
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun */
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /** @file
30*4882a593Smuzhiyun * Integrated TV-out support for the 915GM and 945GM.
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
34*4882a593Smuzhiyun #include <drm/drm_crtc.h>
35*4882a593Smuzhiyun #include <drm/drm_edid.h>
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #include "i915_drv.h"
38*4882a593Smuzhiyun #include "intel_connector.h"
39*4882a593Smuzhiyun #include "intel_display_types.h"
40*4882a593Smuzhiyun #include "intel_hotplug.h"
41*4882a593Smuzhiyun #include "intel_tv.h"
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun enum tv_margin {
44*4882a593Smuzhiyun TV_MARGIN_LEFT, TV_MARGIN_TOP,
45*4882a593Smuzhiyun TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct intel_tv {
49*4882a593Smuzhiyun struct intel_encoder base;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun int type;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct video_levels {
55*4882a593Smuzhiyun u16 blank, black;
56*4882a593Smuzhiyun u8 burst;
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun struct color_conversion {
60*4882a593Smuzhiyun u16 ry, gy, by, ay;
61*4882a593Smuzhiyun u16 ru, gu, bu, au;
62*4882a593Smuzhiyun u16 rv, gv, bv, av;
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun static const u32 filter_table[] = {
66*4882a593Smuzhiyun 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
67*4882a593Smuzhiyun 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
68*4882a593Smuzhiyun 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
69*4882a593Smuzhiyun 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
70*4882a593Smuzhiyun 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
71*4882a593Smuzhiyun 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
72*4882a593Smuzhiyun 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
73*4882a593Smuzhiyun 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
74*4882a593Smuzhiyun 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
75*4882a593Smuzhiyun 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
76*4882a593Smuzhiyun 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
77*4882a593Smuzhiyun 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
78*4882a593Smuzhiyun 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
79*4882a593Smuzhiyun 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
80*4882a593Smuzhiyun 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
81*4882a593Smuzhiyun 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
82*4882a593Smuzhiyun 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
83*4882a593Smuzhiyun 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
84*4882a593Smuzhiyun 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
85*4882a593Smuzhiyun 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
86*4882a593Smuzhiyun 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
87*4882a593Smuzhiyun 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
88*4882a593Smuzhiyun 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
89*4882a593Smuzhiyun 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
90*4882a593Smuzhiyun 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
91*4882a593Smuzhiyun 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
92*4882a593Smuzhiyun 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
93*4882a593Smuzhiyun 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
94*4882a593Smuzhiyun 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
95*4882a593Smuzhiyun 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
96*4882a593Smuzhiyun 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
97*4882a593Smuzhiyun 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
98*4882a593Smuzhiyun 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
99*4882a593Smuzhiyun 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
100*4882a593Smuzhiyun 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
101*4882a593Smuzhiyun 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
102*4882a593Smuzhiyun 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
103*4882a593Smuzhiyun 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
104*4882a593Smuzhiyun 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
105*4882a593Smuzhiyun 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
106*4882a593Smuzhiyun 0x28003100, 0x28002F00, 0x00003100, 0x36403000,
107*4882a593Smuzhiyun 0x2D002CC0, 0x30003640, 0x2D0036C0,
108*4882a593Smuzhiyun 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
109*4882a593Smuzhiyun 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
110*4882a593Smuzhiyun 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
111*4882a593Smuzhiyun 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
112*4882a593Smuzhiyun 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
113*4882a593Smuzhiyun 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
114*4882a593Smuzhiyun 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
115*4882a593Smuzhiyun 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
116*4882a593Smuzhiyun 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
117*4882a593Smuzhiyun 0x28003100, 0x28002F00, 0x00003100,
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun * Color conversion values have 3 separate fixed point formats:
122*4882a593Smuzhiyun *
123*4882a593Smuzhiyun * 10 bit fields (ay, au)
124*4882a593Smuzhiyun * 1.9 fixed point (b.bbbbbbbbb)
125*4882a593Smuzhiyun * 11 bit fields (ry, by, ru, gu, gv)
126*4882a593Smuzhiyun * exp.mantissa (ee.mmmmmmmmm)
127*4882a593Smuzhiyun * ee = 00 = 10^-1 (0.mmmmmmmmm)
128*4882a593Smuzhiyun * ee = 01 = 10^-2 (0.0mmmmmmmmm)
129*4882a593Smuzhiyun * ee = 10 = 10^-3 (0.00mmmmmmmmm)
130*4882a593Smuzhiyun * ee = 11 = 10^-4 (0.000mmmmmmmmm)
131*4882a593Smuzhiyun * 12 bit fields (gy, rv, bu)
132*4882a593Smuzhiyun * exp.mantissa (eee.mmmmmmmmm)
133*4882a593Smuzhiyun * eee = 000 = 10^-1 (0.mmmmmmmmm)
134*4882a593Smuzhiyun * eee = 001 = 10^-2 (0.0mmmmmmmmm)
135*4882a593Smuzhiyun * eee = 010 = 10^-3 (0.00mmmmmmmmm)
136*4882a593Smuzhiyun * eee = 011 = 10^-4 (0.000mmmmmmmmm)
137*4882a593Smuzhiyun * eee = 100 = reserved
138*4882a593Smuzhiyun * eee = 101 = reserved
139*4882a593Smuzhiyun * eee = 110 = reserved
140*4882a593Smuzhiyun * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
141*4882a593Smuzhiyun *
142*4882a593Smuzhiyun * Saturation and contrast are 8 bits, with their own representation:
143*4882a593Smuzhiyun * 8 bit field (saturation, contrast)
144*4882a593Smuzhiyun * exp.mantissa (ee.mmmmmm)
145*4882a593Smuzhiyun * ee = 00 = 10^-1 (0.mmmmmm)
146*4882a593Smuzhiyun * ee = 01 = 10^0 (m.mmmmm)
147*4882a593Smuzhiyun * ee = 10 = 10^1 (mm.mmmm)
148*4882a593Smuzhiyun * ee = 11 = 10^2 (mmm.mmm)
149*4882a593Smuzhiyun *
150*4882a593Smuzhiyun * Simple conversion function:
151*4882a593Smuzhiyun *
152*4882a593Smuzhiyun * static u32
153*4882a593Smuzhiyun * float_to_csc_11(float f)
154*4882a593Smuzhiyun * {
155*4882a593Smuzhiyun * u32 exp;
156*4882a593Smuzhiyun * u32 mant;
157*4882a593Smuzhiyun * u32 ret;
158*4882a593Smuzhiyun *
159*4882a593Smuzhiyun * if (f < 0)
160*4882a593Smuzhiyun * f = -f;
161*4882a593Smuzhiyun *
162*4882a593Smuzhiyun * if (f >= 1) {
163*4882a593Smuzhiyun * exp = 0x7;
164*4882a593Smuzhiyun * mant = 1 << 8;
165*4882a593Smuzhiyun * } else {
166*4882a593Smuzhiyun * for (exp = 0; exp < 3 && f < 0.5; exp++)
167*4882a593Smuzhiyun * f *= 2.0;
168*4882a593Smuzhiyun * mant = (f * (1 << 9) + 0.5);
169*4882a593Smuzhiyun * if (mant >= (1 << 9))
170*4882a593Smuzhiyun * mant = (1 << 9) - 1;
171*4882a593Smuzhiyun * }
172*4882a593Smuzhiyun * ret = (exp << 9) | mant;
173*4882a593Smuzhiyun * return ret;
174*4882a593Smuzhiyun * }
175*4882a593Smuzhiyun */
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun /*
178*4882a593Smuzhiyun * Behold, magic numbers! If we plant them they might grow a big
179*4882a593Smuzhiyun * s-video cable to the sky... or something.
180*4882a593Smuzhiyun *
181*4882a593Smuzhiyun * Pre-converted to appropriate hex value.
182*4882a593Smuzhiyun */
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun /*
185*4882a593Smuzhiyun * PAL & NTSC values for composite & s-video connections
186*4882a593Smuzhiyun */
187*4882a593Smuzhiyun static const struct color_conversion ntsc_m_csc_composite = {
188*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
189*4882a593Smuzhiyun .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
190*4882a593Smuzhiyun .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
191*4882a593Smuzhiyun };
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun static const struct video_levels ntsc_m_levels_composite = {
194*4882a593Smuzhiyun .blank = 225, .black = 267, .burst = 113,
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun static const struct color_conversion ntsc_m_csc_svideo = {
198*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
199*4882a593Smuzhiyun .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
200*4882a593Smuzhiyun .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
201*4882a593Smuzhiyun };
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun static const struct video_levels ntsc_m_levels_svideo = {
204*4882a593Smuzhiyun .blank = 266, .black = 316, .burst = 133,
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun static const struct color_conversion ntsc_j_csc_composite = {
208*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
209*4882a593Smuzhiyun .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
210*4882a593Smuzhiyun .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
211*4882a593Smuzhiyun };
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun static const struct video_levels ntsc_j_levels_composite = {
214*4882a593Smuzhiyun .blank = 225, .black = 225, .burst = 113,
215*4882a593Smuzhiyun };
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun static const struct color_conversion ntsc_j_csc_svideo = {
218*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
219*4882a593Smuzhiyun .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
220*4882a593Smuzhiyun .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
221*4882a593Smuzhiyun };
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun static const struct video_levels ntsc_j_levels_svideo = {
224*4882a593Smuzhiyun .blank = 266, .black = 266, .burst = 133,
225*4882a593Smuzhiyun };
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun static const struct color_conversion pal_csc_composite = {
228*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
229*4882a593Smuzhiyun .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
230*4882a593Smuzhiyun .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
231*4882a593Smuzhiyun };
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun static const struct video_levels pal_levels_composite = {
234*4882a593Smuzhiyun .blank = 237, .black = 237, .burst = 118,
235*4882a593Smuzhiyun };
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun static const struct color_conversion pal_csc_svideo = {
238*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
239*4882a593Smuzhiyun .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
240*4882a593Smuzhiyun .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
241*4882a593Smuzhiyun };
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun static const struct video_levels pal_levels_svideo = {
244*4882a593Smuzhiyun .blank = 280, .black = 280, .burst = 139,
245*4882a593Smuzhiyun };
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun static const struct color_conversion pal_m_csc_composite = {
248*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
249*4882a593Smuzhiyun .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
250*4882a593Smuzhiyun .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
251*4882a593Smuzhiyun };
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun static const struct video_levels pal_m_levels_composite = {
254*4882a593Smuzhiyun .blank = 225, .black = 267, .burst = 113,
255*4882a593Smuzhiyun };
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun static const struct color_conversion pal_m_csc_svideo = {
258*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
259*4882a593Smuzhiyun .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
260*4882a593Smuzhiyun .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
261*4882a593Smuzhiyun };
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun static const struct video_levels pal_m_levels_svideo = {
264*4882a593Smuzhiyun .blank = 266, .black = 316, .burst = 133,
265*4882a593Smuzhiyun };
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun static const struct color_conversion pal_n_csc_composite = {
268*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
269*4882a593Smuzhiyun .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
270*4882a593Smuzhiyun .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
271*4882a593Smuzhiyun };
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun static const struct video_levels pal_n_levels_composite = {
274*4882a593Smuzhiyun .blank = 225, .black = 267, .burst = 118,
275*4882a593Smuzhiyun };
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun static const struct color_conversion pal_n_csc_svideo = {
278*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
279*4882a593Smuzhiyun .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
280*4882a593Smuzhiyun .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
281*4882a593Smuzhiyun };
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun static const struct video_levels pal_n_levels_svideo = {
284*4882a593Smuzhiyun .blank = 266, .black = 316, .burst = 139,
285*4882a593Smuzhiyun };
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun /*
288*4882a593Smuzhiyun * Component connections
289*4882a593Smuzhiyun */
290*4882a593Smuzhiyun static const struct color_conversion sdtv_csc_yprpb = {
291*4882a593Smuzhiyun .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
292*4882a593Smuzhiyun .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
293*4882a593Smuzhiyun .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
294*4882a593Smuzhiyun };
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun static const struct color_conversion hdtv_csc_yprpb = {
297*4882a593Smuzhiyun .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
298*4882a593Smuzhiyun .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
299*4882a593Smuzhiyun .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
300*4882a593Smuzhiyun };
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun static const struct video_levels component_levels = {
303*4882a593Smuzhiyun .blank = 279, .black = 279, .burst = 0,
304*4882a593Smuzhiyun };
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun struct tv_mode {
308*4882a593Smuzhiyun const char *name;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun u32 clock;
311*4882a593Smuzhiyun u16 refresh; /* in millihertz (for precision) */
312*4882a593Smuzhiyun u8 oversample;
313*4882a593Smuzhiyun u8 hsync_end;
314*4882a593Smuzhiyun u16 hblank_start, hblank_end, htotal;
315*4882a593Smuzhiyun bool progressive : 1, trilevel_sync : 1, component_only : 1;
316*4882a593Smuzhiyun u8 vsync_start_f1, vsync_start_f2, vsync_len;
317*4882a593Smuzhiyun bool veq_ena : 1;
318*4882a593Smuzhiyun u8 veq_start_f1, veq_start_f2, veq_len;
319*4882a593Smuzhiyun u8 vi_end_f1, vi_end_f2;
320*4882a593Smuzhiyun u16 nbr_end;
321*4882a593Smuzhiyun bool burst_ena : 1;
322*4882a593Smuzhiyun u8 hburst_start, hburst_len;
323*4882a593Smuzhiyun u8 vburst_start_f1;
324*4882a593Smuzhiyun u16 vburst_end_f1;
325*4882a593Smuzhiyun u8 vburst_start_f2;
326*4882a593Smuzhiyun u16 vburst_end_f2;
327*4882a593Smuzhiyun u8 vburst_start_f3;
328*4882a593Smuzhiyun u16 vburst_end_f3;
329*4882a593Smuzhiyun u8 vburst_start_f4;
330*4882a593Smuzhiyun u16 vburst_end_f4;
331*4882a593Smuzhiyun /*
332*4882a593Smuzhiyun * subcarrier programming
333*4882a593Smuzhiyun */
334*4882a593Smuzhiyun u16 dda2_size, dda3_size;
335*4882a593Smuzhiyun u8 dda1_inc;
336*4882a593Smuzhiyun u16 dda2_inc, dda3_inc;
337*4882a593Smuzhiyun u32 sc_reset;
338*4882a593Smuzhiyun bool pal_burst : 1;
339*4882a593Smuzhiyun /*
340*4882a593Smuzhiyun * blank/black levels
341*4882a593Smuzhiyun */
342*4882a593Smuzhiyun const struct video_levels *composite_levels, *svideo_levels;
343*4882a593Smuzhiyun const struct color_conversion *composite_color, *svideo_color;
344*4882a593Smuzhiyun const u32 *filter_table;
345*4882a593Smuzhiyun };
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun /*
349*4882a593Smuzhiyun * Sub carrier DDA
350*4882a593Smuzhiyun *
351*4882a593Smuzhiyun * I think this works as follows:
352*4882a593Smuzhiyun *
353*4882a593Smuzhiyun * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
354*4882a593Smuzhiyun *
355*4882a593Smuzhiyun * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
356*4882a593Smuzhiyun *
357*4882a593Smuzhiyun * So,
358*4882a593Smuzhiyun * dda1_ideal = subcarrier/pixel * 4096
359*4882a593Smuzhiyun * dda1_inc = floor (dda1_ideal)
360*4882a593Smuzhiyun * dda2 = dda1_ideal - dda1_inc
361*4882a593Smuzhiyun *
362*4882a593Smuzhiyun * then pick a ratio for dda2 that gives the closest approximation. If
363*4882a593Smuzhiyun * you can't get close enough, you can play with dda3 as well. This
364*4882a593Smuzhiyun * seems likely to happen when dda2 is small as the jumps would be larger
365*4882a593Smuzhiyun *
366*4882a593Smuzhiyun * To invert this,
367*4882a593Smuzhiyun *
368*4882a593Smuzhiyun * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
369*4882a593Smuzhiyun *
370*4882a593Smuzhiyun * The constants below were all computed using a 107.520MHz clock
371*4882a593Smuzhiyun */
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun /*
374*4882a593Smuzhiyun * Register programming values for TV modes.
375*4882a593Smuzhiyun *
376*4882a593Smuzhiyun * These values account for -1s required.
377*4882a593Smuzhiyun */
378*4882a593Smuzhiyun static const struct tv_mode tv_modes[] = {
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun .name = "NTSC-M",
381*4882a593Smuzhiyun .clock = 108000,
382*4882a593Smuzhiyun .refresh = 59940,
383*4882a593Smuzhiyun .oversample = 8,
384*4882a593Smuzhiyun .component_only = false,
385*4882a593Smuzhiyun /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 124,
388*4882a593Smuzhiyun .hblank_start = 836, .htotal = 857,
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun .vsync_start_f1 = 6, .vsync_start_f2 = 7,
393*4882a593Smuzhiyun .vsync_len = 6,
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
396*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 18,
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun .vi_end_f1 = 20, .vi_end_f2 = 21,
399*4882a593Smuzhiyun .nbr_end = 240,
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun .burst_ena = true,
402*4882a593Smuzhiyun .hburst_start = 72, .hburst_len = 34,
403*4882a593Smuzhiyun .vburst_start_f1 = 9, .vburst_end_f1 = 240,
404*4882a593Smuzhiyun .vburst_start_f2 = 10, .vburst_end_f2 = 240,
405*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 240,
406*4882a593Smuzhiyun .vburst_start_f4 = 10, .vburst_end_f4 = 240,
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun /* desired 3.5800000 actual 3.5800000 clock 107.52 */
409*4882a593Smuzhiyun .dda1_inc = 135,
410*4882a593Smuzhiyun .dda2_inc = 20800, .dda2_size = 27456,
411*4882a593Smuzhiyun .dda3_inc = 0, .dda3_size = 0,
412*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_EVERY_4,
413*4882a593Smuzhiyun .pal_burst = false,
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun .composite_levels = &ntsc_m_levels_composite,
416*4882a593Smuzhiyun .composite_color = &ntsc_m_csc_composite,
417*4882a593Smuzhiyun .svideo_levels = &ntsc_m_levels_svideo,
418*4882a593Smuzhiyun .svideo_color = &ntsc_m_csc_svideo,
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun .filter_table = filter_table,
421*4882a593Smuzhiyun },
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun .name = "NTSC-443",
424*4882a593Smuzhiyun .clock = 108000,
425*4882a593Smuzhiyun .refresh = 59940,
426*4882a593Smuzhiyun .oversample = 8,
427*4882a593Smuzhiyun .component_only = false,
428*4882a593Smuzhiyun /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
429*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 124,
430*4882a593Smuzhiyun .hblank_start = 836, .htotal = 857,
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun .vsync_start_f1 = 6, .vsync_start_f2 = 7,
435*4882a593Smuzhiyun .vsync_len = 6,
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
438*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 18,
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun .vi_end_f1 = 20, .vi_end_f2 = 21,
441*4882a593Smuzhiyun .nbr_end = 240,
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun .burst_ena = true,
444*4882a593Smuzhiyun .hburst_start = 72, .hburst_len = 34,
445*4882a593Smuzhiyun .vburst_start_f1 = 9, .vburst_end_f1 = 240,
446*4882a593Smuzhiyun .vburst_start_f2 = 10, .vburst_end_f2 = 240,
447*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 240,
448*4882a593Smuzhiyun .vburst_start_f4 = 10, .vburst_end_f4 = 240,
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun /* desired 4.4336180 actual 4.4336180 clock 107.52 */
451*4882a593Smuzhiyun .dda1_inc = 168,
452*4882a593Smuzhiyun .dda2_inc = 4093, .dda2_size = 27456,
453*4882a593Smuzhiyun .dda3_inc = 310, .dda3_size = 525,
454*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_NEVER,
455*4882a593Smuzhiyun .pal_burst = false,
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun .composite_levels = &ntsc_m_levels_composite,
458*4882a593Smuzhiyun .composite_color = &ntsc_m_csc_composite,
459*4882a593Smuzhiyun .svideo_levels = &ntsc_m_levels_svideo,
460*4882a593Smuzhiyun .svideo_color = &ntsc_m_csc_svideo,
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun .filter_table = filter_table,
463*4882a593Smuzhiyun },
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun .name = "NTSC-J",
466*4882a593Smuzhiyun .clock = 108000,
467*4882a593Smuzhiyun .refresh = 59940,
468*4882a593Smuzhiyun .oversample = 8,
469*4882a593Smuzhiyun .component_only = false,
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
472*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 124,
473*4882a593Smuzhiyun .hblank_start = 836, .htotal = 857,
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun .vsync_start_f1 = 6, .vsync_start_f2 = 7,
478*4882a593Smuzhiyun .vsync_len = 6,
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
481*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 18,
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun .vi_end_f1 = 20, .vi_end_f2 = 21,
484*4882a593Smuzhiyun .nbr_end = 240,
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun .burst_ena = true,
487*4882a593Smuzhiyun .hburst_start = 72, .hburst_len = 34,
488*4882a593Smuzhiyun .vburst_start_f1 = 9, .vburst_end_f1 = 240,
489*4882a593Smuzhiyun .vburst_start_f2 = 10, .vburst_end_f2 = 240,
490*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 240,
491*4882a593Smuzhiyun .vburst_start_f4 = 10, .vburst_end_f4 = 240,
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun /* desired 3.5800000 actual 3.5800000 clock 107.52 */
494*4882a593Smuzhiyun .dda1_inc = 135,
495*4882a593Smuzhiyun .dda2_inc = 20800, .dda2_size = 27456,
496*4882a593Smuzhiyun .dda3_inc = 0, .dda3_size = 0,
497*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_EVERY_4,
498*4882a593Smuzhiyun .pal_burst = false,
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun .composite_levels = &ntsc_j_levels_composite,
501*4882a593Smuzhiyun .composite_color = &ntsc_j_csc_composite,
502*4882a593Smuzhiyun .svideo_levels = &ntsc_j_levels_svideo,
503*4882a593Smuzhiyun .svideo_color = &ntsc_j_csc_svideo,
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun .filter_table = filter_table,
506*4882a593Smuzhiyun },
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun .name = "PAL-M",
509*4882a593Smuzhiyun .clock = 108000,
510*4882a593Smuzhiyun .refresh = 59940,
511*4882a593Smuzhiyun .oversample = 8,
512*4882a593Smuzhiyun .component_only = false,
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
515*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 124,
516*4882a593Smuzhiyun .hblank_start = 836, .htotal = 857,
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun .vsync_start_f1 = 6, .vsync_start_f2 = 7,
521*4882a593Smuzhiyun .vsync_len = 6,
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
524*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 18,
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun .vi_end_f1 = 20, .vi_end_f2 = 21,
527*4882a593Smuzhiyun .nbr_end = 240,
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun .burst_ena = true,
530*4882a593Smuzhiyun .hburst_start = 72, .hburst_len = 34,
531*4882a593Smuzhiyun .vburst_start_f1 = 9, .vburst_end_f1 = 240,
532*4882a593Smuzhiyun .vburst_start_f2 = 10, .vburst_end_f2 = 240,
533*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 240,
534*4882a593Smuzhiyun .vburst_start_f4 = 10, .vburst_end_f4 = 240,
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun /* desired 3.5800000 actual 3.5800000 clock 107.52 */
537*4882a593Smuzhiyun .dda1_inc = 135,
538*4882a593Smuzhiyun .dda2_inc = 16704, .dda2_size = 27456,
539*4882a593Smuzhiyun .dda3_inc = 0, .dda3_size = 0,
540*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_EVERY_8,
541*4882a593Smuzhiyun .pal_burst = true,
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun .composite_levels = &pal_m_levels_composite,
544*4882a593Smuzhiyun .composite_color = &pal_m_csc_composite,
545*4882a593Smuzhiyun .svideo_levels = &pal_m_levels_svideo,
546*4882a593Smuzhiyun .svideo_color = &pal_m_csc_svideo,
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun .filter_table = filter_table,
549*4882a593Smuzhiyun },
550*4882a593Smuzhiyun {
551*4882a593Smuzhiyun /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
552*4882a593Smuzhiyun .name = "PAL-N",
553*4882a593Smuzhiyun .clock = 108000,
554*4882a593Smuzhiyun .refresh = 50000,
555*4882a593Smuzhiyun .oversample = 8,
556*4882a593Smuzhiyun .component_only = false,
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 128,
559*4882a593Smuzhiyun .hblank_start = 844, .htotal = 863,
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun .vsync_start_f1 = 6, .vsync_start_f2 = 7,
565*4882a593Smuzhiyun .vsync_len = 6,
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
568*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 18,
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun .vi_end_f1 = 24, .vi_end_f2 = 25,
571*4882a593Smuzhiyun .nbr_end = 286,
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun .burst_ena = true,
574*4882a593Smuzhiyun .hburst_start = 73, .hburst_len = 34,
575*4882a593Smuzhiyun .vburst_start_f1 = 8, .vburst_end_f1 = 285,
576*4882a593Smuzhiyun .vburst_start_f2 = 8, .vburst_end_f2 = 286,
577*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 286,
578*4882a593Smuzhiyun .vburst_start_f4 = 9, .vburst_end_f4 = 285,
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun /* desired 4.4336180 actual 4.4336180 clock 107.52 */
582*4882a593Smuzhiyun .dda1_inc = 135,
583*4882a593Smuzhiyun .dda2_inc = 23578, .dda2_size = 27648,
584*4882a593Smuzhiyun .dda3_inc = 134, .dda3_size = 625,
585*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_EVERY_8,
586*4882a593Smuzhiyun .pal_burst = true,
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun .composite_levels = &pal_n_levels_composite,
589*4882a593Smuzhiyun .composite_color = &pal_n_csc_composite,
590*4882a593Smuzhiyun .svideo_levels = &pal_n_levels_svideo,
591*4882a593Smuzhiyun .svideo_color = &pal_n_csc_svideo,
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun .filter_table = filter_table,
594*4882a593Smuzhiyun },
595*4882a593Smuzhiyun {
596*4882a593Smuzhiyun /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
597*4882a593Smuzhiyun .name = "PAL",
598*4882a593Smuzhiyun .clock = 108000,
599*4882a593Smuzhiyun .refresh = 50000,
600*4882a593Smuzhiyun .oversample = 8,
601*4882a593Smuzhiyun .component_only = false,
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 142,
604*4882a593Smuzhiyun .hblank_start = 844, .htotal = 863,
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun .progressive = false, .trilevel_sync = false,
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun .vsync_start_f1 = 5, .vsync_start_f2 = 6,
609*4882a593Smuzhiyun .vsync_len = 5,
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 0,
612*4882a593Smuzhiyun .veq_start_f2 = 1, .veq_len = 15,
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun .vi_end_f1 = 24, .vi_end_f2 = 25,
615*4882a593Smuzhiyun .nbr_end = 286,
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun .burst_ena = true,
618*4882a593Smuzhiyun .hburst_start = 73, .hburst_len = 32,
619*4882a593Smuzhiyun .vburst_start_f1 = 8, .vburst_end_f1 = 285,
620*4882a593Smuzhiyun .vburst_start_f2 = 8, .vburst_end_f2 = 286,
621*4882a593Smuzhiyun .vburst_start_f3 = 9, .vburst_end_f3 = 286,
622*4882a593Smuzhiyun .vburst_start_f4 = 9, .vburst_end_f4 = 285,
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun /* desired 4.4336180 actual 4.4336180 clock 107.52 */
625*4882a593Smuzhiyun .dda1_inc = 168,
626*4882a593Smuzhiyun .dda2_inc = 4122, .dda2_size = 27648,
627*4882a593Smuzhiyun .dda3_inc = 67, .dda3_size = 625,
628*4882a593Smuzhiyun .sc_reset = TV_SC_RESET_EVERY_8,
629*4882a593Smuzhiyun .pal_burst = true,
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun .composite_levels = &pal_levels_composite,
632*4882a593Smuzhiyun .composite_color = &pal_csc_composite,
633*4882a593Smuzhiyun .svideo_levels = &pal_levels_svideo,
634*4882a593Smuzhiyun .svideo_color = &pal_csc_svideo,
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun .filter_table = filter_table,
637*4882a593Smuzhiyun },
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun .name = "480p",
640*4882a593Smuzhiyun .clock = 108000,
641*4882a593Smuzhiyun .refresh = 59940,
642*4882a593Smuzhiyun .oversample = 4,
643*4882a593Smuzhiyun .component_only = true,
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 122,
646*4882a593Smuzhiyun .hblank_start = 842, .htotal = 857,
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun .progressive = true, .trilevel_sync = false,
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun .vsync_start_f1 = 12, .vsync_start_f2 = 12,
651*4882a593Smuzhiyun .vsync_len = 12,
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun .veq_ena = false,
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun .vi_end_f1 = 44, .vi_end_f2 = 44,
656*4882a593Smuzhiyun .nbr_end = 479,
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun .burst_ena = false,
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun .filter_table = filter_table,
661*4882a593Smuzhiyun },
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun .name = "576p",
664*4882a593Smuzhiyun .clock = 108000,
665*4882a593Smuzhiyun .refresh = 50000,
666*4882a593Smuzhiyun .oversample = 4,
667*4882a593Smuzhiyun .component_only = true,
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun .hsync_end = 64, .hblank_end = 139,
670*4882a593Smuzhiyun .hblank_start = 859, .htotal = 863,
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun .progressive = true, .trilevel_sync = false,
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun .vsync_start_f1 = 10, .vsync_start_f2 = 10,
675*4882a593Smuzhiyun .vsync_len = 10,
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun .veq_ena = false,
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun .vi_end_f1 = 48, .vi_end_f2 = 48,
680*4882a593Smuzhiyun .nbr_end = 575,
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun .burst_ena = false,
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun .filter_table = filter_table,
685*4882a593Smuzhiyun },
686*4882a593Smuzhiyun {
687*4882a593Smuzhiyun .name = "720p@60Hz",
688*4882a593Smuzhiyun .clock = 148500,
689*4882a593Smuzhiyun .refresh = 60000,
690*4882a593Smuzhiyun .oversample = 2,
691*4882a593Smuzhiyun .component_only = true,
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun .hsync_end = 80, .hblank_end = 300,
694*4882a593Smuzhiyun .hblank_start = 1580, .htotal = 1649,
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun .progressive = true, .trilevel_sync = true,
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun .vsync_start_f1 = 10, .vsync_start_f2 = 10,
699*4882a593Smuzhiyun .vsync_len = 10,
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun .veq_ena = false,
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun .vi_end_f1 = 29, .vi_end_f2 = 29,
704*4882a593Smuzhiyun .nbr_end = 719,
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun .burst_ena = false,
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun .filter_table = filter_table,
709*4882a593Smuzhiyun },
710*4882a593Smuzhiyun {
711*4882a593Smuzhiyun .name = "720p@50Hz",
712*4882a593Smuzhiyun .clock = 148500,
713*4882a593Smuzhiyun .refresh = 50000,
714*4882a593Smuzhiyun .oversample = 2,
715*4882a593Smuzhiyun .component_only = true,
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun .hsync_end = 80, .hblank_end = 300,
718*4882a593Smuzhiyun .hblank_start = 1580, .htotal = 1979,
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun .progressive = true, .trilevel_sync = true,
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun .vsync_start_f1 = 10, .vsync_start_f2 = 10,
723*4882a593Smuzhiyun .vsync_len = 10,
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun .veq_ena = false,
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun .vi_end_f1 = 29, .vi_end_f2 = 29,
728*4882a593Smuzhiyun .nbr_end = 719,
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun .burst_ena = false,
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun .filter_table = filter_table,
733*4882a593Smuzhiyun },
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun .name = "1080i@50Hz",
736*4882a593Smuzhiyun .clock = 148500,
737*4882a593Smuzhiyun .refresh = 50000,
738*4882a593Smuzhiyun .oversample = 2,
739*4882a593Smuzhiyun .component_only = true,
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun .hsync_end = 88, .hblank_end = 235,
742*4882a593Smuzhiyun .hblank_start = 2155, .htotal = 2639,
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun .progressive = false, .trilevel_sync = true,
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun .vsync_start_f1 = 4, .vsync_start_f2 = 5,
747*4882a593Smuzhiyun .vsync_len = 10,
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 4,
750*4882a593Smuzhiyun .veq_start_f2 = 4, .veq_len = 10,
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun
753*4882a593Smuzhiyun .vi_end_f1 = 21, .vi_end_f2 = 22,
754*4882a593Smuzhiyun .nbr_end = 539,
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun .burst_ena = false,
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun .filter_table = filter_table,
759*4882a593Smuzhiyun },
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun .name = "1080i@60Hz",
762*4882a593Smuzhiyun .clock = 148500,
763*4882a593Smuzhiyun .refresh = 60000,
764*4882a593Smuzhiyun .oversample = 2,
765*4882a593Smuzhiyun .component_only = true,
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun .hsync_end = 88, .hblank_end = 235,
768*4882a593Smuzhiyun .hblank_start = 2155, .htotal = 2199,
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun .progressive = false, .trilevel_sync = true,
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun .vsync_start_f1 = 4, .vsync_start_f2 = 5,
773*4882a593Smuzhiyun .vsync_len = 10,
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun .veq_ena = true, .veq_start_f1 = 4,
776*4882a593Smuzhiyun .veq_start_f2 = 4, .veq_len = 10,
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun .vi_end_f1 = 21, .vi_end_f2 = 22,
780*4882a593Smuzhiyun .nbr_end = 539,
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun .burst_ena = false,
783*4882a593Smuzhiyun
784*4882a593Smuzhiyun .filter_table = filter_table,
785*4882a593Smuzhiyun },
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun {
788*4882a593Smuzhiyun .name = "1080p@30Hz",
789*4882a593Smuzhiyun .clock = 148500,
790*4882a593Smuzhiyun .refresh = 30000,
791*4882a593Smuzhiyun .oversample = 2,
792*4882a593Smuzhiyun .component_only = true,
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun .hsync_end = 88, .hblank_end = 235,
795*4882a593Smuzhiyun .hblank_start = 2155, .htotal = 2199,
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun .progressive = true, .trilevel_sync = true,
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun .vsync_start_f1 = 8, .vsync_start_f2 = 8,
800*4882a593Smuzhiyun .vsync_len = 10,
801*4882a593Smuzhiyun
802*4882a593Smuzhiyun .veq_ena = false, .veq_start_f1 = 0,
803*4882a593Smuzhiyun .veq_start_f2 = 0, .veq_len = 0,
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun .vi_end_f1 = 44, .vi_end_f2 = 44,
806*4882a593Smuzhiyun .nbr_end = 1079,
807*4882a593Smuzhiyun
808*4882a593Smuzhiyun .burst_ena = false,
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun .filter_table = filter_table,
811*4882a593Smuzhiyun },
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun {
814*4882a593Smuzhiyun .name = "1080p@50Hz",
815*4882a593Smuzhiyun .clock = 148500,
816*4882a593Smuzhiyun .refresh = 50000,
817*4882a593Smuzhiyun .oversample = 1,
818*4882a593Smuzhiyun .component_only = true,
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun .hsync_end = 88, .hblank_end = 235,
821*4882a593Smuzhiyun .hblank_start = 2155, .htotal = 2639,
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun .progressive = true, .trilevel_sync = true,
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun .vsync_start_f1 = 8, .vsync_start_f2 = 8,
826*4882a593Smuzhiyun .vsync_len = 10,
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun .veq_ena = false, .veq_start_f1 = 0,
829*4882a593Smuzhiyun .veq_start_f2 = 0, .veq_len = 0,
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun .vi_end_f1 = 44, .vi_end_f2 = 44,
832*4882a593Smuzhiyun .nbr_end = 1079,
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun .burst_ena = false,
835*4882a593Smuzhiyun
836*4882a593Smuzhiyun .filter_table = filter_table,
837*4882a593Smuzhiyun },
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun {
840*4882a593Smuzhiyun .name = "1080p@60Hz",
841*4882a593Smuzhiyun .clock = 148500,
842*4882a593Smuzhiyun .refresh = 60000,
843*4882a593Smuzhiyun .oversample = 1,
844*4882a593Smuzhiyun .component_only = true,
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun .hsync_end = 88, .hblank_end = 235,
847*4882a593Smuzhiyun .hblank_start = 2155, .htotal = 2199,
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun .progressive = true, .trilevel_sync = true,
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun .vsync_start_f1 = 8, .vsync_start_f2 = 8,
852*4882a593Smuzhiyun .vsync_len = 10,
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun .veq_ena = false, .veq_start_f1 = 0,
855*4882a593Smuzhiyun .veq_start_f2 = 0, .veq_len = 0,
856*4882a593Smuzhiyun
857*4882a593Smuzhiyun .vi_end_f1 = 44, .vi_end_f2 = 44,
858*4882a593Smuzhiyun .nbr_end = 1079,
859*4882a593Smuzhiyun
860*4882a593Smuzhiyun .burst_ena = false,
861*4882a593Smuzhiyun
862*4882a593Smuzhiyun .filter_table = filter_table,
863*4882a593Smuzhiyun },
864*4882a593Smuzhiyun };
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun struct intel_tv_connector_state {
867*4882a593Smuzhiyun struct drm_connector_state base;
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun /*
870*4882a593Smuzhiyun * May need to override the user margins for
871*4882a593Smuzhiyun * gen3 >1024 wide source vertical centering.
872*4882a593Smuzhiyun */
873*4882a593Smuzhiyun struct {
874*4882a593Smuzhiyun u16 top, bottom;
875*4882a593Smuzhiyun } margins;
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun bool bypass_vfilter;
878*4882a593Smuzhiyun };
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun #define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun static struct drm_connector_state *
intel_tv_connector_duplicate_state(struct drm_connector * connector)883*4882a593Smuzhiyun intel_tv_connector_duplicate_state(struct drm_connector *connector)
884*4882a593Smuzhiyun {
885*4882a593Smuzhiyun struct intel_tv_connector_state *state;
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
888*4882a593Smuzhiyun if (!state)
889*4882a593Smuzhiyun return NULL;
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
892*4882a593Smuzhiyun return &state->base;
893*4882a593Smuzhiyun }
894*4882a593Smuzhiyun
enc_to_tv(struct intel_encoder * encoder)895*4882a593Smuzhiyun static struct intel_tv *enc_to_tv(struct intel_encoder *encoder)
896*4882a593Smuzhiyun {
897*4882a593Smuzhiyun return container_of(encoder, struct intel_tv, base);
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun
intel_attached_tv(struct intel_connector * connector)900*4882a593Smuzhiyun static struct intel_tv *intel_attached_tv(struct intel_connector *connector)
901*4882a593Smuzhiyun {
902*4882a593Smuzhiyun return enc_to_tv(intel_attached_encoder(connector));
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun static bool
intel_tv_get_hw_state(struct intel_encoder * encoder,enum pipe * pipe)906*4882a593Smuzhiyun intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
909*4882a593Smuzhiyun u32 tmp = intel_de_read(dev_priv, TV_CTL);
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun *pipe = (tmp & TV_ENC_PIPE_SEL_MASK) >> TV_ENC_PIPE_SEL_SHIFT;
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun return tmp & TV_ENC_ENABLE;
914*4882a593Smuzhiyun }
915*4882a593Smuzhiyun
916*4882a593Smuzhiyun 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)917*4882a593Smuzhiyun intel_enable_tv(struct intel_atomic_state *state,
918*4882a593Smuzhiyun struct intel_encoder *encoder,
919*4882a593Smuzhiyun const struct intel_crtc_state *pipe_config,
920*4882a593Smuzhiyun const struct drm_connector_state *conn_state)
921*4882a593Smuzhiyun {
922*4882a593Smuzhiyun struct drm_device *dev = encoder->base.dev;
923*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(dev);
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun /* Prevents vblank waits from timing out in intel_tv_detect_type() */
926*4882a593Smuzhiyun intel_wait_for_vblank(dev_priv,
927*4882a593Smuzhiyun to_intel_crtc(pipe_config->uapi.crtc)->pipe);
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CTL,
930*4882a593Smuzhiyun intel_de_read(dev_priv, TV_CTL) | TV_ENC_ENABLE);
931*4882a593Smuzhiyun }
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun 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)934*4882a593Smuzhiyun intel_disable_tv(struct intel_atomic_state *state,
935*4882a593Smuzhiyun struct intel_encoder *encoder,
936*4882a593Smuzhiyun const struct intel_crtc_state *old_crtc_state,
937*4882a593Smuzhiyun const struct drm_connector_state *old_conn_state)
938*4882a593Smuzhiyun {
939*4882a593Smuzhiyun struct drm_device *dev = encoder->base.dev;
940*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(dev);
941*4882a593Smuzhiyun
942*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CTL,
943*4882a593Smuzhiyun intel_de_read(dev_priv, TV_CTL) & ~TV_ENC_ENABLE);
944*4882a593Smuzhiyun }
945*4882a593Smuzhiyun
intel_tv_mode_find(const struct drm_connector_state * conn_state)946*4882a593Smuzhiyun static const struct tv_mode *intel_tv_mode_find(const struct drm_connector_state *conn_state)
947*4882a593Smuzhiyun {
948*4882a593Smuzhiyun int format = conn_state->tv.mode;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun return &tv_modes[format];
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun static enum drm_mode_status
intel_tv_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)954*4882a593Smuzhiyun intel_tv_mode_valid(struct drm_connector *connector,
955*4882a593Smuzhiyun struct drm_display_mode *mode)
956*4882a593Smuzhiyun {
957*4882a593Smuzhiyun const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
958*4882a593Smuzhiyun int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
961*4882a593Smuzhiyun return MODE_NO_DBLESCAN;
962*4882a593Smuzhiyun
963*4882a593Smuzhiyun if (mode->clock > max_dotclk)
964*4882a593Smuzhiyun return MODE_CLOCK_HIGH;
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun /* Ensure TV refresh is close to desired refresh */
967*4882a593Smuzhiyun if (abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) >= 1000)
968*4882a593Smuzhiyun return MODE_CLOCK_RANGE;
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun return MODE_OK;
971*4882a593Smuzhiyun }
972*4882a593Smuzhiyun
973*4882a593Smuzhiyun static int
intel_tv_mode_vdisplay(const struct tv_mode * tv_mode)974*4882a593Smuzhiyun intel_tv_mode_vdisplay(const struct tv_mode *tv_mode)
975*4882a593Smuzhiyun {
976*4882a593Smuzhiyun if (tv_mode->progressive)
977*4882a593Smuzhiyun return tv_mode->nbr_end + 1;
978*4882a593Smuzhiyun else
979*4882a593Smuzhiyun return 2 * (tv_mode->nbr_end + 1);
980*4882a593Smuzhiyun }
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun static void
intel_tv_mode_to_mode(struct drm_display_mode * mode,const struct tv_mode * tv_mode)983*4882a593Smuzhiyun intel_tv_mode_to_mode(struct drm_display_mode *mode,
984*4882a593Smuzhiyun const struct tv_mode *tv_mode)
985*4882a593Smuzhiyun {
986*4882a593Smuzhiyun mode->clock = tv_mode->clock /
987*4882a593Smuzhiyun (tv_mode->oversample >> !tv_mode->progressive);
988*4882a593Smuzhiyun
989*4882a593Smuzhiyun /*
990*4882a593Smuzhiyun * tv_mode horizontal timings:
991*4882a593Smuzhiyun *
992*4882a593Smuzhiyun * hsync_end
993*4882a593Smuzhiyun * | hblank_end
994*4882a593Smuzhiyun * | | hblank_start
995*4882a593Smuzhiyun * | | | htotal
996*4882a593Smuzhiyun * | _______ |
997*4882a593Smuzhiyun * ____/ \___
998*4882a593Smuzhiyun * \__/ \
999*4882a593Smuzhiyun */
1000*4882a593Smuzhiyun mode->hdisplay =
1001*4882a593Smuzhiyun tv_mode->hblank_start - tv_mode->hblank_end;
1002*4882a593Smuzhiyun mode->hsync_start = mode->hdisplay +
1003*4882a593Smuzhiyun tv_mode->htotal - tv_mode->hblank_start;
1004*4882a593Smuzhiyun mode->hsync_end = mode->hsync_start +
1005*4882a593Smuzhiyun tv_mode->hsync_end;
1006*4882a593Smuzhiyun mode->htotal = tv_mode->htotal + 1;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun /*
1009*4882a593Smuzhiyun * tv_mode vertical timings:
1010*4882a593Smuzhiyun *
1011*4882a593Smuzhiyun * vsync_start
1012*4882a593Smuzhiyun * | vsync_end
1013*4882a593Smuzhiyun * | | vi_end nbr_end
1014*4882a593Smuzhiyun * | | | |
1015*4882a593Smuzhiyun * | | _______
1016*4882a593Smuzhiyun * \__ ____/ \
1017*4882a593Smuzhiyun * \__/
1018*4882a593Smuzhiyun */
1019*4882a593Smuzhiyun mode->vdisplay = intel_tv_mode_vdisplay(tv_mode);
1020*4882a593Smuzhiyun if (tv_mode->progressive) {
1021*4882a593Smuzhiyun mode->vsync_start = mode->vdisplay +
1022*4882a593Smuzhiyun tv_mode->vsync_start_f1 + 1;
1023*4882a593Smuzhiyun mode->vsync_end = mode->vsync_start +
1024*4882a593Smuzhiyun tv_mode->vsync_len;
1025*4882a593Smuzhiyun mode->vtotal = mode->vdisplay +
1026*4882a593Smuzhiyun tv_mode->vi_end_f1 + 1;
1027*4882a593Smuzhiyun } else {
1028*4882a593Smuzhiyun mode->vsync_start = mode->vdisplay +
1029*4882a593Smuzhiyun tv_mode->vsync_start_f1 + 1 +
1030*4882a593Smuzhiyun tv_mode->vsync_start_f2 + 1;
1031*4882a593Smuzhiyun mode->vsync_end = mode->vsync_start +
1032*4882a593Smuzhiyun 2 * tv_mode->vsync_len;
1033*4882a593Smuzhiyun mode->vtotal = mode->vdisplay +
1034*4882a593Smuzhiyun tv_mode->vi_end_f1 + 1 +
1035*4882a593Smuzhiyun tv_mode->vi_end_f2 + 1;
1036*4882a593Smuzhiyun }
1037*4882a593Smuzhiyun
1038*4882a593Smuzhiyun /* TV has it's own notion of sync and other mode flags, so clear them. */
1039*4882a593Smuzhiyun mode->flags = 0;
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun snprintf(mode->name, sizeof(mode->name),
1042*4882a593Smuzhiyun "%dx%d%c (%s)",
1043*4882a593Smuzhiyun mode->hdisplay, mode->vdisplay,
1044*4882a593Smuzhiyun tv_mode->progressive ? 'p' : 'i',
1045*4882a593Smuzhiyun tv_mode->name);
1046*4882a593Smuzhiyun }
1047*4882a593Smuzhiyun
intel_tv_scale_mode_horiz(struct drm_display_mode * mode,int hdisplay,int left_margin,int right_margin)1048*4882a593Smuzhiyun static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode,
1049*4882a593Smuzhiyun int hdisplay, int left_margin,
1050*4882a593Smuzhiyun int right_margin)
1051*4882a593Smuzhiyun {
1052*4882a593Smuzhiyun int hsync_start = mode->hsync_start - mode->hdisplay + right_margin;
1053*4882a593Smuzhiyun int hsync_end = mode->hsync_end - mode->hdisplay + right_margin;
1054*4882a593Smuzhiyun int new_htotal = mode->htotal * hdisplay /
1055*4882a593Smuzhiyun (mode->hdisplay - left_margin - right_margin);
1056*4882a593Smuzhiyun
1057*4882a593Smuzhiyun mode->clock = mode->clock * new_htotal / mode->htotal;
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun mode->hdisplay = hdisplay;
1060*4882a593Smuzhiyun mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal;
1061*4882a593Smuzhiyun mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal;
1062*4882a593Smuzhiyun mode->htotal = new_htotal;
1063*4882a593Smuzhiyun }
1064*4882a593Smuzhiyun
intel_tv_scale_mode_vert(struct drm_display_mode * mode,int vdisplay,int top_margin,int bottom_margin)1065*4882a593Smuzhiyun static void intel_tv_scale_mode_vert(struct drm_display_mode *mode,
1066*4882a593Smuzhiyun int vdisplay, int top_margin,
1067*4882a593Smuzhiyun int bottom_margin)
1068*4882a593Smuzhiyun {
1069*4882a593Smuzhiyun int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin;
1070*4882a593Smuzhiyun int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin;
1071*4882a593Smuzhiyun int new_vtotal = mode->vtotal * vdisplay /
1072*4882a593Smuzhiyun (mode->vdisplay - top_margin - bottom_margin);
1073*4882a593Smuzhiyun
1074*4882a593Smuzhiyun mode->clock = mode->clock * new_vtotal / mode->vtotal;
1075*4882a593Smuzhiyun
1076*4882a593Smuzhiyun mode->vdisplay = vdisplay;
1077*4882a593Smuzhiyun mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal;
1078*4882a593Smuzhiyun mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal;
1079*4882a593Smuzhiyun mode->vtotal = new_vtotal;
1080*4882a593Smuzhiyun }
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun static void
intel_tv_get_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config)1083*4882a593Smuzhiyun intel_tv_get_config(struct intel_encoder *encoder,
1084*4882a593Smuzhiyun struct intel_crtc_state *pipe_config)
1085*4882a593Smuzhiyun {
1086*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1087*4882a593Smuzhiyun struct drm_display_mode *adjusted_mode =
1088*4882a593Smuzhiyun &pipe_config->hw.adjusted_mode;
1089*4882a593Smuzhiyun struct drm_display_mode mode = {};
1090*4882a593Smuzhiyun u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp;
1091*4882a593Smuzhiyun struct tv_mode tv_mode = {};
1092*4882a593Smuzhiyun int hdisplay = adjusted_mode->crtc_hdisplay;
1093*4882a593Smuzhiyun int vdisplay = adjusted_mode->crtc_vdisplay;
1094*4882a593Smuzhiyun int xsize, ysize, xpos, ypos;
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
1097*4882a593Smuzhiyun
1098*4882a593Smuzhiyun tv_ctl = intel_de_read(dev_priv, TV_CTL);
1099*4882a593Smuzhiyun hctl1 = intel_de_read(dev_priv, TV_H_CTL_1);
1100*4882a593Smuzhiyun hctl3 = intel_de_read(dev_priv, TV_H_CTL_3);
1101*4882a593Smuzhiyun vctl1 = intel_de_read(dev_priv, TV_V_CTL_1);
1102*4882a593Smuzhiyun vctl2 = intel_de_read(dev_priv, TV_V_CTL_2);
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT;
1105*4882a593Smuzhiyun tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT;
1106*4882a593Smuzhiyun
1107*4882a593Smuzhiyun tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT;
1108*4882a593Smuzhiyun tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT;
1109*4882a593Smuzhiyun
1110*4882a593Smuzhiyun tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT;
1111*4882a593Smuzhiyun tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT;
1112*4882a593Smuzhiyun tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT;
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT;
1115*4882a593Smuzhiyun tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT;
1116*4882a593Smuzhiyun tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT;
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun tv_mode.clock = pipe_config->port_clock;
1119*4882a593Smuzhiyun
1120*4882a593Smuzhiyun tv_mode.progressive = tv_ctl & TV_PROGRESSIVE;
1121*4882a593Smuzhiyun
1122*4882a593Smuzhiyun switch (tv_ctl & TV_OVERSAMPLE_MASK) {
1123*4882a593Smuzhiyun case TV_OVERSAMPLE_8X:
1124*4882a593Smuzhiyun tv_mode.oversample = 8;
1125*4882a593Smuzhiyun break;
1126*4882a593Smuzhiyun case TV_OVERSAMPLE_4X:
1127*4882a593Smuzhiyun tv_mode.oversample = 4;
1128*4882a593Smuzhiyun break;
1129*4882a593Smuzhiyun case TV_OVERSAMPLE_2X:
1130*4882a593Smuzhiyun tv_mode.oversample = 2;
1131*4882a593Smuzhiyun break;
1132*4882a593Smuzhiyun default:
1133*4882a593Smuzhiyun tv_mode.oversample = 1;
1134*4882a593Smuzhiyun break;
1135*4882a593Smuzhiyun }
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun tmp = intel_de_read(dev_priv, TV_WIN_POS);
1138*4882a593Smuzhiyun xpos = tmp >> 16;
1139*4882a593Smuzhiyun ypos = tmp & 0xffff;
1140*4882a593Smuzhiyun
1141*4882a593Smuzhiyun tmp = intel_de_read(dev_priv, TV_WIN_SIZE);
1142*4882a593Smuzhiyun xsize = tmp >> 16;
1143*4882a593Smuzhiyun ysize = tmp & 0xffff;
1144*4882a593Smuzhiyun
1145*4882a593Smuzhiyun intel_tv_mode_to_mode(&mode, &tv_mode);
1146*4882a593Smuzhiyun
1147*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "TV mode:\n");
1148*4882a593Smuzhiyun drm_mode_debug_printmodeline(&mode);
1149*4882a593Smuzhiyun
1150*4882a593Smuzhiyun intel_tv_scale_mode_horiz(&mode, hdisplay,
1151*4882a593Smuzhiyun xpos, mode.hdisplay - xsize - xpos);
1152*4882a593Smuzhiyun intel_tv_scale_mode_vert(&mode, vdisplay,
1153*4882a593Smuzhiyun ypos, mode.vdisplay - ysize - ypos);
1154*4882a593Smuzhiyun
1155*4882a593Smuzhiyun adjusted_mode->crtc_clock = mode.clock;
1156*4882a593Smuzhiyun if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
1157*4882a593Smuzhiyun adjusted_mode->crtc_clock /= 2;
1158*4882a593Smuzhiyun
1159*4882a593Smuzhiyun /* pixel counter doesn't work on i965gm TV output */
1160*4882a593Smuzhiyun if (IS_I965GM(dev_priv))
1161*4882a593Smuzhiyun pipe_config->mode_flags |=
1162*4882a593Smuzhiyun I915_MODE_FLAG_USE_SCANLINE_COUNTER;
1163*4882a593Smuzhiyun }
1164*4882a593Smuzhiyun
intel_tv_source_too_wide(struct drm_i915_private * dev_priv,int hdisplay)1165*4882a593Smuzhiyun static bool intel_tv_source_too_wide(struct drm_i915_private *dev_priv,
1166*4882a593Smuzhiyun int hdisplay)
1167*4882a593Smuzhiyun {
1168*4882a593Smuzhiyun return IS_GEN(dev_priv, 3) && hdisplay > 1024;
1169*4882a593Smuzhiyun }
1170*4882a593Smuzhiyun
intel_tv_vert_scaling(const struct drm_display_mode * tv_mode,const struct drm_connector_state * conn_state,int vdisplay)1171*4882a593Smuzhiyun static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode,
1172*4882a593Smuzhiyun const struct drm_connector_state *conn_state,
1173*4882a593Smuzhiyun int vdisplay)
1174*4882a593Smuzhiyun {
1175*4882a593Smuzhiyun return tv_mode->crtc_vdisplay -
1176*4882a593Smuzhiyun conn_state->tv.margins.top -
1177*4882a593Smuzhiyun conn_state->tv.margins.bottom !=
1178*4882a593Smuzhiyun vdisplay;
1179*4882a593Smuzhiyun }
1180*4882a593Smuzhiyun
1181*4882a593Smuzhiyun static int
intel_tv_compute_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config,struct drm_connector_state * conn_state)1182*4882a593Smuzhiyun intel_tv_compute_config(struct intel_encoder *encoder,
1183*4882a593Smuzhiyun struct intel_crtc_state *pipe_config,
1184*4882a593Smuzhiyun struct drm_connector_state *conn_state)
1185*4882a593Smuzhiyun {
1186*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1187*4882a593Smuzhiyun struct intel_tv_connector_state *tv_conn_state =
1188*4882a593Smuzhiyun to_intel_tv_connector_state(conn_state);
1189*4882a593Smuzhiyun const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
1190*4882a593Smuzhiyun struct drm_display_mode *adjusted_mode =
1191*4882a593Smuzhiyun &pipe_config->hw.adjusted_mode;
1192*4882a593Smuzhiyun int hdisplay = adjusted_mode->crtc_hdisplay;
1193*4882a593Smuzhiyun int vdisplay = adjusted_mode->crtc_vdisplay;
1194*4882a593Smuzhiyun
1195*4882a593Smuzhiyun if (!tv_mode)
1196*4882a593Smuzhiyun return -EINVAL;
1197*4882a593Smuzhiyun
1198*4882a593Smuzhiyun if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
1199*4882a593Smuzhiyun return -EINVAL;
1200*4882a593Smuzhiyun
1201*4882a593Smuzhiyun pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
1202*4882a593Smuzhiyun
1203*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "forcing bpc to 8 for TV\n");
1204*4882a593Smuzhiyun pipe_config->pipe_bpp = 8*3;
1205*4882a593Smuzhiyun
1206*4882a593Smuzhiyun pipe_config->port_clock = tv_mode->clock;
1207*4882a593Smuzhiyun
1208*4882a593Smuzhiyun intel_tv_mode_to_mode(adjusted_mode, tv_mode);
1209*4882a593Smuzhiyun drm_mode_set_crtcinfo(adjusted_mode, 0);
1210*4882a593Smuzhiyun
1211*4882a593Smuzhiyun if (intel_tv_source_too_wide(dev_priv, hdisplay) ||
1212*4882a593Smuzhiyun !intel_tv_vert_scaling(adjusted_mode, conn_state, vdisplay)) {
1213*4882a593Smuzhiyun int extra, top, bottom;
1214*4882a593Smuzhiyun
1215*4882a593Smuzhiyun extra = adjusted_mode->crtc_vdisplay - vdisplay;
1216*4882a593Smuzhiyun
1217*4882a593Smuzhiyun if (extra < 0) {
1218*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm,
1219*4882a593Smuzhiyun "No vertical scaling for >1024 pixel wide modes\n");
1220*4882a593Smuzhiyun return -EINVAL;
1221*4882a593Smuzhiyun }
1222*4882a593Smuzhiyun
1223*4882a593Smuzhiyun /* Need to turn off the vertical filter and center the image */
1224*4882a593Smuzhiyun
1225*4882a593Smuzhiyun /* Attempt to maintain the relative sizes of the margins */
1226*4882a593Smuzhiyun top = conn_state->tv.margins.top;
1227*4882a593Smuzhiyun bottom = conn_state->tv.margins.bottom;
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun if (top + bottom)
1230*4882a593Smuzhiyun top = extra * top / (top + bottom);
1231*4882a593Smuzhiyun else
1232*4882a593Smuzhiyun top = extra / 2;
1233*4882a593Smuzhiyun bottom = extra - top;
1234*4882a593Smuzhiyun
1235*4882a593Smuzhiyun tv_conn_state->margins.top = top;
1236*4882a593Smuzhiyun tv_conn_state->margins.bottom = bottom;
1237*4882a593Smuzhiyun
1238*4882a593Smuzhiyun tv_conn_state->bypass_vfilter = true;
1239*4882a593Smuzhiyun
1240*4882a593Smuzhiyun if (!tv_mode->progressive) {
1241*4882a593Smuzhiyun adjusted_mode->clock /= 2;
1242*4882a593Smuzhiyun adjusted_mode->crtc_clock /= 2;
1243*4882a593Smuzhiyun adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun } else {
1246*4882a593Smuzhiyun tv_conn_state->margins.top = conn_state->tv.margins.top;
1247*4882a593Smuzhiyun tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
1248*4882a593Smuzhiyun
1249*4882a593Smuzhiyun tv_conn_state->bypass_vfilter = false;
1250*4882a593Smuzhiyun }
1251*4882a593Smuzhiyun
1252*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "TV mode:\n");
1253*4882a593Smuzhiyun drm_mode_debug_printmodeline(adjusted_mode);
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun /*
1256*4882a593Smuzhiyun * The pipe scanline counter behaviour looks as follows when
1257*4882a593Smuzhiyun * using the TV encoder:
1258*4882a593Smuzhiyun *
1259*4882a593Smuzhiyun * time ->
1260*4882a593Smuzhiyun *
1261*4882a593Smuzhiyun * dsl=vtotal-1 | |
1262*4882a593Smuzhiyun * || ||
1263*4882a593Smuzhiyun * ___| | ___| |
1264*4882a593Smuzhiyun * / | / |
1265*4882a593Smuzhiyun * / | / |
1266*4882a593Smuzhiyun * dsl=0 ___/ |_____/ |
1267*4882a593Smuzhiyun * | | | | | |
1268*4882a593Smuzhiyun * ^ ^ ^ ^ ^
1269*4882a593Smuzhiyun * | | | | pipe vblank/first part of tv vblank
1270*4882a593Smuzhiyun * | | | bottom margin
1271*4882a593Smuzhiyun * | | active
1272*4882a593Smuzhiyun * | top margin
1273*4882a593Smuzhiyun * remainder of tv vblank
1274*4882a593Smuzhiyun *
1275*4882a593Smuzhiyun * When the TV encoder is used the pipe wants to run faster
1276*4882a593Smuzhiyun * than expected rate. During the active portion the TV
1277*4882a593Smuzhiyun * encoder stalls the pipe every few lines to keep it in
1278*4882a593Smuzhiyun * check. When the TV encoder reaches the bottom margin the
1279*4882a593Smuzhiyun * pipe simply stops. Once we reach the TV vblank the pipe is
1280*4882a593Smuzhiyun * no longer stalled and it runs at the max rate (apparently
1281*4882a593Smuzhiyun * oversample clock on gen3, cdclk on gen4). Once the pipe
1282*4882a593Smuzhiyun * reaches the pipe vtotal the pipe stops for the remainder
1283*4882a593Smuzhiyun * of the TV vblank/top margin. The pipe starts up again when
1284*4882a593Smuzhiyun * the TV encoder exits the top margin.
1285*4882a593Smuzhiyun *
1286*4882a593Smuzhiyun * To avoid huge hassles for vblank timestamping we scale
1287*4882a593Smuzhiyun * the pipe timings as if the pipe always runs at the average
1288*4882a593Smuzhiyun * rate it maintains during the active period. This also
1289*4882a593Smuzhiyun * gives us a reasonable guesstimate as to the pixel rate.
1290*4882a593Smuzhiyun * Due to the variation in the actual pipe speed the scanline
1291*4882a593Smuzhiyun * counter will give us slightly erroneous results during the
1292*4882a593Smuzhiyun * TV vblank/margins. But since vtotal was selected such that
1293*4882a593Smuzhiyun * it matches the average rate of the pipe during the active
1294*4882a593Smuzhiyun * portion the error shouldn't cause any serious grief to
1295*4882a593Smuzhiyun * vblank timestamps.
1296*4882a593Smuzhiyun *
1297*4882a593Smuzhiyun * For posterity here is the empirically derived formula
1298*4882a593Smuzhiyun * that gives us the maximum length of the pipe vblank
1299*4882a593Smuzhiyun * we can use without causing display corruption. Following
1300*4882a593Smuzhiyun * this would allow us to have a ticking scanline counter
1301*4882a593Smuzhiyun * everywhere except during the bottom margin (there the
1302*4882a593Smuzhiyun * pipe always stops). Ie. this would eliminate the second
1303*4882a593Smuzhiyun * flat portion of the above graph. However this would also
1304*4882a593Smuzhiyun * complicate vblank timestamping as the pipe vtotal would
1305*4882a593Smuzhiyun * no longer match the average rate the pipe runs at during
1306*4882a593Smuzhiyun * the active portion. Hence following this formula seems
1307*4882a593Smuzhiyun * more trouble that it's worth.
1308*4882a593Smuzhiyun *
1309*4882a593Smuzhiyun * if (IS_GEN(dev_priv, 4)) {
1310*4882a593Smuzhiyun * num = cdclk * (tv_mode->oversample >> !tv_mode->progressive);
1311*4882a593Smuzhiyun * den = tv_mode->clock;
1312*4882a593Smuzhiyun * } else {
1313*4882a593Smuzhiyun * num = tv_mode->oversample >> !tv_mode->progressive;
1314*4882a593Smuzhiyun * den = 1;
1315*4882a593Smuzhiyun * }
1316*4882a593Smuzhiyun * max_pipe_vblank_len ~=
1317*4882a593Smuzhiyun * (num * tv_htotal * (tv_vblank_len + top_margin)) /
1318*4882a593Smuzhiyun * (den * pipe_htotal);
1319*4882a593Smuzhiyun */
1320*4882a593Smuzhiyun intel_tv_scale_mode_horiz(adjusted_mode, hdisplay,
1321*4882a593Smuzhiyun conn_state->tv.margins.left,
1322*4882a593Smuzhiyun conn_state->tv.margins.right);
1323*4882a593Smuzhiyun intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
1324*4882a593Smuzhiyun tv_conn_state->margins.top,
1325*4882a593Smuzhiyun tv_conn_state->margins.bottom);
1326*4882a593Smuzhiyun drm_mode_set_crtcinfo(adjusted_mode, 0);
1327*4882a593Smuzhiyun adjusted_mode->name[0] = '\0';
1328*4882a593Smuzhiyun
1329*4882a593Smuzhiyun /* pixel counter doesn't work on i965gm TV output */
1330*4882a593Smuzhiyun if (IS_I965GM(dev_priv))
1331*4882a593Smuzhiyun pipe_config->mode_flags |=
1332*4882a593Smuzhiyun I915_MODE_FLAG_USE_SCANLINE_COUNTER;
1333*4882a593Smuzhiyun
1334*4882a593Smuzhiyun return 0;
1335*4882a593Smuzhiyun }
1336*4882a593Smuzhiyun
1337*4882a593Smuzhiyun static void
set_tv_mode_timings(struct drm_i915_private * dev_priv,const struct tv_mode * tv_mode,bool burst_ena)1338*4882a593Smuzhiyun set_tv_mode_timings(struct drm_i915_private *dev_priv,
1339*4882a593Smuzhiyun const struct tv_mode *tv_mode,
1340*4882a593Smuzhiyun bool burst_ena)
1341*4882a593Smuzhiyun {
1342*4882a593Smuzhiyun u32 hctl1, hctl2, hctl3;
1343*4882a593Smuzhiyun u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
1346*4882a593Smuzhiyun (tv_mode->htotal << TV_HTOTAL_SHIFT);
1347*4882a593Smuzhiyun
1348*4882a593Smuzhiyun hctl2 = (tv_mode->hburst_start << 16) |
1349*4882a593Smuzhiyun (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
1350*4882a593Smuzhiyun
1351*4882a593Smuzhiyun if (burst_ena)
1352*4882a593Smuzhiyun hctl2 |= TV_BURST_ENA;
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
1355*4882a593Smuzhiyun (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
1358*4882a593Smuzhiyun (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
1359*4882a593Smuzhiyun (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
1360*4882a593Smuzhiyun
1361*4882a593Smuzhiyun vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
1362*4882a593Smuzhiyun (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
1363*4882a593Smuzhiyun (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
1366*4882a593Smuzhiyun (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
1367*4882a593Smuzhiyun (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
1368*4882a593Smuzhiyun
1369*4882a593Smuzhiyun if (tv_mode->veq_ena)
1370*4882a593Smuzhiyun vctl3 |= TV_EQUAL_ENA;
1371*4882a593Smuzhiyun
1372*4882a593Smuzhiyun vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
1373*4882a593Smuzhiyun (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
1374*4882a593Smuzhiyun
1375*4882a593Smuzhiyun vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
1376*4882a593Smuzhiyun (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
1377*4882a593Smuzhiyun
1378*4882a593Smuzhiyun vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
1379*4882a593Smuzhiyun (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
1380*4882a593Smuzhiyun
1381*4882a593Smuzhiyun vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
1382*4882a593Smuzhiyun (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
1383*4882a593Smuzhiyun
1384*4882a593Smuzhiyun intel_de_write(dev_priv, TV_H_CTL_1, hctl1);
1385*4882a593Smuzhiyun intel_de_write(dev_priv, TV_H_CTL_2, hctl2);
1386*4882a593Smuzhiyun intel_de_write(dev_priv, TV_H_CTL_3, hctl3);
1387*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_1, vctl1);
1388*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_2, vctl2);
1389*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_3, vctl3);
1390*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_4, vctl4);
1391*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_5, vctl5);
1392*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_6, vctl6);
1393*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CTL_7, vctl7);
1394*4882a593Smuzhiyun }
1395*4882a593Smuzhiyun
set_color_conversion(struct drm_i915_private * dev_priv,const struct color_conversion * color_conversion)1396*4882a593Smuzhiyun static void set_color_conversion(struct drm_i915_private *dev_priv,
1397*4882a593Smuzhiyun const struct color_conversion *color_conversion)
1398*4882a593Smuzhiyun {
1399*4882a593Smuzhiyun if (!color_conversion)
1400*4882a593Smuzhiyun return;
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_Y,
1403*4882a593Smuzhiyun (color_conversion->ry << 16) | color_conversion->gy);
1404*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_Y2,
1405*4882a593Smuzhiyun (color_conversion->by << 16) | color_conversion->ay);
1406*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_U,
1407*4882a593Smuzhiyun (color_conversion->ru << 16) | color_conversion->gu);
1408*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_U2,
1409*4882a593Smuzhiyun (color_conversion->bu << 16) | color_conversion->au);
1410*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_V,
1411*4882a593Smuzhiyun (color_conversion->rv << 16) | color_conversion->gv);
1412*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CSC_V2,
1413*4882a593Smuzhiyun (color_conversion->bv << 16) | color_conversion->av);
1414*4882a593Smuzhiyun }
1415*4882a593Smuzhiyun
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)1416*4882a593Smuzhiyun static void intel_tv_pre_enable(struct intel_atomic_state *state,
1417*4882a593Smuzhiyun struct intel_encoder *encoder,
1418*4882a593Smuzhiyun const struct intel_crtc_state *pipe_config,
1419*4882a593Smuzhiyun const struct drm_connector_state *conn_state)
1420*4882a593Smuzhiyun {
1421*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1422*4882a593Smuzhiyun struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->uapi.crtc);
1423*4882a593Smuzhiyun struct intel_tv *intel_tv = enc_to_tv(encoder);
1424*4882a593Smuzhiyun const struct intel_tv_connector_state *tv_conn_state =
1425*4882a593Smuzhiyun to_intel_tv_connector_state(conn_state);
1426*4882a593Smuzhiyun const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
1427*4882a593Smuzhiyun u32 tv_ctl, tv_filter_ctl;
1428*4882a593Smuzhiyun u32 scctl1, scctl2, scctl3;
1429*4882a593Smuzhiyun int i, j;
1430*4882a593Smuzhiyun const struct video_levels *video_levels;
1431*4882a593Smuzhiyun const struct color_conversion *color_conversion;
1432*4882a593Smuzhiyun bool burst_ena;
1433*4882a593Smuzhiyun int xpos, ypos;
1434*4882a593Smuzhiyun unsigned int xsize, ysize;
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun if (!tv_mode)
1437*4882a593Smuzhiyun return; /* can't happen (mode_prepare prevents this) */
1438*4882a593Smuzhiyun
1439*4882a593Smuzhiyun tv_ctl = intel_de_read(dev_priv, TV_CTL);
1440*4882a593Smuzhiyun tv_ctl &= TV_CTL_SAVE;
1441*4882a593Smuzhiyun
1442*4882a593Smuzhiyun switch (intel_tv->type) {
1443*4882a593Smuzhiyun default:
1444*4882a593Smuzhiyun case DRM_MODE_CONNECTOR_Unknown:
1445*4882a593Smuzhiyun case DRM_MODE_CONNECTOR_Composite:
1446*4882a593Smuzhiyun tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
1447*4882a593Smuzhiyun video_levels = tv_mode->composite_levels;
1448*4882a593Smuzhiyun color_conversion = tv_mode->composite_color;
1449*4882a593Smuzhiyun burst_ena = tv_mode->burst_ena;
1450*4882a593Smuzhiyun break;
1451*4882a593Smuzhiyun case DRM_MODE_CONNECTOR_Component:
1452*4882a593Smuzhiyun tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
1453*4882a593Smuzhiyun video_levels = &component_levels;
1454*4882a593Smuzhiyun if (tv_mode->burst_ena)
1455*4882a593Smuzhiyun color_conversion = &sdtv_csc_yprpb;
1456*4882a593Smuzhiyun else
1457*4882a593Smuzhiyun color_conversion = &hdtv_csc_yprpb;
1458*4882a593Smuzhiyun burst_ena = false;
1459*4882a593Smuzhiyun break;
1460*4882a593Smuzhiyun case DRM_MODE_CONNECTOR_SVIDEO:
1461*4882a593Smuzhiyun tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
1462*4882a593Smuzhiyun video_levels = tv_mode->svideo_levels;
1463*4882a593Smuzhiyun color_conversion = tv_mode->svideo_color;
1464*4882a593Smuzhiyun burst_ena = tv_mode->burst_ena;
1465*4882a593Smuzhiyun break;
1466*4882a593Smuzhiyun }
1467*4882a593Smuzhiyun
1468*4882a593Smuzhiyun tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe);
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun switch (tv_mode->oversample) {
1471*4882a593Smuzhiyun case 8:
1472*4882a593Smuzhiyun tv_ctl |= TV_OVERSAMPLE_8X;
1473*4882a593Smuzhiyun break;
1474*4882a593Smuzhiyun case 4:
1475*4882a593Smuzhiyun tv_ctl |= TV_OVERSAMPLE_4X;
1476*4882a593Smuzhiyun break;
1477*4882a593Smuzhiyun case 2:
1478*4882a593Smuzhiyun tv_ctl |= TV_OVERSAMPLE_2X;
1479*4882a593Smuzhiyun break;
1480*4882a593Smuzhiyun default:
1481*4882a593Smuzhiyun tv_ctl |= TV_OVERSAMPLE_NONE;
1482*4882a593Smuzhiyun break;
1483*4882a593Smuzhiyun }
1484*4882a593Smuzhiyun
1485*4882a593Smuzhiyun if (tv_mode->progressive)
1486*4882a593Smuzhiyun tv_ctl |= TV_PROGRESSIVE;
1487*4882a593Smuzhiyun if (tv_mode->trilevel_sync)
1488*4882a593Smuzhiyun tv_ctl |= TV_TRILEVEL_SYNC;
1489*4882a593Smuzhiyun if (tv_mode->pal_burst)
1490*4882a593Smuzhiyun tv_ctl |= TV_PAL_BURST;
1491*4882a593Smuzhiyun
1492*4882a593Smuzhiyun scctl1 = 0;
1493*4882a593Smuzhiyun if (tv_mode->dda1_inc)
1494*4882a593Smuzhiyun scctl1 |= TV_SC_DDA1_EN;
1495*4882a593Smuzhiyun if (tv_mode->dda2_inc)
1496*4882a593Smuzhiyun scctl1 |= TV_SC_DDA2_EN;
1497*4882a593Smuzhiyun if (tv_mode->dda3_inc)
1498*4882a593Smuzhiyun scctl1 |= TV_SC_DDA3_EN;
1499*4882a593Smuzhiyun scctl1 |= tv_mode->sc_reset;
1500*4882a593Smuzhiyun if (video_levels)
1501*4882a593Smuzhiyun scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
1502*4882a593Smuzhiyun scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1503*4882a593Smuzhiyun
1504*4882a593Smuzhiyun scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1505*4882a593Smuzhiyun tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1506*4882a593Smuzhiyun
1507*4882a593Smuzhiyun scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1508*4882a593Smuzhiyun tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1509*4882a593Smuzhiyun
1510*4882a593Smuzhiyun /* Enable two fixes for the chips that need them. */
1511*4882a593Smuzhiyun if (IS_I915GM(dev_priv))
1512*4882a593Smuzhiyun tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1513*4882a593Smuzhiyun
1514*4882a593Smuzhiyun set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
1515*4882a593Smuzhiyun
1516*4882a593Smuzhiyun intel_de_write(dev_priv, TV_SC_CTL_1, scctl1);
1517*4882a593Smuzhiyun intel_de_write(dev_priv, TV_SC_CTL_2, scctl2);
1518*4882a593Smuzhiyun intel_de_write(dev_priv, TV_SC_CTL_3, scctl3);
1519*4882a593Smuzhiyun
1520*4882a593Smuzhiyun set_color_conversion(dev_priv, color_conversion);
1521*4882a593Smuzhiyun
1522*4882a593Smuzhiyun if (INTEL_GEN(dev_priv) >= 4)
1523*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CLR_KNOBS, 0x00404000);
1524*4882a593Smuzhiyun else
1525*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CLR_KNOBS, 0x00606000);
1526*4882a593Smuzhiyun
1527*4882a593Smuzhiyun if (video_levels)
1528*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CLR_LEVEL,
1529*4882a593Smuzhiyun ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1530*4882a593Smuzhiyun
1531*4882a593Smuzhiyun assert_pipe_disabled(dev_priv, pipe_config->cpu_transcoder);
1532*4882a593Smuzhiyun
1533*4882a593Smuzhiyun /* Filter ctl must be set before TV_WIN_SIZE */
1534*4882a593Smuzhiyun tv_filter_ctl = TV_AUTO_SCALE;
1535*4882a593Smuzhiyun if (tv_conn_state->bypass_vfilter)
1536*4882a593Smuzhiyun tv_filter_ctl |= TV_V_FILTER_BYPASS;
1537*4882a593Smuzhiyun intel_de_write(dev_priv, TV_FILTER_CTL_1, tv_filter_ctl);
1538*4882a593Smuzhiyun
1539*4882a593Smuzhiyun xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1540*4882a593Smuzhiyun ysize = intel_tv_mode_vdisplay(tv_mode);
1541*4882a593Smuzhiyun
1542*4882a593Smuzhiyun xpos = conn_state->tv.margins.left;
1543*4882a593Smuzhiyun ypos = tv_conn_state->margins.top;
1544*4882a593Smuzhiyun xsize -= (conn_state->tv.margins.left +
1545*4882a593Smuzhiyun conn_state->tv.margins.right);
1546*4882a593Smuzhiyun ysize -= (tv_conn_state->margins.top +
1547*4882a593Smuzhiyun tv_conn_state->margins.bottom);
1548*4882a593Smuzhiyun intel_de_write(dev_priv, TV_WIN_POS, (xpos << 16) | ypos);
1549*4882a593Smuzhiyun intel_de_write(dev_priv, TV_WIN_SIZE, (xsize << 16) | ysize);
1550*4882a593Smuzhiyun
1551*4882a593Smuzhiyun j = 0;
1552*4882a593Smuzhiyun for (i = 0; i < 60; i++)
1553*4882a593Smuzhiyun intel_de_write(dev_priv, TV_H_LUMA(i),
1554*4882a593Smuzhiyun tv_mode->filter_table[j++]);
1555*4882a593Smuzhiyun for (i = 0; i < 60; i++)
1556*4882a593Smuzhiyun intel_de_write(dev_priv, TV_H_CHROMA(i),
1557*4882a593Smuzhiyun tv_mode->filter_table[j++]);
1558*4882a593Smuzhiyun for (i = 0; i < 43; i++)
1559*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_LUMA(i),
1560*4882a593Smuzhiyun tv_mode->filter_table[j++]);
1561*4882a593Smuzhiyun for (i = 0; i < 43; i++)
1562*4882a593Smuzhiyun intel_de_write(dev_priv, TV_V_CHROMA(i),
1563*4882a593Smuzhiyun tv_mode->filter_table[j++]);
1564*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC,
1565*4882a593Smuzhiyun intel_de_read(dev_priv, TV_DAC) & TV_DAC_SAVE);
1566*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CTL, tv_ctl);
1567*4882a593Smuzhiyun }
1568*4882a593Smuzhiyun
1569*4882a593Smuzhiyun static int
intel_tv_detect_type(struct intel_tv * intel_tv,struct drm_connector * connector)1570*4882a593Smuzhiyun intel_tv_detect_type(struct intel_tv *intel_tv,
1571*4882a593Smuzhiyun struct drm_connector *connector)
1572*4882a593Smuzhiyun {
1573*4882a593Smuzhiyun struct drm_crtc *crtc = connector->state->crtc;
1574*4882a593Smuzhiyun struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1575*4882a593Smuzhiyun struct drm_device *dev = connector->dev;
1576*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(dev);
1577*4882a593Smuzhiyun u32 tv_ctl, save_tv_ctl;
1578*4882a593Smuzhiyun u32 tv_dac, save_tv_dac;
1579*4882a593Smuzhiyun int type;
1580*4882a593Smuzhiyun
1581*4882a593Smuzhiyun /* Disable TV interrupts around load detect or we'll recurse */
1582*4882a593Smuzhiyun if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1583*4882a593Smuzhiyun spin_lock_irq(&dev_priv->irq_lock);
1584*4882a593Smuzhiyun i915_disable_pipestat(dev_priv, 0,
1585*4882a593Smuzhiyun PIPE_HOTPLUG_INTERRUPT_STATUS |
1586*4882a593Smuzhiyun PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
1587*4882a593Smuzhiyun spin_unlock_irq(&dev_priv->irq_lock);
1588*4882a593Smuzhiyun }
1589*4882a593Smuzhiyun
1590*4882a593Smuzhiyun save_tv_dac = tv_dac = intel_de_read(dev_priv, TV_DAC);
1591*4882a593Smuzhiyun save_tv_ctl = tv_ctl = intel_de_read(dev_priv, TV_CTL);
1592*4882a593Smuzhiyun
1593*4882a593Smuzhiyun /* Poll for TV detection */
1594*4882a593Smuzhiyun tv_ctl &= ~(TV_ENC_ENABLE | TV_ENC_PIPE_SEL_MASK | TV_TEST_MODE_MASK);
1595*4882a593Smuzhiyun tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
1596*4882a593Smuzhiyun tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe);
1597*4882a593Smuzhiyun
1598*4882a593Smuzhiyun tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
1599*4882a593Smuzhiyun tv_dac |= (TVDAC_STATE_CHG_EN |
1600*4882a593Smuzhiyun TVDAC_A_SENSE_CTL |
1601*4882a593Smuzhiyun TVDAC_B_SENSE_CTL |
1602*4882a593Smuzhiyun TVDAC_C_SENSE_CTL |
1603*4882a593Smuzhiyun DAC_CTL_OVERRIDE |
1604*4882a593Smuzhiyun DAC_A_0_7_V |
1605*4882a593Smuzhiyun DAC_B_0_7_V |
1606*4882a593Smuzhiyun DAC_C_0_7_V);
1607*4882a593Smuzhiyun
1608*4882a593Smuzhiyun
1609*4882a593Smuzhiyun /*
1610*4882a593Smuzhiyun * The TV sense state should be cleared to zero on cantiga platform. Otherwise
1611*4882a593Smuzhiyun * the TV is misdetected. This is hardware requirement.
1612*4882a593Smuzhiyun */
1613*4882a593Smuzhiyun if (IS_GM45(dev_priv))
1614*4882a593Smuzhiyun tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
1615*4882a593Smuzhiyun TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
1616*4882a593Smuzhiyun
1617*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CTL, tv_ctl);
1618*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC, tv_dac);
1619*4882a593Smuzhiyun intel_de_posting_read(dev_priv, TV_DAC);
1620*4882a593Smuzhiyun
1621*4882a593Smuzhiyun intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
1622*4882a593Smuzhiyun
1623*4882a593Smuzhiyun type = -1;
1624*4882a593Smuzhiyun tv_dac = intel_de_read(dev_priv, TV_DAC);
1625*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "TV detected: %x, %x\n", tv_ctl, tv_dac);
1626*4882a593Smuzhiyun /*
1627*4882a593Smuzhiyun * A B C
1628*4882a593Smuzhiyun * 0 1 1 Composite
1629*4882a593Smuzhiyun * 1 0 X svideo
1630*4882a593Smuzhiyun * 0 0 0 Component
1631*4882a593Smuzhiyun */
1632*4882a593Smuzhiyun if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1633*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm,
1634*4882a593Smuzhiyun "Detected Composite TV connection\n");
1635*4882a593Smuzhiyun type = DRM_MODE_CONNECTOR_Composite;
1636*4882a593Smuzhiyun } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1637*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm,
1638*4882a593Smuzhiyun "Detected S-Video TV connection\n");
1639*4882a593Smuzhiyun type = DRM_MODE_CONNECTOR_SVIDEO;
1640*4882a593Smuzhiyun } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1641*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm,
1642*4882a593Smuzhiyun "Detected Component TV connection\n");
1643*4882a593Smuzhiyun type = DRM_MODE_CONNECTOR_Component;
1644*4882a593Smuzhiyun } else {
1645*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "Unrecognised TV connection\n");
1646*4882a593Smuzhiyun type = -1;
1647*4882a593Smuzhiyun }
1648*4882a593Smuzhiyun
1649*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1650*4882a593Smuzhiyun intel_de_write(dev_priv, TV_CTL, save_tv_ctl);
1651*4882a593Smuzhiyun intel_de_posting_read(dev_priv, TV_CTL);
1652*4882a593Smuzhiyun
1653*4882a593Smuzhiyun /* For unknown reasons the hw barfs if we don't do this vblank wait. */
1654*4882a593Smuzhiyun intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
1655*4882a593Smuzhiyun
1656*4882a593Smuzhiyun /* Restore interrupt config */
1657*4882a593Smuzhiyun if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1658*4882a593Smuzhiyun spin_lock_irq(&dev_priv->irq_lock);
1659*4882a593Smuzhiyun i915_enable_pipestat(dev_priv, 0,
1660*4882a593Smuzhiyun PIPE_HOTPLUG_INTERRUPT_STATUS |
1661*4882a593Smuzhiyun PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
1662*4882a593Smuzhiyun spin_unlock_irq(&dev_priv->irq_lock);
1663*4882a593Smuzhiyun }
1664*4882a593Smuzhiyun
1665*4882a593Smuzhiyun return type;
1666*4882a593Smuzhiyun }
1667*4882a593Smuzhiyun
1668*4882a593Smuzhiyun /*
1669*4882a593Smuzhiyun * Here we set accurate tv format according to connector type
1670*4882a593Smuzhiyun * i.e Component TV should not be assigned by NTSC or PAL
1671*4882a593Smuzhiyun */
intel_tv_find_better_format(struct drm_connector * connector)1672*4882a593Smuzhiyun static void intel_tv_find_better_format(struct drm_connector *connector)
1673*4882a593Smuzhiyun {
1674*4882a593Smuzhiyun struct intel_tv *intel_tv = intel_attached_tv(to_intel_connector(connector));
1675*4882a593Smuzhiyun const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1676*4882a593Smuzhiyun int i;
1677*4882a593Smuzhiyun
1678*4882a593Smuzhiyun /* Component supports everything so we can keep the current mode */
1679*4882a593Smuzhiyun if (intel_tv->type == DRM_MODE_CONNECTOR_Component)
1680*4882a593Smuzhiyun return;
1681*4882a593Smuzhiyun
1682*4882a593Smuzhiyun /* If the current mode is fine don't change it */
1683*4882a593Smuzhiyun if (!tv_mode->component_only)
1684*4882a593Smuzhiyun return;
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
1687*4882a593Smuzhiyun tv_mode = &tv_modes[i];
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun if (!tv_mode->component_only)
1690*4882a593Smuzhiyun break;
1691*4882a593Smuzhiyun }
1692*4882a593Smuzhiyun
1693*4882a593Smuzhiyun connector->state->tv.mode = i;
1694*4882a593Smuzhiyun }
1695*4882a593Smuzhiyun
1696*4882a593Smuzhiyun static int
intel_tv_detect(struct drm_connector * connector,struct drm_modeset_acquire_ctx * ctx,bool force)1697*4882a593Smuzhiyun intel_tv_detect(struct drm_connector *connector,
1698*4882a593Smuzhiyun struct drm_modeset_acquire_ctx *ctx,
1699*4882a593Smuzhiyun bool force)
1700*4882a593Smuzhiyun {
1701*4882a593Smuzhiyun struct drm_i915_private *i915 = to_i915(connector->dev);
1702*4882a593Smuzhiyun struct intel_tv *intel_tv = intel_attached_tv(to_intel_connector(connector));
1703*4882a593Smuzhiyun enum drm_connector_status status;
1704*4882a593Smuzhiyun int type;
1705*4882a593Smuzhiyun
1706*4882a593Smuzhiyun drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] force=%d\n",
1707*4882a593Smuzhiyun connector->base.id, connector->name, force);
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun if (!INTEL_DISPLAY_ENABLED(i915))
1710*4882a593Smuzhiyun return connector_status_disconnected;
1711*4882a593Smuzhiyun
1712*4882a593Smuzhiyun if (force) {
1713*4882a593Smuzhiyun struct intel_load_detect_pipe tmp;
1714*4882a593Smuzhiyun int ret;
1715*4882a593Smuzhiyun
1716*4882a593Smuzhiyun ret = intel_get_load_detect_pipe(connector, &tmp, ctx);
1717*4882a593Smuzhiyun if (ret < 0)
1718*4882a593Smuzhiyun return ret;
1719*4882a593Smuzhiyun
1720*4882a593Smuzhiyun if (ret > 0) {
1721*4882a593Smuzhiyun type = intel_tv_detect_type(intel_tv, connector);
1722*4882a593Smuzhiyun intel_release_load_detect_pipe(connector, &tmp, ctx);
1723*4882a593Smuzhiyun status = type < 0 ?
1724*4882a593Smuzhiyun connector_status_disconnected :
1725*4882a593Smuzhiyun connector_status_connected;
1726*4882a593Smuzhiyun } else
1727*4882a593Smuzhiyun status = connector_status_unknown;
1728*4882a593Smuzhiyun
1729*4882a593Smuzhiyun if (status == connector_status_connected) {
1730*4882a593Smuzhiyun intel_tv->type = type;
1731*4882a593Smuzhiyun intel_tv_find_better_format(connector);
1732*4882a593Smuzhiyun }
1733*4882a593Smuzhiyun
1734*4882a593Smuzhiyun return status;
1735*4882a593Smuzhiyun } else
1736*4882a593Smuzhiyun return connector->status;
1737*4882a593Smuzhiyun }
1738*4882a593Smuzhiyun
1739*4882a593Smuzhiyun static const struct input_res {
1740*4882a593Smuzhiyun u16 w, h;
1741*4882a593Smuzhiyun } input_res_table[] = {
1742*4882a593Smuzhiyun { 640, 480 },
1743*4882a593Smuzhiyun { 800, 600 },
1744*4882a593Smuzhiyun { 1024, 768 },
1745*4882a593Smuzhiyun { 1280, 1024 },
1746*4882a593Smuzhiyun { 848, 480 },
1747*4882a593Smuzhiyun { 1280, 720 },
1748*4882a593Smuzhiyun { 1920, 1080 },
1749*4882a593Smuzhiyun };
1750*4882a593Smuzhiyun
1751*4882a593Smuzhiyun /* Choose preferred mode according to line number of TV format */
1752*4882a593Smuzhiyun static bool
intel_tv_is_preferred_mode(const struct drm_display_mode * mode,const struct tv_mode * tv_mode)1753*4882a593Smuzhiyun intel_tv_is_preferred_mode(const struct drm_display_mode *mode,
1754*4882a593Smuzhiyun const struct tv_mode *tv_mode)
1755*4882a593Smuzhiyun {
1756*4882a593Smuzhiyun int vdisplay = intel_tv_mode_vdisplay(tv_mode);
1757*4882a593Smuzhiyun
1758*4882a593Smuzhiyun /* prefer 480 line modes for all SD TV modes */
1759*4882a593Smuzhiyun if (vdisplay <= 576)
1760*4882a593Smuzhiyun vdisplay = 480;
1761*4882a593Smuzhiyun
1762*4882a593Smuzhiyun return vdisplay == mode->vdisplay;
1763*4882a593Smuzhiyun }
1764*4882a593Smuzhiyun
1765*4882a593Smuzhiyun static void
intel_tv_set_mode_type(struct drm_display_mode * mode,const struct tv_mode * tv_mode)1766*4882a593Smuzhiyun intel_tv_set_mode_type(struct drm_display_mode *mode,
1767*4882a593Smuzhiyun const struct tv_mode *tv_mode)
1768*4882a593Smuzhiyun {
1769*4882a593Smuzhiyun mode->type = DRM_MODE_TYPE_DRIVER;
1770*4882a593Smuzhiyun
1771*4882a593Smuzhiyun if (intel_tv_is_preferred_mode(mode, tv_mode))
1772*4882a593Smuzhiyun mode->type |= DRM_MODE_TYPE_PREFERRED;
1773*4882a593Smuzhiyun }
1774*4882a593Smuzhiyun
1775*4882a593Smuzhiyun static int
intel_tv_get_modes(struct drm_connector * connector)1776*4882a593Smuzhiyun intel_tv_get_modes(struct drm_connector *connector)
1777*4882a593Smuzhiyun {
1778*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(connector->dev);
1779*4882a593Smuzhiyun const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
1780*4882a593Smuzhiyun int i, count = 0;
1781*4882a593Smuzhiyun
1782*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(input_res_table); i++) {
1783*4882a593Smuzhiyun const struct input_res *input = &input_res_table[i];
1784*4882a593Smuzhiyun struct drm_display_mode *mode;
1785*4882a593Smuzhiyun
1786*4882a593Smuzhiyun if (input->w > 1024 &&
1787*4882a593Smuzhiyun !tv_mode->progressive &&
1788*4882a593Smuzhiyun !tv_mode->component_only)
1789*4882a593Smuzhiyun continue;
1790*4882a593Smuzhiyun
1791*4882a593Smuzhiyun /* no vertical scaling with wide sources on gen3 */
1792*4882a593Smuzhiyun if (IS_GEN(dev_priv, 3) && input->w > 1024 &&
1793*4882a593Smuzhiyun input->h > intel_tv_mode_vdisplay(tv_mode))
1794*4882a593Smuzhiyun continue;
1795*4882a593Smuzhiyun
1796*4882a593Smuzhiyun mode = drm_mode_create(connector->dev);
1797*4882a593Smuzhiyun if (!mode)
1798*4882a593Smuzhiyun continue;
1799*4882a593Smuzhiyun
1800*4882a593Smuzhiyun /*
1801*4882a593Smuzhiyun * We take the TV mode and scale it to look
1802*4882a593Smuzhiyun * like it had the expected h/vdisplay. This
1803*4882a593Smuzhiyun * provides the most information to userspace
1804*4882a593Smuzhiyun * about the actual timings of the mode. We
1805*4882a593Smuzhiyun * do ignore the margins though.
1806*4882a593Smuzhiyun */
1807*4882a593Smuzhiyun intel_tv_mode_to_mode(mode, tv_mode);
1808*4882a593Smuzhiyun if (count == 0) {
1809*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "TV mode:\n");
1810*4882a593Smuzhiyun drm_mode_debug_printmodeline(mode);
1811*4882a593Smuzhiyun }
1812*4882a593Smuzhiyun intel_tv_scale_mode_horiz(mode, input->w, 0, 0);
1813*4882a593Smuzhiyun intel_tv_scale_mode_vert(mode, input->h, 0, 0);
1814*4882a593Smuzhiyun intel_tv_set_mode_type(mode, tv_mode);
1815*4882a593Smuzhiyun
1816*4882a593Smuzhiyun drm_mode_set_name(mode);
1817*4882a593Smuzhiyun
1818*4882a593Smuzhiyun drm_mode_probed_add(connector, mode);
1819*4882a593Smuzhiyun count++;
1820*4882a593Smuzhiyun }
1821*4882a593Smuzhiyun
1822*4882a593Smuzhiyun return count;
1823*4882a593Smuzhiyun }
1824*4882a593Smuzhiyun
1825*4882a593Smuzhiyun static const struct drm_connector_funcs intel_tv_connector_funcs = {
1826*4882a593Smuzhiyun .late_register = intel_connector_register,
1827*4882a593Smuzhiyun .early_unregister = intel_connector_unregister,
1828*4882a593Smuzhiyun .destroy = intel_connector_destroy,
1829*4882a593Smuzhiyun .fill_modes = drm_helper_probe_single_connector_modes,
1830*4882a593Smuzhiyun .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
1831*4882a593Smuzhiyun .atomic_duplicate_state = intel_tv_connector_duplicate_state,
1832*4882a593Smuzhiyun };
1833*4882a593Smuzhiyun
intel_tv_atomic_check(struct drm_connector * connector,struct drm_atomic_state * state)1834*4882a593Smuzhiyun static int intel_tv_atomic_check(struct drm_connector *connector,
1835*4882a593Smuzhiyun struct drm_atomic_state *state)
1836*4882a593Smuzhiyun {
1837*4882a593Smuzhiyun struct drm_connector_state *new_state;
1838*4882a593Smuzhiyun struct drm_crtc_state *new_crtc_state;
1839*4882a593Smuzhiyun struct drm_connector_state *old_state;
1840*4882a593Smuzhiyun
1841*4882a593Smuzhiyun new_state = drm_atomic_get_new_connector_state(state, connector);
1842*4882a593Smuzhiyun if (!new_state->crtc)
1843*4882a593Smuzhiyun return 0;
1844*4882a593Smuzhiyun
1845*4882a593Smuzhiyun old_state = drm_atomic_get_old_connector_state(state, connector);
1846*4882a593Smuzhiyun new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
1847*4882a593Smuzhiyun
1848*4882a593Smuzhiyun if (old_state->tv.mode != new_state->tv.mode ||
1849*4882a593Smuzhiyun old_state->tv.margins.left != new_state->tv.margins.left ||
1850*4882a593Smuzhiyun old_state->tv.margins.right != new_state->tv.margins.right ||
1851*4882a593Smuzhiyun old_state->tv.margins.top != new_state->tv.margins.top ||
1852*4882a593Smuzhiyun old_state->tv.margins.bottom != new_state->tv.margins.bottom) {
1853*4882a593Smuzhiyun /* Force a modeset. */
1854*4882a593Smuzhiyun
1855*4882a593Smuzhiyun new_crtc_state->connectors_changed = true;
1856*4882a593Smuzhiyun }
1857*4882a593Smuzhiyun
1858*4882a593Smuzhiyun return 0;
1859*4882a593Smuzhiyun }
1860*4882a593Smuzhiyun
1861*4882a593Smuzhiyun static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1862*4882a593Smuzhiyun .detect_ctx = intel_tv_detect,
1863*4882a593Smuzhiyun .mode_valid = intel_tv_mode_valid,
1864*4882a593Smuzhiyun .get_modes = intel_tv_get_modes,
1865*4882a593Smuzhiyun .atomic_check = intel_tv_atomic_check,
1866*4882a593Smuzhiyun };
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun static const struct drm_encoder_funcs intel_tv_enc_funcs = {
1869*4882a593Smuzhiyun .destroy = intel_encoder_destroy,
1870*4882a593Smuzhiyun };
1871*4882a593Smuzhiyun
1872*4882a593Smuzhiyun void
intel_tv_init(struct drm_i915_private * dev_priv)1873*4882a593Smuzhiyun intel_tv_init(struct drm_i915_private *dev_priv)
1874*4882a593Smuzhiyun {
1875*4882a593Smuzhiyun struct drm_device *dev = &dev_priv->drm;
1876*4882a593Smuzhiyun struct drm_connector *connector;
1877*4882a593Smuzhiyun struct intel_tv *intel_tv;
1878*4882a593Smuzhiyun struct intel_encoder *intel_encoder;
1879*4882a593Smuzhiyun struct intel_connector *intel_connector;
1880*4882a593Smuzhiyun u32 tv_dac_on, tv_dac_off, save_tv_dac;
1881*4882a593Smuzhiyun const char *tv_format_names[ARRAY_SIZE(tv_modes)];
1882*4882a593Smuzhiyun int i, initial_mode = 0;
1883*4882a593Smuzhiyun struct drm_connector_state *state;
1884*4882a593Smuzhiyun
1885*4882a593Smuzhiyun if ((intel_de_read(dev_priv, TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1886*4882a593Smuzhiyun return;
1887*4882a593Smuzhiyun
1888*4882a593Smuzhiyun if (!intel_bios_is_tv_present(dev_priv)) {
1889*4882a593Smuzhiyun drm_dbg_kms(&dev_priv->drm, "Integrated TV is not present.\n");
1890*4882a593Smuzhiyun return;
1891*4882a593Smuzhiyun }
1892*4882a593Smuzhiyun
1893*4882a593Smuzhiyun /*
1894*4882a593Smuzhiyun * Sanity check the TV output by checking to see if the
1895*4882a593Smuzhiyun * DAC register holds a value
1896*4882a593Smuzhiyun */
1897*4882a593Smuzhiyun save_tv_dac = intel_de_read(dev_priv, TV_DAC);
1898*4882a593Smuzhiyun
1899*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1900*4882a593Smuzhiyun tv_dac_on = intel_de_read(dev_priv, TV_DAC);
1901*4882a593Smuzhiyun
1902*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1903*4882a593Smuzhiyun tv_dac_off = intel_de_read(dev_priv, TV_DAC);
1904*4882a593Smuzhiyun
1905*4882a593Smuzhiyun intel_de_write(dev_priv, TV_DAC, save_tv_dac);
1906*4882a593Smuzhiyun
1907*4882a593Smuzhiyun /*
1908*4882a593Smuzhiyun * If the register does not hold the state change enable
1909*4882a593Smuzhiyun * bit, (either as a 0 or a 1), assume it doesn't really
1910*4882a593Smuzhiyun * exist
1911*4882a593Smuzhiyun */
1912*4882a593Smuzhiyun if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1913*4882a593Smuzhiyun (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1914*4882a593Smuzhiyun return;
1915*4882a593Smuzhiyun
1916*4882a593Smuzhiyun intel_tv = kzalloc(sizeof(*intel_tv), GFP_KERNEL);
1917*4882a593Smuzhiyun if (!intel_tv) {
1918*4882a593Smuzhiyun return;
1919*4882a593Smuzhiyun }
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun intel_connector = intel_connector_alloc();
1922*4882a593Smuzhiyun if (!intel_connector) {
1923*4882a593Smuzhiyun kfree(intel_tv);
1924*4882a593Smuzhiyun return;
1925*4882a593Smuzhiyun }
1926*4882a593Smuzhiyun
1927*4882a593Smuzhiyun intel_encoder = &intel_tv->base;
1928*4882a593Smuzhiyun connector = &intel_connector->base;
1929*4882a593Smuzhiyun state = connector->state;
1930*4882a593Smuzhiyun
1931*4882a593Smuzhiyun /*
1932*4882a593Smuzhiyun * The documentation, for the older chipsets at least, recommend
1933*4882a593Smuzhiyun * using a polling method rather than hotplug detection for TVs.
1934*4882a593Smuzhiyun * This is because in order to perform the hotplug detection, the PLLs
1935*4882a593Smuzhiyun * for the TV must be kept alive increasing power drain and starving
1936*4882a593Smuzhiyun * bandwidth from other encoders. Notably for instance, it causes
1937*4882a593Smuzhiyun * pipe underruns on Crestline when this encoder is supposedly idle.
1938*4882a593Smuzhiyun *
1939*4882a593Smuzhiyun * More recent chipsets favour HDMI rather than integrated S-Video.
1940*4882a593Smuzhiyun */
1941*4882a593Smuzhiyun intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
1942*4882a593Smuzhiyun
1943*4882a593Smuzhiyun drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1944*4882a593Smuzhiyun DRM_MODE_CONNECTOR_SVIDEO);
1945*4882a593Smuzhiyun
1946*4882a593Smuzhiyun drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
1947*4882a593Smuzhiyun DRM_MODE_ENCODER_TVDAC, "TV");
1948*4882a593Smuzhiyun
1949*4882a593Smuzhiyun intel_encoder->compute_config = intel_tv_compute_config;
1950*4882a593Smuzhiyun intel_encoder->get_config = intel_tv_get_config;
1951*4882a593Smuzhiyun intel_encoder->pre_enable = intel_tv_pre_enable;
1952*4882a593Smuzhiyun intel_encoder->enable = intel_enable_tv;
1953*4882a593Smuzhiyun intel_encoder->disable = intel_disable_tv;
1954*4882a593Smuzhiyun intel_encoder->get_hw_state = intel_tv_get_hw_state;
1955*4882a593Smuzhiyun intel_connector->get_hw_state = intel_connector_get_hw_state;
1956*4882a593Smuzhiyun
1957*4882a593Smuzhiyun intel_connector_attach_encoder(intel_connector, intel_encoder);
1958*4882a593Smuzhiyun
1959*4882a593Smuzhiyun intel_encoder->type = INTEL_OUTPUT_TVOUT;
1960*4882a593Smuzhiyun intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
1961*4882a593Smuzhiyun intel_encoder->port = PORT_NONE;
1962*4882a593Smuzhiyun intel_encoder->pipe_mask = ~0;
1963*4882a593Smuzhiyun intel_encoder->cloneable = 0;
1964*4882a593Smuzhiyun intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
1965*4882a593Smuzhiyun
1966*4882a593Smuzhiyun /* BIOS margin values */
1967*4882a593Smuzhiyun state->tv.margins.left = 54;
1968*4882a593Smuzhiyun state->tv.margins.top = 36;
1969*4882a593Smuzhiyun state->tv.margins.right = 46;
1970*4882a593Smuzhiyun state->tv.margins.bottom = 37;
1971*4882a593Smuzhiyun
1972*4882a593Smuzhiyun state->tv.mode = initial_mode;
1973*4882a593Smuzhiyun
1974*4882a593Smuzhiyun drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1975*4882a593Smuzhiyun connector->interlace_allowed = false;
1976*4882a593Smuzhiyun connector->doublescan_allowed = false;
1977*4882a593Smuzhiyun
1978*4882a593Smuzhiyun /* Create TV properties then attach current values */
1979*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
1980*4882a593Smuzhiyun /* 1080p50/1080p60 not supported on gen3 */
1981*4882a593Smuzhiyun if (IS_GEN(dev_priv, 3) &&
1982*4882a593Smuzhiyun tv_modes[i].oversample == 1)
1983*4882a593Smuzhiyun break;
1984*4882a593Smuzhiyun
1985*4882a593Smuzhiyun tv_format_names[i] = tv_modes[i].name;
1986*4882a593Smuzhiyun }
1987*4882a593Smuzhiyun drm_mode_create_tv_properties(dev, i, tv_format_names);
1988*4882a593Smuzhiyun
1989*4882a593Smuzhiyun drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
1990*4882a593Smuzhiyun state->tv.mode);
1991*4882a593Smuzhiyun drm_object_attach_property(&connector->base,
1992*4882a593Smuzhiyun dev->mode_config.tv_left_margin_property,
1993*4882a593Smuzhiyun state->tv.margins.left);
1994*4882a593Smuzhiyun drm_object_attach_property(&connector->base,
1995*4882a593Smuzhiyun dev->mode_config.tv_top_margin_property,
1996*4882a593Smuzhiyun state->tv.margins.top);
1997*4882a593Smuzhiyun drm_object_attach_property(&connector->base,
1998*4882a593Smuzhiyun dev->mode_config.tv_right_margin_property,
1999*4882a593Smuzhiyun state->tv.margins.right);
2000*4882a593Smuzhiyun drm_object_attach_property(&connector->base,
2001*4882a593Smuzhiyun dev->mode_config.tv_bottom_margin_property,
2002*4882a593Smuzhiyun state->tv.margins.bottom);
2003*4882a593Smuzhiyun }
2004