Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bdc080d1 authored by Ralf Herz's avatar Ralf Herz
Browse files

asoc: qcs405: Add support for AFE SPDIF interfaces



Add support for AFE SPDIF input and output interfaces for QCS405 platform

Change-Id: Ieacd0524046376f237b325d6dcab071c80a4b657
Signed-off-by: default avatarRalf Herz <rherz@codeaurora.org>
parent 2257355f
Loading
Loading
Loading
Loading
+669 −1
Original line number Diff line number Diff line
@@ -61,6 +61,10 @@
#define SAMPLING_RATE_352P8KHZ  352800
#define SAMPLING_RATE_384KHZ    384000

#define SPDIF_TX_CORE_CLK_204_P8_MHZ  204800000
#define TLMM_EAST_SPARE 0x07BA0000
#define TLMM_SPDIF_HDMI_ARC_CTL 0x07BA2000

#define WSA8810_NAME_1 "wsa881x.20170211"
#define WSA8810_NAME_2 "wsa881x.20170212"
#define WCN_CDC_SLIM_RX_CH_MAX 2
@@ -128,6 +132,18 @@ enum {
	CDC_DMA_TX_MAX,
};

enum {
	PRIM_SPDIF_RX = 0,
	SEC_SPDIF_RX,
	SPDIF_RX_MAX,
};

enum {
	PRIM_SPDIF_TX = 0,
	SEC_SPDIF_TX,
	SPDIF_TX_MAX,
};

struct mi2s_conf {
	struct mutex lock;
	u32 ref_cnt;
@@ -396,6 +412,17 @@ static struct dev_config mi2s_rx_cfg[] = {
	[QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
};

/* Default configuration of SPDIF channels */
static struct dev_config spdif_rx_cfg[] = {
	[PRIM_SPDIF_RX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
	[SEC_SPDIF_RX]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
};

static struct dev_config spdif_tx_cfg[] = {
	[PRIM_SPDIF_TX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
	[SEC_SPDIF_TX]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
};

static struct dev_config mi2s_tx_cfg[] = {
	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
@@ -470,6 +497,12 @@ static char const *cdc_dma_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
					"KHZ_192", "KHZ_352P8", "KHZ_384"};

static const char *spdif_rate_text[] = {"KHZ_32", "KHZ_44P1", "KHZ_48",
					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
					"KHZ_192"};
static const char *spdif_ch_text[] = {"One", "Two"};
static const char *spdif_bit_format_text[] = {"S16_LE", "S24_LE"};

static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
@@ -561,6 +594,12 @@ static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_0_sample_rate,
				cdc_dma_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_1_sample_rate,
				cdc_dma_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_sample_rate, spdif_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_sample_rate, spdif_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_chs, spdif_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_chs, spdif_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_format, spdif_bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_format, spdif_bit_format_text);

static struct platform_device *spdev;

@@ -2904,6 +2943,337 @@ static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol,
	return 0;
}

static int spdif_get_port_idx(struct snd_kcontrol *kcontrol)
{
	int idx;

	if (strnstr(kcontrol->id.name, "PRIM_SPDIF_RX",
	    sizeof("PRIM_SPDIF_RX")))
		idx = PRIM_SPDIF_RX;
	else if (strnstr(kcontrol->id.name, "SEC_SPDIF_RX",
		 sizeof("SEC_SPDIF_RX")))
		idx = SEC_SPDIF_RX;
	else if (strnstr(kcontrol->id.name, "PRIM_SPDIF_TX",
		 sizeof("PRIM_SPDIF_TX")))
		idx = PRIM_SPDIF_TX;
	else if (strnstr(kcontrol->id.name, "SEC_SPDIF_TX",
		 sizeof("SEC_SPDIF_TX")))
		idx = SEC_SPDIF_TX;
	else {
		pr_err("%s: unsupported channel: %s",
			__func__, kcontrol->id.name);
		idx = -EINVAL;
	}

	return idx;
}

static int spdif_get_sample_rate_val(int sample_rate)
{
	int sample_rate_val;

	switch (sample_rate) {
	case SAMPLING_RATE_32KHZ:
		sample_rate_val = 0;
		break;
	case SAMPLING_RATE_44P1KHZ:
		sample_rate_val = 1;
		break;
	case SAMPLING_RATE_48KHZ:
		sample_rate_val = 2;
		break;
	case SAMPLING_RATE_88P2KHZ:
		sample_rate_val = 3;
		break;
	case SAMPLING_RATE_96KHZ:
		sample_rate_val = 4;
		break;
	case SAMPLING_RATE_176P4KHZ:
		sample_rate_val = 5;
		break;
	case SAMPLING_RATE_192KHZ:
		sample_rate_val = 6;
		break;
	default:
		sample_rate_val = 2;
		break;
	}
	return sample_rate_val;
}

static int spdif_get_sample_rate(int value)
{
	int sample_rate;

	switch (value) {
	case 0:
		sample_rate = SAMPLING_RATE_32KHZ;
		break;
	case 1:
		sample_rate = SAMPLING_RATE_44P1KHZ;
		break;
	case 2:
		sample_rate = SAMPLING_RATE_48KHZ;
		break;
	case 3:
		sample_rate = SAMPLING_RATE_88P2KHZ;
		break;
	case 4:
		sample_rate = SAMPLING_RATE_96KHZ;
		break;
	case 5:
		sample_rate = SAMPLING_RATE_176P4KHZ;
		break;
	case 6:
		sample_rate = SAMPLING_RATE_192KHZ;
		break;
	default:
		sample_rate = SAMPLING_RATE_48KHZ;
		break;
	}
	return sample_rate;
}

static int spdif_get_format(int value)
{
	int format;

	switch (value) {
	case 0:
		format = SNDRV_PCM_FORMAT_S16_LE;
		break;
	case 1:
		format = SNDRV_PCM_FORMAT_S24_LE;
		break;
	default:
		format = SNDRV_PCM_FORMAT_S16_LE;
		break;
	}
	return format;
}

static int spdif_get_format_value(int format)
{
	int value;

	switch (format) {
	case SNDRV_PCM_FORMAT_S16_LE:
		value = 0;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		value = 1;
		break;
	default:
		value = 0;
		break;
	}
	return value;
}

static int msm_spdif_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_rx_cfg[idx].sample_rate =
		spdif_get_sample_rate(ucontrol->value.enumerated.item[0]);

	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
		 idx, spdif_rx_cfg[idx].sample_rate,
		 ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	ucontrol->value.enumerated.item[0] =
		spdif_get_sample_rate_val(spdif_rx_cfg[idx].sample_rate);

	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
		 idx, spdif_rx_cfg[idx].sample_rate,
		 ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_tx_cfg[idx].sample_rate =
		spdif_get_sample_rate(ucontrol->value.enumerated.item[0]);

	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
		 idx, spdif_tx_cfg[idx].sample_rate,
		 ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	ucontrol->value.enumerated.item[0] =
		spdif_get_sample_rate_val(spdif_tx_cfg[idx].sample_rate);

	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
		 idx, spdif_tx_cfg[idx].sample_rate,
		 ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_rx_ch_get(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	pr_debug("%s: msm_spdif_[%d]_rx_ch  = %d\n", __func__,
		 idx, spdif_rx_cfg[idx].channels);
	ucontrol->value.enumerated.item[0] = spdif_rx_cfg[idx].channels - 1;

	return 0;
}

static int msm_spdif_rx_ch_put(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
	pr_debug("%s: msm_spdif_[%d]_rx_ch  = %d\n", __func__,
		 idx, spdif_rx_cfg[idx].channels);

	return 1;
}

static int msm_spdif_tx_ch_get(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	pr_debug("%s: msm_spdif_[%d]_tx_ch  = %d\n", __func__,
		 idx, spdif_tx_cfg[idx].channels);
	ucontrol->value.enumerated.item[0] = spdif_tx_cfg[idx].channels - 1;

	return 0;
}

static int msm_spdif_tx_ch_put(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
	pr_debug("%s: msm_spdif_[%d]_tx_ch  = %d\n", __func__,
		 idx, spdif_tx_cfg[idx].channels);

	return 1;
}

static int msm_spdif_rx_format_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	ucontrol->value.enumerated.item[0] =
		spdif_get_format_value(spdif_rx_cfg[idx].bit_format);

	pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
		idx, spdif_rx_cfg[idx].bit_format,
		ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_rx_format_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_rx_cfg[idx].bit_format =
		spdif_get_format(ucontrol->value.enumerated.item[0]);

	pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
		  idx, spdif_rx_cfg[idx].bit_format,
		  ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_tx_format_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	ucontrol->value.enumerated.item[0] =
		spdif_get_format_value(spdif_tx_cfg[idx].bit_format);

	pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
		idx, spdif_tx_cfg[idx].bit_format,
		ucontrol->value.enumerated.item[0]);

	return 0;
}

static int msm_spdif_tx_format_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int idx = spdif_get_port_idx(kcontrol);

	if (idx < 0)
		return idx;

	spdif_tx_cfg[idx].bit_format =
		spdif_get_format(ucontrol->value.enumerated.item[0]);

	pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
		  idx, spdif_tx_cfg[idx].bit_format,
		  ucontrol->value.enumerated.item[0]);

	return 0;
}

static const struct snd_kcontrol_new msm_snd_sb_controls[] = {
	SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
			slim_rx_ch_get, slim_rx_ch_put),
@@ -3231,6 +3601,34 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
			msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put),
	SOC_SINGLE_MULTI_EXT("VAD CFG", SND_SOC_NOPM, 0, 1000, 0, 3, NULL,
				msm_snd_vad_cfg_put),
	SOC_ENUM_EXT("PRIM_SPDIF_RX SampleRate", spdif_rx_sample_rate,
			msm_spdif_rx_sample_rate_get,
			msm_spdif_rx_sample_rate_put),
	SOC_ENUM_EXT("PRIM_SPDIF_TX SampleRate", spdif_tx_sample_rate,
			msm_spdif_tx_sample_rate_get,
			msm_spdif_tx_sample_rate_put),
	SOC_ENUM_EXT("SEC_SPDIF_RX SampleRate", spdif_rx_sample_rate,
			msm_spdif_rx_sample_rate_get,
			msm_spdif_rx_sample_rate_put),
	SOC_ENUM_EXT("SEC_SPDIF_TX SampleRate", spdif_tx_sample_rate,
			msm_spdif_tx_sample_rate_get,
			msm_spdif_tx_sample_rate_put),
	SOC_ENUM_EXT("PRIM_SPDIF_RX Channels", spdif_rx_chs,
			msm_spdif_rx_ch_get, msm_spdif_rx_ch_put),
	SOC_ENUM_EXT("PRIM_SPDIF_TX Channels", spdif_tx_chs,
			msm_spdif_tx_ch_get, msm_spdif_tx_ch_put),
	SOC_ENUM_EXT("SEC_SPDIF_RX Channels", spdif_rx_chs,
			msm_spdif_rx_ch_get, msm_spdif_rx_ch_put),
	SOC_ENUM_EXT("SEC_SPDIF_TX Channels", spdif_tx_chs,
			msm_spdif_tx_ch_get, msm_spdif_tx_ch_put),
	SOC_ENUM_EXT("PRIM_SPDIF_RX Format", spdif_rx_format,
			msm_spdif_rx_format_get, msm_spdif_rx_format_put),
	SOC_ENUM_EXT("PRIM_SPDIF_TX Format", spdif_tx_format,
			msm_spdif_tx_format_get, msm_spdif_tx_format_put),
	SOC_ENUM_EXT("SEC_SPDIF_RX Format", spdif_rx_format,
			msm_spdif_rx_format_get, msm_spdif_rx_format_put),
	SOC_ENUM_EXT("SEC_SPDIF_TX Format", spdif_tx_format,
			msm_spdif_tx_format_get, msm_spdif_tx_format_put),
};

static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
@@ -3929,6 +4327,42 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
		channels->min = channels->max = msm_vi_feed_tx_ch;
		break;

	case MSM_BACKEND_DAI_PRI_SPDIF_RX:
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			spdif_rx_cfg[PRIM_SPDIF_RX].bit_format);
		rate->min = rate->max =
				spdif_rx_cfg[PRIM_SPDIF_RX].sample_rate;
		channels->min = channels->max =
			spdif_rx_cfg[PRIM_SPDIF_RX].channels;
		break;

	case MSM_BACKEND_DAI_PRI_SPDIF_TX:
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			spdif_tx_cfg[PRIM_SPDIF_TX].bit_format);
		rate->min = rate->max =
				spdif_tx_cfg[PRIM_SPDIF_TX].sample_rate;
		channels->min = channels->max =
			spdif_tx_cfg[PRIM_SPDIF_TX].channels;
		break;

	case MSM_BACKEND_DAI_SEC_SPDIF_RX:
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			spdif_rx_cfg[SEC_SPDIF_RX].bit_format);
		rate->min = rate->max =
				spdif_rx_cfg[SEC_SPDIF_RX].sample_rate;
		channels->min = channels->max =
			spdif_rx_cfg[SEC_SPDIF_RX].channels;
		break;

	case MSM_BACKEND_DAI_SEC_SPDIF_TX:
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			spdif_tx_cfg[SEC_SPDIF_TX].bit_format);
		rate->min = rate->max =
				spdif_tx_cfg[SEC_SPDIF_TX].sample_rate;
		channels->min = channels->max =
			spdif_tx_cfg[SEC_SPDIF_TX].channels;
	break;

	default:
		rate->min = rate->max = SAMPLING_RATE_48KHZ;
		break;
@@ -5115,6 +5549,136 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
	mutex_unlock(&mi2s_intf_conf[index].lock);
}

static int msm_spdif_set_clk(struct snd_pcm_substream *substream, bool enable)
{
	int ret = 0;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	int port_id = cpu_dai->id;
	struct afe_clk_set clk_cfg;

	clk_cfg.clk_set_minor_version = Q6AFE_LPASS_CLK_CONFIG_API_VERSION;
	clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
	clk_cfg.clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT;
	clk_cfg.enable = enable;

	/* Set core clock (based on sample rate for RX, fixed for TX) */
	switch (port_id) {
	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE;
		/* rate x 2ch x 2_for_biphase_coding x 32_bits_per_sample */
		clk_cfg.clk_freq_in_hz =
			spdif_rx_cfg[PRIM_SPDIF_RX].sample_rate * 2 * 2 * 32;
		break;
	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE;
		clk_cfg.clk_freq_in_hz =
			spdif_rx_cfg[SEC_SPDIF_RX].sample_rate * 2 * 2 * 32;
		break;
	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE;
		clk_cfg.clk_freq_in_hz = SPDIF_TX_CORE_CLK_204_P8_MHZ;
		break;
	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE;
		clk_cfg.clk_freq_in_hz = SPDIF_TX_CORE_CLK_204_P8_MHZ;
		break;
	}

	ret = afe_set_lpass_clock_v2(port_id, &clk_cfg);
	if (ret < 0) {
		dev_err(rtd->card->dev,
			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
			__func__, port_id, ret);
		goto err;
	}

	/* Set NPL clock for RX in addition */
	switch (port_id) {
	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_NPL;

		ret = afe_set_lpass_clock_v2(port_id, &clk_cfg);
		if (ret < 0) {
			dev_err(rtd->card->dev,
				"%s: afe NPL failed port 0x%x, err:%d\n",
				__func__, port_id, ret);
			goto err;
		}
		break;
	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
		clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_NPL;

		ret = afe_set_lpass_clock_v2(port_id, &clk_cfg);
		if (ret < 0) {
			dev_err(rtd->card->dev,
				"%s: afe NPL failed for port 0x%x, err:%d\n",
				__func__, port_id, ret);
			goto err;
		}
		break;
	}

	if (enable) {
		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
				clk_cfg.clk_freq_in_hz);
	}

err:
	return ret;
}

static int msm_spdif_snd_startup(struct snd_pcm_substream *substream)
{
	int ret = 0;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	int port_id = cpu_dai->id;

	dev_dbg(rtd->card->dev,
		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
		__func__, substream->name, substream->stream,
		cpu_dai->name, cpu_dai->id);

	if (port_id < AFE_PORT_ID_PRIMARY_SPDIF_RX ||
	    port_id > AFE_PORT_ID_SECONDARY_SPDIF_TX) {
		ret = -EINVAL;
		dev_err(rtd->card->dev,
			"%s: CPU DAI id (%d) out of range\n",
			__func__, cpu_dai->id);
		goto err;
	}

	ret = msm_spdif_set_clk(substream, true);
	if (ret < 0) {
		dev_err(rtd->card->dev,
			"%s: afe lpass clock failed to enable (%d), err:%d\n",
			__func__, port_id, ret);
	}
err:
	return ret;
}

static void msm_spdif_snd_shutdown(struct snd_pcm_substream *substream)
{
	int ret;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	int port_id = rtd->cpu_dai->id;

	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
		 substream->name, substream->stream);
	if (port_id < AFE_PORT_ID_PRIMARY_SPDIF_RX ||
	    port_id > AFE_PORT_ID_SECONDARY_SPDIF_TX) {
		pr_err("%s:invalid SPDIF DAI(%d)\n", __func__, port_id);
		return;
	}

	ret = msm_spdif_set_clk(substream, false);
	if (ret < 0)
		pr_err("%s:clock disable failed for SPDIF (%d); ret=%d\n",
			__func__, port_id, ret);
}

static struct snd_soc_ops msm_mi2s_be_ops = {
	.startup = msm_mi2s_snd_startup,
	.shutdown = msm_mi2s_snd_shutdown,
@@ -5132,6 +5696,11 @@ static struct snd_soc_ops msm_wcn_ops = {
	.hw_params = msm_wcn_hw_params,
};

static struct snd_soc_ops msm_spdif_be_ops = {
	.startup = msm_spdif_snd_startup,
	.shutdown = msm_spdif_snd_shutdown,
};


/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm_common_dai_links[] = {
@@ -5787,6 +6356,20 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
	{
		.name = MSM_DAILINK_NAME(Compr Capture),
		.stream_name = "Compr Capture",
		.cpu_dai_name = "MultiMedia18",
		.platform_name = "msm-compress-dsp",
		.dynamic = 1,
		.dpcm_capture = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			    SND_SOC_DPCM_TRIGGER_POST},
		.ignore_pmdown_time = 1,
		.id = MSM_FRONTEND_DAI_MULTIMEDIA18,
	},
};

static struct snd_soc_dai_link msm_common_be_dai_links[] = {
@@ -6648,6 +7231,67 @@ static struct snd_soc_dai_link msm_va_cdc_dma_be_dai_links[] = {
	},
};

static struct snd_soc_dai_link msm_spdif_be_dai_links[] = {
	{
		.name = LPASS_BE_PRI_SPDIF_RX,
		.stream_name = "Primary SPDIF Playback",
		.cpu_dai_name = "msm-dai-q6-spdif.20480",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-rx",
		.no_pcm = 1,
		.dpcm_playback = 1,
		.id = MSM_BACKEND_DAI_PRI_SPDIF_RX,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &msm_spdif_be_ops,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
	},
	{
		.name = LPASS_BE_PRI_SPDIF_TX,
		.stream_name = "Primary SPDIF Capture",
		.cpu_dai_name = "msm-dai-q6-spdif.20481",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-tx",
		.no_pcm = 1,
		.dpcm_capture = 1,
		.id = MSM_BACKEND_DAI_PRI_SPDIF_TX,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &msm_spdif_be_ops,
		.ignore_suspend = 1,
	},
	{
		.name = LPASS_BE_SEC_SPDIF_RX,
		.stream_name = "Secondary SPDIF Playback",
		.cpu_dai_name = "msm-dai-q6-spdif.20482",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-rx",
		.no_pcm = 1,
		.dpcm_playback = 1,
		.id = MSM_BACKEND_DAI_SEC_SPDIF_RX,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &msm_spdif_be_ops,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
	},
	{
		.name = LPASS_BE_SEC_SPDIF_TX,
		.stream_name = "Secondary SPDIF Capture",
		.cpu_dai_name = "msm-dai-q6-spdif.20483",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-tx",
		.no_pcm = 1,
		.dpcm_capture = 1,
		.id = MSM_BACKEND_DAI_SEC_SPDIF_TX,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &msm_spdif_be_ops,
		.ignore_suspend = 1,
	},
};

static struct snd_soc_dai_link msm_qcs405_dai_links[
			 ARRAY_SIZE(msm_common_dai_links) +
			 ARRAY_SIZE(msm_common_misc_fe_dai_links) +
@@ -6658,7 +7302,8 @@ static struct snd_soc_dai_link msm_qcs405_dai_links[
			 ARRAY_SIZE(msm_auxpcm_be_dai_links) +
			 ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) +
			 ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links) +
			 ARRAY_SIZE(msm_bolero_fe_dai_links)];
			 ARRAY_SIZE(msm_bolero_fe_dai_links) +
			 ARRAY_SIZE(msm_spdif_be_dai_links)];

static int msm_snd_card_tasha_late_probe(struct snd_soc_card *card)
{
@@ -6907,7 +7552,9 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
	int total_links = 0;
	uint32_t tasha_codec = 0, auxpcm_audio_intf = 0;
	uint32_t va_bolero_codec = 0, wsa_bolero_codec = 0, mi2s_audio_intf = 0;
	uint32_t spdif_audio_intf = 0;
	const struct of_device_id *match;
	char __iomem *spdif_cfg, *spdif_pin_ctl;
	int rc = 0;

	match = of_match_node(qcs405_asoc_machine_of_match, dev->of_node);
@@ -7028,6 +7675,27 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
				ARRAY_SIZE(msm_auxpcm_be_dai_links);
			}
		}
		rc = of_property_read_u32(dev->of_node, "qcom,spdif-audio-intf",
					  &spdif_audio_intf);
		if (rc) {
			dev_dbg(dev, "%s: No DT match SPDIF audio interface\n",
				__func__);
		} else {
			if (spdif_audio_intf) {
				memcpy(msm_qcs405_dai_links + total_links,
				msm_spdif_be_dai_links,
				sizeof(msm_spdif_be_dai_links));
				total_links +=
				ARRAY_SIZE(msm_spdif_be_dai_links);

				/* enable spdif coax pins */
				spdif_cfg = ioremap(TLMM_EAST_SPARE, 0x4);
				spdif_pin_ctl =
					ioremap(TLMM_SPDIF_HDMI_ARC_CTL, 0x4);
				iowrite32(0xc0, spdif_cfg);
				iowrite32(0x2220, spdif_pin_ctl);
			}
		}
		dailink = msm_qcs405_dai_links;
	} else if (!strcmp(match->data, "stub_codec")) {
		card = &snd_soc_card_stub_msm;