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

TOMOYO Linux Cross Reference
Linux/sound/soc/mediatek/mt8365/mt8365-dai-pcm.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 PCM 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 
 16 struct mt8365_pcm_intf_data {
 17         bool slave_mode;
 18         bool lrck_inv;
 19         bool bck_inv;
 20         unsigned int format;
 21 };
 22 
 23 /* DAI Drivers */
 24 
 25 static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)
 26 {
 27         regmap_update_bits(afe->regmap, PCM_INTF_CON1,
 28                            PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);
 29 }
 30 
 31 static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)
 32 {
 33         regmap_update_bits(afe->regmap, PCM_INTF_CON1,
 34                            PCM_INTF_CON1_EN, 0x0);
 35 }
 36 
 37 static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream,
 38                                      struct snd_soc_dai *dai)
 39 {
 40         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
 41         struct mt8365_afe_private *afe_priv = afe->platform_priv;
 42         struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
 43         bool slave_mode = pcm_priv->slave_mode;
 44         bool lrck_inv = pcm_priv->lrck_inv;
 45         bool bck_inv = pcm_priv->bck_inv;
 46         unsigned int fmt = pcm_priv->format;
 47         unsigned int bit_width = dai->sample_bits;
 48         unsigned int val = 0;
 49 
 50         if (!slave_mode) {
 51                 val |= PCM_INTF_CON1_MASTER_MODE |
 52                        PCM_INTF_CON1_BYPASS_ASRC;
 53 
 54                 if (lrck_inv)
 55                         val |= PCM_INTF_CON1_SYNC_OUT_INV;
 56                 if (bck_inv)
 57                         val |= PCM_INTF_CON1_BCLK_OUT_INV;
 58         } else {
 59                 val |= PCM_INTF_CON1_SLAVE_MODE;
 60 
 61                 if (lrck_inv)
 62                         val |= PCM_INTF_CON1_SYNC_IN_INV;
 63                 if (bck_inv)
 64                         val |= PCM_INTF_CON1_BCLK_IN_INV;
 65 
 66                 /* TODO: add asrc setting */
 67         }
 68 
 69         val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);
 70 
 71         if (fmt == MT8365_PCM_FORMAT_PCMA ||
 72             fmt == MT8365_PCM_FORMAT_PCMB)
 73                 val |= PCM_INTF_CON1_SYNC_LEN(1);
 74         else
 75                 val |= PCM_INTF_CON1_SYNC_LEN(bit_width);
 76 
 77         switch (substream->runtime->rate) {
 78         case 48000:
 79                 val |= PCM_INTF_CON1_FS_48K;
 80                 break;
 81         case 32000:
 82                 val |= PCM_INTF_CON1_FS_32K;
 83                 break;
 84         case 16000:
 85                 val |= PCM_INTF_CON1_FS_16K;
 86                 break;
 87         case 8000:
 88                 val |= PCM_INTF_CON1_FS_8K;
 89                 break;
 90         default:
 91                 return -EINVAL;
 92         }
 93 
 94         if (bit_width > 16)
 95                 val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;
 96         else
 97                 val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;
 98 
 99         val |= PCM_INTF_CON1_EXT_MODEM;
100 
101         regmap_update_bits(afe->regmap, PCM_INTF_CON1,
102                            PCM_INTF_CON1_CONFIG_MASK, val);
103 
104         return 0;
105 }
106 
107 static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,
108                                    struct snd_soc_dai *dai)
109 {
110         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
111 
112         if (snd_soc_dai_active(dai))
113                 return 0;
114 
115         mt8365_afe_enable_main_clk(afe);
116 
117         return 0;
118 }
119 
120 static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,
121                                      struct snd_soc_dai *dai)
122 {
123         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
124 
125         if (snd_soc_dai_active(dai))
126                 return;
127 
128         mt8365_dai_disable_pcm1(afe);
129         mt8365_afe_disable_main_clk(afe);
130 }
131 
132 static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,
133                                    struct snd_soc_dai *dai)
134 {
135         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
136         int ret;
137 
138         if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) +
139             snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) {
140                 dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",
141                          __func__, snd_pcm_stream_str(substream),
142                          snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK),
143                          snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE));
144                 return 0;
145         }
146 
147         ret = mt8365_dai_configure_pcm1(substream, dai);
148         if (ret)
149                 return ret;
150 
151         mt8365_dai_enable_pcm1(afe);
152 
153         return 0;
154 }
155 
156 static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
157 {
158         struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
159         struct mt8365_afe_private *afe_priv = afe->platform_priv;
160         struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
161 
162         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
163         case SND_SOC_DAIFMT_I2S:
164                 pcm_priv->format = MT8365_PCM_FORMAT_I2S;
165                 break;
166         default:
167                 return -EINVAL;
168         }
169 
170         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
171         case SND_SOC_DAIFMT_NB_NF:
172                 pcm_priv->bck_inv = false;
173                 pcm_priv->lrck_inv = false;
174                 break;
175         case SND_SOC_DAIFMT_NB_IF:
176                 pcm_priv->bck_inv = false;
177                 pcm_priv->lrck_inv = true;
178                 break;
179         case SND_SOC_DAIFMT_IB_NF:
180                 pcm_priv->bck_inv = true;
181                 pcm_priv->lrck_inv = false;
182                 break;
183         case SND_SOC_DAIFMT_IB_IF:
184                 pcm_priv->bck_inv = true;
185                 pcm_priv->lrck_inv = true;
186                 break;
187         default:
188                 return -EINVAL;
189         }
190 
191         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
192         case SND_SOC_DAIFMT_CBM_CFM:
193                 pcm_priv->slave_mode = true;
194                 break;
195         case SND_SOC_DAIFMT_CBS_CFS:
196                 pcm_priv->slave_mode = false;
197                 break;
198         default:
199                 return -EINVAL;
200         }
201 
202         return 0;
203 }
204 
205 static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {
206         .startup        = mt8365_dai_pcm1_startup,
207         .shutdown       = mt8365_dai_pcm1_shutdown,
208         .prepare        = mt8365_dai_pcm1_prepare,
209         .set_fmt        = mt8365_dai_pcm1_set_fmt,
210 };
211 
212 static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
213         {
214                 .name = "PCM1",
215                 .id = MT8365_AFE_IO_PCM1,
216                 .playback = {
217                         .stream_name = "PCM1 Playback",
218                         .channels_min = 1,
219                         .channels_max = 2,
220                         .rates = SNDRV_PCM_RATE_8000 |
221                                  SNDRV_PCM_RATE_16000 |
222                                  SNDRV_PCM_RATE_32000 |
223                                  SNDRV_PCM_RATE_48000,
224                         .formats = SNDRV_PCM_FMTBIT_S16_LE |
225                                    SNDRV_PCM_FMTBIT_S32_LE,
226                 },
227                 .capture = {
228                         .stream_name = "PCM1 Capture",
229                         .channels_min = 1,
230                         .channels_max = 2,
231                         .rates = SNDRV_PCM_RATE_8000 |
232                                  SNDRV_PCM_RATE_16000 |
233                                  SNDRV_PCM_RATE_32000 |
234                                  SNDRV_PCM_RATE_48000,
235                         .formats = SNDRV_PCM_FMTBIT_S16_LE |
236                                    SNDRV_PCM_FMTBIT_S32_LE,
237                 },
238                 .ops = &mt8365_dai_pcm1_ops,
239                 .symmetric_rate = 1,
240                 .symmetric_sample_bits = 1,
241         }
242 };
243 
244 /* DAI widget */
245 
246 static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
247         SND_SOC_DAPM_OUTPUT("PCM1 Out"),
248         SND_SOC_DAPM_INPUT("PCM1 In"),
249 };
250 
251 /* DAI route */
252 
253 static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
254         {"PCM1 Playback", NULL, "O07"},
255         {"PCM1 Playback", NULL, "O08"},
256         {"PCM1 Out", NULL, "PCM1 Playback"},
257 
258         {"I09", NULL, "PCM1 Capture"},
259         {"I22", NULL, "PCM1 Capture"},
260         {"PCM1 Capture", NULL, "PCM1 In"},
261 };
262 
263 static int init_pcmif_priv_data(struct mtk_base_afe *afe)
264 {
265         struct mt8365_afe_private *afe_priv = afe->platform_priv;
266         struct mt8365_pcm_intf_data *pcmif_priv;
267 
268         pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),
269                                   GFP_KERNEL);
270         if (!pcmif_priv)
271                 return -ENOMEM;
272 
273         afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;
274         return 0;
275 }
276 
277 int mt8365_dai_pcm_register(struct mtk_base_afe *afe)
278 {
279         struct mtk_base_afe_dai *dai;
280 
281         dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
282         if (!dai)
283                 return -ENOMEM;
284 
285         list_add(&dai->list, &afe->sub_dais);
286         dai->dai_drivers = mtk_dai_pcm_driver;
287         dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
288         dai->dapm_widgets = mtk_dai_pcm_widgets;
289         dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
290         dai->dapm_routes = mtk_dai_pcm_routes;
291         dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
292         return init_pcmif_priv_data(afe);
293 }
294 

~ [ 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