1235783Skib/* 2235783Skib * Copyright �� 2006-2008 Intel Corporation 3235783Skib * Jesse Barnes <jesse.barnes@intel.com> 4235783Skib * 5235783Skib * Permission is hereby granted, free of charge, to any person obtaining a 6235783Skib * copy of this software and associated documentation files (the "Software"), 7235783Skib * to deal in the Software without restriction, including without limitation 8235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9235783Skib * and/or sell copies of the Software, and to permit persons to whom the 10235783Skib * Software is furnished to do so, subject to the following conditions: 11235783Skib * 12235783Skib * The above copyright notice and this permission notice (including the next 13235783Skib * paragraph) shall be included in all copies or substantial portions of the 14235783Skib * Software. 15235783Skib * 16235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19235783Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20235783Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21235783Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22235783Skib * DEALINGS IN THE SOFTWARE. 23235783Skib * 24235783Skib * Authors: 25235783Skib * Eric Anholt <eric@anholt.net> 26235783Skib * 27235783Skib */ 28235783Skib 29235783Skib/** @file 30235783Skib * Integrated TV-out support for the 915GM and 945GM. 31235783Skib */ 32235783Skib 33235783Skib#include <sys/cdefs.h> 34235783Skib__FBSDID("$FreeBSD$"); 35235783Skib 36235783Skib#include <dev/drm2/drmP.h> 37235783Skib#include <dev/drm2/drm_crtc.h> 38235783Skib#include <dev/drm2/drm_edid.h> 39296548Sdumbbell#include <dev/drm2/i915/intel_drv.h> 40235783Skib#include <dev/drm2/i915/i915_drm.h> 41235783Skib#include <dev/drm2/i915/i915_drv.h> 42235783Skib 43235783Skibenum tv_margin { 44235783Skib TV_MARGIN_LEFT, TV_MARGIN_TOP, 45235783Skib TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM 46235783Skib}; 47235783Skib 48235783Skib/** Private structure for the integrated TV support */ 49235783Skibstruct intel_tv { 50235783Skib struct intel_encoder base; 51235783Skib 52235783Skib int type; 53235783Skib const char *tv_format; 54235783Skib int margin[4]; 55235783Skib u32 save_TV_H_CTL_1; 56235783Skib u32 save_TV_H_CTL_2; 57235783Skib u32 save_TV_H_CTL_3; 58235783Skib u32 save_TV_V_CTL_1; 59235783Skib u32 save_TV_V_CTL_2; 60235783Skib u32 save_TV_V_CTL_3; 61235783Skib u32 save_TV_V_CTL_4; 62235783Skib u32 save_TV_V_CTL_5; 63235783Skib u32 save_TV_V_CTL_6; 64235783Skib u32 save_TV_V_CTL_7; 65235783Skib u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; 66235783Skib 67235783Skib u32 save_TV_CSC_Y; 68235783Skib u32 save_TV_CSC_Y2; 69235783Skib u32 save_TV_CSC_U; 70235783Skib u32 save_TV_CSC_U2; 71235783Skib u32 save_TV_CSC_V; 72235783Skib u32 save_TV_CSC_V2; 73235783Skib u32 save_TV_CLR_KNOBS; 74235783Skib u32 save_TV_CLR_LEVEL; 75235783Skib u32 save_TV_WIN_POS; 76235783Skib u32 save_TV_WIN_SIZE; 77235783Skib u32 save_TV_FILTER_CTL_1; 78235783Skib u32 save_TV_FILTER_CTL_2; 79235783Skib u32 save_TV_FILTER_CTL_3; 80235783Skib 81235783Skib u32 save_TV_H_LUMA[60]; 82235783Skib u32 save_TV_H_CHROMA[60]; 83235783Skib u32 save_TV_V_LUMA[43]; 84235783Skib u32 save_TV_V_CHROMA[43]; 85235783Skib 86235783Skib u32 save_TV_DAC; 87235783Skib u32 save_TV_CTL; 88235783Skib}; 89235783Skib 90235783Skibstruct video_levels { 91235783Skib int blank, black, burst; 92235783Skib}; 93235783Skib 94235783Skibstruct color_conversion { 95235783Skib u16 ry, gy, by, ay; 96235783Skib u16 ru, gu, bu, au; 97235783Skib u16 rv, gv, bv, av; 98235783Skib}; 99235783Skib 100235783Skibstatic const u32 filter_table[] = { 101235783Skib 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 102235783Skib 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 103235783Skib 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 104235783Skib 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 105235783Skib 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 106235783Skib 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 107235783Skib 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 108235783Skib 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 109235783Skib 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 110235783Skib 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 111235783Skib 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 112235783Skib 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 113235783Skib 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 114235783Skib 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 115235783Skib 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 116235783Skib 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 117235783Skib 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 118235783Skib 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 119235783Skib 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 120235783Skib 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 121235783Skib 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 122235783Skib 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 123235783Skib 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 124235783Skib 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 125235783Skib 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 126235783Skib 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 127235783Skib 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 128235783Skib 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 129235783Skib 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 130235783Skib 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 131235783Skib 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0, 132235783Skib 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 133235783Skib 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 134235783Skib 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 135235783Skib 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 136235783Skib 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 137235783Skib 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 138235783Skib 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 139235783Skib 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 140235783Skib 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 141235783Skib 0x28003100, 0x28002F00, 0x00003100, 0x36403000, 142235783Skib 0x2D002CC0, 0x30003640, 0x2D0036C0, 143235783Skib 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 144235783Skib 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 145235783Skib 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 146235783Skib 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 147235783Skib 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 148235783Skib 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 149235783Skib 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 150235783Skib 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 151235783Skib 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 152235783Skib 0x28003100, 0x28002F00, 0x00003100, 153235783Skib}; 154235783Skib 155235783Skib/* 156235783Skib * Color conversion values have 3 separate fixed point formats: 157235783Skib * 158235783Skib * 10 bit fields (ay, au) 159235783Skib * 1.9 fixed point (b.bbbbbbbbb) 160235783Skib * 11 bit fields (ry, by, ru, gu, gv) 161235783Skib * exp.mantissa (ee.mmmmmmmmm) 162235783Skib * ee = 00 = 10^-1 (0.mmmmmmmmm) 163235783Skib * ee = 01 = 10^-2 (0.0mmmmmmmmm) 164235783Skib * ee = 10 = 10^-3 (0.00mmmmmmmmm) 165235783Skib * ee = 11 = 10^-4 (0.000mmmmmmmmm) 166235783Skib * 12 bit fields (gy, rv, bu) 167235783Skib * exp.mantissa (eee.mmmmmmmmm) 168235783Skib * eee = 000 = 10^-1 (0.mmmmmmmmm) 169235783Skib * eee = 001 = 10^-2 (0.0mmmmmmmmm) 170235783Skib * eee = 010 = 10^-3 (0.00mmmmmmmmm) 171235783Skib * eee = 011 = 10^-4 (0.000mmmmmmmmm) 172235783Skib * eee = 100 = reserved 173235783Skib * eee = 101 = reserved 174235783Skib * eee = 110 = reserved 175235783Skib * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation) 176235783Skib * 177235783Skib * Saturation and contrast are 8 bits, with their own representation: 178235783Skib * 8 bit field (saturation, contrast) 179235783Skib * exp.mantissa (ee.mmmmmm) 180235783Skib * ee = 00 = 10^-1 (0.mmmmmm) 181235783Skib * ee = 01 = 10^0 (m.mmmmm) 182235783Skib * ee = 10 = 10^1 (mm.mmmm) 183235783Skib * ee = 11 = 10^2 (mmm.mmm) 184235783Skib * 185235783Skib * Simple conversion function: 186235783Skib * 187235783Skib * static u32 188235783Skib * float_to_csc_11(float f) 189235783Skib * { 190235783Skib * u32 exp; 191235783Skib * u32 mant; 192235783Skib * u32 ret; 193235783Skib * 194235783Skib * if (f < 0) 195235783Skib * f = -f; 196235783Skib * 197235783Skib * if (f >= 1) { 198235783Skib * exp = 0x7; 199235783Skib * mant = 1 << 8; 200235783Skib * } else { 201235783Skib * for (exp = 0; exp < 3 && f < 0.5; exp++) 202235783Skib * f *= 2.0; 203235783Skib * mant = (f * (1 << 9) + 0.5); 204235783Skib * if (mant >= (1 << 9)) 205235783Skib * mant = (1 << 9) - 1; 206235783Skib * } 207235783Skib * ret = (exp << 9) | mant; 208235783Skib * return ret; 209235783Skib * } 210235783Skib */ 211235783Skib 212235783Skib/* 213235783Skib * Behold, magic numbers! If we plant them they might grow a big 214235783Skib * s-video cable to the sky... or something. 215235783Skib * 216235783Skib * Pre-converted to appropriate hex value. 217235783Skib */ 218235783Skib 219235783Skib/* 220235783Skib * PAL & NTSC values for composite & s-video connections 221235783Skib */ 222235783Skibstatic const struct color_conversion ntsc_m_csc_composite = { 223235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 224235783Skib .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 225235783Skib .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 226235783Skib}; 227235783Skib 228235783Skibstatic const struct video_levels ntsc_m_levels_composite = { 229235783Skib .blank = 225, .black = 267, .burst = 113, 230235783Skib}; 231235783Skib 232235783Skibstatic const struct color_conversion ntsc_m_csc_svideo = { 233235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 234235783Skib .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 235235783Skib .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 236235783Skib}; 237235783Skib 238235783Skibstatic const struct video_levels ntsc_m_levels_svideo = { 239235783Skib .blank = 266, .black = 316, .burst = 133, 240235783Skib}; 241235783Skib 242235783Skibstatic const struct color_conversion ntsc_j_csc_composite = { 243235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119, 244235783Skib .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200, 245235783Skib .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200, 246235783Skib}; 247235783Skib 248235783Skibstatic const struct video_levels ntsc_j_levels_composite = { 249235783Skib .blank = 225, .black = 225, .burst = 113, 250235783Skib}; 251235783Skib 252235783Skibstatic const struct color_conversion ntsc_j_csc_svideo = { 253235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c, 254235783Skib .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200, 255235783Skib .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200, 256235783Skib}; 257235783Skib 258235783Skibstatic const struct video_levels ntsc_j_levels_svideo = { 259235783Skib .blank = 266, .black = 266, .burst = 133, 260235783Skib}; 261235783Skib 262235783Skibstatic const struct color_conversion pal_csc_composite = { 263235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113, 264235783Skib .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200, 265235783Skib .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200, 266235783Skib}; 267235783Skib 268235783Skibstatic const struct video_levels pal_levels_composite = { 269235783Skib .blank = 237, .black = 237, .burst = 118, 270235783Skib}; 271235783Skib 272235783Skibstatic const struct color_conversion pal_csc_svideo = { 273235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, 274235783Skib .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200, 275235783Skib .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200, 276235783Skib}; 277235783Skib 278235783Skibstatic const struct video_levels pal_levels_svideo = { 279235783Skib .blank = 280, .black = 280, .burst = 139, 280235783Skib}; 281235783Skib 282235783Skibstatic const struct color_conversion pal_m_csc_composite = { 283235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 284235783Skib .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 285235783Skib .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 286235783Skib}; 287235783Skib 288235783Skibstatic const struct video_levels pal_m_levels_composite = { 289235783Skib .blank = 225, .black = 267, .burst = 113, 290235783Skib}; 291235783Skib 292235783Skibstatic const struct color_conversion pal_m_csc_svideo = { 293235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 294235783Skib .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 295235783Skib .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 296235783Skib}; 297235783Skib 298235783Skibstatic const struct video_levels pal_m_levels_svideo = { 299235783Skib .blank = 266, .black = 316, .burst = 133, 300235783Skib}; 301235783Skib 302235783Skibstatic const struct color_conversion pal_n_csc_composite = { 303235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, 304235783Skib .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200, 305235783Skib .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200, 306235783Skib}; 307235783Skib 308235783Skibstatic const struct video_levels pal_n_levels_composite = { 309235783Skib .blank = 225, .black = 267, .burst = 118, 310235783Skib}; 311235783Skib 312235783Skibstatic const struct color_conversion pal_n_csc_svideo = { 313235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133, 314235783Skib .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200, 315235783Skib .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200, 316235783Skib}; 317235783Skib 318235783Skibstatic const struct video_levels pal_n_levels_svideo = { 319235783Skib .blank = 266, .black = 316, .burst = 139, 320235783Skib}; 321235783Skib 322235783Skib/* 323235783Skib * Component connections 324235783Skib */ 325235783Skibstatic const struct color_conversion sdtv_csc_yprpb = { 326235783Skib .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, 327235783Skib .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200, 328235783Skib .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200, 329235783Skib}; 330235783Skib 331235783Skibstatic const struct color_conversion sdtv_csc_rgb = { 332235783Skib .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, 333235783Skib .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, 334235783Skib .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, 335235783Skib}; 336235783Skib 337235783Skibstatic const struct color_conversion hdtv_csc_yprpb = { 338235783Skib .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145, 339235783Skib .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200, 340235783Skib .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200, 341235783Skib}; 342235783Skib 343235783Skibstatic const struct color_conversion hdtv_csc_rgb = { 344235783Skib .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, 345235783Skib .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, 346235783Skib .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, 347235783Skib}; 348235783Skib 349235783Skibstatic const struct video_levels component_levels = { 350235783Skib .blank = 279, .black = 279, .burst = 0, 351235783Skib}; 352235783Skib 353235783Skib 354235783Skibstruct tv_mode { 355235783Skib const char *name; 356235783Skib int clock; 357235783Skib int refresh; /* in millihertz (for precision) */ 358235783Skib u32 oversample; 359235783Skib int hsync_end, hblank_start, hblank_end, htotal; 360235783Skib bool progressive, trilevel_sync, component_only; 361235783Skib int vsync_start_f1, vsync_start_f2, vsync_len; 362235783Skib bool veq_ena; 363235783Skib int veq_start_f1, veq_start_f2, veq_len; 364235783Skib int vi_end_f1, vi_end_f2, nbr_end; 365235783Skib bool burst_ena; 366235783Skib int hburst_start, hburst_len; 367235783Skib int vburst_start_f1, vburst_end_f1; 368235783Skib int vburst_start_f2, vburst_end_f2; 369235783Skib int vburst_start_f3, vburst_end_f3; 370235783Skib int vburst_start_f4, vburst_end_f4; 371235783Skib /* 372235783Skib * subcarrier programming 373235783Skib */ 374235783Skib int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; 375235783Skib u32 sc_reset; 376235783Skib bool pal_burst; 377235783Skib /* 378235783Skib * blank/black levels 379235783Skib */ 380235783Skib const struct video_levels *composite_levels, *svideo_levels; 381235783Skib const struct color_conversion *composite_color, *svideo_color; 382235783Skib const u32 *filter_table; 383235783Skib int max_srcw; 384235783Skib}; 385235783Skib 386235783Skib 387235783Skib/* 388235783Skib * Sub carrier DDA 389235783Skib * 390235783Skib * I think this works as follows: 391235783Skib * 392235783Skib * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 393235783Skib * 394235783Skib * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value 395235783Skib * 396235783Skib * So, 397235783Skib * dda1_ideal = subcarrier/pixel * 4096 398235783Skib * dda1_inc = floor (dda1_ideal) 399235783Skib * dda2 = dda1_ideal - dda1_inc 400235783Skib * 401235783Skib * then pick a ratio for dda2 that gives the closest approximation. If 402235783Skib * you can't get close enough, you can play with dda3 as well. This 403235783Skib * seems likely to happen when dda2 is small as the jumps would be larger 404235783Skib * 405235783Skib * To invert this, 406235783Skib * 407235783Skib * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) 408235783Skib * 409235783Skib * The constants below were all computed using a 107.520MHz clock 410235783Skib */ 411235783Skib 412235783Skib/** 413235783Skib * Register programming values for TV modes. 414235783Skib * 415235783Skib * These values account for -1s required. 416235783Skib */ 417235783Skib 418235783Skibstatic const struct tv_mode tv_modes[] = { 419235783Skib { 420235783Skib .name = "NTSC-M", 421235783Skib .clock = 108000, 422235783Skib .refresh = 59940, 423235783Skib .oversample = TV_OVERSAMPLE_8X, 424235783Skib .component_only = 0, 425235783Skib /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 426235783Skib 427235783Skib .hsync_end = 64, .hblank_end = 124, 428235783Skib .hblank_start = 836, .htotal = 857, 429235783Skib 430235783Skib .progressive = false, .trilevel_sync = false, 431235783Skib 432235783Skib .vsync_start_f1 = 6, .vsync_start_f2 = 7, 433235783Skib .vsync_len = 6, 434235783Skib 435235783Skib .veq_ena = true, .veq_start_f1 = 0, 436235783Skib .veq_start_f2 = 1, .veq_len = 18, 437235783Skib 438235783Skib .vi_end_f1 = 20, .vi_end_f2 = 21, 439235783Skib .nbr_end = 240, 440235783Skib 441235783Skib .burst_ena = true, 442235783Skib .hburst_start = 72, .hburst_len = 34, 443235783Skib .vburst_start_f1 = 9, .vburst_end_f1 = 240, 444235783Skib .vburst_start_f2 = 10, .vburst_end_f2 = 240, 445235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 240, 446235783Skib .vburst_start_f4 = 10, .vburst_end_f4 = 240, 447235783Skib 448235783Skib /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 449235783Skib .dda1_inc = 135, 450235783Skib .dda2_inc = 20800, .dda2_size = 27456, 451235783Skib .dda3_inc = 0, .dda3_size = 0, 452235783Skib .sc_reset = TV_SC_RESET_EVERY_4, 453235783Skib .pal_burst = false, 454235783Skib 455235783Skib .composite_levels = &ntsc_m_levels_composite, 456235783Skib .composite_color = &ntsc_m_csc_composite, 457235783Skib .svideo_levels = &ntsc_m_levels_svideo, 458235783Skib .svideo_color = &ntsc_m_csc_svideo, 459235783Skib 460235783Skib .filter_table = filter_table, 461235783Skib }, 462235783Skib { 463235783Skib .name = "NTSC-443", 464235783Skib .clock = 108000, 465235783Skib .refresh = 59940, 466235783Skib .oversample = TV_OVERSAMPLE_8X, 467235783Skib .component_only = 0, 468235783Skib /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ 469235783Skib .hsync_end = 64, .hblank_end = 124, 470235783Skib .hblank_start = 836, .htotal = 857, 471235783Skib 472235783Skib .progressive = false, .trilevel_sync = false, 473235783Skib 474235783Skib .vsync_start_f1 = 6, .vsync_start_f2 = 7, 475235783Skib .vsync_len = 6, 476235783Skib 477235783Skib .veq_ena = true, .veq_start_f1 = 0, 478235783Skib .veq_start_f2 = 1, .veq_len = 18, 479235783Skib 480235783Skib .vi_end_f1 = 20, .vi_end_f2 = 21, 481235783Skib .nbr_end = 240, 482235783Skib 483235783Skib .burst_ena = true, 484235783Skib .hburst_start = 72, .hburst_len = 34, 485235783Skib .vburst_start_f1 = 9, .vburst_end_f1 = 240, 486235783Skib .vburst_start_f2 = 10, .vburst_end_f2 = 240, 487235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 240, 488235783Skib .vburst_start_f4 = 10, .vburst_end_f4 = 240, 489235783Skib 490235783Skib /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 491235783Skib .dda1_inc = 168, 492235783Skib .dda2_inc = 4093, .dda2_size = 27456, 493235783Skib .dda3_inc = 310, .dda3_size = 525, 494235783Skib .sc_reset = TV_SC_RESET_NEVER, 495235783Skib .pal_burst = false, 496235783Skib 497235783Skib .composite_levels = &ntsc_m_levels_composite, 498235783Skib .composite_color = &ntsc_m_csc_composite, 499235783Skib .svideo_levels = &ntsc_m_levels_svideo, 500235783Skib .svideo_color = &ntsc_m_csc_svideo, 501235783Skib 502235783Skib .filter_table = filter_table, 503235783Skib }, 504235783Skib { 505235783Skib .name = "NTSC-J", 506235783Skib .clock = 108000, 507235783Skib .refresh = 59940, 508235783Skib .oversample = TV_OVERSAMPLE_8X, 509235783Skib .component_only = 0, 510235783Skib 511235783Skib /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 512235783Skib .hsync_end = 64, .hblank_end = 124, 513235783Skib .hblank_start = 836, .htotal = 857, 514235783Skib 515235783Skib .progressive = false, .trilevel_sync = false, 516235783Skib 517235783Skib .vsync_start_f1 = 6, .vsync_start_f2 = 7, 518235783Skib .vsync_len = 6, 519235783Skib 520235783Skib .veq_ena = true, .veq_start_f1 = 0, 521235783Skib .veq_start_f2 = 1, .veq_len = 18, 522235783Skib 523235783Skib .vi_end_f1 = 20, .vi_end_f2 = 21, 524235783Skib .nbr_end = 240, 525235783Skib 526235783Skib .burst_ena = true, 527235783Skib .hburst_start = 72, .hburst_len = 34, 528235783Skib .vburst_start_f1 = 9, .vburst_end_f1 = 240, 529235783Skib .vburst_start_f2 = 10, .vburst_end_f2 = 240, 530235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 240, 531235783Skib .vburst_start_f4 = 10, .vburst_end_f4 = 240, 532235783Skib 533235783Skib /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 534235783Skib .dda1_inc = 135, 535235783Skib .dda2_inc = 20800, .dda2_size = 27456, 536235783Skib .dda3_inc = 0, .dda3_size = 0, 537235783Skib .sc_reset = TV_SC_RESET_EVERY_4, 538235783Skib .pal_burst = false, 539235783Skib 540235783Skib .composite_levels = &ntsc_j_levels_composite, 541235783Skib .composite_color = &ntsc_j_csc_composite, 542235783Skib .svideo_levels = &ntsc_j_levels_svideo, 543235783Skib .svideo_color = &ntsc_j_csc_svideo, 544235783Skib 545235783Skib .filter_table = filter_table, 546235783Skib }, 547235783Skib { 548235783Skib .name = "PAL-M", 549235783Skib .clock = 108000, 550235783Skib .refresh = 59940, 551235783Skib .oversample = TV_OVERSAMPLE_8X, 552235783Skib .component_only = 0, 553235783Skib 554235783Skib /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 555235783Skib .hsync_end = 64, .hblank_end = 124, 556235783Skib .hblank_start = 836, .htotal = 857, 557235783Skib 558235783Skib .progressive = false, .trilevel_sync = false, 559235783Skib 560235783Skib .vsync_start_f1 = 6, .vsync_start_f2 = 7, 561235783Skib .vsync_len = 6, 562235783Skib 563235783Skib .veq_ena = true, .veq_start_f1 = 0, 564235783Skib .veq_start_f2 = 1, .veq_len = 18, 565235783Skib 566235783Skib .vi_end_f1 = 20, .vi_end_f2 = 21, 567235783Skib .nbr_end = 240, 568235783Skib 569235783Skib .burst_ena = true, 570235783Skib .hburst_start = 72, .hburst_len = 34, 571235783Skib .vburst_start_f1 = 9, .vburst_end_f1 = 240, 572235783Skib .vburst_start_f2 = 10, .vburst_end_f2 = 240, 573235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 240, 574235783Skib .vburst_start_f4 = 10, .vburst_end_f4 = 240, 575235783Skib 576235783Skib /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 577235783Skib .dda1_inc = 135, 578235783Skib .dda2_inc = 16704, .dda2_size = 27456, 579235783Skib .dda3_inc = 0, .dda3_size = 0, 580235783Skib .sc_reset = TV_SC_RESET_EVERY_8, 581235783Skib .pal_burst = true, 582235783Skib 583235783Skib .composite_levels = &pal_m_levels_composite, 584235783Skib .composite_color = &pal_m_csc_composite, 585235783Skib .svideo_levels = &pal_m_levels_svideo, 586235783Skib .svideo_color = &pal_m_csc_svideo, 587235783Skib 588235783Skib .filter_table = filter_table, 589235783Skib }, 590235783Skib { 591235783Skib /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 592235783Skib .name = "PAL-N", 593235783Skib .clock = 108000, 594235783Skib .refresh = 50000, 595235783Skib .oversample = TV_OVERSAMPLE_8X, 596235783Skib .component_only = 0, 597235783Skib 598235783Skib .hsync_end = 64, .hblank_end = 128, 599235783Skib .hblank_start = 844, .htotal = 863, 600235783Skib 601235783Skib .progressive = false, .trilevel_sync = false, 602235783Skib 603235783Skib 604235783Skib .vsync_start_f1 = 6, .vsync_start_f2 = 7, 605235783Skib .vsync_len = 6, 606235783Skib 607235783Skib .veq_ena = true, .veq_start_f1 = 0, 608235783Skib .veq_start_f2 = 1, .veq_len = 18, 609235783Skib 610235783Skib .vi_end_f1 = 24, .vi_end_f2 = 25, 611235783Skib .nbr_end = 286, 612235783Skib 613235783Skib .burst_ena = true, 614235783Skib .hburst_start = 73, .hburst_len = 34, 615235783Skib .vburst_start_f1 = 8, .vburst_end_f1 = 285, 616235783Skib .vburst_start_f2 = 8, .vburst_end_f2 = 286, 617235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 286, 618235783Skib .vburst_start_f4 = 9, .vburst_end_f4 = 285, 619235783Skib 620235783Skib 621235783Skib /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 622235783Skib .dda1_inc = 135, 623235783Skib .dda2_inc = 23578, .dda2_size = 27648, 624235783Skib .dda3_inc = 134, .dda3_size = 625, 625235783Skib .sc_reset = TV_SC_RESET_EVERY_8, 626235783Skib .pal_burst = true, 627235783Skib 628235783Skib .composite_levels = &pal_n_levels_composite, 629235783Skib .composite_color = &pal_n_csc_composite, 630235783Skib .svideo_levels = &pal_n_levels_svideo, 631235783Skib .svideo_color = &pal_n_csc_svideo, 632235783Skib 633235783Skib .filter_table = filter_table, 634235783Skib }, 635235783Skib { 636235783Skib /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 637235783Skib .name = "PAL", 638235783Skib .clock = 108000, 639235783Skib .refresh = 50000, 640235783Skib .oversample = TV_OVERSAMPLE_8X, 641235783Skib .component_only = 0, 642235783Skib 643235783Skib .hsync_end = 64, .hblank_end = 142, 644235783Skib .hblank_start = 844, .htotal = 863, 645235783Skib 646235783Skib .progressive = false, .trilevel_sync = false, 647235783Skib 648235783Skib .vsync_start_f1 = 5, .vsync_start_f2 = 6, 649235783Skib .vsync_len = 5, 650235783Skib 651235783Skib .veq_ena = true, .veq_start_f1 = 0, 652235783Skib .veq_start_f2 = 1, .veq_len = 15, 653235783Skib 654235783Skib .vi_end_f1 = 24, .vi_end_f2 = 25, 655235783Skib .nbr_end = 286, 656235783Skib 657235783Skib .burst_ena = true, 658235783Skib .hburst_start = 73, .hburst_len = 32, 659235783Skib .vburst_start_f1 = 8, .vburst_end_f1 = 285, 660235783Skib .vburst_start_f2 = 8, .vburst_end_f2 = 286, 661235783Skib .vburst_start_f3 = 9, .vburst_end_f3 = 286, 662235783Skib .vburst_start_f4 = 9, .vburst_end_f4 = 285, 663235783Skib 664235783Skib /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 665235783Skib .dda1_inc = 168, 666235783Skib .dda2_inc = 4122, .dda2_size = 27648, 667235783Skib .dda3_inc = 67, .dda3_size = 625, 668235783Skib .sc_reset = TV_SC_RESET_EVERY_8, 669235783Skib .pal_burst = true, 670235783Skib 671235783Skib .composite_levels = &pal_levels_composite, 672235783Skib .composite_color = &pal_csc_composite, 673235783Skib .svideo_levels = &pal_levels_svideo, 674235783Skib .svideo_color = &pal_csc_svideo, 675235783Skib 676235783Skib .filter_table = filter_table, 677235783Skib }, 678235783Skib { 679296548Sdumbbell .name = "480p", 680296548Sdumbbell .clock = 107520, 681296548Sdumbbell .refresh = 59940, 682296548Sdumbbell .oversample = TV_OVERSAMPLE_4X, 683296548Sdumbbell .component_only = 1, 684296548Sdumbbell 685296548Sdumbbell .hsync_end = 64, .hblank_end = 122, 686296548Sdumbbell .hblank_start = 842, .htotal = 857, 687296548Sdumbbell 688296548Sdumbbell .progressive = true, .trilevel_sync = false, 689296548Sdumbbell 690296548Sdumbbell .vsync_start_f1 = 12, .vsync_start_f2 = 12, 691296548Sdumbbell .vsync_len = 12, 692296548Sdumbbell 693296548Sdumbbell .veq_ena = false, 694296548Sdumbbell 695296548Sdumbbell .vi_end_f1 = 44, .vi_end_f2 = 44, 696296548Sdumbbell .nbr_end = 479, 697296548Sdumbbell 698296548Sdumbbell .burst_ena = false, 699296548Sdumbbell 700296548Sdumbbell .filter_table = filter_table, 701296548Sdumbbell }, 702296548Sdumbbell { 703296548Sdumbbell .name = "576p", 704296548Sdumbbell .clock = 107520, 705296548Sdumbbell .refresh = 50000, 706296548Sdumbbell .oversample = TV_OVERSAMPLE_4X, 707296548Sdumbbell .component_only = 1, 708296548Sdumbbell 709296548Sdumbbell .hsync_end = 64, .hblank_end = 139, 710296548Sdumbbell .hblank_start = 859, .htotal = 863, 711296548Sdumbbell 712296548Sdumbbell .progressive = true, .trilevel_sync = false, 713296548Sdumbbell 714296548Sdumbbell .vsync_start_f1 = 10, .vsync_start_f2 = 10, 715296548Sdumbbell .vsync_len = 10, 716296548Sdumbbell 717296548Sdumbbell .veq_ena = false, 718296548Sdumbbell 719296548Sdumbbell .vi_end_f1 = 48, .vi_end_f2 = 48, 720296548Sdumbbell .nbr_end = 575, 721296548Sdumbbell 722296548Sdumbbell .burst_ena = false, 723296548Sdumbbell 724296548Sdumbbell .filter_table = filter_table, 725296548Sdumbbell }, 726296548Sdumbbell { 727235783Skib .name = "720p@60Hz", 728235783Skib .clock = 148800, 729235783Skib .refresh = 60000, 730235783Skib .oversample = TV_OVERSAMPLE_2X, 731235783Skib .component_only = 1, 732235783Skib 733235783Skib .hsync_end = 80, .hblank_end = 300, 734235783Skib .hblank_start = 1580, .htotal = 1649, 735235783Skib 736235783Skib .progressive = true, .trilevel_sync = true, 737235783Skib 738235783Skib .vsync_start_f1 = 10, .vsync_start_f2 = 10, 739235783Skib .vsync_len = 10, 740235783Skib 741235783Skib .veq_ena = false, 742235783Skib 743235783Skib .vi_end_f1 = 29, .vi_end_f2 = 29, 744235783Skib .nbr_end = 719, 745235783Skib 746235783Skib .burst_ena = false, 747235783Skib 748235783Skib .filter_table = filter_table, 749235783Skib }, 750235783Skib { 751235783Skib .name = "720p@50Hz", 752235783Skib .clock = 148800, 753235783Skib .refresh = 50000, 754235783Skib .oversample = TV_OVERSAMPLE_2X, 755235783Skib .component_only = 1, 756235783Skib 757235783Skib .hsync_end = 80, .hblank_end = 300, 758235783Skib .hblank_start = 1580, .htotal = 1979, 759235783Skib 760235783Skib .progressive = true, .trilevel_sync = true, 761235783Skib 762235783Skib .vsync_start_f1 = 10, .vsync_start_f2 = 10, 763235783Skib .vsync_len = 10, 764235783Skib 765235783Skib .veq_ena = false, 766235783Skib 767235783Skib .vi_end_f1 = 29, .vi_end_f2 = 29, 768235783Skib .nbr_end = 719, 769235783Skib 770235783Skib .burst_ena = false, 771235783Skib 772235783Skib .filter_table = filter_table, 773235783Skib .max_srcw = 800 774235783Skib }, 775235783Skib { 776235783Skib .name = "1080i@50Hz", 777235783Skib .clock = 148800, 778235783Skib .refresh = 50000, 779235783Skib .oversample = TV_OVERSAMPLE_2X, 780235783Skib .component_only = 1, 781235783Skib 782235783Skib .hsync_end = 88, .hblank_end = 235, 783235783Skib .hblank_start = 2155, .htotal = 2639, 784235783Skib 785235783Skib .progressive = false, .trilevel_sync = true, 786235783Skib 787235783Skib .vsync_start_f1 = 4, .vsync_start_f2 = 5, 788235783Skib .vsync_len = 10, 789235783Skib 790235783Skib .veq_ena = true, .veq_start_f1 = 4, 791235783Skib .veq_start_f2 = 4, .veq_len = 10, 792235783Skib 793235783Skib 794235783Skib .vi_end_f1 = 21, .vi_end_f2 = 22, 795235783Skib .nbr_end = 539, 796235783Skib 797235783Skib .burst_ena = false, 798235783Skib 799235783Skib .filter_table = filter_table, 800235783Skib }, 801235783Skib { 802235783Skib .name = "1080i@60Hz", 803235783Skib .clock = 148800, 804235783Skib .refresh = 60000, 805235783Skib .oversample = TV_OVERSAMPLE_2X, 806235783Skib .component_only = 1, 807235783Skib 808235783Skib .hsync_end = 88, .hblank_end = 235, 809235783Skib .hblank_start = 2155, .htotal = 2199, 810235783Skib 811235783Skib .progressive = false, .trilevel_sync = true, 812235783Skib 813235783Skib .vsync_start_f1 = 4, .vsync_start_f2 = 5, 814235783Skib .vsync_len = 10, 815235783Skib 816235783Skib .veq_ena = true, .veq_start_f1 = 4, 817235783Skib .veq_start_f2 = 4, .veq_len = 10, 818235783Skib 819235783Skib 820235783Skib .vi_end_f1 = 21, .vi_end_f2 = 22, 821235783Skib .nbr_end = 539, 822235783Skib 823235783Skib .burst_ena = false, 824235783Skib 825235783Skib .filter_table = filter_table, 826235783Skib }, 827235783Skib}; 828235783Skib 829235783Skibstatic struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder) 830235783Skib{ 831235783Skib return container_of(encoder, struct intel_tv, base.base); 832235783Skib} 833235783Skib 834235783Skibstatic struct intel_tv *intel_attached_tv(struct drm_connector *connector) 835235783Skib{ 836235783Skib return container_of(intel_attached_encoder(connector), 837235783Skib struct intel_tv, 838235783Skib base); 839235783Skib} 840235783Skib 841296548Sdumbbellstatic bool 842296548Sdumbbellintel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe) 843296548Sdumbbell{ 844296548Sdumbbell struct drm_device *dev = encoder->base.dev; 845296548Sdumbbell struct drm_i915_private *dev_priv = dev->dev_private; 846296548Sdumbbell u32 tmp = I915_READ(TV_CTL); 847296548Sdumbbell 848296548Sdumbbell if (!(tmp & TV_ENC_ENABLE)) 849296548Sdumbbell return false; 850296548Sdumbbell 851296548Sdumbbell *pipe = PORT_TO_PIPE(tmp); 852296548Sdumbbell 853296548Sdumbbell return true; 854296548Sdumbbell} 855296548Sdumbbell 856235783Skibstatic void 857296548Sdumbbellintel_enable_tv(struct intel_encoder *encoder) 858235783Skib{ 859296548Sdumbbell struct drm_device *dev = encoder->base.dev; 860235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 861235783Skib 862296548Sdumbbell I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); 863235783Skib} 864235783Skib 865296548Sdumbbellstatic void 866296548Sdumbbellintel_disable_tv(struct intel_encoder *encoder) 867296548Sdumbbell{ 868296548Sdumbbell struct drm_device *dev = encoder->base.dev; 869296548Sdumbbell struct drm_i915_private *dev_priv = dev->dev_private; 870296548Sdumbbell 871296548Sdumbbell I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); 872296548Sdumbbell} 873296548Sdumbbell 874235783Skibstatic const struct tv_mode * 875235783Skibintel_tv_mode_lookup(const char *tv_format) 876235783Skib{ 877235783Skib int i; 878235783Skib 879296548Sdumbbell for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { 880235783Skib const struct tv_mode *tv_mode = &tv_modes[i]; 881235783Skib 882235783Skib if (!strcmp(tv_format, tv_mode->name)) 883235783Skib return tv_mode; 884235783Skib } 885235783Skib return NULL; 886235783Skib} 887235783Skib 888235783Skibstatic const struct tv_mode * 889235783Skibintel_tv_mode_find(struct intel_tv *intel_tv) 890235783Skib{ 891235783Skib return intel_tv_mode_lookup(intel_tv->tv_format); 892235783Skib} 893235783Skib 894235783Skibstatic enum drm_mode_status 895235783Skibintel_tv_mode_valid(struct drm_connector *connector, 896235783Skib struct drm_display_mode *mode) 897235783Skib{ 898235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 899235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 900235783Skib 901235783Skib /* Ensure TV refresh is close to desired refresh */ 902235783Skib if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) 903235783Skib < 1000) 904235783Skib return MODE_OK; 905235783Skib 906235783Skib return MODE_CLOCK_RANGE; 907235783Skib} 908235783Skib 909235783Skib 910235783Skibstatic bool 911296548Sdumbbellintel_tv_mode_fixup(struct drm_encoder *encoder, 912296548Sdumbbell const struct drm_display_mode *mode, 913235783Skib struct drm_display_mode *adjusted_mode) 914235783Skib{ 915235783Skib struct intel_tv *intel_tv = enc_to_intel_tv(encoder); 916235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 917235783Skib 918235783Skib if (!tv_mode) 919235783Skib return false; 920235783Skib 921296548Sdumbbell if (intel_encoder_check_is_cloned(&intel_tv->base)) 922296548Sdumbbell return false; 923235783Skib 924235783Skib adjusted_mode->clock = tv_mode->clock; 925235783Skib return true; 926235783Skib} 927235783Skib 928235783Skibstatic void 929235783Skibintel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, 930235783Skib struct drm_display_mode *adjusted_mode) 931235783Skib{ 932235783Skib struct drm_device *dev = encoder->dev; 933235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 934235783Skib struct drm_crtc *crtc = encoder->crtc; 935235783Skib struct intel_crtc *intel_crtc = to_intel_crtc(crtc); 936235783Skib struct intel_tv *intel_tv = enc_to_intel_tv(encoder); 937235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 938235783Skib u32 tv_ctl; 939235783Skib u32 hctl1, hctl2, hctl3; 940235783Skib u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; 941235783Skib u32 scctl1, scctl2, scctl3; 942235783Skib int i, j; 943235783Skib const struct video_levels *video_levels; 944235783Skib const struct color_conversion *color_conversion; 945235783Skib bool burst_ena; 946235783Skib int pipe = intel_crtc->pipe; 947235783Skib 948235783Skib if (!tv_mode) 949235783Skib return; /* can't happen (mode_prepare prevents this) */ 950235783Skib 951235783Skib tv_ctl = I915_READ(TV_CTL); 952235783Skib tv_ctl &= TV_CTL_SAVE; 953235783Skib 954235783Skib switch (intel_tv->type) { 955235783Skib default: 956235783Skib case DRM_MODE_CONNECTOR_Unknown: 957235783Skib case DRM_MODE_CONNECTOR_Composite: 958235783Skib tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; 959235783Skib video_levels = tv_mode->composite_levels; 960235783Skib color_conversion = tv_mode->composite_color; 961235783Skib burst_ena = tv_mode->burst_ena; 962235783Skib break; 963235783Skib case DRM_MODE_CONNECTOR_Component: 964235783Skib tv_ctl |= TV_ENC_OUTPUT_COMPONENT; 965235783Skib video_levels = &component_levels; 966235783Skib if (tv_mode->burst_ena) 967235783Skib color_conversion = &sdtv_csc_yprpb; 968235783Skib else 969235783Skib color_conversion = &hdtv_csc_yprpb; 970235783Skib burst_ena = false; 971235783Skib break; 972235783Skib case DRM_MODE_CONNECTOR_SVIDEO: 973235783Skib tv_ctl |= TV_ENC_OUTPUT_SVIDEO; 974235783Skib video_levels = tv_mode->svideo_levels; 975235783Skib color_conversion = tv_mode->svideo_color; 976235783Skib burst_ena = tv_mode->burst_ena; 977235783Skib break; 978235783Skib } 979235783Skib hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | 980235783Skib (tv_mode->htotal << TV_HTOTAL_SHIFT); 981235783Skib 982235783Skib hctl2 = (tv_mode->hburst_start << 16) | 983235783Skib (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); 984235783Skib 985235783Skib if (burst_ena) 986235783Skib hctl2 |= TV_BURST_ENA; 987235783Skib 988235783Skib hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | 989235783Skib (tv_mode->hblank_end << TV_HBLANK_END_SHIFT); 990235783Skib 991235783Skib vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | 992235783Skib (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | 993235783Skib (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); 994235783Skib 995235783Skib vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | 996235783Skib (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | 997235783Skib (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); 998235783Skib 999235783Skib vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | 1000235783Skib (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | 1001235783Skib (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); 1002235783Skib 1003235783Skib if (tv_mode->veq_ena) 1004235783Skib vctl3 |= TV_EQUAL_ENA; 1005235783Skib 1006235783Skib vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | 1007235783Skib (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); 1008235783Skib 1009235783Skib vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | 1010235783Skib (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); 1011235783Skib 1012235783Skib vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | 1013235783Skib (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); 1014235783Skib 1015235783Skib vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | 1016235783Skib (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); 1017235783Skib 1018235783Skib if (intel_crtc->pipe == 1) 1019235783Skib tv_ctl |= TV_ENC_PIPEB_SELECT; 1020235783Skib tv_ctl |= tv_mode->oversample; 1021235783Skib 1022235783Skib if (tv_mode->progressive) 1023235783Skib tv_ctl |= TV_PROGRESSIVE; 1024235783Skib if (tv_mode->trilevel_sync) 1025235783Skib tv_ctl |= TV_TRILEVEL_SYNC; 1026235783Skib if (tv_mode->pal_burst) 1027235783Skib tv_ctl |= TV_PAL_BURST; 1028235783Skib 1029235783Skib scctl1 = 0; 1030235783Skib if (tv_mode->dda1_inc) 1031235783Skib scctl1 |= TV_SC_DDA1_EN; 1032235783Skib if (tv_mode->dda2_inc) 1033235783Skib scctl1 |= TV_SC_DDA2_EN; 1034235783Skib if (tv_mode->dda3_inc) 1035235783Skib scctl1 |= TV_SC_DDA3_EN; 1036235783Skib scctl1 |= tv_mode->sc_reset; 1037235783Skib if (video_levels) 1038235783Skib scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; 1039235783Skib scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; 1040235783Skib 1041235783Skib scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | 1042235783Skib tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; 1043235783Skib 1044235783Skib scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | 1045235783Skib tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; 1046235783Skib 1047235783Skib /* Enable two fixes for the chips that need them. */ 1048235783Skib if (dev->pci_device < 0x2772) 1049235783Skib tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; 1050235783Skib 1051235783Skib I915_WRITE(TV_H_CTL_1, hctl1); 1052235783Skib I915_WRITE(TV_H_CTL_2, hctl2); 1053235783Skib I915_WRITE(TV_H_CTL_3, hctl3); 1054235783Skib I915_WRITE(TV_V_CTL_1, vctl1); 1055235783Skib I915_WRITE(TV_V_CTL_2, vctl2); 1056235783Skib I915_WRITE(TV_V_CTL_3, vctl3); 1057235783Skib I915_WRITE(TV_V_CTL_4, vctl4); 1058235783Skib I915_WRITE(TV_V_CTL_5, vctl5); 1059235783Skib I915_WRITE(TV_V_CTL_6, vctl6); 1060235783Skib I915_WRITE(TV_V_CTL_7, vctl7); 1061235783Skib I915_WRITE(TV_SC_CTL_1, scctl1); 1062235783Skib I915_WRITE(TV_SC_CTL_2, scctl2); 1063235783Skib I915_WRITE(TV_SC_CTL_3, scctl3); 1064235783Skib 1065235783Skib if (color_conversion) { 1066235783Skib I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | 1067235783Skib color_conversion->gy); 1068235783Skib I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) | 1069235783Skib color_conversion->ay); 1070235783Skib I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | 1071235783Skib color_conversion->gu); 1072235783Skib I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) | 1073235783Skib color_conversion->au); 1074235783Skib I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) | 1075235783Skib color_conversion->gv); 1076235783Skib I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) | 1077235783Skib color_conversion->av); 1078235783Skib } 1079235783Skib 1080235783Skib if (INTEL_INFO(dev)->gen >= 4) 1081235783Skib I915_WRITE(TV_CLR_KNOBS, 0x00404000); 1082235783Skib else 1083235783Skib I915_WRITE(TV_CLR_KNOBS, 0x00606000); 1084235783Skib 1085235783Skib if (video_levels) 1086235783Skib I915_WRITE(TV_CLR_LEVEL, 1087235783Skib ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | 1088235783Skib (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); 1089235783Skib { 1090235783Skib int pipeconf_reg = PIPECONF(pipe); 1091235783Skib int dspcntr_reg = DSPCNTR(intel_crtc->plane); 1092235783Skib int pipeconf = I915_READ(pipeconf_reg); 1093235783Skib int dspcntr = I915_READ(dspcntr_reg); 1094235783Skib int xpos = 0x0, ypos = 0x0; 1095235783Skib unsigned int xsize, ysize; 1096235783Skib /* Pipe must be off here */ 1097235783Skib I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); 1098296548Sdumbbell intel_flush_display_plane(dev_priv, intel_crtc->plane); 1099235783Skib 1100235783Skib /* Wait for vblank for the disable to take effect */ 1101235783Skib if (IS_GEN2(dev)) 1102235783Skib intel_wait_for_vblank(dev, intel_crtc->pipe); 1103235783Skib 1104235783Skib I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE); 1105235783Skib /* Wait for vblank for the disable to take effect. */ 1106235783Skib intel_wait_for_pipe_off(dev, intel_crtc->pipe); 1107235783Skib 1108235783Skib /* Filter ctl must be set before TV_WIN_SIZE */ 1109235783Skib I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); 1110235783Skib xsize = tv_mode->hblank_start - tv_mode->hblank_end; 1111235783Skib if (tv_mode->progressive) 1112235783Skib ysize = tv_mode->nbr_end + 1; 1113235783Skib else 1114235783Skib ysize = 2*tv_mode->nbr_end + 1; 1115235783Skib 1116235783Skib xpos += intel_tv->margin[TV_MARGIN_LEFT]; 1117235783Skib ypos += intel_tv->margin[TV_MARGIN_TOP]; 1118235783Skib xsize -= (intel_tv->margin[TV_MARGIN_LEFT] + 1119235783Skib intel_tv->margin[TV_MARGIN_RIGHT]); 1120235783Skib ysize -= (intel_tv->margin[TV_MARGIN_TOP] + 1121235783Skib intel_tv->margin[TV_MARGIN_BOTTOM]); 1122235783Skib I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); 1123235783Skib I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize); 1124235783Skib 1125235783Skib I915_WRITE(pipeconf_reg, pipeconf); 1126235783Skib I915_WRITE(dspcntr_reg, dspcntr); 1127296548Sdumbbell intel_flush_display_plane(dev_priv, intel_crtc->plane); 1128235783Skib } 1129235783Skib 1130235783Skib j = 0; 1131235783Skib for (i = 0; i < 60; i++) 1132235783Skib I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1133235783Skib for (i = 0; i < 60; i++) 1134235783Skib I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1135235783Skib for (i = 0; i < 43; i++) 1136235783Skib I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1137235783Skib for (i = 0; i < 43; i++) 1138235783Skib I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1139235783Skib I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE); 1140235783Skib I915_WRITE(TV_CTL, tv_ctl); 1141235783Skib} 1142235783Skib 1143235783Skibstatic const struct drm_display_mode reported_modes[] = { 1144235783Skib { 1145235783Skib .name = "NTSC 480i", 1146235783Skib .clock = 107520, 1147235783Skib .hdisplay = 1280, 1148235783Skib .hsync_start = 1368, 1149235783Skib .hsync_end = 1496, 1150235783Skib .htotal = 1712, 1151235783Skib 1152235783Skib .vdisplay = 1024, 1153235783Skib .vsync_start = 1027, 1154235783Skib .vsync_end = 1034, 1155235783Skib .vtotal = 1104, 1156235783Skib .type = DRM_MODE_TYPE_DRIVER, 1157235783Skib }, 1158235783Skib}; 1159235783Skib 1160235783Skib/** 1161235783Skib * Detects TV presence by checking for load. 1162235783Skib * 1163235783Skib * Requires that the current pipe's DPLL is active. 1164235783Skib 1165235783Skib * \return true if TV is connected. 1166235783Skib * \return false if TV is disconnected. 1167235783Skib */ 1168235783Skibstatic int 1169235783Skibintel_tv_detect_type(struct intel_tv *intel_tv, 1170235783Skib struct drm_connector *connector) 1171235783Skib{ 1172235783Skib struct drm_encoder *encoder = &intel_tv->base.base; 1173235783Skib struct drm_crtc *crtc = encoder->crtc; 1174235783Skib struct intel_crtc *intel_crtc = to_intel_crtc(crtc); 1175235783Skib struct drm_device *dev = encoder->dev; 1176235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 1177235783Skib u32 tv_ctl, save_tv_ctl; 1178235783Skib u32 tv_dac, save_tv_dac; 1179235783Skib int type; 1180235783Skib 1181235783Skib /* Disable TV interrupts around load detect or we'll recurse */ 1182235783Skib if (connector->polled & DRM_CONNECTOR_POLL_HPD) { 1183235783Skib mtx_lock(&dev_priv->irq_lock); 1184235783Skib i915_disable_pipestat(dev_priv, 0, 1185235783Skib PIPE_HOTPLUG_INTERRUPT_ENABLE | 1186235783Skib PIPE_HOTPLUG_TV_INTERRUPT_ENABLE); 1187235783Skib mtx_unlock(&dev_priv->irq_lock); 1188235783Skib } 1189235783Skib 1190235783Skib save_tv_dac = tv_dac = I915_READ(TV_DAC); 1191235783Skib save_tv_ctl = tv_ctl = I915_READ(TV_CTL); 1192235783Skib 1193235783Skib /* Poll for TV detection */ 1194235783Skib tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK); 1195235783Skib tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; 1196235783Skib if (intel_crtc->pipe == 1) 1197235783Skib tv_ctl |= TV_ENC_PIPEB_SELECT; 1198235783Skib else 1199235783Skib tv_ctl &= ~TV_ENC_PIPEB_SELECT; 1200235783Skib 1201235783Skib tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK); 1202235783Skib tv_dac |= (TVDAC_STATE_CHG_EN | 1203235783Skib TVDAC_A_SENSE_CTL | 1204235783Skib TVDAC_B_SENSE_CTL | 1205235783Skib TVDAC_C_SENSE_CTL | 1206235783Skib DAC_CTL_OVERRIDE | 1207235783Skib DAC_A_0_7_V | 1208235783Skib DAC_B_0_7_V | 1209235783Skib DAC_C_0_7_V); 1210235783Skib 1211277487Skib 1212277487Skib /* 1213277487Skib * The TV sense state should be cleared to zero on cantiga platform. Otherwise 1214277487Skib * the TV is misdetected. This is hardware requirement. 1215277487Skib */ 1216277487Skib if (IS_GM45(dev)) 1217277487Skib tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL | 1218277487Skib TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL); 1219277487Skib 1220235783Skib I915_WRITE(TV_CTL, tv_ctl); 1221235783Skib I915_WRITE(TV_DAC, tv_dac); 1222235783Skib POSTING_READ(TV_DAC); 1223235783Skib 1224235783Skib intel_wait_for_vblank(intel_tv->base.base.dev, 1225235783Skib to_intel_crtc(intel_tv->base.base.crtc)->pipe); 1226235783Skib 1227235783Skib type = -1; 1228235783Skib tv_dac = I915_READ(TV_DAC); 1229235783Skib DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); 1230235783Skib /* 1231235783Skib * A B C 1232235783Skib * 0 1 1 Composite 1233235783Skib * 1 0 X svideo 1234235783Skib * 0 0 0 Component 1235235783Skib */ 1236235783Skib if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { 1237235783Skib DRM_DEBUG_KMS("Detected Composite TV connection\n"); 1238235783Skib type = DRM_MODE_CONNECTOR_Composite; 1239235783Skib } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { 1240235783Skib DRM_DEBUG_KMS("Detected S-Video TV connection\n"); 1241235783Skib type = DRM_MODE_CONNECTOR_SVIDEO; 1242235783Skib } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { 1243235783Skib DRM_DEBUG_KMS("Detected Component TV connection\n"); 1244235783Skib type = DRM_MODE_CONNECTOR_Component; 1245235783Skib } else { 1246235783Skib DRM_DEBUG_KMS("Unrecognised TV connection\n"); 1247235783Skib type = -1; 1248235783Skib } 1249235783Skib 1250235783Skib I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); 1251235783Skib I915_WRITE(TV_CTL, save_tv_ctl); 1252296548Sdumbbell POSTING_READ(TV_CTL); 1253235783Skib 1254296548Sdumbbell /* For unknown reasons the hw barfs if we don't do this vblank wait. */ 1255296548Sdumbbell intel_wait_for_vblank(intel_tv->base.base.dev, 1256296548Sdumbbell to_intel_crtc(intel_tv->base.base.crtc)->pipe); 1257296548Sdumbbell 1258235783Skib /* Restore interrupt config */ 1259235783Skib if (connector->polled & DRM_CONNECTOR_POLL_HPD) { 1260235783Skib mtx_lock(&dev_priv->irq_lock); 1261235783Skib i915_enable_pipestat(dev_priv, 0, 1262235783Skib PIPE_HOTPLUG_INTERRUPT_ENABLE | 1263235783Skib PIPE_HOTPLUG_TV_INTERRUPT_ENABLE); 1264235783Skib mtx_unlock(&dev_priv->irq_lock); 1265235783Skib } 1266235783Skib 1267235783Skib return type; 1268235783Skib} 1269235783Skib 1270235783Skib/* 1271235783Skib * Here we set accurate tv format according to connector type 1272235783Skib * i.e Component TV should not be assigned by NTSC or PAL 1273235783Skib */ 1274235783Skibstatic void intel_tv_find_better_format(struct drm_connector *connector) 1275235783Skib{ 1276235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 1277235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1278235783Skib int i; 1279235783Skib 1280235783Skib if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == 1281235783Skib tv_mode->component_only) 1282235783Skib return; 1283235783Skib 1284235783Skib 1285235783Skib for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) { 1286235783Skib tv_mode = tv_modes + i; 1287235783Skib 1288235783Skib if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == 1289235783Skib tv_mode->component_only) 1290235783Skib break; 1291235783Skib } 1292235783Skib 1293235783Skib intel_tv->tv_format = tv_mode->name; 1294280183Sdumbbell drm_object_property_set_value(&connector->base, 1295235783Skib connector->dev->mode_config.tv_mode_property, i); 1296235783Skib} 1297235783Skib 1298235783Skib/** 1299235783Skib * Detect the TV connection. 1300235783Skib * 1301235783Skib * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure 1302235783Skib * we have a pipe programmed in order to probe the TV. 1303235783Skib */ 1304235783Skibstatic enum drm_connector_status 1305235783Skibintel_tv_detect(struct drm_connector *connector, bool force) 1306235783Skib{ 1307235783Skib struct drm_display_mode mode; 1308235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 1309235783Skib int type; 1310235783Skib 1311235783Skib mode = reported_modes[0]; 1312235783Skib 1313277487Skib if (force) { 1314235783Skib struct intel_load_detect_pipe tmp; 1315235783Skib 1316296548Sdumbbell if (intel_get_load_detect_pipe(connector, &mode, &tmp)) { 1317235783Skib type = intel_tv_detect_type(intel_tv, connector); 1318296548Sdumbbell intel_release_load_detect_pipe(connector, &tmp); 1319235783Skib } else 1320235783Skib return connector_status_unknown; 1321235783Skib } else 1322235783Skib return connector->status; 1323235783Skib 1324235783Skib if (type < 0) 1325235783Skib return connector_status_disconnected; 1326235783Skib 1327235783Skib intel_tv->type = type; 1328235783Skib intel_tv_find_better_format(connector); 1329235783Skib 1330235783Skib return connector_status_connected; 1331235783Skib} 1332235783Skib 1333235783Skibstatic const struct input_res { 1334235783Skib const char *name; 1335235783Skib int w, h; 1336235783Skib} input_res_table[] = { 1337235783Skib {"640x480", 640, 480}, 1338235783Skib {"800x600", 800, 600}, 1339235783Skib {"1024x768", 1024, 768}, 1340235783Skib {"1280x1024", 1280, 1024}, 1341235783Skib {"848x480", 848, 480}, 1342235783Skib {"1280x720", 1280, 720}, 1343235783Skib {"1920x1080", 1920, 1080}, 1344235783Skib}; 1345235783Skib 1346235783Skib/* 1347235783Skib * Chose preferred mode according to line number of TV format 1348235783Skib */ 1349235783Skibstatic void 1350235783Skibintel_tv_chose_preferred_modes(struct drm_connector *connector, 1351235783Skib struct drm_display_mode *mode_ptr) 1352235783Skib{ 1353235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 1354235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1355235783Skib 1356235783Skib if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480) 1357235783Skib mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1358235783Skib else if (tv_mode->nbr_end > 480) { 1359235783Skib if (tv_mode->progressive == true && tv_mode->nbr_end < 720) { 1360235783Skib if (mode_ptr->vdisplay == 720) 1361235783Skib mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1362235783Skib } else if (mode_ptr->vdisplay == 1080) 1363235783Skib mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; 1364235783Skib } 1365235783Skib} 1366235783Skib 1367235783Skib/** 1368235783Skib * Stub get_modes function. 1369235783Skib * 1370235783Skib * This should probably return a set of fixed modes, unless we can figure out 1371235783Skib * how to probe modes off of TV connections. 1372235783Skib */ 1373235783Skib 1374235783Skibstatic int 1375235783Skibintel_tv_get_modes(struct drm_connector *connector) 1376235783Skib{ 1377235783Skib struct drm_display_mode *mode_ptr; 1378235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 1379235783Skib const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); 1380235783Skib int j, count = 0; 1381235783Skib u64 tmp; 1382235783Skib 1383280183Sdumbbell for (j = 0; j < ARRAY_SIZE(input_res_table); 1384235783Skib j++) { 1385235783Skib const struct input_res *input = &input_res_table[j]; 1386235783Skib unsigned int hactive_s = input->w; 1387235783Skib unsigned int vactive_s = input->h; 1388235783Skib 1389235783Skib if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) 1390235783Skib continue; 1391235783Skib 1392235783Skib if (input->w > 1024 && (!tv_mode->progressive 1393235783Skib && !tv_mode->component_only)) 1394235783Skib continue; 1395235783Skib 1396235783Skib mode_ptr = drm_mode_create(connector->dev); 1397235783Skib if (!mode_ptr) 1398235783Skib continue; 1399235783Skib strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN); 1400235783Skib 1401235783Skib mode_ptr->hdisplay = hactive_s; 1402235783Skib mode_ptr->hsync_start = hactive_s + 1; 1403235783Skib mode_ptr->hsync_end = hactive_s + 64; 1404235783Skib if (mode_ptr->hsync_end <= mode_ptr->hsync_start) 1405235783Skib mode_ptr->hsync_end = mode_ptr->hsync_start + 1; 1406235783Skib mode_ptr->htotal = hactive_s + 96; 1407235783Skib 1408235783Skib mode_ptr->vdisplay = vactive_s; 1409235783Skib mode_ptr->vsync_start = vactive_s + 1; 1410235783Skib mode_ptr->vsync_end = vactive_s + 32; 1411235783Skib if (mode_ptr->vsync_end <= mode_ptr->vsync_start) 1412235783Skib mode_ptr->vsync_end = mode_ptr->vsync_start + 1; 1413235783Skib mode_ptr->vtotal = vactive_s + 33; 1414235783Skib 1415235783Skib tmp = (u64) tv_mode->refresh * mode_ptr->vtotal; 1416235783Skib tmp *= mode_ptr->htotal; 1417296548Sdumbbell tmp = div_u64(tmp, 1000000); 1418235783Skib mode_ptr->clock = (int) tmp; 1419235783Skib 1420235783Skib mode_ptr->type = DRM_MODE_TYPE_DRIVER; 1421235783Skib intel_tv_chose_preferred_modes(connector, mode_ptr); 1422235783Skib drm_mode_probed_add(connector, mode_ptr); 1423235783Skib count++; 1424235783Skib } 1425235783Skib 1426235783Skib return count; 1427235783Skib} 1428235783Skib 1429235783Skibstatic void 1430235783Skibintel_tv_destroy(struct drm_connector *connector) 1431235783Skib{ 1432235783Skib drm_connector_cleanup(connector); 1433235783Skib free(connector, DRM_MEM_KMS); 1434235783Skib} 1435235783Skib 1436235783Skib 1437235783Skibstatic int 1438235783Skibintel_tv_set_property(struct drm_connector *connector, struct drm_property *property, 1439235783Skib uint64_t val) 1440235783Skib{ 1441235783Skib struct drm_device *dev = connector->dev; 1442235783Skib struct intel_tv *intel_tv = intel_attached_tv(connector); 1443235783Skib struct drm_crtc *crtc = intel_tv->base.base.crtc; 1444235783Skib int ret = 0; 1445235783Skib bool changed = false; 1446235783Skib 1447280183Sdumbbell ret = drm_object_property_set_value(&connector->base, property, val); 1448235783Skib if (ret < 0) 1449235783Skib goto out; 1450235783Skib 1451235783Skib if (property == dev->mode_config.tv_left_margin_property && 1452235783Skib intel_tv->margin[TV_MARGIN_LEFT] != val) { 1453235783Skib intel_tv->margin[TV_MARGIN_LEFT] = val; 1454235783Skib changed = true; 1455235783Skib } else if (property == dev->mode_config.tv_right_margin_property && 1456235783Skib intel_tv->margin[TV_MARGIN_RIGHT] != val) { 1457235783Skib intel_tv->margin[TV_MARGIN_RIGHT] = val; 1458235783Skib changed = true; 1459235783Skib } else if (property == dev->mode_config.tv_top_margin_property && 1460235783Skib intel_tv->margin[TV_MARGIN_TOP] != val) { 1461235783Skib intel_tv->margin[TV_MARGIN_TOP] = val; 1462235783Skib changed = true; 1463235783Skib } else if (property == dev->mode_config.tv_bottom_margin_property && 1464235783Skib intel_tv->margin[TV_MARGIN_BOTTOM] != val) { 1465235783Skib intel_tv->margin[TV_MARGIN_BOTTOM] = val; 1466235783Skib changed = true; 1467235783Skib } else if (property == dev->mode_config.tv_mode_property) { 1468280183Sdumbbell if (val >= ARRAY_SIZE(tv_modes)) { 1469235783Skib ret = -EINVAL; 1470235783Skib goto out; 1471235783Skib } 1472235783Skib if (!strcmp(intel_tv->tv_format, tv_modes[val].name)) 1473235783Skib goto out; 1474235783Skib 1475235783Skib intel_tv->tv_format = tv_modes[val].name; 1476235783Skib changed = true; 1477235783Skib } else { 1478235783Skib ret = -EINVAL; 1479235783Skib goto out; 1480235783Skib } 1481235783Skib 1482235783Skib if (changed && crtc) 1483296548Sdumbbell intel_set_mode(crtc, &crtc->mode, 1484296548Sdumbbell crtc->x, crtc->y, crtc->fb); 1485235783Skibout: 1486235783Skib return ret; 1487235783Skib} 1488235783Skib 1489235783Skibstatic const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { 1490235783Skib .mode_fixup = intel_tv_mode_fixup, 1491235783Skib .mode_set = intel_tv_mode_set, 1492296548Sdumbbell .disable = intel_encoder_noop, 1493235783Skib}; 1494235783Skib 1495235783Skibstatic const struct drm_connector_funcs intel_tv_connector_funcs = { 1496296548Sdumbbell .dpms = intel_connector_dpms, 1497235783Skib .detect = intel_tv_detect, 1498235783Skib .destroy = intel_tv_destroy, 1499235783Skib .set_property = intel_tv_set_property, 1500235783Skib .fill_modes = drm_helper_probe_single_connector_modes, 1501235783Skib}; 1502235783Skib 1503235783Skibstatic const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { 1504235783Skib .mode_valid = intel_tv_mode_valid, 1505235783Skib .get_modes = intel_tv_get_modes, 1506235783Skib .best_encoder = intel_best_encoder, 1507235783Skib}; 1508235783Skib 1509235783Skibstatic const struct drm_encoder_funcs intel_tv_enc_funcs = { 1510235783Skib .destroy = intel_encoder_destroy, 1511235783Skib}; 1512235783Skib 1513235783Skib/* 1514235783Skib * Enumerate the child dev array parsed from VBT to check whether 1515235783Skib * the integrated TV is present. 1516235783Skib * If it is present, return 1. 1517235783Skib * If it is not present, return false. 1518235783Skib * If no child dev is parsed from VBT, it assumes that the TV is present. 1519235783Skib */ 1520235783Skibstatic int tv_is_present_in_vbt(struct drm_device *dev) 1521235783Skib{ 1522235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 1523235783Skib struct child_device_config *p_child; 1524235783Skib int i, ret; 1525235783Skib 1526235783Skib if (!dev_priv->child_dev_num) 1527235783Skib return 1; 1528235783Skib 1529235783Skib ret = 0; 1530235783Skib for (i = 0; i < dev_priv->child_dev_num; i++) { 1531235783Skib p_child = dev_priv->child_dev + i; 1532235783Skib /* 1533235783Skib * If the device type is not TV, continue. 1534235783Skib */ 1535235783Skib if (p_child->device_type != DEVICE_TYPE_INT_TV && 1536235783Skib p_child->device_type != DEVICE_TYPE_TV) 1537235783Skib continue; 1538235783Skib /* Only when the addin_offset is non-zero, it is regarded 1539235783Skib * as present. 1540235783Skib */ 1541235783Skib if (p_child->addin_offset) { 1542235783Skib ret = 1; 1543235783Skib break; 1544235783Skib } 1545235783Skib } 1546235783Skib return ret; 1547235783Skib} 1548235783Skib 1549235783Skibvoid 1550235783Skibintel_tv_init(struct drm_device *dev) 1551235783Skib{ 1552235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 1553235783Skib struct drm_connector *connector; 1554235783Skib struct intel_tv *intel_tv; 1555235783Skib struct intel_encoder *intel_encoder; 1556235783Skib struct intel_connector *intel_connector; 1557235783Skib u32 tv_dac_on, tv_dac_off, save_tv_dac; 1558280183Sdumbbell char *tv_format_names[ARRAY_SIZE(tv_modes)]; 1559235783Skib int i, initial_mode = 0; 1560235783Skib 1561235783Skib if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) 1562235783Skib return; 1563235783Skib 1564235783Skib if (!tv_is_present_in_vbt(dev)) { 1565235783Skib DRM_DEBUG_KMS("Integrated TV is not present.\n"); 1566235783Skib return; 1567235783Skib } 1568235783Skib /* Even if we have an encoder we may not have a connector */ 1569235783Skib if (!dev_priv->int_tv_support) 1570235783Skib return; 1571235783Skib 1572235783Skib /* 1573235783Skib * Sanity check the TV output by checking to see if the 1574235783Skib * DAC register holds a value 1575235783Skib */ 1576235783Skib save_tv_dac = I915_READ(TV_DAC); 1577235783Skib 1578235783Skib I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN); 1579235783Skib tv_dac_on = I915_READ(TV_DAC); 1580235783Skib 1581235783Skib I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); 1582235783Skib tv_dac_off = I915_READ(TV_DAC); 1583235783Skib 1584235783Skib I915_WRITE(TV_DAC, save_tv_dac); 1585235783Skib 1586235783Skib /* 1587235783Skib * If the register does not hold the state change enable 1588235783Skib * bit, (either as a 0 or a 1), assume it doesn't really 1589235783Skib * exist 1590235783Skib */ 1591235783Skib if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 || 1592235783Skib (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) 1593235783Skib return; 1594235783Skib 1595296548Sdumbbell intel_tv = malloc(sizeof(struct intel_tv), DRM_MEM_KMS, M_WAITOK | M_ZERO); 1596296548Sdumbbell if (!intel_tv) { 1597296548Sdumbbell return; 1598296548Sdumbbell } 1599235783Skib 1600296548Sdumbbell intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO); 1601296548Sdumbbell if (!intel_connector) { 1602296548Sdumbbell free(intel_tv, DRM_MEM_KMS); 1603296548Sdumbbell return; 1604296548Sdumbbell } 1605296548Sdumbbell 1606235783Skib intel_encoder = &intel_tv->base; 1607235783Skib connector = &intel_connector->base; 1608235783Skib 1609235783Skib /* The documentation, for the older chipsets at least, recommend 1610235783Skib * using a polling method rather than hotplug detection for TVs. 1611235783Skib * This is because in order to perform the hotplug detection, the PLLs 1612235783Skib * for the TV must be kept alive increasing power drain and starving 1613235783Skib * bandwidth from other encoders. Notably for instance, it causes 1614235783Skib * pipe underruns on Crestline when this encoder is supposedly idle. 1615235783Skib * 1616235783Skib * More recent chipsets favour HDMI rather than integrated S-Video. 1617235783Skib */ 1618235783Skib connector->polled = DRM_CONNECTOR_POLL_CONNECT; 1619235783Skib 1620235783Skib drm_connector_init(dev, connector, &intel_tv_connector_funcs, 1621235783Skib DRM_MODE_CONNECTOR_SVIDEO); 1622235783Skib 1623235783Skib drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, 1624235783Skib DRM_MODE_ENCODER_TVDAC); 1625235783Skib 1626296548Sdumbbell intel_encoder->enable = intel_enable_tv; 1627296548Sdumbbell intel_encoder->disable = intel_disable_tv; 1628296548Sdumbbell intel_encoder->get_hw_state = intel_tv_get_hw_state; 1629296548Sdumbbell intel_connector->get_hw_state = intel_connector_get_hw_state; 1630296548Sdumbbell 1631235783Skib intel_connector_attach_encoder(intel_connector, intel_encoder); 1632235783Skib intel_encoder->type = INTEL_OUTPUT_TVOUT; 1633235783Skib intel_encoder->crtc_mask = (1 << 0) | (1 << 1); 1634296548Sdumbbell intel_encoder->cloneable = false; 1635235783Skib intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1)); 1636235783Skib intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT); 1637235783Skib intel_tv->type = DRM_MODE_CONNECTOR_Unknown; 1638235783Skib 1639235783Skib /* BIOS margin values */ 1640235783Skib intel_tv->margin[TV_MARGIN_LEFT] = 54; 1641235783Skib intel_tv->margin[TV_MARGIN_TOP] = 36; 1642235783Skib intel_tv->margin[TV_MARGIN_RIGHT] = 46; 1643235783Skib intel_tv->margin[TV_MARGIN_BOTTOM] = 37; 1644235783Skib 1645235783Skib intel_tv->tv_format = tv_modes[initial_mode].name; 1646235783Skib 1647235783Skib drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs); 1648235783Skib drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs); 1649235783Skib connector->interlace_allowed = false; 1650235783Skib connector->doublescan_allowed = false; 1651235783Skib 1652235783Skib /* Create TV properties then attach current values */ 1653280183Sdumbbell for (i = 0; i < ARRAY_SIZE(tv_modes); i++) 1654235783Skib tv_format_names[i] = __DECONST(char *, tv_modes[i].name); 1655235783Skib drm_mode_create_tv_properties(dev, 1656280183Sdumbbell ARRAY_SIZE(tv_modes), 1657235783Skib tv_format_names); 1658235783Skib 1659280183Sdumbbell drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property, 1660235783Skib initial_mode); 1661280183Sdumbbell drm_object_attach_property(&connector->base, 1662235783Skib dev->mode_config.tv_left_margin_property, 1663235783Skib intel_tv->margin[TV_MARGIN_LEFT]); 1664280183Sdumbbell drm_object_attach_property(&connector->base, 1665235783Skib dev->mode_config.tv_top_margin_property, 1666235783Skib intel_tv->margin[TV_MARGIN_TOP]); 1667280183Sdumbbell drm_object_attach_property(&connector->base, 1668235783Skib dev->mode_config.tv_right_margin_property, 1669235783Skib intel_tv->margin[TV_MARGIN_RIGHT]); 1670280183Sdumbbell drm_object_attach_property(&connector->base, 1671235783Skib dev->mode_config.tv_bottom_margin_property, 1672235783Skib intel_tv->margin[TV_MARGIN_BOTTOM]); 1673235783Skib} 1674