1632628dfSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+
2632628dfSKuninori Morimoto //
3632628dfSKuninori Morimoto // soc-util.c -- ALSA SoC Audio Layer utility functions
4632628dfSKuninori Morimoto //
5632628dfSKuninori Morimoto // Copyright 2009 Wolfson Microelectronics PLC.
6632628dfSKuninori Morimoto //
7632628dfSKuninori Morimoto // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8632628dfSKuninori Morimoto // Liam Girdwood <lrg@slimlogic.co.uk>
97aae816dSMark Brown
10848dd8beSMark Brown #include <linux/platform_device.h>
11d81a6d71SPaul Gortmaker #include <linux/export.h>
121ef34dd2SRichard Fitzgerald #include <linux/math.h>
137aae816dSMark Brown #include <sound/core.h>
147aae816dSMark Brown #include <sound/pcm.h>
157aae816dSMark Brown #include <sound/pcm_params.h>
167aae816dSMark Brown #include <sound/soc.h>
177aae816dSMark Brown
snd_soc_calc_frame_size(int sample_size,int channels,int tdm_slots)187aae816dSMark Brown int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots)
197aae816dSMark Brown {
207aae816dSMark Brown return sample_size * channels * tdm_slots;
217aae816dSMark Brown }
227aae816dSMark Brown EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size);
237aae816dSMark Brown
snd_soc_params_to_frame_size(const struct snd_pcm_hw_params * params)24f3ac3da7SKrzysztof Kozlowski int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params)
257aae816dSMark Brown {
267aae816dSMark Brown int sample_size;
277aae816dSMark Brown
283d8b2ce0SMark Brown sample_size = snd_pcm_format_width(params_format(params));
293d8b2ce0SMark Brown if (sample_size < 0)
303d8b2ce0SMark Brown return sample_size;
317aae816dSMark Brown
327aae816dSMark Brown return snd_soc_calc_frame_size(sample_size, params_channels(params),
337aae816dSMark Brown 1);
347aae816dSMark Brown }
357aae816dSMark Brown EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size);
367aae816dSMark Brown
snd_soc_calc_bclk(int fs,int sample_size,int channels,int tdm_slots)37c0fa59dfSMark Brown int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots)
38c0fa59dfSMark Brown {
39c0fa59dfSMark Brown return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots);
40c0fa59dfSMark Brown }
41c0fa59dfSMark Brown EXPORT_SYMBOL_GPL(snd_soc_calc_bclk);
42c0fa59dfSMark Brown
snd_soc_params_to_bclk(const struct snd_pcm_hw_params * params)43f3ac3da7SKrzysztof Kozlowski int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *params)
447aae816dSMark Brown {
457aae816dSMark Brown int ret;
467aae816dSMark Brown
477aae816dSMark Brown ret = snd_soc_params_to_frame_size(params);
487aae816dSMark Brown
497aae816dSMark Brown if (ret > 0)
507aae816dSMark Brown return ret * params_rate(params);
517aae816dSMark Brown else
527aae816dSMark Brown return ret;
537aae816dSMark Brown }
547aae816dSMark Brown EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
55848dd8beSMark Brown
561ef34dd2SRichard Fitzgerald /**
571ef34dd2SRichard Fitzgerald * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info.
581ef34dd2SRichard Fitzgerald *
59efe30e2cSRichard Fitzgerald * Calculate the bclk from the params sample rate, the tdm slot count and the
60efe30e2cSRichard Fitzgerald * tdm slot width. Optionally round-up the slot count to a given multiple.
61efe30e2cSRichard Fitzgerald * Either or both of tdm_width and tdm_slots can be 0.
621ef34dd2SRichard Fitzgerald *
63efe30e2cSRichard Fitzgerald * If tdm_width == 0: use params_width() as the slot width.
64efe30e2cSRichard Fitzgerald * If tdm_slots == 0: use params_channels() as the slot count.
651ef34dd2SRichard Fitzgerald *
66efe30e2cSRichard Fitzgerald * If slot_multiple > 1 the slot count (or params_channels() if tdm_slots == 0)
67efe30e2cSRichard Fitzgerald * will be rounded up to a multiple of slot_multiple. This is mainly useful for
681ef34dd2SRichard Fitzgerald * I2S mode, which has a left and right phase so the number of slots is always
691ef34dd2SRichard Fitzgerald * a multiple of 2.
701ef34dd2SRichard Fitzgerald *
71efe30e2cSRichard Fitzgerald * If tdm_width == 0 && tdm_slots == 0 && slot_multiple < 2, this is equivalent
72efe30e2cSRichard Fitzgerald * to calling snd_soc_params_to_bclk().
73efe30e2cSRichard Fitzgerald *
741ef34dd2SRichard Fitzgerald * @params: Pointer to struct_pcm_hw_params.
75efe30e2cSRichard Fitzgerald * @tdm_width: Width in bits of the tdm slots. Must be >= 0.
76efe30e2cSRichard Fitzgerald * @tdm_slots: Number of tdm slots per frame. Must be >= 0.
771ef34dd2SRichard Fitzgerald * @slot_multiple: If >1 roundup slot count to a multiple of this value.
781ef34dd2SRichard Fitzgerald *
791ef34dd2SRichard Fitzgerald * Return: bclk frequency in Hz, else a negative error code if params format
801ef34dd2SRichard Fitzgerald * is invalid.
811ef34dd2SRichard Fitzgerald */
snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params * params,int tdm_width,int tdm_slots,int slot_multiple)82f3ac3da7SKrzysztof Kozlowski int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params,
831ef34dd2SRichard Fitzgerald int tdm_width, int tdm_slots, int slot_multiple)
841ef34dd2SRichard Fitzgerald {
851ef34dd2SRichard Fitzgerald if (!tdm_slots)
861ef34dd2SRichard Fitzgerald tdm_slots = params_channels(params);
871ef34dd2SRichard Fitzgerald
881ef34dd2SRichard Fitzgerald if (slot_multiple > 1)
891ef34dd2SRichard Fitzgerald tdm_slots = roundup(tdm_slots, slot_multiple);
901ef34dd2SRichard Fitzgerald
911ef34dd2SRichard Fitzgerald if (!tdm_width) {
921ef34dd2SRichard Fitzgerald tdm_width = snd_pcm_format_width(params_format(params));
931ef34dd2SRichard Fitzgerald if (tdm_width < 0)
941ef34dd2SRichard Fitzgerald return tdm_width;
951ef34dd2SRichard Fitzgerald }
961ef34dd2SRichard Fitzgerald
971ef34dd2SRichard Fitzgerald return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots);
981ef34dd2SRichard Fitzgerald }
991ef34dd2SRichard Fitzgerald EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk);
1001ef34dd2SRichard Fitzgerald
101cefcc03fSMark Brown static const struct snd_pcm_hardware dummy_dma_hardware = {
102cefcc03fSMark Brown /* Random values to keep userspace happy when checking constraints */
103cefcc03fSMark Brown .info = SNDRV_PCM_INFO_INTERLEAVED |
104cefcc03fSMark Brown SNDRV_PCM_INFO_BLOCK_TRANSFER,
105cefcc03fSMark Brown .buffer_bytes_max = 128*1024,
106cefcc03fSMark Brown .period_bytes_min = PAGE_SIZE,
107cefcc03fSMark Brown .period_bytes_max = PAGE_SIZE*2,
108cefcc03fSMark Brown .periods_min = 2,
109cefcc03fSMark Brown .periods_max = 128,
110cefcc03fSMark Brown };
111cefcc03fSMark Brown
1126c504663SAmadeusz Sławiński
1136c504663SAmadeusz Sławiński static const struct snd_soc_component_driver dummy_platform;
1146c504663SAmadeusz Sławiński
dummy_dma_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)115a49e460fSKuninori Morimoto static int dummy_dma_open(struct snd_soc_component *component,
116a49e460fSKuninori Morimoto struct snd_pcm_substream *substream)
117cefcc03fSMark Brown {
118b1f96e94SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1196c504663SAmadeusz Sławiński int i;
1206c504663SAmadeusz Sławiński
1216c504663SAmadeusz Sławiński /*
1226c504663SAmadeusz Sławiński * If there are other components associated with rtd, we shouldn't
1236c504663SAmadeusz Sławiński * override their hwparams
1246c504663SAmadeusz Sławiński */
1256c504663SAmadeusz Sławiński for_each_rtd_components(rtd, i, component) {
1266c504663SAmadeusz Sławiński if (component->driver == &dummy_platform)
1276c504663SAmadeusz Sławiński return 0;
1286c504663SAmadeusz Sławiński }
1297f05cc98SLiam Girdwood
1307f05cc98SLiam Girdwood /* BE's dont need dummy params */
1317f05cc98SLiam Girdwood if (!rtd->dai_link->no_pcm)
132cefcc03fSMark Brown snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware);
133cefcc03fSMark Brown
134cefcc03fSMark Brown return 0;
135cefcc03fSMark Brown }
136cefcc03fSMark Brown
1372d59ebd3SKuninori Morimoto static const struct snd_soc_component_driver dummy_platform = {
138a49e460fSKuninori Morimoto .open = dummy_dma_open,
139cefcc03fSMark Brown };
140848dd8beSMark Brown
14103a0ddedSKuninori Morimoto static const struct snd_soc_component_driver dummy_codec = {
14203a0ddedSKuninori Morimoto .idle_bias_on = 1,
14303a0ddedSKuninori Morimoto .use_pmdown_time = 1,
14403a0ddedSKuninori Morimoto .endianness = 1,
14503a0ddedSKuninori Morimoto };
14660b6f1a1SStas Sergeev
14760b6f1a1SStas Sergeev #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
14860b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U8 | \
14960b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S16_LE | \
15060b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U16_LE | \
15160b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S24_LE | \
152abc17b29SRohit kumar SNDRV_PCM_FMTBIT_S24_3LE | \
15360b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U24_LE | \
15460b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S32_LE | \
15560b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U32_LE | \
15660b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
157ba9e82a1SKuninori Morimoto
158ba9e82a1SKuninori Morimoto /*
159ba9e82a1SKuninori Morimoto * Select these from Sound Card Manually
160ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP
161ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC
162ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP
163ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC
164ba9e82a1SKuninori Morimoto */
165595265c9SKrzysztof Kozlowski static const u64 dummy_dai_formats =
166ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_I2S |
167ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
168ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
169ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_DSP_A |
170ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_DSP_B |
171ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_AC97 |
172ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_PDM |
173ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_GATED |
174ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_CONT |
175ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_NB_NF |
176ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_NB_IF |
177ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_IB_NF |
178ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_IB_IF;
179ba9e82a1SKuninori Morimoto
180ba9e82a1SKuninori Morimoto static const struct snd_soc_dai_ops dummy_dai_ops = {
181ba9e82a1SKuninori Morimoto .auto_selectable_formats = &dummy_dai_formats,
182ba9e82a1SKuninori Morimoto .num_auto_selectable_formats = 1,
183ba9e82a1SKuninori Morimoto };
184ba9e82a1SKuninori Morimoto
185d76f4198SAnatol Pomozov /*
186d76f4198SAnatol Pomozov * The dummy CODEC is only meant to be used in situations where there is no
187d76f4198SAnatol Pomozov * actual hardware.
188d76f4198SAnatol Pomozov *
189d76f4198SAnatol Pomozov * If there is actual hardware even if it does not have a control bus
190d76f4198SAnatol Pomozov * the hardware will still have constraints like supported samplerates, etc.
191d76f4198SAnatol Pomozov * which should be modelled. And the data flow graph also should be modelled
192d76f4198SAnatol Pomozov * using DAPM.
193d76f4198SAnatol Pomozov */
1947aca69f9SLiam Girdwood static struct snd_soc_dai_driver dummy_dai = {
1957aca69f9SLiam Girdwood .name = "snd-soc-dummy-dai",
19660b6f1a1SStas Sergeev .playback = {
19760b6f1a1SStas Sergeev .stream_name = "Playback",
19860b6f1a1SStas Sergeev .channels_min = 1,
19960b6f1a1SStas Sergeev .channels_max = 384,
200*4adf454fSJerome Brunet .rates = SNDRV_PCM_RATE_CONTINUOUS,
201*4adf454fSJerome Brunet .rate_min = 5512,
202*4adf454fSJerome Brunet .rate_max = 768000,
20360b6f1a1SStas Sergeev .formats = STUB_FORMATS,
20460b6f1a1SStas Sergeev },
20560b6f1a1SStas Sergeev .capture = {
20660b6f1a1SStas Sergeev .stream_name = "Capture",
20760b6f1a1SStas Sergeev .channels_min = 1,
20860b6f1a1SStas Sergeev .channels_max = 384,
209*4adf454fSJerome Brunet .rates = SNDRV_PCM_RATE_CONTINUOUS,
210*4adf454fSJerome Brunet .rate_min = 5512,
211*4adf454fSJerome Brunet .rate_max = 768000,
21260b6f1a1SStas Sergeev .formats = STUB_FORMATS,
21360b6f1a1SStas Sergeev },
214ba9e82a1SKuninori Morimoto .ops = &dummy_dai_ops,
2157aca69f9SLiam Girdwood };
2167aca69f9SLiam Girdwood
snd_soc_dai_is_dummy(const struct snd_soc_dai * dai)217f3ac3da7SKrzysztof Kozlowski int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai)
218bece9e95SLiam Girdwood {
219bece9e95SLiam Girdwood if (dai->driver == &dummy_dai)
220bece9e95SLiam Girdwood return 1;
221bece9e95SLiam Girdwood return 0;
222bece9e95SLiam Girdwood }
223f101583fSSameer Pujar EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy);
224bece9e95SLiam Girdwood
snd_soc_component_is_dummy(struct snd_soc_component * component)2258f1a1681SKuninori Morimoto int snd_soc_component_is_dummy(struct snd_soc_component *component)
2268f1a1681SKuninori Morimoto {
2278f1a1681SKuninori Morimoto return ((component->driver == &dummy_platform) ||
2288f1a1681SKuninori Morimoto (component->driver == &dummy_codec));
2298f1a1681SKuninori Morimoto }
2308f1a1681SKuninori Morimoto
2311d5a2b5dSKuninori Morimoto struct snd_soc_dai_link_component snd_soc_dummy_dlc = {
232d2a4e0d7SKuninori Morimoto .of_node = NULL,
233d2a4e0d7SKuninori Morimoto .dai_name = "snd-soc-dummy-dai",
234d2a4e0d7SKuninori Morimoto .name = "snd-soc-dummy",
235d2a4e0d7SKuninori Morimoto };
2361d5a2b5dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc);
237d2a4e0d7SKuninori Morimoto
snd_soc_dummy_probe(struct platform_device * pdev)238e51e97eeSBill Pemberton static int snd_soc_dummy_probe(struct platform_device *pdev)
239848dd8beSMark Brown {
2407aca69f9SLiam Girdwood int ret;
2417aca69f9SLiam Girdwood
24203a0ddedSKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev,
24303a0ddedSKuninori Morimoto &dummy_codec, &dummy_dai, 1);
2447aca69f9SLiam Girdwood if (ret < 0)
2457aca69f9SLiam Girdwood return ret;
2467aca69f9SLiam Girdwood
2472d59ebd3SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform,
2482d59ebd3SKuninori Morimoto NULL, 0);
2497aca69f9SLiam Girdwood
2507aca69f9SLiam Girdwood return ret;
251848dd8beSMark Brown }
252848dd8beSMark Brown
253848dd8beSMark Brown static struct platform_driver soc_dummy_driver = {
254848dd8beSMark Brown .driver = {
255848dd8beSMark Brown .name = "snd-soc-dummy",
256848dd8beSMark Brown },
257848dd8beSMark Brown .probe = snd_soc_dummy_probe,
258848dd8beSMark Brown };
259848dd8beSMark Brown
260848dd8beSMark Brown static struct platform_device *soc_dummy_dev;
261848dd8beSMark Brown
snd_soc_util_init(void)262fb257897SMark Brown int __init snd_soc_util_init(void)
263848dd8beSMark Brown {
264848dd8beSMark Brown int ret;
265848dd8beSMark Brown
2667d0cd223SUwe Kleine-König soc_dummy_dev =
2677d0cd223SUwe Kleine-König platform_device_register_simple("snd-soc-dummy", -1, NULL, 0);
2687d0cd223SUwe Kleine-König if (IS_ERR(soc_dummy_dev))
2697d0cd223SUwe Kleine-König return PTR_ERR(soc_dummy_dev);
270848dd8beSMark Brown
271848dd8beSMark Brown ret = platform_driver_register(&soc_dummy_driver);
272848dd8beSMark Brown if (ret != 0)
273848dd8beSMark Brown platform_device_unregister(soc_dummy_dev);
274848dd8beSMark Brown
275848dd8beSMark Brown return ret;
276848dd8beSMark Brown }
277848dd8beSMark Brown
snd_soc_util_exit(void)278314d34feSChen Zhongjin void snd_soc_util_exit(void)
279848dd8beSMark Brown {
280848dd8beSMark Brown platform_driver_unregister(&soc_dummy_driver);
281b66c9b91SFabio Estevam platform_device_unregister(soc_dummy_dev);
282848dd8beSMark Brown }
283