summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Wentland <harry.wentland@amd.com>2016-02-04 12:14:17 -0500
committerAlex Deucher <alexander.deucher@amd.com>2016-03-09 14:40:51 -0500
commitc15f08c2449c214acb151b932591cc8f2a4811a2 (patch)
tree725c40824fb3fb66dc410ea4090b9cfff87f0a0d
parentcb34ce34c0b9dec68aa0c5db9fa7f22079de9580 (diff)
drm/amd/dal: Add Carrizo HW sequencer and resource
Adds dce110_resource and dce110_hw_sequencer files. dce110_resource manages creation of HW resources, along with correct ASIC register offset for each block. dce110_hw_sequencers is responsible for programming HW sequences, such as enable_stream, program_scaler, power_down_encoders, etc. Signed-off-by: Harry Wentland <harry.wentland@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/Makefile15
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c1658
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h36
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c1238
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h46
5 files changed, 2993 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/Makefile b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile
new file mode 100644
index 000000000000..ae9d2de92da2
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the 'controller' sub-component of DAL.
+# It provides the control and status of HW CRTC block.
+
+DCE110 = dce110_ipp.o dce110_ipp_cursor.o \
+dce110_ipp_gamma.o dce110_link_encoder.o dce110_opp.o \
+dce110_opp_formatter.o dce110_opp_regamma.o dce110_stream_encoder.o \
+dce110_timing_generator.o dce110_transform.o dce110_transform_gamut.o \
+dce110_transform_scl.o dce110_transform_sclv.o dce110_opp_csc.o\
+dce110_compressor.o dce110_mem_input.o dce110_hw_sequencer.o \
+dce110_resource.o dce110_transform_bit_depth.o dce110_clock_source.o
+
+AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110))
+
+AMD_DAL_FILES += $(AMD_DAL_DCE110)
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c
new file mode 100644
index 000000000000..71fa7b1f8061
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c
@@ -0,0 +1,1658 @@
+/*
+ * Copyright 2015 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
+ *
+ */
+#include "dm_services.h"
+#include "dc.h"
+#include "dc_bios_types.h"
+#include "core_types.h"
+#include "core_status.h"
+#include "resource.h"
+#include "hw_sequencer.h"
+#include "dm_helpers.h"
+#include "dce110_hw_sequencer.h"
+
+#include "gpu/dce110/dc_clock_gating_dce110.h"
+
+#include "timing_generator.h"
+#include "mem_input.h"
+#include "opp.h"
+#include "ipp.h"
+#include "transform.h"
+#include "stream_encoder.h"
+#include "link_encoder.h"
+#include "clock_source.h"
+
+/* include DCE11 register header files */
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+struct dce110_hw_seq_reg_offsets {
+ uint32_t dcfe;
+ uint32_t blnd;
+ uint32_t crtc;
+};
+
+enum pipe_lock_control {
+ PIPE_LOCK_CONTROL_GRAPHICS = 1 << 0,
+ PIPE_LOCK_CONTROL_BLENDER = 1 << 1,
+ PIPE_LOCK_CONTROL_SCL = 1 << 2,
+ PIPE_LOCK_CONTROL_SURFACE = 1 << 3,
+ PIPE_LOCK_CONTROL_MODE = 1 << 4
+};
+
+enum blender_mode {
+ BLENDER_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */
+ BLENDER_MODE_OTHER_PIPE, /* Data from other pipe only */
+ BLENDER_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */
+ BLENDER_MODE_STEREO
+};
+
+static const struct dce110_hw_seq_reg_offsets reg_offsets[] = {
+{
+ .dcfe = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .blnd = (mmBLND0_BLND_CONTROL - mmBLND_CONTROL),
+ .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
+},
+{
+ .dcfe = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .blnd = (mmBLND1_BLND_CONTROL - mmBLND_CONTROL),
+ .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
+},
+{
+ .dcfe = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .blnd = (mmBLND2_BLND_CONTROL - mmBLND_CONTROL),
+ .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL),
+}
+};
+
+#define HW_REG_DCFE(reg, id)\
+ (reg + reg_offsets[id].dcfe)
+
+#define HW_REG_BLND(reg, id)\
+ (reg + reg_offsets[id].blnd)
+
+#define HW_REG_CRTC(reg, id)\
+ (reg + reg_offsets[id].crtc)
+
+
+/*******************************************************************************
+ * Private definitions
+ ******************************************************************************/
+/***************************PIPE_CONTROL***********************************/
+static void dce110_enable_fe_clock(
+ struct dc_context *ctx, uint8_t controller_id, bool enable)
+{
+ uint32_t value = 0;
+ uint32_t addr;
+
+ /*TODO: proper offset*/
+ addr = HW_REG_DCFE(mmDCFE_CLOCK_CONTROL, controller_id);
+
+ value = dm_read_reg(ctx, addr);
+
+ set_reg_field_value(
+ value,
+ enable,
+ DCFE_CLOCK_CONTROL,
+ DCFE_CLOCK_ENABLE);
+
+ dm_write_reg(ctx, addr, value);
+}
+
+static void dce110_init_pte(struct dc_context *ctx)
+{
+ uint32_t addr;
+ uint32_t value = 0;
+ uint32_t chunk_int = 0;
+ uint32_t chunk_mul = 0;
+
+ addr = mmUNP_DVMM_PTE_CONTROL;
+ value = dm_read_reg(ctx, addr);
+
+ set_reg_field_value(
+ value,
+ 0,
+ DVMM_PTE_CONTROL,
+ DVMM_USE_SINGLE_PTE);
+
+ set_reg_field_value(
+ value,
+ 1,
+ DVMM_PTE_CONTROL,
+ DVMM_PTE_BUFFER_MODE0);
+
+ set_reg_field_value(
+ value,
+ 1,
+ DVMM_PTE_CONTROL,
+ DVMM_PTE_BUFFER_MODE1);
+
+ dm_write_reg(ctx, addr, value);
+
+ addr = mmDVMM_PTE_REQ;
+ value = dm_read_reg(ctx, addr);
+
+ chunk_int = get_reg_field_value(
+ value,
+ DVMM_PTE_REQ,
+ HFLIP_PTEREQ_PER_CHUNK_INT);
+
+ chunk_mul = get_reg_field_value(
+ value,
+ DVMM_PTE_REQ,
+ HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER);
+
+ if (chunk_int != 0x4 || chunk_mul != 0x4) {
+
+ set_reg_field_value(
+ value,
+ 255,
+ DVMM_PTE_REQ,
+ MAX_PTEREQ_TO_ISSUE);
+
+ set_reg_field_value(
+ value,
+ 4,
+ DVMM_PTE_REQ,
+ HFLIP_PTEREQ_PER_CHUNK_INT);
+
+ set_reg_field_value(
+ value,
+ 4,
+ DVMM_PTE_REQ,
+ HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER);
+
+ dm_write_reg(ctx, addr, value);
+ }
+}
+
+/* this is a workaround for hw bug - it is a trigger on r/w */
+static void trigger_write_crtc_h_blank_start_end(
+ struct dc_context *ctx,
+ uint8_t controller_id)
+{
+ uint32_t value;
+ uint32_t addr;
+
+ addr = HW_REG_CRTC(mmCRTC_H_BLANK_START_END, controller_id);
+ value = dm_read_reg(ctx, addr);
+ dm_write_reg(ctx, addr, value);
+}
+
+static bool dce110_pipe_control_lock(
+ struct dc_context *ctx,
+ uint8_t controller_idx,
+ uint32_t control_mask,
+ bool lock)
+{
+ uint32_t addr = HW_REG_BLND(mmBLND_V_UPDATE_LOCK, controller_idx);
+ uint32_t value = dm_read_reg(ctx, addr);
+ bool need_to_wait = false;
+
+ if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS)
+ set_reg_field_value(
+ value,
+ lock,
+ BLND_V_UPDATE_LOCK,
+ BLND_DCP_GRPH_V_UPDATE_LOCK);
+
+ if (control_mask & PIPE_LOCK_CONTROL_SCL)
+ set_reg_field_value(
+ value,
+ lock,
+ BLND_V_UPDATE_LOCK,
+ BLND_SCL_V_UPDATE_LOCK);
+
+ if (control_mask & PIPE_LOCK_CONTROL_SURFACE)
+ set_reg_field_value(
+ value,
+ lock,
+ BLND_V_UPDATE_LOCK,
+ BLND_DCP_GRPH_SURF_V_UPDATE_LOCK);
+
+ if (control_mask & PIPE_LOCK_CONTROL_BLENDER) {
+ set_reg_field_value(
+ value,
+ lock,
+ BLND_V_UPDATE_LOCK,
+ BLND_BLND_V_UPDATE_LOCK);
+ need_to_wait = true;
+ }
+
+ if (control_mask & PIPE_LOCK_CONTROL_MODE)
+ set_reg_field_value(
+ value,
+ lock,
+ BLND_V_UPDATE_LOCK,
+ BLND_V_UPDATE_LOCK_MODE);
+
+ dm_write_reg(ctx, addr, value);
+
+ if (!lock && need_to_wait) {
+ uint8_t counter = 0;
+ const uint8_t counter_limit = 100;
+ const uint16_t delay_us = 1000;
+
+ uint8_t pipe_pending;
+
+ addr = HW_REG_BLND(mmBLND_REG_UPDATE_STATUS,
+ controller_idx);
+
+ while (counter < counter_limit) {
+ value = dm_read_reg(ctx, addr);
+
+ pipe_pending = 0;
+
+ if (control_mask & PIPE_LOCK_CONTROL_BLENDER) {
+ pipe_pending |=
+ get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ BLND_BLNDC_UPDATE_PENDING);
+ pipe_pending |= get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ BLND_BLNDO_UPDATE_PENDING);
+ }
+
+ if (control_mask & PIPE_LOCK_CONTROL_SCL) {
+ pipe_pending |=
+ get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ SCL_BLNDC_UPDATE_PENDING);
+ pipe_pending |=
+ get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ SCL_BLNDO_UPDATE_PENDING);
+ }
+ if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) {
+ pipe_pending |=
+ get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ DCP_BLNDC_GRPH_UPDATE_PENDING);
+ pipe_pending |=
+ get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ DCP_BLNDO_GRPH_UPDATE_PENDING);
+ }
+ if (control_mask & PIPE_LOCK_CONTROL_SURFACE) {
+ pipe_pending |= get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ DCP_BLNDC_GRPH_SURF_UPDATE_PENDING);
+ pipe_pending |= get_reg_field_value(
+ value,
+ BLND_REG_UPDATE_STATUS,
+ DCP_BLNDO_GRPH_SURF_UPDATE_PENDING);
+ }
+
+ if (pipe_pending == 0)
+ break;
+
+ counter++;
+ dm_delay_in_microseconds(ctx, delay_us);
+ }
+
+ if (counter == counter_limit) {
+ dal_logger_write(
+ ctx->logger,
+ LOG_MAJOR_WARNING,
+ LOG_MINOR_COMPONENT_CONTROLLER,
+ "%s: wait for update exceeded (wait %d us)\n",
+ __func__,
+ counter * delay_us);
+ dal_logger_write(
+ ctx->logger,
+ LOG_MAJOR_WARNING,
+ LOG_MINOR_COMPONENT_CONTROLLER,
+ "%s: control %d, remain value %x\n",
+ __func__,
+ control_mask,
+ value);
+ } else {
+ /* OK. */
+ }
+ }
+
+ if (!lock && (control_mask & PIPE_LOCK_CONTROL_BLENDER))
+ trigger_write_crtc_h_blank_start_end(ctx, controller_idx);
+
+ return true;
+}
+
+static void dce110_set_blender_mode(
+ struct dc_context *ctx,
+ uint8_t controller_id,
+ uint32_t mode)
+{
+ uint32_t value;
+ uint32_t addr = HW_REG_BLND(mmBLND_CONTROL, controller_id);
+ uint32_t blnd_mode;
+ uint32_t feedthrough = 0;
+
+ switch (mode) {
+ case BLENDER_MODE_OTHER_PIPE:
+ feedthrough = 0;
+ blnd_mode = 1;
+ break;
+ case BLENDER_MODE_BLENDING:
+ feedthrough = 0;
+ blnd_mode = 2;
+ break;
+ case BLENDER_MODE_CURRENT_PIPE:
+ default:
+ feedthrough = 1;
+ blnd_mode = 0;
+ break;
+ }
+
+ value = dm_read_reg(ctx, addr);
+
+ set_reg_field_value(
+ value,
+ feedthrough,
+ BLND_CONTROL,
+ BLND_FEEDTHROUGH_EN);
+
+ set_reg_field_value(
+ value,
+ blnd_mode,
+ BLND_CONTROL,
+ BLND_MODE);
+
+ dm_write_reg(ctx, addr, value);
+}
+
+static void dce110_crtc_switch_to_clk_src(
+ struct clock_source *clk_src, uint8_t crtc_inst)
+{
+ uint32_t pixel_rate_cntl_value;
+ uint32_t addr;
+
+ addr = mmCRTC0_PIXEL_RATE_CNTL + crtc_inst *
+ (mmCRTC1_PIXEL_RATE_CNTL - mmCRTC0_PIXEL_RATE_CNTL);
+
+ pixel_rate_cntl_value = dm_read_reg(clk_src->ctx, addr);
+
+ if (clk_src->id == CLOCK_SOURCE_ID_EXTERNAL)
+ set_reg_field_value(pixel_rate_cntl_value, 1,
+ CRTC0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE);
+ else {
+ set_reg_field_value(pixel_rate_cntl_value,
+ 0,
+ CRTC0_PIXEL_RATE_CNTL,
+ DP_DTO0_ENABLE);
+
+ set_reg_field_value(pixel_rate_cntl_value,
+ clk_src->id - 1,
+ CRTC0_PIXEL_RATE_CNTL,
+ CRTC0_PIXEL_RATE_SOURCE);
+ }
+ dm_write_reg(clk_src->ctx, addr, pixel_rate_cntl_value);
+}
+/**************************************************************************/
+
+static void enable_display_pipe_clock_gating(
+ struct dc_context *ctx,
+ bool clock_gating)
+{
+ /*TODO*/
+}
+
+static bool dce110_enable_display_power_gating(
+ struct dc_context *ctx,
+ uint8_t controller_id,
+ struct dc_bios *dcb,
+ enum pipe_gating_control power_gating)
+{
+ enum bp_result bp_result = BP_RESULT_OK;
+ enum bp_pipe_control_action cntl;
+
+ if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment))
+ return true;
+
+ if (power_gating == PIPE_GATING_CONTROL_INIT)
+ cntl = ASIC_PIPE_INIT;
+ else if (power_gating == PIPE_GATING_CONTROL_ENABLE)
+ cntl = ASIC_PIPE_ENABLE;
+ else
+ cntl = ASIC_PIPE_DISABLE;
+
+ if (!(power_gating == PIPE_GATING_CONTROL_INIT && controller_id != 0))
+ bp_result = dcb->funcs->enable_disp_power_gating(
+ dcb, controller_id + 1, cntl);
+
+ if (power_gating != PIPE_GATING_CONTROL_ENABLE)
+ dce110_init_pte(ctx);
+
+ if (bp_result == BP_RESULT_OK)
+ return true;
+ else
+ return false;
+}
+
+
+static bool set_gamma_ramp(
+ struct input_pixel_processor *ipp,
+ struct output_pixel_processor *opp,
+ const struct gamma_ramp *ramp,
+ const struct gamma_parameters *params)
+{
+ /*Power on LUT memory*/
+ opp->funcs->opp_power_on_regamma_lut(opp, true);
+
+
+ if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8 ||
+ params->selected_gamma_lut == GRAPHICS_GAMMA_LUT_LEGACY) {
+ /* do legacy DCP for 256 colors if we are requested to do so */
+ ipp->funcs->ipp_set_legacy_input_gamma_ramp(
+ ipp, ramp, params);
+
+ ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, true);
+
+ /* set bypass */
+ ipp->funcs->ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED);
+
+ ipp->funcs->ipp_set_degamma(ipp, params, true);
+
+ opp->funcs->opp_set_regamma(opp, ramp, params, true);
+ } else if (params->selected_gamma_lut ==
+ GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA) {
+ if (!opp->funcs->opp_map_legacy_and_regamma_hw_to_x_user(
+ opp, ramp, params)) {
+ BREAK_TO_DEBUGGER();
+ /* invalid parameters or bug */
+ return false;
+ }
+
+ /* do legacy DCP for 256 colors if we are requested to do so */
+ ipp->funcs->ipp_set_legacy_input_gamma_ramp(
+ ipp, ramp, params);
+
+ ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, true);
+
+ /* set bypass */
+ ipp->funcs->ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED);
+ } else {
+ ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, false);
+
+ ipp->funcs->ipp_program_prescale(ipp, params->surface_pixel_format);
+
+ /* Do degamma step : remove the given gamma value from FB.
+ * For FP16 or no degamma do by pass */
+ ipp->funcs->ipp_set_degamma(ipp, params, false);
+
+ opp->funcs->opp_set_regamma(opp, ramp, params, false);
+ }
+
+ /*re-enable low power mode for LUT memory*/
+ opp->funcs->opp_power_on_regamma_lut(opp, false);
+
+ return true;
+}
+
+static enum dc_status bios_parser_crtc_source_select(
+ struct core_stream *stream)
+{
+ struct dc_bios *dcb;
+ /* call VBIOS table to set CRTC source for the HW
+ * encoder block
+ * note: video bios clears all FMT setting here. */
+ struct bp_crtc_source_select crtc_source_select = {0};
+ const struct core_sink *sink = stream->sink;
+
+ crtc_source_select.engine_id = stream->stream_enc->id;
+ crtc_source_select.controller_id = stream->controller_idx + 1;
+ /*TODO: Need to un-hardcode color depth, dp_audio and account for
+ * the case where signal and sink signal is different (translator
+ * encoder)*/
+ crtc_source_select.signal = sink->public.sink_signal;
+ crtc_source_select.enable_dp_audio = false;
+ crtc_source_select.sink_signal = sink->public.sink_signal;
+ crtc_source_select.display_output_bit_depth = PANEL_8BIT_COLOR;
+
+ dcb = dal_adapter_service_get_bios_parser(sink->link->adapter_srv);
+
+ if (BP_RESULT_OK != dcb->funcs->crtc_source_select(
+ dcb,
+ &crtc_source_select)) {
+ return DC_ERROR_UNEXPECTED;
+ }
+
+ return DC_OK;
+}
+
+static enum color_space surface_color_to_color_space(
+ struct plane_colorimetry *colorimetry)
+{
+ enum color_space color_space = COLOR_SPACE_UNKNOWN;
+
+ switch (colorimetry->color_space) {
+ case SURFACE_COLOR_SPACE_SRGB:
+ case SURFACE_COLOR_SPACE_XRRGB:
+ if (colorimetry->limited_range)
+ color_space = COLOR_SPACE_SRGB_LIMITED_RANGE;
+ else
+ color_space = COLOR_SPACE_SRGB_FULL_RANGE;
+ break;
+ case SURFACE_COLOR_SPACE_BT601:
+ case SURFACE_COLOR_SPACE_XVYCC_BT601:
+ color_space = COLOR_SPACE_YCBCR601;
+ break;
+ case SURFACE_COLOR_SPACE_BT709:
+ case SURFACE_COLOR_SPACE_XVYCC_BT709:
+ color_space = COLOR_SPACE_YCBCR709;
+ break;
+ }
+
+ return color_space;
+}
+
+/*******************************FMT**************************************/
+static void program_fmt(
+ struct output_pixel_processor *opp,
+ struct bit_depth_reduction_params *fmt_bit_depth,
+ struct clamping_and_pixel_encoding_params *clamping)
+{
+ /* dithering is affected by <CrtcSourceSelect>, hence should be
+ * programmed afterwards */
+
+ opp->funcs->opp_program_bit_depth_reduction(
+ opp,
+ fmt_bit_depth);
+
+ opp->funcs->opp_program_clamping_and_pixel_encoding(
+ opp,
+ clamping);
+
+ return;
+}
+
+static void update_bios_scratch_critical_state(struct adapter_service *as,
+ bool state)
+{
+ struct dc_bios *dcb = dal_adapter_service_get_bios_parser(as);
+
+ dcb->funcs->set_scratch_critical_state(dcb, state);
+}
+
+static void update_info_frame(struct core_stream *stream)
+{
+ if (dc_is_hdmi_signal(stream->signal))
+ stream->stream_enc->funcs->update_hdmi_info_packets(
+ stream->stream_enc,
+ &stream->encoder_info_frame);
+ else if (dc_is_dp_signal(stream->signal))
+ stream->stream_enc->funcs->update_dp_info_packets(
+ stream->stream_enc,
+ &stream->encoder_info_frame);
+}
+
+
+static void enable_stream(struct core_stream *stream)
+{
+ enum lane_count lane_count =
+ stream->sink->link->cur_link_settings.lane_count;
+
+ struct dc_crtc_timing *timing = &stream->public.timing;
+ struct core_link *link = stream->sink->link;
+
+ /* 1. update AVI info frame (HDMI, DP)
+ * we always need to update info frame
+ */
+ uint32_t active_total_with_borders;
+ uint32_t early_control = 0;
+ struct timing_generator *tg = stream->tg;
+
+ update_info_frame(stream);
+ /* enable early control to avoid corruption on DP monitor*/
+ active_total_with_borders =
+ timing->h_addressable
+ + timing->h_border_left
+ + timing->h_border_right;
+
+ if (lane_count != 0)
+ early_control = active_total_with_borders % lane_count;
+
+ if (early_control == 0)
+ early_control = lane_count;
+
+ tg->funcs->set_early_control(tg, early_control);
+
+ /* enable audio only within mode set */
+ if (stream->audio != NULL) {
+ dal_audio_enable_output(
+ stream->audio,
+ stream->stream_enc->id,
+ stream->signal);
+ }
+
+ /* For MST, there are multiply stream go to only one link.
+ * connect DIG back_end to front_end while enable_stream and
+ * disconnect them during disable_stream
+ * BY this, it is logic clean to separate stream and link */
+ link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc,
+ stream->stream_enc->id, true);
+
+}
+
+static void disable_stream(struct core_stream *stream)
+{
+ struct core_link *link = stream->sink->link;
+
+ if (dc_is_hdmi_signal(stream->signal))
+ stream->stream_enc->funcs->stop_hdmi_info_packets(
+ stream->stream_enc);
+
+ if (dc_is_dp_signal(stream->signal))
+ stream->stream_enc->funcs->stop_dp_info_packets(
+ stream->stream_enc);
+
+ if (stream->audio) {
+ /* mute audio */
+ dal_audio_mute(stream->audio, stream->stream_enc->id,
+ stream->signal);
+
+ /* TODO: notify audio driver for if audio modes list changed
+ * add audio mode list change flag */
+ /* dal_audio_disable_azalia_audio_jack_presence(stream->audio,
+ * stream->stream_engine_id);
+ */
+ }
+
+ /* blank at encoder level */
+ if (dc_is_dp_signal(stream->signal))
+ stream->stream_enc->funcs->dp_blank(stream->stream_enc);
+
+ link->link_enc->funcs->connect_dig_be_to_fe(
+ link->link_enc,
+ stream->stream_enc->id,
+ false);
+
+}
+
+static void unblank_stream(struct core_stream *stream,
+ struct link_settings *link_settings)
+{
+ struct encoder_unblank_param params = { { 0 } };
+
+ /* only 3 items below are used by unblank */
+ params.crtc_timing.pixel_clock =
+ stream->public.timing.pix_clk_khz;
+ params.link_settings.link_rate = link_settings->link_rate;
+ stream->stream_enc->funcs->dp_unblank(
+ stream->stream_enc, &params);
+}
+
+static enum color_space get_output_color_space(
+ const struct dc_crtc_timing *dc_crtc_timing)
+{
+ enum color_space color_space = COLOR_SPACE_SRGB_FULL_RANGE;
+
+ switch (dc_crtc_timing->pixel_encoding) {
+ case PIXEL_ENCODING_YCBCR422:
+ case PIXEL_ENCODING_YCBCR444:
+ case PIXEL_ENCODING_YCBCR420:
+ {
+ if ((dc_crtc_timing->timing_standard ==
+ TIMING_STANDARD_CEA770) ||
+ (dc_crtc_timing->timing_standard ==
+ TIMING_STANDARD_CEA861)) {
+ if (dc_crtc_timing->pix_clk_khz > 27030) {
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space =
+ COLOR_SPACE_YCBCR709_YONLY;
+ else
+ color_space = COLOR_SPACE_YCBCR709;
+ } else {
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space =
+ COLOR_SPACE_YCBCR601_YONLY;
+ else
+ color_space = COLOR_SPACE_YCBCR601;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return color_space;
+}
+
+static enum dc_status apply_single_controller_ctx_to_hw(uint8_t controller_idx,
+ struct validate_context *context,
+ const struct dc *dc)
+{
+ struct core_stream *stream =
+ context->res_ctx.controller_ctx[controller_idx].stream;
+ struct output_pixel_processor *opp =
+ context->res_ctx.pool.opps[controller_idx];
+ bool timing_changed = context->res_ctx.controller_ctx[controller_idx]
+ .flags.timing_changed;
+ enum color_space color_space;
+
+ if (timing_changed) {
+ /* Must blank CRTC after disabling power gating and before any
+ * programming, otherwise CRTC will be hung in bad state
+ */
+ stream->tg->funcs->set_blank(stream->tg, true);
+
+ core_link_disable_stream(stream->sink->link, stream);
+
+ /*TODO: AUTO check if timing changed*/
+ if (false == stream->clock_source->funcs->program_pix_clk(
+ stream->clock_source,
+ &stream->pix_clk_params,
+ &stream->pll_settings)) {
+ BREAK_TO_DEBUGGER();
+ return DC_ERROR_UNEXPECTED;
+ }
+
+ stream->tg->funcs->program_timing(
+ stream->tg,
+ &stream->public.timing,
+ true);
+ }
+
+ /*TODO: mst support - use total stream count*/
+ stream->mi->funcs->mem_input_allocate_dmif_buffer(
+ stream->mi,
+ &stream->public.timing,
+ context->target_count);
+
+ if (timing_changed) {
+ if (false == stream->tg->funcs->enable_crtc(
+ stream->tg)) {
+ BREAK_TO_DEBUGGER();
+ return DC_ERROR_UNEXPECTED;
+ }
+ }
+
+ /* TODO: move to stream encoder */
+ if (stream->signal != SIGNAL_TYPE_VIRTUAL)
+ if (DC_OK != bios_parser_crtc_source_select(stream)) {
+ BREAK_TO_DEBUGGER();
+ return DC_ERROR_UNEXPECTED;
+ }
+
+ opp->funcs->opp_set_dyn_expansion(
+ opp,
+ COLOR_SPACE_YCBCR601,
+ stream->public.timing.display_color_depth,
+ stream->sink->public.sink_signal);
+
+ program_fmt(opp, &stream->bit_depth_params, &stream->clamping);
+
+ stream->sink->link->link_enc->funcs->setup(
+ stream->sink->link->link_enc,
+ stream->signal);
+
+ if (dc_is_dp_signal(stream->signal))
+ stream->stream_enc->funcs->dp_set_stream_attribute(
+ stream->stream_enc,
+ &stream->public.timing);
+
+ if (dc_is_hdmi_signal(stream->signal))
+ stream->stream_enc->funcs->hdmi_set_stream_attribute(
+ stream->stream_enc,
+ &stream->public.timing,
+ stream->audio != NULL);
+
+ if (dc_is_dvi_signal(stream->signal))
+ stream->stream_enc->funcs->dvi_set_stream_attribute(
+ stream->stream_enc,
+ &stream->public.timing,
+ (stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ?
+ true : false);
+
+ if (stream->audio != NULL) {
+ if (AUDIO_RESULT_OK != dal_audio_setup(
+ stream->audio,
+ &stream->audio_output,
+ &stream->public.audio_info)) {
+ BREAK_TO_DEBUGGER();
+ return DC_ERROR_UNEXPECTED;
+ }
+ }
+
+ /* Setup audio rate clock source */
+ if (stream->audio != NULL)
+ dal_audio_setup_audio_wall_dto(
+ stream->audio,
+ stream->signal,
+ &stream->audio_output.crtc_info,
+ &stream->audio_output.pll_info);
+
+ /* program blank color */
+ color_space = get_output_color_space(&stream->public.timing);
+ stream->tg->funcs->set_blank_color(
+ context->res_ctx.pool.timing_generators[controller_idx],
+ color_space);
+
+ if (timing_changed)
+ core_link_enable_stream(stream->sink->link, stream);
+
+ if (dc_is_dp_signal(stream->signal))
+ unblank_stream(stream, &stream->sink->link->cur_link_settings);
+
+ return DC_OK;
+}
+
+
+/******************************************************************************/
+
+static void power_down_encoders(struct dc *dc)
+{
+ int i;
+
+ for (i = 0; i < dc->link_count; i++) {
+ dc->links[i]->link_enc->funcs->disable_output(
+ dc->links[i]->link_enc, SIGNAL_TYPE_NONE);
+ }
+}
+
+static void power_down_controllers(struct dc *dc)
+{
+ int i;
+
+ for (i = 0; i < dc->res_pool.controller_count; i++) {
+ dc->res_pool.timing_generators[i]->funcs->disable_crtc(
+ dc->res_pool.timing_generators[i]);
+ }
+}
+
+static void power_down_clock_sources(struct dc *dc)
+{
+ int i;
+
+ for (i = 0; i < dc->res_pool.clk_src_count; i++) {
+ if (dc->res_pool.clock_sources[i]->funcs->cs_power_down(
+ dc->res_pool.clock_sources[i]) == false)
+ dm_error("Failed to power down pll! (clk src index=%d)\n", i);
+ }
+}
+
+static void power_down_all_hw_blocks(struct dc *dc)
+{
+ power_down_encoders(dc);
+
+ power_down_controllers(dc);
+
+ power_down_clock_sources(dc);
+}
+
+static void disable_vga_and_power_gate_all_controllers(
+ struct dc *dc)
+{
+ int i;
+ struct timing_generator *tg;
+ struct dc_bios *dcb;
+ struct dc_context *ctx;
+
+ dcb = dal_adapter_service_get_bios_parser(
+ dc->res_pool.adapter_srv);
+
+ for (i = 0; i < dc->res_pool.controller_count; i++) {
+ tg = dc->res_pool.timing_generators[i];
+ ctx = dc->ctx;
+
+ tg->funcs->disable_vga(tg);
+
+ /* Enable CLOCK gating for each pipe BEFORE controller
+ * powergating. */
+ enable_display_pipe_clock_gating(ctx,
+ true);
+ dc->hwss.enable_display_power_gating(ctx, i, dcb,
+ PIPE_GATING_CONTROL_ENABLE);
+ }
+}
+
+/**
+ * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need:
+ * 1. Power down all DC HW blocks
+ * 2. Disable VGA engine on all controllers
+ * 3. Enable power gating for controller
+ * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS)
+ */
+static void enable_accelerated_mode(struct dc *dc)
+{
+ struct dc_bios *dcb;
+
+ dcb = dal_adapter_service_get_bios_parser(dc->res_pool.adapter_srv);
+
+ power_down_all_hw_blocks(dc);
+
+ disable_vga_and_power_gate_all_controllers(dc);
+
+ dcb->funcs->set_scratch_acc_mode_change(dcb);
+}
+
+#if 0
+static enum clocks_state get_required_clocks_state(
+ struct display_clock *display_clock,
+ struct state_dependent_clocks *req_state_dep_clks)
+{
+ enum clocks_state clocks_required_state;
+ enum clocks_state dp_link_required_state;
+ enum clocks_state overall_required_state;
+
+ clocks_required_state = dal_display_clock_get_required_clocks_state(
+ display_clock, req_state_dep_clks);
+
+ dp_link_required_state = CLOCKS_STATE_ULTRA_LOW;
+
+ /* overall required state is the max of required state for clocks
+ * (pixel, display clock) and the required state for DP link. */
+ overall_required_state =
+ clocks_required_state > dp_link_required_state ?
+ clocks_required_state : dp_link_required_state;
+
+ /* return the min required state */
+ return overall_required_state;
+}
+
+static bool dc_pre_clock_change(
+ struct dc_context *ctx,
+ struct minimum_clocks_calculation_result *min_clk_in,
+ enum clocks_state required_clocks_state,
+ struct power_to_dal_info *output)
+{
+ struct dal_to_power_info input = {0};
+
+ input.min_deep_sleep_sclk = min_clk_in->min_deep_sleep_sclk;
+ input.min_mclk = min_clk_in->min_mclk_khz;
+ input.min_sclk = min_clk_in->min_sclk_khz;
+
+ switch (required_clocks_state) {
+ case CLOCKS_STATE_ULTRA_LOW:
+ input.required_clock = PP_CLOCKS_STATE_ULTRA_LOW;
+ break;
+ case CLOCKS_STATE_LOW:
+ input.required_clock = PP_CLOCKS_STATE_LOW;
+ break;
+ case CLOCKS_STATE_NOMINAL:
+ input.required_clock = PP_CLOCKS_STATE_NOMINAL;
+ break;
+ case CLOCKS_STATE_PERFORMANCE:
+ input.required_clock = PP_CLOCKS_STATE_PERFORMANCE;
+ break;
+ default:
+ input.required_clock = PP_CLOCKS_STATE_NOMINAL;
+ break;
+ }
+
+ if (!dc_service_pp_pre_dce_clock_change(ctx, &input, output)) {
+ dm_error("DC: dc_service_pp_pre_dce_clock_change failed!\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool dc_set_clocks_and_clock_state (
+ struct validate_context *context)
+{
+ struct power_to_dal_info output = {0};
+
+ struct display_clock *disp_clk = context->res_ctx.pool.display_clock;
+ struct dc_context *ctx = context->targets[0]->ctx;
+
+
+ if (!dc_pre_clock_change(
+ ctx,
+ &context->res_ctx.min_clocks,
+ get_required_clocks_state(
+ context->res_ctx.pool.display_clock,
+ &context->res_ctx.state_clocks),
+ &output)) {
+ /* "output" was not updated by PPLib.
+ * DAL will use default values for set mode.
+ *
+ * Do NOT fail this call. */
+ return true;
+ }
+
+ /* PPLib accepted the "clock state" that we need, that means we
+ * can store it as minimum state because PPLib guarantees not go below
+ * that state.
+ *
+ * Update the clock state here (prior to setting Pixel clock,
+ * or Display clock)
+ **/
+ if (!dal_display_clock_set_min_clocks_state(
+ disp_clk, context->res_ctx.required_clocks_state)) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to set minimum clock state!\n");
+ }
+
+
+ /*bm_clk_info.max_mclk_khz = output.max_mclk;
+ bm_clk_info.min_mclk_khz = output.min_mclk;
+ bm_clk_info.max_sclk_khz = output.max_sclk;
+ bm_clk_info.min_sclk_khz = output.min_sclk;*/
+
+ /* Now let Bandwidth Manager know about values we got from PPLib. */
+ /*dal_bandwidth_manager_set_dynamic_clock_info(bw_mgr, &bm_clk_info);*/
+
+ return true;
+}
+#endif
+
+/**
+ * Call display_engine_clock_dce80 to perform the Dclk programming.
+ */
+static void set_display_clock(struct validate_context *context)
+{
+ /* Program the display engine clock.
+ * Check DFS bypass mode support or not. DFSbypass feature is only when
+ * BIOS GPU info table reports support. */
+
+ if (/*dal_adapter_service_is_dfs_bypass_enabled()*/ false) {
+ /*TODO: set_display_clock_dfs_bypass(
+ hws,
+ path_set,
+ context->res_ctx.pool.display_clock,
+ context->res_ctx.min_clocks.min_dclk_khz);*/
+ } else
+ dal_display_clock_set_clock(context->res_ctx.pool.display_clock,
+ context->bw_results.dispclk_khz);
+
+ /* TODO: When changing display engine clock, DMCU WaitLoop must be
+ * reconfigured in order to maintain the same delays within DMCU
+ * programming sequences. */
+
+ /* TODO: Start GTC counter */
+}
+
+static void set_displaymarks(
+ const struct dc *dc, struct validate_context *context)
+{
+ uint8_t i, j;
+ uint8_t total_streams = 0;
+ uint8_t target_count = context->target_count;
+
+ for (i = 0; i < target_count; i++) {
+ struct core_target *target = context->targets[i];
+
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+
+ stream->mi->funcs->mem_input_program_display_marks(
+ stream->mi,
+ context->bw_results
+ .nbp_state_change_wm_ns[total_streams],
+ context->bw_results
+ .stutter_exit_wm_ns[total_streams],
+ context->bw_results
+ .urgent_wm_ns[total_streams],
+ stream->public.timing.h_total,
+ stream->public.timing.pix_clk_khz,
+ 1000 * dc->bw_vbios.blackout_duration
+ .value >> 24);
+ total_streams++;
+ }
+ }
+}
+
+static void set_safe_displaymarks(struct validate_context *context)
+{
+ uint8_t i, j;
+ uint8_t target_count = context->target_count;
+
+ for (i = 0; i < target_count; i++) {
+ struct core_target *target = context->targets[i];
+
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+
+ stream->mi->funcs->mem_input_program_safe_display_marks(
+ stream->mi);
+ }
+ }
+}
+
+static void program_bw(struct dc *dc, struct validate_context *context)
+{
+ set_safe_displaymarks(context);
+ /*TODO: when pplib works*/
+ /*dc_set_clocks_and_clock_state(context);*/
+
+ dc->hwss.set_display_clock(context);
+ dc->hwss.set_displaymarks(dc, context);
+}
+
+static void switch_dp_clock_sources(
+ const struct dc *dc,
+ struct validate_context *val_context)
+{
+ uint8_t i, j;
+ for (i = 0; i < val_context->target_count; i++) {
+ struct core_target *target = val_context->targets[i];
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+
+ if (dc_is_dp_signal(stream->signal)) {
+ struct clock_source *clk_src =
+ find_used_clk_src_for_sharing(
+ val_context, stream);
+
+ if (clk_src &&
+ clk_src != stream->clock_source) {
+ unreference_clock_source(
+ &val_context->res_ctx,
+ stream->clock_source);
+ stream->clock_source = clk_src;
+ reference_clock_source(
+ &val_context->res_ctx, clk_src);
+ dc->hwss.crtc_switch_to_clk_src(
+ clk_src, stream->opp->inst);
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+ * Public functions
+ ******************************************************************************/
+
+/*TODO: const validate_context*/
+static enum dc_status apply_ctx_to_hw(
+ const struct dc *dc,
+ struct validate_context *context)
+{
+ enum dc_status status;
+ uint8_t i;
+ struct resource_pool *pool = &context->res_ctx.pool;
+
+ update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv,
+ true);
+
+ for (i = 0; i < pool->controller_count; i++) {
+ struct controller_ctx *ctlr_ctx
+ = &context->res_ctx.controller_ctx[i];
+ struct dc_bios *dcb;
+
+ if (ctlr_ctx->flags.unchanged || !ctlr_ctx->stream)
+ continue;
+
+ dcb = dal_adapter_service_get_bios_parser(
+ context->res_ctx.pool.adapter_srv);
+
+ dc->hwss.enable_display_power_gating(
+ dc->ctx, i, dcb,
+ PIPE_GATING_CONTROL_DISABLE);
+ }
+
+ set_safe_displaymarks(context);
+ /*TODO: when pplib works*/
+ /*dc_set_clocks_and_clock_state(context);*/
+
+ if (context->bw_results.dispclk_khz
+ > dc->current_context.bw_results.dispclk_khz)
+ set_display_clock(context);
+
+ for (i = 0; i < pool->controller_count; i++) {
+ struct controller_ctx *ctlr_ctx
+ = &context->res_ctx.controller_ctx[i];
+ if (ctlr_ctx->flags.unchanged || !ctlr_ctx->stream)
+ continue;
+
+ status = apply_single_controller_ctx_to_hw(
+ i,
+ context,
+ dc);
+
+ if (DC_OK != status)
+ return status;
+ }
+ dc->hwss.set_displaymarks(dc, context);
+
+ update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv,
+ false);
+
+ switch_dp_clock_sources(dc, context);
+
+ return DC_OK;
+}
+
+
+/*******************************************************************************
+ * Front End programming
+ ******************************************************************************/
+
+static bool setup_line_buffer_pixel_depth(
+ const struct core_stream *stream,
+ enum lb_pixel_depth depth,
+ bool blank)
+{
+ enum lb_pixel_depth current_depth;
+
+ struct timing_generator *tg = stream->tg;
+ struct transform *xfm = stream->xfm;
+
+ if (!xfm->funcs->transform_get_current_pixel_storage_depth(
+ xfm,
+ &current_depth))
+ return false;
+
+ if (current_depth != depth) {
+ if (blank)
+ tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK);
+
+ return xfm->funcs->transform_set_pixel_storage_depth(xfm, depth,
+ &stream->bit_depth_params);
+ }
+
+ return false;
+}
+
+static void hw_sequencer_build_scaler_parameter_plane(
+ const struct core_stream *stream,
+ struct scaler_data *scaler_data)
+{
+ /*TODO: per pipe not per stream*/
+ /*TODO: get from feature from adapterservice*/
+ scaler_data->flags.bits.SHOW_COLOURED_BORDER = false;
+
+ scaler_data->flags.bits.SHOULD_PROGRAM_ALPHA = 1;
+
+ scaler_data->flags.bits.SHOULD_PROGRAM_VIEWPORT = 0;
+
+ scaler_data->flags.bits.SHOULD_UNLOCK = 0;
+
+ scaler_data->flags.bits.INTERLACED = 0;
+
+ scaler_data->dal_pixel_format = stream->format;
+
+ scaler_data->taps = stream->taps;
+
+ scaler_data->viewport = stream->viewport;
+
+ scaler_data->overscan = stream->overscan;
+
+ scaler_data->ratios = &stream->ratios;
+
+ /*TODO rotation and adjustment */
+ scaler_data->h_sharpness = 0;
+ scaler_data->v_sharpness = 0;
+
+}
+
+static void set_default_colors(
+ struct input_pixel_processor *ipp,
+ struct output_pixel_processor *opp,
+ enum pixel_format format,
+ enum color_space input_color_space,
+ enum color_space output_color_space,
+ enum dc_color_depth color_depth)
+{
+ struct default_adjustment default_adjust = { 0 };
+
+ default_adjust.force_hw_default = false;
+ default_adjust.color_space = output_color_space;
+ default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW;
+ default_adjust.surface_pixel_format = format;
+
+ /* display color depth */
+ default_adjust.color_depth = color_depth;
+
+ /* Lb color depth */
+ default_adjust.lb_color_depth = LB_PIXEL_DEPTH_24BPP;
+ /*dal_hw_sequencer_translate_to_lb_color_depth(
+ build_params->
+ line_buffer_params[path_id][plane_id].depth);*/
+
+ opp->funcs->opp_set_csc_default(opp, &default_adjust);
+}
+
+static void program_scaler(
+ uint8_t controller_idx,
+ struct timing_generator *tg,
+ struct transform *xfm,
+ const struct core_surface *surface,
+ const struct core_stream *stream)
+{
+ struct scaler_data scaler_data = { { 0 } };
+
+ hw_sequencer_build_scaler_parameter_plane(
+ stream,
+ &scaler_data);
+
+ setup_line_buffer_pixel_depth(
+ stream,
+ LB_PIXEL_DEPTH_24BPP,
+ false);
+
+ tg->funcs->set_overscan_blank_color(tg, surface->public.colorimetry.color_space);
+
+ xfm->funcs->transform_set_scaler(xfm, &scaler_data);
+
+ xfm->funcs->transform_update_viewport(
+ xfm,
+ &scaler_data.viewport,
+ false);
+}
+
+/**
+ * Program the Front End of the Pipe.
+ * The Back End was already programmed by Set Mode.
+ */
+static bool set_plane_config(
+ const struct dc *dc,
+ struct core_surface *surface,
+ struct core_target *target)
+{
+ const struct core_stream *core_stream =
+ DC_STREAM_TO_CORE(target->public.streams[0]);
+ const struct dc_crtc_timing *dc_crtc_timing =
+ &target->public.streams[0]->timing;
+ struct mem_input *mi = core_stream->mi;
+ struct input_pixel_processor *ipp = core_stream->ipp;
+ struct timing_generator *tg = core_stream->tg;
+ struct transform *xfm = core_stream->xfm;
+ struct output_pixel_processor *opp = core_stream->opp;
+ struct dc_context *ctx = core_stream->ctx;
+ uint8_t controller_idx = core_stream->controller_idx;
+
+ /* TODO: Clean up change, possibly change to use same type */
+ enum color_space input_color_space =
+ surface_color_to_color_space(&(surface->public.colorimetry));
+
+ dc->hwss.pipe_control_lock(
+ ctx,
+ controller_idx,
+ PIPE_LOCK_CONTROL_MODE,
+ false);
+
+ /* While a non-root controller is programmed we
+ * have to lock the root controller. */
+ dc->hwss.pipe_control_lock(
+ ctx,
+ controller_idx,
+ PIPE_LOCK_CONTROL_GRAPHICS |
+ PIPE_LOCK_CONTROL_SCL |
+ PIPE_LOCK_CONTROL_BLENDER |
+ PIPE_LOCK_CONTROL_SURFACE,
+ true);
+
+ tg->funcs->program_timing(tg, dc_crtc_timing, false);
+
+ dc->hwss.enable_fe_clock(ctx, controller_idx, true);
+
+ set_default_colors(
+ ipp,
+ opp,
+ core_stream->format,
+ input_color_space,
+ get_output_color_space(dc_crtc_timing),
+ dc_crtc_timing->display_color_depth);
+
+ /* program Scaler */
+ program_scaler(
+ controller_idx, tg, xfm, surface, core_stream);
+
+ dc->hwss.set_blender_mode(
+ ctx,
+ controller_idx,
+ BLENDER_MODE_CURRENT_PIPE);
+
+ mi->funcs->mem_input_program_surface_config(
+ mi,
+ surface->public.format,
+ &surface->public.tiling_info,
+ &surface->public.plane_size,
+ surface->public.rotation);
+
+ dc->hwss.pipe_control_lock(
+ ctx,
+ controller_idx,
+ PIPE_LOCK_CONTROL_GRAPHICS |
+ PIPE_LOCK_CONTROL_SCL |
+ PIPE_LOCK_CONTROL_BLENDER |
+ PIPE_LOCK_CONTROL_SURFACE,
+ false);
+
+ return true;
+}
+
+static bool update_plane_address(
+ const struct dc *dc,
+ const struct core_surface *surface,
+ struct core_target *target)
+{
+ const struct core_stream *core_stream =
+ DC_STREAM_TO_CORE(target->public.streams[0]);
+ struct dc_context *ctx = core_stream->ctx;
+ struct mem_input *mi = core_stream->mi;
+ uint8_t controller_id = core_stream->controller_idx;
+
+ /* TODO: crtc should be per surface, NOT per-target */
+ dc->hwss.pipe_control_lock(
+ ctx,
+ controller_id,
+ PIPE_LOCK_CONTROL_SURFACE,
+ true);
+
+ if (false ==
+ core_stream->mi->funcs->mem_input_program_surface_flip_and_addr(
+ mi, &surface->public.address, surface->public.flip_immediate))
+ return false;
+
+ dc->hwss.pipe_control_lock(
+ ctx,
+ controller_id,
+ PIPE_LOCK_CONTROL_SURFACE,
+ false);
+
+ return true;
+}
+
+static void reset_single_stream_hw_ctx(
+ const struct dc *dc,
+ struct core_stream *stream,
+ struct validate_context *context)
+{
+ struct dc_bios *dcb;
+
+ dcb = dal_adapter_service_get_bios_parser(
+ context->res_ctx.pool.adapter_srv);
+ if (stream->audio) {
+ dal_audio_disable_output(stream->audio,
+ stream->stream_enc->id,
+ stream->signal);
+ stream->audio = NULL;
+ }
+
+ core_link_disable_stream(stream->sink->link, stream);
+
+ stream->tg->funcs->set_blank(stream->tg, true);
+ stream->tg->funcs->disable_crtc(stream->tg);
+ stream->mi->funcs->mem_input_deallocate_dmif_buffer(
+ stream->mi, context->target_count);
+ stream->xfm->funcs->transform_set_scaler_bypass(stream->xfm);
+ unreference_clock_source(&context->res_ctx, stream->clock_source);
+ dc->hwss.enable_display_power_gating(
+ stream->ctx, stream->controller_idx, dcb,
+ PIPE_GATING_CONTROL_ENABLE);
+}
+
+static void reset_hw_ctx(struct dc *dc,
+ struct validate_context *context,
+ uint8_t target_count)
+{
+ uint8_t i;
+ /* look up the targets that have been removed since last commit */
+ for (i = 0; i < dc->current_context.target_count; i++) {
+ const struct core_target *core_target =
+ dc->current_context.targets[i];
+ struct core_stream *core_stream =
+ DC_STREAM_TO_CORE(core_target->public.streams[0]);
+ uint8_t controller_idx = core_stream->controller_idx;
+
+ if (context->res_ctx.controller_ctx[controller_idx].stream &&
+ !context->res_ctx.controller_ctx[controller_idx]
+ .flags.timing_changed)
+ continue;
+
+ reset_single_stream_hw_ctx(dc, core_stream, &dc->current_context);
+ }
+}
+
+static void power_down(struct dc *dc)
+{
+ power_down_all_hw_blocks(dc);
+ disable_vga_and_power_gate_all_controllers(dc);
+
+}
+
+static bool wait_for_reset_trigger_to_occur(
+ struct dc_context *dc_ctx,
+ struct timing_generator *tg)
+{
+ bool rc = false;
+
+ /* To avoid endless loop we wait at most
+ * frames_to_wait_on_triggered_reset frames for the reset to occur. */
+ const uint32_t frames_to_wait_on_triggered_reset = 10;
+ uint32_t i;
+
+ for (i = 0; i < frames_to_wait_on_triggered_reset; i++) {
+
+ if (!tg->funcs->is_counter_moving(tg)) {
+ DC_ERROR("TG counter is not moving!\n");
+ break;
+ }
+
+ if (tg->funcs->did_triggered_reset_occur(tg)) {
+ rc = true;
+ /* usually occurs at i=1 */
+ DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n",
+ i);
+ break;
+ }
+
+ /* Wait for one frame. */
+ tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE);
+ tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK);
+ }
+
+ if (false == rc)
+ DC_ERROR("GSL: Timeout on reset trigger!\n");
+
+ return rc;
+}
+
+/* Enable timing synchronization for a group of Timing Generators. */
+static void enable_timing_synchronization(
+ struct dc_context *dc_ctx,
+ uint32_t timing_generator_num,
+ struct timing_generator *tgs[])
+{
+ struct dcp_gsl_params gsl_params = { 0 };
+ struct trigger_params trigger_params;
+ uint32_t i;
+
+ DC_SYNC_INFO("GSL: Setting-up...\n");
+
+ gsl_params.gsl_group = SYNC_SOURCE_GSL_GROUP0;
+ gsl_params.gsl_purpose = DCP_GSL_PURPOSE_SURFACE_FLIP;
+
+ for (i = 0; i < timing_generator_num; i++) {
+ /* Designate a single TG in the group as a master.
+ * Since HW doesn't care which one, we always assign
+ * the 1st one in the group. */
+ gsl_params.timing_server = (0 == i ? true : false);
+
+ tgs[i]->funcs->setup_global_swap_lock(tgs[i], &gsl_params);
+ }
+
+ /* Reset slave controllers on master VSync */
+ DC_SYNC_INFO("GSL: enabling trigger-reset\n");
+ dm_memset(&trigger_params, 0, sizeof(trigger_params));
+
+ trigger_params.edge = TRIGGER_EDGE_DEFAULT;
+ trigger_params.source = SYNC_SOURCE_GSL_GROUP0;
+
+ for (i = 1 /* skip the master */; i < timing_generator_num; i++) {
+ tgs[i]->funcs->enable_reset_trigger(tgs[i], &trigger_params);
+
+ DC_SYNC_INFO("GSL: waiting for reset to occur.\n");
+ wait_for_reset_trigger_to_occur(dc_ctx, tgs[i]);
+
+ /* Regardless of success of the wait above, remove the reset or
+ * the driver will start timing out on Display requests. */
+ DC_SYNC_INFO("GSL: disabling trigger-reset.\n");
+ tgs[i]->funcs->disable_reset_trigger(tgs[i]);
+ }
+
+ /* GSL Vblank synchronization is a one time sync mechanism, assumption
+ * is that the sync'ed displays will not drift out of sync over time*/
+ DC_SYNC_INFO("GSL: Restoring register states.\n");
+ for (i = 0; i < timing_generator_num; i++)
+ tgs[i]->funcs->tear_down_global_swap_lock(tgs[i]);
+
+ DC_SYNC_INFO("GSL: Set-up complete.\n");
+}
+
+static const struct hw_sequencer_funcs dce110_funcs = {
+ .apply_ctx_to_hw = apply_ctx_to_hw,
+ .reset_hw_ctx = reset_hw_ctx,
+ .set_plane_config = set_plane_config,
+ .update_plane_address = update_plane_address,
+ .set_gamma_ramp = set_gamma_ramp,
+ .power_down = power_down,
+ .enable_accelerated_mode = enable_accelerated_mode,
+ .enable_timing_synchronization = enable_timing_synchronization,
+ .program_bw = program_bw,
+ .enable_stream = enable_stream,
+ .disable_stream = disable_stream,
+ .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating,
+ .crtc_switch_to_clk_src = dce110_crtc_switch_to_clk_src,
+ .enable_display_power_gating = dce110_enable_display_power_gating,
+ .enable_fe_clock = dce110_enable_fe_clock,
+ .pipe_control_lock = dce110_pipe_control_lock,
+ .set_blender_mode = dce110_set_blender_mode,
+ .clock_gating_power_up = dal_dc_clock_gating_dce110_power_up,/*todo*/
+ .set_display_clock = set_display_clock,
+ .set_displaymarks = set_displaymarks,
+};
+
+bool dce110_hw_sequencer_construct(struct dc *dc)
+{
+ dc->hwss = dce110_funcs;
+
+ return true;
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h
new file mode 100644
index 000000000000..def54df283d5
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h
@@ -0,0 +1,36 @@
+/*
+* 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_HWSS_DCE110_H__
+#define __DC_HWSS_DCE110_H__
+
+#include "core_types.h"
+
+struct dc;
+
+bool dce110_hw_sequencer_construct(struct dc *dc);
+
+#endif /* __DC_HWSS_DCE110_H__ */
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c
new file mode 100644
index 000000000000..9e2b5d95a521
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c
@@ -0,0 +1,1238 @@
+/*
+* 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
+ *
+ */
+
+#include "dm_services.h"
+
+#include "link_encoder.h"
+#include "stream_encoder.h"
+
+#include "resource.h"
+#include "include/irq_service_interface.h"
+#include "../virtual/virtual_stream_encoder.h"
+#include "dce110/dce110_timing_generator.h"
+#include "dce110/dce110_link_encoder.h"
+#include "dce110/dce110_mem_input.h"
+#include "dce110/dce110_ipp.h"
+#include "dce110/dce110_transform.h"
+#include "dce110/dce110_stream_encoder.h"
+#include "dce110/dce110_opp.h"
+#include "dce110/dce110_clock_source.h"
+
+#include "dce/dce_11_0_d.h"
+
+#ifndef mmDP_DPHY_INTERNAL_CTRL
+ #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7
+ #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7
+ #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7
+ #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7
+ #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7
+ #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7
+ #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7
+ #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7
+ #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7
+ #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7
+#endif
+
+enum dce110_clk_src_array_id {
+ DCE110_CLK_SRC_PLL0 = 0,
+ DCE110_CLK_SRC_PLL1,
+ DCE110_CLK_SRC_EXT,
+
+ DCE110_CLK_SRC_TOTAL
+};
+
+static const struct dce110_timing_generator_offsets dce110_tg_offsets[] = {
+ {
+ .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL),
+ },
+ {
+ .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL),
+ },
+ {
+ .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL),
+ },
+ {
+ .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL),
+ },
+ {
+ .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL),
+ },
+ {
+ .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL),
+ .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL),
+ }
+};
+
+static const struct dce110_mem_input_reg_offsets dce110_mi_reg_offsets[] = {
+ {
+ .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL),
+ .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL
+ - mmDPG_WATERMARK_MASK_CONTROL),
+ .pipe = (mmPIPE0_DMIF_BUFFER_CONTROL
+ - mmPIPE0_DMIF_BUFFER_CONTROL),
+ },
+ {
+ .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL),
+ .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL
+ - mmDPG_WATERMARK_MASK_CONTROL),
+ .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL
+ - mmPIPE0_DMIF_BUFFER_CONTROL),
+ },
+ {
+ .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL),
+ .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL
+ - mmDPG_WATERMARK_MASK_CONTROL),
+ .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL
+ - mmPIPE0_DMIF_BUFFER_CONTROL),
+ }
+};
+
+static const struct dce110_transform_reg_offsets dce110_xfm_offsets[] = {
+{
+ .scl_offset = (mmSCL0_SCL_CONTROL - mmSCL_CONTROL),
+ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL),
+ .lb_offset = (mmLB0_LB_DATA_FORMAT - mmLB_DATA_FORMAT),
+},
+{ .scl_offset = (mmSCL1_SCL_CONTROL - mmSCL_CONTROL),
+ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL),
+ .lb_offset = (mmLB1_LB_DATA_FORMAT - mmLB_DATA_FORMAT),
+},
+{ .scl_offset = (mmSCL2_SCL_CONTROL - mmSCL_CONTROL),
+ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL),
+ .lb_offset = (mmLB2_LB_DATA_FORMAT - mmLB_DATA_FORMAT),
+}
+};
+
+static const struct dce110_ipp_reg_offsets dce110_ipp_reg_offsets[] = {
+{
+ .dcp_offset = (mmDCP0_CUR_CONTROL - mmCUR_CONTROL),
+},
+{
+ .dcp_offset = (mmDCP1_CUR_CONTROL - mmCUR_CONTROL),
+},
+{
+ .dcp_offset = (mmDCP2_CUR_CONTROL - mmCUR_CONTROL),
+},
+{
+ .dcp_offset = (mmDCP3_CUR_CONTROL - mmCUR_CONTROL),
+},
+{
+ .dcp_offset = (mmDCP4_CUR_CONTROL - mmCUR_CONTROL),
+},
+{
+ .dcp_offset = (mmDCP5_CUR_CONTROL - mmCUR_CONTROL),
+}
+};
+
+static const struct dce110_link_enc_bl_registers link_enc_bl_regs = {
+ .BL_PWM_CNTL = mmBL_PWM_CNTL,
+ .BL_PWM_GRP1_REG_LOCK = mmBL_PWM_GRP1_REG_LOCK,
+ .BL_PWM_PERIOD_CNTL = mmBL_PWM_PERIOD_CNTL,
+ .LVTMA_PWRSEQ_CNTL = mmLVTMA_PWRSEQ_CNTL,
+ .LVTMA_PWRSEQ_STATE = mmLVTMA_PWRSEQ_STATE
+};
+
+#define aux_regs(id)\
+[id] = {\
+ .AUX_CONTROL = mmDP_AUX ## id ## _AUX_CONTROL,\
+ .AUX_DPHY_RX_CONTROL0 = mmDP_AUX ## id ## _AUX_DPHY_RX_CONTROL0\
+}
+
+static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = {
+ aux_regs(0),
+ aux_regs(1),
+ aux_regs(2),
+ aux_regs(3),
+ aux_regs(4),
+ aux_regs(5)
+};
+
+#define link_regs(id)\
+[id] = {\
+ .DIG_BE_CNTL = mmDIG ## id ## _DIG_BE_CNTL,\
+ .DIG_BE_EN_CNTL = mmDIG ## id ## _DIG_BE_EN_CNTL,\
+ .DP_CONFIG = mmDP ## id ## _DP_CONFIG,\
+ .DP_DPHY_CNTL = mmDP ## id ## _DP_DPHY_CNTL,\
+ .DP_DPHY_INTERNAL_CTRL = mmDP ## id ## _DP_DPHY_INTERNAL_CTRL,\
+ .DP_DPHY_PRBS_CNTL = mmDP ## id ## _DP_DPHY_PRBS_CNTL,\
+ .DP_DPHY_SYM0 = mmDP ## id ## _DP_DPHY_SYM0,\
+ .DP_DPHY_SYM1 = mmDP ## id ## _DP_DPHY_SYM1,\
+ .DP_DPHY_SYM2 = mmDP ## id ## _DP_DPHY_SYM2,\
+ .DP_DPHY_TRAINING_PATTERN_SEL = mmDP ## id ## _DP_DPHY_TRAINING_PATTERN_SEL,\
+ .DP_LINK_CNTL = mmDP ## id ## _DP_LINK_CNTL,\
+ .DP_LINK_FRAMING_CNTL = mmDP ## id ## _DP_LINK_FRAMING_CNTL,\
+ .DP_MSE_SAT0 = mmDP ## id ## _DP_MSE_SAT0,\
+ .DP_MSE_SAT1 = mmDP ## id ## _DP_MSE_SAT1,\
+ .DP_MSE_SAT2 = mmDP ## id ## _DP_MSE_SAT2,\
+ .DP_MSE_SAT_UPDATE = mmDP ## id ## _DP_MSE_SAT_UPDATE,\
+ .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\
+ .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL\
+}
+
+static const struct dce110_link_enc_registers link_enc_regs[] = {
+ link_regs(0),
+ link_regs(1),
+ link_regs(2),
+ link_regs(3),
+ link_regs(4),
+ link_regs(5),
+ link_regs(6)
+};
+
+#define stream_enc_regs(id)\
+[id] = {\
+ .AFMT_AVI_INFO0 = mmDIG ## id ## _AFMT_AVI_INFO0,\
+ .AFMT_AVI_INFO1 = mmDIG ## id ## _AFMT_AVI_INFO1,\
+ .AFMT_AVI_INFO2 = mmDIG ## id ## _AFMT_AVI_INFO2,\
+ .AFMT_AVI_INFO3 = mmDIG ## id ## _AFMT_AVI_INFO3,\
+ .AFMT_GENERIC_0 = mmDIG ## id ## _AFMT_GENERIC_0,\
+ .AFMT_GENERIC_7 = mmDIG ## id ## _AFMT_GENERIC_7,\
+ .AFMT_GENERIC_HDR = mmDIG ## id ## _AFMT_GENERIC_HDR,\
+ .AFMT_INFOFRAME_CONTROL0 = mmDIG ## id ## _AFMT_INFOFRAME_CONTROL0,\
+ .AFMT_VBI_PACKET_CONTROL = mmDIG ## id ## _AFMT_VBI_PACKET_CONTROL,\
+ .DIG_FE_CNTL = mmDIG ## id ## _DIG_FE_CNTL,\
+ .DP_MSE_RATE_CNTL = mmDP ## id ## _DP_MSE_RATE_CNTL,\
+ .DP_MSE_RATE_UPDATE = mmDP ## id ## _DP_MSE_RATE_UPDATE,\
+ .DP_PIXEL_FORMAT = mmDP ## id ## _DP_PIXEL_FORMAT,\
+ .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\
+ .DP_STEER_FIFO = mmDP ## id ## _DP_STEER_FIFO,\
+ .DP_VID_M = mmDP ## id ## _DP_VID_M,\
+ .DP_VID_N = mmDP ## id ## _DP_VID_N,\
+ .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL,\
+ .DP_VID_TIMING = mmDP ## id ## _DP_VID_TIMING,\
+ .HDMI_CONTROL = mmDIG ## id ## _HDMI_CONTROL,\
+ .HDMI_GC = mmDIG ## id ## _HDMI_GC,\
+ .HDMI_GENERIC_PACKET_CONTROL0 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL0,\
+ .HDMI_GENERIC_PACKET_CONTROL1 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL1,\
+ .HDMI_INFOFRAME_CONTROL0 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL0,\
+ .HDMI_INFOFRAME_CONTROL1 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL1,\
+ .HDMI_VBI_PACKET_CONTROL = mmDIG ## id ## _HDMI_VBI_PACKET_CONTROL,\
+ .TMDS_CNTL = mmDIG ## id ## _TMDS_CNTL\
+}
+
+static const struct dce110_stream_enc_registers stream_enc_regs[] = {
+ stream_enc_regs(0),
+ stream_enc_regs(1),
+ stream_enc_regs(2),
+ stream_enc_regs(3),
+ stream_enc_regs(4),
+ stream_enc_regs(5),
+ stream_enc_regs(6)
+};
+
+
+/* AG TBD Needs to be reduced back to 3 pipes once dce10 hw sequencer implemented. */
+static const struct dce110_opp_reg_offsets dce110_opp_reg_offsets[] = {
+{
+ .fmt_offset = (mmFMT0_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+},
+{ .fmt_offset = (mmFMT1_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+},
+{ .fmt_offset = (mmFMT2_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+},
+{
+ .fmt_offset = (mmFMT3_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE3_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+},
+{ .fmt_offset = (mmFMT4_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE4_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+},
+{ .fmt_offset = (mmFMT5_FMT_CONTROL - mmFMT0_FMT_CONTROL),
+ .dcfe_offset = (mmDCFE5_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL),
+ .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL),
+}
+};
+
+
+static const struct dce110_clk_src_reg_offsets dce110_clk_src_reg_offsets[] = {
+ {
+ .pll_cntl = mmBPHYC_PLL0_PLL_CNTL,
+ .pixclk_resync_cntl = mmPIXCLK0_RESYNC_CNTL
+ },
+ {
+ .pll_cntl = mmBPHYC_PLL1_PLL_CNTL,
+ .pixclk_resync_cntl = mmPIXCLK1_RESYNC_CNTL
+ }
+};
+
+static struct timing_generator *dce110_timing_generator_create(
+ struct adapter_service *as,
+ struct dc_context *ctx,
+ uint32_t instance,
+ const struct dce110_timing_generator_offsets *offsets)
+{
+ struct dce110_timing_generator *tg110 =
+ dm_alloc(ctx, sizeof(struct dce110_timing_generator));
+
+ if (!tg110)
+ return NULL;
+
+ if (dce110_timing_generator_construct(tg110, as, ctx, instance, offsets))
+ return &tg110->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, tg110);
+ return NULL;
+}
+
+static struct stream_encoder *dce110_stream_encoder_create(
+ enum engine_id eng_id,
+ struct dc_context *ctx,
+ struct dc_bios *bp,
+ const struct dce110_stream_enc_registers *regs)
+{
+ struct dce110_stream_encoder *enc110 =
+ dm_alloc(ctx, sizeof(struct dce110_stream_encoder));
+
+ if (!enc110)
+ return NULL;
+
+ if (dce110_stream_encoder_construct(enc110, ctx, bp, eng_id, regs))
+ return &enc110->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, enc110);
+ return NULL;
+}
+
+static struct mem_input *dce110_mem_input_create(
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_mem_input_reg_offsets *offset)
+{
+ struct dce110_mem_input *mem_input110 =
+ dm_alloc(ctx, sizeof(struct dce110_mem_input));
+
+ if (!mem_input110)
+ return NULL;
+
+ if (dce110_mem_input_construct(mem_input110,
+ ctx, inst, offset))
+ return &mem_input110->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, mem_input110);
+ return NULL;
+}
+
+static void dce110_transform_destroy(struct transform **xfm)
+{
+ dm_free((*xfm)->ctx, TO_DCE110_TRANSFORM(*xfm));
+ *xfm = NULL;
+}
+
+static struct transform *dce110_transform_create(
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_transform_reg_offsets *offsets)
+{
+ struct dce110_transform *transform =
+ dm_alloc(ctx, sizeof(struct dce110_transform));
+
+ if (!transform)
+ return NULL;
+
+ if (dce110_transform_construct(transform, ctx, inst, offsets))
+ return &transform->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, transform);
+ return NULL;
+}
+
+static struct input_pixel_processor *dce110_ipp_create(
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_ipp_reg_offsets *offsets)
+{
+ struct dce110_ipp *ipp =
+ dm_alloc(ctx, sizeof(struct dce110_ipp));
+
+ if (!ipp)
+ return NULL;
+
+ if (dce110_ipp_construct(ipp, ctx, inst, offsets))
+ return &ipp->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, ipp);
+ return NULL;
+}
+
+struct link_encoder *dce110_link_encoder_create(
+ const struct encoder_init_data *enc_init_data)
+{
+ struct dce110_link_encoder *enc110 =
+ dm_alloc(
+ enc_init_data->ctx,
+ sizeof(struct dce110_link_encoder));
+
+ if (!enc110)
+ return NULL;
+
+ if (dce110_link_encoder_construct(
+ enc110,
+ enc_init_data,
+ &link_enc_regs[enc_init_data->transmitter],
+ &link_enc_aux_regs[enc_init_data->channel - 1],
+ &link_enc_bl_regs))
+ return &enc110->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(enc_init_data->ctx, enc110);
+ return NULL;
+}
+
+void dce110_link_encoder_destroy(struct link_encoder **enc)
+{
+ dm_free((*enc)->ctx, TO_DCE110_LINK_ENC(*enc));
+ *enc = NULL;
+}
+
+
+static struct output_pixel_processor *dce110_opp_create(
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_opp_reg_offsets *offsets)
+{
+ struct dce110_opp *opp =
+ dm_alloc(ctx, sizeof(struct dce110_opp));
+
+ if (!opp)
+ return NULL;
+
+ if (dce110_opp_construct(opp,
+ ctx, inst, offsets))
+ return &opp->base;
+
+ BREAK_TO_DEBUGGER();
+ dm_free(ctx, opp);
+ return NULL;
+}
+
+struct clock_source *dce110_clock_source_create(
+ struct dc_context *ctx,
+ struct dc_bios *bios,
+ enum clock_source_id id,
+ const struct dce110_clk_src_reg_offsets *offsets)
+{
+ struct dce110_clk_src *clk_src =
+ dm_alloc(ctx, sizeof(struct dce110_clk_src));
+
+ if (!clk_src)
+ return NULL;
+
+ if (dce110_clk_src_construct(clk_src, ctx, bios, id, offsets))
+ return &clk_src->base;
+
+ BREAK_TO_DEBUGGER();
+ return NULL;
+}
+
+void dce110_clock_source_destroy(struct clock_source **clk_src)
+{
+ dm_free((*clk_src)->ctx, TO_DCE110_CLK_SRC(*clk_src));
+ *clk_src = NULL;
+}
+
+void dce110_destruct_resource_pool(struct resource_pool *pool)
+{
+ unsigned int i;
+
+ for (i = 0; i < pool->controller_count; i++) {
+ if (pool->opps[i] != NULL)
+ dce110_opp_destroy(&pool->opps[i]);
+
+ if (pool->transforms[i] != NULL)
+ dce110_transform_destroy(&pool->transforms[i]);
+
+ if (pool->ipps[i] != NULL)
+ dce110_ipp_destroy(&pool->ipps[i]);
+
+ if (pool->mis[i] != NULL) {
+ dm_free(pool->mis[i]->ctx,
+ TO_DCE110_MEM_INPUT(pool->mis[i]));
+ pool->mis[i] = NULL;
+ }
+
+ if (pool->timing_generators[i] != NULL) {
+ dm_free(pool->timing_generators[i]->ctx, DCE110TG_FROM_TG(pool->timing_generators[i]));
+ pool->timing_generators[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < pool->stream_enc_count; i++) {
+ if (pool->stream_enc[i] != NULL)
+ dm_free(pool->stream_enc[i]->ctx,
+ DCE110STRENC_FROM_STRENC(pool->stream_enc[i]));
+ }
+
+ for (i = 0; i < pool->clk_src_count; i++) {
+ if (pool->clock_sources[i] != NULL) {
+ dce110_clock_source_destroy(&pool->clock_sources[i]);
+ }
+ }
+
+ for (i = 0; i < pool->audio_count; i++) {
+ if (pool->audios[i] != NULL) {
+ dal_audio_destroy(&pool->audios[i]);
+ }
+ }
+
+ if (pool->display_clock != NULL) {
+ dal_display_clock_destroy(&pool->display_clock);
+ }
+
+ if (pool->scaler_filter != NULL) {
+ dal_scaler_filter_destroy(&pool->scaler_filter);
+ }
+ if (pool->irqs != NULL) {
+ dal_irq_service_destroy(&pool->irqs);
+ }
+
+ if (pool->adapter_srv != NULL) {
+ dal_adapter_service_destroy(&pool->adapter_srv);
+ }
+}
+
+static struct clock_source *find_first_free_pll(
+ struct resource_context *res_ctx)
+{
+ if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL0] == 0) {
+ return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL0];
+ }
+ if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL1] == 0) {
+ return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL1];
+ }
+
+ return 0;
+}
+
+static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id)
+{
+ switch (crtc_id) {
+ case CONTROLLER_ID_D0:
+ return DTO_SOURCE_ID0;
+ case CONTROLLER_ID_D1:
+ return DTO_SOURCE_ID1;
+ case CONTROLLER_ID_D2:
+ return DTO_SOURCE_ID2;
+ case CONTROLLER_ID_D3:
+ return DTO_SOURCE_ID3;
+ case CONTROLLER_ID_D4:
+ return DTO_SOURCE_ID4;
+ case CONTROLLER_ID_D5:
+ return DTO_SOURCE_ID5;
+ default:
+ return DTO_SOURCE_UNKNOWN;
+ }
+}
+
+static void build_audio_output(
+ const struct core_stream *stream,
+ struct audio_output *audio_output)
+{
+ audio_output->engine_id = stream->stream_enc->id;
+
+ audio_output->signal = stream->signal;
+
+ /* audio_crtc_info */
+
+ audio_output->crtc_info.h_total =
+ stream->public.timing.h_total;
+
+ /* Audio packets are sent during actual CRTC blank physical signal, we
+ * need to specify actual active signal portion */
+ audio_output->crtc_info.h_active =
+ stream->public.timing.h_addressable
+ + stream->public.timing.h_border_left
+ + stream->public.timing.h_border_right;
+
+ audio_output->crtc_info.v_active =
+ stream->public.timing.v_addressable
+ + stream->public.timing.v_border_top
+ + stream->public.timing.v_border_bottom;
+
+ audio_output->crtc_info.pixel_repetition = 1;
+
+ audio_output->crtc_info.interlaced =
+ stream->public.timing.flags.INTERLACE;
+
+ audio_output->crtc_info.refresh_rate =
+ (stream->public.timing.pix_clk_khz*1000)/
+ (stream->public.timing.h_total*stream->public.timing.v_total);
+
+ audio_output->crtc_info.color_depth =
+ stream->public.timing.display_color_depth;
+
+ audio_output->crtc_info.requested_pixel_clock =
+ stream->pix_clk_params.requested_pix_clk;
+
+ /* TODO - Investigate why calculated pixel clk has to be
+ * requested pixel clk */
+ audio_output->crtc_info.calculated_pixel_clock =
+ stream->pix_clk_params.requested_pix_clk;
+
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+ audio_output->pll_info.dp_dto_source_clock_in_khz =
+ dal_display_clock_get_dp_ref_clk_frequency(
+ stream->dis_clk);
+ }
+
+ audio_output->pll_info.feed_back_divider =
+ stream->pll_settings.feedback_divider;
+
+ audio_output->pll_info.dto_source =
+ translate_to_dto_source(
+ stream->controller_idx + 1);
+
+ /* TODO hard code to enable for now. Need get from stream */
+ audio_output->pll_info.ss_enabled = true;
+
+ audio_output->pll_info.ss_percentage =
+ stream->pll_settings.ss_percentage;
+}
+
+static void get_pixel_clock_parameters(
+ const struct core_stream *stream,
+ struct pixel_clk_params *pixel_clk_params)
+{
+ pixel_clk_params->requested_pix_clk = stream->public.timing.pix_clk_khz;
+ pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id;
+ pixel_clk_params->signal_type = stream->sink->public.sink_signal;
+ pixel_clk_params->controller_id = stream->controller_idx + 1;
+ /* TODO: un-hardcode*/
+ pixel_clk_params->requested_sym_clk = LINK_RATE_LOW *
+ LINK_RATE_REF_FREQ_IN_KHZ;
+ pixel_clk_params->flags.ENABLE_SS = 0;
+ pixel_clk_params->color_depth =
+ stream->public.timing.display_color_depth;
+ pixel_clk_params->flags.DISPLAY_BLANKED = 1;
+}
+
+static enum dc_status build_stream_hw_param(struct core_stream *stream)
+{
+ /*TODO: unhardcode*/
+ stream->max_tmds_clk_from_edid_in_mhz = 0;
+ stream->max_hdmi_deep_color = COLOR_DEPTH_121212;
+ stream->max_hdmi_pixel_clock = 600000;
+
+ get_pixel_clock_parameters(stream, &stream->pix_clk_params);
+ stream->clock_source->funcs->get_pix_clk_dividers(
+ stream->clock_source,
+ &stream->pix_clk_params,
+ &stream->pll_settings);
+
+ build_audio_output(stream, &stream->audio_output);
+
+ return DC_OK;
+}
+
+static enum dc_status validate_mapped_resource(
+ const struct dc *dc,
+ struct validate_context *context)
+{
+ enum dc_status status = DC_OK;
+ uint8_t i, j;
+
+ for (i = 0; i < context->target_count; i++) {
+ struct core_target *target = context->targets[i];
+ if (context->target_flags[i].unchanged)
+ continue;
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+ struct core_link *link = stream->sink->link;
+
+ if (!stream->tg->funcs->validate_timing(
+ stream->tg, &stream->public.timing))
+ return DC_FAIL_CONTROLLER_VALIDATE;
+
+ status = build_stream_hw_param(stream);
+
+ if (status != DC_OK)
+ return status;
+
+ if (!link->link_enc->funcs->validate_output_with_stream(
+ link->link_enc,
+ stream))
+ return DC_FAIL_ENC_VALIDATE;
+
+ /* TODO: validate audio ASIC caps, encoder */
+
+ status = dc_link_validate_mode_timing(stream->sink,
+ link,
+ &stream->public.timing);
+
+ if (status != DC_OK)
+ return status;
+
+ build_info_frame(stream);
+ }
+ }
+
+ return DC_OK;
+}
+
+enum dc_status dce110_validate_bandwidth(
+ const struct dc *dc,
+ struct validate_context *context)
+{
+ uint8_t i, j;
+ enum dc_status result = DC_ERROR_UNEXPECTED;
+ uint8_t number_of_displays = 0;
+ uint8_t max_htaps = 1;
+ uint8_t max_vtaps = 1;
+ bool all_displays_in_sync = true;
+ struct dc_crtc_timing prev_timing;
+
+ memset(&context->bw_mode_data, 0, sizeof(context->bw_mode_data));
+
+ for (i = 0; i < context->target_count; i++) {
+ struct core_target *target = context->targets[i];
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+ struct bw_calcs_input_single_display *disp = &context->
+ bw_mode_data.displays_data[number_of_displays];
+
+ if (target->status.surface_count == 0) {
+ disp->graphics_scale_ratio = bw_int_to_fixed(1);
+ disp->graphics_h_taps = 2;
+ disp->graphics_v_taps = 2;
+
+ /* TODO: remove when bw formula accepts taps per
+ * display
+ */
+ if (max_vtaps < 2)
+ max_vtaps = 2;
+ if (max_htaps < 2)
+ max_htaps = 2;
+
+ } else {
+ disp->graphics_scale_ratio =
+ fixed31_32_to_bw_fixed(
+ stream->ratios.vert.value);
+ disp->graphics_h_taps = stream->taps.h_taps;
+ disp->graphics_v_taps = stream->taps.v_taps;
+
+ /* TODO: remove when bw formula accepts taps per
+ * display
+ */
+ if (max_vtaps < stream->taps.v_taps)
+ max_vtaps = stream->taps.v_taps;
+ if (max_htaps < stream->taps.h_taps)
+ max_htaps = stream->taps.h_taps;
+ }
+
+ disp->graphics_src_width =
+ stream->public.timing.h_addressable;
+ disp->graphics_src_height =
+ stream->public.timing.v_addressable;
+ disp->h_total = stream->public.timing.h_total;
+ disp->pixel_rate = bw_frc_to_fixed(
+ stream->public.timing.pix_clk_khz, 1000);
+
+ /*TODO: get from surface*/
+ disp->graphics_bytes_per_pixel = 4;
+ disp->graphics_tiling_mode = bw_def_tiled;
+
+ /* DCE11 defaults*/
+ disp->graphics_lb_bpc = 10;
+ disp->graphics_interlace_mode = false;
+ disp->fbc_enable = false;
+ disp->lpt_enable = false;
+ disp->graphics_stereo_mode = bw_def_mono;
+ disp->underlay_mode = bw_def_none;
+
+ /*All displays will be synchronized if timings are all
+ * the same
+ */
+ if (number_of_displays != 0 && all_displays_in_sync)
+ if (dm_memcmp(&prev_timing,
+ &stream->public.timing,
+ sizeof(struct dc_crtc_timing))!= 0)
+ all_displays_in_sync = false;
+ if (number_of_displays == 0)
+ prev_timing = stream->public.timing;
+
+ number_of_displays++;
+ }
+ }
+
+ /* TODO: remove when bw formula accepts taps per
+ * display
+ */
+ context->bw_mode_data.displays_data[0].graphics_v_taps = max_vtaps;
+ context->bw_mode_data.displays_data[0].graphics_h_taps = max_htaps;
+
+ context->bw_mode_data.number_of_displays = number_of_displays;
+ context->bw_mode_data.display_synchronization_enabled =
+ all_displays_in_sync;
+
+ dal_logger_write(
+ dc->ctx->logger,
+ LOG_MAJOR_BWM,
+ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS,
+ "%s: start\n",
+ __func__);
+
+ if (!bw_calcs(
+ dc->ctx,
+ &dc->bw_dceip,
+ &dc->bw_vbios,
+ &context->bw_mode_data,
+ &context->bw_results))
+ result = DC_FAIL_BANDWIDTH_VALIDATE;
+ else
+ result = DC_OK;
+
+ if (result == DC_FAIL_BANDWIDTH_VALIDATE)
+ dal_logger_write(dc->ctx->logger,
+ LOG_MAJOR_BWM,
+ LOG_MINOR_BWM_MODE_VALIDATION,
+ "%s: Bandwidth validation failed!",
+ __func__);
+
+ if (dm_memcmp(&dc->current_context.bw_results,
+ &context->bw_results, sizeof(context->bw_results))) {
+ struct log_entry log_entry;
+ dal_logger_open(
+ dc->ctx->logger,
+ &log_entry,
+ LOG_MAJOR_BWM,
+ LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS);
+ dal_logger_append(&log_entry, "%s: finish, numDisplays: %d\n"
+ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n"
+ "stutMark_b: %d stutMark_a: %d\n",
+ __func__, number_of_displays,
+ context->bw_results.nbp_state_change_wm_ns[0].b_mark,
+ context->bw_results.nbp_state_change_wm_ns[0].a_mark,
+ context->bw_results.urgent_wm_ns[0].b_mark,
+ context->bw_results.urgent_wm_ns[0].a_mark,
+ context->bw_results.stutter_exit_wm_ns[0].b_mark,
+ context->bw_results.stutter_exit_wm_ns[0].a_mark);
+ dal_logger_append(&log_entry,
+ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n"
+ "stutMark_b: %d stutMark_a: %d\n",
+ context->bw_results.nbp_state_change_wm_ns[1].b_mark,
+ context->bw_results.nbp_state_change_wm_ns[1].a_mark,
+ context->bw_results.urgent_wm_ns[1].b_mark,
+ context->bw_results.urgent_wm_ns[1].a_mark,
+ context->bw_results.stutter_exit_wm_ns[1].b_mark,
+ context->bw_results.stutter_exit_wm_ns[1].a_mark);
+ dal_logger_append(&log_entry,
+ "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n"
+ "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n",
+ context->bw_results.nbp_state_change_wm_ns[2].b_mark,
+ context->bw_results.nbp_state_change_wm_ns[2].a_mark,
+ context->bw_results.urgent_wm_ns[2].b_mark,
+ context->bw_results.urgent_wm_ns[2].a_mark,
+ context->bw_results.stutter_exit_wm_ns[2].b_mark,
+ context->bw_results.stutter_exit_wm_ns[2].a_mark,
+ context->bw_results.stutter_mode_enable);
+ dal_logger_append(&log_entry,
+ "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n"
+ "sclk: %d sclk_sleep: %d yclk: %d blackout_duration: %d\n",
+ context->bw_results.cpuc_state_change_enable,
+ context->bw_results.cpup_state_change_enable,
+ context->bw_results.nbp_state_change_enable,
+ context->bw_results.all_displays_in_sync,
+ context->bw_results.dispclk_khz,
+ context->bw_results.required_sclk,
+ context->bw_results.required_sclk_deep_sleep,
+ context->bw_results.required_yclk,
+ context->bw_results.required_blackout_duration_us);
+ dal_logger_close(&log_entry);
+ }
+ return result;
+}
+
+static void set_target_unchanged(
+ struct validate_context *context,
+ uint8_t target_idx)
+{
+ uint8_t i;
+ struct core_target *target = context->targets[target_idx];
+ context->target_flags[target_idx].unchanged = true;
+ for (i = 0; i < target->public.stream_count; i++) {
+ struct core_stream *core_stream =
+ DC_STREAM_TO_CORE(target->public.streams[i]);
+ uint8_t index = core_stream->controller_idx;
+ context->res_ctx.controller_ctx[index].flags.unchanged = true;
+ }
+}
+
+static enum dc_status map_clock_resources(
+ const struct dc *dc,
+ struct validate_context *context)
+{
+ uint8_t i, j;
+
+ /* mark resources used for targets that are already active */
+ for (i = 0; i < context->target_count; i++) {
+ struct core_target *target = context->targets[i];
+
+ if (!context->target_flags[i].unchanged)
+ continue;
+
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+
+ reference_clock_source(
+ &context->res_ctx,
+ stream->clock_source);
+ }
+ }
+
+ /* acquire new resources */
+ for (i = 0; i < context->target_count; i++) {
+ struct core_target *target = context->targets[i];
+
+ if (context->target_flags[i].unchanged)
+ continue;
+
+ for (j = 0; j < target->public.stream_count; j++) {
+ struct core_stream *stream =
+ DC_STREAM_TO_CORE(target->public.streams[j]);
+
+ if (dc_is_dp_signal(stream->signal)
+ || stream->signal == SIGNAL_TYPE_VIRTUAL)
+ stream->clock_source = context->res_ctx.
+ pool.clock_sources[DCE110_CLK_SRC_EXT];
+ else
+ stream->clock_source =
+ find_used_clk_src_for_sharing(
+ context, stream);
+ if (stream->clock_source == NULL)
+ stream->clock_source =
+ find_first_free_pll(&context->res_ctx);
+
+ if (stream->clock_source == NULL)
+ return DC_NO_CLOCK_SOURCE_RESOURCE;
+
+ reference_clock_source(
+ &context->res_ctx,
+ stream->clock_source);
+ }
+ }
+
+ return DC_OK;
+}
+
+enum dc_status dce110_validate_with_context(
+ const struct dc *dc,
+ const struct dc_validation_set set[],
+ uint8_t set_count,
+ struct validate_context *context)
+{
+ enum dc_status result = DC_ERROR_UNEXPECTED;
+ uint8_t i, j;
+ struct dc_context *dc_ctx = dc->ctx;
+
+ for (i = 0; i < set_count; i++) {
+ context->targets[i] = DC_TARGET_TO_CORE(set[i].target);
+
+ for (j = 0; j < dc->current_context.target_count; j++)
+ if (dc->current_context.targets[j] == context->targets[i])
+ set_target_unchanged(context, i);
+
+ if (!context->target_flags[i].unchanged)
+ if (!logical_attach_surfaces_to_target(
+ (struct dc_surface **)set[i].surfaces,
+ set[i].surface_count,
+ &context->targets[i]->public)) {
+ DC_ERROR("Failed to attach surface to target!\n");
+ return DC_FAIL_ATTACH_SURFACES;
+ }
+ }
+
+ context->target_count = set_count;
+
+ context->res_ctx.pool = dc->res_pool;
+
+ result = map_resources(dc, context);
+
+ if (result == DC_OK)
+ result = map_clock_resources(dc, context);
+
+ if (result == DC_OK)
+ result = validate_mapped_resource(dc, context);
+
+ if (result == DC_OK)
+ build_scaling_params_for_context(dc, context);
+
+ if (result == DC_OK)
+ result = dce110_validate_bandwidth(dc, context);
+
+ return result;
+}
+
+static struct resource_funcs dce110_res_pool_funcs = {
+ .destruct = dce110_destruct_resource_pool,
+ .link_enc_create = dce110_link_encoder_create,
+ .link_enc_destroy = dce110_link_encoder_destroy,
+ .validate_with_context = dce110_validate_with_context,
+ .validate_bandwidth = dce110_validate_bandwidth
+};
+
+bool dce110_construct_resource_pool(
+ struct adapter_service *adapter_serv,
+ uint8_t num_virtual_links,
+ struct dc *dc,
+ struct resource_pool *pool)
+{
+ unsigned int i;
+ struct audio_init_data audio_init_data = { 0 };
+ struct dc_context *ctx = dc->ctx;
+ pool->adapter_srv = adapter_serv;
+ pool->funcs = &dce110_res_pool_funcs;
+
+ pool->stream_engines.engine.ENGINE_ID_DIGA = 1;
+ pool->stream_engines.engine.ENGINE_ID_DIGB = 1;
+ pool->stream_engines.engine.ENGINE_ID_DIGC = 1;
+ pool->stream_engines.engine.ENGINE_ID_DIGD = 1;
+ pool->stream_engines.engine.ENGINE_ID_DIGE = 1;
+ pool->stream_engines.engine.ENGINE_ID_DIGF = 1;
+
+ pool->clock_sources[DCE110_CLK_SRC_PLL0] = dce110_clock_source_create(
+ ctx, dal_adapter_service_get_bios_parser(adapter_serv),
+ CLOCK_SOURCE_ID_PLL0, &dce110_clk_src_reg_offsets[0]);
+ pool->clock_sources[DCE110_CLK_SRC_PLL1] = dce110_clock_source_create(
+ ctx, dal_adapter_service_get_bios_parser(adapter_serv),
+ CLOCK_SOURCE_ID_PLL1, &dce110_clk_src_reg_offsets[1]);
+ pool->clock_sources[DCE110_CLK_SRC_EXT] = dce110_clock_source_create(
+ ctx, dal_adapter_service_get_bios_parser(adapter_serv),
+ CLOCK_SOURCE_ID_EXTERNAL, &dce110_clk_src_reg_offsets[0]);
+ pool->clk_src_count = DCE110_CLK_SRC_TOTAL;
+
+ for (i = 0; i < pool->clk_src_count; i++) {
+ if (pool->clock_sources[i] == NULL) {
+ dm_error("DC: failed to create clock sources!\n");
+ BREAK_TO_DEBUGGER();
+ goto clk_src_create_fail;
+ }
+ }
+
+ pool->display_clock = dal_display_clock_dce110_create(ctx, adapter_serv);
+ if (pool->display_clock == NULL) {
+ dm_error("DC: failed to create display clock!\n");
+ BREAK_TO_DEBUGGER();
+ goto disp_clk_create_fail;
+ }
+
+ {
+ struct irq_service_init_data init_data;
+ init_data.ctx = dc->ctx;
+ pool->irqs = dal_irq_service_create(
+ dal_adapter_service_get_dce_version(
+ dc->res_pool.adapter_srv),
+ &init_data);
+ if (!pool->irqs)
+ goto irqs_create_fail;
+
+ }
+
+ pool->controller_count =
+ dal_adapter_service_get_func_controllers_num(adapter_serv);
+ pool->stream_enc_count = dal_adapter_service_get_stream_engines_num(
+ adapter_serv);
+ pool->scaler_filter = dal_scaler_filter_create(ctx);
+ if (pool->scaler_filter == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to create filter!\n");
+ goto filter_create_fail;
+ }
+
+ for (i = 0; i < pool->controller_count; i++) {
+ pool->timing_generators[i] = dce110_timing_generator_create(
+ adapter_serv, ctx, i, &dce110_tg_offsets[i]);
+ if (pool->timing_generators[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to create tg!\n");
+ goto controller_create_fail;
+ }
+
+ pool->mis[i] = dce110_mem_input_create(ctx, i,
+ &dce110_mi_reg_offsets[i]);
+ if (pool->mis[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error(
+ "DC: failed to create memory input!\n");
+ goto controller_create_fail;
+ }
+
+ pool->ipps[i] = dce110_ipp_create(ctx, i, &dce110_ipp_reg_offsets[i]);
+ if (pool->ipps[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error(
+ "DC: failed to create input pixel processor!\n");
+ goto controller_create_fail;
+ }
+
+ pool->transforms[i] = dce110_transform_create(
+ ctx, i, &dce110_xfm_offsets[i]);
+ if (pool->transforms[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error(
+ "DC: failed to create transform!\n");
+ goto controller_create_fail;
+ }
+ pool->transforms[i]->funcs->transform_set_scaler_filter(
+ pool->transforms[i],
+ pool->scaler_filter);
+
+ pool->opps[i] = dce110_opp_create(ctx, i, &dce110_opp_reg_offsets[i]);
+ if (pool->opps[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error(
+ "DC: failed to create output pixel processor!\n");
+ goto controller_create_fail;
+ }
+ }
+
+ audio_init_data.as = adapter_serv;
+ audio_init_data.ctx = ctx;
+ pool->audio_count = 0;
+ for (i = 0; i < pool->controller_count; i++) {
+ struct graphics_object_id obj_id;
+
+ obj_id = dal_adapter_service_enum_audio_object(adapter_serv, i);
+ if (false == dal_graphics_object_id_is_valid(obj_id)) {
+ /* no more valid audio objects */
+ break;
+ }
+
+ audio_init_data.audio_stream_id = obj_id;
+ pool->audios[i] = dal_audio_create(&audio_init_data);
+ if (pool->audios[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to create DPPs!\n");
+ goto audio_create_fail;
+ }
+ pool->audio_count++;
+ }
+
+ for (i = 0; i < pool->stream_enc_count; i++) {
+ /* TODO: rework fragile code*/
+ if (pool->stream_engines.u_all & 1 << i) {
+ pool->stream_enc[i] = dce110_stream_encoder_create(
+ i, dc->ctx,
+ dal_adapter_service_get_bios_parser(
+ adapter_serv),
+ &stream_enc_regs[i]);
+ if (pool->stream_enc[i] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to create stream_encoder!\n");
+ goto stream_enc_create_fail;
+ }
+ }
+ }
+
+ for (i = 0; i < num_virtual_links; i++) {
+ pool->stream_enc[pool->stream_enc_count] =
+ virtual_stream_encoder_create(
+ dc->ctx, dal_adapter_service_get_bios_parser(
+ adapter_serv));
+ if (pool->stream_enc[pool->stream_enc_count] == NULL) {
+ BREAK_TO_DEBUGGER();
+ dm_error("DC: failed to create stream_encoder!\n");
+ goto stream_enc_create_fail;
+ }
+ pool->stream_enc_count++;
+ }
+
+ return true;
+
+stream_enc_create_fail:
+ for (i = 0; i < pool->stream_enc_count; i++) {
+ if (pool->stream_enc[i] != NULL)
+ dm_free(pool->stream_enc[i]->ctx,
+ DCE110STRENC_FROM_STRENC(pool->stream_enc[i]));
+ }
+
+audio_create_fail:
+ for (i = 0; i < pool->controller_count; i++) {
+ if (pool->audios[i] != NULL)
+ dal_audio_destroy(&pool->audios[i]);
+ }
+
+controller_create_fail:
+ for (i = 0; i < pool->controller_count; i++) {
+ if (pool->opps[i] != NULL)
+ dce110_opp_destroy(&pool->opps[i]);
+
+ if (pool->transforms[i] != NULL)
+ dce110_transform_destroy(&pool->transforms[i]);
+
+ if (pool->ipps[i] != NULL)
+ dce110_ipp_destroy(&pool->ipps[i]);
+
+ if (pool->mis[i] != NULL) {
+ dm_free(pool->mis[i]->ctx,
+ TO_DCE110_MEM_INPUT(pool->mis[i]));
+ pool->mis[i] = NULL;
+ }
+
+ if (pool->timing_generators[i] != NULL) {
+ dm_free(pool->timing_generators[i]->ctx,
+ DCE110TG_FROM_TG(pool->timing_generators[i]));
+ pool->timing_generators[i] = NULL;
+ }
+ }
+
+filter_create_fail:
+ dal_irq_service_destroy(&pool->irqs);
+
+irqs_create_fail:
+ dal_display_clock_destroy(&pool->display_clock);
+
+disp_clk_create_fail:
+clk_src_create_fail:
+ for (i = 0; i < pool->clk_src_count; i++) {
+ if (pool->clock_sources[i] != NULL)
+ dce110_clock_source_destroy(&pool->clock_sources[i]);
+ }
+
+ return false;
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h
new file mode 100644
index 000000000000..5d60df286835
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h
@@ -0,0 +1,46 @@
+/*
+* 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_RESOURCE_DCE110_H__
+#define __DC_RESOURCE_DCE110_H__
+
+#include "core_types.h"
+
+struct adapter_service;
+struct dc;
+struct resource_pool;
+
+bool dce110_construct_resource_pool(
+ struct adapter_service *adapter_serv,
+ uint8_t num_virtual_links,
+ struct dc *dc,
+ struct resource_pool *pool);
+
+void dce110_destruct_resource_pool(struct resource_pool *pool);
+
+void dce110_link_encoder_destroy(struct link_encoder **enc);
+
+#endif /* __DC_RESOURCE_DCE110_H__ */
+