1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // soc-util.c -- ALSA SoC Audio Layer utility functions 4 // 5 // Copyright 2009 Wolfson Microelectronics PLC. 6 // 7 // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 // Liam Girdwood <lrg@slimlogic.co.uk> 9 10 #include <linux/platform_device.h> 11 #include <linux/export.h> 12 #include <linux/math.h> 13 #include <sound/core.h> 14 #include <sound/pcm.h> 15 #include <sound/pcm_params.h> 16 #include <sound/soc.h> 17 18 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) 19 { 20 return sample_size * channels * tdm_slots; 21 } 22 EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); 23 24 int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) 25 { 26 int sample_size; 27 28 sample_size = snd_pcm_format_width(params_format(params)); 29 if (sample_size < 0) 30 return sample_size; 31 32 return snd_soc_calc_frame_size(sample_size, params_channels(params), 33 1); 34 } 35 EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); 36 37 int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) 38 { 39 return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots); 40 } 41 EXPORT_SYMBOL_GPL(snd_soc_calc_bclk); 42 43 int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) 44 { 45 int ret; 46 47 ret = snd_soc_params_to_frame_size(params); 48 49 if (ret > 0) 50 return ret * params_rate(params); 51 else 52 return ret; 53 } 54 EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); 55 56 /** 57 * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info. 58 * 59 * Calculate the bclk from the params sample rate, the tdm slot count and the 60 * tdm slot width. Optionally round-up the slot count to a given multiple. 61 * Either or both of tdm_width and tdm_slots can be 0. 62 * 63 * If tdm_width == 0: use params_width() as the slot width. 64 * If tdm_slots == 0: use params_channels() as the slot count. 65 * 66 * If slot_multiple > 1 the slot count (or params_channels() if tdm_slots == 0) 67 * will be rounded up to a multiple of slot_multiple. This is mainly useful for 68 * I2S mode, which has a left and right phase so the number of slots is always 69 * a multiple of 2. 70 * 71 * If tdm_width == 0 && tdm_slots == 0 && slot_multiple < 2, this is equivalent 72 * to calling snd_soc_params_to_bclk(). 73 * 74 * @params: Pointer to struct_pcm_hw_params. 75 * @tdm_width: Width in bits of the tdm slots. Must be >= 0. 76 * @tdm_slots: Number of tdm slots per frame. Must be >= 0. 77 * @slot_multiple: If >1 roundup slot count to a multiple of this value. 78 * 79 * Return: bclk frequency in Hz, else a negative error code if params format 80 * is invalid. 81 */ 82 int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, 83 int tdm_width, int tdm_slots, int slot_multiple) 84 { 85 if (!tdm_slots) 86 tdm_slots = params_channels(params); 87 88 if (slot_multiple > 1) 89 tdm_slots = roundup(tdm_slots, slot_multiple); 90 91 if (!tdm_width) { 92 tdm_width = snd_pcm_format_width(params_format(params)); 93 if (tdm_width < 0) 94 return tdm_width; 95 } 96 97 return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); 98 } 99 EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); 100 101 static const struct snd_pcm_hardware dummy_dma_hardware = { 102 /* Random values to keep userspace happy when checking constraints */ 103 .info = SNDRV_PCM_INFO_INTERLEAVED | 104 SNDRV_PCM_INFO_BLOCK_TRANSFER, 105 .buffer_bytes_max = 128*1024, 106 .period_bytes_min = PAGE_SIZE, 107 .period_bytes_max = PAGE_SIZE*2, 108 .periods_min = 2, 109 .periods_max = 128, 110 }; 111 112 113 static const struct snd_soc_component_driver dummy_platform; 114 115 static int dummy_dma_open(struct snd_soc_component *component, 116 struct snd_pcm_substream *substream) 117 { 118 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 119 int i; 120 121 /* 122 * If there are other components associated with rtd, we shouldn't 123 * override their hwparams 124 */ 125 for_each_rtd_components(rtd, i, component) { 126 if (component->driver == &dummy_platform) 127 return 0; 128 } 129 130 /* BE's dont need dummy params */ 131 if (!rtd->dai_link->no_pcm) 132 snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); 133 134 return 0; 135 } 136 137 static const struct snd_soc_component_driver dummy_platform = { 138 .open = dummy_dma_open, 139 }; 140 141 static const struct snd_soc_component_driver dummy_codec = { 142 .idle_bias_on = 1, 143 .use_pmdown_time = 1, 144 .endianness = 1, 145 }; 146 147 #define STUB_RATES SNDRV_PCM_RATE_8000_384000 148 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 149 SNDRV_PCM_FMTBIT_U8 | \ 150 SNDRV_PCM_FMTBIT_S16_LE | \ 151 SNDRV_PCM_FMTBIT_U16_LE | \ 152 SNDRV_PCM_FMTBIT_S24_LE | \ 153 SNDRV_PCM_FMTBIT_S24_3LE | \ 154 SNDRV_PCM_FMTBIT_U24_LE | \ 155 SNDRV_PCM_FMTBIT_S32_LE | \ 156 SNDRV_PCM_FMTBIT_U32_LE | \ 157 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 158 159 /* 160 * Select these from Sound Card Manually 161 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP 162 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC 163 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP 164 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC 165 */ 166 static u64 dummy_dai_formats = 167 SND_SOC_POSSIBLE_DAIFMT_I2S | 168 SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 169 SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 170 SND_SOC_POSSIBLE_DAIFMT_DSP_A | 171 SND_SOC_POSSIBLE_DAIFMT_DSP_B | 172 SND_SOC_POSSIBLE_DAIFMT_AC97 | 173 SND_SOC_POSSIBLE_DAIFMT_PDM | 174 SND_SOC_POSSIBLE_DAIFMT_GATED | 175 SND_SOC_POSSIBLE_DAIFMT_CONT | 176 SND_SOC_POSSIBLE_DAIFMT_NB_NF | 177 SND_SOC_POSSIBLE_DAIFMT_NB_IF | 178 SND_SOC_POSSIBLE_DAIFMT_IB_NF | 179 SND_SOC_POSSIBLE_DAIFMT_IB_IF; 180 181 static const struct snd_soc_dai_ops dummy_dai_ops = { 182 .auto_selectable_formats = &dummy_dai_formats, 183 .num_auto_selectable_formats = 1, 184 }; 185 186 /* 187 * The dummy CODEC is only meant to be used in situations where there is no 188 * actual hardware. 189 * 190 * If there is actual hardware even if it does not have a control bus 191 * the hardware will still have constraints like supported samplerates, etc. 192 * which should be modelled. And the data flow graph also should be modelled 193 * using DAPM. 194 */ 195 static struct snd_soc_dai_driver dummy_dai = { 196 .name = "snd-soc-dummy-dai", 197 .playback = { 198 .stream_name = "Playback", 199 .channels_min = 1, 200 .channels_max = 384, 201 .rates = STUB_RATES, 202 .formats = STUB_FORMATS, 203 }, 204 .capture = { 205 .stream_name = "Capture", 206 .channels_min = 1, 207 .channels_max = 384, 208 .rates = STUB_RATES, 209 .formats = STUB_FORMATS, 210 }, 211 .ops = &dummy_dai_ops, 212 }; 213 214 int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) 215 { 216 if (dai->driver == &dummy_dai) 217 return 1; 218 return 0; 219 } 220 221 int snd_soc_component_is_dummy(struct snd_soc_component *component) 222 { 223 return ((component->driver == &dummy_platform) || 224 (component->driver == &dummy_codec)); 225 } 226 227 static int snd_soc_dummy_probe(struct platform_device *pdev) 228 { 229 int ret; 230 231 ret = devm_snd_soc_register_component(&pdev->dev, 232 &dummy_codec, &dummy_dai, 1); 233 if (ret < 0) 234 return ret; 235 236 ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 237 NULL, 0); 238 239 return ret; 240 } 241 242 static struct platform_driver soc_dummy_driver = { 243 .driver = { 244 .name = "snd-soc-dummy", 245 }, 246 .probe = snd_soc_dummy_probe, 247 }; 248 249 static struct platform_device *soc_dummy_dev; 250 251 int __init snd_soc_util_init(void) 252 { 253 int ret; 254 255 soc_dummy_dev = 256 platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 257 if (IS_ERR(soc_dummy_dev)) 258 return PTR_ERR(soc_dummy_dev); 259 260 ret = platform_driver_register(&soc_dummy_driver); 261 if (ret != 0) 262 platform_device_unregister(soc_dummy_dev); 263 264 return ret; 265 } 266 267 void snd_soc_util_exit(void) 268 { 269 platform_driver_unregister(&soc_dummy_driver); 270 platform_device_unregister(soc_dummy_dev); 271 } 272