1 /* 2 * Copyright 2017 Advanced Micro Devices, Inc. 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 shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 24 #include "dm_services.h" 25 26 /* include DCE11 register header files */ 27 #include "dce/dce_11_0_d.h" 28 #include "dce/dce_11_0_sh_mask.h" 29 30 #include "dc_types.h" 31 #include "dc_bios_types.h" 32 #include "dc.h" 33 34 #include "include/grph_object_id.h" 35 #include "include/logger_interface.h" 36 #include "dce110_timing_generator.h" 37 #include "dce110_timing_generator_v.h" 38 39 #include "timing_generator.h" 40 41 #define DC_LOGGER \ 42 tg->ctx->logger 43 /** ******************************************************************************** 44 * 45 * DCE11 Timing Generator Implementation 46 * 47 **********************************************************************************/ 48 49 /* 50 * Enable CRTCV 51 */ 52 53 static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg) 54 { 55 /* 56 * Set MASTER_UPDATE_MODE to 0 57 * This is needed for DRR, and also suggested to be default value by Syed. 58 */ 59 uint32_t value; 60 61 value = 0; 62 set_reg_field_value(value, 0, 63 CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); 64 dm_write_reg(tg->ctx, 65 mmCRTCV_MASTER_UPDATE_MODE, value); 66 67 /* TODO: may want this on for looking for underflow */ 68 value = 0; 69 dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value); 70 71 value = 0; 72 set_reg_field_value(value, 1, 73 CRTCV_MASTER_EN, CRTC_MASTER_EN); 74 dm_write_reg(tg->ctx, 75 mmCRTCV_MASTER_EN, value); 76 77 return true; 78 } 79 80 static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg) 81 { 82 uint32_t value; 83 84 value = dm_read_reg(tg->ctx, 85 mmCRTCV_CONTROL); 86 set_reg_field_value(value, 0, 87 CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL); 88 set_reg_field_value(value, 0, 89 CRTCV_CONTROL, CRTC_MASTER_EN); 90 dm_write_reg(tg->ctx, 91 mmCRTCV_CONTROL, value); 92 /* 93 * TODO: call this when adding stereo support 94 * tg->funcs->disable_stereo(tg); 95 */ 96 return true; 97 } 98 99 static void dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) 100 { 101 uint32_t addr = mmCRTCV_BLANK_CONTROL; 102 uint32_t value = dm_read_reg(tg->ctx, addr); 103 104 set_reg_field_value( 105 value, 106 1, 107 CRTCV_BLANK_CONTROL, 108 CRTC_BLANK_DATA_EN); 109 110 set_reg_field_value( 111 value, 112 0, 113 CRTCV_BLANK_CONTROL, 114 CRTC_BLANK_DE_MODE); 115 116 dm_write_reg(tg->ctx, addr, value); 117 } 118 119 static void dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) 120 { 121 uint32_t addr = mmCRTCV_BLANK_CONTROL; 122 uint32_t value = dm_read_reg(tg->ctx, addr); 123 124 set_reg_field_value( 125 value, 126 0, 127 CRTCV_BLANK_CONTROL, 128 CRTC_BLANK_DATA_EN); 129 130 set_reg_field_value( 131 value, 132 0, 133 CRTCV_BLANK_CONTROL, 134 CRTC_BLANK_DE_MODE); 135 136 dm_write_reg(tg->ctx, addr, value); 137 } 138 139 static bool dce110_timing_generator_v_is_in_vertical_blank( 140 struct timing_generator *tg) 141 { 142 uint32_t addr = 0; 143 uint32_t value = 0; 144 uint32_t field = 0; 145 146 addr = mmCRTCV_STATUS; 147 value = dm_read_reg(tg->ctx, addr); 148 field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK); 149 return field == 1; 150 } 151 152 static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg) 153 { 154 uint32_t value; 155 uint32_t h1 = 0; 156 uint32_t h2 = 0; 157 uint32_t v1 = 0; 158 uint32_t v2 = 0; 159 160 value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); 161 162 h1 = get_reg_field_value( 163 value, 164 CRTCV_STATUS_POSITION, 165 CRTC_HORZ_COUNT); 166 167 v1 = get_reg_field_value( 168 value, 169 CRTCV_STATUS_POSITION, 170 CRTC_VERT_COUNT); 171 172 value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); 173 174 h2 = get_reg_field_value( 175 value, 176 CRTCV_STATUS_POSITION, 177 CRTC_HORZ_COUNT); 178 179 v2 = get_reg_field_value( 180 value, 181 CRTCV_STATUS_POSITION, 182 CRTC_VERT_COUNT); 183 184 if (h1 == h2 && v1 == v2) 185 return false; 186 else 187 return true; 188 } 189 190 static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg) 191 { 192 /* We want to catch beginning of VBlank here, so if the first try are 193 * in VBlank, we might be very close to Active, in this case wait for 194 * another frame 195 */ 196 while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { 197 if (!dce110_timing_generator_v_is_counter_moving(tg)) { 198 /* error - no point to wait if counter is not moving */ 199 break; 200 } 201 } 202 203 while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) { 204 if (!dce110_timing_generator_v_is_counter_moving(tg)) { 205 /* error - no point to wait if counter is not moving */ 206 break; 207 } 208 } 209 } 210 211 /* 212 * Wait till we are in VActive (anywhere in VActive) 213 */ 214 static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg) 215 { 216 while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { 217 if (!dce110_timing_generator_v_is_counter_moving(tg)) { 218 /* error - no point to wait if counter is not moving */ 219 break; 220 } 221 } 222 } 223 224 static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg, 225 enum crtc_state state) 226 { 227 switch (state) { 228 case CRTC_STATE_VBLANK: 229 dce110_timing_generator_v_wait_for_vblank(tg); 230 break; 231 232 case CRTC_STATE_VACTIVE: 233 dce110_timing_generator_v_wait_for_vactive(tg); 234 break; 235 236 default: 237 break; 238 } 239 } 240 241 static void dce110_timing_generator_v_program_blanking( 242 struct timing_generator *tg, 243 const struct dc_crtc_timing *timing) 244 { 245 uint32_t vsync_offset = timing->v_border_bottom + 246 timing->v_front_porch; 247 uint32_t v_sync_start = timing->v_addressable + vsync_offset; 248 249 uint32_t hsync_offset = timing->h_border_right + 250 timing->h_front_porch; 251 uint32_t h_sync_start = timing->h_addressable + hsync_offset; 252 253 struct dc_context *ctx = tg->ctx; 254 uint32_t value = 0; 255 uint32_t addr = 0; 256 uint32_t tmp = 0; 257 258 addr = mmCRTCV_H_TOTAL; 259 value = dm_read_reg(ctx, addr); 260 set_reg_field_value( 261 value, 262 timing->h_total - 1, 263 CRTCV_H_TOTAL, 264 CRTC_H_TOTAL); 265 dm_write_reg(ctx, addr, value); 266 267 addr = mmCRTCV_V_TOTAL; 268 value = dm_read_reg(ctx, addr); 269 set_reg_field_value( 270 value, 271 timing->v_total - 1, 272 CRTCV_V_TOTAL, 273 CRTC_V_TOTAL); 274 dm_write_reg(ctx, addr, value); 275 276 addr = mmCRTCV_H_BLANK_START_END; 277 value = dm_read_reg(ctx, addr); 278 279 tmp = timing->h_total - 280 (h_sync_start + timing->h_border_left); 281 282 set_reg_field_value( 283 value, 284 tmp, 285 CRTCV_H_BLANK_START_END, 286 CRTC_H_BLANK_END); 287 288 tmp = tmp + timing->h_addressable + 289 timing->h_border_left + timing->h_border_right; 290 291 set_reg_field_value( 292 value, 293 tmp, 294 CRTCV_H_BLANK_START_END, 295 CRTC_H_BLANK_START); 296 297 dm_write_reg(ctx, addr, value); 298 299 addr = mmCRTCV_V_BLANK_START_END; 300 value = dm_read_reg(ctx, addr); 301 302 tmp = timing->v_total - (v_sync_start + timing->v_border_top); 303 304 set_reg_field_value( 305 value, 306 tmp, 307 CRTCV_V_BLANK_START_END, 308 CRTC_V_BLANK_END); 309 310 tmp = tmp + timing->v_addressable + timing->v_border_top + 311 timing->v_border_bottom; 312 313 set_reg_field_value( 314 value, 315 tmp, 316 CRTCV_V_BLANK_START_END, 317 CRTC_V_BLANK_START); 318 319 dm_write_reg(ctx, addr, value); 320 321 addr = mmCRTCV_H_SYNC_A; 322 value = 0; 323 set_reg_field_value( 324 value, 325 timing->h_sync_width, 326 CRTCV_H_SYNC_A, 327 CRTC_H_SYNC_A_END); 328 dm_write_reg(ctx, addr, value); 329 330 addr = mmCRTCV_H_SYNC_A_CNTL; 331 value = dm_read_reg(ctx, addr); 332 if (timing->flags.HSYNC_POSITIVE_POLARITY) { 333 set_reg_field_value( 334 value, 335 0, 336 CRTCV_H_SYNC_A_CNTL, 337 CRTC_H_SYNC_A_POL); 338 } else { 339 set_reg_field_value( 340 value, 341 1, 342 CRTCV_H_SYNC_A_CNTL, 343 CRTC_H_SYNC_A_POL); 344 } 345 dm_write_reg(ctx, addr, value); 346 347 addr = mmCRTCV_V_SYNC_A; 348 value = 0; 349 set_reg_field_value( 350 value, 351 timing->v_sync_width, 352 CRTCV_V_SYNC_A, 353 CRTC_V_SYNC_A_END); 354 dm_write_reg(ctx, addr, value); 355 356 addr = mmCRTCV_V_SYNC_A_CNTL; 357 value = dm_read_reg(ctx, addr); 358 if (timing->flags.VSYNC_POSITIVE_POLARITY) { 359 set_reg_field_value( 360 value, 361 0, 362 CRTCV_V_SYNC_A_CNTL, 363 CRTC_V_SYNC_A_POL); 364 } else { 365 set_reg_field_value( 366 value, 367 1, 368 CRTCV_V_SYNC_A_CNTL, 369 CRTC_V_SYNC_A_POL); 370 } 371 dm_write_reg(ctx, addr, value); 372 373 addr = mmCRTCV_INTERLACE_CONTROL; 374 value = dm_read_reg(ctx, addr); 375 set_reg_field_value( 376 value, 377 timing->flags.INTERLACE, 378 CRTCV_INTERLACE_CONTROL, 379 CRTC_INTERLACE_ENABLE); 380 dm_write_reg(ctx, addr, value); 381 } 382 383 static void dce110_timing_generator_v_enable_advanced_request( 384 struct timing_generator *tg, 385 bool enable, 386 const struct dc_crtc_timing *timing) 387 { 388 uint32_t addr = mmCRTCV_START_LINE_CONTROL; 389 uint32_t value = dm_read_reg(tg->ctx, addr); 390 391 if (enable) { 392 if ((timing->v_sync_width + timing->v_front_porch) <= 3) { 393 set_reg_field_value( 394 value, 395 3, 396 CRTCV_START_LINE_CONTROL, 397 CRTC_ADVANCED_START_LINE_POSITION); 398 } else { 399 set_reg_field_value( 400 value, 401 4, 402 CRTCV_START_LINE_CONTROL, 403 CRTC_ADVANCED_START_LINE_POSITION); 404 } 405 set_reg_field_value( 406 value, 407 0, 408 CRTCV_START_LINE_CONTROL, 409 CRTC_LEGACY_REQUESTOR_EN); 410 } else { 411 set_reg_field_value( 412 value, 413 2, 414 CRTCV_START_LINE_CONTROL, 415 CRTC_ADVANCED_START_LINE_POSITION); 416 set_reg_field_value( 417 value, 418 1, 419 CRTCV_START_LINE_CONTROL, 420 CRTC_LEGACY_REQUESTOR_EN); 421 } 422 423 dm_write_reg(tg->ctx, addr, value); 424 } 425 426 static void dce110_timing_generator_v_set_blank(struct timing_generator *tg, 427 bool enable_blanking) 428 { 429 if (enable_blanking) 430 dce110_timing_generator_v_blank_crtc(tg); 431 else 432 dce110_timing_generator_v_unblank_crtc(tg); 433 } 434 435 static void dce110_timing_generator_v_program_timing(struct timing_generator *tg, 436 const struct dc_crtc_timing *timing, 437 int vready_offset, 438 int vstartup_start, 439 int vupdate_offset, 440 int vupdate_width, 441 const enum signal_type signal, 442 bool use_vbios) 443 { 444 if (use_vbios) 445 dce110_timing_generator_program_timing_generator(tg, timing); 446 else 447 dce110_timing_generator_v_program_blanking(tg, timing); 448 } 449 450 static void dce110_timing_generator_v_program_blank_color( 451 struct timing_generator *tg, 452 const struct tg_color *black_color) 453 { 454 uint32_t addr = mmCRTCV_BLACK_COLOR; 455 uint32_t value = dm_read_reg(tg->ctx, addr); 456 457 set_reg_field_value( 458 value, 459 black_color->color_b_cb, 460 CRTCV_BLACK_COLOR, 461 CRTC_BLACK_COLOR_B_CB); 462 set_reg_field_value( 463 value, 464 black_color->color_g_y, 465 CRTCV_BLACK_COLOR, 466 CRTC_BLACK_COLOR_G_Y); 467 set_reg_field_value( 468 value, 469 black_color->color_r_cr, 470 CRTCV_BLACK_COLOR, 471 CRTC_BLACK_COLOR_R_CR); 472 473 dm_write_reg(tg->ctx, addr, value); 474 } 475 476 static void dce110_timing_generator_v_set_overscan_color_black( 477 struct timing_generator *tg, 478 const struct tg_color *color) 479 { 480 struct dc_context *ctx = tg->ctx; 481 uint32_t addr; 482 uint32_t value = 0; 483 484 set_reg_field_value( 485 value, 486 color->color_b_cb, 487 CRTC_OVERSCAN_COLOR, 488 CRTC_OVERSCAN_COLOR_BLUE); 489 490 set_reg_field_value( 491 value, 492 color->color_r_cr, 493 CRTC_OVERSCAN_COLOR, 494 CRTC_OVERSCAN_COLOR_RED); 495 496 set_reg_field_value( 497 value, 498 color->color_g_y, 499 CRTC_OVERSCAN_COLOR, 500 CRTC_OVERSCAN_COLOR_GREEN); 501 502 addr = mmCRTCV_OVERSCAN_COLOR; 503 dm_write_reg(ctx, addr, value); 504 addr = mmCRTCV_BLACK_COLOR; 505 dm_write_reg(ctx, addr, value); 506 /* This is desirable to have a constant DAC output voltage during the 507 * blank time that is higher than the 0 volt reference level that the 508 * DAC outputs when the NBLANK signal 509 * is asserted low, such as for output to an analog TV. */ 510 addr = mmCRTCV_BLANK_DATA_COLOR; 511 dm_write_reg(ctx, addr, value); 512 513 /* TO DO we have to program EXT registers and we need to know LB DATA 514 * format because it is used when more 10 , i.e. 12 bits per color 515 * 516 * m_mmDxCRTC_OVERSCAN_COLOR_EXT 517 * m_mmDxCRTC_BLACK_COLOR_EXT 518 * m_mmDxCRTC_BLANK_DATA_COLOR_EXT 519 */ 520 } 521 522 static void dce110_tg_v_program_blank_color(struct timing_generator *tg, 523 const struct tg_color *black_color) 524 { 525 uint32_t addr = mmCRTCV_BLACK_COLOR; 526 uint32_t value = dm_read_reg(tg->ctx, addr); 527 528 set_reg_field_value( 529 value, 530 black_color->color_b_cb, 531 CRTCV_BLACK_COLOR, 532 CRTC_BLACK_COLOR_B_CB); 533 set_reg_field_value( 534 value, 535 black_color->color_g_y, 536 CRTCV_BLACK_COLOR, 537 CRTC_BLACK_COLOR_G_Y); 538 set_reg_field_value( 539 value, 540 black_color->color_r_cr, 541 CRTCV_BLACK_COLOR, 542 CRTC_BLACK_COLOR_R_CR); 543 544 dm_write_reg(tg->ctx, addr, value); 545 546 addr = mmCRTCV_BLANK_DATA_COLOR; 547 dm_write_reg(tg->ctx, addr, value); 548 } 549 550 static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg, 551 const struct tg_color *overscan_color) 552 { 553 struct dc_context *ctx = tg->ctx; 554 uint32_t value = 0; 555 uint32_t addr; 556 557 set_reg_field_value( 558 value, 559 overscan_color->color_b_cb, 560 CRTCV_OVERSCAN_COLOR, 561 CRTC_OVERSCAN_COLOR_BLUE); 562 563 set_reg_field_value( 564 value, 565 overscan_color->color_g_y, 566 CRTCV_OVERSCAN_COLOR, 567 CRTC_OVERSCAN_COLOR_GREEN); 568 569 set_reg_field_value( 570 value, 571 overscan_color->color_r_cr, 572 CRTCV_OVERSCAN_COLOR, 573 CRTC_OVERSCAN_COLOR_RED); 574 575 addr = mmCRTCV_OVERSCAN_COLOR; 576 dm_write_reg(ctx, addr, value); 577 } 578 579 static void dce110_timing_generator_v_set_colors(struct timing_generator *tg, 580 const struct tg_color *blank_color, 581 const struct tg_color *overscan_color) 582 { 583 if (blank_color != NULL) 584 dce110_tg_v_program_blank_color(tg, blank_color); 585 if (overscan_color != NULL) 586 dce110_timing_generator_v_set_overscan_color(tg, overscan_color); 587 } 588 589 static void dce110_timing_generator_v_set_early_control( 590 struct timing_generator *tg, 591 uint32_t early_cntl) 592 { 593 uint32_t regval; 594 uint32_t address = mmCRTC_CONTROL; 595 596 regval = dm_read_reg(tg->ctx, address); 597 set_reg_field_value(regval, early_cntl, 598 CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL); 599 dm_write_reg(tg->ctx, address, regval); 600 } 601 602 static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) 603 { 604 uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT; 605 uint32_t value = dm_read_reg(tg->ctx, addr); 606 uint32_t field = get_reg_field_value( 607 value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); 608 609 return field; 610 } 611 612 static bool dce110_timing_generator_v_did_triggered_reset_occur( 613 struct timing_generator *tg) 614 { 615 DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); 616 return false; 617 } 618 619 static void dce110_timing_generator_v_setup_global_swap_lock( 620 struct timing_generator *tg, 621 const struct dcp_gsl_params *gsl_params) 622 { 623 DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); 624 return; 625 } 626 627 static void dce110_timing_generator_v_enable_reset_trigger( 628 struct timing_generator *tg, 629 int source_tg_inst) 630 { 631 DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); 632 return; 633 } 634 635 static void dce110_timing_generator_v_disable_reset_trigger( 636 struct timing_generator *tg) 637 { 638 DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); 639 return; 640 } 641 642 static void dce110_timing_generator_v_tear_down_global_swap_lock( 643 struct timing_generator *tg) 644 { 645 DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); 646 return; 647 } 648 649 static void dce110_timing_generator_v_disable_vga( 650 struct timing_generator *tg) 651 { 652 return; 653 } 654 655 /** ******************************************************************************************** 656 * 657 * DCE11 Timing Generator Constructor / Destructor 658 * 659 *********************************************************************************************/ 660 static const struct timing_generator_funcs dce110_tg_v_funcs = { 661 .validate_timing = dce110_tg_validate_timing, 662 .program_timing = dce110_timing_generator_v_program_timing, 663 .enable_crtc = dce110_timing_generator_v_enable_crtc, 664 .disable_crtc = dce110_timing_generator_v_disable_crtc, 665 .is_counter_moving = dce110_timing_generator_v_is_counter_moving, 666 .get_position = NULL, /* Not to be implemented for underlay*/ 667 .get_frame_count = dce110_timing_generator_v_get_vblank_counter, 668 .set_early_control = dce110_timing_generator_v_set_early_control, 669 .wait_for_state = dce110_timing_generator_v_wait_for_state, 670 .set_blank = dce110_timing_generator_v_set_blank, 671 .set_colors = dce110_timing_generator_v_set_colors, 672 .set_overscan_blank_color = 673 dce110_timing_generator_v_set_overscan_color_black, 674 .set_blank_color = dce110_timing_generator_v_program_blank_color, 675 .disable_vga = dce110_timing_generator_v_disable_vga, 676 .did_triggered_reset_occur = 677 dce110_timing_generator_v_did_triggered_reset_occur, 678 .setup_global_swap_lock = 679 dce110_timing_generator_v_setup_global_swap_lock, 680 .enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger, 681 .disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger, 682 .tear_down_global_swap_lock = 683 dce110_timing_generator_v_tear_down_global_swap_lock, 684 .enable_advanced_request = 685 dce110_timing_generator_v_enable_advanced_request 686 }; 687 688 void dce110_timing_generator_v_construct( 689 struct dce110_timing_generator *tg110, 690 struct dc_context *ctx) 691 { 692 tg110->controller_id = CONTROLLER_ID_UNDERLAY0; 693 694 tg110->base.funcs = &dce110_tg_v_funcs; 695 696 tg110->base.ctx = ctx; 697 tg110->base.bp = ctx->dc_bios; 698 699 tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; 700 tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; 701 702 tg110->min_h_blank = 56; 703 tg110->min_h_front_porch = 4; 704 tg110->min_h_back_porch = 4; 705 } 706