1/* 2 * Copyright © 2006 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: 24 * Eric Anholt <eric@anholt.net> 25 * 26 */ 27 28/** @file 29 * Integrated TV-out support for the 915GM and 945GM. 30 */ 31 32#ifdef HAVE_CONFIG_H 33#include "config.h" 34#endif 35 36#include "xf86.h" 37#include "i830.h" 38#include "i830_display.h" 39#include "i830_bios.h" 40#include "X11/Xatom.h" 41#include <string.h> 42 43enum tv_type { 44 TV_TYPE_NONE, 45 TV_TYPE_UNKNOWN, 46 TV_TYPE_COMPOSITE, 47 TV_TYPE_SVIDEO, 48 TV_TYPE_COMPONENT 49}; 50 51enum tv_margin { 52 TV_MARGIN_LEFT, TV_MARGIN_TOP, 53 TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM 54}; 55 56/** Private structure for the integrated TV support */ 57struct i830_tv_priv { 58 int type; 59 Bool force_type; 60 char *tv_format; 61 int margin[4]; 62 uint8_t brightness; 63 uint8_t contrast; 64 uint8_t saturation; 65 uint8_t hue; 66 uint32_t save_TV_H_CTL_1; 67 uint32_t save_TV_H_CTL_2; 68 uint32_t save_TV_H_CTL_3; 69 uint32_t save_TV_V_CTL_1; 70 uint32_t save_TV_V_CTL_2; 71 uint32_t save_TV_V_CTL_3; 72 uint32_t save_TV_V_CTL_4; 73 uint32_t save_TV_V_CTL_5; 74 uint32_t save_TV_V_CTL_6; 75 uint32_t save_TV_V_CTL_7; 76 uint32_t save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; 77 78 uint32_t save_TV_CSC_Y; 79 uint32_t save_TV_CSC_Y2; 80 uint32_t save_TV_CSC_U; 81 uint32_t save_TV_CSC_U2; 82 uint32_t save_TV_CSC_V; 83 uint32_t save_TV_CSC_V2; 84 uint32_t save_TV_CLR_KNOBS; 85 uint32_t save_TV_CLR_LEVEL; 86 uint32_t save_TV_WIN_POS; 87 uint32_t save_TV_WIN_SIZE; 88 uint32_t save_TV_FILTER_CTL_1; 89 uint32_t save_TV_FILTER_CTL_2; 90 uint32_t save_TV_FILTER_CTL_3; 91 92 uint32_t save_TV_H_LUMA[60]; 93 uint32_t save_TV_H_CHROMA[60]; 94 uint32_t save_TV_V_LUMA[43]; 95 uint32_t save_TV_V_CHROMA[43]; 96 97 uint32_t save_TV_DAC; 98 uint32_t save_TV_CTL; 99}; 100 101typedef struct { 102 int blank, black, burst; 103} video_levels_t; 104 105typedef struct { 106 float ry, gy, by, ay; 107 float ru, gu, bu, au; 108 float rv, gv, bv, av; 109} color_conversion_t; 110 111static const uint32_t filter_table[] = { 112 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 113 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 114 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 115 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 116 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 117 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 118 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 119 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 120 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 121 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 122 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 123 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 124 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 125 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 126 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 127 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, 128 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, 129 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, 130 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, 131 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, 132 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, 133 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, 134 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, 135 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, 136 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, 137 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, 138 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, 139 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, 140 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, 141 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, 142 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0, 143 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 144 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 145 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 146 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 147 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 148 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 149 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 150 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 151 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 152 0x28003100, 0x28002F00, 0x00003100, 0x36403000, 153 0x2D002CC0, 0x30003640, 0x2D0036C0, 154 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, 155 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, 156 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, 157 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, 158 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, 159 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, 160 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, 161 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, 162 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, 163 0x28003100, 0x28002F00, 0x00003100, 164}; 165 166typedef struct { 167 char *name; 168 int clock; 169 double refresh; 170 uint32_t oversample; 171 int hsync_end, hblank_start, hblank_end, htotal; 172 Bool progressive, trilevel_sync, component_only; 173 int vsync_start_f1, vsync_start_f2, vsync_len; 174 Bool veq_ena; 175 int veq_start_f1, veq_start_f2, veq_len; 176 int vi_end_f1, vi_end_f2, nbr_end; 177 Bool burst_ena; 178 int hburst_start, hburst_len; 179 int vburst_start_f1, vburst_end_f1; 180 int vburst_start_f2, vburst_end_f2; 181 int vburst_start_f3, vburst_end_f3; 182 int vburst_start_f4, vburst_end_f4; 183 /* 184 * subcarrier programming 185 */ 186 int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; 187 uint32_t sc_reset; 188 Bool pal_burst; 189 /* 190 * blank/black levels 191 */ 192 video_levels_t composite_levels, svideo_levels; 193 color_conversion_t composite_color, svideo_color; 194 const uint32_t *filter_table; 195 int max_srcw; 196} tv_mode_t; 197 198 199/* 200 * Sub carrier DDA 201 * 202 * I think this works as follows: 203 * 204 * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 205 * 206 * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value 207 * 208 * So, 209 * dda1_ideal = subcarrier/pixel * 4096 210 * dda1_inc = floor (dda1_ideal) 211 * dda2 = dda1_ideal - dda1_inc 212 * 213 * then pick a ratio for dda2 that gives the closest approximation. If 214 * you can't get close enough, you can play with dda3 as well. This 215 * seems likely to happen when dda2 is small as the jumps would be larger 216 * 217 * To invert this, 218 * 219 * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) 220 * 221 * The constants below were all computed using a 107.520MHz clock 222 */ 223 224/** 225 * Register programming values for TV modes. 226 * 227 * These values account for -1s required. 228 */ 229 230const static tv_mode_t tv_modes[] = { 231 { 232 .name = "NTSC-M", 233 .clock = 108000, 234 .refresh = 29.97, 235 .oversample = TV_OVERSAMPLE_8X, 236 .component_only = 0, 237 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 238 239 .hsync_end = 64, .hblank_end = 124, 240 .hblank_start = 836, .htotal = 857, 241 242 .progressive = FALSE, .trilevel_sync = FALSE, 243 244 .vsync_start_f1 = 6, .vsync_start_f2 = 7, 245 .vsync_len = 6, 246 247 .veq_ena = TRUE, .veq_start_f1 = 0, 248 .veq_start_f2 = 1, .veq_len = 18, 249 250 .vi_end_f1 = 20, .vi_end_f2 = 21, 251 .nbr_end = 240, 252 253 .burst_ena = TRUE, 254 .hburst_start = 72, .hburst_len = 34, 255 .vburst_start_f1 = 9, .vburst_end_f1 = 240, 256 .vburst_start_f2 = 10, .vburst_end_f2 = 240, 257 .vburst_start_f3 = 9, .vburst_end_f3 = 240, 258 .vburst_start_f4 = 10, .vburst_end_f4 = 240, 259 260 /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 261 .dda1_inc = 135, 262 .dda2_inc = 20800, .dda2_size = 27456, 263 .dda3_inc = 0, .dda3_size = 0, 264 .sc_reset = TV_SC_RESET_EVERY_4, 265 .pal_burst = FALSE, 266 267 .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, 268 .composite_color = { 269 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, 270 .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, 271 .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, 272 }, 273 274 .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, 275 .svideo_color = { 276 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, 277 .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, 278 .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, 279 }, 280 .filter_table = filter_table, 281 }, 282 { 283 .name = "NTSC-443", 284 .clock = 108000, 285 .refresh = 29.97, 286 .oversample = TV_OVERSAMPLE_8X, 287 .component_only = 0, 288 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ 289 .hsync_end = 64, .hblank_end = 124, 290 .hblank_start = 836, .htotal = 857, 291 292 .progressive = FALSE, .trilevel_sync = FALSE, 293 294 .vsync_start_f1 = 6, .vsync_start_f2 = 7, 295 .vsync_len = 6, 296 297 .veq_ena = TRUE, .veq_start_f1 = 0, 298 .veq_start_f2 = 1, .veq_len = 18, 299 300 .vi_end_f1 = 20, .vi_end_f2 = 21, 301 .nbr_end = 240, 302 303 .burst_ena = 8, 304 .hburst_start = 72, .hburst_len = 34, 305 .vburst_start_f1 = 9, .vburst_end_f1 = 240, 306 .vburst_start_f2 = 10, .vburst_end_f2 = 240, 307 .vburst_start_f3 = 9, .vburst_end_f3 = 240, 308 .vburst_start_f4 = 10, .vburst_end_f4 = 240, 309 310 /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 311 .dda1_inc = 168, 312 .dda2_inc = 4093, .dda2_size = 27456, 313 .dda3_inc = 310, .dda3_size = 525, 314 .sc_reset = TV_SC_RESET_NEVER, 315 .pal_burst = FALSE, 316 317 .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, 318 .composite_color = { 319 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, 320 .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, 321 .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, 322 }, 323 324 .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, 325 .svideo_color = { 326 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, 327 .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, 328 .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, 329 }, 330 .filter_table = filter_table, 331 }, 332 { 333 .name = "NTSC-J", 334 .clock = 108000, 335 .refresh = 29.97, 336 .oversample = TV_OVERSAMPLE_8X, 337 .component_only = 0, 338 339 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 340 .hsync_end = 64, .hblank_end = 124, 341 .hblank_start = 836, .htotal = 857, 342 343 .progressive = FALSE, .trilevel_sync = FALSE, 344 345 .vsync_start_f1 = 6, .vsync_start_f2 = 7, 346 .vsync_len = 6, 347 348 .veq_ena = TRUE, .veq_start_f1 = 0, 349 .veq_start_f2 = 1, .veq_len = 18, 350 351 .vi_end_f1 = 20, .vi_end_f2 = 21, 352 .nbr_end = 240, 353 354 .burst_ena = TRUE, 355 .hburst_start = 72, .hburst_len = 34, 356 .vburst_start_f1 = 9, .vburst_end_f1 = 240, 357 .vburst_start_f2 = 10, .vburst_end_f2 = 240, 358 .vburst_start_f3 = 9, .vburst_end_f3 = 240, 359 .vburst_start_f4 = 10, .vburst_end_f4 = 240, 360 361 /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 362 .dda1_inc = 135, 363 .dda2_inc = 20800, .dda2_size = 27456, 364 .dda3_inc = 0, .dda3_size = 0, 365 .sc_reset = TV_SC_RESET_EVERY_4, 366 .pal_burst = FALSE, 367 368 .composite_levels = { .blank = 225, .black = 225, .burst = 113 }, 369 .composite_color = { 370 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5495, 371 .ru =-0.0810, .gu =-0.1590, .bu = 0.2400, .au = 1.0000, 372 .rv = 0.3378, .gv =-0.2829, .bv =-0.0549, .av = 1.0000, 373 }, 374 375 .svideo_levels = { .blank = 266, .black = 266, .burst = 133 }, 376 .svideo_color = { 377 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6494, 378 .ru =-0.0957, .gu =-0.1879, .bu = 0.2836, .au = 1.0000, 379 .rv = 0.3992, .gv =-0.3343, .bv =-0.0649, .av = 1.0000, 380 }, 381 .filter_table = filter_table, 382 }, 383 { 384 .name = "PAL-M", 385 .clock = 108000, 386 .refresh = 29.97, 387 .oversample = TV_OVERSAMPLE_8X, 388 .component_only = 0, 389 390 /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ 391 .hsync_end = 64, .hblank_end = 124, 392 .hblank_start = 836, .htotal = 857, 393 394 .progressive = FALSE, .trilevel_sync = FALSE, 395 396 .vsync_start_f1 = 6, .vsync_start_f2 = 7, 397 .vsync_len = 6, 398 399 .veq_ena = TRUE, .veq_start_f1 = 0, 400 .veq_start_f2 = 1, .veq_len = 18, 401 402 .vi_end_f1 = 20, .vi_end_f2 = 21, 403 .nbr_end = 240, 404 405 .burst_ena = TRUE, 406 .hburst_start = 72, .hburst_len = 34, 407 .vburst_start_f1 = 9, .vburst_end_f1 = 240, 408 .vburst_start_f2 = 10, .vburst_end_f2 = 240, 409 .vburst_start_f3 = 9, .vburst_end_f3 = 240, 410 .vburst_start_f4 = 10, .vburst_end_f4 = 240, 411 412 /* desired 3.5800000 actual 3.5800000 clock 107.52 */ 413 .dda1_inc = 135, 414 .dda2_inc = 16704, .dda2_size = 27456, 415 .dda3_inc = 0, .dda3_size = 0, 416 .sc_reset = TV_SC_RESET_EVERY_8, 417 .pal_burst = TRUE, 418 419 .composite_levels = { .blank = 225, .black = 267, .burst = 113 }, 420 .composite_color = { 421 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, 422 .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, 423 .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, 424 }, 425 426 .svideo_levels = { .blank = 266, .black = 316, .burst = 133 }, 427 .svideo_color = { 428 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, 429 .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, 430 .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, 431 }, 432 .filter_table = filter_table, 433 }, 434 { 435 /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 436 .name = "PAL-N", 437 .clock = 108000, 438 .refresh = 25.0, 439 .oversample = TV_OVERSAMPLE_8X, 440 .component_only = 0, 441 442 .hsync_end = 64, .hblank_end = 128, 443 .hblank_start = 844, .htotal = 863, 444 445 .progressive = FALSE, .trilevel_sync = FALSE, 446 447 448 .vsync_start_f1 = 6, .vsync_start_f2 = 7, 449 .vsync_len = 6, 450 451 .veq_ena = TRUE, .veq_start_f1 = 0, 452 .veq_start_f2 = 1, .veq_len = 18, 453 454 .vi_end_f1 = 24, .vi_end_f2 = 25, 455 .nbr_end = 286, 456 457 .burst_ena = TRUE, 458 .hburst_start = 73, .hburst_len = 34, 459 .vburst_start_f1 = 8, .vburst_end_f1 = 285, 460 .vburst_start_f2 = 8, .vburst_end_f2 = 286, 461 .vburst_start_f3 = 9, .vburst_end_f3 = 286, 462 .vburst_start_f4 = 9, .vburst_end_f4 = 285, 463 464 465 /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 466 .dda1_inc = 135, 467 .dda2_inc = 23578, .dda2_size = 27648, 468 .dda3_inc = 134, .dda3_size = 625, 469 .sc_reset = TV_SC_RESET_EVERY_8, 470 .pal_burst = TRUE, 471 472 .composite_levels = { .blank = 225, .black = 267, .burst = 118 }, 473 .composite_color = { 474 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5082, 475 .ru =-0.0749, .gu =-0.1471, .bu = 0.2220, .au = 1.0000, 476 .rv = 0.3125, .gv =-0.2616, .bv =-0.0508, .av = 1.0000, 477 }, 478 479 .svideo_levels = { .blank = 266, .black = 316, .burst = 139 }, 480 .svideo_color = { 481 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6006, 482 .ru =-0.0885, .gu =-0.1738, .bu = 0.2624, .au = 1.0000, 483 .rv = 0.3693, .gv =-0.3092, .bv =-0.0601, .av = 1.0000, 484 }, 485 .filter_table = filter_table, 486 }, 487 { 488 /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ 489 .name = "PAL", 490 .clock = 108000, 491 .refresh = 25.0, 492 .oversample = TV_OVERSAMPLE_8X, 493 .component_only = 0, 494 495 .hsync_end = 64, .hblank_end = 142, 496 .hblank_start = 844, .htotal = 863, 497 498 .progressive = FALSE, .trilevel_sync = FALSE, 499 500 .vsync_start_f1 = 5, .vsync_start_f2 = 6, 501 .vsync_len = 5, 502 503 .veq_ena = TRUE, .veq_start_f1 = 0, 504 .veq_start_f2 = 1, .veq_len = 15, 505 506 .vi_end_f1 = 24, .vi_end_f2 = 25, 507 .nbr_end = 286, 508 509 .burst_ena = TRUE, 510 .hburst_start = 73, .hburst_len = 32, 511 .vburst_start_f1 = 8, .vburst_end_f1 = 285, 512 .vburst_start_f2 = 8, .vburst_end_f2 = 286, 513 .vburst_start_f3 = 9, .vburst_end_f3 = 286, 514 .vburst_start_f4 = 9, .vburst_end_f4 = 285, 515 516 /* desired 4.4336180 actual 4.4336180 clock 107.52 */ 517 .dda1_inc = 168, 518 .dda2_inc = 4122, .dda2_size = 27648, 519 .dda3_inc = 67, .dda3_size = 625, 520 .sc_reset = TV_SC_RESET_EVERY_8, 521 .pal_burst = TRUE, 522 523 .composite_levels = { .blank = 237, .black = 237, .burst = 118 }, 524 .composite_color = { 525 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.5379, 526 .ru =-0.0793, .gu =-0.1557, .bu = 0.2350, .au = 1.0000, 527 .rv = 0.3307, .gv =-0.2769, .bv =-0.0538, .av = 1.0000, 528 }, 529 530 .svideo_levels = { .blank = 280, .black = 280, .burst = 139 }, 531 .svideo_color = { 532 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6357, 533 .ru =-0.0937, .gu =-0.1840, .bu = 0.2777, .au = 1.0000, 534 .rv = 0.3908, .gv =-0.3273, .bv =-0.0636, .av = 1.0000, 535 }, 536 .filter_table = filter_table, 537 }, 538 { 539 .name = "480p@59.94Hz", 540 .clock = 107520, 541 .refresh = 59.94, 542 .oversample = TV_OVERSAMPLE_4X, 543 .component_only = 1, 544 545 .hsync_end = 64, .hblank_end = 122, 546 .hblank_start = 842, .htotal = 857, 547 548 .progressive = TRUE, .trilevel_sync = FALSE, 549 550 .vsync_start_f1 = 12, .vsync_start_f2 = 12, 551 .vsync_len = 12, 552 553 .veq_ena = FALSE, 554 555 .vi_end_f1 = 44, .vi_end_f2 = 44, 556 .nbr_end = 479, 557 558 .burst_ena = FALSE, 559 560 .filter_table = filter_table, 561 }, 562 { 563 .name = "480p@60Hz", 564 .clock = 107520, 565 .refresh = 60.0, 566 .oversample = TV_OVERSAMPLE_4X, 567 .component_only = 1, 568 569 .hsync_end = 64, .hblank_end = 122, 570 .hblank_start = 842, .htotal = 856, 571 572 .progressive = TRUE, .trilevel_sync = FALSE, 573 574 .vsync_start_f1 = 12, .vsync_start_f2 = 12, 575 .vsync_len = 12, 576 577 .veq_ena = FALSE, 578 579 .vi_end_f1 = 44, .vi_end_f2 = 44, 580 .nbr_end = 479, 581 582 .burst_ena = FALSE, 583 584 .filter_table = filter_table, 585 }, 586 { 587 .name = "576p", 588 .clock = 107520, 589 .refresh = 50.0, 590 .oversample = TV_OVERSAMPLE_4X, 591 .component_only = 1, 592 593 .hsync_end = 64, .hblank_end = 139, 594 .hblank_start = 859, .htotal = 863, 595 596 .progressive = TRUE, .trilevel_sync = FALSE, 597 598 .vsync_start_f1 = 10, .vsync_start_f2 = 10, 599 .vsync_len = 10, 600 601 .veq_ena = FALSE, 602 603 .vi_end_f1 = 48, .vi_end_f2 = 48, 604 .nbr_end = 575, 605 606 .burst_ena = FALSE, 607 608 .filter_table = filter_table, 609 }, 610 { 611 .name = "720p@60Hz", 612 .clock = 148800, 613 .refresh = 60.0, 614 .oversample = TV_OVERSAMPLE_2X, 615 .component_only = 1, 616 617 .hsync_end = 80, .hblank_end = 300, 618 .hblank_start = 1580, .htotal = 1649, 619 620 .progressive = TRUE, .trilevel_sync = TRUE, 621 622 .vsync_start_f1 = 10, .vsync_start_f2 = 10, 623 .vsync_len = 10, 624 625 .veq_ena = FALSE, 626 627 .vi_end_f1 = 29, .vi_end_f2 = 29, 628 .nbr_end = 719, 629 630 .burst_ena = FALSE, 631 632 .filter_table = filter_table, 633 }, 634 { 635 .name = "720p@59.94Hz", 636 .clock = 148800, 637 .refresh = 59.94, 638 .oversample = TV_OVERSAMPLE_2X, 639 .component_only = 1, 640 641 .hsync_end = 80, .hblank_end = 300, 642 .hblank_start = 1580, .htotal = 1651, 643 644 .progressive = TRUE, .trilevel_sync = TRUE, 645 646 .vsync_start_f1 = 10, .vsync_start_f2 = 10, 647 .vsync_len = 10, 648 649 .veq_ena = FALSE, 650 651 .vi_end_f1 = 29, .vi_end_f2 = 29, 652 .nbr_end = 719, 653 654 .burst_ena = FALSE, 655 656 .filter_table = filter_table, 657 }, 658 { 659 .name = "720p@50Hz", 660 .clock = 148800, 661 .refresh = 50.0, 662 .oversample = TV_OVERSAMPLE_2X, 663 .component_only = 1, 664 665 .hsync_end = 80, .hblank_end = 300, 666 .hblank_start = 1580, .htotal = 1979, 667 668 .progressive = TRUE, .trilevel_sync = TRUE, 669 670 .vsync_start_f1 = 10, .vsync_start_f2 = 10, 671 .vsync_len = 10, 672 673 .veq_ena = FALSE, 674 675 .vi_end_f1 = 29, .vi_end_f2 = 29, 676 .nbr_end = 719, 677 678 .burst_ena = FALSE, 679 680 .filter_table = filter_table, 681 .max_srcw = 800 682 }, 683 { 684 .name = "1080i@50Hz", 685 .clock = 148800, 686 .refresh = 25.0, 687 .oversample = TV_OVERSAMPLE_2X, 688 .component_only = 1, 689 690 .hsync_end = 88, .hblank_end = 235, 691 .hblank_start = 2155, .htotal = 2639, 692 693 .progressive = FALSE, .trilevel_sync = TRUE, 694 695 .vsync_start_f1 = 4, .vsync_start_f2 = 5, 696 .vsync_len = 10, 697 698 .veq_ena = TRUE, .veq_start_f1 = 4, 699 .veq_start_f2 = 4, .veq_len = 10, 700 701 .vi_end_f1 = 21, .vi_end_f2 = 22, 702 .nbr_end = 539, 703 704 .burst_ena = FALSE, 705 706 .filter_table = filter_table, 707 }, 708 { 709 .name = "1080i@60Hz", 710 .clock = 148800, 711 .refresh = 30.0, 712 .oversample = TV_OVERSAMPLE_2X, 713 .component_only = 1, 714 715 .hsync_end = 88, .hblank_end = 235, 716 .hblank_start = 2155, .htotal = 2199, 717 718 .progressive = FALSE, .trilevel_sync = TRUE, 719 720 .vsync_start_f1 = 4, .vsync_start_f2 = 5, 721 .vsync_len = 10, 722 723 .veq_ena = TRUE, .veq_start_f1 = 4, 724 .veq_start_f2 = 4, .veq_len = 10, 725 726 .vi_end_f1 = 21, .vi_end_f2 = 22, 727 .nbr_end = 539, 728 729 .burst_ena = FALSE, 730 731 .filter_table = filter_table, 732 }, 733 { 734 .name = "1080i@59.94Hz", 735 .clock = 148800, 736 .refresh = 29.97, 737 .oversample = TV_OVERSAMPLE_2X, 738 .component_only = 1, 739 740 .hsync_end = 88, .hblank_end = 235, 741 .hblank_start = 2155, .htotal = 2201, 742 743 .progressive = FALSE, .trilevel_sync = TRUE, 744 745 .vsync_start_f1 = 4, .vsync_start_f2 = 5, 746 .vsync_len = 10, 747 748 .veq_ena = TRUE, .veq_start_f1 = 4, 749 .veq_start_f2 = 4, .veq_len = 10, 750 751 752 .vi_end_f1 = 21, .vi_end_f2 = 22, 753 .nbr_end = 539, 754 755 .burst_ena = FALSE, 756 757 .filter_table = filter_table, 758 }, 759}; 760 761#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0]) 762 763static const video_levels_t component_level = { 764 .blank = 279, .black = 279, .burst = 0, 765}; 766 767static const color_conversion_t sdtv_component_color = { 768 .ry = 0.2990, .gy = 0.5870, .by = 0.1140, .ay = 0.6364, 769 .ru =-0.1687, .gu =-0.3313, .bu = 0.5000, .au = 1.0000, 770 .rv = 0.5000, .gv =-0.4187, .bv =-0.0813, .av = 1.0000, 771}; 772 773static const color_conversion_t hdtv_component_color = { 774 .ry = 0.2126, .gy = 0.7152, .by = 0.0722, .ay = 0.6364, 775 .ru =-0.1146, .gu =-0.3854, .bu = 0.5000, .au = 1.0000, 776 .rv = 0.5000, .gv =-0.4542, .bv =-0.0458, .av = 1.0000, 777}; 778 779static void 780i830_tv_dpms(xf86OutputPtr output, int mode) 781{ 782 ScrnInfoPtr pScrn = output->scrn; 783 I830Ptr pI830 = I830PTR(pScrn); 784 785 switch(mode) { 786 case DPMSModeOn: 787 OUTREG(TV_CTL, INREG(TV_CTL) | TV_ENC_ENABLE); 788 break; 789 case DPMSModeStandby: 790 case DPMSModeSuspend: 791 case DPMSModeOff: 792 OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE); 793 break; 794 } 795 i830WaitForVblank(pScrn); 796} 797 798static void 799i830_tv_save(xf86OutputPtr output) 800{ 801 ScrnInfoPtr pScrn = output->scrn; 802 I830Ptr pI830 = I830PTR(pScrn); 803 I830OutputPrivatePtr intel_output = output->driver_private; 804 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 805 int i; 806 807 dev_priv->save_TV_H_CTL_1 = INREG(TV_H_CTL_1); 808 dev_priv->save_TV_H_CTL_2 = INREG(TV_H_CTL_2); 809 dev_priv->save_TV_H_CTL_3 = INREG(TV_H_CTL_3); 810 dev_priv->save_TV_V_CTL_1 = INREG(TV_V_CTL_1); 811 dev_priv->save_TV_V_CTL_2 = INREG(TV_V_CTL_2); 812 dev_priv->save_TV_V_CTL_3 = INREG(TV_V_CTL_3); 813 dev_priv->save_TV_V_CTL_4 = INREG(TV_V_CTL_4); 814 dev_priv->save_TV_V_CTL_5 = INREG(TV_V_CTL_5); 815 dev_priv->save_TV_V_CTL_6 = INREG(TV_V_CTL_6); 816 dev_priv->save_TV_V_CTL_7 = INREG(TV_V_CTL_7); 817 dev_priv->save_TV_SC_CTL_1 = INREG(TV_SC_CTL_1); 818 dev_priv->save_TV_SC_CTL_2 = INREG(TV_SC_CTL_2); 819 dev_priv->save_TV_SC_CTL_3 = INREG(TV_SC_CTL_3); 820 821 dev_priv->save_TV_CSC_Y = INREG(TV_CSC_Y); 822 dev_priv->save_TV_CSC_Y2 = INREG(TV_CSC_Y2); 823 dev_priv->save_TV_CSC_U = INREG(TV_CSC_U); 824 dev_priv->save_TV_CSC_U2 = INREG(TV_CSC_U2); 825 dev_priv->save_TV_CSC_V = INREG(TV_CSC_V); 826 dev_priv->save_TV_CSC_V2 = INREG(TV_CSC_V2); 827 dev_priv->save_TV_CLR_KNOBS = INREG(TV_CLR_KNOBS); 828 dev_priv->save_TV_CLR_LEVEL = INREG(TV_CLR_LEVEL); 829 dev_priv->save_TV_WIN_POS = INREG(TV_WIN_POS); 830 dev_priv->save_TV_WIN_SIZE = INREG(TV_WIN_SIZE); 831 dev_priv->save_TV_FILTER_CTL_1 = INREG(TV_FILTER_CTL_1); 832 dev_priv->save_TV_FILTER_CTL_2 = INREG(TV_FILTER_CTL_2); 833 dev_priv->save_TV_FILTER_CTL_3 = INREG(TV_FILTER_CTL_3); 834 835 for (i = 0; i < 60; i++) 836 dev_priv->save_TV_H_LUMA[i] = INREG(TV_H_LUMA_0 + (i <<2)); 837 for (i = 0; i < 60; i++) 838 dev_priv->save_TV_H_CHROMA[i] = INREG(TV_H_CHROMA_0 + (i <<2)); 839 for (i = 0; i < 43; i++) 840 dev_priv->save_TV_V_LUMA[i] = INREG(TV_V_LUMA_0 + (i <<2)); 841 for (i = 0; i < 43; i++) 842 dev_priv->save_TV_V_CHROMA[i] = INREG(TV_V_CHROMA_0 + (i <<2)); 843 844 dev_priv->save_TV_DAC = INREG(TV_DAC); 845 dev_priv->save_TV_CTL = INREG(TV_CTL); 846} 847 848static void 849i830_tv_restore(xf86OutputPtr output) 850{ 851 ScrnInfoPtr pScrn = output->scrn; 852 I830Ptr pI830 = I830PTR(pScrn); 853 I830OutputPrivatePtr intel_output = output->driver_private; 854 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 855 int i; 856 857 xf86CrtcPtr crtc = output->crtc; 858 I830CrtcPrivatePtr intel_crtc; 859 if (!crtc) 860 return; 861 intel_crtc = crtc->driver_private; 862 OUTREG(TV_H_CTL_1, dev_priv->save_TV_H_CTL_1); 863 OUTREG(TV_H_CTL_2, dev_priv->save_TV_H_CTL_2); 864 OUTREG(TV_H_CTL_3, dev_priv->save_TV_H_CTL_3); 865 OUTREG(TV_V_CTL_1, dev_priv->save_TV_V_CTL_1); 866 OUTREG(TV_V_CTL_2, dev_priv->save_TV_V_CTL_2); 867 OUTREG(TV_V_CTL_3, dev_priv->save_TV_V_CTL_3); 868 OUTREG(TV_V_CTL_4, dev_priv->save_TV_V_CTL_4); 869 OUTREG(TV_V_CTL_5, dev_priv->save_TV_V_CTL_5); 870 OUTREG(TV_V_CTL_6, dev_priv->save_TV_V_CTL_6); 871 OUTREG(TV_V_CTL_7, dev_priv->save_TV_V_CTL_7); 872 OUTREG(TV_SC_CTL_1, dev_priv->save_TV_SC_CTL_1); 873 OUTREG(TV_SC_CTL_2, dev_priv->save_TV_SC_CTL_2); 874 OUTREG(TV_SC_CTL_3, dev_priv->save_TV_SC_CTL_3); 875 876 OUTREG(TV_CSC_Y, dev_priv->save_TV_CSC_Y); 877 OUTREG(TV_CSC_Y2, dev_priv->save_TV_CSC_Y2); 878 OUTREG(TV_CSC_U, dev_priv->save_TV_CSC_U); 879 OUTREG(TV_CSC_U2, dev_priv->save_TV_CSC_U2); 880 OUTREG(TV_CSC_V, dev_priv->save_TV_CSC_V); 881 OUTREG(TV_CSC_V2, dev_priv->save_TV_CSC_V2); 882 OUTREG(TV_CLR_KNOBS, dev_priv->save_TV_CLR_KNOBS); 883 OUTREG(TV_CLR_LEVEL, dev_priv->save_TV_CLR_LEVEL); 884 885 { 886 int pipeconf_reg = (intel_crtc->pipe == 0) ? PIPEACONF : PIPEBCONF; 887 int dspcntr_reg = (intel_crtc->plane == 0) ? DSPACNTR : DSPBCNTR; 888 int pipeconf = INREG(pipeconf_reg); 889 int dspcntr = INREG(dspcntr_reg); 890 int dspbase_reg = (intel_crtc->plane == 0) ? DSPABASE : DSPBBASE; 891 /* Pipe must be off here */ 892 OUTREG(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); 893 /* Flush the plane changes */ 894 OUTREG(dspbase_reg, INREG(dspbase_reg)); 895 896 if (!IS_I9XX(pI830)) { 897 /* Wait for vblank for the disable to take effect */ 898 i830WaitForVblank(pScrn); 899 } 900 901 OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); 902 /* Wait for vblank for the disable to take effect. */ 903 i830WaitForVblank(pScrn); 904 905 /* Filter ctl must be set before TV_WIN_SIZE */ 906 OUTREG(TV_FILTER_CTL_1, dev_priv->save_TV_FILTER_CTL_1); 907 OUTREG(TV_FILTER_CTL_2, dev_priv->save_TV_FILTER_CTL_2); 908 OUTREG(TV_FILTER_CTL_3, dev_priv->save_TV_FILTER_CTL_3); 909 OUTREG(TV_WIN_POS, dev_priv->save_TV_WIN_POS); 910 OUTREG(TV_WIN_SIZE, dev_priv->save_TV_WIN_SIZE); 911 OUTREG(pipeconf_reg, pipeconf); 912 OUTREG(dspcntr_reg, dspcntr); 913 /* Flush the plane changes */ 914 OUTREG(dspbase_reg, INREG(dspbase_reg)); 915 } 916 917 for (i = 0; i < 60; i++) 918 OUTREG(TV_H_LUMA_0 + (i <<2), dev_priv->save_TV_H_LUMA[i]); 919 for (i = 0; i < 60; i++) 920 OUTREG(TV_H_CHROMA_0 + (i <<2), dev_priv->save_TV_H_CHROMA[i]); 921 for (i = 0; i < 43; i++) 922 OUTREG(TV_V_LUMA_0 + (i <<2), dev_priv->save_TV_V_LUMA[i]); 923 for (i = 0; i < 43; i++) 924 OUTREG(TV_V_CHROMA_0 + (i <<2), dev_priv->save_TV_V_CHROMA[i]); 925 926 OUTREG(TV_DAC, dev_priv->save_TV_DAC); 927 OUTREG(TV_CTL, dev_priv->save_TV_CTL); 928 i830WaitForVblank(pScrn); 929} 930 931static const tv_mode_t * 932i830_tv_mode_lookup (char *tv_format) 933{ 934 int i; 935 936 for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) 937 { 938 const tv_mode_t *tv_mode = &tv_modes[i]; 939 940 if (xf86nameCompare (tv_format, tv_mode->name) == 0) 941 return tv_mode; 942 } 943 return NULL; 944} 945 946static const tv_mode_t * 947i830_tv_mode_find (xf86OutputPtr output) 948{ 949 I830OutputPrivatePtr intel_output = output->driver_private; 950 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 951 952 return i830_tv_mode_lookup (dev_priv->tv_format); 953} 954 955static int 956i830_tv_mode_valid(xf86OutputPtr output, DisplayModePtr mode) 957{ 958 const tv_mode_t *tv_mode = i830_tv_mode_find (output); 959 960 if (tv_mode && fabs (tv_mode->refresh - xf86ModeVRefresh (mode)) < 1.0) 961 return MODE_OK; 962 return MODE_CLOCK_RANGE; 963} 964 965 966static Bool 967i830_tv_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 968 DisplayModePtr adjusted_mode) 969{ 970 ScrnInfoPtr pScrn = output->scrn; 971 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 972 int i; 973 const tv_mode_t *tv_mode = i830_tv_mode_find (output); 974 975 if (!tv_mode) 976 return FALSE; 977 978 for (i = 0; i < xf86_config->num_output; i++) 979 { 980 xf86OutputPtr other_output = xf86_config->output[i]; 981 982 if (other_output != output && other_output->crtc == output->crtc) 983 return FALSE; 984 } 985 986 adjusted_mode->Clock = tv_mode->clock; 987 return TRUE; 988} 989 990static uint32_t 991i830_float_to_csc (float fin) 992{ 993 uint32_t exp; 994 uint32_t mant; 995 uint32_t ret; 996 float f = fin; 997 998 /* somehow the color conversion knows the signs of all the values */ 999 if (f < 0) f = -f; 1000 1001 if (f >= 1) 1002 { 1003 exp = 0x7; 1004 mant = 1 << 8; 1005 } 1006 else 1007 { 1008 for (exp = 0; exp < 3 && f < 0.5; exp++) 1009 f *= 2.0; 1010 mant = (f * (1 << 9) + 0.5); 1011 if (mant >= (1 << 9)) 1012 mant = (1 << 9) - 1; 1013 } 1014 ret = (exp << 9) | mant; 1015 return ret; 1016} 1017 1018static uint16_t 1019i830_float_to_luma (float f) 1020{ 1021 uint16_t ret; 1022 1023 ret = (f * (1 << 9)); 1024 return ret; 1025} 1026 1027static uint8_t 1028float_to_float_2_6(float fin) 1029{ 1030 uint8_t exp; 1031 uint8_t mant; 1032 float f = fin; 1033 uint32_t tmp; 1034 1035 if (f < 0) f = -f; 1036 1037 tmp = f; 1038 for (exp = 0; exp <= 3 && tmp > 0; exp++) 1039 tmp /= 2; 1040 1041 mant = (f * (1 << 6) + 0.5); 1042 mant >>= exp; 1043 if (mant > (1 << 6)) 1044 mant = (1 << 6) - 1; 1045 1046 return (exp << 6) | mant; 1047} 1048 1049static uint8_t 1050float_to_fix_2_6(float f) 1051{ 1052 uint8_t ret; 1053 1054 ret = f * (1 << 6); 1055 return ret; 1056} 1057 1058static void 1059i830_tv_update_brightness(I830Ptr pI830, uint8_t brightness) 1060{ 1061 /* brightness in 2's comp value */ 1062 uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_BRIGHTNESS_MASK; 1063 int8_t bri = brightness - 128; /* remove bias */ 1064 1065 val |= (bri << TV_BRIGHTNESS_SHIFT) & TV_BRIGHTNESS_MASK; 1066 OUTREG(TV_CLR_KNOBS, val); 1067} 1068 1069static void 1070i830_tv_update_contrast(I830Ptr pI830, uint8_t contrast) 1071{ 1072 uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_CONTRAST_MASK;; 1073 float con; 1074 uint8_t c; 1075 1076 if (IS_I965G(pI830)) { 1077 /* 2.6 fixed point */ 1078 con = 3.0 * ((float) contrast / 255); 1079 c = float_to_fix_2_6(con); 1080 } else { 1081 /* 2.6 floating point */ 1082 con = 2.65625 * ((float) contrast / 255); 1083 c = float_to_float_2_6(con); 1084 } 1085 val |= (c << TV_CONTRAST_SHIFT) & TV_CONTRAST_MASK; 1086 OUTREG(TV_CLR_KNOBS, val); 1087} 1088 1089static void 1090i830_tv_update_saturation(I830Ptr pI830, uint8_t saturation) 1091{ 1092 uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_SATURATION_MASK; 1093 float sat; 1094 uint8_t s; 1095 1096 /* same as contrast */ 1097 if (IS_I965G(pI830)) { 1098 sat = 3.0 * ((float) saturation / 255); 1099 s = float_to_fix_2_6(sat); 1100 } else { 1101 sat = 2.65625 * ((float) saturation / 255); 1102 s = float_to_float_2_6(sat); 1103 } 1104 val |= (s << TV_SATURATION_SHIFT) & TV_SATURATION_MASK; 1105 OUTREG(TV_CLR_KNOBS, val); 1106} 1107 1108static void 1109i830_tv_update_hue(I830Ptr pI830, uint8_t hue) 1110{ 1111 uint32_t val = INREG(TV_CLR_KNOBS) & ~TV_HUE_MASK; 1112 1113 val |= (hue << TV_HUE_SHIFT) & TV_HUE_MASK; 1114 OUTREG(TV_CLR_KNOBS, val); 1115} 1116 1117static void 1118i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, 1119 DisplayModePtr adjusted_mode) 1120{ 1121 ScrnInfoPtr pScrn = output->scrn; 1122 I830Ptr pI830 = I830PTR(pScrn); 1123 xf86CrtcPtr crtc = output->crtc; 1124 I830OutputPrivatePtr intel_output = output->driver_private; 1125 I830CrtcPrivatePtr intel_crtc = crtc->driver_private; 1126 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1127 const tv_mode_t *tv_mode = i830_tv_mode_find (output); 1128 uint32_t tv_ctl; 1129 uint32_t hctl1, hctl2, hctl3; 1130 uint32_t vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; 1131 uint32_t scctl1, scctl2, scctl3; 1132 int i, j; 1133 const video_levels_t *video_levels; 1134 const color_conversion_t *color_conversion; 1135 Bool burst_ena; 1136 1137 if (!tv_mode) 1138 return; /* can't happen (mode_prepare prevents this) */ 1139 1140 tv_ctl = INREG(TV_CTL); 1141 tv_ctl &= TV_CTL_SAVE; 1142 1143 switch (dev_priv->type) { 1144 default: 1145 case TV_TYPE_UNKNOWN: 1146 case TV_TYPE_COMPOSITE: 1147 tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; 1148 video_levels = &tv_mode->composite_levels; 1149 color_conversion = &tv_mode->composite_color; 1150 burst_ena = tv_mode->burst_ena; 1151 break; 1152 case TV_TYPE_COMPONENT: 1153 tv_ctl |= TV_ENC_OUTPUT_COMPONENT; 1154 video_levels = &component_level; 1155 if (tv_mode->burst_ena) 1156 color_conversion = &sdtv_component_color; 1157 else 1158 color_conversion = &hdtv_component_color; 1159 burst_ena = FALSE; 1160 break; 1161 case TV_TYPE_SVIDEO: 1162 tv_ctl |= TV_ENC_OUTPUT_SVIDEO; 1163 video_levels = &tv_mode->svideo_levels; 1164 color_conversion = &tv_mode->svideo_color; 1165 burst_ena = tv_mode->burst_ena; 1166 break; 1167 } 1168 hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | 1169 (tv_mode->htotal << TV_HTOTAL_SHIFT); 1170 1171 hctl2 = (tv_mode->hburst_start << 16) | 1172 (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); 1173 1174 if (burst_ena) 1175 hctl2 |= TV_BURST_ENA; 1176 1177 hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | 1178 (tv_mode->hblank_end << TV_HBLANK_END_SHIFT); 1179 1180 vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | 1181 (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | 1182 (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); 1183 1184 vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | 1185 (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | 1186 (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); 1187 1188 vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | 1189 (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | 1190 (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); 1191 1192 if (tv_mode->veq_ena) 1193 vctl3 |= TV_EQUAL_ENA; 1194 1195 vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | 1196 (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); 1197 1198 vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | 1199 (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); 1200 1201 vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | 1202 (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); 1203 1204 vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | 1205 (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); 1206 1207 if (intel_crtc->pipe == 1) 1208 tv_ctl |= TV_ENC_PIPEB_SELECT; 1209 tv_ctl |= tv_mode->oversample; 1210 1211 if (tv_mode->progressive) 1212 tv_ctl |= TV_PROGRESSIVE; 1213 if (tv_mode->trilevel_sync) 1214 tv_ctl |= TV_TRILEVEL_SYNC; 1215 if (tv_mode->pal_burst) 1216 tv_ctl |= TV_PAL_BURST; 1217 scctl1 = 0; 1218 if (tv_mode->dda1_inc) 1219 scctl1 |= TV_SC_DDA1_EN; 1220 1221 if (tv_mode->dda2_inc) 1222 scctl1 |= TV_SC_DDA2_EN; 1223 1224 if (tv_mode->dda3_inc) 1225 scctl1 |= TV_SC_DDA3_EN; 1226 1227 scctl1 |= tv_mode->sc_reset; 1228 scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; 1229 scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; 1230 1231 scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | 1232 tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; 1233 1234 scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | 1235 tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; 1236 1237 /* Enable two fixes for the chips that need them. */ 1238 if (DEVICE_ID(pI830->PciInfo) < PCI_CHIP_I945_G) 1239 tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; 1240 1241 OUTREG(TV_H_CTL_1, hctl1); 1242 OUTREG(TV_H_CTL_2, hctl2); 1243 OUTREG(TV_H_CTL_3, hctl3); 1244 OUTREG(TV_V_CTL_1, vctl1); 1245 OUTREG(TV_V_CTL_2, vctl2); 1246 OUTREG(TV_V_CTL_3, vctl3); 1247 OUTREG(TV_V_CTL_4, vctl4); 1248 OUTREG(TV_V_CTL_5, vctl5); 1249 OUTREG(TV_V_CTL_6, vctl6); 1250 OUTREG(TV_V_CTL_7, vctl7); 1251 OUTREG(TV_SC_CTL_1, scctl1); 1252 OUTREG(TV_SC_CTL_2, scctl2); 1253 OUTREG(TV_SC_CTL_3, scctl3); 1254 1255 OUTREG(TV_CSC_Y, 1256 (i830_float_to_csc(color_conversion->ry) << 16) | 1257 (i830_float_to_csc(color_conversion->gy))); 1258 OUTREG(TV_CSC_Y2, 1259 (i830_float_to_csc(color_conversion->by) << 16) | 1260 (i830_float_to_luma(color_conversion->ay))); 1261 1262 OUTREG(TV_CSC_U, 1263 (i830_float_to_csc(color_conversion->ru) << 16) | 1264 (i830_float_to_csc(color_conversion->gu))); 1265 1266 OUTREG(TV_CSC_U2, 1267 (i830_float_to_csc(color_conversion->bu) << 16) | 1268 (i830_float_to_luma(color_conversion->au))); 1269 1270 OUTREG(TV_CSC_V, 1271 (i830_float_to_csc(color_conversion->rv) << 16) | 1272 (i830_float_to_csc(color_conversion->gv))); 1273 1274 OUTREG(TV_CSC_V2, 1275 (i830_float_to_csc(color_conversion->bv) << 16) | 1276 (i830_float_to_luma(color_conversion->av))); 1277 1278 OUTREG(TV_CLR_LEVEL, ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | 1279 (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); 1280 { 1281 int pipeconf_reg = (intel_crtc->pipe == 0) ? PIPEACONF : PIPEBCONF; 1282 int dspcntr_reg = (intel_crtc->plane == 0) ? DSPACNTR : DSPBCNTR; 1283 int pipeconf = INREG(pipeconf_reg); 1284 int dspcntr = INREG(dspcntr_reg); 1285 int dspbase_reg = (intel_crtc->plane == 0) ? DSPABASE : DSPBBASE; 1286 int xpos = 0x0, ypos = 0x0; 1287 unsigned int xsize, ysize; 1288 /* Pipe must be off here */ 1289 OUTREG(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); 1290 /* Flush the plane changes */ 1291 OUTREG(dspbase_reg, INREG(dspbase_reg)); 1292 1293 if (!IS_I9XX(pI830)) { 1294 /* Wait for vblank for the disable to take effect */ 1295 i830WaitForVblank(pScrn); 1296 } 1297 1298 OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); 1299 /* Wait for vblank for the disable to take effect. */ 1300 i830WaitForVblank(pScrn); 1301 1302 /* Filter ctl must be set before TV_WIN_SIZE */ 1303 OUTREG(TV_FILTER_CTL_1, TV_AUTO_SCALE); 1304 xsize = tv_mode->hblank_start - tv_mode->hblank_end; 1305 if (tv_mode->progressive) 1306 ysize = tv_mode->nbr_end + 1; 1307 else 1308 ysize = 2*tv_mode->nbr_end + 1; 1309 1310 xpos += dev_priv->margin[TV_MARGIN_LEFT]; 1311 ypos += dev_priv->margin[TV_MARGIN_TOP]; 1312 xsize -= (dev_priv->margin[TV_MARGIN_LEFT] + 1313 dev_priv->margin[TV_MARGIN_RIGHT]); 1314 ysize -= (dev_priv->margin[TV_MARGIN_TOP] + 1315 dev_priv->margin[TV_MARGIN_BOTTOM]); 1316 OUTREG(TV_WIN_POS, (xpos<<16)|ypos); 1317 OUTREG(TV_WIN_SIZE, (xsize<<16)|ysize); 1318 1319 OUTREG(pipeconf_reg, pipeconf); 1320 OUTREG(dspcntr_reg, dspcntr); 1321 /* Flush the plane changes */ 1322 OUTREG(dspbase_reg, INREG(dspbase_reg)); 1323 } 1324 1325 j = 0; 1326 for (i = 0; i < 60; i++) 1327 OUTREG(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1328 for (i = 0; i < 60; i++) 1329 OUTREG(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1330 for (i = 0; i < 43; i++) 1331 OUTREG(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); 1332 for (i = 0; i < 43; i++) 1333 OUTREG(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); 1334 OUTREG(TV_DAC, 0); 1335 OUTREG(TV_CTL, tv_ctl); 1336 i830WaitForVblank(pScrn); 1337} 1338 1339static const DisplayModeRec reported_modes[] = { 1340 { 1341 .name = "NTSC 480i", 1342 .Clock = 107520, 1343 .HDisplay = 1280, 1344 .HSyncStart = 1368, 1345 .HSyncEnd = 1496, 1346 .HTotal = 1712, 1347 1348 .VDisplay = 1024, 1349 .VSyncStart = 1027, 1350 .VSyncEnd = 1034, 1351 .VTotal = 1104, 1352 .type = M_T_DRIVER 1353 }, 1354}; 1355 1356/** 1357 * Detects TV presence by checking for load. 1358 * 1359 * Requires that the current pipe's DPLL is active. 1360 1361 * \return TRUE if TV is connected. 1362 * \return FALSE if TV is disconnected. 1363 */ 1364static int 1365i830_tv_detect_type (xf86CrtcPtr crtc, 1366 xf86OutputPtr output) 1367{ 1368 ScrnInfoPtr pScrn = output->scrn; 1369 I830Ptr pI830 = I830PTR(pScrn); 1370 I830OutputPrivatePtr intel_output = output->driver_private; 1371 uint32_t tv_ctl, save_tv_ctl; 1372 uint32_t tv_dac, save_tv_dac; 1373 int type = TV_TYPE_UNKNOWN; 1374 1375 tv_dac = INREG(TV_DAC); 1376 /* 1377 * Detect TV by polling) 1378 */ 1379 if (intel_output->load_detect_temp) 1380 { 1381 /* TV not currently running, prod it with destructive detect */ 1382 save_tv_dac = tv_dac; 1383 tv_ctl = INREG(TV_CTL); 1384 save_tv_ctl = tv_ctl; 1385 tv_ctl &= ~TV_ENC_ENABLE; 1386 tv_ctl &= ~TV_TEST_MODE_MASK; 1387 tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; 1388 tv_dac &= ~TVDAC_SENSE_MASK; 1389 tv_dac &= ~DAC_A_MASK; 1390 tv_dac &= ~DAC_B_MASK; 1391 tv_dac &= ~DAC_C_MASK; 1392 tv_dac |= (TVDAC_STATE_CHG_EN | 1393 TVDAC_A_SENSE_CTL | 1394 TVDAC_B_SENSE_CTL | 1395 TVDAC_C_SENSE_CTL | 1396 DAC_CTL_OVERRIDE | 1397 DAC_A_0_7_V | 1398 DAC_B_0_7_V | 1399 DAC_C_0_7_V); 1400 OUTREG(TV_CTL, tv_ctl); 1401 OUTREG(TV_DAC, tv_dac); 1402 i830WaitForVblank(pScrn); 1403 tv_dac = INREG(TV_DAC); 1404 OUTREG(TV_DAC, save_tv_dac); 1405 OUTREG(TV_CTL, save_tv_ctl); 1406 i830WaitForVblank(pScrn); 1407 } 1408 /* 1409 * A B C 1410 * 0 1 1 Composite 1411 * 1 0 X svideo 1412 * 0 0 0 Component 1413 */ 1414 if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { 1415 if (pI830->debug_modes) { 1416 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 1417 "Detected Composite TV connection\n"); 1418 } 1419 type = TV_TYPE_COMPOSITE; 1420 } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { 1421 if (pI830->debug_modes) { 1422 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 1423 "Detected S-Video TV connection\n"); 1424 } 1425 type = TV_TYPE_SVIDEO; 1426 } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { 1427 if (pI830->debug_modes) { 1428 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 1429 "Detected Component TV connection\n"); 1430 } 1431 type = TV_TYPE_COMPONENT; 1432 } else { 1433 if (pI830->debug_modes) { 1434 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 1435 "No TV connection detected\n"); 1436 } 1437 type = TV_TYPE_NONE; 1438 } 1439 1440 return type; 1441} 1442 1443#ifdef RANDR_12_INTERFACE 1444static int 1445i830_tv_format_configure_property (xf86OutputPtr output); 1446#endif 1447 1448/** 1449 * Detect the TV connection. 1450 * 1451 * Currently this always returns OUTPUT_STATUS_UNKNOWN, as we need to be sure 1452 * we have a pipe programmed in order to probe the TV. 1453 */ 1454static xf86OutputStatus 1455i830_tv_detect(xf86OutputPtr output) 1456{ 1457 xf86CrtcPtr crtc; 1458 DisplayModeRec mode; 1459 I830OutputPrivatePtr intel_output = output->driver_private; 1460 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1461 int dpms_mode; 1462 int type = dev_priv->type; 1463 1464 /* If TV connector type set by user, always return connected */ 1465 if (dev_priv->force_type) 1466 return XF86OutputStatusConnected; 1467 1468 mode = reported_modes[0]; 1469 xf86SetModeCrtc (&mode, INTERLACE_HALVE_V); 1470 crtc = i830GetLoadDetectPipe (output, &mode, &dpms_mode); 1471 if (crtc) 1472 { 1473 type = i830_tv_detect_type (crtc, output); 1474 i830ReleaseLoadDetectPipe (output, dpms_mode); 1475 } 1476 1477 if (type != dev_priv->type) 1478 { 1479 dev_priv->type = type; 1480#ifdef RANDR_12_INTERFACE 1481 i830_tv_format_configure_property (output); 1482#endif 1483 } 1484 1485 switch (type) { 1486 case TV_TYPE_NONE: 1487 return XF86OutputStatusDisconnected; 1488 case TV_TYPE_UNKNOWN: 1489 return XF86OutputStatusUnknown; 1490 default: 1491 return XF86OutputStatusConnected; 1492 } 1493} 1494 1495static struct input_res { 1496 char *name; 1497 int w, h; 1498} input_res_table[] = 1499{ 1500 {"640x480", 640, 480}, 1501 {"800x600", 800, 600}, 1502 {"848x480", 848, 480}, 1503 {"1024x768", 1024, 768}, 1504 {"1280x720", 1280, 720}, 1505 {"1280x1024", 1280, 1024}, 1506 {"1920x1080", 1920, 1080}, 1507}; 1508 1509/** 1510 * Stub get_modes function. 1511 * 1512 * This should probably return a set of fixed modes, unless we can figure out 1513 * how to probe modes off of TV connections. 1514 */ 1515 1516static DisplayModePtr 1517i830_tv_get_modes(xf86OutputPtr output) 1518{ 1519 DisplayModePtr ret = NULL, mode_ptr; 1520 int j; 1521 const tv_mode_t *tv_mode = i830_tv_mode_find (output); 1522 1523 for (j = 0; j < sizeof(input_res_table)/sizeof(input_res_table[0]); j++) 1524 { 1525 struct input_res *input = &input_res_table[j]; 1526 unsigned int hactive_s = input->w; 1527 unsigned int vactive_s = input->h; 1528 1529 if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) 1530 continue; 1531 1532 if (input->w > 1024 && (!tv_mode->progressive 1533 && !tv_mode->component_only)) 1534 continue; 1535 1536 mode_ptr = xnfcalloc(1, sizeof(DisplayModeRec)); 1537 mode_ptr->name = xnfalloc(strlen(input->name) + 1); 1538 strcpy (mode_ptr->name, input->name); 1539 1540 mode_ptr->HDisplay = hactive_s; 1541 mode_ptr->HSyncStart = hactive_s + 1; 1542 mode_ptr->HSyncEnd = hactive_s + 64; 1543 if ( mode_ptr->HSyncEnd <= mode_ptr->HSyncStart) 1544 mode_ptr->HSyncEnd = mode_ptr->HSyncStart + 1; 1545 mode_ptr->HTotal = hactive_s + 96; 1546 1547 mode_ptr->VDisplay = vactive_s; 1548 mode_ptr->VSyncStart = vactive_s + 1; 1549 mode_ptr->VSyncEnd = vactive_s + 32; 1550 if ( mode_ptr->VSyncEnd <= mode_ptr->VSyncStart) 1551 mode_ptr->VSyncEnd = mode_ptr->VSyncStart + 1; 1552 mode_ptr->VTotal = vactive_s + 33; 1553 1554 mode_ptr->Clock = (int) (tv_mode->refresh * 1555 mode_ptr->VTotal * 1556 mode_ptr->HTotal / 1000.0); 1557 1558 mode_ptr->type = M_T_DRIVER; 1559 mode_ptr->next = ret; 1560 mode_ptr->prev = NULL; 1561 if (ret != NULL) 1562 ret->prev = mode_ptr; 1563 ret = mode_ptr; 1564 } 1565 1566 return ret; 1567} 1568 1569static void 1570i830_tv_destroy (xf86OutputPtr output) 1571{ 1572 if (output->driver_private) 1573 xfree (output->driver_private); 1574} 1575 1576#ifdef RANDR_12_INTERFACE 1577#define TV_FORMAT_NAME "TV_FORMAT" 1578static Atom tv_format_atom; 1579static Atom tv_format_name_atoms[NUM_TV_MODES]; 1580static Atom margin_atoms[4]; 1581static char *margin_names[4] = { 1582 "LEFT", "TOP", "RIGHT", "BOTTOM" 1583}; 1584 1585/** 1586 * contrast and saturation has different format on 915/945 with 965. 1587 * On 915/945, it's 2.6 floating point number. 1588 * On 965, it's 2.6 fixed point number. 1589 */ 1590#define TV_BRIGHTNESS_NAME "BRIGHTNESS" 1591#define TV_BRIGHTNESS_DEFAULT 128 /* bias */ 1592static Atom brightness_atom; 1593#define TV_CONTRAST_NAME "CONTRAST" 1594#define TV_CONTRAST_DEFAULT 0x40 1595#define TV_CONTRAST_DEFAULT_945G 0x60 1596static Atom contrast_atom; 1597#define TV_SATURATION_NAME "SATURATION" 1598#define TV_SATURATION_DEFAULT 0x40 1599#define TV_SATURATION_DEFAULT_945G 0x60 1600static Atom saturation_atom; 1601#define TV_HUE_NAME "HUE" 1602#define TV_HUE_DEFAULT 0 1603static Atom hue_atom; 1604 1605static Bool 1606i830_tv_format_set_property (xf86OutputPtr output) 1607{ 1608 I830OutputPrivatePtr intel_output = output->driver_private; 1609 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1610 const tv_mode_t *tv_mode = i830_tv_mode_lookup (dev_priv->tv_format); 1611 int err; 1612 1613 if (!tv_mode) 1614 tv_mode = &tv_modes[0]; 1615 err = RRChangeOutputProperty (output->randr_output, tv_format_atom, 1616 XA_ATOM, 32, PropModeReplace, 1, 1617 &tv_format_name_atoms[tv_mode - tv_modes], 1618 FALSE, TRUE); 1619 return err == Success; 1620} 1621 1622/** 1623 * Configure the TV_FORMAT property to list only supported formats 1624 * 1625 * Unless the connector is component, list only the formats supported by 1626 * svideo and composite 1627 */ 1628 1629static int 1630i830_tv_format_configure_property (xf86OutputPtr output) 1631{ 1632 I830OutputPrivatePtr intel_output = output->driver_private; 1633 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1634 Atom current_atoms[NUM_TV_MODES]; 1635 int num_atoms = 0; 1636 int i; 1637 1638 if (!output->randr_output) 1639 return Success; 1640 1641 for (i = 0; i < NUM_TV_MODES; i++) 1642 if (!tv_modes[i].component_only || dev_priv->type == TV_TYPE_COMPONENT) 1643 current_atoms[num_atoms++] = tv_format_name_atoms[i]; 1644 1645 return RRConfigureOutputProperty(output->randr_output, tv_format_atom, 1646 TRUE, FALSE, FALSE, 1647 num_atoms, (INT32 *) current_atoms); 1648} 1649 1650static void 1651i830_tv_color_set_property(xf86OutputPtr output, Atom property, 1652 uint8_t val) 1653{ 1654 ScrnInfoPtr pScrn = output->scrn; 1655 I830Ptr pI830 = I830PTR(pScrn); 1656 I830OutputPrivatePtr intel_output = output->driver_private; 1657 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1658 1659 if (property == brightness_atom) { 1660 dev_priv->brightness = val; 1661 i830_tv_update_brightness(pI830, val); 1662 } else if (property == contrast_atom) { 1663 dev_priv->contrast = val; 1664 i830_tv_update_contrast(pI830, val); 1665 } else if (property == saturation_atom) { 1666 dev_priv->saturation = val; 1667 i830_tv_update_saturation(pI830, val); 1668 } else if (property == hue_atom) { 1669 dev_priv->hue = val; 1670 i830_tv_update_hue(pI830, val); 1671 } 1672} 1673 1674static void 1675i830_tv_color_create_property(xf86OutputPtr output, Atom *property, 1676 char *name, int name_len, uint8_t val) 1677{ 1678 ScrnInfoPtr pScrn = output->scrn; 1679 INT32 range[2]; 1680 int err = 0; 1681 1682 *property = MakeAtom(name, name_len - 1, TRUE); 1683 range[0] = 0; 1684 range[1] = 255; 1685 err = RRConfigureOutputProperty(output->randr_output, *property, 1686 FALSE, TRUE, FALSE, 2, range); 1687 if (err != 0) { 1688 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1689 "RRConfigureOutputProperty error, %d\n", err); 1690 goto out; 1691 } 1692 /* Set the current value */ 1693 i830_tv_color_set_property(output, *property, val); 1694 1695 err = RRChangeOutputProperty(output->randr_output, *property, 1696 XA_INTEGER, 32, PropModeReplace, 1, &val, 1697 FALSE, FALSE); 1698 if (err != 0) { 1699 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1700 "RRChangeOutputProperty error, %d\n", err); 1701 } 1702out: 1703 return; 1704} 1705 1706#endif /* RANDR_12_INTERFACE */ 1707 1708static void 1709i830_tv_create_resources(xf86OutputPtr output) 1710{ 1711#ifdef RANDR_12_INTERFACE 1712 ScrnInfoPtr pScrn = output->scrn; 1713 I830Ptr pI830 = I830PTR(pScrn); 1714 I830OutputPrivatePtr intel_output = output->driver_private; 1715 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1716 int err, i; 1717 1718 /* Set up the tv_format property, which takes effect on mode set 1719 * and accepts strings that match exactly 1720 */ 1721 tv_format_atom = MakeAtom(TV_FORMAT_NAME, sizeof(TV_FORMAT_NAME) - 1, 1722 TRUE); 1723 1724 for (i = 0; i < NUM_TV_MODES; i++) 1725 tv_format_name_atoms[i] = MakeAtom (tv_modes[i].name, 1726 strlen (tv_modes[i].name), 1727 TRUE); 1728 1729 err = i830_tv_format_configure_property (output); 1730 1731 if (err != 0) { 1732 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1733 "RRConfigureOutputProperty error, %d\n", err); 1734 } 1735 1736 /* Set the current value of the tv_format property */ 1737 if (!i830_tv_format_set_property (output)) 1738 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1739 "RRChangeOutputProperty error, %d\n", err); 1740 1741 for (i = 0; i < 4; i++) 1742 { 1743 INT32 range[2]; 1744 margin_atoms[i] = MakeAtom(margin_names[i], strlen (margin_names[i]), 1745 TRUE); 1746 1747 range[0] = 0; 1748 range[1] = 100; 1749 err = RRConfigureOutputProperty(output->randr_output, margin_atoms[i], 1750 TRUE, TRUE, FALSE, 2, range); 1751 1752 if (err != 0) 1753 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1754 "RRConfigureOutputProperty error, %d\n", err); 1755 1756 err = RRChangeOutputProperty(output->randr_output, margin_atoms[i], 1757 XA_INTEGER, 32, PropModeReplace, 1758 1, &dev_priv->margin[i], 1759 FALSE, TRUE); 1760 if (err != 0) 1761 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 1762 "RRChangeOutputProperty error, %d\n", err); 1763 } 1764 1765 i830_tv_color_create_property(output, &brightness_atom, 1766 TV_BRIGHTNESS_NAME, 1767 sizeof(TV_BRIGHTNESS_NAME), 1768 TV_BRIGHTNESS_DEFAULT); 1769 i830_tv_color_create_property(output, &contrast_atom, 1770 TV_CONTRAST_NAME, 1771 sizeof(TV_CONTRAST_NAME), 1772 IS_I965G(pI830) ? TV_CONTRAST_DEFAULT : 1773 TV_CONTRAST_DEFAULT_945G); 1774 i830_tv_color_create_property(output, &saturation_atom, 1775 TV_SATURATION_NAME, 1776 sizeof(TV_SATURATION_NAME), 1777 IS_I965G(pI830) ? TV_SATURATION_DEFAULT : 1778 TV_SATURATION_DEFAULT_945G); 1779 i830_tv_color_create_property(output, &hue_atom, TV_HUE_NAME, 1780 sizeof(TV_HUE_NAME), TV_HUE_DEFAULT); 1781#endif /* RANDR_12_INTERFACE */ 1782} 1783 1784#ifdef RANDR_12_INTERFACE 1785static Bool 1786i830_tv_set_property(xf86OutputPtr output, Atom property, 1787 RRPropertyValuePtr value) 1788{ 1789 int i; 1790 1791 if (property == tv_format_atom) 1792 { 1793 I830OutputPrivatePtr intel_output = output->driver_private; 1794 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1795 I830Ptr pI830 = I830PTR(output->scrn); 1796 Atom atom; 1797 const char *name; 1798 char *val; 1799 RRCrtcPtr randr_crtc; 1800 xRRModeInfo modeinfo; 1801 RRModePtr mode; 1802 DisplayModePtr crtc_mode; 1803 1804 if (value->type != XA_ATOM || value->format != 32 || value->size != 1) 1805 return FALSE; 1806 1807 memcpy (&atom, value->data, 4); 1808 name = NameForAtom (atom); 1809 1810 val = xalloc (strlen (name) + 1); 1811 if (!val) 1812 return FALSE; 1813 strcpy (val, name); 1814 if (!i830_tv_mode_lookup (val)) 1815 { 1816 xfree (val); 1817 return FALSE; 1818 } 1819 xfree (dev_priv->tv_format); 1820 dev_priv->tv_format = val; 1821 1822 if (pI830->starting || output->crtc == NULL) 1823 return TRUE; 1824 1825 /* TV format change will generate new modelines, try 1826 to probe them and update outputs. */ 1827 xf86ProbeOutputModes(output->scrn, 0, 0); 1828 /* Mirror output modes to scrn mode list */ 1829 xf86SetScrnInfoModes (output->scrn); 1830 1831 for (crtc_mode = output->probed_modes; crtc_mode; 1832 crtc_mode = crtc_mode->next) 1833 { 1834 if (output->crtc->mode.HDisplay == crtc_mode->HDisplay && 1835 output->crtc->mode.VDisplay == crtc_mode->VDisplay) 1836 break; 1837 } 1838 if (!crtc_mode) 1839 crtc_mode = output->probed_modes; 1840 1841 xf86CrtcSetMode(output->crtc, crtc_mode, output->crtc->rotation, 1842 output->crtc->x, output->crtc->y); 1843 1844 xf86RandR12TellChanged(output->scrn->pScreen); 1845 1846 modeinfo.width = crtc_mode->HDisplay; 1847 modeinfo.height = crtc_mode->VDisplay; 1848 modeinfo.dotClock = crtc_mode->Clock * 1000; 1849 modeinfo.hSyncStart = crtc_mode->HSyncStart; 1850 modeinfo.hSyncEnd = crtc_mode->HSyncEnd; 1851 modeinfo.hTotal = crtc_mode->HTotal; 1852 modeinfo.hSkew = crtc_mode->HSkew; 1853 modeinfo.vSyncStart = crtc_mode->VSyncStart; 1854 modeinfo.vSyncEnd = crtc_mode->VSyncEnd; 1855 modeinfo.vTotal = crtc_mode->VTotal; 1856 modeinfo.nameLength = strlen(crtc_mode->name); 1857 modeinfo.modeFlags = crtc_mode->Flags; 1858 1859 mode = RRModeGet(&modeinfo, crtc_mode->name); 1860 randr_crtc = output->crtc->randr_crtc; 1861 if (mode != randr_crtc->mode) { 1862 if (randr_crtc->mode) 1863 RRModeDestroy(randr_crtc->mode); 1864 randr_crtc->mode = mode; 1865 } 1866 return TRUE; 1867 } 1868 for (i = 0; i < 4; i++) 1869 { 1870 if (property == margin_atoms[i]) 1871 { 1872 I830OutputPrivatePtr intel_output = output->driver_private; 1873 struct i830_tv_priv *dev_priv = intel_output->dev_priv; 1874 INT32 val; 1875 1876 if (value->type != XA_INTEGER || value->format != 32 || 1877 value->size != 1) 1878 return FALSE; 1879 1880 memcpy (&val, value->data, 4); 1881 dev_priv->margin[i] = val; 1882 return TRUE; 1883 } 1884 } 1885 if (property == brightness_atom || property == contrast_atom || 1886 property == saturation_atom || property == hue_atom) { 1887 uint8_t val; 1888 1889 /* Make sure value is sane */ 1890 if (value->type != XA_INTEGER || value->format != 32 || 1891 value->size != 1) 1892 return FALSE; 1893 1894 memcpy (&val, value->data, 1); 1895 i830_tv_color_set_property(output, property, val); 1896 } 1897 1898 return TRUE; 1899} 1900#endif /* RANDR_12_INTERFACE */ 1901 1902#ifdef RANDR_GET_CRTC_INTERFACE 1903static xf86CrtcPtr 1904i830_tv_get_crtc(xf86OutputPtr output) 1905{ 1906 ScrnInfoPtr pScrn = output->scrn; 1907 I830Ptr pI830 = I830PTR(pScrn); 1908 int pipe = !!(INREG(TV_CTL) & TV_ENC_PIPEB_SELECT); 1909 1910 return i830_pipe_to_crtc(pScrn, pipe); 1911} 1912#endif 1913 1914static const xf86OutputFuncsRec i830_tv_output_funcs = { 1915 .create_resources = i830_tv_create_resources, 1916 .dpms = i830_tv_dpms, 1917 .save = i830_tv_save, 1918 .restore = i830_tv_restore, 1919 .mode_valid = i830_tv_mode_valid, 1920 .mode_fixup = i830_tv_mode_fixup, 1921 .prepare = i830_output_prepare, 1922 .mode_set = i830_tv_mode_set, 1923 .commit = i830_output_commit, 1924 .detect = i830_tv_detect, 1925 .get_modes = i830_tv_get_modes, 1926 .destroy = i830_tv_destroy, 1927#ifdef RANDR_12_INTERFACE 1928 .set_property = i830_tv_set_property, 1929#endif 1930#ifdef RANDR_GET_CRTC_INTERFACE 1931 .get_crtc = i830_tv_get_crtc, 1932#endif 1933}; 1934 1935void 1936i830_tv_init(ScrnInfoPtr pScrn) 1937{ 1938 I830Ptr pI830 = I830PTR(pScrn); 1939 xf86OutputPtr output; 1940 I830OutputPrivatePtr intel_output; 1941 struct i830_tv_priv *dev_priv; 1942 uint32_t tv_dac_on, tv_dac_off, save_tv_dac; 1943 XF86OptionPtr mon_option_lst = NULL; 1944 char *tv_format = NULL; 1945 char *tv_type = NULL; 1946 1947 if (pI830->quirk_flag & QUIRK_IGNORE_TV) 1948 return; 1949 1950 if ((INREG(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) 1951 return; 1952 1953 /* 1954 * Sanity check the TV output by checking to see if the 1955 * DAC register holds a value 1956 */ 1957 save_tv_dac = INREG(TV_DAC); 1958 1959 OUTREG(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN); 1960 tv_dac_on = INREG(TV_DAC); 1961 1962 OUTREG(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); 1963 tv_dac_off = INREG(TV_DAC); 1964 1965 OUTREG(TV_DAC, save_tv_dac); 1966 1967 /* 1968 * If the register does not hold the state change enable 1969 * bit, (either as a 0 or a 1), assume it doesn't really 1970 * exist 1971 */ 1972 if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 || 1973 (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) 1974 return; 1975 1976 if (!pI830->tv_present) /* VBIOS claims no TV connector */ 1977 return; 1978 1979 output = xf86OutputCreate (pScrn, &i830_tv_output_funcs, "TV"); 1980 1981 if (!output) 1982 return; 1983 1984 intel_output = xnfcalloc (sizeof (I830OutputPrivateRec) + 1985 sizeof (struct i830_tv_priv), 1); 1986 if (!intel_output) 1987 { 1988 xf86OutputDestroy (output); 1989 return; 1990 } 1991 dev_priv = (struct i830_tv_priv *) (intel_output + 1); 1992 intel_output->type = I830_OUTPUT_TVOUT; 1993 intel_output->pipe_mask = ((1 << 0) | (1 << 1)); 1994 intel_output->clone_mask = (1 << I830_OUTPUT_TVOUT); 1995 intel_output->dev_priv = dev_priv; 1996 dev_priv->type = TV_TYPE_UNKNOWN; 1997 1998 dev_priv->tv_format = NULL; 1999 2000 if (output->conf_monitor) 2001 mon_option_lst = output->conf_monitor->mon_option_lst; 2002 2003 /* BIOS margin values */ 2004 dev_priv->margin[TV_MARGIN_LEFT] = xf86SetIntOption (mon_option_lst, 2005 "Left", 54); 2006 dev_priv->margin[TV_MARGIN_TOP] = xf86SetIntOption (mon_option_lst, 2007 "Top", 36); 2008 dev_priv->margin[TV_MARGIN_RIGHT] = xf86SetIntOption (mon_option_lst, 2009 "Right", 46); 2010 dev_priv->margin[TV_MARGIN_BOTTOM] = xf86SetIntOption (mon_option_lst, 2011 "Bottom", 37); 2012 2013 tv_format = xf86findOptionValue (mon_option_lst, "TV_Format"); 2014 if (tv_format) 2015 dev_priv->tv_format = xstrdup (tv_format); 2016 else 2017 dev_priv->tv_format = xstrdup (tv_modes[0].name); 2018 2019 tv_type = xf86findOptionValue (mon_option_lst, "TV_Connector"); 2020 if (tv_type) { 2021 dev_priv->force_type = TRUE; 2022 if (strcasecmp(tv_type, "S-Video") == 0) 2023 dev_priv->type = TV_TYPE_SVIDEO; 2024 else if (strcasecmp(tv_type, "Composite") == 0) 2025 dev_priv->type = TV_TYPE_COMPOSITE; 2026 else if (strcasecmp(tv_type, "Component") == 0) 2027 dev_priv->type = TV_TYPE_COMPONENT; 2028 else { 2029 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 2030 "Unknown TV Connector type %s\n", tv_type); 2031 dev_priv->force_type = FALSE; 2032 } 2033 } 2034 2035 if (dev_priv->force_type) 2036 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 2037 "Force TV Connector type as %s\n", tv_type); 2038 2039 output->driver_private = intel_output; 2040 output->interlaceAllowed = FALSE; 2041 output->doubleScanAllowed = FALSE; 2042} 2043