/* TODO store/restore FP was here*/
bool dal_clock_source_adjust_pxl_clk_by_pix_amount(
					struct clock_source *clk_src,
					struct pixel_clk_params *pix_clk_params,
					int32_t pix_num)
{
	bool success = false;
	uint32_t requested_pix_rate_hz;
	uint32_t cur_pix_rate_hz = retrieve_raw_pix_rate_hz(
			clk_src,
			pix_clk_params);
	requested_pix_rate_hz = cur_pix_rate_hz + pix_num;

	if (pix_clk_params == NULL)
		return false;

	dal_logger_write(clk_src->dal_ctx->logger,
		LOG_MAJOR_SYNC,
		LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
		"%s[start]: Current(Raw): %u,%03u,%03uHz, Requested(Raw): %u,%03u,%03uHz\n",
		__func__,
		(cur_pix_rate_hz / 1000000),
		(cur_pix_rate_hz / 1000) % 1000,
		(cur_pix_rate_hz % 1000),
		(requested_pix_rate_hz / 1000000),
		(requested_pix_rate_hz / 1000) % 1000,
		(requested_pix_rate_hz % 1000));

	if (dal_is_dp_signal(pix_clk_params->signal_type))
		success = clk_src->funcs->adjust_dto_pixel_rate(
				clk_src,
				pix_clk_params,
				requested_pix_rate_hz);
	else
		success = clk_src->funcs->adjust_pll_pixel_rate(
				clk_src,
				pix_clk_params,
				requested_pix_rate_hz);

	cur_pix_rate_hz = retrieve_raw_pix_rate_hz(clk_src, pix_clk_params);

	dal_logger_write(clk_src->dal_ctx->logger,
		LOG_MAJOR_SYNC,
		LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
		"%s[end]: Current(Raw): %u,%03u,%03uHz,Requested(Raw): %u,%03u,%03uHz\n\n",
		__func__,
		(cur_pix_rate_hz / 1000000),
		(cur_pix_rate_hz / 1000) % 1000,
		(cur_pix_rate_hz % 1000),
		(requested_pix_rate_hz / 1000000),
		(requested_pix_rate_hz / 1000) % 1000,
		(requested_pix_rate_hz % 1000));

	return success;
}
/* audio_dce110 is derived from audio directly, not via dce80  */
struct hw_ctx_audio *dal_hw_ctx_audio_dce110_create(
	struct dal_context *dal_context,
	uint32_t azalia_stream_id)
{
	/* allocate memory for struc hw_ctx_audio_dce110 */
	struct hw_ctx_audio_dce110 *hw_ctx_dce110 =
		dal_alloc(sizeof(struct hw_ctx_audio_dce110));

	if (!hw_ctx_dce110) {
		ASSERT_CRITICAL(hw_ctx_dce110);
		return NULL;
	}

	/*return pointer to hw_ctx_audio back to caller  -- audio object */
	if (construct(
			hw_ctx_dce110, azalia_stream_id, dal_context))
		return &hw_ctx_dce110->base;

	dal_logger_write(
		dal_context->logger,
		LOG_MAJOR_ERROR,
		LOG_MINOR_COMPONENT_AUDIO,
		"Failed to create hw_ctx_audio for DCE11\n");


	dal_free(hw_ctx_dce110);

	return NULL;
}
static enum clocks_state get_required_clocks_state(
		struct display_clock *dc,
		struct state_dependent_clocks *req_clocks)
{
	int32_t i;
	struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc);
	enum clocks_state low_req_clk = disp_clk->max_clks_state;

	if (!req_clocks) {
		/* NULL pointer*/
		dal_logger_write(dc->ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"%s: Invalid parameter",
				__func__);
		return CLOCKS_STATE_INVALID;
	}

	/* Iterate from highest supported to lowest valid state, and update
	 * lowest RequiredState with the lowest state that satisfies
	 * all required clocks
	 */
	for (i = disp_clk->max_clks_state; i >= CLOCKS_STATE_ULTRA_LOW; --i) {
		if ((req_clocks->display_clk_khz <=
			max_clks_by_state[i].display_clk_khz) &&
			(req_clocks->pixel_clk_khz <=
				max_clks_by_state[i].pixel_clk_khz))
			low_req_clk = i;
	}
	return low_req_clk;
}
/**
 * Calculate PLL Dividers for given Clock Value.
 * First will call VBIOS Adjust Exec table to check if requested Pixel clock
 * will be Adjusted based on usage.
 * Then it will calculate PLL Dividers for this Adjusted clock using preferred
 * method (Maximum VCO frequency).
 *
 * \return
 *     Calculation error in units of 0.01%
 */
static uint32_t dce112_get_pix_clk_dividers(
		struct clock_source *cs,
		struct pixel_clk_params *pix_clk_params,
		struct pll_settings *pll_settings)
{
	struct dce112_clk_src *clk_src = TO_DCE112_CLK_SRC(cs);
	uint32_t actualPixelClockInKHz;

	if (pix_clk_params == NULL || pll_settings == NULL
			|| pix_clk_params->requested_pix_clk == 0) {
		dal_logger_write(cs->ctx->logger,
			LOG_MAJOR_ERROR,
			LOG_MINOR_COMPONENT_GPU,
			"%s: Invalid parameters!!\n", __func__);
		return 0;
	}

	memset(pll_settings, 0, sizeof(*pll_settings));

	if (clk_src->base.id == CLOCK_SOURCE_ID_DP_DTO) {
		pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz;
		pll_settings->calculated_pix_clk = clk_src->ext_clk_khz;
		pll_settings->actual_pix_clk =
					pix_clk_params->requested_pix_clk;
		return 0;
	}
	/* PLL only after this point */

	actualPixelClockInKHz = pix_clk_params->requested_pix_clk;

	/* Calculate Dividers */
	if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) {
		switch (pix_clk_params->color_depth) {
		case COLOR_DEPTH_101010:
			actualPixelClockInKHz = (actualPixelClockInKHz * 5) >> 2;
			break;
		case COLOR_DEPTH_121212:
			actualPixelClockInKHz = (actualPixelClockInKHz * 6) >> 2;
			break;
		case COLOR_DEPTH_161616:
			actualPixelClockInKHz = actualPixelClockInKHz * 2;
			break;
		default:
			break;
		}
	}

	pll_settings->actual_pix_clk = actualPixelClockInKHz;
	pll_settings->adjusted_pix_clk = actualPixelClockInKHz;
	pll_settings->calculated_pix_clk = pix_clk_params->requested_pix_clk;

	return 0;
}
static bool dummy_ack(
    struct irq_service *irq_service,
    const struct irq_source_info *info)
{
    dal_logger_write(
        irq_service->ctx->logger,
        LOG_MAJOR_ERROR,
        LOG_MINOR_COMPONENT_IRQ_SERVICE,
        "%s: called for non-implemented irq source\n",
        __func__);
    return false;
}
static bool set_pixel_storage_depth(
	struct line_buffer *base,
	enum lb_pixel_depth depth)
{
	bool ret = true;
	struct line_buffer_dce110 *lb = LB110_FROM_BASE(base);
	uint32_t value;

	value = dal_read_reg(
			base->dal_context,
			lb->lbx_data_format);
	switch (depth) {
	case LB_PIXEL_DEPTH_18BPP:
		set_reg_field_value(value, 2, LB_DATA_FORMAT, PIXEL_DEPTH);
		set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE);
		break;
	case LB_PIXEL_DEPTH_24BPP:
		set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_DEPTH);
		set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE);
		break;
	case LB_PIXEL_DEPTH_30BPP:
		set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_DEPTH);
		set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE);
		break;
	case LB_PIXEL_DEPTH_36BPP:
		set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH);
		set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_EXPAN_MODE);
		break;
	default:
		ret = false;
		break;
	}

	if (ret == true) {

		set_reg_field_value(value, 0, LB_DATA_FORMAT, ALPHA_EN);
		dal_write_reg(
				base->dal_context, lb->lbx_data_format, value);
		if (!(lb->caps & depth)) {
			/*we should use unsupported capabilities
			 *  unless it is required by w/a*/
			dal_logger_write(base->dal_context->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"%s: Capability not supported",
				__func__);
		}

	}

	return ret;
}
bool dce110_transform_get_max_num_of_supported_lines(
	struct dce110_transform *xfm110,
	enum lb_pixel_depth depth,
	uint32_t pixel_width,
	uint32_t *lines)
{
	uint32_t pixels_per_entries = 0;
	uint32_t max_pixels_supports = 0;

	if (pixel_width == 0)
		return false;

	/* Find number of pixels that can fit into a single LB entry and
	 * take floor of the value since we cannot store a single pixel
	 * across multiple entries. */
	switch (depth) {
	case LB_PIXEL_DEPTH_18BPP:
		pixels_per_entries = LB_BITS_PER_ENTRY / 18;
		break;

	case LB_PIXEL_DEPTH_24BPP:
		pixels_per_entries = LB_BITS_PER_ENTRY / 24;
		break;

	case LB_PIXEL_DEPTH_30BPP:
		pixels_per_entries = LB_BITS_PER_ENTRY / 30;
		break;

	case LB_PIXEL_DEPTH_36BPP:
		pixels_per_entries = LB_BITS_PER_ENTRY / 36;
		break;

	default:
		dal_logger_write(xfm110->base.ctx->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_GPU,
			"%s: Invalid LB pixel depth",
			__func__);
		break;
	}

	if (pixels_per_entries == 0)
		return false;

	max_pixels_supports = pixels_per_entries * LB_TOTAL_NUMBER_OF_ENTRIES;

	*lines = max_pixels_supports / pixel_width;
	return true;
}
static void dce80_link_encoder_enable_tmds_output(
	struct link_encoder *enc,
	enum clock_source_id clock_source,
	enum dc_color_depth color_depth,
	bool hdmi,
	bool dual_link,
	uint32_t pixel_clock)
{
	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
	struct dc_context *ctx = enc110->base.ctx;
	struct bp_transmitter_control cntl = { 0 };
	enum bp_result result;

	/* Enable the PHY */

	cntl.action = TRANSMITTER_CONTROL_ENABLE;
	cntl.engine_id = enc->preferred_engine;
	cntl.transmitter = enc110->base.transmitter;
	cntl.pll_id = clock_source;
	if (hdmi) {
		cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A;
		cntl.lanes_number = 4;
	} else if (dual_link) {
		cntl.signal = SIGNAL_TYPE_DVI_DUAL_LINK;
		cntl.lanes_number = 8;
	} else {
		cntl.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
		cntl.lanes_number = 4;
	}
	cntl.hpd_sel = enc110->base.hpd_source;

	cntl.pixel_clock = pixel_clock;
	cntl.color_depth = color_depth;

	result = link_transmitter_control(enc110, &cntl);

	if (result != BP_RESULT_OK) {
		dal_logger_write(ctx->logger,
			LOG_MAJOR_ERROR,
			LOG_MINOR_COMPONENT_ENCODER,
			"%s: Failed to execute VBIOS command table!\n",
			__func__);
		BREAK_TO_DEBUGGER();
	}
}
struct clock_source *dal_clock_source_create(
		struct clock_source_init_data *clk_src_init_data)
{
	enum dce_version dce_ver =
		dal_adapter_service_get_dce_version(clk_src_init_data->as);
	enum clock_source_id clk_src_id =
		dal_graphics_object_id_get_clock_source_id(
			clk_src_init_data->clk_src_id);
	switch (dce_ver) {
#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
	break;
	case DCE_VERSION_11_0:
	{
		switch (clk_src_id) {
		case CLOCK_SOURCE_ID_PLL0:
		/* fall through */
		case CLOCK_SOURCE_ID_PLL1:
		/* fall through */
		case CLOCK_SOURCE_ID_PLL2:
			return dal_pll_clock_source_dce110_create(
					clk_src_init_data);
		case CLOCK_SOURCE_ID_EXTERNAL:
			return dal_ext_clock_source_dce110_create(
					clk_src_init_data);
		case CLOCK_SOURCE_ID_VCE:
			return dal_vce_clock_source_dce110_create(
					clk_src_init_data);
		default:
			return NULL;
		}
	}
	break;
#endif
	default:
		dal_logger_write(clk_src_init_data->dal_ctx->logger,
			LOG_MAJOR_ERROR,
			LOG_MINOR_COMPONENT_GPU,
			"Clock Source: not supported DCE version %d", dce_ver);
		ASSERT_CRITICAL(false);
	break;
	}
	return NULL;
}
/* enables DP PHY output */
static void dce80_link_encoder_enable_dp_output(
	struct link_encoder *enc,
	const struct dc_link_settings *link_settings,
	enum clock_source_id clock_source)
{
	struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc);
	struct dc_context *ctx = enc110->base.ctx;
	struct bp_transmitter_control cntl = { 0 };
	enum bp_result result;

	/* Enable the PHY */

	/* number_of_lanes is used for pixel clock adjust,
	 * but it's not passed to asic_control.
	 * We need to set number of lanes manually.
	 */
	configure_encoder(enc110, link_settings);

	cntl.action = TRANSMITTER_CONTROL_ENABLE;
	cntl.engine_id = enc->preferred_engine;
	cntl.transmitter = enc110->base.transmitter;
	cntl.pll_id = clock_source;
	cntl.signal = SIGNAL_TYPE_DISPLAY_PORT;
	cntl.lanes_number = link_settings->lane_count;
	cntl.hpd_sel = enc110->base.hpd_source;
	cntl.pixel_clock = link_settings->link_rate
						* LINK_RATE_REF_FREQ_IN_KHZ;
	/* TODO: check if undefined works */
	cntl.color_depth = COLOR_DEPTH_UNDEFINED;

	result = link_transmitter_control(enc110, &cntl);

	if (result != BP_RESULT_OK) {
		dal_logger_write(ctx->logger,
			LOG_MAJOR_ERROR,
			LOG_MINOR_COMPONENT_ENCODER,
			"%s: Failed to execute VBIOS command table!\n",
			__func__);
		BREAK_TO_DEBUGGER();
	}
}
static uint32_t read_indirect_azalia_reg(
	const struct hw_ctx_audio *hw_ctx,
	uint32_t reg_index)
{
	uint32_t ret_val = 0;
	uint32_t addr = 0;
	uint32_t value = 0;


	/* AZALIA_F0_CODEC_ENDPOINT_INDEX  endpoint index  */
	{
		addr =
			FROM_BASE(hw_ctx)->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_index;

		set_reg_field_value(value, reg_index,
			AZALIA_F0_CODEC_ENDPOINT_INDEX,
			AZALIA_ENDPOINT_REG_INDEX);

		dal_write_reg(hw_ctx->ctx, addr, value);
	}

	/* AZALIA_F0_CODEC_ENDPOINT_DATA  endpoint data  */
	{
		addr =
			FROM_BASE(hw_ctx)->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_data;

		value = dal_read_reg(hw_ctx->ctx, addr);
		ret_val = value;
	}

	dal_logger_write(
		hw_ctx->ctx->logger,
		LOG_MAJOR_HW_TRACE,
		LOG_MINOR_HW_TRACE_AUDIO,
		"AUDIO:read_indirect_azalia_reg: index: %u  data: %u\n",
		reg_index, ret_val);

	return ret_val;
}
/* Assign GTC group and enable GTC value embedding */
static void enable_gtc_embedding_with_group(
	const struct hw_ctx_audio *hw_ctx,
	uint32_t group_num,
	uint32_t audio_latency)
{
	/*need to replace the static number with variable */
	if (group_num <= 6) {
		uint32_t value = read_indirect_azalia_reg(
			hw_ctx,
			ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING);

		set_reg_field_value(
			value,
			group_num,
			AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
			PRESENTATION_TIME_EMBEDDING_GROUP);

		set_reg_field_value(
			value,
			1,
			AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
			PRESENTATION_TIME_EMBEDDING_ENABLE);

		write_indirect_azalia_reg(
			hw_ctx,
			ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
			value);

		/*update audio latency to LIPSYNC*/
		set_audio_latency(hw_ctx, audio_latency);
	} else {
		dal_logger_write(
			hw_ctx->ctx->logger,
			LOG_MAJOR_HW_TRACE,
			LOG_MINOR_COMPONENT_AUDIO,
			"GTC group number %d is too big",
			group_num);
	}
}
static bool get_current_pixel_storage_depth(
	struct line_buffer *base,
	enum lb_pixel_depth *depth)
{
	struct line_buffer_dce110 *lb = LB110_FROM_BASE(base);
	uint32_t value = 0;

	if (depth == NULL)
		return false;

	value = dal_read_reg(
			base->dal_context,
			lb->lbx_data_format);

	switch (get_reg_field_value(value, LB_DATA_FORMAT, PIXEL_DEPTH)) {
	case 0:
		*depth = LB_PIXEL_DEPTH_30BPP;
		break;
	case 1:
		*depth = LB_PIXEL_DEPTH_24BPP;
		break;
	case 2:
		*depth = LB_PIXEL_DEPTH_18BPP;
		break;
	case 3:
		*depth = LB_PIXEL_DEPTH_36BPP;
		break;
	default:
		dal_logger_write(base->dal_context->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_GPU,
			"%s: Invalid LB pixel depth",
			__func__);
		*depth = LB_PIXEL_DEPTH_30BPP;
		break;
	}
	return true;

}
bool dal_line_buffer_construct_base(
	struct line_buffer *lb,
	struct line_buffer_init_data *init_data
	)
{
	struct dal_context *dal_context = init_data->dal_context;

	if (init_data == NULL || init_data->as == NULL)
		return false;
	lb->dal_context = init_data->dal_context;
	lb->size = dal_adapter_service_get_line_buffer_size(init_data->as);

	lb->power_gating = dal_adapter_service_is_feature_supported(
			FEATURE_POWER_GATING_LB_PORTION);
	dal_logger_write(dal_context->logger,
		LOG_MAJOR_LINE_BUFFER,
		LOG_MINOR_LINE_BUFFER_POWERGATING,
		"LB Partial Power Gating option: %s\n",
		(lb->power_gating == true ? "Enabled" : "Disabled"));

	return true;
}
static uint32_t get_validation_clock(struct display_clock *dc)
{
	uint32_t clk = 0;
	struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc);

	switch (disp_clk->max_clks_state) {
	case CLOCKS_STATE_ULTRA_LOW:
		/*Currently not supported, it has 0 in table entry*/
	case CLOCKS_STATE_LOW:
		clk = max_clks_by_state[CLOCKS_STATE_LOW].
						display_clk_khz;
		break;

	case CLOCKS_STATE_NOMINAL:
		clk = max_clks_by_state[CLOCKS_STATE_NOMINAL].
						display_clk_khz;
		break;

	case CLOCKS_STATE_PERFORMANCE:
		clk = max_clks_by_state[CLOCKS_STATE_PERFORMANCE].
						display_clk_khz;
		break;

	case CLOCKS_STATE_INVALID:
	default:
		/*Invalid Clocks State*/
		dal_logger_write(dc->ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"Invalid clock state");
		/* just return the display engine clock for
		 * lowest supported state*/
		clk = max_clks_by_state[CLOCKS_STATE_LOW].
						display_clk_khz;
		break;
	}
	return clk;
}
static bool set_min_clocks_state(
	struct display_clock *base,
	enum clocks_state clocks_state)
{
	struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base);

	if (clocks_state > dc->max_clks_state) {
		/*Requested state exceeds max supported state.*/
		dal_logger_write(base->ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"Requested state exceeds max supported state");
		return false;
	} else if (clocks_state == base->cur_min_clks_state) {
		/*if we're trying to set the same state, we can just return
		 * since nothing needs to be done*/
		return true;
	}

	base->cur_min_clks_state = clocks_state;

	return true;
}
/**
 * prepare_scratch_active_and_requested
 *
 * @brief
 *  prepare and update VBIOS scratch pad registers about active and requested
 *  displays
 *
 * @param
 * data - helper's shared data
 * enum controller_ild - controller Id
 * enum signal_type - signal type used on display
 * const struct connector_device_tag_info* - pointer to display type and enum id
 */
static void prepare_scratch_active_and_requested(
	struct dal_context *dal_context,
	struct vbios_helper_data *data,
	enum controller_id id,
	enum signal_type s,
	const struct connector_device_tag_info *dev_tag)
{
	switch (s) {
	case SIGNAL_TYPE_DVI_SINGLE_LINK:
	case SIGNAL_TYPE_DVI_SINGLE_LINK1:
	case SIGNAL_TYPE_DVI_DUAL_LINK:
	case SIGNAL_TYPE_HDMI_TYPE_A:
	case SIGNAL_TYPE_DISPLAY_PORT:
	case SIGNAL_TYPE_DISPLAY_PORT_MST:
		if (dev_tag->dev_id.device_type == DEVICE_TYPE_DFP)
			switch (dev_tag->dev_id.enum_id) {
			case 1:
				data->requested |= ATOM_S6_ACC_REQ_DFP1;
				data->active |= ATOM_S3_DFP1_ACTIVE;
				break;
			case 2:
				data->requested |= ATOM_S6_ACC_REQ_DFP2;
				data->active |= ATOM_S3_DFP2_ACTIVE;
				break;
			case 3:
				data->requested |= ATOM_S6_ACC_REQ_DFP3;
				data->active |= ATOM_S3_DFP3_ACTIVE;
				break;
			case 4:
				data->requested |= ATOM_S6_ACC_REQ_DFP4;
				data->active |= ATOM_S3_DFP4_ACTIVE;
				break;
			case 5:
				data->requested |= ATOM_S6_ACC_REQ_DFP5;
				data->active |= ATOM_S3_DFP5_ACTIVE;
				break;
			case 6:
				data->requested |= ATOM_S6_ACC_REQ_DFP6;
				data->active |= ATOM_S3_DFP6_ACTIVE;
				break;
			default:
				break;
			}
		break;
	case SIGNAL_TYPE_LVDS:
	case SIGNAL_TYPE_EDP:
		data->requested |= ATOM_S6_ACC_REQ_LCD1;
		data->active |= ATOM_S3_LCD1_ACTIVE;
		break;
	case SIGNAL_TYPE_RGB:
		if (dev_tag->dev_id.device_type == DEVICE_TYPE_CRT)
			switch (dev_tag->dev_id.enum_id) {
			case 1:
				data->requested |= ATOM_S6_ACC_REQ_CRT1;
				data->active |= ATOM_S3_CRT1_ACTIVE;
				break;
			case 2:
				dal_logger_write(dal_context->logger,
					LOG_MAJOR_BIOS,
					LOG_MINOR_COMPONENT_BIOS,
					"%s: DAL does not support DAC2!\n",
					__func__);
				break;
			default:
				break;
			}
		break;
	case SIGNAL_TYPE_DVO:
		dal_logger_write(dal_context->logger,
				LOG_MAJOR_BIOS,
				LOG_MINOR_COMPONENT_BIOS,
				"%s: Passing unsupported Signal!\n",
				__func__);
		break;
	default:
		dal_logger_write(dal_context->logger,
				LOG_MAJOR_BIOS,
				LOG_MINOR_COMPONENT_BIOS,
				"%s: No such signal!\n",
				__func__);
		break;
	}
}
/* get current channel spliting */
static bool get_channel_splitting_mapping(
	const struct hw_ctx_audio *hw_ctx,
	enum engine_id engine_id,
	struct audio_channel_associate_info *audio_mapping)
{
	uint32_t value = 0;

	if (audio_mapping == NULL)
		return false;

	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_ASSOCIATION_INFO);

	/*0xFFFFFFFF*/
	if (get_reg_field_value(value,
			AZALIA_F0_CODEC_PIN_ASSOCIATION_INFO,
			ASSOCIATION_INFO) !=
			MULTI_CHANNEL_SPLIT_NO_ASSO_INFO) {
		uint32_t multi_channel01_enable = 0;
		uint32_t multi_channel23_enable = 0;
		uint32_t multi_channel45_enable = 0;
		uint32_t multi_channel67_enable = 0;
		/* get the one we set.*/
		audio_mapping->u32all = value;

		/* check each enable status*/
		value = read_indirect_azalia_reg(
			hw_ctx,
			ixAZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE);

		multi_channel01_enable = get_reg_field_value(value,
		AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE,
		MULTICHANNEL01_ENABLE);

		multi_channel23_enable = get_reg_field_value(value,
		AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE,
		MULTICHANNEL23_ENABLE);

		multi_channel45_enable = get_reg_field_value(value,
		AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE,
		MULTICHANNEL45_ENABLE);

		multi_channel67_enable = get_reg_field_value(value,
		AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE,
		MULTICHANNEL67_ENABLE);

		if (multi_channel01_enable == 0 &&
			multi_channel23_enable == 0 &&
			multi_channel45_enable == 0 &&
			multi_channel67_enable == 0)
			dal_logger_write(hw_ctx->ctx->logger,
				LOG_MAJOR_HW_TRACE,
				LOG_MINOR_COMPONENT_AUDIO,
				"Audio driver did not enable multi-channel\n");

		return true;
	}

	return false;
}
static void dce80_update_hdmi_info_packet(
	struct dce110_stream_encoder *enc110,
	uint32_t packet_index,
	const struct encoder_info_packet *info_packet)
{
	struct dc_context *ctx = enc110->base.ctx;
	uint32_t cont, send, line;
	uint32_t addr = 0;
	uint32_t regval;

	if (info_packet->valid) {
		dce80_update_generic_info_packet(
			enc110,
			packet_index,
			info_packet);

		/* enable transmission of packet(s) -
		* packet transmission begins on the next frame
		*/
		cont = 1;
		/* send packet(s) every frame */
		send = 1;
		/* select line number to send packets on */
		line = 2;
	} else {
		cont = 0;
		send = 0;
		line = 0;
	}

	/* choose which generic packet control to use */

	switch (packet_index) {
	case 0:
	case 1:
		addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL0);
		break;
	case 2:
	case 3:
		addr = LINK_REG(HDMI_GENERIC_PACKET_CONTROL1);
		break;
	default:
		/* invalid HW packet index */
		dal_logger_write(
			ctx->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_ENCODER,
			"Invalid HW packet index: %s()\n",
			__func__);
		break;
	}

	regval = dm_read_reg(ctx, addr);

	switch (packet_index) {
	case 0:
	case 2:
		set_reg_field_value(
			regval,
			cont,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC0_CONT);
		set_reg_field_value(
			regval,
			send,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC0_SEND);
		set_reg_field_value(
			regval,
			line,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC0_LINE);
		break;
	case 1:
	case 3:
		set_reg_field_value(
			regval,
			cont,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC1_CONT);
		set_reg_field_value(
			regval,
			send,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC1_SEND);
		set_reg_field_value(
			regval,
			line,
			HDMI_GENERIC_PACKET_CONTROL0,
			HDMI_GENERIC1_LINE);
		break;
	default:
		/* invalid HW packet index */
		dal_logger_write(
			ctx->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_ENCODER,
			"Invalid HW packet index: %s()\n",
			__func__);
		break;
	}

	dm_write_reg(ctx, addr, regval);
}
static bool dal_display_clock_dce110_construct(
	struct display_clock_dce110 *dc110,
	struct dc_context *ctx,
	struct adapter_service *as)
{
	struct display_clock *dc_base = &dc110->disp_clk_base;

	if (NULL == as)
		return false;

	if (!dal_display_clock_construct_base(dc_base, ctx, as))
		return false;

	dc_base->funcs = &funcs;

	dc110->dfs_bypass_disp_clk = 0;

	if (!display_clock_integrated_info_construct(dc110, as))
		dal_logger_write(dc_base->ctx->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_GPU,
			"Cannot obtain VBIOS integrated info\n");

	dc110->gpu_pll_ss_percentage = 0;
	dc110->gpu_pll_ss_divider = 1000;
	dc110->ss_on_gpu_pll = false;

	dc_base->id = CLOCK_SOURCE_ID_DFS;
/* Initially set max clocks state to nominal.  This should be updated by
 * via a pplib call to DAL IRI eventually calling a
 * DisplayEngineClock_Dce110::StoreMaxClocksState().  This call will come in
 * on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/
	dc110->max_clks_state = CLOCKS_STATE_NOMINAL;

	dal_divider_range_construct(
		&divider_ranges[DIVIDER_RANGE_01],
		DIVIDER_RANGE_01_START,
		DIVIDER_RANGE_01_STEP_SIZE,
		DIVIDER_RANGE_01_BASE_DIVIDER_ID,
		DIVIDER_RANGE_02_BASE_DIVIDER_ID);
	dal_divider_range_construct(
		&divider_ranges[DIVIDER_RANGE_02],
		DIVIDER_RANGE_02_START,
		DIVIDER_RANGE_02_STEP_SIZE,
		DIVIDER_RANGE_02_BASE_DIVIDER_ID,
		DIVIDER_RANGE_03_BASE_DIVIDER_ID);
	dal_divider_range_construct(
		&divider_ranges[DIVIDER_RANGE_03],
		DIVIDER_RANGE_03_START,
		DIVIDER_RANGE_03_STEP_SIZE,
		DIVIDER_RANGE_03_BASE_DIVIDER_ID,
		DIVIDER_RANGE_MAX_DIVIDER_ID);

	{
		uint32_t ss_info_num =
			dal_adapter_service_get_ss_info_num(
				as,
				AS_SIGNAL_TYPE_GPU_PLL);

		if (ss_info_num) {
			struct spread_spectrum_info info;
			bool result;

			memset(&info, 0, sizeof(info));

			result =
				dal_adapter_service_get_ss_info(
					as,
					AS_SIGNAL_TYPE_GPU_PLL,
					0,
					&info);

			/* Based on VBIOS, VBIOS will keep entry for GPU PLL SS
			 * even if SS not enabled and in that case
			 * SSInfo.spreadSpectrumPercentage !=0 would be sign
			 * that SS is enabled
			 */
			if (result && info.spread_spectrum_percentage != 0) {
				dc110->ss_on_gpu_pll = true;
				dc110->gpu_pll_ss_divider =
					info.spread_percentage_divider;

				if (info.type.CENTER_MODE == 0) {
					/* Currently for DP Reference clock we
					 * need only SS percentage for
					 * downspread */
					dc110->gpu_pll_ss_percentage =
						info.spread_spectrum_percentage;
				}
			}

		}
	}

	return true;
}
bool dce80_link_encoder_construct(
	struct dce110_link_encoder *enc110,
	const struct encoder_init_data *init_data,
	const struct dce110_link_enc_registers *link_regs,
	const struct dce110_link_enc_aux_registers *aux_regs,
	const struct dce110_link_enc_bl_registers *bl_regs)
{
	struct graphics_object_encoder_cap_info enc_cap_info = {0};

	enc110->base.funcs = &dce80_lnk_enc_funcs;
	enc110->base.ctx = init_data->ctx;
	enc110->base.id = init_data->encoder;

	enc110->base.hpd_source = init_data->hpd_source;
	enc110->base.connector = init_data->connector;
	enc110->base.input_signals = SIGNAL_TYPE_ALL;

	enc110->base.adapter_service = init_data->adapter_service;

	enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;

	enc110->base.features.flags.raw = 0;

	enc110->base.transmitter = init_data->transmitter;

	enc110->base.features.flags.bits.IS_AUDIO_CAPABLE = true;

	enc110->base.features.max_pixel_clock = DCE8_UNIPHY_MAX_PIXEL_CLK_IN_KHZ;

	enc110->base.features.max_hdmi_pixel_clock =
			DCE8_UNIPHY_MAX_PIXEL_CLK_IN_KHZ;
	enc110->base.features.max_deep_color = COLOR_DEPTH_121212;
	enc110->base.features.max_hdmi_deep_color = COLOR_DEPTH_121212;

	/* set the flag to indicate whether driver poll the I2C data pin
	 * while doing the DP sink detect
	 */

	if (dal_adapter_service_is_feature_supported(
		FEATURE_DP_SINK_DETECT_POLL_DATA_PIN))
		enc110->base.features.flags.bits.
			DP_SINK_DETECT_POLL_DATA_PIN = true;

	enc110->base.output_signals =
		SIGNAL_TYPE_DVI_SINGLE_LINK |
		SIGNAL_TYPE_DVI_DUAL_LINK |
		SIGNAL_TYPE_LVDS |
		SIGNAL_TYPE_DISPLAY_PORT |
		SIGNAL_TYPE_DISPLAY_PORT_MST |
		SIGNAL_TYPE_EDP |
		SIGNAL_TYPE_HDMI_TYPE_A;

	/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
	 * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
	 * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
	 * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS.
	 * Prefer DIG assignment is decided by board design.
	 * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design
	 * and VBIOS will filter out 7 UNIPHY for DCE 8.0.
	 * By this, adding DIGG should not hurt DCE 8.0.
	 * This will let DCE 8.1 share DCE 8.0 as much as possible
	 */

	enc110->link_regs = link_regs;
	enc110->aux_regs = aux_regs;
	enc110->bl_regs = bl_regs;

	switch (enc110->base.transmitter) {
	case TRANSMITTER_UNIPHY_A:
		enc110->base.preferred_engine = ENGINE_ID_DIGA;
	break;
	case TRANSMITTER_UNIPHY_B:
		enc110->base.preferred_engine = ENGINE_ID_DIGB;

	break;
	case TRANSMITTER_UNIPHY_C:
		enc110->base.preferred_engine = ENGINE_ID_DIGC;
	break;
	case TRANSMITTER_UNIPHY_D:
		enc110->base.preferred_engine = ENGINE_ID_DIGD;
	break;
	case TRANSMITTER_UNIPHY_E:
		enc110->base.preferred_engine = ENGINE_ID_DIGE;
	break;
	case TRANSMITTER_UNIPHY_F:
		enc110->base.preferred_engine = ENGINE_ID_DIGF;
	break;
	default:
		ASSERT_CRITICAL(false);
		enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
		break;
	}

	dal_logger_write(init_data->ctx->logger,
			LOG_MAJOR_I2C_AUX,
			LOG_MINOR_I2C_AUX_CFG,
			"Using channel: %s [%d]\n",
			DECODE_CHANNEL_ID(init_data->channel),
			init_data->channel);

	/* Override features with DCE-specific values */
	if (dal_adapter_service_get_encoder_cap_info(
			enc110->base.adapter_service,
			enc110->base.id, &enc_cap_info))
		enc110->base.features.flags.bits.IS_HBR2_CAPABLE =
				enc_cap_info.dp_hbr2_cap;

	/* test pattern 3 support */
	enc110->base.features.flags.bits.IS_TPS3_CAPABLE = true;
	enc110->base.features.max_deep_color = COLOR_DEPTH_121212;

	enc110->base.features.flags.bits.IS_Y_ONLY_CAPABLE =
		dal_adapter_service_is_feature_supported(
			FEATURE_SUPPORT_DP_Y_ONLY);

	enc110->base.features.flags.bits.IS_YCBCR_CAPABLE =
		dal_adapter_service_is_feature_supported(
			FEATURE_SUPPORT_DP_YUV);

	return true;
}
static uint32_t calc_single_display_min_clks(
	struct display_clock *base,
	struct min_clock_params *params,
	bool set_clk)
{
	struct fixed32_32 h_scale_ratio = dal_fixed32_32_one;
	struct fixed32_32 v_scale_ratio = dal_fixed32_32_one;
	uint32_t pix_clk_khz = 0;
	uint32_t lb_source_width = 0;
	struct fixed32_32 deep_color_factor;
	struct fixed32_32 scaler_efficiency;
	struct fixed32_32 v_filter_init;
	uint32_t v_filter_init_trunc;
	uint32_t num_lines_at_frame_start = 3;
	struct fixed32_32 v_filter_init_ceil;
	struct fixed32_32 lines_per_lines_out_at_frame_start;
	struct fixed32_32 lb_lines_in_per_line_out; /* in middle of the frame*/
	uint32_t src_wdth_rnd_to_chunks;
	struct fixed32_32 scaling_coeff;
	struct fixed32_32 h_blank_granularity_factor =
			dal_fixed32_32_one;
	struct fixed32_32 fx_disp_clk_mhz;
	struct fixed32_32 line_time;
	struct fixed32_32 disp_pipe_pix_throughput;
	struct fixed32_32 fx_alt_disp_clk_mhz;
	uint32_t disp_clk_khz;
	uint32_t alt_disp_clk_khz;
	struct display_clock_dce110 *disp_clk_110 = DCLCK110_FROM_BASE(base);
	uint32_t max_clk_khz = get_validation_clock(base);
	bool panning_allowed = false; /* TODO: receive this value from AS */

	if (params == NULL) {
		dal_logger_write(base->ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"Invalid input parameter in %s",
				__func__);
		return 0;
	}

	deep_color_factor = get_deep_color_factor(params);
	scaler_efficiency = get_scaler_efficiency(base->ctx, params);
	pix_clk_khz = params->requested_pixel_clock;
	lb_source_width = params->source_view.width;

	if (0 != params->dest_view.height && 0 != params->dest_view.width) {

		h_scale_ratio = dal_fixed32_32_from_fraction(
			params->source_view.width,
			params->dest_view.width);
		v_scale_ratio = dal_fixed32_32_from_fraction(
			params->source_view.height,
			params->dest_view.height);
	} else {
		dal_logger_write(base->ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_GPU,
				"Destination height or width is 0!\n");
	}

	v_filter_init =
		dal_fixed32_32_add(
			v_scale_ratio,
			dal_fixed32_32_add_int(
				dal_fixed32_32_div_int(
					dal_fixed32_32_mul_int(
						v_scale_ratio,
						params->timing_info.INTERLACED),
					2),
				params->scaling_info.v_taps + 1));
	v_filter_init = dal_fixed32_32_div_int(v_filter_init, 2);

	v_filter_init_trunc = dal_fixed32_32_floor(v_filter_init);

	v_filter_init_ceil = dal_fixed32_32_from_fraction(
						v_filter_init_trunc, 2);
	v_filter_init_ceil = dal_fixed32_32_from_int(
		dal_fixed32_32_ceil(v_filter_init_ceil));
	v_filter_init_ceil = dal_fixed32_32_mul_int(v_filter_init_ceil, 2);

	lines_per_lines_out_at_frame_start =
			dal_fixed32_32_div_int(v_filter_init_ceil,
					num_lines_at_frame_start);
	lb_lines_in_per_line_out =
			get_lb_lines_in_per_line_out(params, v_scale_ratio);

	if (panning_allowed)
		src_wdth_rnd_to_chunks =
			((lb_source_width - 1) / 128) * 128 + 256;
	else
		src_wdth_rnd_to_chunks =
			((lb_source_width + 127) / 128) * 128;

	scaling_coeff =
		dal_fixed32_32_div(
			dal_fixed32_32_from_int(params->scaling_info.v_taps),
			scaler_efficiency);

	if (dal_fixed32_32_le(h_scale_ratio, dal_fixed32_32_one))
		scaling_coeff = dal_fixed32_32_max(
			dal_fixed32_32_from_int(
				dal_fixed32_32_ceil(
					dal_fixed32_32_from_fraction(
						params->scaling_info.h_taps,
						4))),
			dal_fixed32_32_max(
				dal_fixed32_32_mul(
					scaling_coeff,
					h_scale_ratio),
				dal_fixed32_32_one));

	if (!params->line_buffer_prefetch_enabled &&
		dal_fixed32_32_floor(lb_lines_in_per_line_out) != 2 &&
		dal_fixed32_32_floor(lb_lines_in_per_line_out) != 4) {
		uint32_t line_total_pixel =
			params->timing_info.h_total + lb_source_width - 256;
		h_blank_granularity_factor = dal_fixed32_32_div(
			dal_fixed32_32_from_int(params->timing_info.h_total),
			dal_fixed32_32_div(
			dal_fixed32_32_from_fraction(
				line_total_pixel, 2),
				h_scale_ratio));
	}

	/* Calculate display clock with ramping. Ramping factor is 1.1*/
	fx_disp_clk_mhz =
		dal_fixed32_32_div_int(
			dal_fixed32_32_mul_int(scaling_coeff, 11),
			10);
	line_time = dal_fixed32_32_from_fraction(
			params->timing_info.h_total * 1000, pix_clk_khz);

	disp_pipe_pix_throughput = dal_fixed32_32_mul(
			lb_lines_in_per_line_out, h_blank_granularity_factor);
	disp_pipe_pix_throughput = dal_fixed32_32_max(
			disp_pipe_pix_throughput,
			lines_per_lines_out_at_frame_start);
	disp_pipe_pix_throughput = dal_fixed32_32_div(dal_fixed32_32_mul_int(
			disp_pipe_pix_throughput, src_wdth_rnd_to_chunks),
			line_time);

	if (0 != params->timing_info.h_total) {
		fx_disp_clk_mhz =
			dal_fixed32_32_max(
				dal_fixed32_32_div_int(
					dal_fixed32_32_mul_int(
						scaling_coeff, pix_clk_khz),
						1000),
				disp_pipe_pix_throughput);
		fx_disp_clk_mhz =
			dal_fixed32_32_mul(
				fx_disp_clk_mhz,
				dal_fixed32_32_from_fraction(11, 10));
	}

	fx_disp_clk_mhz = dal_fixed32_32_max(fx_disp_clk_mhz,
		dal_fixed32_32_mul(deep_color_factor,
		dal_fixed32_32_from_fraction(11, 10)));

	/* Calculate display clock without ramping */
	fx_alt_disp_clk_mhz = scaling_coeff;

	if (0 != params->timing_info.h_total) {
		fx_alt_disp_clk_mhz = dal_fixed32_32_max(
				dal_fixed32_32_div_int(dal_fixed32_32_mul_int(
						scaling_coeff, pix_clk_khz),
						1000),
				dal_fixed32_32_div_int(dal_fixed32_32_mul_int(
						disp_pipe_pix_throughput, 105),
						100));
	}

	if (set_clk && disp_clk_110->ss_on_gpu_pll &&
			disp_clk_110->gpu_pll_ss_divider)
		fx_alt_disp_clk_mhz = dal_fixed32_32_mul(fx_alt_disp_clk_mhz,
				dal_fixed32_32_add_int(
				dal_fixed32_32_div_int(
				dal_fixed32_32_div_int(
				dal_fixed32_32_from_fraction(
				disp_clk_110->gpu_pll_ss_percentage,
				disp_clk_110->gpu_pll_ss_divider), 100),
				2),
				1));

	/* convert to integer */
	disp_clk_khz = dal_fixed32_32_round(
			dal_fixed32_32_mul_int(fx_disp_clk_mhz, 1000));
	alt_disp_clk_khz = dal_fixed32_32_round(
			dal_fixed32_32_mul_int(fx_alt_disp_clk_mhz, 1000));

	if ((disp_clk_khz > max_clk_khz) && (alt_disp_clk_khz <= max_clk_khz))
		disp_clk_khz = alt_disp_clk_khz;

	if (set_clk) { /* only compensate clock if we are going to set it.*/
		disp_clk_khz = get_actual_required_display_clk(
			disp_clk_110, disp_clk_khz);
	}

	disp_clk_khz = disp_clk_khz > max_clk_khz ? max_clk_khz : disp_clk_khz;

	return disp_clk_khz;
}
void dal_clock_source_get_ss_info_from_atombios(
		struct clock_source *clk_src,
		enum as_signal_type as_signal,
		struct spread_spectrum_data *spread_spectrum_data[],
		uint32_t *ss_entries_num)
{
	enum bp_result bp_result = BP_RESULT_FAILURE;
	struct spread_spectrum_info *ss_info;
	struct spread_spectrum_data *ss_data;
	struct spread_spectrum_info *ss_info_cur;
	struct spread_spectrum_data *ss_data_cur;
	uint32_t i;

	if (ss_entries_num == NULL) {
		dal_logger_write(clk_src->dal_ctx->logger,
			LOG_MAJOR_SYNC,
			LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
			"Invalid entry !!!\n");
		return;
	}
	if (spread_spectrum_data == NULL) {
		dal_logger_write(clk_src->dal_ctx->logger,
			LOG_MAJOR_SYNC,
			LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
			"Invalid array pointer!!!\n");
		return;
	}

	spread_spectrum_data[0] = NULL;
	*ss_entries_num = 0;

	*ss_entries_num = dal_bios_parser_get_ss_entry_number(
			clk_src->bios_parser,
			as_signal);
	if (*ss_entries_num == 0)
		return;

	ss_info = dal_alloc(sizeof(struct spread_spectrum_info)
				* (*ss_entries_num));
	ss_info_cur = ss_info;
	if (ss_info == NULL)
		return;

	ss_data = dal_alloc(sizeof(struct spread_spectrum_data) *
							(*ss_entries_num));
	if (ss_data == NULL)
		goto out_free_info;

	for (i = 0, ss_info_cur = ss_info;
		i < (*ss_entries_num);
		++i, ++ss_info_cur) {
		bp_result = dal_bios_parser_get_spread_spectrum_info(
				clk_src->bios_parser,
				as_signal,
				i,
				ss_info_cur);
		if (bp_result != BP_RESULT_OK)
			goto out_free_data;
	}

	for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data;
		i < (*ss_entries_num);
		++i, ++ss_info_cur, ++ss_data_cur) {

		if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) {
			dal_logger_write(clk_src->dal_ctx->logger,
				LOG_MAJOR_SYNC,
				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
				"Invalid ATOMBIOS SS Table!!!\n");
			goto out_free_data;
		}

		/* for HDMI check SS percentage,
		 * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/
		if (as_signal == AS_SIGNAL_TYPE_HDMI
				&& ss_info_cur->spread_spectrum_percentage > 6){
			/* invalid input, do nothing */
			dal_logger_write(clk_src->dal_ctx->logger,
				LOG_MAJOR_SYNC,
				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
				"Invalid SS percentage ");
			dal_logger_write(clk_src->dal_ctx->logger,
				LOG_MAJOR_SYNC,
				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
				"for HDMI in ATOMBIOS info Table!!!\n");
			continue;
		}
		if (ss_info_cur->spread_percentage_divider == 1000) {
			/* Keep previous precision from ATOMBIOS for these
			* in case new precision set by ATOMBIOS for these
			* (otherwise all code in DCE specific classes
			* for all previous ASICs would need
			* to be updated for SS calculations,
			* Audio SS compensation and DP DTO SS compensation
			* which assumes fixed SS percentage Divider = 100)*/
			ss_info_cur->spread_spectrum_percentage /= 10;
			ss_info_cur->spread_percentage_divider = 100;
		}

		ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range;
		ss_data_cur->percentage =
				ss_info_cur->spread_spectrum_percentage;
		ss_data_cur->percentage_divider =
				ss_info_cur->spread_percentage_divider;
		ss_data_cur->modulation_freq_hz =
				ss_info_cur->spread_spectrum_range;

		if (ss_info_cur->type.CENTER_MODE)
			ss_data_cur->flags.CENTER_SPREAD = 1;

		if (ss_info_cur->type.EXTERNAL)
			ss_data_cur->flags.EXTERNAL_SS = 1;

	}

	*spread_spectrum_data = ss_data;
	dal_free(ss_info);
	return;

out_free_data:
	dal_free(ss_data);
	*ss_entries_num = 0;
out_free_info:
	dal_free(ss_info);
}
static bool dce110_transform_v_set_scaler(
	struct transform *xfm,
	const struct scaler_data *data)
{
	struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm);
	bool is_scaling_required = false;
	bool filter_updated = false;
	struct rect luma_viewport = {0};
	struct rect chroma_viewport = {0};
	struct dc_context *ctx = xfm->ctx;

	/* 1. Calculate viewport, viewport programming should happen after init
	 * calculations as they may require an adjustment in the viewport.
	 */

	calculate_viewport(data, &luma_viewport, &chroma_viewport);

	/* 2. Program overscan */
	program_overscan(xfm110, &data->overscan);

	/* 3. Program taps and configuration */
	is_scaling_required = setup_scaling_configuration(xfm110, data);

	if (is_scaling_required) {
		/* 4. Calculate and program ratio, filter initialization */

		struct sclv_ratios_inits inits = { 0 };

		calculate_inits(
			xfm110,
			data,
			&inits,
			&luma_viewport,
			&chroma_viewport);

		program_scl_ratios_inits(xfm110, &inits);

		/*scaler coeff of 2-TAPS use hardware auto calculated value*/

		/* 5. Program vertical filters */
		if (data->taps.v_taps > 2) {
			program_two_taps_filter_vert(xfm110, false);

			if (!program_multi_taps_filter(xfm110, data, false)) {
				dal_logger_write(ctx->logger,
					LOG_MAJOR_DCP,
					LOG_MINOR_DCP_SCALER,
					"Failed vertical taps programming\n");
				return false;
			}
			filter_updated = true;
		} else
			program_two_taps_filter_vert(xfm110, true);

		/* 6. Program horizontal filters */
		if (data->taps.h_taps > 2) {
			program_two_taps_filter_horz(xfm110, false);

			if (!program_multi_taps_filter(xfm110, data, true)) {
				dal_logger_write(ctx->logger,
					LOG_MAJOR_DCP,
					LOG_MINOR_DCP_SCALER,
					"Failed horizontal taps programming\n");
				return false;
			}
			filter_updated = true;
		} else
			program_two_taps_filter_horz(xfm110, true);
	}

	/* 7. Program the viewport */
	program_viewport(xfm110, &luma_viewport, &chroma_viewport);

	/* 8. Set bit to flip to new coefficient memory */
	if (filter_updated)
		set_coeff_update_complete(xfm110);

	return true;
}
static bool construct(
	struct hw_ctx_audio_dce110 *hw_ctx,
	uint8_t azalia_stream_id,
	struct dal_context *dal_context)
{
	struct hw_ctx_audio *base = &hw_ctx->base;

	if (!dal_audio_construct_hw_ctx_audio(base))
		return false;

	base->funcs = &funcs;

	/* save audio endpoint or dig front for current dce110 audio object */
	hw_ctx->azalia_stream_id = azalia_stream_id;
	hw_ctx->base.ctx = dal_context;

	/* azalia audio endpoints register offsets. azalia is associated with
	DIG front. save AUDIO register offset */
	switch (azalia_stream_id) {
	case 1: {
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_index =
			mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX;
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_data =
			mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA;
		}
		break;
	case 2: {
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_index =
			mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_INDEX;
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_data =
			mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_DATA;
		}
		break;
	case 3: {
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_index =
			mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_INDEX;
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_data =
			mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_DATA;
		}
		break;
	case 4: {
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_index =
			mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_INDEX;
			hw_ctx->az_mm_reg_offsets.
			azf0endpointx_azalia_f0_codec_endpoint_data =
			mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_DATA;
		}
		break;
	default:
		dal_logger_write(
			hw_ctx->base.ctx->logger,
			LOG_MAJOR_WARNING,
			LOG_MINOR_COMPONENT_AUDIO,
			"Invalid Azalia stream ID!");
		break;
	}

	return true;
}
static bool construct(
	struct i2c_hw_engine_dce110 *engine_dce110,
	const struct i2c_hw_engine_dce110_create_arg *arg)
{
	uint32_t xtal_ref_div = 0;
	uint32_t value = 0;

	/*ddc_setup_offset of dce80 and dce110 have the same register name
	 * but different offset. Do not need different array*/
	if (arg->engine_id >= sizeof(ddc_setup_offset) / sizeof(int32_t))
		return false;
	if (arg->engine_id >= sizeof(ddc_speed_offset) / sizeof(int32_t))
		return false;
	if (!arg->reference_frequency)
		return false;

	if (!dal_i2c_hw_engine_construct(&engine_dce110->base, arg->ctx))
		return false;

	engine_dce110->base.base.base.funcs = &engine_funcs;
	engine_dce110->base.base.funcs = &i2c_engine_funcs;
	engine_dce110->base.funcs = &i2c_hw_engine_funcs;
	engine_dce110->base.default_speed = arg->default_speed;

	engine_dce110->engine_id = arg->engine_id;

	engine_dce110->buffer_used_bytes = 0;
	engine_dce110->transaction_count = 0;
	engine_dce110->engine_keep_power_up_count = 1;

	/*values which are not included by arg*/
	engine_dce110->addr.DC_I2C_DDCX_SETUP =
		mmDC_I2C_DDC1_SETUP + ddc_setup_offset[arg->engine_id];
	engine_dce110->addr.DC_I2C_DDCX_SPEED =
		mmDC_I2C_DDC1_SPEED + ddc_speed_offset[arg->engine_id];


	value = dal_read_reg(
		engine_dce110->base.base.base.ctx,
		mmMICROSECOND_TIME_BASE_DIV);

	xtal_ref_div = get_reg_field_value(
			value,
			MICROSECOND_TIME_BASE_DIV,
			XTAL_REF_DIV);

	if (xtal_ref_div == 0) {
		dal_logger_write(
				engine_dce110->base.base.base.ctx->logger,
				LOG_MAJOR_WARNING,
				LOG_MINOR_COMPONENT_I2C_AUX,
				"Invalid base timer divider\n",
				__func__);
		xtal_ref_div = 2;
	}

	/*Calculating Reference Clock by divding original frequency by
	 * XTAL_REF_DIV.
	 * At upper level, uint32_t reference_frequency =
	 *  dal_i2caux_get_reference_clock(as) >> 1
	 *  which already divided by 2. So we need x2 to get original
	 *  reference clock from ppll_info
	 */
	engine_dce110->reference_frequency =
		(arg->reference_frequency * 2) / xtal_ref_div;


	return true;
}
/*
 * Initializes asic_capability instance.
 */
static bool construct(
	struct asic_capability *cap,
	struct hw_asic_id *init,
	struct dc_context *ctx)
{
	bool asic_supported = false;

	cap->ctx = ctx;
	memset(cap->data, 0, sizeof(cap->data));

	/* ASIC data */
	cap->data[ASIC_DATA_VRAM_TYPE] = init->vram_type;
	cap->data[ASIC_DATA_VRAM_BITWIDTH] = init->vram_width;
	cap->data[ASIC_DATA_FEATURE_FLAGS] = init->feature_flags;
	cap->runtime_flags = init->runtime_flags;
	cap->data[ASIC_DATA_REVISION_ID] = init->hw_internal_rev;
	cap->data[ASIC_DATA_MAX_UNDERSCAN_PERCENTAGE] = 10;
	cap->data[ASIC_DATA_VIEWPORT_PIXEL_GRANULARITY] = 4;
	cap->data[ASIC_DATA_SUPPORTED_HDMI_CONNECTION_NUM] = 1;
	cap->data[ASIC_DATA_NUM_OF_VIDEO_PLANES] = 0;
	cap->data[ASIC_DATA_DEFAULT_I2C_SPEED_IN_KHZ] = 25;

	/* ASIC basic capability */
	cap->caps.UNDERSCAN_FOR_HDMI_ONLY = true;
	cap->caps.SUPPORT_CEA861E_FINAL = true;
	cap->caps.MIRABILIS_SUPPORTED = false;
	cap->caps.MIRABILIS_ENABLED_BY_DEFAULT = false;
	cap->caps.WIRELESS_LIMIT_TO_720P = false;
	cap->caps.WIRELESS_FULL_TIMING_ADJUSTMENT = false;
	cap->caps.WIRELESS_TIMING_ADJUSTMENT = true;
	cap->caps.WIRELESS_COMPRESSED_AUDIO = false;
	cap->caps.VCE_SUPPORTED = false;
	cap->caps.HPD_CHECK_FOR_EDID = false;
	cap->caps.NO_VCC_OFF_HPD_POLLING = false;
	cap->caps.NEED_MC_TUNING = false;
	cap->caps.SUPPORT_8BPP = true;

	/* ASIC stereo 3D capability */
	cap->stereo_3d_caps.SUPPORTED = true;

	switch (init->chip_family) {
	case FAMILY_CI:
#if defined(CONFIG_DRM_AMD_DAL_DCE8_0)
		dal_hawaii_asic_capability_create(cap, init);
		asic_supported = true;
#endif
		break;

	case FAMILY_KV:
		if (ASIC_REV_IS_KALINDI(init->hw_internal_rev) ||
			ASIC_REV_IS_BHAVANI(init->hw_internal_rev)) {
		} else {
		}
		break;

	case FAMILY_CZ:
#if defined(CONFIG_DRM_AMD_DAL_DCE11_0)
		carrizo_asic_capability_create(cap, init);
		asic_supported = true;
#endif
		break;

	case FAMILY_VI:
#if defined(CONFIG_DRM_AMD_DAL_DCE10_0)
		if (ASIC_REV_IS_TONGA_P(init->hw_internal_rev) ||
				ASIC_REV_IS_FIJI_P(init->hw_internal_rev)) {
			tonga_asic_capability_create(cap, init);
			asic_supported = true;
			break;
		}
#endif
#if defined(CONFIG_DRM_AMD_DAL_DCE11_2)
		if (ASIC_REV_IS_POLARIS10_P(init->hw_internal_rev) ||
				ASIC_REV_IS_POLARIS11_M(init->hw_internal_rev)) {
			polaris10_asic_capability_create(cap, init);
			asic_supported = true;
		}
#endif
		break;

	default:
		/* unsupported "chip_family" */
		break;
	}

	if (false == asic_supported) {
		dal_logger_write(ctx->logger,
			LOG_MAJOR_ERROR,
			LOG_MINOR_MASK_ALL,
			"%s: ASIC not supported!\n", __func__);
	}

	return asic_supported;
}