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 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 24*f3ac3da7SKrzysztof 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 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 43*f3ac3da7SKrzysztof 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 */ 82*f3ac3da7SKrzysztof 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 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 147abc17b29SRohit kumar #define STUB_RATES SNDRV_PCM_RATE_8000_384000 14860b6f1a1SStas Sergeev #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 14960b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U8 | \ 15060b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S16_LE | \ 15160b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U16_LE | \ 15260b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S24_LE | \ 153abc17b29SRohit kumar SNDRV_PCM_FMTBIT_S24_3LE | \ 15460b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U24_LE | \ 15560b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_S32_LE | \ 15660b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_U32_LE | \ 15760b6f1a1SStas Sergeev SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 158ba9e82a1SKuninori Morimoto 159ba9e82a1SKuninori Morimoto /* 160ba9e82a1SKuninori Morimoto * Select these from Sound Card Manually 161ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP 162ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC 163ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP 164ba9e82a1SKuninori Morimoto * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC 165ba9e82a1SKuninori Morimoto */ 166ba9e82a1SKuninori Morimoto static u64 dummy_dai_formats = 167ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_I2S | 168ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 169ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 170ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_DSP_A | 171ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_DSP_B | 172ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_AC97 | 173ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_PDM | 174ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_GATED | 175ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_CONT | 176ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_NB_NF | 177ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_NB_IF | 178ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_IB_NF | 179ba9e82a1SKuninori Morimoto SND_SOC_POSSIBLE_DAIFMT_IB_IF; 180ba9e82a1SKuninori Morimoto 181ba9e82a1SKuninori Morimoto static const struct snd_soc_dai_ops dummy_dai_ops = { 182ba9e82a1SKuninori Morimoto .auto_selectable_formats = &dummy_dai_formats, 183ba9e82a1SKuninori Morimoto .num_auto_selectable_formats = 1, 184ba9e82a1SKuninori Morimoto }; 185ba9e82a1SKuninori Morimoto 186d76f4198SAnatol Pomozov /* 187d76f4198SAnatol Pomozov * The dummy CODEC is only meant to be used in situations where there is no 188d76f4198SAnatol Pomozov * actual hardware. 189d76f4198SAnatol Pomozov * 190d76f4198SAnatol Pomozov * If there is actual hardware even if it does not have a control bus 191d76f4198SAnatol Pomozov * the hardware will still have constraints like supported samplerates, etc. 192d76f4198SAnatol Pomozov * which should be modelled. And the data flow graph also should be modelled 193d76f4198SAnatol Pomozov * using DAPM. 194d76f4198SAnatol Pomozov */ 1957aca69f9SLiam Girdwood static struct snd_soc_dai_driver dummy_dai = { 1967aca69f9SLiam Girdwood .name = "snd-soc-dummy-dai", 19760b6f1a1SStas Sergeev .playback = { 19860b6f1a1SStas Sergeev .stream_name = "Playback", 19960b6f1a1SStas Sergeev .channels_min = 1, 20060b6f1a1SStas Sergeev .channels_max = 384, 20160b6f1a1SStas Sergeev .rates = STUB_RATES, 20260b6f1a1SStas Sergeev .formats = STUB_FORMATS, 20360b6f1a1SStas Sergeev }, 20460b6f1a1SStas Sergeev .capture = { 20560b6f1a1SStas Sergeev .stream_name = "Capture", 20660b6f1a1SStas Sergeev .channels_min = 1, 20760b6f1a1SStas Sergeev .channels_max = 384, 20860b6f1a1SStas Sergeev .rates = STUB_RATES, 20960b6f1a1SStas Sergeev .formats = STUB_FORMATS, 21060b6f1a1SStas Sergeev }, 211ba9e82a1SKuninori Morimoto .ops = &dummy_dai_ops, 2127aca69f9SLiam Girdwood }; 2137aca69f9SLiam Girdwood 214*f3ac3da7SKrzysztof Kozlowski int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai) 215bece9e95SLiam Girdwood { 216bece9e95SLiam Girdwood if (dai->driver == &dummy_dai) 217bece9e95SLiam Girdwood return 1; 218bece9e95SLiam Girdwood return 0; 219bece9e95SLiam Girdwood } 220f101583fSSameer Pujar EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy); 221bece9e95SLiam Girdwood 2228f1a1681SKuninori Morimoto int snd_soc_component_is_dummy(struct snd_soc_component *component) 2238f1a1681SKuninori Morimoto { 2248f1a1681SKuninori Morimoto return ((component->driver == &dummy_platform) || 2258f1a1681SKuninori Morimoto (component->driver == &dummy_codec)); 2268f1a1681SKuninori Morimoto } 2278f1a1681SKuninori Morimoto 2281d5a2b5dSKuninori Morimoto struct snd_soc_dai_link_component snd_soc_dummy_dlc = { 229d2a4e0d7SKuninori Morimoto .of_node = NULL, 230d2a4e0d7SKuninori Morimoto .dai_name = "snd-soc-dummy-dai", 231d2a4e0d7SKuninori Morimoto .name = "snd-soc-dummy", 232d2a4e0d7SKuninori Morimoto }; 2331d5a2b5dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc); 234d2a4e0d7SKuninori Morimoto 235e51e97eeSBill Pemberton static int snd_soc_dummy_probe(struct platform_device *pdev) 236848dd8beSMark Brown { 2377aca69f9SLiam Girdwood int ret; 2387aca69f9SLiam Girdwood 23903a0ddedSKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, 24003a0ddedSKuninori Morimoto &dummy_codec, &dummy_dai, 1); 2417aca69f9SLiam Girdwood if (ret < 0) 2427aca69f9SLiam Girdwood return ret; 2437aca69f9SLiam Girdwood 2442d59ebd3SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 2452d59ebd3SKuninori Morimoto NULL, 0); 2467aca69f9SLiam Girdwood 2477aca69f9SLiam Girdwood return ret; 248848dd8beSMark Brown } 249848dd8beSMark Brown 250848dd8beSMark Brown static struct platform_driver soc_dummy_driver = { 251848dd8beSMark Brown .driver = { 252848dd8beSMark Brown .name = "snd-soc-dummy", 253848dd8beSMark Brown }, 254848dd8beSMark Brown .probe = snd_soc_dummy_probe, 255848dd8beSMark Brown }; 256848dd8beSMark Brown 257848dd8beSMark Brown static struct platform_device *soc_dummy_dev; 258848dd8beSMark Brown 259fb257897SMark Brown int __init snd_soc_util_init(void) 260848dd8beSMark Brown { 261848dd8beSMark Brown int ret; 262848dd8beSMark Brown 2637d0cd223SUwe Kleine-König soc_dummy_dev = 2647d0cd223SUwe Kleine-König platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 2657d0cd223SUwe Kleine-König if (IS_ERR(soc_dummy_dev)) 2667d0cd223SUwe Kleine-König return PTR_ERR(soc_dummy_dev); 267848dd8beSMark Brown 268848dd8beSMark Brown ret = platform_driver_register(&soc_dummy_driver); 269848dd8beSMark Brown if (ret != 0) 270848dd8beSMark Brown platform_device_unregister(soc_dummy_dev); 271848dd8beSMark Brown 272848dd8beSMark Brown return ret; 273848dd8beSMark Brown } 274848dd8beSMark Brown 275314d34feSChen Zhongjin void snd_soc_util_exit(void) 276848dd8beSMark Brown { 277848dd8beSMark Brown platform_driver_unregister(&soc_dummy_driver); 278b66c9b91SFabio Estevam platform_device_unregister(soc_dummy_dev); 279848dd8beSMark Brown } 280