linux/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c
<<
>>
Prefs
   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
  53static 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
  80static 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
  99static 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
 119static 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
 139static 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
 152static 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
 190static 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 */
 214static 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
 224static 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
 241static 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
 383static 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
 426static 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
 435static 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
 450static 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
 476static 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
 522static 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
 550static 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
 579static 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
 589static 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
 602static 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
 612static 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
 619static 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
 627static 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
 635static 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
 642static 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
 649static 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 *********************************************************************************************/
 660static 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
 688void 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