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

Commit 90a0786b authored by Surendar karka's avatar Surendar karka Committed by Gerrit - the friendly Code Review server
Browse files

ASoC: msm: add tdm support in machine driver for apq8009



Add dai_links and soc_ops to support TDM in apq8009.

CRs-Fixed: 2147847
Change-Id: Ic47c3f36665e1d61a1ee0140634262f78bcd2025
Signed-off-by: default avatarSurendar karka <sukark@codeaurora.org>
parent b8c6afd0
Loading
Loading
Loading
Loading
+672 −8
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)
{

@@ -1086,6 +1635,12 @@ static struct snd_soc_dai_link msm_afe_rxtx_lb_be_dai_link[] = {
	},
};

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 */
	{
@@ -1754,10 +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(msm_tdm_fe_dai) +
				ARRAY_SIZE(apq8009_9326_dai) +
				ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link)];
				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;

@@ -1777,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 ret, len1, len2;
	int ret, len1, len2, len3;

	card->dev = dev;
	ret = snd_soc_of_parse_card_name(card, "qcom,model");
@@ -1789,24 +2442,35 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
	pr_debug("%s: CARD is %s\n", __func__, card->name);

	len1 = ARRAY_SIZE(apq8009_dai);
	len2 = len1 + ARRAY_SIZE(apq8009_9326_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 + len1,
			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 + len2,
		memcpy(apq8009_9326_dai_links + len3,
		       msm_afe_rxtx_lb_be_dai_link,
		       sizeof(msm_afe_rxtx_lb_be_dai_link));
		len2 += ARRAY_SIZE(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 = len2;
	card->num_links = len3;
	card->dev = dev;

	return card;