aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0778-drm-amd-dal-Add-timing-generator-for-underlay-pipe.patch
diff options
context:
space:
mode:
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.patch877
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
+