1e3adcf8fSFrançois Tigeot /* 2e3adcf8fSFrançois Tigeot * Copyright © 2006-2008 Intel Corporation 3e3adcf8fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 4e3adcf8fSFrançois Tigeot * 5e3adcf8fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 6e3adcf8fSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 7e3adcf8fSFrançois Tigeot * to deal in the Software without restriction, including without limitation 8e3adcf8fSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9e3adcf8fSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 10e3adcf8fSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 11e3adcf8fSFrançois Tigeot * 12e3adcf8fSFrançois Tigeot * The above copyright notice and this permission notice (including the next 13e3adcf8fSFrançois Tigeot * paragraph) shall be included in all copies or substantial portions of the 14e3adcf8fSFrançois Tigeot * Software. 15e3adcf8fSFrançois Tigeot * 16e3adcf8fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17e3adcf8fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18e3adcf8fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19e3adcf8fSFrançois Tigeot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20e3adcf8fSFrançois Tigeot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21e3adcf8fSFrançois Tigeot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22e3adcf8fSFrançois Tigeot * DEALINGS IN THE SOFTWARE. 23e3adcf8fSFrançois Tigeot * 24e3adcf8fSFrançois Tigeot * Authors: 25e3adcf8fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 26e3adcf8fSFrançois Tigeot * 27e3adcf8fSFrançois Tigeot * $FreeBSD: src/sys/dev/drm2/i915/intel_tv.c,v 1.1 2012/05/22 11:07:44 kib Exp $ 28e3adcf8fSFrançois Tigeot */ 29e3adcf8fSFrançois Tigeot 30e3adcf8fSFrançois Tigeot /** @file 31e3adcf8fSFrançois Tigeot * Integrated TV-out support for the 915GM and 945GM. 32e3adcf8fSFrançois Tigeot */ 33e3adcf8fSFrançois Tigeot 34e3adcf8fSFrançois Tigeot 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 3818e26a6dSFrançois Tigeot #include "intel_drv.h" 395c6c6f23SFrançois Tigeot #include <drm/i915_drm.h> 40e3adcf8fSFrançois Tigeot #include "i915_drv.h" 41e3adcf8fSFrançois Tigeot 42e3adcf8fSFrançois Tigeot enum tv_margin { 43e3adcf8fSFrançois Tigeot TV_MARGIN_LEFT, TV_MARGIN_TOP, 44e3adcf8fSFrançois Tigeot TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM 45e3adcf8fSFrançois Tigeot }; 46e3adcf8fSFrançois Tigeot 47e3adcf8fSFrançois Tigeot /** Private structure for the integrated TV support */ 48e3adcf8fSFrançois Tigeot struct intel_tv { 49e3adcf8fSFrançois Tigeot struct intel_encoder base; 50e3adcf8fSFrançois Tigeot 51e3adcf8fSFrançois Tigeot int type; 52e3adcf8fSFrançois Tigeot const char *tv_format; 53e3adcf8fSFrançois Tigeot int margin[4]; 54e3adcf8fSFrançois Tigeot u32 save_TV_H_CTL_1; 55e3adcf8fSFrançois Tigeot u32 save_TV_H_CTL_2; 56e3adcf8fSFrançois Tigeot u32 save_TV_H_CTL_3; 57e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_1; 58e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_2; 59e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_3; 60e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_4; 61e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_5; 62e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_6; 63e3adcf8fSFrançois Tigeot u32 save_TV_V_CTL_7; 64e3adcf8fSFrançois Tigeot u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; 65e3adcf8fSFrançois Tigeot 66e3adcf8fSFrançois Tigeot u32 save_TV_CSC_Y; 67e3adcf8fSFrançois Tigeot u32 save_TV_CSC_Y2; 68e3adcf8fSFrançois Tigeot u32 save_TV_CSC_U; 69e3adcf8fSFrançois Tigeot u32 save_TV_CSC_U2; 70e3adcf8fSFrançois Tigeot u32 save_TV_CSC_V; 71e3adcf8fSFrançois Tigeot u32 save_TV_CSC_V2; 72e3adcf8fSFrançois Tigeot u32 save_TV_CLR_KNOBS; 73e3adcf8fSFrançois Tigeot u32 save_TV_CLR_LEVEL; 74e3adcf8fSFrançois Tigeot u32 save_TV_WIN_POS; 75e3adcf8fSFrançois Tigeot u32 save_TV_WIN_SIZE; 76e3adcf8fSFrançois Tigeot u32 save_TV_FILTER_CTL_1; 77e3adcf8fSFrançois Tigeot u32 save_TV_FILTER_CTL_2; 78e3adcf8fSFrançois Tigeot u32 save_TV_FILTER_CTL_3; 79e3adcf8fSFrançois Tigeot 80e3adcf8fSFrançois Tigeot u32 save_TV_H_LUMA[60]; 81e3adcf8fSFrançois Tigeot u32 save_TV_H_CHROMA[60]; 82e3adcf8fSFrançois Tigeot u32 save_TV_V_LUMA[43]; 83e3adcf8fSFrançois Tigeot u32 save_TV_V_CHROMA[43]; 84e3adcf8fSFrançois Tigeot 85e3adcf8fSFrançois Tigeot u32 save_TV_DAC; 86e3adcf8fSFrançois Tigeot u32 save_TV_CTL; 87e3adcf8fSFrançois Tigeot }; 88e3adcf8fSFrançois Tigeot 89e3adcf8fSFrançois Tigeot struct video_levels { 90e3adcf8fSFrançois Tigeot int blank, black, burst; 91e3adcf8fSFrançois Tigeot }; 92e3adcf8fSFrançois Tigeot 93e3adcf8fSFrançois Tigeot struct color_conversion { 94e3adcf8fSFrançois Tigeot u16 ry, gy, by, ay; 95e3adcf8fSFrançois Tigeot u16 ru, gu, bu, au; 96e3adcf8fSFrançois Tigeot u16 rv, gv, bv, av; 97e3adcf8fSFrançois Tigeot }; 98e3adcf8fSFrançois Tigeot 99e3adcf8fSFrançois Tigeot static const u32 filter_table[] = { 100e3adcf8fSFrançois Tigeot 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 101e3adcf8fSFrançois Tigeot 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 102e3adcf8fSFrançois Tigeot 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 103e3adcf8fSFrançois Tigeot 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 104e3adcf8fSFrançois Tigeot 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 105e3adcf8fSFrançois Tigeot 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 106e3adcf8fSFrançois Tigeot 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 107e3adcf8fSFrançois Tigeot 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 108e3adcf8fSFrançois Tigeot 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 109e3adcf8fSFrançois Tigeot 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 110e3adcf8fSFrançois Tigeot 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 111e3adcf8fSFrançois Tigeot 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 112e3adcf8fSFrançois Tigeot 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 113e3adcf8fSFrançois Tigeot 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 114e3adcf8fSFrançois Tigeot 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 115e3adcf8fSFrançois Tigeot 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 116e3adcf8fSFrançois Tigeot 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 117e3adcf8fSFrançois Tigeot 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 118e3adcf8fSFrançois Tigeot 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 119e3adcf8fSFrançois Tigeot 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 120e3adcf8fSFrançois Tigeot 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 121e3adcf8fSFrançois Tigeot 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 122e3adcf8fSFrançois Tigeot 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 123e3adcf8fSFrançois Tigeot 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 124e3adcf8fSFrançois Tigeot 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 125e3adcf8fSFrançois Tigeot 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 126e3adcf8fSFrançois Tigeot 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 127e3adcf8fSFrançois Tigeot 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 128e3adcf8fSFrançois Tigeot 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 129e3adcf8fSFrançois Tigeot 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 130e3adcf8fSFrançois Tigeot 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0, 131e3adcf8fSFrançois Tigeot 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 132e3adcf8fSFrançois Tigeot 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 133e3adcf8fSFrançois Tigeot 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 134e3adcf8fSFrançois Tigeot 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 135e3adcf8fSFrançois Tigeot 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 136e3adcf8fSFrançois Tigeot 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 137e3adcf8fSFrançois Tigeot 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 138e3adcf8fSFrançois Tigeot 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 139e3adcf8fSFrançois Tigeot 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 140e3adcf8fSFrançois Tigeot 0x28003100, 0x28002F00, 0x00003100, 0x36403000, 141e3adcf8fSFrançois Tigeot 0x2D002CC0, 0x30003640, 0x2D0036C0, 142e3adcf8fSFrançois Tigeot 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 143e3adcf8fSFrançois Tigeot 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 144e3adcf8fSFrançois Tigeot 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 145e3adcf8fSFrançois Tigeot 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 146e3adcf8fSFrançois Tigeot 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 147e3adcf8fSFrançois Tigeot 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 148e3adcf8fSFrançois Tigeot 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 149e3adcf8fSFrançois Tigeot 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 150e3adcf8fSFrançois Tigeot 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 151e3adcf8fSFrançois Tigeot 0x28003100, 0x28002F00, 0x00003100, 152e3adcf8fSFrançois Tigeot }; 153e3adcf8fSFrançois Tigeot 154e3adcf8fSFrançois Tigeot /* 155e3adcf8fSFrançois Tigeot * Color conversion values have 3 separate fixed point formats: 156e3adcf8fSFrançois Tigeot * 157e3adcf8fSFrançois Tigeot * 10 bit fields (ay, au) 158e3adcf8fSFrançois Tigeot * 1.9 fixed point (b.bbbbbbbbb) 159e3adcf8fSFrançois Tigeot * 11 bit fields (ry, by, ru, gu, gv) 160e3adcf8fSFrançois Tigeot * exp.mantissa (ee.mmmmmmmmm) 161e3adcf8fSFrançois Tigeot * ee = 00 = 10^-1 (0.mmmmmmmmm) 162e3adcf8fSFrançois Tigeot * ee = 01 = 10^-2 (0.0mmmmmmmmm) 163e3adcf8fSFrançois Tigeot * ee = 10 = 10^-3 (0.00mmmmmmmmm) 164e3adcf8fSFrançois Tigeot * ee = 11 = 10^-4 (0.000mmmmmmmmm) 165e3adcf8fSFrançois Tigeot * 12 bit fields (gy, rv, bu) 166e3adcf8fSFrançois Tigeot * exp.mantissa (eee.mmmmmmmmm) 167e3adcf8fSFrançois Tigeot * eee = 000 = 10^-1 (0.mmmmmmmmm) 168e3adcf8fSFrançois Tigeot * eee = 001 = 10^-2 (0.0mmmmmmmmm) 169e3adcf8fSFrançois Tigeot * eee = 010 = 10^-3 (0.00mmmmmmmmm) 170e3adcf8fSFrançois Tigeot * eee = 011 = 10^-4 (0.000mmmmmmmmm) 171e3adcf8fSFrançois Tigeot * eee = 100 = reserved 172e3adcf8fSFrançois Tigeot * eee = 101 = reserved 173e3adcf8fSFrançois Tigeot * eee = 110 = reserved 174e3adcf8fSFrançois Tigeot * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation) 175e3adcf8fSFrançois Tigeot * 176e3adcf8fSFrançois Tigeot * Saturation and contrast are 8 bits, with their own representation: 177e3adcf8fSFrançois Tigeot * 8 bit field (saturation, contrast) 178e3adcf8fSFrançois Tigeot * exp.mantissa (ee.mmmmmm) 179e3adcf8fSFrançois Tigeot * ee = 00 = 10^-1 (0.mmmmmm) 180e3adcf8fSFrançois Tigeot * ee = 01 = 10^0 (m.mmmmm) 181e3adcf8fSFrançois Tigeot * ee = 10 = 10^1 (mm.mmmm) 182e3adcf8fSFrançois Tigeot * ee = 11 = 10^2 (mmm.mmm) 183e3adcf8fSFrançois Tigeot * 184e3adcf8fSFrançois Tigeot * Simple conversion function: 185e3adcf8fSFrançois Tigeot * 186e3adcf8fSFrançois Tigeot * static u32 187e3adcf8fSFrançois Tigeot * float_to_csc_11(float f) 188e3adcf8fSFrançois Tigeot * { 189e3adcf8fSFrançois Tigeot * u32 exp; 190e3adcf8fSFrançois Tigeot * u32 mant; 191e3adcf8fSFrançois Tigeot * u32 ret; 192e3adcf8fSFrançois Tigeot * 193e3adcf8fSFrançois Tigeot * if (f < 0) 194e3adcf8fSFrançois Tigeot * f = -f; 195e3adcf8fSFrançois Tigeot * 196e3adcf8fSFrançois Tigeot * if (f >= 1) { 197e3adcf8fSFrançois Tigeot * exp = 0x7; 198e3adcf8fSFrançois Tigeot * mant = 1 << 8; 199e3adcf8fSFrançois Tigeot * } else { 200e3adcf8fSFrançois Tigeot * for (exp = 0; exp < 3 && f < 0.5; exp++) 201e3adcf8fSFrançois Tigeot * f *= 2.0; 202e3adcf8fSFrançois Tigeot * mant = (f * (1 << 9) + 0.5); 203e3adcf8fSFrançois Tigeot * if (mant >= (1 << 9)) 204e3adcf8fSFrançois Tigeot * mant = (1 << 9) - 1; 205e3adcf8fSFrançois Tigeot * } 206e3adcf8fSFrançois Tigeot * ret = (exp << 9) | mant; 207e3adcf8fSFrançois Tigeot * return ret; 208e3adcf8fSFrançois Tigeot * } 209e3adcf8fSFrançois Tigeot */ 210e3adcf8fSFrançois Tigeot 211e3adcf8fSFrançois Tigeot /* 212e3adcf8fSFrançois Tigeot * Behold, magic numbers! If we plant them they might grow a big 213e3adcf8fSFrançois Tigeot * s-video cable to the sky... or something. 214e3adcf8fSFrançois Tigeot * 215e3adcf8fSFrançois Tigeot * Pre-converted to appropriate hex value. 216e3adcf8fSFrançois Tigeot */ 217e3adcf8fSFrançois Tigeot 218e3adcf8fSFrançois Tigeot /* 219e3adcf8fSFrançois Tigeot * PAL & NTSC values for composite & s-video connections 220e3adcf8fSFrançois Tigeot */ 221e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_composite = { 222e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 223e3adcf8fSFrançois Tigeot .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 224e3adcf8fSFrançois Tigeot .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 225e3adcf8fSFrançois Tigeot }; 226e3adcf8fSFrançois Tigeot 227e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_composite = { 228e3adcf8fSFrançois Tigeot .blank = 225, .black = 267, .burst = 113, 229e3adcf8fSFrançois Tigeot }; 230e3adcf8fSFrançois Tigeot 231e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_m_csc_svideo = { 232e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 233e3adcf8fSFrançois Tigeot .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 234e3adcf8fSFrançois Tigeot .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 235e3adcf8fSFrançois Tigeot }; 236e3adcf8fSFrançois Tigeot 237e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_m_levels_svideo = { 238e3adcf8fSFrançois Tigeot .blank = 266, .black = 316, .burst = 133, 239e3adcf8fSFrançois Tigeot }; 240e3adcf8fSFrançois Tigeot 241e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_composite = { 242e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119, 243e3adcf8fSFrançois Tigeot .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200, 244e3adcf8fSFrançois Tigeot .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200, 245e3adcf8fSFrançois Tigeot }; 246e3adcf8fSFrançois Tigeot 247e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_composite = { 248e3adcf8fSFrançois Tigeot .blank = 225, .black = 225, .burst = 113, 249e3adcf8fSFrançois Tigeot }; 250e3adcf8fSFrançois Tigeot 251e3adcf8fSFrançois Tigeot static const struct color_conversion ntsc_j_csc_svideo = { 252e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c, 253e3adcf8fSFrançois Tigeot .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200, 254e3adcf8fSFrançois Tigeot .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200, 255e3adcf8fSFrançois Tigeot }; 256e3adcf8fSFrançois Tigeot 257e3adcf8fSFrançois Tigeot static const struct video_levels ntsc_j_levels_svideo = { 258e3adcf8fSFrançois Tigeot .blank = 266, .black = 266, .burst = 133, 259e3adcf8fSFrançois Tigeot }; 260e3adcf8fSFrançois Tigeot 261e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_composite = { 262e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113, 263e3adcf8fSFrançois Tigeot .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200, 264e3adcf8fSFrançois Tigeot .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200, 265e3adcf8fSFrançois Tigeot }; 266e3adcf8fSFrançois Tigeot 267e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_composite = { 268e3adcf8fSFrançois Tigeot .blank = 237, .black = 237, .burst = 118, 269e3adcf8fSFrançois Tigeot }; 270e3adcf8fSFrançois Tigeot 271e3adcf8fSFrançois Tigeot static const struct color_conversion pal_csc_svideo = { 272e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, 273e3adcf8fSFrançois Tigeot .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200, 274e3adcf8fSFrançois Tigeot .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200, 275e3adcf8fSFrançois Tigeot }; 276e3adcf8fSFrançois Tigeot 277e3adcf8fSFrançois Tigeot static const struct video_levels pal_levels_svideo = { 278e3adcf8fSFrançois Tigeot .blank = 280, .black = 280, .burst = 139, 279e3adcf8fSFrançois Tigeot }; 280e3adcf8fSFrançois Tigeot 281e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_composite = { 282e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 283e3adcf8fSFrançois Tigeot .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 284e3adcf8fSFrançois Tigeot .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 285e3adcf8fSFrançois Tigeot }; 286e3adcf8fSFrançois Tigeot 287e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_composite = { 288e3adcf8fSFrançois Tigeot .blank = 225, .black = 267, .burst = 113, 289e3adcf8fSFrançois Tigeot }; 290e3adcf8fSFrançois Tigeot 291e3adcf8fSFrançois Tigeot static const struct color_conversion pal_m_csc_svideo = { 292e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 293e3adcf8fSFrançois Tigeot .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 294e3adcf8fSFrançois Tigeot .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 295e3adcf8fSFrançois Tigeot }; 296e3adcf8fSFrançois Tigeot 297e3adcf8fSFrançois Tigeot static const struct video_levels pal_m_levels_svideo = { 298e3adcf8fSFrançois Tigeot .blank = 266, .black = 316, .burst = 133, 299e3adcf8fSFrançois Tigeot }; 300e3adcf8fSFrançois Tigeot 301e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_composite = { 302e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 303e3adcf8fSFrançois Tigeot .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 304e3adcf8fSFrançois Tigeot .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 305e3adcf8fSFrançois Tigeot }; 306e3adcf8fSFrançois Tigeot 307e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_composite = { 308e3adcf8fSFrançois Tigeot .blank = 225, .black = 267, .burst = 118, 309e3adcf8fSFrançois Tigeot }; 310e3adcf8fSFrançois Tigeot 311e3adcf8fSFrançois Tigeot static const struct color_conversion pal_n_csc_svideo = { 312e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 313e3adcf8fSFrançois Tigeot .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 314e3adcf8fSFrançois Tigeot .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 315e3adcf8fSFrançois Tigeot }; 316e3adcf8fSFrançois Tigeot 317e3adcf8fSFrançois Tigeot static const struct video_levels pal_n_levels_svideo = { 318e3adcf8fSFrançois Tigeot .blank = 266, .black = 316, .burst = 139, 319e3adcf8fSFrançois Tigeot }; 320e3adcf8fSFrançois Tigeot 321e3adcf8fSFrançois Tigeot /* 322e3adcf8fSFrançois Tigeot * Component connections 323e3adcf8fSFrançois Tigeot */ 324e3adcf8fSFrançois Tigeot static const struct color_conversion sdtv_csc_yprpb = { 325e3adcf8fSFrançois Tigeot .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, 326e3adcf8fSFrançois Tigeot .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200, 327e3adcf8fSFrançois Tigeot .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200, 328e3adcf8fSFrançois Tigeot }; 329e3adcf8fSFrançois Tigeot 330e3adcf8fSFrançois Tigeot static const struct color_conversion sdtv_csc_rgb = { 331e3adcf8fSFrançois Tigeot .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, 332e3adcf8fSFrançois Tigeot .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, 333e3adcf8fSFrançois Tigeot .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, 334e3adcf8fSFrançois Tigeot }; 335e3adcf8fSFrançois Tigeot 336e3adcf8fSFrançois Tigeot static const struct color_conversion hdtv_csc_yprpb = { 337e3adcf8fSFrançois Tigeot .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145, 338e3adcf8fSFrançois Tigeot .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200, 339e3adcf8fSFrançois Tigeot .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200, 340e3adcf8fSFrançois Tigeot }; 341e3adcf8fSFrançois Tigeot 342e3adcf8fSFrançois Tigeot static const struct color_conversion hdtv_csc_rgb = { 343e3adcf8fSFrançois Tigeot .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, 344e3adcf8fSFrançois Tigeot .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, 345e3adcf8fSFrançois Tigeot .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, 346e3adcf8fSFrançois Tigeot }; 347e3adcf8fSFrançois Tigeot 348e3adcf8fSFrançois Tigeot static const struct video_levels component_levels = { 349e3adcf8fSFrançois Tigeot .blank = 279, .black = 279, .burst = 0, 350e3adcf8fSFrançois Tigeot }; 351e3adcf8fSFrançois Tigeot 352e3adcf8fSFrançois Tigeot 353e3adcf8fSFrançois Tigeot struct tv_mode { 354e3adcf8fSFrançois Tigeot const char *name; 355e3adcf8fSFrançois Tigeot int clock; 356e3adcf8fSFrançois Tigeot int refresh; /* in millihertz (for precision) */ 357e3adcf8fSFrançois Tigeot u32 oversample; 358e3adcf8fSFrançois Tigeot int hsync_end, hblank_start, hblank_end, htotal; 359e3adcf8fSFrançois Tigeot bool progressive, trilevel_sync, component_only; 360e3adcf8fSFrançois Tigeot int vsync_start_f1, vsync_start_f2, vsync_len; 361e3adcf8fSFrançois Tigeot bool veq_ena; 362e3adcf8fSFrançois Tigeot int veq_start_f1, veq_start_f2, veq_len; 363e3adcf8fSFrançois Tigeot int vi_end_f1, vi_end_f2, nbr_end; 364e3adcf8fSFrançois Tigeot bool burst_ena; 365e3adcf8fSFrançois Tigeot int hburst_start, hburst_len; 366e3adcf8fSFrançois Tigeot int vburst_start_f1, vburst_end_f1; 367e3adcf8fSFrançois Tigeot int vburst_start_f2, vburst_end_f2; 368e3adcf8fSFrançois Tigeot int vburst_start_f3, vburst_end_f3; 369e3adcf8fSFrançois Tigeot int vburst_start_f4, vburst_end_f4; 370e3adcf8fSFrançois Tigeot /* 371e3adcf8fSFrançois Tigeot * subcarrier programming 372e3adcf8fSFrançois Tigeot */ 373e3adcf8fSFrançois Tigeot int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; 374e3adcf8fSFrançois Tigeot u32 sc_reset; 375e3adcf8fSFrançois Tigeot bool pal_burst; 376e3adcf8fSFrançois Tigeot /* 377e3adcf8fSFrançois Tigeot * blank/black levels 378e3adcf8fSFrançois Tigeot */ 379e3adcf8fSFrançois Tigeot const struct video_levels *composite_levels, *svideo_levels; 380e3adcf8fSFrançois Tigeot const struct color_conversion *composite_color, *svideo_color; 381e3adcf8fSFrançois Tigeot const u32 *filter_table; 382e3adcf8fSFrançois Tigeot int max_srcw; 383e3adcf8fSFrançois Tigeot }; 384e3adcf8fSFrançois Tigeot 385e3adcf8fSFrançois Tigeot 386e3adcf8fSFrançois Tigeot /* 387e3adcf8fSFrançois Tigeot * Sub carrier DDA 388e3adcf8fSFrançois Tigeot * 389e3adcf8fSFrançois Tigeot * I think this works as follows: 390e3adcf8fSFrançois Tigeot * 391e3adcf8fSFrançois Tigeot * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 392e3adcf8fSFrançois Tigeot * 393e3adcf8fSFrançois Tigeot * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value 394e3adcf8fSFrançois Tigeot * 395e3adcf8fSFrançois Tigeot * So, 396e3adcf8fSFrançois Tigeot * dda1_ideal = subcarrier/pixel * 4096 397e3adcf8fSFrançois Tigeot * dda1_inc = floor (dda1_ideal) 398e3adcf8fSFrançois Tigeot * dda2 = dda1_ideal - dda1_inc 399e3adcf8fSFrançois Tigeot * 400e3adcf8fSFrançois Tigeot * then pick a ratio for dda2 that gives the closest approximation. If 401e3adcf8fSFrançois Tigeot * you can't get close enough, you can play with dda3 as well. This 402e3adcf8fSFrançois Tigeot * seems likely to happen when dda2 is small as the jumps would be larger 403e3adcf8fSFrançois Tigeot * 404e3adcf8fSFrançois Tigeot * To invert this, 405e3adcf8fSFrançois Tigeot * 406e3adcf8fSFrançois Tigeot * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) 407e3adcf8fSFrançois Tigeot * 408e3adcf8fSFrançois Tigeot * The constants below were all computed using a 107.520MHz clock 409e3adcf8fSFrançois Tigeot */ 410e3adcf8fSFrançois Tigeot 411e3adcf8fSFrançois Tigeot /** 412e3adcf8fSFrançois Tigeot * Register programming values for TV modes. 413e3adcf8fSFrançois Tigeot * 414e3adcf8fSFrançois Tigeot * These values account for -1s required. 415e3adcf8fSFrançois Tigeot */ 416e3adcf8fSFrançois Tigeot 417e3adcf8fSFrançois Tigeot static const struct tv_mode tv_modes[] = { 418e3adcf8fSFrançois Tigeot { 419e3adcf8fSFrançois Tigeot .name = "NTSC-M", 420e3adcf8fSFrançois Tigeot .clock = 108000, 421e3adcf8fSFrançois Tigeot .refresh = 59940, 422e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 423e3adcf8fSFrançois Tigeot .component_only = 0, 424e3adcf8fSFrançois Tigeot /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 425e3adcf8fSFrançois Tigeot 426e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 124, 427e3adcf8fSFrançois Tigeot .hblank_start = 836, .htotal = 857, 428e3adcf8fSFrançois Tigeot 429e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 430e3adcf8fSFrançois Tigeot 431e3adcf8fSFrançois Tigeot .vsync_start_f1 = 6, .vsync_start_f2 = 7, 432e3adcf8fSFrançois Tigeot .vsync_len = 6, 433e3adcf8fSFrançois Tigeot 434e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 435e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 18, 436e3adcf8fSFrançois Tigeot 437e3adcf8fSFrançois Tigeot .vi_end_f1 = 20, .vi_end_f2 = 21, 438e3adcf8fSFrançois Tigeot .nbr_end = 240, 439e3adcf8fSFrançois Tigeot 440e3adcf8fSFrançois Tigeot .burst_ena = true, 441e3adcf8fSFrançois Tigeot .hburst_start = 72, .hburst_len = 34, 442e3adcf8fSFrançois Tigeot .vburst_start_f1 = 9, .vburst_end_f1 = 240, 443e3adcf8fSFrançois Tigeot .vburst_start_f2 = 10, .vburst_end_f2 = 240, 444e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 240, 445e3adcf8fSFrançois Tigeot .vburst_start_f4 = 10, .vburst_end_f4 = 240, 446e3adcf8fSFrançois Tigeot 447e3adcf8fSFrançois Tigeot /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 448e3adcf8fSFrançois Tigeot .dda1_inc = 135, 449e3adcf8fSFrançois Tigeot .dda2_inc = 20800, .dda2_size = 27456, 450e3adcf8fSFrançois Tigeot .dda3_inc = 0, .dda3_size = 0, 451e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_EVERY_4, 452e3adcf8fSFrançois Tigeot .pal_burst = false, 453e3adcf8fSFrançois Tigeot 454e3adcf8fSFrançois Tigeot .composite_levels = &ntsc_m_levels_composite, 455e3adcf8fSFrançois Tigeot .composite_color = &ntsc_m_csc_composite, 456e3adcf8fSFrançois Tigeot .svideo_levels = &ntsc_m_levels_svideo, 457e3adcf8fSFrançois Tigeot .svideo_color = &ntsc_m_csc_svideo, 458e3adcf8fSFrançois Tigeot 459e3adcf8fSFrançois Tigeot .filter_table = filter_table, 460e3adcf8fSFrançois Tigeot }, 461e3adcf8fSFrançois Tigeot { 462e3adcf8fSFrançois Tigeot .name = "NTSC-443", 463e3adcf8fSFrançois Tigeot .clock = 108000, 464e3adcf8fSFrançois Tigeot .refresh = 59940, 465e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 466e3adcf8fSFrançois Tigeot .component_only = 0, 467e3adcf8fSFrançois Tigeot /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ 468e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 124, 469e3adcf8fSFrançois Tigeot .hblank_start = 836, .htotal = 857, 470e3adcf8fSFrançois Tigeot 471e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 472e3adcf8fSFrançois Tigeot 473e3adcf8fSFrançois Tigeot .vsync_start_f1 = 6, .vsync_start_f2 = 7, 474e3adcf8fSFrançois Tigeot .vsync_len = 6, 475e3adcf8fSFrançois Tigeot 476e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 477e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 18, 478e3adcf8fSFrançois Tigeot 479e3adcf8fSFrançois Tigeot .vi_end_f1 = 20, .vi_end_f2 = 21, 480e3adcf8fSFrançois Tigeot .nbr_end = 240, 481e3adcf8fSFrançois Tigeot 482e3adcf8fSFrançois Tigeot .burst_ena = true, 483e3adcf8fSFrançois Tigeot .hburst_start = 72, .hburst_len = 34, 484e3adcf8fSFrançois Tigeot .vburst_start_f1 = 9, .vburst_end_f1 = 240, 485e3adcf8fSFrançois Tigeot .vburst_start_f2 = 10, .vburst_end_f2 = 240, 486e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 240, 487e3adcf8fSFrançois Tigeot .vburst_start_f4 = 10, .vburst_end_f4 = 240, 488e3adcf8fSFrançois Tigeot 489e3adcf8fSFrançois Tigeot /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 490e3adcf8fSFrançois Tigeot .dda1_inc = 168, 491e3adcf8fSFrançois Tigeot .dda2_inc = 4093, .dda2_size = 27456, 492e3adcf8fSFrançois Tigeot .dda3_inc = 310, .dda3_size = 525, 493e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_NEVER, 494e3adcf8fSFrançois Tigeot .pal_burst = false, 495e3adcf8fSFrançois Tigeot 496e3adcf8fSFrançois Tigeot .composite_levels = &ntsc_m_levels_composite, 497e3adcf8fSFrançois Tigeot .composite_color = &ntsc_m_csc_composite, 498e3adcf8fSFrançois Tigeot .svideo_levels = &ntsc_m_levels_svideo, 499e3adcf8fSFrançois Tigeot .svideo_color = &ntsc_m_csc_svideo, 500e3adcf8fSFrançois Tigeot 501e3adcf8fSFrançois Tigeot .filter_table = filter_table, 502e3adcf8fSFrançois Tigeot }, 503e3adcf8fSFrançois Tigeot { 504e3adcf8fSFrançois Tigeot .name = "NTSC-J", 505e3adcf8fSFrançois Tigeot .clock = 108000, 506e3adcf8fSFrançois Tigeot .refresh = 59940, 507e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 508e3adcf8fSFrançois Tigeot .component_only = 0, 509e3adcf8fSFrançois Tigeot 510e3adcf8fSFrançois Tigeot /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 511e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 124, 512e3adcf8fSFrançois Tigeot .hblank_start = 836, .htotal = 857, 513e3adcf8fSFrançois Tigeot 514e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 515e3adcf8fSFrançois Tigeot 516e3adcf8fSFrançois Tigeot .vsync_start_f1 = 6, .vsync_start_f2 = 7, 517e3adcf8fSFrançois Tigeot .vsync_len = 6, 518e3adcf8fSFrançois Tigeot 519e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 520e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 18, 521e3adcf8fSFrançois Tigeot 522e3adcf8fSFrançois Tigeot .vi_end_f1 = 20, .vi_end_f2 = 21, 523e3adcf8fSFrançois Tigeot .nbr_end = 240, 524e3adcf8fSFrançois Tigeot 525e3adcf8fSFrançois Tigeot .burst_ena = true, 526e3adcf8fSFrançois Tigeot .hburst_start = 72, .hburst_len = 34, 527e3adcf8fSFrançois Tigeot .vburst_start_f1 = 9, .vburst_end_f1 = 240, 528e3adcf8fSFrançois Tigeot .vburst_start_f2 = 10, .vburst_end_f2 = 240, 529e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 240, 530e3adcf8fSFrançois Tigeot .vburst_start_f4 = 10, .vburst_end_f4 = 240, 531e3adcf8fSFrançois Tigeot 532e3adcf8fSFrançois Tigeot /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 533e3adcf8fSFrançois Tigeot .dda1_inc = 135, 534e3adcf8fSFrançois Tigeot .dda2_inc = 20800, .dda2_size = 27456, 535e3adcf8fSFrançois Tigeot .dda3_inc = 0, .dda3_size = 0, 536e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_EVERY_4, 537e3adcf8fSFrançois Tigeot .pal_burst = false, 538e3adcf8fSFrançois Tigeot 539e3adcf8fSFrançois Tigeot .composite_levels = &ntsc_j_levels_composite, 540e3adcf8fSFrançois Tigeot .composite_color = &ntsc_j_csc_composite, 541e3adcf8fSFrançois Tigeot .svideo_levels = &ntsc_j_levels_svideo, 542e3adcf8fSFrançois Tigeot .svideo_color = &ntsc_j_csc_svideo, 543e3adcf8fSFrançois Tigeot 544e3adcf8fSFrançois Tigeot .filter_table = filter_table, 545e3adcf8fSFrançois Tigeot }, 546e3adcf8fSFrançois Tigeot { 547e3adcf8fSFrançois Tigeot .name = "PAL-M", 548e3adcf8fSFrançois Tigeot .clock = 108000, 549e3adcf8fSFrançois Tigeot .refresh = 59940, 550e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 551e3adcf8fSFrançois Tigeot .component_only = 0, 552e3adcf8fSFrançois Tigeot 553e3adcf8fSFrançois Tigeot /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 554e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 124, 555e3adcf8fSFrançois Tigeot .hblank_start = 836, .htotal = 857, 556e3adcf8fSFrançois Tigeot 557e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 558e3adcf8fSFrançois Tigeot 559e3adcf8fSFrançois Tigeot .vsync_start_f1 = 6, .vsync_start_f2 = 7, 560e3adcf8fSFrançois Tigeot .vsync_len = 6, 561e3adcf8fSFrançois Tigeot 562e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 563e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 18, 564e3adcf8fSFrançois Tigeot 565e3adcf8fSFrançois Tigeot .vi_end_f1 = 20, .vi_end_f2 = 21, 566e3adcf8fSFrançois Tigeot .nbr_end = 240, 567e3adcf8fSFrançois Tigeot 568e3adcf8fSFrançois Tigeot .burst_ena = true, 569e3adcf8fSFrançois Tigeot .hburst_start = 72, .hburst_len = 34, 570e3adcf8fSFrançois Tigeot .vburst_start_f1 = 9, .vburst_end_f1 = 240, 571e3adcf8fSFrançois Tigeot .vburst_start_f2 = 10, .vburst_end_f2 = 240, 572e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 240, 573e3adcf8fSFrançois Tigeot .vburst_start_f4 = 10, .vburst_end_f4 = 240, 574e3adcf8fSFrançois Tigeot 575e3adcf8fSFrançois Tigeot /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 576e3adcf8fSFrançois Tigeot .dda1_inc = 135, 577e3adcf8fSFrançois Tigeot .dda2_inc = 16704, .dda2_size = 27456, 578e3adcf8fSFrançois Tigeot .dda3_inc = 0, .dda3_size = 0, 579e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_EVERY_8, 580e3adcf8fSFrançois Tigeot .pal_burst = true, 581e3adcf8fSFrançois Tigeot 582e3adcf8fSFrançois Tigeot .composite_levels = &pal_m_levels_composite, 583e3adcf8fSFrançois Tigeot .composite_color = &pal_m_csc_composite, 584e3adcf8fSFrançois Tigeot .svideo_levels = &pal_m_levels_svideo, 585e3adcf8fSFrançois Tigeot .svideo_color = &pal_m_csc_svideo, 586e3adcf8fSFrançois Tigeot 587e3adcf8fSFrançois Tigeot .filter_table = filter_table, 588e3adcf8fSFrançois Tigeot }, 589e3adcf8fSFrançois Tigeot { 590e3adcf8fSFrançois Tigeot /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 591e3adcf8fSFrançois Tigeot .name = "PAL-N", 592e3adcf8fSFrançois Tigeot .clock = 108000, 593e3adcf8fSFrançois Tigeot .refresh = 50000, 594e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 595e3adcf8fSFrançois Tigeot .component_only = 0, 596e3adcf8fSFrançois Tigeot 597e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 128, 598e3adcf8fSFrançois Tigeot .hblank_start = 844, .htotal = 863, 599e3adcf8fSFrançois Tigeot 600e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 601e3adcf8fSFrançois Tigeot 602e3adcf8fSFrançois Tigeot 603e3adcf8fSFrançois Tigeot .vsync_start_f1 = 6, .vsync_start_f2 = 7, 604e3adcf8fSFrançois Tigeot .vsync_len = 6, 605e3adcf8fSFrançois Tigeot 606e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 607e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 18, 608e3adcf8fSFrançois Tigeot 609e3adcf8fSFrançois Tigeot .vi_end_f1 = 24, .vi_end_f2 = 25, 610e3adcf8fSFrançois Tigeot .nbr_end = 286, 611e3adcf8fSFrançois Tigeot 612e3adcf8fSFrançois Tigeot .burst_ena = true, 613e3adcf8fSFrançois Tigeot .hburst_start = 73, .hburst_len = 34, 614e3adcf8fSFrançois Tigeot .vburst_start_f1 = 8, .vburst_end_f1 = 285, 615e3adcf8fSFrançois Tigeot .vburst_start_f2 = 8, .vburst_end_f2 = 286, 616e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 286, 617e3adcf8fSFrançois Tigeot .vburst_start_f4 = 9, .vburst_end_f4 = 285, 618e3adcf8fSFrançois Tigeot 619e3adcf8fSFrançois Tigeot 620e3adcf8fSFrançois Tigeot /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 621e3adcf8fSFrançois Tigeot .dda1_inc = 135, 622e3adcf8fSFrançois Tigeot .dda2_inc = 23578, .dda2_size = 27648, 623e3adcf8fSFrançois Tigeot .dda3_inc = 134, .dda3_size = 625, 624e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_EVERY_8, 625e3adcf8fSFrançois Tigeot .pal_burst = true, 626e3adcf8fSFrançois Tigeot 627e3adcf8fSFrançois Tigeot .composite_levels = &pal_n_levels_composite, 628e3adcf8fSFrançois Tigeot .composite_color = &pal_n_csc_composite, 629e3adcf8fSFrançois Tigeot .svideo_levels = &pal_n_levels_svideo, 630e3adcf8fSFrançois Tigeot .svideo_color = &pal_n_csc_svideo, 631e3adcf8fSFrançois Tigeot 632e3adcf8fSFrançois Tigeot .filter_table = filter_table, 633e3adcf8fSFrançois Tigeot }, 634e3adcf8fSFrançois Tigeot { 635e3adcf8fSFrançois Tigeot /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 636e3adcf8fSFrançois Tigeot .name = "PAL", 637e3adcf8fSFrançois Tigeot .clock = 108000, 638e3adcf8fSFrançois Tigeot .refresh = 50000, 639e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_8X, 640e3adcf8fSFrançois Tigeot .component_only = 0, 641e3adcf8fSFrançois Tigeot 642e3adcf8fSFrançois Tigeot .hsync_end = 64, .hblank_end = 142, 643e3adcf8fSFrançois Tigeot .hblank_start = 844, .htotal = 863, 644e3adcf8fSFrançois Tigeot 645e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = false, 646e3adcf8fSFrançois Tigeot 647e3adcf8fSFrançois Tigeot .vsync_start_f1 = 5, .vsync_start_f2 = 6, 648e3adcf8fSFrançois Tigeot .vsync_len = 5, 649e3adcf8fSFrançois Tigeot 650e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 0, 651e3adcf8fSFrançois Tigeot .veq_start_f2 = 1, .veq_len = 15, 652e3adcf8fSFrançois Tigeot 653e3adcf8fSFrançois Tigeot .vi_end_f1 = 24, .vi_end_f2 = 25, 654e3adcf8fSFrançois Tigeot .nbr_end = 286, 655e3adcf8fSFrançois Tigeot 656e3adcf8fSFrançois Tigeot .burst_ena = true, 657e3adcf8fSFrançois Tigeot .hburst_start = 73, .hburst_len = 32, 658e3adcf8fSFrançois Tigeot .vburst_start_f1 = 8, .vburst_end_f1 = 285, 659e3adcf8fSFrançois Tigeot .vburst_start_f2 = 8, .vburst_end_f2 = 286, 660e3adcf8fSFrançois Tigeot .vburst_start_f3 = 9, .vburst_end_f3 = 286, 661e3adcf8fSFrançois Tigeot .vburst_start_f4 = 9, .vburst_end_f4 = 285, 662e3adcf8fSFrançois Tigeot 663e3adcf8fSFrançois Tigeot /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 664e3adcf8fSFrançois Tigeot .dda1_inc = 168, 665e3adcf8fSFrançois Tigeot .dda2_inc = 4122, .dda2_size = 27648, 666e3adcf8fSFrançois Tigeot .dda3_inc = 67, .dda3_size = 625, 667e3adcf8fSFrançois Tigeot .sc_reset = TV_SC_RESET_EVERY_8, 668e3adcf8fSFrançois Tigeot .pal_burst = true, 669e3adcf8fSFrançois Tigeot 670e3adcf8fSFrançois Tigeot .composite_levels = &pal_levels_composite, 671e3adcf8fSFrançois Tigeot .composite_color = &pal_csc_composite, 672e3adcf8fSFrançois Tigeot .svideo_levels = &pal_levels_svideo, 673e3adcf8fSFrançois Tigeot .svideo_color = &pal_csc_svideo, 674e3adcf8fSFrançois Tigeot 675e3adcf8fSFrançois Tigeot .filter_table = filter_table, 676e3adcf8fSFrançois Tigeot }, 677e3adcf8fSFrançois Tigeot { 678e3adcf8fSFrançois Tigeot .name = "720p@60Hz", 679e3adcf8fSFrançois Tigeot .clock = 148800, 680e3adcf8fSFrançois Tigeot .refresh = 60000, 681e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_2X, 682e3adcf8fSFrançois Tigeot .component_only = 1, 683e3adcf8fSFrançois Tigeot 684e3adcf8fSFrançois Tigeot .hsync_end = 80, .hblank_end = 300, 685e3adcf8fSFrançois Tigeot .hblank_start = 1580, .htotal = 1649, 686e3adcf8fSFrançois Tigeot 687e3adcf8fSFrançois Tigeot .progressive = true, .trilevel_sync = true, 688e3adcf8fSFrançois Tigeot 689e3adcf8fSFrançois Tigeot .vsync_start_f1 = 10, .vsync_start_f2 = 10, 690e3adcf8fSFrançois Tigeot .vsync_len = 10, 691e3adcf8fSFrançois Tigeot 692e3adcf8fSFrançois Tigeot .veq_ena = false, 693e3adcf8fSFrançois Tigeot 694e3adcf8fSFrançois Tigeot .vi_end_f1 = 29, .vi_end_f2 = 29, 695e3adcf8fSFrançois Tigeot .nbr_end = 719, 696e3adcf8fSFrançois Tigeot 697e3adcf8fSFrançois Tigeot .burst_ena = false, 698e3adcf8fSFrançois Tigeot 699e3adcf8fSFrançois Tigeot .filter_table = filter_table, 700e3adcf8fSFrançois Tigeot }, 701e3adcf8fSFrançois Tigeot { 702e3adcf8fSFrançois Tigeot .name = "720p@50Hz", 703e3adcf8fSFrançois Tigeot .clock = 148800, 704e3adcf8fSFrançois Tigeot .refresh = 50000, 705e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_2X, 706e3adcf8fSFrançois Tigeot .component_only = 1, 707e3adcf8fSFrançois Tigeot 708e3adcf8fSFrançois Tigeot .hsync_end = 80, .hblank_end = 300, 709e3adcf8fSFrançois Tigeot .hblank_start = 1580, .htotal = 1979, 710e3adcf8fSFrançois Tigeot 711e3adcf8fSFrançois Tigeot .progressive = true, .trilevel_sync = true, 712e3adcf8fSFrançois Tigeot 713e3adcf8fSFrançois Tigeot .vsync_start_f1 = 10, .vsync_start_f2 = 10, 714e3adcf8fSFrançois Tigeot .vsync_len = 10, 715e3adcf8fSFrançois Tigeot 716e3adcf8fSFrançois Tigeot .veq_ena = false, 717e3adcf8fSFrançois Tigeot 718e3adcf8fSFrançois Tigeot .vi_end_f1 = 29, .vi_end_f2 = 29, 719e3adcf8fSFrançois Tigeot .nbr_end = 719, 720e3adcf8fSFrançois Tigeot 721e3adcf8fSFrançois Tigeot .burst_ena = false, 722e3adcf8fSFrançois Tigeot 723e3adcf8fSFrançois Tigeot .filter_table = filter_table, 724e3adcf8fSFrançois Tigeot .max_srcw = 800 725e3adcf8fSFrançois Tigeot }, 726e3adcf8fSFrançois Tigeot { 727e3adcf8fSFrançois Tigeot .name = "1080i@50Hz", 728e3adcf8fSFrançois Tigeot .clock = 148800, 729e3adcf8fSFrançois Tigeot .refresh = 50000, 730e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_2X, 731e3adcf8fSFrançois Tigeot .component_only = 1, 732e3adcf8fSFrançois Tigeot 733e3adcf8fSFrançois Tigeot .hsync_end = 88, .hblank_end = 235, 734e3adcf8fSFrançois Tigeot .hblank_start = 2155, .htotal = 2639, 735e3adcf8fSFrançois Tigeot 736e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = true, 737e3adcf8fSFrançois Tigeot 738e3adcf8fSFrançois Tigeot .vsync_start_f1 = 4, .vsync_start_f2 = 5, 739e3adcf8fSFrançois Tigeot .vsync_len = 10, 740e3adcf8fSFrançois Tigeot 741e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 4, 742e3adcf8fSFrançois Tigeot .veq_start_f2 = 4, .veq_len = 10, 743e3adcf8fSFrançois Tigeot 744e3adcf8fSFrançois Tigeot 745e3adcf8fSFrançois Tigeot .vi_end_f1 = 21, .vi_end_f2 = 22, 746e3adcf8fSFrançois Tigeot .nbr_end = 539, 747e3adcf8fSFrançois Tigeot 748e3adcf8fSFrançois Tigeot .burst_ena = false, 749e3adcf8fSFrançois Tigeot 750e3adcf8fSFrançois Tigeot .filter_table = filter_table, 751e3adcf8fSFrançois Tigeot }, 752e3adcf8fSFrançois Tigeot { 753e3adcf8fSFrançois Tigeot .name = "1080i@60Hz", 754e3adcf8fSFrançois Tigeot .clock = 148800, 755e3adcf8fSFrançois Tigeot .refresh = 60000, 756e3adcf8fSFrançois Tigeot .oversample = TV_OVERSAMPLE_2X, 757e3adcf8fSFrançois Tigeot .component_only = 1, 758e3adcf8fSFrançois Tigeot 759e3adcf8fSFrançois Tigeot .hsync_end = 88, .hblank_end = 235, 760e3adcf8fSFrançois Tigeot .hblank_start = 2155, .htotal = 2199, 761e3adcf8fSFrançois Tigeot 762e3adcf8fSFrançois Tigeot .progressive = false, .trilevel_sync = true, 763e3adcf8fSFrançois Tigeot 764e3adcf8fSFrançois Tigeot .vsync_start_f1 = 4, .vsync_start_f2 = 5, 765e3adcf8fSFrançois Tigeot .vsync_len = 10, 766e3adcf8fSFrançois Tigeot 767e3adcf8fSFrançois Tigeot .veq_ena = true, .veq_start_f1 = 4, 768e3adcf8fSFrançois Tigeot .veq_start_f2 = 4, .veq_len = 10, 769e3adcf8fSFrançois Tigeot 770e3adcf8fSFrançois Tigeot 771e3adcf8fSFrançois Tigeot .vi_end_f1 = 21, .vi_end_f2 = 22, 772e3adcf8fSFrançois Tigeot .nbr_end = 539, 773e3adcf8fSFrançois Tigeot 774e3adcf8fSFrançois Tigeot .burst_ena = false, 775e3adcf8fSFrançois Tigeot 776e3adcf8fSFrançois Tigeot .filter_table = filter_table, 777e3adcf8fSFrançois Tigeot }, 778e3adcf8fSFrançois Tigeot }; 779e3adcf8fSFrançois Tigeot 780e3adcf8fSFrançois Tigeot static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder) 781e3adcf8fSFrançois Tigeot { 782e3adcf8fSFrançois Tigeot return container_of(encoder, struct intel_tv, base.base); 783e3adcf8fSFrançois Tigeot } 784e3adcf8fSFrançois Tigeot 785e3adcf8fSFrançois Tigeot static struct intel_tv *intel_attached_tv(struct drm_connector *connector) 786e3adcf8fSFrançois Tigeot { 787e3adcf8fSFrançois Tigeot return container_of(intel_attached_encoder(connector), 788e3adcf8fSFrançois Tigeot struct intel_tv, 789e3adcf8fSFrançois Tigeot base); 790e3adcf8fSFrançois Tigeot } 791e3adcf8fSFrançois Tigeot 792e3adcf8fSFrançois Tigeot static void 793e3adcf8fSFrançois Tigeot intel_tv_dpms(struct drm_encoder *encoder, int mode) 794e3adcf8fSFrançois Tigeot { 795e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 796e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 797e3adcf8fSFrançois Tigeot 798e3adcf8fSFrançois Tigeot switch (mode) { 799e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_ON: 800e3adcf8fSFrançois Tigeot I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); 801e3adcf8fSFrançois Tigeot break; 802e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_STANDBY: 803e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_SUSPEND: 804e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_OFF: 805e3adcf8fSFrançois Tigeot I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); 806e3adcf8fSFrançois Tigeot break; 807e3adcf8fSFrançois Tigeot } 808e3adcf8fSFrançois Tigeot } 809e3adcf8fSFrançois Tigeot 810e3adcf8fSFrançois Tigeot static const struct tv_mode * 811e3adcf8fSFrançois Tigeot intel_tv_mode_lookup(const char *tv_format) 812e3adcf8fSFrançois Tigeot { 813e3adcf8fSFrançois Tigeot int i; 814e3adcf8fSFrançois Tigeot 815e3adcf8fSFrançois Tigeot for (i = 0; i < sizeof(tv_modes) / sizeof(tv_modes[0]); i++) { 816e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = &tv_modes[i]; 817e3adcf8fSFrançois Tigeot 818e3adcf8fSFrançois Tigeot if (!strcmp(tv_format, tv_mode->name)) 819e3adcf8fSFrançois Tigeot return tv_mode; 820e3adcf8fSFrançois Tigeot } 821e3adcf8fSFrançois Tigeot return NULL; 822e3adcf8fSFrançois Tigeot } 823e3adcf8fSFrançois Tigeot 824e3adcf8fSFrançois Tigeot static const struct tv_mode * 825e3adcf8fSFrançois Tigeot intel_tv_mode_find(struct intel_tv *intel_tv) 826e3adcf8fSFrançois Tigeot { 827e3adcf8fSFrançois Tigeot return intel_tv_mode_lookup(intel_tv->tv_format); 828e3adcf8fSFrançois Tigeot } 829e3adcf8fSFrançois Tigeot 830e3adcf8fSFrançois Tigeot static enum drm_mode_status 831e3adcf8fSFrançois Tigeot intel_tv_mode_valid(struct drm_connector *connector, 832e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 833e3adcf8fSFrançois Tigeot { 834e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 835e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 836e3adcf8fSFrançois Tigeot 837e3adcf8fSFrançois Tigeot /* Ensure TV refresh is close to desired refresh */ 838e3adcf8fSFrançois Tigeot if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) 839e3adcf8fSFrançois Tigeot < 1000) 840e3adcf8fSFrançois Tigeot return MODE_OK; 841e3adcf8fSFrançois Tigeot 842e3adcf8fSFrançois Tigeot return MODE_CLOCK_RANGE; 843e3adcf8fSFrançois Tigeot } 844e3adcf8fSFrançois Tigeot 845e3adcf8fSFrançois Tigeot 846e3adcf8fSFrançois Tigeot static bool 8476f486c69SFrançois Tigeot intel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, 848e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 849e3adcf8fSFrançois Tigeot { 850e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 851e3adcf8fSFrançois Tigeot struct drm_mode_config *drm_config = &dev->mode_config; 852e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = enc_to_intel_tv(encoder); 853e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 854e3adcf8fSFrançois Tigeot struct drm_encoder *other_encoder; 855e3adcf8fSFrançois Tigeot 856e3adcf8fSFrançois Tigeot if (!tv_mode) 857e3adcf8fSFrançois Tigeot return false; 858e3adcf8fSFrançois Tigeot 859e3adcf8fSFrançois Tigeot /* FIXME: lock encoder list */ 860e3adcf8fSFrançois Tigeot list_for_each_entry(other_encoder, &drm_config->encoder_list, head) { 861e3adcf8fSFrançois Tigeot if (other_encoder != encoder && 862e3adcf8fSFrançois Tigeot other_encoder->crtc == encoder->crtc) 863e3adcf8fSFrançois Tigeot return false; 864e3adcf8fSFrançois Tigeot } 865e3adcf8fSFrançois Tigeot 866e3adcf8fSFrançois Tigeot adjusted_mode->clock = tv_mode->clock; 867e3adcf8fSFrançois Tigeot return true; 868e3adcf8fSFrançois Tigeot } 869e3adcf8fSFrançois Tigeot 870e3adcf8fSFrançois Tigeot static void 871e3adcf8fSFrançois Tigeot intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, 872e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 873e3adcf8fSFrançois Tigeot { 874e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 875e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 876e3adcf8fSFrançois Tigeot struct drm_crtc *crtc = encoder->crtc; 877e3adcf8fSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(crtc); 878e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = enc_to_intel_tv(encoder); 879e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 880e3adcf8fSFrançois Tigeot u32 tv_ctl; 881e3adcf8fSFrançois Tigeot u32 hctl1, hctl2, hctl3; 882e3adcf8fSFrançois Tigeot u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; 883e3adcf8fSFrançois Tigeot u32 scctl1, scctl2, scctl3; 884e3adcf8fSFrançois Tigeot int i, j; 885e3adcf8fSFrançois Tigeot const struct video_levels *video_levels; 886e3adcf8fSFrançois Tigeot const struct color_conversion *color_conversion; 887e3adcf8fSFrançois Tigeot bool burst_ena; 888e3adcf8fSFrançois Tigeot int pipe = intel_crtc->pipe; 889e3adcf8fSFrançois Tigeot 890e3adcf8fSFrançois Tigeot if (!tv_mode) 891e3adcf8fSFrançois Tigeot return; /* can't happen (mode_prepare prevents this) */ 892e3adcf8fSFrançois Tigeot 893e3adcf8fSFrançois Tigeot tv_ctl = I915_READ(TV_CTL); 894e3adcf8fSFrançois Tigeot tv_ctl &= TV_CTL_SAVE; 895e3adcf8fSFrançois Tigeot 896e3adcf8fSFrançois Tigeot switch (intel_tv->type) { 897e3adcf8fSFrançois Tigeot default: 898e3adcf8fSFrançois Tigeot case DRM_MODE_CONNECTOR_Unknown: 899e3adcf8fSFrançois Tigeot case DRM_MODE_CONNECTOR_Composite: 900e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; 901e3adcf8fSFrançois Tigeot video_levels = tv_mode->composite_levels; 902e3adcf8fSFrançois Tigeot color_conversion = tv_mode->composite_color; 903e3adcf8fSFrançois Tigeot burst_ena = tv_mode->burst_ena; 904e3adcf8fSFrançois Tigeot break; 905e3adcf8fSFrançois Tigeot case DRM_MODE_CONNECTOR_Component: 906e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_OUTPUT_COMPONENT; 907e3adcf8fSFrançois Tigeot video_levels = &component_levels; 908e3adcf8fSFrançois Tigeot if (tv_mode->burst_ena) 909e3adcf8fSFrançois Tigeot color_conversion = &sdtv_csc_yprpb; 910e3adcf8fSFrançois Tigeot else 911e3adcf8fSFrançois Tigeot color_conversion = &hdtv_csc_yprpb; 912e3adcf8fSFrançois Tigeot burst_ena = false; 913e3adcf8fSFrançois Tigeot break; 914e3adcf8fSFrançois Tigeot case DRM_MODE_CONNECTOR_SVIDEO: 915e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_OUTPUT_SVIDEO; 916e3adcf8fSFrançois Tigeot video_levels = tv_mode->svideo_levels; 917e3adcf8fSFrançois Tigeot color_conversion = tv_mode->svideo_color; 918e3adcf8fSFrançois Tigeot burst_ena = tv_mode->burst_ena; 919e3adcf8fSFrançois Tigeot break; 920e3adcf8fSFrançois Tigeot } 921e3adcf8fSFrançois Tigeot hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | 922e3adcf8fSFrançois Tigeot (tv_mode->htotal << TV_HTOTAL_SHIFT); 923e3adcf8fSFrançois Tigeot 924e3adcf8fSFrançois Tigeot hctl2 = (tv_mode->hburst_start << 16) | 925e3adcf8fSFrançois Tigeot (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); 926e3adcf8fSFrançois Tigeot 927e3adcf8fSFrançois Tigeot if (burst_ena) 928e3adcf8fSFrançois Tigeot hctl2 |= TV_BURST_ENA; 929e3adcf8fSFrançois Tigeot 930e3adcf8fSFrançois Tigeot hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | 931e3adcf8fSFrançois Tigeot (tv_mode->hblank_end << TV_HBLANK_END_SHIFT); 932e3adcf8fSFrançois Tigeot 933e3adcf8fSFrançois Tigeot vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | 934e3adcf8fSFrançois Tigeot (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | 935e3adcf8fSFrançois Tigeot (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); 936e3adcf8fSFrançois Tigeot 937e3adcf8fSFrançois Tigeot vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | 938e3adcf8fSFrançois Tigeot (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | 939e3adcf8fSFrançois Tigeot (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); 940e3adcf8fSFrançois Tigeot 941e3adcf8fSFrançois Tigeot vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | 942e3adcf8fSFrançois Tigeot (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | 943e3adcf8fSFrançois Tigeot (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); 944e3adcf8fSFrançois Tigeot 945e3adcf8fSFrançois Tigeot if (tv_mode->veq_ena) 946e3adcf8fSFrançois Tigeot vctl3 |= TV_EQUAL_ENA; 947e3adcf8fSFrançois Tigeot 948e3adcf8fSFrançois Tigeot vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | 949e3adcf8fSFrançois Tigeot (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); 950e3adcf8fSFrançois Tigeot 951e3adcf8fSFrançois Tigeot vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | 952e3adcf8fSFrançois Tigeot (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); 953e3adcf8fSFrançois Tigeot 954e3adcf8fSFrançois Tigeot vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | 955e3adcf8fSFrançois Tigeot (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); 956e3adcf8fSFrançois Tigeot 957e3adcf8fSFrançois Tigeot vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | 958e3adcf8fSFrançois Tigeot (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); 959e3adcf8fSFrançois Tigeot 960e3adcf8fSFrançois Tigeot if (intel_crtc->pipe == 1) 961e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_PIPEB_SELECT; 962e3adcf8fSFrançois Tigeot tv_ctl |= tv_mode->oversample; 963e3adcf8fSFrançois Tigeot 964e3adcf8fSFrançois Tigeot if (tv_mode->progressive) 965e3adcf8fSFrançois Tigeot tv_ctl |= TV_PROGRESSIVE; 966e3adcf8fSFrançois Tigeot if (tv_mode->trilevel_sync) 967e3adcf8fSFrançois Tigeot tv_ctl |= TV_TRILEVEL_SYNC; 968e3adcf8fSFrançois Tigeot if (tv_mode->pal_burst) 969e3adcf8fSFrançois Tigeot tv_ctl |= TV_PAL_BURST; 970e3adcf8fSFrançois Tigeot 971e3adcf8fSFrançois Tigeot scctl1 = 0; 972e3adcf8fSFrançois Tigeot if (tv_mode->dda1_inc) 973e3adcf8fSFrançois Tigeot scctl1 |= TV_SC_DDA1_EN; 974e3adcf8fSFrançois Tigeot if (tv_mode->dda2_inc) 975e3adcf8fSFrançois Tigeot scctl1 |= TV_SC_DDA2_EN; 976e3adcf8fSFrançois Tigeot if (tv_mode->dda3_inc) 977e3adcf8fSFrançois Tigeot scctl1 |= TV_SC_DDA3_EN; 978e3adcf8fSFrançois Tigeot scctl1 |= tv_mode->sc_reset; 979e3adcf8fSFrançois Tigeot if (video_levels) 980e3adcf8fSFrançois Tigeot scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; 981e3adcf8fSFrançois Tigeot scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; 982e3adcf8fSFrançois Tigeot 983e3adcf8fSFrançois Tigeot scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | 984e3adcf8fSFrançois Tigeot tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; 985e3adcf8fSFrançois Tigeot 986e3adcf8fSFrançois Tigeot scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | 987e3adcf8fSFrançois Tigeot tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; 988e3adcf8fSFrançois Tigeot 989e3adcf8fSFrançois Tigeot /* Enable two fixes for the chips that need them. */ 990e3adcf8fSFrançois Tigeot if (dev->pci_device < 0x2772) 991e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; 992e3adcf8fSFrançois Tigeot 993e3adcf8fSFrançois Tigeot I915_WRITE(TV_H_CTL_1, hctl1); 994e3adcf8fSFrançois Tigeot I915_WRITE(TV_H_CTL_2, hctl2); 995e3adcf8fSFrançois Tigeot I915_WRITE(TV_H_CTL_3, hctl3); 996e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_1, vctl1); 997e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_2, vctl2); 998e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_3, vctl3); 999e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_4, vctl4); 1000e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_5, vctl5); 1001e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_6, vctl6); 1002e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CTL_7, vctl7); 1003e3adcf8fSFrançois Tigeot I915_WRITE(TV_SC_CTL_1, scctl1); 1004e3adcf8fSFrançois Tigeot I915_WRITE(TV_SC_CTL_2, scctl2); 1005e3adcf8fSFrançois Tigeot I915_WRITE(TV_SC_CTL_3, scctl3); 1006e3adcf8fSFrançois Tigeot 1007e3adcf8fSFrançois Tigeot if (color_conversion) { 1008e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | 1009e3adcf8fSFrançois Tigeot color_conversion->gy); 1010e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) | 1011e3adcf8fSFrançois Tigeot color_conversion->ay); 1012e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | 1013e3adcf8fSFrançois Tigeot color_conversion->gu); 1014e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) | 1015e3adcf8fSFrançois Tigeot color_conversion->au); 1016e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) | 1017e3adcf8fSFrançois Tigeot color_conversion->gv); 1018e3adcf8fSFrançois Tigeot I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) | 1019e3adcf8fSFrançois Tigeot color_conversion->av); 1020e3adcf8fSFrançois Tigeot } 1021e3adcf8fSFrançois Tigeot 1022e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 4) 1023e3adcf8fSFrançois Tigeot I915_WRITE(TV_CLR_KNOBS, 0x00404000); 1024e3adcf8fSFrançois Tigeot else 1025e3adcf8fSFrançois Tigeot I915_WRITE(TV_CLR_KNOBS, 0x00606000); 1026e3adcf8fSFrançois Tigeot 1027e3adcf8fSFrançois Tigeot if (video_levels) 1028e3adcf8fSFrançois Tigeot I915_WRITE(TV_CLR_LEVEL, 1029e3adcf8fSFrançois Tigeot ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | 1030e3adcf8fSFrançois Tigeot (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); 1031e3adcf8fSFrançois Tigeot { 1032e3adcf8fSFrançois Tigeot int pipeconf_reg = PIPECONF(pipe); 1033e3adcf8fSFrançois Tigeot int dspcntr_reg = DSPCNTR(intel_crtc->plane); 1034e3adcf8fSFrançois Tigeot int pipeconf = I915_READ(pipeconf_reg); 1035e3adcf8fSFrançois Tigeot int dspcntr = I915_READ(dspcntr_reg); 1036e3adcf8fSFrançois Tigeot int dspbase_reg = DSPADDR(intel_crtc->plane); 1037e3adcf8fSFrançois Tigeot int xpos = 0x0, ypos = 0x0; 1038e3adcf8fSFrançois Tigeot unsigned int xsize, ysize; 1039e3adcf8fSFrançois Tigeot /* Pipe must be off here */ 1040e3adcf8fSFrançois Tigeot I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); 1041e3adcf8fSFrançois Tigeot /* Flush the plane changes */ 1042e3adcf8fSFrançois Tigeot I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); 1043e3adcf8fSFrançois Tigeot 1044e3adcf8fSFrançois Tigeot /* Wait for vblank for the disable to take effect */ 1045e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 1046e3adcf8fSFrançois Tigeot intel_wait_for_vblank(dev, intel_crtc->pipe); 1047e3adcf8fSFrançois Tigeot 1048e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE); 1049e3adcf8fSFrançois Tigeot /* Wait for vblank for the disable to take effect. */ 1050e3adcf8fSFrançois Tigeot intel_wait_for_pipe_off(dev, intel_crtc->pipe); 1051e3adcf8fSFrançois Tigeot 1052e3adcf8fSFrançois Tigeot /* Filter ctl must be set before TV_WIN_SIZE */ 1053e3adcf8fSFrançois Tigeot I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); 1054e3adcf8fSFrançois Tigeot xsize = tv_mode->hblank_start - tv_mode->hblank_end; 1055e3adcf8fSFrançois Tigeot if (tv_mode->progressive) 1056e3adcf8fSFrançois Tigeot ysize = tv_mode->nbr_end + 1; 1057e3adcf8fSFrançois Tigeot else 1058e3adcf8fSFrançois Tigeot ysize = 2*tv_mode->nbr_end + 1; 1059e3adcf8fSFrançois Tigeot 1060e3adcf8fSFrançois Tigeot xpos += intel_tv->margin[TV_MARGIN_LEFT]; 1061e3adcf8fSFrançois Tigeot ypos += intel_tv->margin[TV_MARGIN_TOP]; 1062e3adcf8fSFrançois Tigeot xsize -= (intel_tv->margin[TV_MARGIN_LEFT] + 1063e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_RIGHT]); 1064e3adcf8fSFrançois Tigeot ysize -= (intel_tv->margin[TV_MARGIN_TOP] + 1065e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_BOTTOM]); 1066e3adcf8fSFrançois Tigeot I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); 1067e3adcf8fSFrançois Tigeot I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize); 1068e3adcf8fSFrançois Tigeot 1069e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf); 1070e3adcf8fSFrançois Tigeot I915_WRITE(dspcntr_reg, dspcntr); 1071e3adcf8fSFrançois Tigeot /* Flush the plane changes */ 1072e3adcf8fSFrançois Tigeot I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); 1073e3adcf8fSFrançois Tigeot } 1074e3adcf8fSFrançois Tigeot 1075e3adcf8fSFrançois Tigeot j = 0; 1076e3adcf8fSFrançois Tigeot for (i = 0; i < 60; i++) 1077e3adcf8fSFrançois Tigeot I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1078e3adcf8fSFrançois Tigeot for (i = 0; i < 60; i++) 1079e3adcf8fSFrançois Tigeot I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1080e3adcf8fSFrançois Tigeot for (i = 0; i < 43; i++) 1081e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1082e3adcf8fSFrançois Tigeot for (i = 0; i < 43; i++) 1083e3adcf8fSFrançois Tigeot I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1084e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE); 1085e3adcf8fSFrançois Tigeot I915_WRITE(TV_CTL, tv_ctl); 1086e3adcf8fSFrançois Tigeot } 1087e3adcf8fSFrançois Tigeot 1088e3adcf8fSFrançois Tigeot static const struct drm_display_mode reported_modes[] = { 1089e3adcf8fSFrançois Tigeot { 1090e3adcf8fSFrançois Tigeot .name = "NTSC 480i", 1091e3adcf8fSFrançois Tigeot .clock = 107520, 1092e3adcf8fSFrançois Tigeot .hdisplay = 1280, 1093e3adcf8fSFrançois Tigeot .hsync_start = 1368, 1094e3adcf8fSFrançois Tigeot .hsync_end = 1496, 1095e3adcf8fSFrançois Tigeot .htotal = 1712, 1096e3adcf8fSFrançois Tigeot 1097e3adcf8fSFrançois Tigeot .vdisplay = 1024, 1098e3adcf8fSFrançois Tigeot .vsync_start = 1027, 1099e3adcf8fSFrançois Tigeot .vsync_end = 1034, 1100e3adcf8fSFrançois Tigeot .vtotal = 1104, 1101e3adcf8fSFrançois Tigeot .type = DRM_MODE_TYPE_DRIVER, 1102e3adcf8fSFrançois Tigeot }, 1103e3adcf8fSFrançois Tigeot }; 1104e3adcf8fSFrançois Tigeot 1105e3adcf8fSFrançois Tigeot /** 1106e3adcf8fSFrançois Tigeot * Detects TV presence by checking for load. 1107e3adcf8fSFrançois Tigeot * 1108e3adcf8fSFrançois Tigeot * Requires that the current pipe's DPLL is active. 1109e3adcf8fSFrançois Tigeot 1110e3adcf8fSFrançois Tigeot * \return true if TV is connected. 1111e3adcf8fSFrançois Tigeot * \return false if TV is disconnected. 1112e3adcf8fSFrançois Tigeot */ 1113e3adcf8fSFrançois Tigeot static int 1114e3adcf8fSFrançois Tigeot intel_tv_detect_type(struct intel_tv *intel_tv, 1115e3adcf8fSFrançois Tigeot struct drm_connector *connector) 1116e3adcf8fSFrançois Tigeot { 1117e3adcf8fSFrançois Tigeot struct drm_encoder *encoder = &intel_tv->base.base; 1118e3adcf8fSFrançois Tigeot struct drm_crtc *crtc = encoder->crtc; 1119e3adcf8fSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(crtc); 1120e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 1121e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1122e3adcf8fSFrançois Tigeot u32 tv_ctl, save_tv_ctl; 1123e3adcf8fSFrançois Tigeot u32 tv_dac, save_tv_dac; 1124e3adcf8fSFrançois Tigeot int type; 1125e3adcf8fSFrançois Tigeot 1126e3adcf8fSFrançois Tigeot /* Disable TV interrupts around load detect or we'll recurse */ 1127e3adcf8fSFrançois Tigeot if (connector->polled & DRM_CONNECTOR_POLL_HPD) { 1128e3adcf8fSFrançois Tigeot lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE); 1129e3adcf8fSFrançois Tigeot i915_disable_pipestat(dev_priv, 0, 1130e3adcf8fSFrançois Tigeot PIPE_HOTPLUG_INTERRUPT_ENABLE | 1131e3adcf8fSFrançois Tigeot PIPE_HOTPLUG_TV_INTERRUPT_ENABLE); 1132e3adcf8fSFrançois Tigeot lockmgr(&dev_priv->irq_lock, LK_RELEASE); 1133e3adcf8fSFrançois Tigeot } 1134e3adcf8fSFrançois Tigeot 1135e3adcf8fSFrançois Tigeot save_tv_dac = tv_dac = I915_READ(TV_DAC); 1136e3adcf8fSFrançois Tigeot save_tv_ctl = tv_ctl = I915_READ(TV_CTL); 1137e3adcf8fSFrançois Tigeot 1138e3adcf8fSFrançois Tigeot /* Poll for TV detection */ 1139e3adcf8fSFrançois Tigeot tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK); 1140e3adcf8fSFrançois Tigeot tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; 1141e3adcf8fSFrançois Tigeot if (intel_crtc->pipe == 1) 1142e3adcf8fSFrançois Tigeot tv_ctl |= TV_ENC_PIPEB_SELECT; 1143e3adcf8fSFrançois Tigeot else 1144e3adcf8fSFrançois Tigeot tv_ctl &= ~TV_ENC_PIPEB_SELECT; 1145e3adcf8fSFrançois Tigeot 1146e3adcf8fSFrançois Tigeot tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK); 1147e3adcf8fSFrançois Tigeot tv_dac |= (TVDAC_STATE_CHG_EN | 1148e3adcf8fSFrançois Tigeot TVDAC_A_SENSE_CTL | 1149e3adcf8fSFrançois Tigeot TVDAC_B_SENSE_CTL | 1150e3adcf8fSFrançois Tigeot TVDAC_C_SENSE_CTL | 1151e3adcf8fSFrançois Tigeot DAC_CTL_OVERRIDE | 1152e3adcf8fSFrançois Tigeot DAC_A_0_7_V | 1153e3adcf8fSFrançois Tigeot DAC_B_0_7_V | 1154e3adcf8fSFrançois Tigeot DAC_C_0_7_V); 1155e3adcf8fSFrançois Tigeot 1156e3adcf8fSFrançois Tigeot I915_WRITE(TV_CTL, tv_ctl); 1157e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, tv_dac); 1158e3adcf8fSFrançois Tigeot POSTING_READ(TV_DAC); 1159e3adcf8fSFrançois Tigeot 1160e3adcf8fSFrançois Tigeot intel_wait_for_vblank(intel_tv->base.base.dev, 1161e3adcf8fSFrançois Tigeot to_intel_crtc(intel_tv->base.base.crtc)->pipe); 1162e3adcf8fSFrançois Tigeot 1163e3adcf8fSFrançois Tigeot type = -1; 1164e3adcf8fSFrançois Tigeot tv_dac = I915_READ(TV_DAC); 1165e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); 1166e3adcf8fSFrançois Tigeot /* 1167e3adcf8fSFrançois Tigeot * A B C 1168e3adcf8fSFrançois Tigeot * 0 1 1 Composite 1169e3adcf8fSFrançois Tigeot * 1 0 X svideo 1170e3adcf8fSFrançois Tigeot * 0 0 0 Component 1171e3adcf8fSFrançois Tigeot */ 1172e3adcf8fSFrançois Tigeot if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { 1173e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Detected Composite TV connection\n"); 1174e3adcf8fSFrançois Tigeot type = DRM_MODE_CONNECTOR_Composite; 1175e3adcf8fSFrançois Tigeot } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { 1176e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Detected S-Video TV connection\n"); 1177e3adcf8fSFrançois Tigeot type = DRM_MODE_CONNECTOR_SVIDEO; 1178e3adcf8fSFrançois Tigeot } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { 1179e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Detected Component TV connection\n"); 1180e3adcf8fSFrançois Tigeot type = DRM_MODE_CONNECTOR_Component; 1181e3adcf8fSFrançois Tigeot } else { 1182e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Unrecognised TV connection\n"); 1183e3adcf8fSFrançois Tigeot type = -1; 1184e3adcf8fSFrançois Tigeot } 1185e3adcf8fSFrançois Tigeot 1186e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); 1187e3adcf8fSFrançois Tigeot I915_WRITE(TV_CTL, save_tv_ctl); 1188e3adcf8fSFrançois Tigeot 1189e3adcf8fSFrançois Tigeot /* Restore interrupt config */ 1190e3adcf8fSFrançois Tigeot if (connector->polled & DRM_CONNECTOR_POLL_HPD) { 1191e3adcf8fSFrançois Tigeot lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE); 1192e3adcf8fSFrançois Tigeot i915_enable_pipestat(dev_priv, 0, 1193e3adcf8fSFrançois Tigeot PIPE_HOTPLUG_INTERRUPT_ENABLE | 1194e3adcf8fSFrançois Tigeot PIPE_HOTPLUG_TV_INTERRUPT_ENABLE); 1195e3adcf8fSFrançois Tigeot lockmgr(&dev_priv->irq_lock, LK_RELEASE); 1196e3adcf8fSFrançois Tigeot } 1197e3adcf8fSFrançois Tigeot 1198e3adcf8fSFrançois Tigeot return type; 1199e3adcf8fSFrançois Tigeot } 1200e3adcf8fSFrançois Tigeot 1201e3adcf8fSFrançois Tigeot /* 1202e3adcf8fSFrançois Tigeot * Here we set accurate tv format according to connector type 1203e3adcf8fSFrançois Tigeot * i.e Component TV should not be assigned by NTSC or PAL 1204e3adcf8fSFrançois Tigeot */ 1205e3adcf8fSFrançois Tigeot static void intel_tv_find_better_format(struct drm_connector *connector) 1206e3adcf8fSFrançois Tigeot { 1207e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 1208e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1209e3adcf8fSFrançois Tigeot int i; 1210e3adcf8fSFrançois Tigeot 1211e3adcf8fSFrançois Tigeot if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == 1212e3adcf8fSFrançois Tigeot tv_mode->component_only) 1213e3adcf8fSFrançois Tigeot return; 1214e3adcf8fSFrançois Tigeot 1215e3adcf8fSFrançois Tigeot 1216e3adcf8fSFrançois Tigeot for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) { 1217e3adcf8fSFrançois Tigeot tv_mode = tv_modes + i; 1218e3adcf8fSFrançois Tigeot 1219e3adcf8fSFrançois Tigeot if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == 1220e3adcf8fSFrançois Tigeot tv_mode->component_only) 1221e3adcf8fSFrançois Tigeot break; 1222e3adcf8fSFrançois Tigeot } 1223e3adcf8fSFrançois Tigeot 1224e3adcf8fSFrançois Tigeot intel_tv->tv_format = tv_mode->name; 1225*b5162e19SFrançois Tigeot drm_object_property_set_value(&connector->base, 1226e3adcf8fSFrançois Tigeot connector->dev->mode_config.tv_mode_property, i); 1227e3adcf8fSFrançois Tigeot } 1228e3adcf8fSFrançois Tigeot 1229e3adcf8fSFrançois Tigeot /** 1230e3adcf8fSFrançois Tigeot * Detect the TV connection. 1231e3adcf8fSFrançois Tigeot * 1232e3adcf8fSFrançois Tigeot * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure 1233e3adcf8fSFrançois Tigeot * we have a pipe programmed in order to probe the TV. 1234e3adcf8fSFrançois Tigeot */ 1235e3adcf8fSFrançois Tigeot static enum drm_connector_status 1236e3adcf8fSFrançois Tigeot intel_tv_detect(struct drm_connector *connector, bool force) 1237e3adcf8fSFrançois Tigeot { 1238e3adcf8fSFrançois Tigeot struct drm_display_mode mode; 1239e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 1240e3adcf8fSFrançois Tigeot int type; 1241e3adcf8fSFrançois Tigeot 1242e3adcf8fSFrançois Tigeot mode = reported_modes[0]; 1243e3adcf8fSFrançois Tigeot drm_mode_set_crtcinfo(&mode, 0); 1244e3adcf8fSFrançois Tigeot 1245e3adcf8fSFrançois Tigeot if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) { 1246e3adcf8fSFrançois Tigeot type = intel_tv_detect_type(intel_tv, connector); 1247e3adcf8fSFrançois Tigeot } else if (force) { 1248e3adcf8fSFrançois Tigeot struct intel_load_detect_pipe tmp; 1249e3adcf8fSFrançois Tigeot 1250e3adcf8fSFrançois Tigeot if (intel_get_load_detect_pipe(&intel_tv->base, connector, 1251e3adcf8fSFrançois Tigeot &mode, &tmp)) { 1252e3adcf8fSFrançois Tigeot type = intel_tv_detect_type(intel_tv, connector); 1253e3adcf8fSFrançois Tigeot intel_release_load_detect_pipe(&intel_tv->base, 1254e3adcf8fSFrançois Tigeot connector, 1255e3adcf8fSFrançois Tigeot &tmp); 1256e3adcf8fSFrançois Tigeot } else 1257e3adcf8fSFrançois Tigeot return connector_status_unknown; 1258e3adcf8fSFrançois Tigeot } else 1259e3adcf8fSFrançois Tigeot return connector->status; 1260e3adcf8fSFrançois Tigeot 1261e3adcf8fSFrançois Tigeot if (type < 0) 1262e3adcf8fSFrançois Tigeot return connector_status_disconnected; 1263e3adcf8fSFrançois Tigeot 1264e3adcf8fSFrançois Tigeot intel_tv->type = type; 1265e3adcf8fSFrançois Tigeot intel_tv_find_better_format(connector); 1266e3adcf8fSFrançois Tigeot 1267e3adcf8fSFrançois Tigeot return connector_status_connected; 1268e3adcf8fSFrançois Tigeot } 1269e3adcf8fSFrançois Tigeot 1270e3adcf8fSFrançois Tigeot static const struct input_res { 1271e3adcf8fSFrançois Tigeot const char *name; 1272e3adcf8fSFrançois Tigeot int w, h; 1273e3adcf8fSFrançois Tigeot } input_res_table[] = { 1274e3adcf8fSFrançois Tigeot {"640x480", 640, 480}, 1275e3adcf8fSFrançois Tigeot {"800x600", 800, 600}, 1276e3adcf8fSFrançois Tigeot {"1024x768", 1024, 768}, 1277e3adcf8fSFrançois Tigeot {"1280x1024", 1280, 1024}, 1278e3adcf8fSFrançois Tigeot {"848x480", 848, 480}, 1279e3adcf8fSFrançois Tigeot {"1280x720", 1280, 720}, 1280e3adcf8fSFrançois Tigeot {"1920x1080", 1920, 1080}, 1281e3adcf8fSFrançois Tigeot }; 1282e3adcf8fSFrançois Tigeot 1283e3adcf8fSFrançois Tigeot /* 1284e3adcf8fSFrançois Tigeot * Chose preferred mode according to line number of TV format 1285e3adcf8fSFrançois Tigeot */ 1286e3adcf8fSFrançois Tigeot static void 1287e3adcf8fSFrançois Tigeot intel_tv_chose_preferred_modes(struct drm_connector *connector, 1288e3adcf8fSFrançois Tigeot struct drm_display_mode *mode_ptr) 1289e3adcf8fSFrançois Tigeot { 1290e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 1291e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1292e3adcf8fSFrançois Tigeot 1293e3adcf8fSFrançois Tigeot if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480) 1294e3adcf8fSFrançois Tigeot mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1295e3adcf8fSFrançois Tigeot else if (tv_mode->nbr_end > 480) { 1296e3adcf8fSFrançois Tigeot if (tv_mode->progressive == true && tv_mode->nbr_end < 720) { 1297e3adcf8fSFrançois Tigeot if (mode_ptr->vdisplay == 720) 1298e3adcf8fSFrançois Tigeot mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1299e3adcf8fSFrançois Tigeot } else if (mode_ptr->vdisplay == 1080) 1300e3adcf8fSFrançois Tigeot mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1301e3adcf8fSFrançois Tigeot } 1302e3adcf8fSFrançois Tigeot } 1303e3adcf8fSFrançois Tigeot 1304e3adcf8fSFrançois Tigeot /** 1305e3adcf8fSFrançois Tigeot * Stub get_modes function. 1306e3adcf8fSFrançois Tigeot * 1307e3adcf8fSFrançois Tigeot * This should probably return a set of fixed modes, unless we can figure out 1308e3adcf8fSFrançois Tigeot * how to probe modes off of TV connections. 1309e3adcf8fSFrançois Tigeot */ 1310e3adcf8fSFrançois Tigeot 1311e3adcf8fSFrançois Tigeot static int 1312e3adcf8fSFrançois Tigeot intel_tv_get_modes(struct drm_connector *connector) 1313e3adcf8fSFrançois Tigeot { 1314e3adcf8fSFrançois Tigeot struct drm_display_mode *mode_ptr; 1315e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 1316e3adcf8fSFrançois Tigeot const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1317e3adcf8fSFrançois Tigeot int j, count = 0; 1318e3adcf8fSFrançois Tigeot u64 tmp; 1319e3adcf8fSFrançois Tigeot 1320e3adcf8fSFrançois Tigeot for (j = 0; j < DRM_ARRAY_SIZE(input_res_table); 1321e3adcf8fSFrançois Tigeot j++) { 1322e3adcf8fSFrançois Tigeot const struct input_res *input = &input_res_table[j]; 1323e3adcf8fSFrançois Tigeot unsigned int hactive_s = input->w; 1324e3adcf8fSFrançois Tigeot unsigned int vactive_s = input->h; 1325e3adcf8fSFrançois Tigeot 1326e3adcf8fSFrançois Tigeot if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) 1327e3adcf8fSFrançois Tigeot continue; 1328e3adcf8fSFrançois Tigeot 1329e3adcf8fSFrançois Tigeot if (input->w > 1024 && (!tv_mode->progressive 1330e3adcf8fSFrançois Tigeot && !tv_mode->component_only)) 1331e3adcf8fSFrançois Tigeot continue; 1332e3adcf8fSFrançois Tigeot 1333e3adcf8fSFrançois Tigeot mode_ptr = drm_mode_create(connector->dev); 1334e3adcf8fSFrançois Tigeot if (!mode_ptr) 1335e3adcf8fSFrançois Tigeot continue; 1336e3adcf8fSFrançois Tigeot strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN); 1337e3adcf8fSFrançois Tigeot 1338e3adcf8fSFrançois Tigeot mode_ptr->hdisplay = hactive_s; 1339e3adcf8fSFrançois Tigeot mode_ptr->hsync_start = hactive_s + 1; 1340e3adcf8fSFrançois Tigeot mode_ptr->hsync_end = hactive_s + 64; 1341e3adcf8fSFrançois Tigeot if (mode_ptr->hsync_end <= mode_ptr->hsync_start) 1342e3adcf8fSFrançois Tigeot mode_ptr->hsync_end = mode_ptr->hsync_start + 1; 1343e3adcf8fSFrançois Tigeot mode_ptr->htotal = hactive_s + 96; 1344e3adcf8fSFrançois Tigeot 1345e3adcf8fSFrançois Tigeot mode_ptr->vdisplay = vactive_s; 1346e3adcf8fSFrançois Tigeot mode_ptr->vsync_start = vactive_s + 1; 1347e3adcf8fSFrançois Tigeot mode_ptr->vsync_end = vactive_s + 32; 1348e3adcf8fSFrançois Tigeot if (mode_ptr->vsync_end <= mode_ptr->vsync_start) 1349e3adcf8fSFrançois Tigeot mode_ptr->vsync_end = mode_ptr->vsync_start + 1; 1350e3adcf8fSFrançois Tigeot mode_ptr->vtotal = vactive_s + 33; 1351e3adcf8fSFrançois Tigeot 1352e3adcf8fSFrançois Tigeot tmp = (u64) tv_mode->refresh * mode_ptr->vtotal; 1353e3adcf8fSFrançois Tigeot tmp *= mode_ptr->htotal; 1354e3adcf8fSFrançois Tigeot tmp = tmp / 1000000; 1355e3adcf8fSFrançois Tigeot mode_ptr->clock = (int) tmp; 1356e3adcf8fSFrançois Tigeot 1357e3adcf8fSFrançois Tigeot mode_ptr->type = DRM_MODE_TYPE_DRIVER; 1358e3adcf8fSFrançois Tigeot intel_tv_chose_preferred_modes(connector, mode_ptr); 1359e3adcf8fSFrançois Tigeot drm_mode_probed_add(connector, mode_ptr); 1360e3adcf8fSFrançois Tigeot count++; 1361e3adcf8fSFrançois Tigeot } 1362e3adcf8fSFrançois Tigeot 1363e3adcf8fSFrançois Tigeot return count; 1364e3adcf8fSFrançois Tigeot } 1365e3adcf8fSFrançois Tigeot 1366e3adcf8fSFrançois Tigeot static void 1367e3adcf8fSFrançois Tigeot intel_tv_destroy(struct drm_connector *connector) 1368e3adcf8fSFrançois Tigeot { 1369e3adcf8fSFrançois Tigeot #if 0 1370e3adcf8fSFrançois Tigeot drm_sysfs_connector_remove(connector); 1371e3adcf8fSFrançois Tigeot #endif 1372e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 1373e3adcf8fSFrançois Tigeot drm_free(connector, DRM_MEM_KMS); 1374e3adcf8fSFrançois Tigeot } 1375e3adcf8fSFrançois Tigeot 1376e3adcf8fSFrançois Tigeot 1377e3adcf8fSFrançois Tigeot static int 1378e3adcf8fSFrançois Tigeot intel_tv_set_property(struct drm_connector *connector, struct drm_property *property, 1379e3adcf8fSFrançois Tigeot uint64_t val) 1380e3adcf8fSFrançois Tigeot { 1381e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 1382e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv = intel_attached_tv(connector); 1383e3adcf8fSFrançois Tigeot struct drm_crtc *crtc = intel_tv->base.base.crtc; 1384e3adcf8fSFrançois Tigeot int ret = 0; 1385e3adcf8fSFrançois Tigeot bool changed = false; 1386e3adcf8fSFrançois Tigeot 1387*b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, property, val); 1388e3adcf8fSFrançois Tigeot if (ret < 0) 1389e3adcf8fSFrançois Tigeot goto out; 1390e3adcf8fSFrançois Tigeot 1391e3adcf8fSFrançois Tigeot if (property == dev->mode_config.tv_left_margin_property && 1392e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_LEFT] != val) { 1393e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_LEFT] = val; 1394e3adcf8fSFrançois Tigeot changed = true; 1395e3adcf8fSFrançois Tigeot } else if (property == dev->mode_config.tv_right_margin_property && 1396e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_RIGHT] != val) { 1397e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_RIGHT] = val; 1398e3adcf8fSFrançois Tigeot changed = true; 1399e3adcf8fSFrançois Tigeot } else if (property == dev->mode_config.tv_top_margin_property && 1400e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_TOP] != val) { 1401e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_TOP] = val; 1402e3adcf8fSFrançois Tigeot changed = true; 1403e3adcf8fSFrançois Tigeot } else if (property == dev->mode_config.tv_bottom_margin_property && 1404e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_BOTTOM] != val) { 1405e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_BOTTOM] = val; 1406e3adcf8fSFrançois Tigeot changed = true; 1407e3adcf8fSFrançois Tigeot } else if (property == dev->mode_config.tv_mode_property) { 1408e3adcf8fSFrançois Tigeot if (val >= DRM_ARRAY_SIZE(tv_modes)) { 1409e3adcf8fSFrançois Tigeot ret = -EINVAL; 1410e3adcf8fSFrançois Tigeot goto out; 1411e3adcf8fSFrançois Tigeot } 1412e3adcf8fSFrançois Tigeot if (!strcmp(intel_tv->tv_format, tv_modes[val].name)) 1413e3adcf8fSFrançois Tigeot goto out; 1414e3adcf8fSFrançois Tigeot 1415e3adcf8fSFrançois Tigeot intel_tv->tv_format = tv_modes[val].name; 1416e3adcf8fSFrançois Tigeot changed = true; 1417e3adcf8fSFrançois Tigeot } else { 1418e3adcf8fSFrançois Tigeot ret = -EINVAL; 1419e3adcf8fSFrançois Tigeot goto out; 1420e3adcf8fSFrançois Tigeot } 1421e3adcf8fSFrançois Tigeot 1422e3adcf8fSFrançois Tigeot if (changed && crtc) 1423e3adcf8fSFrançois Tigeot drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, 1424e3adcf8fSFrançois Tigeot crtc->y, crtc->fb); 1425e3adcf8fSFrançois Tigeot out: 1426e3adcf8fSFrançois Tigeot return ret; 1427e3adcf8fSFrançois Tigeot } 1428e3adcf8fSFrançois Tigeot 1429e3adcf8fSFrançois Tigeot static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { 1430e3adcf8fSFrançois Tigeot .dpms = intel_tv_dpms, 1431e3adcf8fSFrançois Tigeot .mode_fixup = intel_tv_mode_fixup, 1432e3adcf8fSFrançois Tigeot .prepare = intel_encoder_prepare, 1433e3adcf8fSFrançois Tigeot .mode_set = intel_tv_mode_set, 1434e3adcf8fSFrançois Tigeot .commit = intel_encoder_commit, 1435e3adcf8fSFrançois Tigeot }; 1436e3adcf8fSFrançois Tigeot 1437e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_tv_connector_funcs = { 1438e3adcf8fSFrançois Tigeot .dpms = drm_helper_connector_dpms, 1439e3adcf8fSFrançois Tigeot .detect = intel_tv_detect, 1440e3adcf8fSFrançois Tigeot .destroy = intel_tv_destroy, 1441e3adcf8fSFrançois Tigeot .set_property = intel_tv_set_property, 1442e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 1443e3adcf8fSFrançois Tigeot }; 1444e3adcf8fSFrançois Tigeot 1445e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { 1446e3adcf8fSFrançois Tigeot .mode_valid = intel_tv_mode_valid, 1447e3adcf8fSFrançois Tigeot .get_modes = intel_tv_get_modes, 1448e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 1449e3adcf8fSFrançois Tigeot }; 1450e3adcf8fSFrançois Tigeot 1451e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_tv_enc_funcs = { 1452e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 1453e3adcf8fSFrançois Tigeot }; 1454e3adcf8fSFrançois Tigeot 1455e3adcf8fSFrançois Tigeot /* 1456e3adcf8fSFrançois Tigeot * Enumerate the child dev array parsed from VBT to check whether 1457e3adcf8fSFrançois Tigeot * the integrated TV is present. 1458e3adcf8fSFrançois Tigeot * If it is present, return 1. 1459e3adcf8fSFrançois Tigeot * If it is not present, return false. 1460e3adcf8fSFrançois Tigeot * If no child dev is parsed from VBT, it assumes that the TV is present. 1461e3adcf8fSFrançois Tigeot */ 1462e3adcf8fSFrançois Tigeot static int tv_is_present_in_vbt(struct drm_device *dev) 1463e3adcf8fSFrançois Tigeot { 1464e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1465e3adcf8fSFrançois Tigeot struct child_device_config *p_child; 1466e3adcf8fSFrançois Tigeot int i, ret; 1467e3adcf8fSFrançois Tigeot 1468e3adcf8fSFrançois Tigeot if (!dev_priv->child_dev_num) 1469e3adcf8fSFrançois Tigeot return 1; 1470e3adcf8fSFrançois Tigeot 1471e3adcf8fSFrançois Tigeot ret = 0; 1472e3adcf8fSFrançois Tigeot for (i = 0; i < dev_priv->child_dev_num; i++) { 1473e3adcf8fSFrançois Tigeot p_child = dev_priv->child_dev + i; 1474e3adcf8fSFrançois Tigeot /* 1475e3adcf8fSFrançois Tigeot * If the device type is not TV, continue. 1476e3adcf8fSFrançois Tigeot */ 1477e3adcf8fSFrançois Tigeot if (p_child->device_type != DEVICE_TYPE_INT_TV && 1478e3adcf8fSFrançois Tigeot p_child->device_type != DEVICE_TYPE_TV) 1479e3adcf8fSFrançois Tigeot continue; 1480e3adcf8fSFrançois Tigeot /* Only when the addin_offset is non-zero, it is regarded 1481e3adcf8fSFrançois Tigeot * as present. 1482e3adcf8fSFrançois Tigeot */ 1483e3adcf8fSFrançois Tigeot if (p_child->addin_offset) { 1484e3adcf8fSFrançois Tigeot ret = 1; 1485e3adcf8fSFrançois Tigeot break; 1486e3adcf8fSFrançois Tigeot } 1487e3adcf8fSFrançois Tigeot } 1488e3adcf8fSFrançois Tigeot return ret; 1489e3adcf8fSFrançois Tigeot } 1490e3adcf8fSFrançois Tigeot 1491e3adcf8fSFrançois Tigeot void 1492e3adcf8fSFrançois Tigeot intel_tv_init(struct drm_device *dev) 1493e3adcf8fSFrançois Tigeot { 1494e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1495e3adcf8fSFrançois Tigeot struct drm_connector *connector; 1496e3adcf8fSFrançois Tigeot struct intel_tv *intel_tv; 1497e3adcf8fSFrançois Tigeot struct intel_encoder *intel_encoder; 1498e3adcf8fSFrançois Tigeot struct intel_connector *intel_connector; 1499e3adcf8fSFrançois Tigeot u32 tv_dac_on, tv_dac_off, save_tv_dac; 1500e3adcf8fSFrançois Tigeot char *tv_format_names[DRM_ARRAY_SIZE(tv_modes)]; 1501e3adcf8fSFrançois Tigeot int i, initial_mode = 0; 1502e3adcf8fSFrançois Tigeot 1503e3adcf8fSFrançois Tigeot if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) 1504e3adcf8fSFrançois Tigeot return; 1505e3adcf8fSFrançois Tigeot 1506e3adcf8fSFrançois Tigeot if (!tv_is_present_in_vbt(dev)) { 1507e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Integrated TV is not present.\n"); 1508e3adcf8fSFrançois Tigeot return; 1509e3adcf8fSFrançois Tigeot } 1510e3adcf8fSFrançois Tigeot /* Even if we have an encoder we may not have a connector */ 1511e3adcf8fSFrançois Tigeot if (!dev_priv->int_tv_support) 1512e3adcf8fSFrançois Tigeot return; 1513e3adcf8fSFrançois Tigeot 1514e3adcf8fSFrançois Tigeot /* 1515e3adcf8fSFrançois Tigeot * Sanity check the TV output by checking to see if the 1516e3adcf8fSFrançois Tigeot * DAC register holds a value 1517e3adcf8fSFrançois Tigeot */ 1518e3adcf8fSFrançois Tigeot save_tv_dac = I915_READ(TV_DAC); 1519e3adcf8fSFrançois Tigeot 1520e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN); 1521e3adcf8fSFrançois Tigeot tv_dac_on = I915_READ(TV_DAC); 1522e3adcf8fSFrançois Tigeot 1523e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); 1524e3adcf8fSFrançois Tigeot tv_dac_off = I915_READ(TV_DAC); 1525e3adcf8fSFrançois Tigeot 1526e3adcf8fSFrançois Tigeot I915_WRITE(TV_DAC, save_tv_dac); 1527e3adcf8fSFrançois Tigeot 1528e3adcf8fSFrançois Tigeot /* 1529e3adcf8fSFrançois Tigeot * If the register does not hold the state change enable 1530e3adcf8fSFrançois Tigeot * bit, (either as a 0 or a 1), assume it doesn't really 1531e3adcf8fSFrançois Tigeot * exist 1532e3adcf8fSFrançois Tigeot */ 1533e3adcf8fSFrançois Tigeot if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 || 1534e3adcf8fSFrançois Tigeot (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) 1535e3adcf8fSFrançois Tigeot return; 1536e3adcf8fSFrançois Tigeot 1537e3adcf8fSFrançois Tigeot intel_tv = kmalloc(sizeof(struct intel_tv), DRM_MEM_KMS, 1538e3adcf8fSFrançois Tigeot M_WAITOK | M_ZERO); 1539e3adcf8fSFrançois Tigeot intel_connector = kmalloc(sizeof(struct intel_connector), DRM_MEM_KMS, 1540e3adcf8fSFrançois Tigeot M_WAITOK | M_ZERO); 1541e3adcf8fSFrançois Tigeot 1542e3adcf8fSFrançois Tigeot intel_encoder = &intel_tv->base; 1543e3adcf8fSFrançois Tigeot connector = &intel_connector->base; 1544e3adcf8fSFrançois Tigeot 1545e3adcf8fSFrançois Tigeot /* The documentation, for the older chipsets at least, recommend 1546e3adcf8fSFrançois Tigeot * using a polling method rather than hotplug detection for TVs. 1547e3adcf8fSFrançois Tigeot * This is because in order to perform the hotplug detection, the PLLs 1548e3adcf8fSFrançois Tigeot * for the TV must be kept alive increasing power drain and starving 1549e3adcf8fSFrançois Tigeot * bandwidth from other encoders. Notably for instance, it causes 1550e3adcf8fSFrançois Tigeot * pipe underruns on Crestline when this encoder is supposedly idle. 1551e3adcf8fSFrançois Tigeot * 1552e3adcf8fSFrançois Tigeot * More recent chipsets favour HDMI rather than integrated S-Video. 1553e3adcf8fSFrançois Tigeot */ 1554e3adcf8fSFrançois Tigeot connector->polled = DRM_CONNECTOR_POLL_CONNECT; 1555e3adcf8fSFrançois Tigeot 1556e3adcf8fSFrançois Tigeot drm_connector_init(dev, connector, &intel_tv_connector_funcs, 1557e3adcf8fSFrançois Tigeot DRM_MODE_CONNECTOR_SVIDEO); 1558e3adcf8fSFrançois Tigeot 1559e3adcf8fSFrançois Tigeot drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, 1560e3adcf8fSFrançois Tigeot DRM_MODE_ENCODER_TVDAC); 1561e3adcf8fSFrançois Tigeot 1562e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, intel_encoder); 1563e3adcf8fSFrançois Tigeot intel_encoder->type = INTEL_OUTPUT_TVOUT; 1564e3adcf8fSFrançois Tigeot intel_encoder->crtc_mask = (1 << 0) | (1 << 1); 1565e3adcf8fSFrançois Tigeot intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT); 1566e3adcf8fSFrançois Tigeot intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1)); 1567e3adcf8fSFrançois Tigeot intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT); 1568e3adcf8fSFrançois Tigeot intel_tv->type = DRM_MODE_CONNECTOR_Unknown; 1569e3adcf8fSFrançois Tigeot 1570e3adcf8fSFrançois Tigeot /* BIOS margin values */ 1571e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_LEFT] = 54; 1572e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_TOP] = 36; 1573e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_RIGHT] = 46; 1574e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_BOTTOM] = 37; 1575e3adcf8fSFrançois Tigeot 1576e3adcf8fSFrançois Tigeot intel_tv->tv_format = tv_modes[initial_mode].name; 1577e3adcf8fSFrançois Tigeot 1578e3adcf8fSFrançois Tigeot drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs); 1579e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs); 1580e3adcf8fSFrançois Tigeot connector->interlace_allowed = false; 1581e3adcf8fSFrançois Tigeot connector->doublescan_allowed = false; 1582e3adcf8fSFrançois Tigeot 1583e3adcf8fSFrançois Tigeot /* Create TV properties then attach current values */ 1584e3adcf8fSFrançois Tigeot for (i = 0; i < DRM_ARRAY_SIZE(tv_modes); i++) 1585e3adcf8fSFrançois Tigeot tv_format_names[i] = __DECONST(char *, tv_modes[i].name); 1586e3adcf8fSFrançois Tigeot drm_mode_create_tv_properties(dev, 1587e3adcf8fSFrançois Tigeot DRM_ARRAY_SIZE(tv_modes), 1588e3adcf8fSFrançois Tigeot tv_format_names); 1589e3adcf8fSFrançois Tigeot 1590*b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property, 1591e3adcf8fSFrançois Tigeot initial_mode); 1592*b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 1593e3adcf8fSFrançois Tigeot dev->mode_config.tv_left_margin_property, 1594e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_LEFT]); 1595*b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 1596e3adcf8fSFrançois Tigeot dev->mode_config.tv_top_margin_property, 1597e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_TOP]); 1598*b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 1599e3adcf8fSFrançois Tigeot dev->mode_config.tv_right_margin_property, 1600e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_RIGHT]); 1601*b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 1602e3adcf8fSFrançois Tigeot dev->mode_config.tv_bottom_margin_property, 1603e3adcf8fSFrançois Tigeot intel_tv->margin[TV_MARGIN_BOTTOM]); 1604e3adcf8fSFrançois Tigeot #if 0 1605e3adcf8fSFrançois Tigeot drm_sysfs_connector_add(connector); 1606e3adcf8fSFrançois Tigeot #endif 1607e3adcf8fSFrançois Tigeot } 1608