Loading arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi +48 −2 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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", Loading @@ -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>; Loading Loading @@ -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>; }; }; }; arch/arm/boot/dts/qcom/msm8909.dtsi +5 −0 Original line number Diff line number Diff line Loading @@ -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>; Loading sound/soc/msm/apq8009-i2s-ext-codec.c +696 −6 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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", Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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 */ { Loading Loading @@ -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; Loading @@ -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"); Loading @@ -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; Loading Loading
arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi +48 −2 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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", Loading @@ -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>; Loading Loading @@ -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>; }; }; };
arch/arm/boot/dts/qcom/msm8909.dtsi +5 −0 Original line number Diff line number Diff line Loading @@ -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>; Loading
sound/soc/msm/apq8009-i2s-ext-codec.c +696 −6 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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", Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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 */ { Loading Loading @@ -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; Loading @@ -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"); Loading @@ -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; Loading