~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/mediatek/mt8365/mt8365-dai-adda.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * MediaTek 8365 ALSA SoC Audio DAI ADDA Control
  4  *
  5  * Copyright (c) 2024 MediaTek Inc.
  6  * Authors: Jia Zeng <jia.zeng@mediatek.com>
  7  *          Alexandre Mergnat <amergnat@baylibre.com>
  8  */
  9 
 10 #include <linux/bitops.h>
 11 #include <linux/regmap.h>
 12 #include <sound/pcm_params.h>
 13 #include "mt8365-afe-clk.h"
 14 #include "mt8365-afe-common.h"
 15 #include "../common/mtk-dai-adda-common.h"
 16 
 17 static int adda_afe_on_ref_cnt;
 18 
 19 /* DAI Drivers */
 20 
 21 static int mt8365_dai_set_adda_out(struct mtk_base_afe *afe, unsigned int rate)
 22 {
 23         unsigned int val;
 24 
 25         if (rate == 8000 || rate == 16000)
 26                 val = AFE_ADDA_DL_VOICE_DATA;
 27         else
 28                 val = 0;
 29 
 30         val |= FIELD_PREP(AFE_ADDA_DL_SAMPLING_RATE,
 31                 mtk_adda_dl_rate_transform(afe, rate));
 32         val |= AFE_ADDA_DL_8X_UPSAMPLE |
 33                AFE_ADDA_DL_MUTE_OFF_CH1 |
 34                AFE_ADDA_DL_MUTE_OFF_CH2 |
 35                AFE_ADDA_DL_DEGRADE_GAIN;
 36 
 37         regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON0, 0xffffffff, 0);
 38         regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON1, 0xffffffff, 0);
 39         regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0xffffffff, val);
 40         /* SA suggest apply -0.3db to audio/speech path */
 41         regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON1,
 42                            0xffffffff, 0xf74f0000);
 43         /* SA suggest use default value for sdm */
 44         regmap_update_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON,
 45                            0xffffffff, 0x0700701e);
 46 
 47         return 0;
 48 }
 49 
 50 static int mt8365_dai_set_adda_in(struct mtk_base_afe *afe, unsigned int rate)
 51 {
 52         unsigned int val;
 53 
 54         val = FIELD_PREP(AFE_ADDA_UL_SAMPLING_RATE,
 55                          mtk_adda_ul_rate_transform(afe, rate));
 56         regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
 57                            AFE_ADDA_UL_SAMPLING_RATE, val);
 58         /* Using Internal ADC */
 59         regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x0);
 60 
 61         return 0;
 62 }
 63 
 64 int mt8365_dai_enable_adda_on(struct mtk_base_afe *afe)
 65 {
 66         unsigned long flags;
 67         struct mt8365_afe_private *afe_priv = afe->platform_priv;
 68 
 69         spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
 70 
 71         adda_afe_on_ref_cnt++;
 72         if (adda_afe_on_ref_cnt == 1)
 73                 regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
 74                                    AFE_ADDA_UL_DL_ADDA_AFE_ON,
 75                                    AFE_ADDA_UL_DL_ADDA_AFE_ON);
 76 
 77         spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
 78 
 79         return 0;
 80 }
 81 
 82 int mt8365_dai_disable_adda_on(struct mtk_base_afe *afe)
 83 {
 84         unsigned long flags;
 85         struct mt8365_afe_private *afe_priv = afe->platform_priv;
 86 
 87         spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
 88 
 89         adda_afe_on_ref_cnt--;
 90         if (adda_afe_on_ref_cnt == 0)
 91                 regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
 92                                    AFE_ADDA_UL_DL_ADDA_AFE_ON,
 93                                    ~AFE_ADDA_UL_DL_ADDA_AFE_ON);
 94         else if (adda_afe_on_ref_cnt < 0) {
 95                 adda_afe_on_ref_cnt = 0;
 96                 dev_warn(afe->dev, "Abnormal adda_on ref count. Force it to 0\n");
 97         }
 98 
 99         spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
100 
101         return 0;
102 }
103 
104 static void mt8365_dai_set_adda_out_enable(struct mtk_base_afe *afe,
105                                            bool enable)
106 {
107         regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0x1, enable);
108 
109         if (enable)
110                 mt8365_dai_enable_adda_on(afe);
111         else
112                 mt8365_dai_disable_adda_on(afe);
113 }
114 
115 static void mt8365_dai_set_adda_in_enable(struct mtk_base_afe *afe, bool enable)
116 {
117         if (enable) {
118                 regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x1);
119                 mt8365_dai_enable_adda_on(afe);
120                 /* enable aud_pad_top fifo */
121                 regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
122                                    0xffffffff, 0x31);
123         } else {
124                 /* disable aud_pad_top fifo */
125                 regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
126                                    0xffffffff, 0x30);
127                 regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x0);
128                 /* de suggest disable ADDA_UL_SRC at least wait 125us */
129                 usleep_range(150, 300);
130                 mt8365_dai_disable_adda_on(afe);
131         }
132 }
133 
134 static int mt8365_dai_int_adda_startup(struct snd_pcm_substream *substream,
135                                        struct snd_soc_dai *dai)
136 {
137         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
138         unsigned int stream = substream->stream;
139 
140         mt8365_afe_enable_main_clk(afe);
141 
142         if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
143                 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC);
144                 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
145         } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
146                 mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_ADC);
147         }
148 
149         return 0;
150 }
151 
152 static void mt8365_dai_int_adda_shutdown(struct snd_pcm_substream *substream,
153                                          struct snd_soc_dai *dai)
154 {
155         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
156         struct mt8365_afe_private *afe_priv = afe->platform_priv;
157         struct mt8365_be_dai_data *be =
158                 &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
159         unsigned int stream = substream->stream;
160 
161         if (be->prepared[stream]) {
162                 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
163                         mt8365_dai_set_adda_out_enable(afe, false);
164                         mt8365_afe_set_i2s_out_enable(afe, false);
165                 } else {
166                         mt8365_dai_set_adda_in_enable(afe, false);
167                 }
168                 be->prepared[stream] = false;
169         }
170 
171         if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
172                 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
173                 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC);
174         } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
175                 mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_ADC);
176         }
177 
178         mt8365_afe_disable_main_clk(afe);
179 }
180 
181 static int mt8365_dai_int_adda_prepare(struct snd_pcm_substream *substream,
182                                        struct snd_soc_dai *dai)
183 {
184         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
185         struct mt8365_afe_private *afe_priv = afe->platform_priv;
186         struct mt8365_be_dai_data *be =
187                 &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
188         unsigned int rate = substream->runtime->rate;
189         int bit_width = snd_pcm_format_width(substream->runtime->format);
190         int ret;
191 
192         dev_info(afe->dev, "%s '%s' rate = %u\n", __func__,
193                  snd_pcm_stream_str(substream), rate);
194 
195         if (be->prepared[substream->stream]) {
196                 dev_info(afe->dev, "%s '%s' prepared already\n",
197                          __func__, snd_pcm_stream_str(substream));
198                 return 0;
199         }
200 
201         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
202                 ret = mt8365_dai_set_adda_out(afe, rate);
203                 if (ret)
204                         return ret;
205 
206                 ret = mt8365_afe_set_i2s_out(afe, rate, bit_width);
207                 if (ret)
208                         return ret;
209 
210                 mt8365_dai_set_adda_out_enable(afe, true);
211                 mt8365_afe_set_i2s_out_enable(afe, true);
212         } else {
213                 ret = mt8365_dai_set_adda_in(afe, rate);
214                 if (ret)
215                         return ret;
216 
217                 mt8365_dai_set_adda_in_enable(afe, true);
218         }
219         be->prepared[substream->stream] = true;
220         return 0;
221 }
222 
223 static const struct snd_soc_dai_ops mt8365_afe_int_adda_ops = {
224         .startup        = mt8365_dai_int_adda_startup,
225         .shutdown       = mt8365_dai_int_adda_shutdown,
226         .prepare        = mt8365_dai_int_adda_prepare,
227 };
228 
229 static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
230         {
231                 .name = "INT ADDA",
232                 .id = MT8365_AFE_IO_INT_ADDA,
233                 .playback = {
234                         .stream_name = "INT ADDA Playback",
235                         .channels_min = 1,
236                         .channels_max = 2,
237                         .rates = SNDRV_PCM_RATE_8000_48000,
238                         .formats = SNDRV_PCM_FMTBIT_S16_LE,
239                 },
240                 .capture = {
241                         .stream_name = "INT ADDA Capture",
242                         .channels_min = 1,
243                         .channels_max = 2,
244                         .rates = SNDRV_PCM_RATE_16000 |
245                                  SNDRV_PCM_RATE_32000 |
246                                  SNDRV_PCM_RATE_48000,
247                         .formats = SNDRV_PCM_FMTBIT_S16_LE |
248                                    SNDRV_PCM_FMTBIT_S32_LE,
249                 },
250                 .ops = &mt8365_afe_int_adda_ops,
251         }
252 };
253 
254 /* DAI Controls */
255 
256 static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
257         SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN3,
258                                     10, 1, 0),
259 };
260 
261 static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
262         SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN4,
263                                     11, 1, 0),
264 };
265 
266 static const struct snd_kcontrol_new int_adda_o03_o04_enable_ctl =
267         SOC_DAPM_SINGLE_VIRT("Switch", 1);
268 
269 /* DAI widget */
270 
271 static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
272         SND_SOC_DAPM_SWITCH("INT ADDA O03_O04", SND_SOC_NOPM, 0, 0,
273                             &int_adda_o03_o04_enable_ctl),
274         /* inter-connections */
275         SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
276                            mtk_adda_dl_ch1_mix,
277                            ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
278         SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
279                            mtk_adda_dl_ch2_mix,
280                            ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
281 };
282 
283 /* DAI route */
284 
285 static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
286         {"INT ADDA O03_O04", "Switch", "O03"},
287         {"INT ADDA O03_O04", "Switch", "O04"},
288         {"INT ADDA Playback", NULL, "INT ADDA O03_O04"},
289         {"INT ADDA Playback", NULL, "ADDA_DL_CH1"},
290         {"INT ADDA Playback", NULL, "ADDA_DL_CH2"},
291         {"AIN Mux", "INT ADC", "INT ADDA Capture"},
292         {"ADDA_DL_CH1", "GAIN1_OUT_CH1", "Hostless FM DL"},
293         {"ADDA_DL_CH2", "GAIN1_OUT_CH2", "Hostless FM DL"},
294 };
295 
296 int mt8365_dai_adda_register(struct mtk_base_afe *afe)
297 {
298         struct mtk_base_afe_dai *dai;
299 
300         dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
301         if (!dai)
302                 return -ENOMEM;
303         list_add(&dai->list, &afe->sub_dais);
304         dai->dai_drivers = mtk_dai_adda_driver;
305         dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
306         dai->dapm_widgets = mtk_dai_adda_widgets;
307         dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
308         dai->dapm_routes = mtk_dai_adda_routes;
309         dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
310         return 0;
311 }
312 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php