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> 12*1ef34dd2SRichard 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 247aae816dSMark Brown int snd_soc_params_to_frame_size(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 437aae816dSMark Brown int snd_soc_params_to_bclk(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 56*1ef34dd2SRichard Fitzgerald /** 57*1ef34dd2SRichard Fitzgerald * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info. 58*1ef34dd2SRichard Fitzgerald * 59*1ef34dd2SRichard Fitzgerald * Calculate the bclk from the params sample rate and the tdm slot count and 60*1ef34dd2SRichard Fitzgerald * tdm slot width. Either or both of tdm_width and tdm_slots can be 0. 61*1ef34dd2SRichard Fitzgerald * 62*1ef34dd2SRichard Fitzgerald * If tdm_width == 0 and tdm_slots > 0: the params_width will be used. 63*1ef34dd2SRichard Fitzgerald * If tdm_width > 0 and tdm_slots == 0: the params_channels will be used 64*1ef34dd2SRichard Fitzgerald * as the slot count. 65*1ef34dd2SRichard Fitzgerald * Both tdm_width and tdm_slots are 0: this is equivalent to calling 66*1ef34dd2SRichard Fitzgerald * snd_soc_params_to_bclk(). 67*1ef34dd2SRichard Fitzgerald * 68*1ef34dd2SRichard Fitzgerald * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0) 69*1ef34dd2SRichard Fitzgerald * will be rounded up to a multiple of this value. This is mainly useful for 70*1ef34dd2SRichard Fitzgerald * I2S mode, which has a left and right phase so the number of slots is always 71*1ef34dd2SRichard Fitzgerald * a multiple of 2. 72*1ef34dd2SRichard Fitzgerald * 73*1ef34dd2SRichard Fitzgerald * @params: Pointer to struct_pcm_hw_params. 74*1ef34dd2SRichard Fitzgerald * @tdm_width: Width in bits of the tdm slots. 75*1ef34dd2SRichard Fitzgerald * @tdm_slots: Number of tdm slots per frame. 76*1ef34dd2SRichard Fitzgerald * @slot_multiple: If >1 roundup slot count to a multiple of this value. 77*1ef34dd2SRichard Fitzgerald * 78*1ef34dd2SRichard Fitzgerald * Return: bclk frequency in Hz, else a negative error code if params format 79*1ef34dd2SRichard Fitzgerald * is invalid. 80*1ef34dd2SRichard Fitzgerald */ 81*1ef34dd2SRichard Fitzgerald int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, 82*1ef34dd2SRichard Fitzgerald int tdm_width, int tdm_slots, int slot_multiple) 83*1ef34dd2SRichard Fitzgerald { 84*1ef34dd2SRichard Fitzgerald if (!tdm_slots) 85*1ef34dd2SRichard Fitzgerald tdm_slots = params_channels(params); 86*1ef34dd2SRichard Fitzgerald 87*1ef34dd2SRichard Fitzgerald if (slot_multiple > 1) 88*1ef34dd2SRichard Fitzgerald tdm_slots = roundup(tdm_slots, slot_multiple); 89*1ef34dd2SRichard Fitzgerald 90*1ef34dd2SRichard Fitzgerald if (!tdm_width) { 91*1ef34dd2SRichard Fitzgerald tdm_width = snd_pcm_format_width(params_format(params)); 92*1ef34dd2SRichard Fitzgerald if (tdm_width < 0) 93*1ef34dd2SRichard Fitzgerald return tdm_width; 94*1ef34dd2SRichard Fitzgerald } 95*1ef34dd2SRichard Fitzgerald 96*1ef34dd2SRichard Fitzgerald return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); 97*1ef34dd2SRichard Fitzgerald } 98*1ef34dd2SRichard Fitzgerald EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); 99*1ef34dd2SRichard Fitzgerald 100cefcc03fSMark Brown static const struct snd_pcm_hardware dummy_dma_hardware = { 101cefcc03fSMark Brown /* Random values to keep userspace happy when checking constraints */ 102cefcc03fSMark Brown .info = SNDRV_PCM_INFO_INTERLEAVED | 103cefcc03fSMark Brown SNDRV_PCM_INFO_BLOCK_TRANSFER, 104cefcc03fSMark Brown .buffer_bytes_max = 128*1024, 105cefcc03fSMark Brown .period_bytes_min = PAGE_SIZE, 106cefcc03fSMark Brown .period_bytes_max = PAGE_SIZE*2, 107cefcc03fSMark Brown .periods_min = 2, 108cefcc03fSMark Brown .periods_max = 128, 109cefcc03fSMark Brown }; 110cefcc03fSMark Brown 1116c504663SAmadeusz Sławiński 1126c504663SAmadeusz Sławiński static const struct snd_soc_component_driver dummy_platform; 1136c504663SAmadeusz Sławiński 114a49e460fSKuninori Morimoto static int dummy_dma_open(struct snd_soc_component *component, 115a49e460fSKuninori Morimoto struct snd_pcm_substream *substream) 116cefcc03fSMark Brown { 1170ceef681SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1186c504663SAmadeusz Sławiński int i; 1196c504663SAmadeusz Sławiński 1206c504663SAmadeusz Sławiński /* 1216c504663SAmadeusz Sławiński * If there are other components associated with rtd, we shouldn't 1226c504663SAmadeusz Sławiński * override their hwparams 1236c504663SAmadeusz Sławiński */ 1246c504663SAmadeusz Sławiński for_each_rtd_components(rtd, i, component) { 1256c504663SAmadeusz Sławiński if (component->driver == &dummy_platform) 1266c504663SAmadeusz Sławiński return 0; 1276c504663SAmadeusz Sławiński } 1287f05cc98SLiam Girdwood 1297f05cc98SLiam Girdwood /* BE's dont need dummy params */ 1307f05cc98SLiam Girdwood if (!rtd->dai_link->no_pcm) 131cefcc03fSMark Brown snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); 132cefcc03fSMark Brown 133cefcc03fSMark Brown return 0; 134cefcc03fSMark Brown } 135cefcc03fSMark Brown 1362d59ebd3SKuninori Morimoto static const struct snd_soc_component_driver dummy_platform = { 137a49e460fSKuninori Morimoto .open = dummy_dma_open, 138cefcc03fSMark Brown }; 139848dd8beSMark Brown 14003a0ddedSKuninori Morimoto static const struct snd_soc_component_driver dummy_codec = { 14103a0ddedSKuninori Morimoto .idle_bias_on = 1, 14203a0ddedSKuninori Morimoto .use_pmdown_time = 1, 14303a0ddedSKuninori Morimoto .endianness = 1, 14403a0ddedSKuninori Morimoto .non_legacy_dai_naming = 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 214bece9e95SLiam Girdwood int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) 215bece9e95SLiam Girdwood { 216bece9e95SLiam Girdwood if (dai->driver == &dummy_dai) 217bece9e95SLiam Girdwood return 1; 218bece9e95SLiam Girdwood return 0; 219bece9e95SLiam Girdwood } 220bece9e95SLiam Girdwood 2218f1a1681SKuninori Morimoto int snd_soc_component_is_dummy(struct snd_soc_component *component) 2228f1a1681SKuninori Morimoto { 2238f1a1681SKuninori Morimoto return ((component->driver == &dummy_platform) || 2248f1a1681SKuninori Morimoto (component->driver == &dummy_codec)); 2258f1a1681SKuninori Morimoto } 2268f1a1681SKuninori Morimoto 227e51e97eeSBill Pemberton static int snd_soc_dummy_probe(struct platform_device *pdev) 228848dd8beSMark Brown { 2297aca69f9SLiam Girdwood int ret; 2307aca69f9SLiam Girdwood 23103a0ddedSKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, 23203a0ddedSKuninori Morimoto &dummy_codec, &dummy_dai, 1); 2337aca69f9SLiam Girdwood if (ret < 0) 2347aca69f9SLiam Girdwood return ret; 2357aca69f9SLiam Girdwood 2362d59ebd3SKuninori Morimoto ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 2372d59ebd3SKuninori Morimoto NULL, 0); 2387aca69f9SLiam Girdwood 2397aca69f9SLiam Girdwood return ret; 240848dd8beSMark Brown } 241848dd8beSMark Brown 242848dd8beSMark Brown static struct platform_driver soc_dummy_driver = { 243848dd8beSMark Brown .driver = { 244848dd8beSMark Brown .name = "snd-soc-dummy", 245848dd8beSMark Brown }, 246848dd8beSMark Brown .probe = snd_soc_dummy_probe, 247848dd8beSMark Brown }; 248848dd8beSMark Brown 249848dd8beSMark Brown static struct platform_device *soc_dummy_dev; 250848dd8beSMark Brown 251fb257897SMark Brown int __init snd_soc_util_init(void) 252848dd8beSMark Brown { 253848dd8beSMark Brown int ret; 254848dd8beSMark Brown 2557d0cd223SUwe Kleine-König soc_dummy_dev = 2567d0cd223SUwe Kleine-König platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 2577d0cd223SUwe Kleine-König if (IS_ERR(soc_dummy_dev)) 2587d0cd223SUwe Kleine-König return PTR_ERR(soc_dummy_dev); 259848dd8beSMark Brown 260848dd8beSMark Brown ret = platform_driver_register(&soc_dummy_driver); 261848dd8beSMark Brown if (ret != 0) 262848dd8beSMark Brown platform_device_unregister(soc_dummy_dev); 263848dd8beSMark Brown 264848dd8beSMark Brown return ret; 265848dd8beSMark Brown } 266848dd8beSMark Brown 267fb257897SMark Brown void __exit snd_soc_util_exit(void) 268848dd8beSMark Brown { 269848dd8beSMark Brown platform_driver_unregister(&soc_dummy_driver); 270b66c9b91SFabio Estevam platform_device_unregister(soc_dummy_dev); 271848dd8beSMark Brown } 272