diff options
Diffstat (limited to 'common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch')
-rw-r--r-- | common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch b/common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch new file mode 100644 index 00000000..93b86f71 --- /dev/null +++ b/common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch @@ -0,0 +1,877 @@ +From 7da2abf1b61ca0b039d2d1683aff6cef8f086934 Mon Sep 17 00:00:00 2001 +From: Eric Yang <eric.yang2@amd.com> +Date: Wed, 3 Feb 2016 16:13:11 -0500 +Subject: [PATCH 0778/1110] drm/amd/dal: Add timing generator for underlay pipe + +Signed-off-by: Eric Yang <eric.yang2@amd.com> +Acked-by: Jordan Lazare <Jordan.Lazare@amd.com> +--- + .../amd/dal/dc/dce110/dce110_timing_generator.c | 2 +- + .../amd/dal/dc/dce110/dce110_timing_generator.h | 3 + + .../amd/dal/dc/dce110/dce110_timing_generator_v.c | 785 +++++++++++++++++++++ + .../amd/dal/dc/dce110/dce110_timing_generator_v.h | 34 + + 4 files changed, 823 insertions(+), 1 deletion(-) + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.c + create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.h + +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c +index d7cdd91..8fb90c0 100644 +--- a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.c +@@ -190,7 +190,7 @@ static void dce110_timing_generator_apply_front_porch_workaround( + } + } + +-static void dce110_timing_generator_color_space_to_black_color( ++void dce110_timing_generator_color_space_to_black_color( + enum color_space colorspace, + struct crtc_black_color *black_color) + { +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h +index 163fadd..3579736 100644 +--- a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator.h +@@ -175,6 +175,9 @@ void dce110_timing_generator_program_blank_color( + void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + enum color_space black_color); ++void dce110_timing_generator_color_space_to_black_color( ++ enum color_space colorspace, ++ struct crtc_black_color *black_color); + /*************** End-of-move ********************/ + + +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.c +new file mode 100644 +index 0000000..722f636 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.c +@@ -0,0 +1,785 @@ ++ ++ ++#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 "include/grph_object_id.h" ++#include "include/adapter_service_interface.h" ++#include "include/logger_interface.h" ++#include "dce110_timing_generator.h" ++#include "dce110_timing_generator_v.h" ++ ++#include "../inc/timing_generator.h" ++ ++ ++/** ******************************************************************************** ++ * ++ * 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; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ value = dm_read_reg(tg->ctx, ++ mmCRTCV_MASTER_UPDATE_MODE); ++ set_reg_field_value(value, 0, ++ CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); ++ dm_write_reg(tg->ctx, ++ mmCRTCV_MASTER_UPDATE_MODE, value); ++ ++ value = dm_read_reg(tg->ctx, ++ mmCRTCV_MASTER_EN); ++ 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; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ 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 bool dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) ++{ ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ uint32_t addr = mmCRTCV_BLANK_CONTROL; ++ uint32_t value = dm_read_reg(tg->ctx, addr); ++ uint8_t counter = 100; ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTCV_BLANK_CONTROL, ++ CRTC_BLANK_DATA_EN); ++ ++ set_reg_field_value( ++ value, ++ 1, ++ CRTCV_BLANK_CONTROL, ++ CRTC_BLANK_DE_MODE); ++ ++ dm_write_reg(tg->ctx, addr, value); ++ ++ while (counter > 0) { ++ value = dm_read_reg(tg->ctx, addr); ++ ++ if (get_reg_field_value( ++ value, ++ CRTCV_BLANK_CONTROL, ++ CRTC_BLANK_DATA_EN) == 1 && ++ get_reg_field_value( ++ value, ++ CRTCV_BLANK_CONTROL, ++ CRTC_CURRENT_BLANK_STATE) == 1) ++ break; ++ ++ dm_sleep_in_milliseconds(tg->ctx, 1); ++ counter--; ++ } ++ ++ if (!counter) { ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "timing generator %d blank timing out.\n", ++ tg110->controller_id); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) ++{ ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(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); ++ ++ return true; ++} ++ ++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; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ 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; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ 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 dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ 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); ++} ++ ++static void dce110_timing_generator_v_enable_advanced_request( ++ struct timing_generator *tg, ++ bool enable, ++ const struct dc_crtc_timing *timing) ++{ ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ 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 bool dce110_timing_generator_v_set_blank(struct timing_generator *tg, ++ bool enable_blanking) ++{ ++ if (enable_blanking) ++ return dce110_timing_generator_v_blank_crtc(tg); ++ else ++ return 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, ++ 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, ++ enum color_space color_space) ++{ ++ struct crtc_black_color black_color; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ uint32_t addr = mmCRTCV_BLACK_COLOR; ++ uint32_t value = dm_read_reg(tg->ctx, addr); ++ ++ dce110_timing_generator_color_space_to_black_color( ++ color_space, ++ &black_color); ++ ++ set_reg_field_value( ++ value, ++ black_color.black_color_b_cb, ++ CRTCV_BLACK_COLOR, ++ CRTC_BLACK_COLOR_B_CB); ++ set_reg_field_value( ++ value, ++ black_color.black_color_g_y, ++ CRTCV_BLACK_COLOR, ++ CRTC_BLACK_COLOR_G_Y); ++ set_reg_field_value( ++ value, ++ black_color.black_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, ++ enum color_space black_color) ++{ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t value = 0; ++ uint32_t addr; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ /* Overscan Color for YUV display modes: ++ * to achieve a black color for both the explicit and implicit overscan, ++ * the overscan color registers should be programmed to: */ ++ ++ switch (black_color) { ++ case COLOR_SPACE_YPBPR601: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_YPBPR709: ++ case COLOR_SPACE_YCBCR601: ++ case COLOR_SPACE_YCBCR709: ++ case COLOR_SPACE_YCBCR601_YONLY: ++ case COLOR_SPACE_YCBCR709_YONLY: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_N_MVPU_SUPER_AA: ++ /* In crossfire SuperAA mode, the slave overscan data is forced ++ * to 0 in the pixel mixer on the master. As a result, we need ++ * to adjust the blank color so that after blending the ++ * master+slave, it will appear black */ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ case COLOR_SPACE_SRGB_LIMITED_RANGE: ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_RED); ++ break; ++ ++ default: ++ /* default is sRGB black 0. */ ++ break; ++ } ++ 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 crtc_black_color *black_color) ++{ ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ uint32_t addr = mmCRTCV_BLACK_COLOR; ++ uint32_t value = dm_read_reg(tg->ctx, addr); ++ ++ set_reg_field_value( ++ value, ++ black_color->black_color_b_cb, ++ CRTCV_BLACK_COLOR, ++ CRTC_BLACK_COLOR_B_CB); ++ set_reg_field_value( ++ value, ++ black_color->black_color_g_y, ++ CRTCV_BLACK_COLOR, ++ CRTC_BLACK_COLOR_G_Y); ++ set_reg_field_value( ++ value, ++ black_color->black_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 crtc_black_color *overscan_color) ++{ ++ struct dc_context *ctx = tg->ctx; ++ uint32_t value = 0; ++ uint32_t addr; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ set_reg_field_value( ++ value, ++ overscan_color->black_color_b_cb, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_BLUE); ++ ++ set_reg_field_value( ++ value, ++ overscan_color->black_color_g_y, ++ CRTCV_OVERSCAN_COLOR, ++ CRTC_OVERSCAN_COLOR_GREEN); ++ ++ set_reg_field_value( ++ value, ++ overscan_color->black_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 crtc_black_color *blank_color, ++ const struct crtc_black_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; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ 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 void dce110_timing_generator_v_get_crtc_positions( ++ struct timing_generator *tg, ++ int32_t *h_position, ++ int32_t *v_position) ++{ ++ uint32_t value; ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); ++ ++ value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); ++ ++ *h_position = get_reg_field_value( ++ value, ++ CRTCV_STATUS_POSITION, ++ CRTC_HORZ_COUNT); ++ ++ *v_position = get_reg_field_value( ++ value, ++ CRTCV_STATUS_POSITION, ++ CRTC_VERT_COUNT); ++} ++ ++static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) ++{ ++ struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(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) ++{ ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "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) ++{ ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "Timing Sync not supported on underlay pipe\n"); ++ return; ++} ++ ++static void dce110_timing_generator_v_enable_reset_trigger( ++ struct timing_generator *tg, ++ const struct trigger_params *trigger_params) ++{ ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "Timing Sync not supported on underlay pipe\n"); ++ return; ++} ++ ++static void dce110_timing_generator_v_disable_reset_trigger( ++ struct timing_generator *tg) ++{ ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "Timing Sync not supported on underlay pipe\n"); ++ return; ++} ++ ++static void dce110_timing_generator_v_tear_down_global_swap_lock( ++ struct timing_generator *tg) ++{ ++ dal_logger_write(tg->ctx->logger, LOG_MAJOR_ERROR, ++ LOG_MINOR_COMPONENT_CONTROLLER, ++ "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 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 = dce110_timing_generator_v_get_crtc_positions, ++ .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 ++}; ++ ++bool dce110_timing_generator_v_construct( ++ struct dce110_timing_generator *tg110, ++ struct adapter_service *as, ++ struct dc_context *ctx) ++{ ++ if (!tg110) ++ return false; ++ ++ if (!as) ++ return false; ++ ++ tg110->controller_id = CONTROLLER_ID_UNDERLAY0; ++ ++ tg110->base.funcs = &dce110_tg_v_funcs; ++ ++ tg110->base.ctx = ctx; ++ tg110->base.bp = dal_adapter_service_get_bios_parser(as); ++ ++ 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; ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.h +new file mode 100644 +index 0000000..fe3fb81 +--- /dev/null ++++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_timing_generator_v.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2012-15 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. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DC_TIMING_GENERATOR_V_DCE110_H__ ++#define __DC_TIMING_GENERATOR_V_DCE110_H__ ++ ++bool dce110_timing_generator_v_construct( ++ struct dce110_timing_generator *tg110, ++ struct adapter_service *as, ++ struct dc_context *ctx); ++ ++#endif /* __DC_TIMING_GENERATOR_V_DCE110_H__ */ +-- +2.7.4 + |