| /* |
| * Copyright 2017 Advanced Micro Devices, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| */ |
| |
| #include "dm_services.h" |
| |
| /* include DCE11 register header files */ |
| #include "dce/dce_11_0_d.h" |
| #include "dce/dce_11_0_sh_mask.h" |
| |
| #include "dc_types.h" |
| #include "dc_bios_types.h" |
| #include "dc.h" |
| |
| #include "include/grph_object_id.h" |
| #include "include/logger_interface.h" |
| #include "dce110_timing_generator.h" |
| #include "dce110_timing_generator_v.h" |
| |
| #include "timing_generator.h" |
| |
| #define DC_LOGGER \ |
| tg->ctx->logger |
| /** ******************************************************************************** |
| * |
| * DCE11 Timing Generator Implementation |
| * |
| **********************************************************************************/ |
| |
| /** |
| * Enable CRTCV |
| */ |
| |
| static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg) |
| { |
| /* |
| * Set MASTER_UPDATE_MODE to 0 |
| * This is needed for DRR, and also suggested to be default value by Syed. |
| */ |
| |
| uint32_t value; |
| |
| value = 0; |
| set_reg_field_value(value, 0, |
| CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); |
| dm_write_reg(tg->ctx, |
| mmCRTCV_MASTER_UPDATE_MODE, value); |
| |
| /* TODO: may want this on for looking for underflow */ |
| value = 0; |
| dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value); |
| |
| value = 0; |
| set_reg_field_value(value, 1, |
| CRTCV_MASTER_EN, CRTC_MASTER_EN); |
| dm_write_reg(tg->ctx, |
| mmCRTCV_MASTER_EN, value); |
| |
| return true; |
| } |
| |
| static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg) |
| { |
| uint32_t value; |
| |
| value = dm_read_reg(tg->ctx, |
| mmCRTCV_CONTROL); |
| set_reg_field_value(value, 0, |
| CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL); |
| set_reg_field_value(value, 0, |
| CRTCV_CONTROL, CRTC_MASTER_EN); |
| dm_write_reg(tg->ctx, |
| mmCRTCV_CONTROL, value); |
| /* |
| * TODO: call this when adding stereo support |
| * tg->funcs->disable_stereo(tg); |
| */ |
| return true; |
| } |
| |
| static void dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) |
| { |
| uint32_t addr = mmCRTCV_BLANK_CONTROL; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| |
| set_reg_field_value( |
| value, |
| 1, |
| CRTCV_BLANK_CONTROL, |
| CRTC_BLANK_DATA_EN); |
| |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_BLANK_CONTROL, |
| CRTC_BLANK_DE_MODE); |
| |
| dm_write_reg(tg->ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) |
| { |
| uint32_t addr = mmCRTCV_BLANK_CONTROL; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_BLANK_CONTROL, |
| CRTC_BLANK_DATA_EN); |
| |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_BLANK_CONTROL, |
| CRTC_BLANK_DE_MODE); |
| |
| dm_write_reg(tg->ctx, addr, value); |
| } |
| |
| static bool dce110_timing_generator_v_is_in_vertical_blank( |
| struct timing_generator *tg) |
| { |
| uint32_t addr = 0; |
| uint32_t value = 0; |
| uint32_t field = 0; |
| |
| addr = mmCRTCV_STATUS; |
| value = dm_read_reg(tg->ctx, addr); |
| field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK); |
| return field == 1; |
| } |
| |
| static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg) |
| { |
| uint32_t value; |
| uint32_t h1 = 0; |
| uint32_t h2 = 0; |
| uint32_t v1 = 0; |
| uint32_t v2 = 0; |
| |
| value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); |
| |
| h1 = get_reg_field_value( |
| value, |
| CRTCV_STATUS_POSITION, |
| CRTC_HORZ_COUNT); |
| |
| v1 = get_reg_field_value( |
| value, |
| CRTCV_STATUS_POSITION, |
| CRTC_VERT_COUNT); |
| |
| value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); |
| |
| h2 = get_reg_field_value( |
| value, |
| CRTCV_STATUS_POSITION, |
| CRTC_HORZ_COUNT); |
| |
| v2 = get_reg_field_value( |
| value, |
| CRTCV_STATUS_POSITION, |
| CRTC_VERT_COUNT); |
| |
| if (h1 == h2 && v1 == v2) |
| return false; |
| else |
| return true; |
| } |
| |
| static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg) |
| { |
| /* We want to catch beginning of VBlank here, so if the first try are |
| * in VBlank, we might be very close to Active, in this case wait for |
| * another frame |
| */ |
| while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { |
| if (!dce110_timing_generator_v_is_counter_moving(tg)) { |
| /* error - no point to wait if counter is not moving */ |
| break; |
| } |
| } |
| |
| while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) { |
| if (!dce110_timing_generator_v_is_counter_moving(tg)) { |
| /* error - no point to wait if counter is not moving */ |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Wait till we are in VActive (anywhere in VActive) |
| */ |
| static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg) |
| { |
| while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { |
| if (!dce110_timing_generator_v_is_counter_moving(tg)) { |
| /* error - no point to wait if counter is not moving */ |
| break; |
| } |
| } |
| } |
| |
| static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg, |
| enum crtc_state state) |
| { |
| switch (state) { |
| case CRTC_STATE_VBLANK: |
| dce110_timing_generator_v_wait_for_vblank(tg); |
| break; |
| |
| case CRTC_STATE_VACTIVE: |
| dce110_timing_generator_v_wait_for_vactive(tg); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static void dce110_timing_generator_v_program_blanking( |
| struct timing_generator *tg, |
| const struct dc_crtc_timing *timing) |
| { |
| uint32_t vsync_offset = timing->v_border_bottom + |
| timing->v_front_porch; |
| uint32_t v_sync_start = timing->v_addressable + vsync_offset; |
| |
| uint32_t hsync_offset = timing->h_border_right + |
| timing->h_front_porch; |
| uint32_t h_sync_start = timing->h_addressable + hsync_offset; |
| |
| struct dc_context *ctx = tg->ctx; |
| uint32_t value = 0; |
| uint32_t addr = 0; |
| uint32_t tmp = 0; |
| |
| addr = mmCRTCV_H_TOTAL; |
| value = dm_read_reg(ctx, addr); |
| set_reg_field_value( |
| value, |
| timing->h_total - 1, |
| CRTCV_H_TOTAL, |
| CRTC_H_TOTAL); |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_V_TOTAL; |
| value = dm_read_reg(ctx, addr); |
| set_reg_field_value( |
| value, |
| timing->v_total - 1, |
| CRTCV_V_TOTAL, |
| CRTC_V_TOTAL); |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_H_BLANK_START_END; |
| value = dm_read_reg(ctx, addr); |
| |
| tmp = timing->h_total - |
| (h_sync_start + timing->h_border_left); |
| |
| set_reg_field_value( |
| value, |
| tmp, |
| CRTCV_H_BLANK_START_END, |
| CRTC_H_BLANK_END); |
| |
| tmp = tmp + timing->h_addressable + |
| timing->h_border_left + timing->h_border_right; |
| |
| set_reg_field_value( |
| value, |
| tmp, |
| CRTCV_H_BLANK_START_END, |
| CRTC_H_BLANK_START); |
| |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_V_BLANK_START_END; |
| value = dm_read_reg(ctx, addr); |
| |
| tmp = timing->v_total - (v_sync_start + timing->v_border_top); |
| |
| set_reg_field_value( |
| value, |
| tmp, |
| CRTCV_V_BLANK_START_END, |
| CRTC_V_BLANK_END); |
| |
| tmp = tmp + timing->v_addressable + timing->v_border_top + |
| timing->v_border_bottom; |
| |
| set_reg_field_value( |
| value, |
| tmp, |
| CRTCV_V_BLANK_START_END, |
| CRTC_V_BLANK_START); |
| |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_H_SYNC_A; |
| value = 0; |
| set_reg_field_value( |
| value, |
| timing->h_sync_width, |
| CRTCV_H_SYNC_A, |
| CRTC_H_SYNC_A_END); |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_H_SYNC_A_CNTL; |
| value = dm_read_reg(ctx, addr); |
| if (timing->flags.HSYNC_POSITIVE_POLARITY) { |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_H_SYNC_A_CNTL, |
| CRTC_H_SYNC_A_POL); |
| } else { |
| set_reg_field_value( |
| value, |
| 1, |
| CRTCV_H_SYNC_A_CNTL, |
| CRTC_H_SYNC_A_POL); |
| } |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_V_SYNC_A; |
| value = 0; |
| set_reg_field_value( |
| value, |
| timing->v_sync_width, |
| CRTCV_V_SYNC_A, |
| CRTC_V_SYNC_A_END); |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_V_SYNC_A_CNTL; |
| value = dm_read_reg(ctx, addr); |
| if (timing->flags.VSYNC_POSITIVE_POLARITY) { |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_V_SYNC_A_CNTL, |
| CRTC_V_SYNC_A_POL); |
| } else { |
| set_reg_field_value( |
| value, |
| 1, |
| CRTCV_V_SYNC_A_CNTL, |
| CRTC_V_SYNC_A_POL); |
| } |
| dm_write_reg(ctx, addr, value); |
| |
| addr = mmCRTCV_INTERLACE_CONTROL; |
| value = dm_read_reg(ctx, addr); |
| set_reg_field_value( |
| value, |
| timing->flags.INTERLACE, |
| CRTCV_INTERLACE_CONTROL, |
| CRTC_INTERLACE_ENABLE); |
| dm_write_reg(ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_enable_advanced_request( |
| struct timing_generator *tg, |
| bool enable, |
| const struct dc_crtc_timing *timing) |
| { |
| uint32_t addr = mmCRTCV_START_LINE_CONTROL; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| |
| if (enable) { |
| if ((timing->v_sync_width + timing->v_front_porch) <= 3) { |
| set_reg_field_value( |
| value, |
| 3, |
| CRTCV_START_LINE_CONTROL, |
| CRTC_ADVANCED_START_LINE_POSITION); |
| } else { |
| set_reg_field_value( |
| value, |
| 4, |
| CRTCV_START_LINE_CONTROL, |
| CRTC_ADVANCED_START_LINE_POSITION); |
| } |
| set_reg_field_value( |
| value, |
| 0, |
| CRTCV_START_LINE_CONTROL, |
| CRTC_LEGACY_REQUESTOR_EN); |
| } else { |
| set_reg_field_value( |
| value, |
| 2, |
| CRTCV_START_LINE_CONTROL, |
| CRTC_ADVANCED_START_LINE_POSITION); |
| set_reg_field_value( |
| value, |
| 1, |
| CRTCV_START_LINE_CONTROL, |
| CRTC_LEGACY_REQUESTOR_EN); |
| } |
| |
| dm_write_reg(tg->ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_set_blank(struct timing_generator *tg, |
| bool enable_blanking) |
| { |
| if (enable_blanking) |
| dce110_timing_generator_v_blank_crtc(tg); |
| else |
| dce110_timing_generator_v_unblank_crtc(tg); |
| } |
| |
| static void dce110_timing_generator_v_program_timing(struct timing_generator *tg, |
| const struct dc_crtc_timing *timing, |
| int vready_offset, |
| int vstartup_start, |
| int vupdate_offset, |
| int vupdate_width, |
| const enum signal_type signal, |
| bool use_vbios) |
| { |
| if (use_vbios) |
| dce110_timing_generator_program_timing_generator(tg, timing); |
| else |
| dce110_timing_generator_v_program_blanking(tg, timing); |
| } |
| |
| static void dce110_timing_generator_v_program_blank_color( |
| struct timing_generator *tg, |
| const struct tg_color *black_color) |
| { |
| uint32_t addr = mmCRTCV_BLACK_COLOR; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| |
| set_reg_field_value( |
| value, |
| black_color->color_b_cb, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_B_CB); |
| set_reg_field_value( |
| value, |
| black_color->color_g_y, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_G_Y); |
| set_reg_field_value( |
| value, |
| black_color->color_r_cr, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_R_CR); |
| |
| dm_write_reg(tg->ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_set_overscan_color_black( |
| struct timing_generator *tg, |
| const struct tg_color *color) |
| { |
| struct dc_context *ctx = tg->ctx; |
| uint32_t addr; |
| uint32_t value = 0; |
| |
| set_reg_field_value( |
| value, |
| color->color_b_cb, |
| CRTC_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_BLUE); |
| |
| set_reg_field_value( |
| value, |
| color->color_r_cr, |
| CRTC_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_RED); |
| |
| set_reg_field_value( |
| value, |
| color->color_g_y, |
| CRTC_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_GREEN); |
| |
| addr = mmCRTCV_OVERSCAN_COLOR; |
| dm_write_reg(ctx, addr, value); |
| addr = mmCRTCV_BLACK_COLOR; |
| dm_write_reg(ctx, addr, value); |
| /* This is desirable to have a constant DAC output voltage during the |
| * blank time that is higher than the 0 volt reference level that the |
| * DAC outputs when the NBLANK signal |
| * is asserted low, such as for output to an analog TV. */ |
| addr = mmCRTCV_BLANK_DATA_COLOR; |
| dm_write_reg(ctx, addr, value); |
| |
| /* TO DO we have to program EXT registers and we need to know LB DATA |
| * format because it is used when more 10 , i.e. 12 bits per color |
| * |
| * m_mmDxCRTC_OVERSCAN_COLOR_EXT |
| * m_mmDxCRTC_BLACK_COLOR_EXT |
| * m_mmDxCRTC_BLANK_DATA_COLOR_EXT |
| */ |
| } |
| |
| static void dce110_tg_v_program_blank_color(struct timing_generator *tg, |
| const struct tg_color *black_color) |
| { |
| uint32_t addr = mmCRTCV_BLACK_COLOR; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| |
| set_reg_field_value( |
| value, |
| black_color->color_b_cb, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_B_CB); |
| set_reg_field_value( |
| value, |
| black_color->color_g_y, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_G_Y); |
| set_reg_field_value( |
| value, |
| black_color->color_r_cr, |
| CRTCV_BLACK_COLOR, |
| CRTC_BLACK_COLOR_R_CR); |
| |
| dm_write_reg(tg->ctx, addr, value); |
| |
| addr = mmCRTCV_BLANK_DATA_COLOR; |
| dm_write_reg(tg->ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg, |
| const struct tg_color *overscan_color) |
| { |
| struct dc_context *ctx = tg->ctx; |
| uint32_t value = 0; |
| uint32_t addr; |
| |
| set_reg_field_value( |
| value, |
| overscan_color->color_b_cb, |
| CRTCV_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_BLUE); |
| |
| set_reg_field_value( |
| value, |
| overscan_color->color_g_y, |
| CRTCV_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_GREEN); |
| |
| set_reg_field_value( |
| value, |
| overscan_color->color_r_cr, |
| CRTCV_OVERSCAN_COLOR, |
| CRTC_OVERSCAN_COLOR_RED); |
| |
| addr = mmCRTCV_OVERSCAN_COLOR; |
| dm_write_reg(ctx, addr, value); |
| } |
| |
| static void dce110_timing_generator_v_set_colors(struct timing_generator *tg, |
| const struct tg_color *blank_color, |
| const struct tg_color *overscan_color) |
| { |
| if (blank_color != NULL) |
| dce110_tg_v_program_blank_color(tg, blank_color); |
| if (overscan_color != NULL) |
| dce110_timing_generator_v_set_overscan_color(tg, overscan_color); |
| } |
| |
| static void dce110_timing_generator_v_set_early_control( |
| struct timing_generator *tg, |
| uint32_t early_cntl) |
| { |
| uint32_t regval; |
| uint32_t address = mmCRTC_CONTROL; |
| |
| regval = dm_read_reg(tg->ctx, address); |
| set_reg_field_value(regval, early_cntl, |
| CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL); |
| dm_write_reg(tg->ctx, address, regval); |
| } |
| |
| static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) |
| { |
| uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT; |
| uint32_t value = dm_read_reg(tg->ctx, addr); |
| uint32_t field = get_reg_field_value( |
| value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); |
| |
| return field; |
| } |
| |
| static bool dce110_timing_generator_v_did_triggered_reset_occur( |
| struct timing_generator *tg) |
| { |
| DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); |
| return false; |
| } |
| |
| static void dce110_timing_generator_v_setup_global_swap_lock( |
| struct timing_generator *tg, |
| const struct dcp_gsl_params *gsl_params) |
| { |
| DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); |
| return; |
| } |
| |
| static void dce110_timing_generator_v_enable_reset_trigger( |
| struct timing_generator *tg, |
| int source_tg_inst) |
| { |
| DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); |
| return; |
| } |
| |
| static void dce110_timing_generator_v_disable_reset_trigger( |
| struct timing_generator *tg) |
| { |
| DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); |
| return; |
| } |
| |
| static void dce110_timing_generator_v_tear_down_global_swap_lock( |
| struct timing_generator *tg) |
| { |
| DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); |
| return; |
| } |
| |
| static void dce110_timing_generator_v_disable_vga( |
| struct timing_generator *tg) |
| { |
| return; |
| } |
| |
| /** ******************************************************************************************** |
| * |
| * DCE11 Timing Generator Constructor / Destructor |
| * |
| *********************************************************************************************/ |
| static const struct timing_generator_funcs dce110_tg_v_funcs = { |
| .validate_timing = dce110_tg_validate_timing, |
| .program_timing = dce110_timing_generator_v_program_timing, |
| .enable_crtc = dce110_timing_generator_v_enable_crtc, |
| .disable_crtc = dce110_timing_generator_v_disable_crtc, |
| .is_counter_moving = dce110_timing_generator_v_is_counter_moving, |
| .get_position = NULL, /* Not to be implemented for underlay*/ |
| .get_frame_count = dce110_timing_generator_v_get_vblank_counter, |
| .set_early_control = dce110_timing_generator_v_set_early_control, |
| .wait_for_state = dce110_timing_generator_v_wait_for_state, |
| .set_blank = dce110_timing_generator_v_set_blank, |
| .set_colors = dce110_timing_generator_v_set_colors, |
| .set_overscan_blank_color = |
| dce110_timing_generator_v_set_overscan_color_black, |
| .set_blank_color = dce110_timing_generator_v_program_blank_color, |
| .disable_vga = dce110_timing_generator_v_disable_vga, |
| .did_triggered_reset_occur = |
| dce110_timing_generator_v_did_triggered_reset_occur, |
| .setup_global_swap_lock = |
| dce110_timing_generator_v_setup_global_swap_lock, |
| .enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger, |
| .disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger, |
| .tear_down_global_swap_lock = |
| dce110_timing_generator_v_tear_down_global_swap_lock, |
| .enable_advanced_request = |
| dce110_timing_generator_v_enable_advanced_request |
| }; |
| |
| void dce110_timing_generator_v_construct( |
| struct dce110_timing_generator *tg110, |
| struct dc_context *ctx) |
| { |
| tg110->controller_id = CONTROLLER_ID_UNDERLAY0; |
| |
| tg110->base.funcs = &dce110_tg_v_funcs; |
| |
| tg110->base.ctx = ctx; |
| tg110->base.bp = ctx->dc_bios; |
| |
| tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; |
| tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; |
| |
| tg110->min_h_blank = 56; |
| tg110->min_h_front_porch = 4; |
| tg110->min_h_back_porch = 4; |
| } |