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

Commit d96c71eb authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ARM: dts: msm: add TDM support for apq8009"

parents 85108283 3c9d4c6b
Loading
Loading
Loading
Loading
+48 −2
Original line number Diff line number Diff line
@@ -25,7 +25,9 @@
		qcom,model = "apq8009-tashalite-snd-card";
		qcom,msm-mbhc-hphl-swh = <0>;
		qcom,msm-mbhc-gnd-swh = <0>;
		qcom,afe-rxtx-lb;
		qcom,msm-mclk-freq = <9600000>;
		qcom,tdm-audio-intf;
		qcom,msm-hs-micbias-type = "internal";
		qcom,audio-routing =
			"AIF4 VI", "MCLK",
@@ -87,7 +89,9 @@
				<&incall_record_rx>, <&incall_record_tx>,
				<&incall_music_rx>, <&incall_music_2_rx>,
				<&bt_sco_rx>,
				<&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>;
				<&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
				<&afe_loopback_tx>, <&dai_pri_tdm_rx_0>,
				<&dai_pri_tdm_tx_0>;
		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
				 "msm-dai-q6-mi2s.0",
				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
@@ -101,7 +105,9 @@
				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
				"msm-dai-q6-dev.32770",
				"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293";
				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
				"msm-dai-q6-dev.24577", "msm-dai-q6-tdm.36864",
				"msm-dai-q6-tdm.36865";
		asoc-codec = <&stub_codec>;
		asoc-codec-names = "msm-stub-codec.1";
		qcom,wsa-max-devs = <2>;
@@ -221,4 +227,44 @@
			};
		};
	};

	pri_tdm_rx: qcom,msm-dai-tdm-pri-rx {
		compatible = "qcom,msm-dai-tdm";
		qcom,msm-cpudai-tdm-group-id = <37120>;
		qcom,msm-cpudai-tdm-group-num-ports = <1>;
		qcom,msm-cpudai-tdm-group-port-id = <36864>;
		qcom,msm-cpudai-tdm-clk-rate = <12288000>;
		qcom,msm-cpudai-tdm-sec-port-enable;
		qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
		dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
			compatible = "qcom,msm-dai-q6-tdm";
			qcom,msm-cpudai-tdm-dev-id = <36864>;
			qcom,msm-cpudai-tdm-sync-mode = <0>;
			qcom,msm-cpudai-tdm-sync-src = <1>;
			qcom,msm-cpudai-tdm-data-out = <0>;
			qcom,msm-cpudai-tdm-invert-sync = <0>;
			qcom,msm-cpudai-tdm-data-delay = <1>;
			qcom,msm-cpudai-tdm-data-align = <0>;
		};
	};

	pri_tdm_tx: qcom,msm-dai-tdm-pri-tx {
		compatible = "qcom,msm-dai-tdm";
		qcom,msm-cpudai-tdm-group-id = <37121>;
		qcom,msm-cpudai-tdm-group-num-ports = <1>;
		qcom,msm-cpudai-tdm-group-port-id = <36865>;
		qcom,msm-cpudai-tdm-clk-rate = <12288000>;
		qcom,msm-cpudai-tdm-sec-port-enable;
		qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
		dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
			compatible = "qcom,msm-dai-q6-tdm";
			qcom,msm-cpudai-tdm-dev-id = <36865>;
			qcom,msm-cpudai-tdm-sync-mode = <0>;
			qcom,msm-cpudai-tdm-sync-src = <1>;
			qcom,msm-cpudai-tdm-data-out = <0>;
			qcom,msm-cpudai-tdm-invert-sync = <0>;
			qcom,msm-cpudai-tdm-data-delay = <1>;
			qcom,msm-cpudai-tdm-data-align = <0>;
		};
	};
};
+5 −0
Original line number Diff line number Diff line
@@ -1726,6 +1726,11 @@
			qcom,msm-dai-q6-dev-id = <240>;
		};

		afe_loopback_tx: qcom,msm-dai-q6-afe-loopback-tx {
			compatible = "qcom,msm-dai-q6-dev";
			qcom,msm-dai-q6-dev-id = <24577>;
		};

		incall_record_rx: qcom,msm-dai-q6-incall-record-rx {
			compatible = "qcom,msm-dai-q6-dev";
			qcom,msm-dai-q6-dev-id = <32771>;
+696 −6
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/jack.h>
#include <sound/q6afe-v2.h>
#include <soc/qcom/socinfo.h>
@@ -69,11 +70,48 @@
#define WSA8810_NAME_1 "wsa881x.20170211"
#define WSA8810_NAME_2 "wsa881x.20170212"

#define TDM_SLOT_OFFSET_MAX   8

enum btsco_rates {
	RATE_8KHZ_ID,
	RATE_16KHZ_ID,
};

enum {
	PRIMARY_TDM_RX_0,
	PRIMARY_TDM_TX_0,
	SECONDARY_TDM_RX_0,
	SECONDARY_TDM_TX_0,
	TDM_MAX,
};


/* TDM default channels */
static int msm_pri_tdm_rx_0_ch = 8;
static int msm_pri_tdm_tx_0_ch = 8;

/* TDM default bit format */
static int msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;

/* TDM default sampling rate */
static int msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ;
static int msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ;

static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
	"Five", "Six", "Seven", "Eight"};
static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
	"S32_LE"};
static char const *tdm_sample_rate_text[] = {"KHZ_16", "KHZ_48"};

/* TDM default offset */
static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
	/* PRI_TDM_RX */
	{0, 4, 8, 12, 16, 20, 24, 28},
	/* PRI_TDM_TX */
	{0, 4, 8, 12, 16, 20, 24, 28},
};

struct apq8009_asoc_mach_data {
	int mclk_freq;
	struct afe_digital_clk_cfg digital_cdc_clk;
@@ -292,6 +330,203 @@ static char const *pri_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
					"KHZ_192", "KHZ_8",
					"KHZ_16", "KHZ_32"};


static int msm_pri_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	pr_debug("msm_pri_tdm_rx_0_ch = %d",
		 msm_pri_tdm_rx_0_ch);
	ucontrol->value.integer.value[0] = msm_pri_tdm_rx_0_ch - 1;
	return 0;
}

static int msm_pri_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	msm_pri_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
	pr_debug("msm_pri_tdm_rx_0_ch = %d",
		 msm_pri_tdm_rx_0_ch);
	return 0;
}

static int msm_pri_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	pr_debug("msm_pri_tdm_tx_0_ch = %d",
		 msm_pri_tdm_tx_0_ch);
	ucontrol->value.integer.value[0] = msm_pri_tdm_tx_0_ch - 1;
	return 0;
}

static int msm_pri_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	msm_pri_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
	pr_debug("msm_pri_tdm_tx_0_ch = %d",
		 msm_pri_tdm_tx_0_ch);
	return 0;
}

static int msm_pri_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (msm_pri_tdm_rx_0_bit_format) {
	case SNDRV_PCM_FORMAT_S32_LE:
		ucontrol->value.integer.value[0] = 3;
		break;
	case SNDRV_PCM_FORMAT_S24_3LE:
		ucontrol->value.integer.value[0] = 2;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		ucontrol->value.integer.value[0] = 1;
		break;
	case SNDRV_PCM_FORMAT_S16_LE:
	default:
		ucontrol->value.integer.value[0] = 0;
		break;
	}
	pr_debug("msm_pri_tdm_rx_0_bit_format = %ld",
		  ucontrol->value.integer.value[0]);
	return 0;
}

static int msm_pri_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (ucontrol->value.integer.value[0]) {
	case 3:
		msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE;
		break;
	case 2:
		msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
		break;
	case 1:
		msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
		break;
	case 0:
	default:
		msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
		break;
	}
	pr_debug("msm_pri_tdm_rx_0_bit_format = %d",
		  msm_pri_tdm_rx_0_bit_format);
	return 0;
}

static int msm_pri_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (msm_pri_tdm_tx_0_bit_format) {
	case SNDRV_PCM_FORMAT_S32_LE:
		ucontrol->value.integer.value[0] = 3;
		break;
	case SNDRV_PCM_FORMAT_S24_3LE:
		ucontrol->value.integer.value[0] = 2;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		ucontrol->value.integer.value[0] = 1;
		break;
	case SNDRV_PCM_FORMAT_S16_LE:
	default:
		ucontrol->value.integer.value[0] = 0;
		break;
	}
	pr_debug("msm_pri_tdm_tx_0_bit_format = %ld",
		  ucontrol->value.integer.value[0]);
	return 0;
}

static int msm_pri_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (ucontrol->value.integer.value[0]) {
	case 3:
		msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE;
		break;
	case 2:
		msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
		break;
	case 1:
		msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
		break;
	case 0:
	default:
		msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
		break;
	}
	pr_debug("msm_pri_tdm_tx_0_bit_format = %d",
		  msm_pri_tdm_tx_0_bit_format);
	return 0;
}

static int msm_pri_tdm_rx_0_sample_rate_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (msm_pri_tdm_rx_0_sample_rate) {
	case SAMPLING_RATE_16KHZ:
		ucontrol->value.integer.value[0] = 0;
		break;
	case SAMPLING_RATE_48KHZ:
	default:
		ucontrol->value.integer.value[0] = 1;
		break;
	}
	pr_debug("msm_pri_tdm_rx_0_sample_rate = %ld",
		  ucontrol->value.integer.value[0]);
	return 0;
}

static int  msm_pri_tdm_rx_0_sample_rate_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (ucontrol->value.integer.value[0]) {
	case 0:
		msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_16KHZ;
		break;
	case 1:
	default:
		msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ;
		break;
	}
	pr_debug("msm_pri_tdm_rx_0_sample_rate = %d",
		  msm_pri_tdm_rx_0_sample_rate);
	return 0;
}

static int msm_pri_tdm_tx_0_sample_rate_get(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (msm_pri_tdm_tx_0_sample_rate) {
	case SAMPLING_RATE_16KHZ:
		ucontrol->value.integer.value[0] = 0;
		break;
	case SAMPLING_RATE_48KHZ:
	default:
		ucontrol->value.integer.value[0] = 1;
		break;
	}
	pr_debug("msm_pri_tdm_tx_0_sample_rate = %ld",
		  ucontrol->value.integer.value[0]);
	return 0;
}

static int  msm_pri_tdm_tx_0_sample_rate_put(struct snd_kcontrol *kcontrol,
		struct snd_ctl_elem_value *ucontrol)
{
	switch (ucontrol->value.integer.value[0]) {
	case 0:
		msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_16KHZ;
		break;
	case 1:
	default:
		msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ;
		break;
	}
	pr_debug("msm_pri_tdm_tx_0_sample_rate = %d",
		  msm_pri_tdm_tx_0_sample_rate);
	return 0;
}

static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
					struct snd_pcm_hw_params *params)
{
@@ -502,6 +737,203 @@ static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
	return 0;
}


static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
		struct snd_pcm_hw_params *params)
{
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_interval *rate = hw_param_interval(params,
			SNDRV_PCM_HW_PARAM_RATE);
	struct snd_interval *channels = hw_param_interval(params,
			SNDRV_PCM_HW_PARAM_CHANNELS);

	switch (cpu_dai->id) {
	case AFE_PORT_ID_PRIMARY_TDM_RX:
		channels->min = channels->max = msm_pri_tdm_rx_0_ch;
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			       msm_pri_tdm_rx_0_bit_format);
		rate->min = rate->max = msm_pri_tdm_rx_0_sample_rate;
		break;
	case AFE_PORT_ID_PRIMARY_TDM_TX:
		channels->min = channels->max = msm_pri_tdm_tx_0_ch;
		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
			       msm_pri_tdm_tx_0_bit_format);
		rate->min = rate->max = msm_pri_tdm_tx_0_sample_rate;
		break;
	default:
		pr_err("%s: dai id 0x%x not supported", __func__,
		       cpu_dai->id);
		return -EINVAL;
	}

	pr_debug("%s: dai id = 0x%x channels = %d rate = %d", __func__,
		 cpu_dai->id, channels->max, rate->max);

	return 0;
}

static unsigned int tdm_param_set_slot_mask(u16 port_id,
				int slot_width, int channels)
{
	unsigned int slot_mask = 0;
	int upper, lower, i, j;
	unsigned int *slot_offset;

	switch (port_id) {
	case AFE_PORT_ID_PRIMARY_TDM_RX:
		lower = PRIMARY_TDM_RX_0;
		upper = PRIMARY_TDM_RX_0;
		break;
	case AFE_PORT_ID_PRIMARY_TDM_TX:
		lower = PRIMARY_TDM_TX_0;
		upper = PRIMARY_TDM_TX_0;
		break;
	default:
		return slot_mask;
	}

	for (i = lower; i <= upper; i++) {
		slot_offset = tdm_slot_offset[i];
		for (j = 0; j < channels; j++) {
			if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
				/*
				 * set the mask of active slot according to
				 * the offset table for the group of devices
				 */
				slot_mask |=
				    (1 << ((slot_offset[j] * 8) / slot_width));
			else
				break;
		}
	}

	return slot_mask;
}

static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	int ret = 0;
	int channels, slot_width, slots;
	unsigned int slot_mask;
	unsigned int *slot_offset;
	int offset_channels = 0;
	int i;

	pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);

	channels = params_channels(params);
	switch (channels) {
	case 1:
	case 2:
	case 3:
	case 4:
	case 6:
	case 8:
		switch (params_format(params)) {
		case SNDRV_PCM_FORMAT_S32_LE:
		case SNDRV_PCM_FORMAT_S24_LE:
		case SNDRV_PCM_FORMAT_S24_3LE:
		case SNDRV_PCM_FORMAT_S16_LE:
			/*
			 * up to 8 channel HW configuration should
			 * use 32 bit slot width for max support of
			 * stream bit width. (slot_width > bit_width)
			 */
			slot_width = 32;
			break;
		default:
			pr_err("%s: invalid param format 0x%x\n",
				__func__, params_format(params));
			return -EINVAL;
		}
		slots = 8;
		slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
			slot_width, channels);
		if (!slot_mask) {
			pr_err("%s: invalid slot_mask 0x%x\n",
				__func__, slot_mask);
			return -EINVAL;
		}
		break;
	default:
		pr_err("%s: invalid param channels %d\n",
			__func__, channels);
		return -EINVAL;
	}

	switch (cpu_dai->id) {
	case AFE_PORT_ID_PRIMARY_TDM_RX:
		slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_0];
		break;
	case AFE_PORT_ID_PRIMARY_TDM_TX:
		slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_0];
		break;
	default:
		pr_err("%s: dai id 0x%x not supported\n",
			__func__, cpu_dai->id);
		return -EINVAL;
	}

	for (i = 0; i < channels; i++) {
		if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
			offset_channels++;
		else
			break;
	}

	if (offset_channels == 0) {
		pr_err("%s: slot offset not supported, offset_channels %d\n",
			__func__, offset_channels);
		return -EINVAL;
	}

	if (channels > offset_channels) {
		pr_err("%s: channels %d exceed offset_channels %d\n",
			__func__, channels, offset_channels);
		return -EINVAL;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
			slots, slot_width);
		if (ret < 0) {
			pr_err("%s: failed to set tdm slot, err:%d\n",
				__func__, ret);
			goto end;
		}

		ret = snd_soc_dai_set_channel_map(cpu_dai,
			0, NULL, channels, slot_offset);
		if (ret < 0) {
			pr_err("%s: failed to set channel map, err:%d\n",
				__func__, ret);
			goto end;
		}
	} else {
		ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
			slots, slot_width);
		if (ret < 0) {
			pr_err("%s: failed to set tdm slot, err:%d\n",
				__func__, ret);
			goto end;
		}

		ret = snd_soc_dai_set_channel_map(cpu_dai,
			channels, slot_offset, 0, NULL);
		if (ret < 0) {
			pr_err("%s: failed to set channel map, err:%d\n",
				__func__, ret);
			goto end;
		}
	}

end:
	return ret;
}

static int msm_pri_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
@@ -779,6 +1211,12 @@ static const struct soc_enum msm_snd_enum[] = {
	SOC_ENUM_SINGLE_EXT(3, rx_bit_format_text),
	SOC_ENUM_SINGLE_EXT(4, mi2s_tx_ch_text),
	SOC_ENUM_SINGLE_EXT(6, pri_rx_sample_rate_text),
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_ch_text),
			tdm_ch_text),
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_bit_format_text),
			tdm_bit_format_text),
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_sample_rate_text),
			tdm_sample_rate_text),
};

static const char *const btsco_rate_text[] = {"BTSCO_RATE_8KHZ",
@@ -800,6 +1238,22 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
		     msm_btsco_rate_get, msm_btsco_rate_put),
	SOC_ENUM_EXT("MI2S_RX SampleRate", msm_snd_enum[2],
			pri_rx_sample_rate_get, pri_rx_sample_rate_put),
	SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", msm_snd_enum[3],
			msm_pri_tdm_rx_0_ch_get, msm_pri_tdm_rx_0_ch_put),
	SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", msm_snd_enum[3],
			msm_pri_tdm_tx_0_ch_get, msm_pri_tdm_tx_0_ch_put),
	SOC_ENUM_EXT("PRI_TDM_RX_0 Bit Format", msm_snd_enum[4],
			msm_pri_tdm_rx_0_bit_format_get,
			msm_pri_tdm_rx_0_bit_format_put),
	SOC_ENUM_EXT("PRI_TDM_TX_0 Bit Format", msm_snd_enum[4],
			msm_pri_tdm_tx_0_bit_format_get,
			msm_pri_tdm_tx_0_bit_format_put),
	SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", msm_snd_enum[5],
			msm_pri_tdm_rx_0_sample_rate_get,
			msm_pri_tdm_rx_0_sample_rate_put),
	SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", msm_snd_enum[5],
			msm_pri_tdm_tx_0_sample_rate_get,
			msm_pri_tdm_tx_0_sample_rate_put),
};

static int apq8009_mclk_event(struct snd_soc_dapm_widget *w,
@@ -974,6 +1428,101 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
				__func__, "pri_i2s");
}


static int msm_tdm_startup(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_card *card = rtd->card;
	struct apq8009_asoc_mach_data *pdata =
		snd_soc_card_get_drvdata(card);
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	int ret = 0, val = 0;

	pr_debug("substream = %s  stream = %d",
		 substream->name, substream->stream);
	pr_debug("dai id = 0x%x", cpu_dai->id);

	switch (cpu_dai->id) {
	case AFE_PORT_ID_PRIMARY_TDM_RX:
	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
	case AFE_PORT_ID_PRIMARY_TDM_TX:
	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
		/* Configure mux for Primary TDM */
		if (pdata->vaddr_gpio_mux_pcm_ctl) {
			val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl);
			val = val | 0x00000001;
			iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl);
		} else {
			return -EINVAL;
		}
		if (pdata->vaddr_gpio_mux_mic_ctl) {
			val = ioread32(pdata->vaddr_gpio_mux_mic_ctl);
			/*0x1808000 Use this value for slave mode*/
			val = val | 0x02020002; /*this is for master mode*/
			iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl);
		} else {
			return -EINVAL;
		}

		ret = msm_gpioset_activate(CLIENT_WCD_EXT, "quat_i2s");
		if (ret < 0)
			pr_err("%s: failed to activate primary TDM gpio set\n",
				 __func__);
		break;
	default:
		pr_err("dai id 0x%x not supported", cpu_dai->id);
		break;
		return -EINVAL;
	}
	return ret;
}

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

	switch (cpu_dai->id) {
	case AFE_PORT_ID_PRIMARY_TDM_RX:
	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
	case AFE_PORT_ID_PRIMARY_TDM_TX:
	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
		ret = msm_gpioset_suspend(CLIENT_WCD_EXT, "quat_i2s");
		if (ret < 0) {
			pr_err("%s: gpio set cannot be de-activated %s\n",
				__func__, "pri_tdm");
			return;
		}
		break;
	default:
		break;
	}
}

static int msm_audrx_init_wcd(struct snd_soc_pcm_runtime *rtd)
{

@@ -1070,6 +1619,28 @@ static struct snd_soc_ops msm_pri_auxpcm_be_ops = {
	.startup = msm_prim_auxpcm_startup,
};

static struct snd_soc_dai_link msm_afe_rxtx_lb_be_dai_link[] = {
	{
		.name = LPASS_BE_AFE_LOOPBACK_TX,
		.stream_name = "AFE Loopback Capture",
		.cpu_dai_name = "msm-dai-q6-dev.24577",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-tx",
		.no_pcm = 1,
		.dpcm_capture = 1,
		.be_id = MSM_BACKEND_DAI_AFE_LOOPBACK_TX,
		.ignore_pmdown_time = 1,
		.ignore_suspend = 1,
	},
};

static struct snd_soc_ops msm_tdm_be_ops = {
	.startup = msm_tdm_startup,
	.hw_params = msm_tdm_snd_hw_params,
	.shutdown = msm_tdm_shutdown,
};

static struct snd_soc_dai_link apq8009_9326_dai[] = {
	/* Backend DAI Links */
	{
@@ -1738,9 +2309,108 @@ static struct snd_soc_dai_link apq8009_dai[] = {
	},
};

static struct snd_soc_dai_link msm_tdm_fe_dai[] = {
	/* FE TDM DAI links */
	{
		.name = "Primary TDM RX 0 Hostless",
		.stream_name = "Primary TDM RX 0 Hostless",
		.cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS",
		.platform_name = "msm-pcm-hostless",
		.dynamic = 1,
		.dpcm_playback = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
	{
		.name = "Primary TDM TX 0 Hostless",
		.stream_name = "Primary TDM TX 0 Hostless",
		.cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS",
		.platform_name = "msm-pcm-hostless",
		.dynamic = 1,
		.dpcm_capture = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
	{
		.name = "Secondary TDM RX 0 Hostless",
		.stream_name = "Secondary TDM RX 0 Hostless",
		.cpu_dai_name = "SEC_TDM_RX_0_HOSTLESS",
		.platform_name = "msm-pcm-hostless",
		.dynamic = 1,
		.dpcm_playback = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
	{
		.name = "Secondary TDM TX 0 Hostless",
		.stream_name = "Secondary TDM TX 0 Hostless",
		.cpu_dai_name = "SEC_TDM_TX_0_HOSTLESS",
		.platform_name = "msm-pcm-hostless",
		.dynamic = 1,
		.dpcm_capture = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
};

static struct snd_soc_dai_link msm_tdm_be_dai[] = {
	/* TDM be dai links */
	{
		.name = LPASS_BE_PRI_TDM_RX_0,
		.stream_name = "Primary TDM0 Playback",
		.cpu_dai_name = "msm-dai-q6-tdm.36864",
		.platform_name = "msm-pcm-routing",
		.codec_name     = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-rx",
		.no_pcm = 1,
		.dpcm_playback = 1,
		.be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
		.be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
		.ops = &msm_tdm_be_ops,
		.ignore_suspend = 1,
	},
	{
		.name = LPASS_BE_PRI_TDM_TX_0,
		.stream_name = "Primary TDM0 Capture",
		.cpu_dai_name = "msm-dai-q6-tdm.36865",
		.platform_name = "msm-pcm-routing",
		.codec_name     = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-tx",
		.no_pcm = 1,
		.dpcm_capture = 1,
		.be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
		.be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
		.ops = &msm_tdm_be_ops,
		.ignore_suspend = 1,
	},
};

static struct snd_soc_dai_link apq8009_9326_dai_links[
				ARRAY_SIZE(apq8009_dai) +
				ARRAY_SIZE(apq8009_9326_dai)];
				ARRAY_SIZE(msm_tdm_fe_dai) +
				ARRAY_SIZE(apq8009_9326_dai) +
				ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) +
				ARRAY_SIZE(msm_tdm_be_dai)];

static struct snd_soc_card snd_soc_card_9326_apq8009;

@@ -1760,7 +2430,7 @@ static int populate_ext_snd_card_dt_data(struct platform_device *pdev)
struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
{
	struct snd_soc_card *card = &snd_soc_card_9326_apq8009;
	int num_links, ret;
	int ret, len1, len2, len3;

	card->dev = dev;
	ret = snd_soc_of_parse_card_name(card, "qcom,model");
@@ -1771,16 +2441,36 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
	}
	pr_debug("%s: CARD is %s\n", __func__, card->name);

	num_links = ARRAY_SIZE(apq8009_9326_dai_links);
	len1 = ARRAY_SIZE(apq8009_dai);
	len2 = len1 + ARRAY_SIZE(msm_tdm_fe_dai);
	len3 = len2 + ARRAY_SIZE(apq8009_9326_dai);

	memcpy(apq8009_9326_dai_links, apq8009_dai,
			sizeof(apq8009_dai));
	memcpy(apq8009_9326_dai_links + ARRAY_SIZE(apq8009_dai),
			apq8009_9326_dai, sizeof(apq8009_9326_dai));
	memcpy(apq8009_9326_dai_links + len1, msm_tdm_fe_dai,
			sizeof(msm_tdm_fe_dai));
	memcpy(apq8009_9326_dai_links + len2, apq8009_9326_dai,
			sizeof(apq8009_9326_dai));

	if (of_property_read_bool(dev->of_node, "qcom,afe-rxtx-lb")) {
		dev_dbg(dev, "%s(): AFE RX to TX loopback supported\n",
				__func__);
		memcpy(apq8009_9326_dai_links + len3,
		       msm_afe_rxtx_lb_be_dai_link,
		       sizeof(msm_afe_rxtx_lb_be_dai_link));
		len3 += ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link);
	}

	if (of_property_read_bool(dev->of_node, "qcom,tdm-audio-intf")) {
		dev_dbg(dev, "%s(): TDM support present\n",
				__func__);
		memcpy(apq8009_9326_dai_links + len3, msm_tdm_be_dai,
			sizeof(msm_tdm_be_dai));
		len3 += ARRAY_SIZE(msm_tdm_be_dai);
	}

	card->dai_link = apq8009_9326_dai_links;
	card->num_links = num_links;
	card->num_links = len3;
	card->dev = dev;

	return card;