blob: 003ef9e92804f2c7e9c13e7722475802f6f39b28 [file] [log] [blame]
Jesse Barnes79e53942008-11-07 14:24:08 -08001/*
2 * Copyright © 2006-2008 Intel Corporation
3 * Jesse Barnes <jesse.barnes@intel.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Eric Anholt <eric@anholt.net>
26 *
27 */
28
29/** @file
30 * Integrated TV-out support for the 915GM and 945GM.
31 */
32
33#include "drmP.h"
34#include "drm.h"
35#include "drm_crtc.h"
36#include "drm_edid.h"
37#include "intel_drv.h"
38#include "i915_drm.h"
39#include "i915_drv.h"
40
41enum tv_margin {
42 TV_MARGIN_LEFT, TV_MARGIN_TOP,
43 TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
44};
45
46/** Private structure for the integrated TV support */
Chris Wilsonea5b2132010-08-04 13:50:23 +010047struct intel_tv {
48 struct intel_encoder base;
49
Jesse Barnes79e53942008-11-07 14:24:08 -080050 int type;
Chris Wilson763a4a02010-09-05 00:52:34 +010051 const char *tv_format;
Jesse Barnes79e53942008-11-07 14:24:08 -080052 int margin[4];
53 u32 save_TV_H_CTL_1;
54 u32 save_TV_H_CTL_2;
55 u32 save_TV_H_CTL_3;
56 u32 save_TV_V_CTL_1;
57 u32 save_TV_V_CTL_2;
58 u32 save_TV_V_CTL_3;
59 u32 save_TV_V_CTL_4;
60 u32 save_TV_V_CTL_5;
61 u32 save_TV_V_CTL_6;
62 u32 save_TV_V_CTL_7;
63 u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
64
65 u32 save_TV_CSC_Y;
66 u32 save_TV_CSC_Y2;
67 u32 save_TV_CSC_U;
68 u32 save_TV_CSC_U2;
69 u32 save_TV_CSC_V;
70 u32 save_TV_CSC_V2;
71 u32 save_TV_CLR_KNOBS;
72 u32 save_TV_CLR_LEVEL;
73 u32 save_TV_WIN_POS;
74 u32 save_TV_WIN_SIZE;
75 u32 save_TV_FILTER_CTL_1;
76 u32 save_TV_FILTER_CTL_2;
77 u32 save_TV_FILTER_CTL_3;
78
79 u32 save_TV_H_LUMA[60];
80 u32 save_TV_H_CHROMA[60];
81 u32 save_TV_V_LUMA[43];
82 u32 save_TV_V_CHROMA[43];
83
84 u32 save_TV_DAC;
85 u32 save_TV_CTL;
86};
87
88struct video_levels {
89 int blank, black, burst;
90};
91
92struct color_conversion {
93 u16 ry, gy, by, ay;
94 u16 ru, gu, bu, au;
95 u16 rv, gv, bv, av;
96};
97
98static const u32 filter_table[] = {
99 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
100 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
101 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
102 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
103 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
104 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
105 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
106 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
107 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
108 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
109 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
110 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
111 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
112 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
113 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
114 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
115 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
116 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
117 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
118 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
119 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
120 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
121 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
122 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
123 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
124 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
125 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
126 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
127 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
128 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
129 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
130 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
131 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
132 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
133 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
134 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
135 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
136 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
137 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
138 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
139 0x28003100, 0x28002F00, 0x00003100, 0x36403000,
140 0x2D002CC0, 0x30003640, 0x2D0036C0,
141 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
142 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
143 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
144 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
145 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
146 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
147 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
148 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
149 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
150 0x28003100, 0x28002F00, 0x00003100,
151};
152
153/*
154 * Color conversion values have 3 separate fixed point formats:
155 *
156 * 10 bit fields (ay, au)
157 * 1.9 fixed point (b.bbbbbbbbb)
158 * 11 bit fields (ry, by, ru, gu, gv)
159 * exp.mantissa (ee.mmmmmmmmm)
160 * ee = 00 = 10^-1 (0.mmmmmmmmm)
161 * ee = 01 = 10^-2 (0.0mmmmmmmmm)
162 * ee = 10 = 10^-3 (0.00mmmmmmmmm)
163 * ee = 11 = 10^-4 (0.000mmmmmmmmm)
164 * 12 bit fields (gy, rv, bu)
165 * exp.mantissa (eee.mmmmmmmmm)
166 * eee = 000 = 10^-1 (0.mmmmmmmmm)
167 * eee = 001 = 10^-2 (0.0mmmmmmmmm)
168 * eee = 010 = 10^-3 (0.00mmmmmmmmm)
169 * eee = 011 = 10^-4 (0.000mmmmmmmmm)
170 * eee = 100 = reserved
171 * eee = 101 = reserved
172 * eee = 110 = reserved
173 * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
174 *
175 * Saturation and contrast are 8 bits, with their own representation:
176 * 8 bit field (saturation, contrast)
177 * exp.mantissa (ee.mmmmmm)
178 * ee = 00 = 10^-1 (0.mmmmmm)
179 * ee = 01 = 10^0 (m.mmmmm)
180 * ee = 10 = 10^1 (mm.mmmm)
181 * ee = 11 = 10^2 (mmm.mmm)
182 *
183 * Simple conversion function:
184 *
185 * static u32
186 * float_to_csc_11(float f)
187 * {
188 * u32 exp;
189 * u32 mant;
190 * u32 ret;
191 *
192 * if (f < 0)
193 * f = -f;
194 *
195 * if (f >= 1) {
196 * exp = 0x7;
Akshay Joshi0206e352011-08-16 15:34:10 -0400197 * mant = 1 << 8;
Jesse Barnes79e53942008-11-07 14:24:08 -0800198 * } else {
199 * for (exp = 0; exp < 3 && f < 0.5; exp++)
Akshay Joshi0206e352011-08-16 15:34:10 -0400200 * f *= 2.0;
Jesse Barnes79e53942008-11-07 14:24:08 -0800201 * mant = (f * (1 << 9) + 0.5);
202 * if (mant >= (1 << 9))
203 * mant = (1 << 9) - 1;
204 * }
205 * ret = (exp << 9) | mant;
206 * return ret;
207 * }
208 */
209
210/*
211 * Behold, magic numbers! If we plant them they might grow a big
212 * s-video cable to the sky... or something.
213 *
214 * Pre-converted to appropriate hex value.
215 */
216
217/*
218 * PAL & NTSC values for composite & s-video connections
219 */
220static const struct color_conversion ntsc_m_csc_composite = {
221 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
Zhenyu Wangba010792009-03-04 20:23:02 +0800222 .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
223 .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800224};
225
226static const struct video_levels ntsc_m_levels_composite = {
227 .blank = 225, .black = 267, .burst = 113,
228};
229
230static const struct color_conversion ntsc_m_csc_svideo = {
Zhenyu Wangba010792009-03-04 20:23:02 +0800231 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
232 .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
233 .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800234};
235
236static const struct video_levels ntsc_m_levels_svideo = {
237 .blank = 266, .black = 316, .burst = 133,
238};
239
240static const struct color_conversion ntsc_j_csc_composite = {
241 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
Zhenyu Wangba010792009-03-04 20:23:02 +0800242 .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
243 .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800244};
245
246static const struct video_levels ntsc_j_levels_composite = {
247 .blank = 225, .black = 225, .burst = 113,
248};
249
250static const struct color_conversion ntsc_j_csc_svideo = {
251 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
Zhenyu Wangba010792009-03-04 20:23:02 +0800252 .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
253 .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800254};
255
256static const struct video_levels ntsc_j_levels_svideo = {
257 .blank = 266, .black = 266, .burst = 133,
258};
259
260static const struct color_conversion pal_csc_composite = {
261 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
Zhenyu Wangba010792009-03-04 20:23:02 +0800262 .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
263 .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800264};
265
266static const struct video_levels pal_levels_composite = {
267 .blank = 237, .black = 237, .burst = 118,
268};
269
270static const struct color_conversion pal_csc_svideo = {
271 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
Zhenyu Wangba010792009-03-04 20:23:02 +0800272 .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
273 .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800274};
275
276static const struct video_levels pal_levels_svideo = {
277 .blank = 280, .black = 280, .burst = 139,
278};
279
280static const struct color_conversion pal_m_csc_composite = {
281 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
Zhenyu Wangba010792009-03-04 20:23:02 +0800282 .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
283 .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800284};
285
286static const struct video_levels pal_m_levels_composite = {
287 .blank = 225, .black = 267, .burst = 113,
288};
289
290static const struct color_conversion pal_m_csc_svideo = {
Zhenyu Wangba010792009-03-04 20:23:02 +0800291 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
292 .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
293 .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800294};
295
296static const struct video_levels pal_m_levels_svideo = {
297 .blank = 266, .black = 316, .burst = 133,
298};
299
300static const struct color_conversion pal_n_csc_composite = {
301 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
Zhenyu Wangba010792009-03-04 20:23:02 +0800302 .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
303 .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800304};
305
306static const struct video_levels pal_n_levels_composite = {
307 .blank = 225, .black = 267, .burst = 118,
308};
309
310static const struct color_conversion pal_n_csc_svideo = {
Zhenyu Wangba010792009-03-04 20:23:02 +0800311 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
312 .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
313 .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800314};
315
316static const struct video_levels pal_n_levels_svideo = {
317 .blank = 266, .black = 316, .burst = 139,
318};
319
320/*
321 * Component connections
322 */
323static const struct color_conversion sdtv_csc_yprpb = {
Zhenyu Wangba010792009-03-04 20:23:02 +0800324 .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
325 .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
326 .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800327};
328
329static const struct color_conversion sdtv_csc_rgb = {
330 .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
331 .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
332 .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
333};
334
335static const struct color_conversion hdtv_csc_yprpb = {
Zhenyu Wangba010792009-03-04 20:23:02 +0800336 .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
337 .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
338 .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
Jesse Barnes79e53942008-11-07 14:24:08 -0800339};
340
341static const struct color_conversion hdtv_csc_rgb = {
342 .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
343 .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
344 .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
345};
346
347static const struct video_levels component_levels = {
348 .blank = 279, .black = 279, .burst = 0,
349};
350
351
352struct tv_mode {
Chris Wilson763a4a02010-09-05 00:52:34 +0100353 const char *name;
Jesse Barnes79e53942008-11-07 14:24:08 -0800354 int clock;
355 int refresh; /* in millihertz (for precision) */
356 u32 oversample;
357 int hsync_end, hblank_start, hblank_end, htotal;
358 bool progressive, trilevel_sync, component_only;
359 int vsync_start_f1, vsync_start_f2, vsync_len;
360 bool veq_ena;
361 int veq_start_f1, veq_start_f2, veq_len;
362 int vi_end_f1, vi_end_f2, nbr_end;
363 bool burst_ena;
364 int hburst_start, hburst_len;
365 int vburst_start_f1, vburst_end_f1;
366 int vburst_start_f2, vburst_end_f2;
367 int vburst_start_f3, vburst_end_f3;
368 int vburst_start_f4, vburst_end_f4;
369 /*
370 * subcarrier programming
371 */
372 int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
373 u32 sc_reset;
374 bool pal_burst;
375 /*
376 * blank/black levels
377 */
378 const struct video_levels *composite_levels, *svideo_levels;
379 const struct color_conversion *composite_color, *svideo_color;
380 const u32 *filter_table;
381 int max_srcw;
382};
383
384
385/*
386 * Sub carrier DDA
387 *
388 * I think this works as follows:
389 *
390 * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
391 *
392 * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
393 *
394 * So,
395 * dda1_ideal = subcarrier/pixel * 4096
396 * dda1_inc = floor (dda1_ideal)
397 * dda2 = dda1_ideal - dda1_inc
398 *
399 * then pick a ratio for dda2 that gives the closest approximation. If
400 * you can't get close enough, you can play with dda3 as well. This
401 * seems likely to happen when dda2 is small as the jumps would be larger
402 *
403 * To invert this,
404 *
405 * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
406 *
407 * The constants below were all computed using a 107.520MHz clock
408 */
409
410/**
411 * Register programming values for TV modes.
412 *
413 * These values account for -1s required.
414 */
415
Tobias Klauser005568b2009-02-09 22:02:42 +0100416static const struct tv_mode tv_modes[] = {
Jesse Barnes79e53942008-11-07 14:24:08 -0800417 {
418 .name = "NTSC-M",
Zhenyu Wangba010792009-03-04 20:23:02 +0800419 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200420 .refresh = 59940,
Jesse Barnes79e53942008-11-07 14:24:08 -0800421 .oversample = TV_OVERSAMPLE_8X,
422 .component_only = 0,
423 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
424
425 .hsync_end = 64, .hblank_end = 124,
426 .hblank_start = 836, .htotal = 857,
427
428 .progressive = false, .trilevel_sync = false,
429
430 .vsync_start_f1 = 6, .vsync_start_f2 = 7,
431 .vsync_len = 6,
432
Akshay Joshi0206e352011-08-16 15:34:10 -0400433 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800434 .veq_start_f2 = 1, .veq_len = 18,
435
436 .vi_end_f1 = 20, .vi_end_f2 = 21,
437 .nbr_end = 240,
438
439 .burst_ena = true,
440 .hburst_start = 72, .hburst_len = 34,
441 .vburst_start_f1 = 9, .vburst_end_f1 = 240,
442 .vburst_start_f2 = 10, .vburst_end_f2 = 240,
443 .vburst_start_f3 = 9, .vburst_end_f3 = 240,
444 .vburst_start_f4 = 10, .vburst_end_f4 = 240,
445
446 /* desired 3.5800000 actual 3.5800000 clock 107.52 */
Zhenyu Wangba010792009-03-04 20:23:02 +0800447 .dda1_inc = 135,
448 .dda2_inc = 20800, .dda2_size = 27456,
Jesse Barnes79e53942008-11-07 14:24:08 -0800449 .dda3_inc = 0, .dda3_size = 0,
450 .sc_reset = TV_SC_RESET_EVERY_4,
451 .pal_burst = false,
452
453 .composite_levels = &ntsc_m_levels_composite,
454 .composite_color = &ntsc_m_csc_composite,
455 .svideo_levels = &ntsc_m_levels_svideo,
456 .svideo_color = &ntsc_m_csc_svideo,
457
458 .filter_table = filter_table,
459 },
460 {
461 .name = "NTSC-443",
Zhenyu Wangba010792009-03-04 20:23:02 +0800462 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200463 .refresh = 59940,
Jesse Barnes79e53942008-11-07 14:24:08 -0800464 .oversample = TV_OVERSAMPLE_8X,
465 .component_only = 0,
466 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
467 .hsync_end = 64, .hblank_end = 124,
468 .hblank_start = 836, .htotal = 857,
469
470 .progressive = false, .trilevel_sync = false,
471
472 .vsync_start_f1 = 6, .vsync_start_f2 = 7,
473 .vsync_len = 6,
474
Akshay Joshi0206e352011-08-16 15:34:10 -0400475 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800476 .veq_start_f2 = 1, .veq_len = 18,
477
478 .vi_end_f1 = 20, .vi_end_f2 = 21,
479 .nbr_end = 240,
480
Chris Wilson3ca87e82010-06-06 15:40:23 +0100481 .burst_ena = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800482 .hburst_start = 72, .hburst_len = 34,
483 .vburst_start_f1 = 9, .vburst_end_f1 = 240,
484 .vburst_start_f2 = 10, .vburst_end_f2 = 240,
485 .vburst_start_f3 = 9, .vburst_end_f3 = 240,
486 .vburst_start_f4 = 10, .vburst_end_f4 = 240,
487
488 /* desired 4.4336180 actual 4.4336180 clock 107.52 */
489 .dda1_inc = 168,
Zhenyu Wangba010792009-03-04 20:23:02 +0800490 .dda2_inc = 4093, .dda2_size = 27456,
491 .dda3_inc = 310, .dda3_size = 525,
492 .sc_reset = TV_SC_RESET_NEVER,
493 .pal_burst = false,
Jesse Barnes79e53942008-11-07 14:24:08 -0800494
495 .composite_levels = &ntsc_m_levels_composite,
496 .composite_color = &ntsc_m_csc_composite,
497 .svideo_levels = &ntsc_m_levels_svideo,
498 .svideo_color = &ntsc_m_csc_svideo,
499
500 .filter_table = filter_table,
501 },
502 {
503 .name = "NTSC-J",
Zhenyu Wangba010792009-03-04 20:23:02 +0800504 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200505 .refresh = 59940,
Jesse Barnes79e53942008-11-07 14:24:08 -0800506 .oversample = TV_OVERSAMPLE_8X,
507 .component_only = 0,
508
509 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
510 .hsync_end = 64, .hblank_end = 124,
511 .hblank_start = 836, .htotal = 857,
512
513 .progressive = false, .trilevel_sync = false,
514
515 .vsync_start_f1 = 6, .vsync_start_f2 = 7,
516 .vsync_len = 6,
517
Akshay Joshi0206e352011-08-16 15:34:10 -0400518 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800519 .veq_start_f2 = 1, .veq_len = 18,
520
521 .vi_end_f1 = 20, .vi_end_f2 = 21,
522 .nbr_end = 240,
523
524 .burst_ena = true,
525 .hburst_start = 72, .hburst_len = 34,
526 .vburst_start_f1 = 9, .vburst_end_f1 = 240,
527 .vburst_start_f2 = 10, .vburst_end_f2 = 240,
528 .vburst_start_f3 = 9, .vburst_end_f3 = 240,
529 .vburst_start_f4 = 10, .vburst_end_f4 = 240,
530
531 /* desired 3.5800000 actual 3.5800000 clock 107.52 */
Zhenyu Wangba010792009-03-04 20:23:02 +0800532 .dda1_inc = 135,
533 .dda2_inc = 20800, .dda2_size = 27456,
Jesse Barnes79e53942008-11-07 14:24:08 -0800534 .dda3_inc = 0, .dda3_size = 0,
535 .sc_reset = TV_SC_RESET_EVERY_4,
536 .pal_burst = false,
537
538 .composite_levels = &ntsc_j_levels_composite,
539 .composite_color = &ntsc_j_csc_composite,
540 .svideo_levels = &ntsc_j_levels_svideo,
541 .svideo_color = &ntsc_j_csc_svideo,
542
543 .filter_table = filter_table,
544 },
545 {
546 .name = "PAL-M",
Zhenyu Wangba010792009-03-04 20:23:02 +0800547 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200548 .refresh = 59940,
Jesse Barnes79e53942008-11-07 14:24:08 -0800549 .oversample = TV_OVERSAMPLE_8X,
550 .component_only = 0,
551
552 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
553 .hsync_end = 64, .hblank_end = 124,
554 .hblank_start = 836, .htotal = 857,
555
556 .progressive = false, .trilevel_sync = false,
557
558 .vsync_start_f1 = 6, .vsync_start_f2 = 7,
559 .vsync_len = 6,
560
Akshay Joshi0206e352011-08-16 15:34:10 -0400561 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800562 .veq_start_f2 = 1, .veq_len = 18,
563
564 .vi_end_f1 = 20, .vi_end_f2 = 21,
565 .nbr_end = 240,
566
567 .burst_ena = true,
568 .hburst_start = 72, .hburst_len = 34,
569 .vburst_start_f1 = 9, .vburst_end_f1 = 240,
570 .vburst_start_f2 = 10, .vburst_end_f2 = 240,
571 .vburst_start_f3 = 9, .vburst_end_f3 = 240,
572 .vburst_start_f4 = 10, .vburst_end_f4 = 240,
573
574 /* desired 3.5800000 actual 3.5800000 clock 107.52 */
Zhenyu Wangba010792009-03-04 20:23:02 +0800575 .dda1_inc = 135,
576 .dda2_inc = 16704, .dda2_size = 27456,
Jesse Barnes79e53942008-11-07 14:24:08 -0800577 .dda3_inc = 0, .dda3_size = 0,
Zhenyu Wangba010792009-03-04 20:23:02 +0800578 .sc_reset = TV_SC_RESET_EVERY_8,
579 .pal_burst = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800580
581 .composite_levels = &pal_m_levels_composite,
582 .composite_color = &pal_m_csc_composite,
583 .svideo_levels = &pal_m_levels_svideo,
584 .svideo_color = &pal_m_csc_svideo,
585
586 .filter_table = filter_table,
587 },
588 {
589 /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
590 .name = "PAL-N",
Zhenyu Wangba010792009-03-04 20:23:02 +0800591 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200592 .refresh = 50000,
Jesse Barnes79e53942008-11-07 14:24:08 -0800593 .oversample = TV_OVERSAMPLE_8X,
594 .component_only = 0,
595
596 .hsync_end = 64, .hblank_end = 128,
597 .hblank_start = 844, .htotal = 863,
598
599 .progressive = false, .trilevel_sync = false,
600
601
602 .vsync_start_f1 = 6, .vsync_start_f2 = 7,
603 .vsync_len = 6,
604
Akshay Joshi0206e352011-08-16 15:34:10 -0400605 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800606 .veq_start_f2 = 1, .veq_len = 18,
607
608 .vi_end_f1 = 24, .vi_end_f2 = 25,
609 .nbr_end = 286,
610
611 .burst_ena = true,
Akshay Joshi0206e352011-08-16 15:34:10 -0400612 .hburst_start = 73, .hburst_len = 34,
Jesse Barnes79e53942008-11-07 14:24:08 -0800613 .vburst_start_f1 = 8, .vburst_end_f1 = 285,
614 .vburst_start_f2 = 8, .vburst_end_f2 = 286,
615 .vburst_start_f3 = 9, .vburst_end_f3 = 286,
616 .vburst_start_f4 = 9, .vburst_end_f4 = 285,
617
618
619 /* desired 4.4336180 actual 4.4336180 clock 107.52 */
Zhenyu Wangba010792009-03-04 20:23:02 +0800620 .dda1_inc = 135,
621 .dda2_inc = 23578, .dda2_size = 27648,
622 .dda3_inc = 134, .dda3_size = 625,
Jesse Barnes79e53942008-11-07 14:24:08 -0800623 .sc_reset = TV_SC_RESET_EVERY_8,
624 .pal_burst = true,
625
626 .composite_levels = &pal_n_levels_composite,
627 .composite_color = &pal_n_csc_composite,
628 .svideo_levels = &pal_n_levels_svideo,
629 .svideo_color = &pal_n_csc_svideo,
630
631 .filter_table = filter_table,
632 },
633 {
634 /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
635 .name = "PAL",
Zhenyu Wangba010792009-03-04 20:23:02 +0800636 .clock = 108000,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200637 .refresh = 50000,
Jesse Barnes79e53942008-11-07 14:24:08 -0800638 .oversample = TV_OVERSAMPLE_8X,
639 .component_only = 0,
640
Zhenyu Wangba010792009-03-04 20:23:02 +0800641 .hsync_end = 64, .hblank_end = 142,
Jesse Barnes79e53942008-11-07 14:24:08 -0800642 .hblank_start = 844, .htotal = 863,
643
644 .progressive = false, .trilevel_sync = false,
645
646 .vsync_start_f1 = 5, .vsync_start_f2 = 6,
647 .vsync_len = 5,
648
Akshay Joshi0206e352011-08-16 15:34:10 -0400649 .veq_ena = true, .veq_start_f1 = 0,
Jesse Barnes79e53942008-11-07 14:24:08 -0800650 .veq_start_f2 = 1, .veq_len = 15,
651
652 .vi_end_f1 = 24, .vi_end_f2 = 25,
653 .nbr_end = 286,
654
655 .burst_ena = true,
656 .hburst_start = 73, .hburst_len = 32,
657 .vburst_start_f1 = 8, .vburst_end_f1 = 285,
658 .vburst_start_f2 = 8, .vburst_end_f2 = 286,
659 .vburst_start_f3 = 9, .vburst_end_f3 = 286,
660 .vburst_start_f4 = 9, .vburst_end_f4 = 285,
661
662 /* desired 4.4336180 actual 4.4336180 clock 107.52 */
663 .dda1_inc = 168,
Zhenyu Wangba010792009-03-04 20:23:02 +0800664 .dda2_inc = 4122, .dda2_size = 27648,
665 .dda3_inc = 67, .dda3_size = 625,
Jesse Barnes79e53942008-11-07 14:24:08 -0800666 .sc_reset = TV_SC_RESET_EVERY_8,
667 .pal_burst = true,
668
669 .composite_levels = &pal_levels_composite,
670 .composite_color = &pal_csc_composite,
671 .svideo_levels = &pal_levels_svideo,
672 .svideo_color = &pal_csc_svideo,
673
674 .filter_table = filter_table,
675 },
676 {
Rodrigo Vivi95899192012-05-22 15:23:24 -0300677 .name = "480p",
678 .clock = 107520,
679 .refresh = 59940,
680 .oversample = TV_OVERSAMPLE_4X,
681 .component_only = 1,
682
683 .hsync_end = 64, .hblank_end = 122,
684 .hblank_start = 842, .htotal = 857,
685
686 .progressive = true, .trilevel_sync = false,
687
688 .vsync_start_f1 = 12, .vsync_start_f2 = 12,
689 .vsync_len = 12,
690
691 .veq_ena = false,
692
693 .vi_end_f1 = 44, .vi_end_f2 = 44,
694 .nbr_end = 479,
695
696 .burst_ena = false,
697
698 .filter_table = filter_table,
699 },
700 {
701 .name = "576p",
702 .clock = 107520,
703 .refresh = 50000,
704 .oversample = TV_OVERSAMPLE_4X,
705 .component_only = 1,
706
707 .hsync_end = 64, .hblank_end = 139,
708 .hblank_start = 859, .htotal = 863,
709
710 .progressive = true, .trilevel_sync = false,
711
712 .vsync_start_f1 = 10, .vsync_start_f2 = 10,
713 .vsync_len = 10,
714
715 .veq_ena = false,
716
717 .vi_end_f1 = 48, .vi_end_f2 = 48,
718 .nbr_end = 575,
719
720 .burst_ena = false,
721
722 .filter_table = filter_table,
723 },
724 {
Jesse Barnes79e53942008-11-07 14:24:08 -0800725 .name = "720p@60Hz",
726 .clock = 148800,
727 .refresh = 60000,
728 .oversample = TV_OVERSAMPLE_2X,
729 .component_only = 1,
730
731 .hsync_end = 80, .hblank_end = 300,
732 .hblank_start = 1580, .htotal = 1649,
733
Akshay Joshi0206e352011-08-16 15:34:10 -0400734 .progressive = true, .trilevel_sync = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800735
736 .vsync_start_f1 = 10, .vsync_start_f2 = 10,
737 .vsync_len = 10,
738
739 .veq_ena = false,
740
741 .vi_end_f1 = 29, .vi_end_f2 = 29,
742 .nbr_end = 719,
743
744 .burst_ena = false,
745
746 .filter_table = filter_table,
747 },
748 {
Jesse Barnes79e53942008-11-07 14:24:08 -0800749 .name = "720p@50Hz",
750 .clock = 148800,
751 .refresh = 50000,
752 .oversample = TV_OVERSAMPLE_2X,
753 .component_only = 1,
754
755 .hsync_end = 80, .hblank_end = 300,
756 .hblank_start = 1580, .htotal = 1979,
757
Akshay Joshi0206e352011-08-16 15:34:10 -0400758 .progressive = true, .trilevel_sync = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800759
760 .vsync_start_f1 = 10, .vsync_start_f2 = 10,
761 .vsync_len = 10,
762
763 .veq_ena = false,
764
765 .vi_end_f1 = 29, .vi_end_f2 = 29,
766 .nbr_end = 719,
767
768 .burst_ena = false,
769
770 .filter_table = filter_table,
771 .max_srcw = 800
772 },
773 {
774 .name = "1080i@50Hz",
775 .clock = 148800,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200776 .refresh = 50000,
Jesse Barnes79e53942008-11-07 14:24:08 -0800777 .oversample = TV_OVERSAMPLE_2X,
778 .component_only = 1,
779
780 .hsync_end = 88, .hblank_end = 235,
781 .hblank_start = 2155, .htotal = 2639,
782
Akshay Joshi0206e352011-08-16 15:34:10 -0400783 .progressive = false, .trilevel_sync = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800784
785 .vsync_start_f1 = 4, .vsync_start_f2 = 5,
786 .vsync_len = 10,
787
Akshay Joshi0206e352011-08-16 15:34:10 -0400788 .veq_ena = true, .veq_start_f1 = 4,
Jesse Barnes79e53942008-11-07 14:24:08 -0800789 .veq_start_f2 = 4, .veq_len = 10,
790
791
792 .vi_end_f1 = 21, .vi_end_f2 = 22,
793 .nbr_end = 539,
794
795 .burst_ena = false,
796
797 .filter_table = filter_table,
798 },
799 {
800 .name = "1080i@60Hz",
801 .clock = 148800,
Rodrigo Vivi23bd15e2011-12-14 21:10:06 -0200802 .refresh = 60000,
Jesse Barnes79e53942008-11-07 14:24:08 -0800803 .oversample = TV_OVERSAMPLE_2X,
804 .component_only = 1,
805
806 .hsync_end = 88, .hblank_end = 235,
807 .hblank_start = 2155, .htotal = 2199,
808
Akshay Joshi0206e352011-08-16 15:34:10 -0400809 .progressive = false, .trilevel_sync = true,
Jesse Barnes79e53942008-11-07 14:24:08 -0800810
811 .vsync_start_f1 = 4, .vsync_start_f2 = 5,
812 .vsync_len = 10,
813
Akshay Joshi0206e352011-08-16 15:34:10 -0400814 .veq_ena = true, .veq_start_f1 = 4,
Jesse Barnes79e53942008-11-07 14:24:08 -0800815 .veq_start_f2 = 4, .veq_len = 10,
816
817
818 .vi_end_f1 = 21, .vi_end_f2 = 22,
819 .nbr_end = 539,
820
821 .burst_ena = false,
822
823 .filter_table = filter_table,
824 },
Jesse Barnes79e53942008-11-07 14:24:08 -0800825};
826
Chris Wilsonea5b2132010-08-04 13:50:23 +0100827static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder)
828{
Chris Wilson4ef69c72010-09-09 15:14:28 +0100829 return container_of(encoder, struct intel_tv, base.base);
Chris Wilsonea5b2132010-08-04 13:50:23 +0100830}
831
Chris Wilsondf0e9242010-09-09 16:20:55 +0100832static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
833{
834 return container_of(intel_attached_encoder(connector),
835 struct intel_tv,
836 base);
837}
838
Daniel Vetter9a8ee982012-07-02 13:34:59 +0200839static bool
840intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
841{
842 struct drm_device *dev = encoder->base.dev;
843 struct drm_i915_private *dev_priv = dev->dev_private;
844 u32 tmp = I915_READ(TV_CTL);
845
846 if (!(tmp & TV_ENC_ENABLE))
847 return false;
848
849 *pipe = PORT_TO_PIPE(tmp);
850
851 return true;
852}
853
Jesse Barnes79e53942008-11-07 14:24:08 -0800854static void
Daniel Vetter6b5756a2012-06-30 10:33:44 +0200855intel_enable_tv(struct intel_encoder *encoder)
Jesse Barnes79e53942008-11-07 14:24:08 -0800856{
Daniel Vetter6b5756a2012-06-30 10:33:44 +0200857 struct drm_device *dev = encoder->base.dev;
Jesse Barnes79e53942008-11-07 14:24:08 -0800858 struct drm_i915_private *dev_priv = dev->dev_private;
859
Daniel Vetter6b5756a2012-06-30 10:33:44 +0200860 I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
861}
862
863static void
864intel_disable_tv(struct intel_encoder *encoder)
865{
866 struct drm_device *dev = encoder->base.dev;
867 struct drm_i915_private *dev_priv = dev->dev_private;
868
869 I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
Jesse Barnes79e53942008-11-07 14:24:08 -0800870}
871
Jesse Barnes79e53942008-11-07 14:24:08 -0800872static const struct tv_mode *
Chris Wilson763a4a02010-09-05 00:52:34 +0100873intel_tv_mode_lookup(const char *tv_format)
Jesse Barnes79e53942008-11-07 14:24:08 -0800874{
875 int i;
876
Dave Airlie3801a7f2012-04-20 13:13:54 +0100877 for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
Jesse Barnes79e53942008-11-07 14:24:08 -0800878 const struct tv_mode *tv_mode = &tv_modes[i];
879
880 if (!strcmp(tv_format, tv_mode->name))
881 return tv_mode;
882 }
883 return NULL;
884}
885
886static const struct tv_mode *
Chris Wilson763a4a02010-09-05 00:52:34 +0100887intel_tv_mode_find(struct intel_tv *intel_tv)
Jesse Barnes79e53942008-11-07 14:24:08 -0800888{
Chris Wilsonea5b2132010-08-04 13:50:23 +0100889 return intel_tv_mode_lookup(intel_tv->tv_format);
Jesse Barnes79e53942008-11-07 14:24:08 -0800890}
891
892static enum drm_mode_status
Chris Wilson763a4a02010-09-05 00:52:34 +0100893intel_tv_mode_valid(struct drm_connector *connector,
894 struct drm_display_mode *mode)
Jesse Barnes79e53942008-11-07 14:24:08 -0800895{
Chris Wilsondf0e9242010-09-09 16:20:55 +0100896 struct intel_tv *intel_tv = intel_attached_tv(connector);
Chris Wilsonea5b2132010-08-04 13:50:23 +0100897 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
Jesse Barnes79e53942008-11-07 14:24:08 -0800898
899 /* Ensure TV refresh is close to desired refresh */
Zhao Yakui0d0884c2009-09-29 16:31:49 +0800900 if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
901 < 1000)
Jesse Barnes79e53942008-11-07 14:24:08 -0800902 return MODE_OK;
Chris Wilson763a4a02010-09-05 00:52:34 +0100903
Jesse Barnes79e53942008-11-07 14:24:08 -0800904 return MODE_CLOCK_RANGE;
905}
906
907
908static bool
Laurent Pincharte811f5a2012-07-17 17:56:50 +0200909intel_tv_mode_fixup(struct drm_encoder *encoder,
910 const struct drm_display_mode *mode,
Jesse Barnes79e53942008-11-07 14:24:08 -0800911 struct drm_display_mode *adjusted_mode)
912{
Chris Wilsonea5b2132010-08-04 13:50:23 +0100913 struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
914 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
Jesse Barnes79e53942008-11-07 14:24:08 -0800915
916 if (!tv_mode)
917 return false;
918
Daniel Vetter6ed0f792012-07-08 19:41:43 +0200919 if (intel_encoder_check_is_cloned(&intel_tv->base))
920 return false;
Jesse Barnes79e53942008-11-07 14:24:08 -0800921
922 adjusted_mode->clock = tv_mode->clock;
923 return true;
924}
925
926static void
927intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
928 struct drm_display_mode *adjusted_mode)
929{
930 struct drm_device *dev = encoder->dev;
931 struct drm_i915_private *dev_priv = dev->dev_private;
932 struct drm_crtc *crtc = encoder->crtc;
933 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
Chris Wilsonea5b2132010-08-04 13:50:23 +0100934 struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
935 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
Jesse Barnes79e53942008-11-07 14:24:08 -0800936 u32 tv_ctl;
937 u32 hctl1, hctl2, hctl3;
938 u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
939 u32 scctl1, scctl2, scctl3;
940 int i, j;
941 const struct video_levels *video_levels;
942 const struct color_conversion *color_conversion;
943 bool burst_ena;
Jesse Barnes9db4a9c2011-02-07 12:26:52 -0800944 int pipe = intel_crtc->pipe;
Jesse Barnes79e53942008-11-07 14:24:08 -0800945
946 if (!tv_mode)
947 return; /* can't happen (mode_prepare prevents this) */
948
Zhenyu Wangd2d9f232009-03-04 19:36:02 +0800949 tv_ctl = I915_READ(TV_CTL);
950 tv_ctl &= TV_CTL_SAVE;
Jesse Barnes79e53942008-11-07 14:24:08 -0800951
Chris Wilsonea5b2132010-08-04 13:50:23 +0100952 switch (intel_tv->type) {
Jesse Barnes79e53942008-11-07 14:24:08 -0800953 default:
954 case DRM_MODE_CONNECTOR_Unknown:
955 case DRM_MODE_CONNECTOR_Composite:
956 tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
957 video_levels = tv_mode->composite_levels;
958 color_conversion = tv_mode->composite_color;
959 burst_ena = tv_mode->burst_ena;
960 break;
961 case DRM_MODE_CONNECTOR_Component:
962 tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
963 video_levels = &component_levels;
964 if (tv_mode->burst_ena)
965 color_conversion = &sdtv_csc_yprpb;
966 else
967 color_conversion = &hdtv_csc_yprpb;
968 burst_ena = false;
969 break;
970 case DRM_MODE_CONNECTOR_SVIDEO:
971 tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
972 video_levels = tv_mode->svideo_levels;
973 color_conversion = tv_mode->svideo_color;
974 burst_ena = tv_mode->burst_ena;
975 break;
976 }
977 hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
978 (tv_mode->htotal << TV_HTOTAL_SHIFT);
979
980 hctl2 = (tv_mode->hburst_start << 16) |
981 (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
982
983 if (burst_ena)
984 hctl2 |= TV_BURST_ENA;
985
986 hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
987 (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
988
989 vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
990 (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
991 (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
992
993 vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
994 (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
995 (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
996
997 vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
998 (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
999 (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
1000
1001 if (tv_mode->veq_ena)
1002 vctl3 |= TV_EQUAL_ENA;
1003
1004 vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
1005 (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
1006
1007 vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
1008 (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
1009
1010 vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
1011 (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
1012
1013 vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
1014 (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
1015
1016 if (intel_crtc->pipe == 1)
1017 tv_ctl |= TV_ENC_PIPEB_SELECT;
1018 tv_ctl |= tv_mode->oversample;
1019
1020 if (tv_mode->progressive)
1021 tv_ctl |= TV_PROGRESSIVE;
1022 if (tv_mode->trilevel_sync)
1023 tv_ctl |= TV_TRILEVEL_SYNC;
1024 if (tv_mode->pal_burst)
1025 tv_ctl |= TV_PAL_BURST;
Jesse Barnes79e53942008-11-07 14:24:08 -08001026
Chris Wilsond2718172009-11-27 13:06:56 +00001027 scctl1 = 0;
1028 if (tv_mode->dda1_inc)
1029 scctl1 |= TV_SC_DDA1_EN;
Jesse Barnes79e53942008-11-07 14:24:08 -08001030 if (tv_mode->dda2_inc)
1031 scctl1 |= TV_SC_DDA2_EN;
Jesse Barnes79e53942008-11-07 14:24:08 -08001032 if (tv_mode->dda3_inc)
1033 scctl1 |= TV_SC_DDA3_EN;
Jesse Barnes79e53942008-11-07 14:24:08 -08001034 scctl1 |= tv_mode->sc_reset;
Chris Wilsond2718172009-11-27 13:06:56 +00001035 if (video_levels)
1036 scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
Jesse Barnes79e53942008-11-07 14:24:08 -08001037 scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
1038
1039 scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
1040 tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
1041
1042 scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
1043 tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
1044
1045 /* Enable two fixes for the chips that need them. */
1046 if (dev->pci_device < 0x2772)
1047 tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
1048
1049 I915_WRITE(TV_H_CTL_1, hctl1);
1050 I915_WRITE(TV_H_CTL_2, hctl2);
1051 I915_WRITE(TV_H_CTL_3, hctl3);
1052 I915_WRITE(TV_V_CTL_1, vctl1);
1053 I915_WRITE(TV_V_CTL_2, vctl2);
1054 I915_WRITE(TV_V_CTL_3, vctl3);
1055 I915_WRITE(TV_V_CTL_4, vctl4);
1056 I915_WRITE(TV_V_CTL_5, vctl5);
1057 I915_WRITE(TV_V_CTL_6, vctl6);
1058 I915_WRITE(TV_V_CTL_7, vctl7);
1059 I915_WRITE(TV_SC_CTL_1, scctl1);
1060 I915_WRITE(TV_SC_CTL_2, scctl2);
1061 I915_WRITE(TV_SC_CTL_3, scctl3);
1062
1063 if (color_conversion) {
1064 I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
1065 color_conversion->gy);
Akshay Joshi0206e352011-08-16 15:34:10 -04001066 I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
Jesse Barnes79e53942008-11-07 14:24:08 -08001067 color_conversion->ay);
1068 I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
1069 color_conversion->gu);
1070 I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
1071 color_conversion->au);
1072 I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
1073 color_conversion->gv);
1074 I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
1075 color_conversion->av);
1076 }
1077
Chris Wilsona6c45cf2010-09-17 00:32:17 +01001078 if (INTEL_INFO(dev)->gen >= 4)
Zhenyu Wangd2d9f232009-03-04 19:36:02 +08001079 I915_WRITE(TV_CLR_KNOBS, 0x00404000);
1080 else
1081 I915_WRITE(TV_CLR_KNOBS, 0x00606000);
1082
Jesse Barnes79e53942008-11-07 14:24:08 -08001083 if (video_levels)
1084 I915_WRITE(TV_CLR_LEVEL,
1085 ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
1086 (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
1087 {
Jesse Barnes9db4a9c2011-02-07 12:26:52 -08001088 int pipeconf_reg = PIPECONF(pipe);
Sitsofe Wheelerccacfec2011-04-12 06:51:39 +01001089 int dspcntr_reg = DSPCNTR(intel_crtc->plane);
Jesse Barnes79e53942008-11-07 14:24:08 -08001090 int pipeconf = I915_READ(pipeconf_reg);
1091 int dspcntr = I915_READ(dspcntr_reg);
Sitsofe Wheelerccacfec2011-04-12 06:51:39 +01001092 int dspbase_reg = DSPADDR(intel_crtc->plane);
Jesse Barnes79e53942008-11-07 14:24:08 -08001093 int xpos = 0x0, ypos = 0x0;
1094 unsigned int xsize, ysize;
1095 /* Pipe must be off here */
1096 I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
1097 /* Flush the plane changes */
1098 I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1099
1100 /* Wait for vblank for the disable to take effect */
Chris Wilsona6c45cf2010-09-17 00:32:17 +01001101 if (IS_GEN2(dev))
Jesse Barnes9d0498a2010-08-18 13:20:54 -07001102 intel_wait_for_vblank(dev, intel_crtc->pipe);
Jesse Barnes79e53942008-11-07 14:24:08 -08001103
Chris Wilson5eddb702010-09-11 13:48:45 +01001104 I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
Jesse Barnes79e53942008-11-07 14:24:08 -08001105 /* Wait for vblank for the disable to take effect. */
Chris Wilson58e10eb2010-10-03 10:56:11 +01001106 intel_wait_for_pipe_off(dev, intel_crtc->pipe);
Jesse Barnes79e53942008-11-07 14:24:08 -08001107
1108 /* Filter ctl must be set before TV_WIN_SIZE */
1109 I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
1110 xsize = tv_mode->hblank_start - tv_mode->hblank_end;
1111 if (tv_mode->progressive)
1112 ysize = tv_mode->nbr_end + 1;
1113 else
1114 ysize = 2*tv_mode->nbr_end + 1;
1115
Chris Wilsonea5b2132010-08-04 13:50:23 +01001116 xpos += intel_tv->margin[TV_MARGIN_LEFT];
1117 ypos += intel_tv->margin[TV_MARGIN_TOP];
1118 xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
1119 intel_tv->margin[TV_MARGIN_RIGHT]);
1120 ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
1121 intel_tv->margin[TV_MARGIN_BOTTOM]);
Jesse Barnes79e53942008-11-07 14:24:08 -08001122 I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
1123 I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
1124
1125 I915_WRITE(pipeconf_reg, pipeconf);
1126 I915_WRITE(dspcntr_reg, dspcntr);
1127 /* Flush the plane changes */
1128 I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
1129 }
1130
1131 j = 0;
1132 for (i = 0; i < 60; i++)
1133 I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1134 for (i = 0; i < 60; i++)
1135 I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
1136 for (i = 0; i < 43; i++)
1137 I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
1138 for (i = 0; i < 43; i++)
1139 I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
Chris Wilsonb8ed2a42010-09-05 00:43:42 +01001140 I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
Jesse Barnes79e53942008-11-07 14:24:08 -08001141 I915_WRITE(TV_CTL, tv_ctl);
1142}
1143
1144static const struct drm_display_mode reported_modes[] = {
1145 {
1146 .name = "NTSC 480i",
1147 .clock = 107520,
1148 .hdisplay = 1280,
1149 .hsync_start = 1368,
1150 .hsync_end = 1496,
1151 .htotal = 1712,
1152
1153 .vdisplay = 1024,
1154 .vsync_start = 1027,
1155 .vsync_end = 1034,
1156 .vtotal = 1104,
1157 .type = DRM_MODE_TYPE_DRIVER,
1158 },
1159};
1160
1161/**
1162 * Detects TV presence by checking for load.
1163 *
1164 * Requires that the current pipe's DPLL is active.
1165
1166 * \return true if TV is connected.
1167 * \return false if TV is disconnected.
1168 */
1169static int
Akshay Joshi0206e352011-08-16 15:34:10 -04001170intel_tv_detect_type(struct intel_tv *intel_tv,
Chris Wilson8102e122011-02-10 10:05:35 +00001171 struct drm_connector *connector)
Jesse Barnes79e53942008-11-07 14:24:08 -08001172{
Chris Wilson4ef69c72010-09-09 15:14:28 +01001173 struct drm_encoder *encoder = &intel_tv->base.base;
Keith Packard835bff72011-05-12 17:10:57 -07001174 struct drm_crtc *crtc = encoder->crtc;
1175 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
Jesse Barnes79e53942008-11-07 14:24:08 -08001176 struct drm_device *dev = encoder->dev;
1177 struct drm_i915_private *dev_priv = dev->dev_private;
1178 unsigned long irqflags;
1179 u32 tv_ctl, save_tv_ctl;
1180 u32 tv_dac, save_tv_dac;
Chris Wilson974b9332010-09-05 00:44:20 +01001181 int type;
Jesse Barnes79e53942008-11-07 14:24:08 -08001182
1183 /* Disable TV interrupts around load detect or we'll recurse */
Chris Wilson8102e122011-02-10 10:05:35 +00001184 if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1185 spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
1186 i915_disable_pipestat(dev_priv, 0,
1187 PIPE_HOTPLUG_INTERRUPT_ENABLE |
1188 PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1189 spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
1190 }
Jesse Barnes79e53942008-11-07 14:24:08 -08001191
Chris Wilson974b9332010-09-05 00:44:20 +01001192 save_tv_dac = tv_dac = I915_READ(TV_DAC);
1193 save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
1194
1195 /* Poll for TV detection */
1196 tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
ling.ma@intel.com8ed9a5b2009-06-22 22:08:35 +08001197 tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
Keith Packard835bff72011-05-12 17:10:57 -07001198 if (intel_crtc->pipe == 1)
1199 tv_ctl |= TV_ENC_PIPEB_SELECT;
1200 else
1201 tv_ctl &= ~TV_ENC_PIPEB_SELECT;
Chris Wilson974b9332010-09-05 00:44:20 +01001202
1203 tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
ling.ma@intel.com8ed9a5b2009-06-22 22:08:35 +08001204 tv_dac |= (TVDAC_STATE_CHG_EN |
1205 TVDAC_A_SENSE_CTL |
1206 TVDAC_B_SENSE_CTL |
1207 TVDAC_C_SENSE_CTL |
1208 DAC_CTL_OVERRIDE |
1209 DAC_A_0_7_V |
1210 DAC_B_0_7_V |
1211 DAC_C_0_7_V);
Chris Wilson974b9332010-09-05 00:44:20 +01001212
Daniel Vetterd42c9e22012-03-25 22:56:14 +02001213
1214 /*
1215 * The TV sense state should be cleared to zero on cantiga platform. Otherwise
1216 * the TV is misdetected. This is hardware requirement.
1217 */
1218 if (IS_GM45(dev))
1219 tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
1220 TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
1221
ling.ma@intel.com8ed9a5b2009-06-22 22:08:35 +08001222 I915_WRITE(TV_CTL, tv_ctl);
1223 I915_WRITE(TV_DAC, tv_dac);
Pekka Enberg4f233ef2010-09-04 19:24:04 +03001224 POSTING_READ(TV_DAC);
Pekka Enberg4f233ef2010-09-04 19:24:04 +03001225
Chris Wilson29e13162010-09-22 19:10:09 +01001226 intel_wait_for_vblank(intel_tv->base.base.dev,
1227 to_intel_crtc(intel_tv->base.base.crtc)->pipe);
1228
Chris Wilson974b9332010-09-05 00:44:20 +01001229 type = -1;
Keith Packard2bf71162011-05-12 17:10:58 -07001230 tv_dac = I915_READ(TV_DAC);
1231 DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
1232 /*
1233 * A B C
1234 * 0 1 1 Composite
1235 * 1 0 X svideo
1236 * 0 0 0 Component
1237 */
1238 if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
1239 DRM_DEBUG_KMS("Detected Composite TV connection\n");
1240 type = DRM_MODE_CONNECTOR_Composite;
1241 } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
1242 DRM_DEBUG_KMS("Detected S-Video TV connection\n");
1243 type = DRM_MODE_CONNECTOR_SVIDEO;
1244 } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
1245 DRM_DEBUG_KMS("Detected Component TV connection\n");
1246 type = DRM_MODE_CONNECTOR_Component;
1247 } else {
1248 DRM_DEBUG_KMS("Unrecognised TV connection\n");
1249 type = -1;
Jesse Barnes79e53942008-11-07 14:24:08 -08001250 }
1251
Chris Wilson974b9332010-09-05 00:44:20 +01001252 I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1253 I915_WRITE(TV_CTL, save_tv_ctl);
Daniel Vetterbf2125e2012-05-22 21:41:25 +02001254 POSTING_READ(TV_CTL);
1255
1256 /* For unknown reasons the hw barfs if we don't do this vblank wait. */
1257 intel_wait_for_vblank(intel_tv->base.base.dev,
1258 to_intel_crtc(intel_tv->base.base.crtc)->pipe);
Chris Wilson974b9332010-09-05 00:44:20 +01001259
Jesse Barnes79e53942008-11-07 14:24:08 -08001260 /* Restore interrupt config */
Chris Wilson8102e122011-02-10 10:05:35 +00001261 if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
1262 spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
1263 i915_enable_pipestat(dev_priv, 0,
1264 PIPE_HOTPLUG_INTERRUPT_ENABLE |
1265 PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
1266 spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
1267 }
Jesse Barnes79e53942008-11-07 14:24:08 -08001268
1269 return type;
1270}
1271
Ma Ling213c2e62009-08-24 13:50:25 +08001272/*
1273 * Here we set accurate tv format according to connector type
1274 * i.e Component TV should not be assigned by NTSC or PAL
1275 */
1276static void intel_tv_find_better_format(struct drm_connector *connector)
1277{
Chris Wilsondf0e9242010-09-09 16:20:55 +01001278 struct intel_tv *intel_tv = intel_attached_tv(connector);
Chris Wilsonea5b2132010-08-04 13:50:23 +01001279 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
Ma Ling213c2e62009-08-24 13:50:25 +08001280 int i;
1281
Chris Wilsonea5b2132010-08-04 13:50:23 +01001282 if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
Ma Ling213c2e62009-08-24 13:50:25 +08001283 tv_mode->component_only)
1284 return;
1285
1286
1287 for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) {
1288 tv_mode = tv_modes + i;
1289
Chris Wilsonea5b2132010-08-04 13:50:23 +01001290 if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
Ma Ling213c2e62009-08-24 13:50:25 +08001291 tv_mode->component_only)
1292 break;
1293 }
1294
Chris Wilsonea5b2132010-08-04 13:50:23 +01001295 intel_tv->tv_format = tv_mode->name;
Ma Ling213c2e62009-08-24 13:50:25 +08001296 drm_connector_property_set_value(connector,
1297 connector->dev->mode_config.tv_mode_property, i);
1298}
1299
Jesse Barnes79e53942008-11-07 14:24:08 -08001300/**
1301 * Detect the TV connection.
1302 *
1303 * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
1304 * we have a pipe programmed in order to probe the TV.
1305 */
1306static enum drm_connector_status
Chris Wilson930a9e22010-09-14 11:07:23 +01001307intel_tv_detect(struct drm_connector *connector, bool force)
Jesse Barnes79e53942008-11-07 14:24:08 -08001308{
Jesse Barnes79e53942008-11-07 14:24:08 -08001309 struct drm_display_mode mode;
Chris Wilsondf0e9242010-09-09 16:20:55 +01001310 struct intel_tv *intel_tv = intel_attached_tv(connector);
Chris Wilsonea5b2132010-08-04 13:50:23 +01001311 int type;
Jesse Barnes79e53942008-11-07 14:24:08 -08001312
1313 mode = reported_modes[0];
Jesse Barnes79e53942008-11-07 14:24:08 -08001314
Daniel Vetter38de45c2012-04-20 21:25:04 +02001315 if (force) {
Chris Wilson8261b192011-04-19 23:18:09 +01001316 struct intel_load_detect_pipe tmp;
Chris Wilsonea5b2132010-08-04 13:50:23 +01001317
Daniel Vetterd2434ab2012-08-12 21:20:10 +02001318 if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
Chris Wilson8102e122011-02-10 10:05:35 +00001319 type = intel_tv_detect_type(intel_tv, connector);
Daniel Vetterd2434ab2012-08-12 21:20:10 +02001320 intel_release_load_detect_pipe(connector, &tmp);
Jesse Barnes79e53942008-11-07 14:24:08 -08001321 } else
Chris Wilson7b334fc2010-09-09 23:51:02 +01001322 return connector_status_unknown;
1323 } else
1324 return connector->status;
Zhenyu Wangbf5a2692009-03-04 19:36:03 +08001325
Jesse Barnes79e53942008-11-07 14:24:08 -08001326 if (type < 0)
1327 return connector_status_disconnected;
1328
Mathew McKernand5627662011-04-12 06:51:37 +01001329 intel_tv->type = type;
Ma Ling213c2e62009-08-24 13:50:25 +08001330 intel_tv_find_better_format(connector);
Mathew McKernand5627662011-04-12 06:51:37 +01001331
Jesse Barnes79e53942008-11-07 14:24:08 -08001332 return connector_status_connected;
1333}
1334
Chris Wilson763a4a02010-09-05 00:52:34 +01001335static const struct input_res {
1336 const char *name;
Jesse Barnes79e53942008-11-07 14:24:08 -08001337 int w, h;
Chris Wilson763a4a02010-09-05 00:52:34 +01001338} input_res_table[] = {
Jesse Barnes79e53942008-11-07 14:24:08 -08001339 {"640x480", 640, 480},
1340 {"800x600", 800, 600},
1341 {"1024x768", 1024, 768},
1342 {"1280x1024", 1280, 1024},
1343 {"848x480", 848, 480},
1344 {"1280x720", 1280, 720},
1345 {"1920x1080", 1920, 1080},
1346};
1347
ling.ma@intel.combcae2ca2009-07-20 13:20:23 +08001348/*
1349 * Chose preferred mode according to line number of TV format
1350 */
1351static void
1352intel_tv_chose_preferred_modes(struct drm_connector *connector,
1353 struct drm_display_mode *mode_ptr)
1354{
Chris Wilsondf0e9242010-09-09 16:20:55 +01001355 struct intel_tv *intel_tv = intel_attached_tv(connector);
Chris Wilsonea5b2132010-08-04 13:50:23 +01001356 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
ling.ma@intel.combcae2ca2009-07-20 13:20:23 +08001357
1358 if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
1359 mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1360 else if (tv_mode->nbr_end > 480) {
1361 if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
1362 if (mode_ptr->vdisplay == 720)
1363 mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1364 } else if (mode_ptr->vdisplay == 1080)
1365 mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
1366 }
1367}
1368
Jesse Barnes79e53942008-11-07 14:24:08 -08001369/**
1370 * Stub get_modes function.
1371 *
1372 * This should probably return a set of fixed modes, unless we can figure out
1373 * how to probe modes off of TV connections.
1374 */
1375
1376static int
1377intel_tv_get_modes(struct drm_connector *connector)
1378{
1379 struct drm_display_mode *mode_ptr;
Chris Wilsondf0e9242010-09-09 16:20:55 +01001380 struct intel_tv *intel_tv = intel_attached_tv(connector);
Chris Wilsonea5b2132010-08-04 13:50:23 +01001381 const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
Zhenyu Wang02c5dd92009-03-04 19:36:01 +08001382 int j, count = 0;
1383 u64 tmp;
Jesse Barnes79e53942008-11-07 14:24:08 -08001384
Kulikov Vasiliy04ad3272010-06-28 15:54:56 +04001385 for (j = 0; j < ARRAY_SIZE(input_res_table);
Jesse Barnes79e53942008-11-07 14:24:08 -08001386 j++) {
Chris Wilson763a4a02010-09-05 00:52:34 +01001387 const struct input_res *input = &input_res_table[j];
Jesse Barnes79e53942008-11-07 14:24:08 -08001388 unsigned int hactive_s = input->w;
1389 unsigned int vactive_s = input->h;
1390
1391 if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
1392 continue;
1393
1394 if (input->w > 1024 && (!tv_mode->progressive
1395 && !tv_mode->component_only))
1396 continue;
1397
Zhenyu Wang02c5dd92009-03-04 19:36:01 +08001398 mode_ptr = drm_mode_create(connector->dev);
1399 if (!mode_ptr)
1400 continue;
Jesse Barnes79e53942008-11-07 14:24:08 -08001401 strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
1402
1403 mode_ptr->hdisplay = hactive_s;
1404 mode_ptr->hsync_start = hactive_s + 1;
1405 mode_ptr->hsync_end = hactive_s + 64;
1406 if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
1407 mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
1408 mode_ptr->htotal = hactive_s + 96;
1409
1410 mode_ptr->vdisplay = vactive_s;
1411 mode_ptr->vsync_start = vactive_s + 1;
1412 mode_ptr->vsync_end = vactive_s + 32;
1413 if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
1414 mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
1415 mode_ptr->vtotal = vactive_s + 33;
1416
Zhenyu Wang02c5dd92009-03-04 19:36:01 +08001417 tmp = (u64) tv_mode->refresh * mode_ptr->vtotal;
1418 tmp *= mode_ptr->htotal;
1419 tmp = div_u64(tmp, 1000000);
1420 mode_ptr->clock = (int) tmp;
Jesse Barnes79e53942008-11-07 14:24:08 -08001421
1422 mode_ptr->type = DRM_MODE_TYPE_DRIVER;
ling.ma@intel.combcae2ca2009-07-20 13:20:23 +08001423 intel_tv_chose_preferred_modes(connector, mode_ptr);
Jesse Barnes79e53942008-11-07 14:24:08 -08001424 drm_mode_probed_add(connector, mode_ptr);
Zhenyu Wang02c5dd92009-03-04 19:36:01 +08001425 count++;
Jesse Barnes79e53942008-11-07 14:24:08 -08001426 }
1427
Zhenyu Wang02c5dd92009-03-04 19:36:01 +08001428 return count;
Jesse Barnes79e53942008-11-07 14:24:08 -08001429}
1430
1431static void
Akshay Joshi0206e352011-08-16 15:34:10 -04001432intel_tv_destroy(struct drm_connector *connector)
Jesse Barnes79e53942008-11-07 14:24:08 -08001433{
Jesse Barnes79e53942008-11-07 14:24:08 -08001434 drm_sysfs_connector_remove(connector);
1435 drm_connector_cleanup(connector);
Zhenyu Wang0c41ee22010-03-29 16:38:44 +08001436 kfree(connector);
Jesse Barnes79e53942008-11-07 14:24:08 -08001437}
1438
1439
1440static int
1441intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
1442 uint64_t val)
1443{
1444 struct drm_device *dev = connector->dev;
Chris Wilsondf0e9242010-09-09 16:20:55 +01001445 struct intel_tv *intel_tv = intel_attached_tv(connector);
1446 struct drm_crtc *crtc = intel_tv->base.base.crtc;
Jesse Barnes79e53942008-11-07 14:24:08 -08001447 int ret = 0;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001448 bool changed = false;
Jesse Barnes79e53942008-11-07 14:24:08 -08001449
1450 ret = drm_connector_property_set_value(connector, property, val);
1451 if (ret < 0)
1452 goto out;
1453
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001454 if (property == dev->mode_config.tv_left_margin_property &&
Chris Wilsonea5b2132010-08-04 13:50:23 +01001455 intel_tv->margin[TV_MARGIN_LEFT] != val) {
1456 intel_tv->margin[TV_MARGIN_LEFT] = val;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001457 changed = true;
1458 } else if (property == dev->mode_config.tv_right_margin_property &&
Chris Wilsonea5b2132010-08-04 13:50:23 +01001459 intel_tv->margin[TV_MARGIN_RIGHT] != val) {
1460 intel_tv->margin[TV_MARGIN_RIGHT] = val;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001461 changed = true;
1462 } else if (property == dev->mode_config.tv_top_margin_property &&
Chris Wilsonea5b2132010-08-04 13:50:23 +01001463 intel_tv->margin[TV_MARGIN_TOP] != val) {
1464 intel_tv->margin[TV_MARGIN_TOP] = val;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001465 changed = true;
1466 } else if (property == dev->mode_config.tv_bottom_margin_property &&
Chris Wilsonea5b2132010-08-04 13:50:23 +01001467 intel_tv->margin[TV_MARGIN_BOTTOM] != val) {
1468 intel_tv->margin[TV_MARGIN_BOTTOM] = val;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001469 changed = true;
1470 } else if (property == dev->mode_config.tv_mode_property) {
Dan Carpenter29911962010-06-23 19:29:54 +02001471 if (val >= ARRAY_SIZE(tv_modes)) {
Jesse Barnes79e53942008-11-07 14:24:08 -08001472 ret = -EINVAL;
1473 goto out;
1474 }
Chris Wilsonea5b2132010-08-04 13:50:23 +01001475 if (!strcmp(intel_tv->tv_format, tv_modes[val].name))
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001476 goto out;
1477
Chris Wilsonea5b2132010-08-04 13:50:23 +01001478 intel_tv->tv_format = tv_modes[val].name;
Zhenyu Wangebcc8f22009-03-23 19:40:57 +08001479 changed = true;
Jesse Barnes79e53942008-11-07 14:24:08 -08001480 } else {
1481 ret = -EINVAL;
1482 goto out;
1483 }
1484
Zhenyu Wang7d6ff782009-03-24 00:45:13 +08001485 if (changed && crtc)
Daniel Vettera6778b32012-07-02 09:56:42 +02001486 intel_set_mode(crtc, &crtc->mode,
1487 crtc->x, crtc->y, crtc->fb);
Jesse Barnes79e53942008-11-07 14:24:08 -08001488out:
1489 return ret;
1490}
1491
1492static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
Jesse Barnes79e53942008-11-07 14:24:08 -08001493 .mode_fixup = intel_tv_mode_fixup,
Jesse Barnes79e53942008-11-07 14:24:08 -08001494 .mode_set = intel_tv_mode_set,
Daniel Vetter6b5756a2012-06-30 10:33:44 +02001495 .disable = intel_encoder_disable,
Jesse Barnes79e53942008-11-07 14:24:08 -08001496};
1497
1498static const struct drm_connector_funcs intel_tv_connector_funcs = {
Daniel Vetter6b5756a2012-06-30 10:33:44 +02001499 .dpms = intel_connector_dpms,
Jesse Barnes79e53942008-11-07 14:24:08 -08001500 .detect = intel_tv_detect,
1501 .destroy = intel_tv_destroy,
1502 .set_property = intel_tv_set_property,
1503 .fill_modes = drm_helper_probe_single_connector_modes,
1504};
1505
1506static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
1507 .mode_valid = intel_tv_mode_valid,
1508 .get_modes = intel_tv_get_modes,
Chris Wilsondf0e9242010-09-09 16:20:55 +01001509 .best_encoder = intel_best_encoder,
Jesse Barnes79e53942008-11-07 14:24:08 -08001510};
1511
Jesse Barnes79e53942008-11-07 14:24:08 -08001512static const struct drm_encoder_funcs intel_tv_enc_funcs = {
Chris Wilsonea5b2132010-08-04 13:50:23 +01001513 .destroy = intel_encoder_destroy,
Jesse Barnes79e53942008-11-07 14:24:08 -08001514};
1515
Zhao Yakuic3561432009-11-24 09:48:48 +08001516/*
1517 * Enumerate the child dev array parsed from VBT to check whether
1518 * the integrated TV is present.
1519 * If it is present, return 1.
1520 * If it is not present, return false.
1521 * If no child dev is parsed from VBT, it assumes that the TV is present.
1522 */
Zhao Yakui6e365952009-12-02 10:03:34 +08001523static int tv_is_present_in_vbt(struct drm_device *dev)
Zhao Yakuic3561432009-11-24 09:48:48 +08001524{
1525 struct drm_i915_private *dev_priv = dev->dev_private;
1526 struct child_device_config *p_child;
1527 int i, ret;
1528
1529 if (!dev_priv->child_dev_num)
1530 return 1;
1531
1532 ret = 0;
1533 for (i = 0; i < dev_priv->child_dev_num; i++) {
1534 p_child = dev_priv->child_dev + i;
1535 /*
1536 * If the device type is not TV, continue.
1537 */
1538 if (p_child->device_type != DEVICE_TYPE_INT_TV &&
1539 p_child->device_type != DEVICE_TYPE_TV)
1540 continue;
1541 /* Only when the addin_offset is non-zero, it is regarded
1542 * as present.
1543 */
1544 if (p_child->addin_offset) {
1545 ret = 1;
1546 break;
1547 }
1548 }
1549 return ret;
1550}
Jesse Barnes79e53942008-11-07 14:24:08 -08001551
1552void
1553intel_tv_init(struct drm_device *dev)
1554{
1555 struct drm_i915_private *dev_priv = dev->dev_private;
1556 struct drm_connector *connector;
Chris Wilsonea5b2132010-08-04 13:50:23 +01001557 struct intel_tv *intel_tv;
Eric Anholt21d40d32010-03-25 11:11:14 -07001558 struct intel_encoder *intel_encoder;
Zhenyu Wang0c41ee22010-03-29 16:38:44 +08001559 struct intel_connector *intel_connector;
Jesse Barnes79e53942008-11-07 14:24:08 -08001560 u32 tv_dac_on, tv_dac_off, save_tv_dac;
Chris Wilson763a4a02010-09-05 00:52:34 +01001561 char *tv_format_names[ARRAY_SIZE(tv_modes)];
Jesse Barnes79e53942008-11-07 14:24:08 -08001562 int i, initial_mode = 0;
1563
1564 if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
1565 return;
1566
Zhao Yakuic3561432009-11-24 09:48:48 +08001567 if (!tv_is_present_in_vbt(dev)) {
1568 DRM_DEBUG_KMS("Integrated TV is not present.\n");
1569 return;
1570 }
Jesse Barnes79e53942008-11-07 14:24:08 -08001571 /* Even if we have an encoder we may not have a connector */
1572 if (!dev_priv->int_tv_support)
1573 return;
1574
1575 /*
1576 * Sanity check the TV output by checking to see if the
1577 * DAC register holds a value
1578 */
1579 save_tv_dac = I915_READ(TV_DAC);
1580
1581 I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
1582 tv_dac_on = I915_READ(TV_DAC);
1583
1584 I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
1585 tv_dac_off = I915_READ(TV_DAC);
1586
1587 I915_WRITE(TV_DAC, save_tv_dac);
1588
1589 /*
1590 * If the register does not hold the state change enable
1591 * bit, (either as a 0 or a 1), assume it doesn't really
1592 * exist
1593 */
1594 if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
1595 (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
1596 return;
1597
Chris Wilsonea5b2132010-08-04 13:50:23 +01001598 intel_tv = kzalloc(sizeof(struct intel_tv), GFP_KERNEL);
1599 if (!intel_tv) {
Jesse Barnes79e53942008-11-07 14:24:08 -08001600 return;
1601 }
Ma Lingf8aed702009-08-24 13:50:24 +08001602
Zhenyu Wang0c41ee22010-03-29 16:38:44 +08001603 intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
1604 if (!intel_connector) {
Chris Wilsonea5b2132010-08-04 13:50:23 +01001605 kfree(intel_tv);
Zhenyu Wang0c41ee22010-03-29 16:38:44 +08001606 return;
1607 }
1608
Chris Wilsonea5b2132010-08-04 13:50:23 +01001609 intel_encoder = &intel_tv->base;
Zhenyu Wang0c41ee22010-03-29 16:38:44 +08001610 connector = &intel_connector->base;
Jesse Barnes79e53942008-11-07 14:24:08 -08001611
Chris Wilson8102e122011-02-10 10:05:35 +00001612 /* The documentation, for the older chipsets at least, recommend
1613 * using a polling method rather than hotplug detection for TVs.
1614 * This is because in order to perform the hotplug detection, the PLLs
1615 * for the TV must be kept alive increasing power drain and starving
1616 * bandwidth from other encoders. Notably for instance, it causes
1617 * pipe underruns on Crestline when this encoder is supposedly idle.
1618 *
1619 * More recent chipsets favour HDMI rather than integrated S-Video.
1620 */
Mathew McKernan89ea42d2011-04-12 06:51:38 +01001621 connector->polled = DRM_CONNECTOR_POLL_CONNECT;
Chris Wilson8102e122011-02-10 10:05:35 +00001622
Jesse Barnes79e53942008-11-07 14:24:08 -08001623 drm_connector_init(dev, connector, &intel_tv_connector_funcs,
1624 DRM_MODE_CONNECTOR_SVIDEO);
1625
Chris Wilson4ef69c72010-09-09 15:14:28 +01001626 drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
Jesse Barnes79e53942008-11-07 14:24:08 -08001627 DRM_MODE_ENCODER_TVDAC);
1628
Daniel Vetter6b5756a2012-06-30 10:33:44 +02001629 intel_encoder->enable = intel_enable_tv;
1630 intel_encoder->disable = intel_disable_tv;
Daniel Vetter9a8ee982012-07-02 13:34:59 +02001631 intel_encoder->get_hw_state = intel_tv_get_hw_state;
1632 intel_connector->get_hw_state = intel_connector_get_hw_state;
Daniel Vetter6b5756a2012-06-30 10:33:44 +02001633
Chris Wilsondf0e9242010-09-09 16:20:55 +01001634 intel_connector_attach_encoder(intel_connector, intel_encoder);
Eric Anholt21d40d32010-03-25 11:11:14 -07001635 intel_encoder->type = INTEL_OUTPUT_TVOUT;
1636 intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
Daniel Vetter66a92782012-07-12 20:08:18 +02001637 intel_encoder->cloneable = false;
Chris Wilson4ef69c72010-09-09 15:14:28 +01001638 intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
1639 intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
Chris Wilsonea5b2132010-08-04 13:50:23 +01001640 intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
Jesse Barnes79e53942008-11-07 14:24:08 -08001641
1642 /* BIOS margin values */
Chris Wilsonea5b2132010-08-04 13:50:23 +01001643 intel_tv->margin[TV_MARGIN_LEFT] = 54;
1644 intel_tv->margin[TV_MARGIN_TOP] = 36;
1645 intel_tv->margin[TV_MARGIN_RIGHT] = 46;
1646 intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
Jesse Barnes79e53942008-11-07 14:24:08 -08001647
Chris Wilson763a4a02010-09-05 00:52:34 +01001648 intel_tv->tv_format = tv_modes[initial_mode].name;
Jesse Barnes79e53942008-11-07 14:24:08 -08001649
Chris Wilson4ef69c72010-09-09 15:14:28 +01001650 drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs);
Jesse Barnes79e53942008-11-07 14:24:08 -08001651 drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
1652 connector->interlace_allowed = false;
1653 connector->doublescan_allowed = false;
1654
1655 /* Create TV properties then attach current values */
Dan Carpenter29911962010-06-23 19:29:54 +02001656 for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
Chris Wilson763a4a02010-09-05 00:52:34 +01001657 tv_format_names[i] = (char *)tv_modes[i].name;
1658 drm_mode_create_tv_properties(dev,
1659 ARRAY_SIZE(tv_modes),
1660 tv_format_names);
Jesse Barnes79e53942008-11-07 14:24:08 -08001661
1662 drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
1663 initial_mode);
1664 drm_connector_attach_property(connector,
1665 dev->mode_config.tv_left_margin_property,
Chris Wilsonea5b2132010-08-04 13:50:23 +01001666 intel_tv->margin[TV_MARGIN_LEFT]);
Jesse Barnes79e53942008-11-07 14:24:08 -08001667 drm_connector_attach_property(connector,
1668 dev->mode_config.tv_top_margin_property,
Chris Wilsonea5b2132010-08-04 13:50:23 +01001669 intel_tv->margin[TV_MARGIN_TOP]);
Jesse Barnes79e53942008-11-07 14:24:08 -08001670 drm_connector_attach_property(connector,
1671 dev->mode_config.tv_right_margin_property,
Chris Wilsonea5b2132010-08-04 13:50:23 +01001672 intel_tv->margin[TV_MARGIN_RIGHT]);
Jesse Barnes79e53942008-11-07 14:24:08 -08001673 drm_connector_attach_property(connector,
1674 dev->mode_config.tv_bottom_margin_property,
Chris Wilsonea5b2132010-08-04 13:50:23 +01001675 intel_tv->margin[TV_MARGIN_BOTTOM]);
Jesse Barnes79e53942008-11-07 14:24:08 -08001676 drm_sysfs_connector_add(connector);
1677}