Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: tlv320aic3x: Add TDM support

TDM support is achieved using DSP transfer mode and setting a
programmable offset which specifies where data begins with
respect to the frame sync.

It requires 256-clock mode if CODEC is master (not currently
supported in the driver). No additional dependency if CODEC
is slave.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Peter Ujfalusi and committed by
Mark Brown
36849409 f114040e

+60 -3
+59 -3
sound/soc/codecs/tlv320aic3x.c
··· 78 78 struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; 79 79 struct aic3x_setup_data *setup; 80 80 unsigned int sysclk; 81 + unsigned int dai_fmt; 82 + unsigned int tdm_delay; 81 83 struct list_head list; 82 84 int master; 83 85 int gpio_reset; ··· 1011 1009 return 0; 1012 1010 } 1013 1011 1012 + static int aic3x_prepare(struct snd_pcm_substream *substream, 1013 + struct snd_soc_dai *dai) 1014 + { 1015 + struct snd_soc_codec *codec = dai->codec; 1016 + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); 1017 + int delay = 0; 1018 + 1019 + /* TDM slot selection only valid in DSP_A/_B mode */ 1020 + if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A) 1021 + delay += (aic3x->tdm_delay + 1); 1022 + else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B) 1023 + delay += aic3x->tdm_delay; 1024 + 1025 + /* Configure data delay */ 1026 + snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay); 1027 + 1028 + return 0; 1029 + } 1030 + 1014 1031 static int aic3x_mute(struct snd_soc_dai *dai, int mute) 1015 1032 { 1016 1033 struct snd_soc_codec *codec = dai->codec; ··· 1069 1048 struct snd_soc_codec *codec = codec_dai->codec; 1070 1049 struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); 1071 1050 u8 iface_areg, iface_breg; 1072 - int delay = 0; 1073 1051 1074 1052 iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f; 1075 1053 iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f; ··· 1096 1076 case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): 1097 1077 break; 1098 1078 case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): 1099 - delay = 1; 1100 1079 case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): 1101 1080 iface_breg |= (0x01 << 6); 1102 1081 break; ··· 1109 1090 return -EINVAL; 1110 1091 } 1111 1092 1093 + aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 1094 + 1112 1095 /* set iface */ 1113 1096 snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg); 1114 1097 snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg); 1115 - snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay); 1098 + 1099 + return 0; 1100 + } 1101 + 1102 + static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, 1103 + unsigned int tx_mask, unsigned int rx_mask, 1104 + int slots, int slot_width) 1105 + { 1106 + struct snd_soc_codec *codec = codec_dai->codec; 1107 + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); 1108 + unsigned int lsb; 1109 + 1110 + if (tx_mask != rx_mask) { 1111 + dev_err(codec->dev, "tx and rx masks must be symmetric\n"); 1112 + return -EINVAL; 1113 + } 1114 + 1115 + if (unlikely(!tx_mask)) { 1116 + dev_err(codec->dev, "tx and rx masks need to be non 0\n"); 1117 + return -EINVAL; 1118 + } 1119 + 1120 + /* TDM based on DSP mode requires slots to be adjacent */ 1121 + lsb = __ffs(tx_mask); 1122 + if ((lsb + 1) != __fls(tx_mask)) { 1123 + dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); 1124 + return -EINVAL; 1125 + } 1126 + 1127 + aic3x->tdm_delay = lsb * slot_width; 1128 + 1129 + /* DOUT in high-impedance on inactive bit clocks */ 1130 + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, 1131 + DOUT_TRISTATE, DOUT_TRISTATE); 1116 1132 1117 1133 return 0; 1118 1134 } ··· 1266 1212 1267 1213 static const struct snd_soc_dai_ops aic3x_dai_ops = { 1268 1214 .hw_params = aic3x_hw_params, 1215 + .prepare = aic3x_prepare, 1269 1216 .digital_mute = aic3x_mute, 1270 1217 .set_sysclk = aic3x_set_dai_sysclk, 1271 1218 .set_fmt = aic3x_set_dai_fmt, 1219 + .set_tdm_slot = aic3x_set_dai_tdm_slot, 1272 1220 }; 1273 1221 1274 1222 static struct snd_soc_dai_driver aic3x_dai = {
+1
sound/soc/codecs/tlv320aic3x.h
··· 169 169 /* Audio serial data interface control register A bits */ 170 170 #define BIT_CLK_MASTER 0x80 171 171 #define WORD_CLK_MASTER 0x40 172 + #define DOUT_TRISTATE 0x20 172 173 173 174 /* Codec Datapath setup register 7 */ 174 175 #define FSREF_44100 (1 << 7)