/* disable Azalia audio */
static void disable_azalia_audio(
	const struct hw_ctx_audio *hw_ctx,
	enum engine_id engine_id)
{
	uint32_t value;

	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL);

	set_reg_field_value(value, 0,
		AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL,
		AUDIO_ENABLED);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL,
		value);
}
/* expose/not expose HBR capability to Audio driver */
static void set_high_bit_rate_capable(
	const struct hw_ctx_audio *hw_ctx,
	bool capable)
{
	uint32_t value = 0;

	/* set high bit rate audio capable*/
	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR);

	set_reg_field_value(value, capable,
		AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR,
		HBR_CAPABLE);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR,
		value);
}
 /* Disable GTC value embedding */
static void disable_gtc_embedding(
	const struct hw_ctx_audio *hw_ctx)
{
	uint32_t value = 0;

	value = read_indirect_azalia_reg(
	hw_ctx,
	ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING);

	set_reg_field_value(value, 0,
	AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
	PRESENTATION_TIME_EMBEDDING_ENABLE);

	set_reg_field_value(value, 0,
	AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
	PRESENTATION_TIME_EMBEDDING_GROUP);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING,
		value);
}
/* 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);
	}
}
/* set video latency in in ms/2+1 */
static void set_video_latency(
	const struct hw_ctx_audio *hw_ctx,
	int latency_in_ms)
{
	uint32_t value = 0;

	if ((latency_in_ms < 0) || (latency_in_ms > 255))
		return;

	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC);

	set_reg_field_value(value, latency_in_ms,
		AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC,
		VIDEO_LIPSYNC);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC,
		value);

}
/* 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 configure_azalia(
	const struct hw_ctx_audio *hw_ctx,
	enum signal_type signal,
	const struct audio_crtc_info *crtc_info,
	const struct audio_info *audio_info)
{
	uint32_t speakers = audio_info->flags.info.ALLSPEAKERS;
	uint32_t value;
	uint32_t field = 0;
	enum audio_format_code audio_format_code;
	uint32_t format_index;
	uint32_t index;
	bool is_ac3_supported = false;
	bool is_audio_format_supported = false;
	union audio_sample_rates sample_rate;
	uint32_t strlen = 0;

	/* Speaker Allocation */
	/*
	uint32_t value;
	uint32_t field = 0;*/
	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER);

	set_reg_field_value(value,
		speakers,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		SPEAKER_ALLOCATION);

	/* LFE_PLAYBACK_LEVEL = LFEPBL
	 * LFEPBL = 0 : Unknown or refer to other information
	 * LFEPBL = 1 : 0dB playback
	 * LFEPBL = 2 : +10dB playback
	 * LFE_BL = 3 : Reserved
	 */
	set_reg_field_value(value,
		0,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		LFE_PLAYBACK_LEVEL);

	set_reg_field_value(value,
		0,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		HDMI_CONNECTION);

	set_reg_field_value(value,
		0,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		DP_CONNECTION);

	field = get_reg_field_value(value,
			AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
			EXTRA_CONNECTION_INFO);

	field &= ~0x1;

	set_reg_field_value(value,
		field,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		EXTRA_CONNECTION_INFO);

	/* set audio for output signal */
	switch (signal) {
	case SIGNAL_TYPE_HDMI_TYPE_A:
		set_reg_field_value(value,
			1,
			AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
			HDMI_CONNECTION);

		break;
	case SIGNAL_TYPE_WIRELESS: {
		/*LSB used for "is wireless" flag */
		field = 0;
		field = get_reg_field_value(value,
		AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		EXTRA_CONNECTION_INFO);
		field |= 0x1;
		set_reg_field_value(value,
			field,
			AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
			EXTRA_CONNECTION_INFO);

		set_reg_field_value(value,
			1,
			AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
			HDMI_CONNECTION);

		}
		break;
	case SIGNAL_TYPE_EDP:
	case SIGNAL_TYPE_DISPLAY_PORT:
	case SIGNAL_TYPE_DISPLAY_PORT_MST:
		set_reg_field_value(value,
			1,
			AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
			DP_CONNECTION);

		break;
	default:
		break;
	}

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
		value);

	/* Wireless Display identification */
	value = read_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION);

	set_reg_field_value(value,
		signal == SIGNAL_TYPE_WIRELESS ? 1 : 0,
		AZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION,
		WIRELESS_DISPLAY_IDENTIFICATION);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION,
		value);


	/*  Audio Descriptors   */
	/* pass through all formats */
	for (format_index = 0; format_index < AUDIO_FORMAT_CODE_COUNT;
			format_index++) {
		audio_format_code =
			(AUDIO_FORMAT_CODE_FIRST + format_index);

		/* those are unsupported, skip programming */
		if (audio_format_code == AUDIO_FORMAT_CODE_1BITAUDIO ||
			audio_format_code == AUDIO_FORMAT_CODE_DST)
			continue;

		value = 0;

		/* check if supported */
		is_audio_format_supported =
			dal_audio_hw_ctx_is_audio_format_supported(
				hw_ctx,
				audio_info,
				audio_format_code, &index);

		if (is_audio_format_supported) {
			const struct audio_mode *audio_mode =
					&audio_info->modes[index];
			union audio_sample_rates sample_rates =
					audio_mode->sample_rates;
			uint8_t byte2 = audio_mode->max_bit_rate;

			/* adjust specific properties */
			switch (audio_format_code) {
			case AUDIO_FORMAT_CODE_LINEARPCM: {
				dal_hw_ctx_audio_check_audio_bandwidth(
					hw_ctx,
					crtc_info,
					audio_mode->channel_count,
					signal,
					&sample_rates);

				byte2 = audio_mode->sample_size;

				set_reg_field_value(value,
				sample_rates.all,
		AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0,
				SUPPORTED_FREQUENCIES_STEREO);

				}
				break;
			case AUDIO_FORMAT_CODE_AC3:
				is_ac3_supported = true;
				break;
			case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS:
			case AUDIO_FORMAT_CODE_DTS_HD:
			case AUDIO_FORMAT_CODE_MAT_MLP:
			case AUDIO_FORMAT_CODE_DST:
			case AUDIO_FORMAT_CODE_WMAPRO:
				byte2 = audio_mode->vendor_specific;
				break;
			default:
				break;
			}

			/* fill audio format data */
			set_reg_field_value(value,
			audio_mode->channel_count - 1,
			AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0,
			MAX_CHANNELS);

			set_reg_field_value(value,
			sample_rates.all,
			AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0,
			SUPPORTED_FREQUENCIES);

			set_reg_field_value(value,
			byte2,
			AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0,
			DESCRIPTOR_BYTE_2);

		} /* if */

		write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 +
		format_index,
		value);
	} /* for */

	if (is_ac3_supported)
		dal_write_reg(hw_ctx->ctx,
		mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS,
		0x05);

	/* check for 192khz/8-Ch support for HBR requirements */
	sample_rate.all = 0;
	sample_rate.rate.RATE_192 = 1;
	dal_hw_ctx_audio_check_audio_bandwidth(
		hw_ctx,
		crtc_info,
		8,
		signal,
		&sample_rate);

	set_high_bit_rate_capable(hw_ctx, sample_rate.rate.RATE_192);

	/* Audio and Video Lipsync */
	set_video_latency(hw_ctx, audio_info->video_latency);
	set_audio_latency(hw_ctx, audio_info->audio_latency);

	value = 0;
	set_reg_field_value(value, audio_info->manufacture_id,
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0,
		MANUFACTURER_ID);

	set_reg_field_value(value, audio_info->product_id,
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0,
		PRODUCT_ID);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0,
		value);


	value = 0;

	/*get display name string length */
	while (audio_info->display_name[strlen++] != '\0') {
		if (strlen >=
		MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS)
			break;
		}
	set_reg_field_value(value, strlen,
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1,
		SINK_DESCRIPTION_LEN);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1,
		value);


	/*
	*write the port ID:
	*PORT_ID0 = display index
	*PORT_ID1 = 16bit BDF
	*(format MSB->LSB: 8bit Bus, 5bit Device, 3bit Function)
	*/

	value = 0;

	set_reg_field_value(value, audio_info->port_id[0],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2,
		PORT_ID0);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2,
		value);

	value = 0;
	set_reg_field_value(value, audio_info->port_id[1],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3,
		PORT_ID1);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3,
		value);

	/*write the 18 char monitor string */

	value = 0;
	set_reg_field_value(value, audio_info->display_name[0],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4,
		DESCRIPTION0);

	set_reg_field_value(value, audio_info->display_name[1],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4,
		DESCRIPTION1);

	set_reg_field_value(value, audio_info->display_name[2],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4,
		DESCRIPTION2);

	set_reg_field_value(value, audio_info->display_name[3],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4,
		DESCRIPTION3);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4,
		value);


	value = 0;
	set_reg_field_value(value, audio_info->display_name[4],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5,
		DESCRIPTION4);

	set_reg_field_value(value, audio_info->display_name[5],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5,
		DESCRIPTION5);

	set_reg_field_value(value, audio_info->display_name[6],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5,
		DESCRIPTION6);

	set_reg_field_value(value, audio_info->display_name[7],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5,
		DESCRIPTION7);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5,
		value);

	value = 0;
	set_reg_field_value(value, audio_info->display_name[8],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6,
		DESCRIPTION8);

	set_reg_field_value(value, audio_info->display_name[9],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6,
		DESCRIPTION9);

	set_reg_field_value(value, audio_info->display_name[10],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6,
		DESCRIPTION10);

	set_reg_field_value(value, audio_info->display_name[11],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6,
		DESCRIPTION11);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6,
		value);

	value = 0;
	set_reg_field_value(value, audio_info->display_name[12],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7,
		DESCRIPTION12);

	set_reg_field_value(value, audio_info->display_name[13],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7,
		DESCRIPTION13);

	set_reg_field_value(value, audio_info->display_name[14],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7,
		DESCRIPTION14);

	set_reg_field_value(value, audio_info->display_name[15],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7,
		DESCRIPTION15);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7,
		value);


	value = 0;
	set_reg_field_value(value, audio_info->display_name[16],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8,
		DESCRIPTION16);

	set_reg_field_value(value, audio_info->display_name[17],
		AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8,
		DESCRIPTION17);

	write_indirect_azalia_reg(
		hw_ctx,
		ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8,
		value);

}