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/device/faux.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_ret(const struct device *dev, int ret, const char *fmt, ...) 19 { 20 struct va_format vaf; 21 va_list args; 22 23 /* Positive, Zero values are not errors */ 24 if (ret >= 0) 25 return ret; 26 27 /* Negative values might be errors */ 28 switch (ret) { 29 case -EPROBE_DEFER: 30 case -ENOTSUPP: 31 case -EOPNOTSUPP: 32 break; 33 default: 34 va_start(args, fmt); 35 vaf.fmt = fmt; 36 vaf.va = &args; 37 38 dev_err(dev, "ASoC error (%d): %pV", ret, &vaf); 39 } 40 41 return ret; 42 } 43 EXPORT_SYMBOL_GPL(snd_soc_ret); 44 45 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) 46 { 47 return sample_size * channels * tdm_slots; 48 } 49 EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); 50 51 int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params) 52 { 53 int sample_size; 54 55 sample_size = snd_pcm_format_width(params_format(params)); 56 if (sample_size < 0) 57 return sample_size; 58 59 return snd_soc_calc_frame_size(sample_size, params_channels(params), 60 1); 61 } 62 EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); 63 64 int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) 65 { 66 return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots); 67 } 68 EXPORT_SYMBOL_GPL(snd_soc_calc_bclk); 69 70 int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *params) 71 { 72 int ret; 73 74 ret = snd_soc_params_to_frame_size(params); 75 76 if (ret > 0) 77 return ret * params_rate(params); 78 else 79 return ret; 80 } 81 EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); 82 83 /** 84 * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info. 85 * 86 * Calculate the bclk from the params sample rate, the tdm slot count and the 87 * tdm slot width. Optionally round-up the slot count to a given multiple. 88 * Either or both of tdm_width and tdm_slots can be 0. 89 * 90 * If tdm_width == 0: use params_width() as the slot width. 91 * If tdm_slots == 0: use params_channels() as the slot count. 92 * 93 * If slot_multiple > 1 the slot count (or params_channels() if tdm_slots == 0) 94 * will be rounded up to a multiple of slot_multiple. This is mainly useful for 95 * I2S mode, which has a left and right phase so the number of slots is always 96 * a multiple of 2. 97 * 98 * If tdm_width == 0 && tdm_slots == 0 && slot_multiple < 2, this is equivalent 99 * to calling snd_soc_params_to_bclk(). 100 * 101 * @params: Pointer to struct_pcm_hw_params. 102 * @tdm_width: Width in bits of the tdm slots. Must be >= 0. 103 * @tdm_slots: Number of tdm slots per frame. Must be >= 0. 104 * @slot_multiple: If >1 roundup slot count to a multiple of this value. 105 * 106 * Return: bclk frequency in Hz, else a negative error code if params format 107 * is invalid. 108 */ 109 int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params, 110 int tdm_width, int tdm_slots, int slot_multiple) 111 { 112 if (!tdm_slots) 113 tdm_slots = params_channels(params); 114 115 if (slot_multiple > 1) 116 tdm_slots = roundup(tdm_slots, slot_multiple); 117 118 if (!tdm_width) { 119 tdm_width = snd_pcm_format_width(params_format(params)); 120 if (tdm_width < 0) 121 return tdm_width; 122 } 123 124 return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); 125 } 126 EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); 127 128 static const struct snd_pcm_hardware dummy_dma_hardware = { 129 /* Random values to keep userspace happy when checking constraints */ 130 .info = SNDRV_PCM_INFO_INTERLEAVED | 131 SNDRV_PCM_INFO_BLOCK_TRANSFER, 132 .buffer_bytes_max = 128*1024, 133 .period_bytes_min = 4096, 134 .period_bytes_max = 4096*2, 135 .periods_min = 2, 136 .periods_max = 128, 137 }; 138 139 140 static const struct snd_soc_component_driver dummy_platform; 141 142 static int dummy_dma_open(struct snd_soc_component *component, 143 struct snd_pcm_substream *substream) 144 { 145 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 146 int i; 147 148 /* 149 * If there are other components associated with rtd, we shouldn't 150 * override their hwparams 151 */ 152 for_each_rtd_components(rtd, i, component) { 153 if (component->driver == &dummy_platform) 154 return 0; 155 } 156 157 /* BE's dont need dummy params */ 158 if (!rtd->dai_link->no_pcm) 159 snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); 160 161 return 0; 162 } 163 164 static const struct snd_soc_component_driver dummy_platform = { 165 .open = dummy_dma_open, 166 }; 167 168 static const struct snd_soc_component_driver dummy_codec = { 169 .idle_bias_on = 1, 170 .use_pmdown_time = 1, 171 .endianness = 1, 172 }; 173 174 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 175 SNDRV_PCM_FMTBIT_U8 | \ 176 SNDRV_PCM_FMTBIT_S16_LE | \ 177 SNDRV_PCM_FMTBIT_U16_LE | \ 178 SNDRV_PCM_FMTBIT_S24_LE | \ 179 SNDRV_PCM_FMTBIT_S24_3LE | \ 180 SNDRV_PCM_FMTBIT_U24_LE | \ 181 SNDRV_PCM_FMTBIT_S32_LE | \ 182 SNDRV_PCM_FMTBIT_U32_LE | \ 183 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 184 185 /* 186 * Select these from Sound Card Manually 187 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP 188 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC 189 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP 190 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC 191 */ 192 static const u64 dummy_dai_formats = 193 SND_SOC_POSSIBLE_DAIFMT_I2S | 194 SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 195 SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 196 SND_SOC_POSSIBLE_DAIFMT_DSP_A | 197 SND_SOC_POSSIBLE_DAIFMT_DSP_B | 198 SND_SOC_POSSIBLE_DAIFMT_AC97 | 199 SND_SOC_POSSIBLE_DAIFMT_PDM | 200 SND_SOC_POSSIBLE_DAIFMT_GATED | 201 SND_SOC_POSSIBLE_DAIFMT_CONT | 202 SND_SOC_POSSIBLE_DAIFMT_NB_NF | 203 SND_SOC_POSSIBLE_DAIFMT_NB_IF | 204 SND_SOC_POSSIBLE_DAIFMT_IB_NF | 205 SND_SOC_POSSIBLE_DAIFMT_IB_IF; 206 207 static const struct snd_soc_dai_ops dummy_dai_ops = { 208 .auto_selectable_formats = &dummy_dai_formats, 209 .num_auto_selectable_formats = 1, 210 }; 211 212 /* 213 * The dummy CODEC is only meant to be used in situations where there is no 214 * actual hardware. 215 * 216 * If there is actual hardware even if it does not have a control bus 217 * the hardware will still have constraints like supported samplerates, etc. 218 * which should be modelled. And the data flow graph also should be modelled 219 * using DAPM. 220 */ 221 static struct snd_soc_dai_driver dummy_dai = { 222 .name = "snd-soc-dummy-dai", 223 .playback = { 224 .stream_name = "Playback", 225 .channels_min = 1, 226 .channels_max = 384, 227 .rates = SNDRV_PCM_RATE_CONTINUOUS, 228 .rate_min = 5512, 229 .rate_max = 768000, 230 .formats = STUB_FORMATS, 231 }, 232 .capture = { 233 .stream_name = "Capture", 234 .channels_min = 1, 235 .channels_max = 384, 236 .rates = SNDRV_PCM_RATE_CONTINUOUS, 237 .rate_min = 5512, 238 .rate_max = 768000, 239 .formats = STUB_FORMATS, 240 }, 241 .ops = &dummy_dai_ops, 242 }; 243 244 int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai) 245 { 246 if (dai->driver == &dummy_dai) 247 return 1; 248 return 0; 249 } 250 EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy); 251 252 int snd_soc_component_is_dummy(struct snd_soc_component *component) 253 { 254 return ((component->driver == &dummy_platform) || 255 (component->driver == &dummy_codec)); 256 } 257 258 struct snd_soc_dai_link_component snd_soc_dummy_dlc = { 259 .of_node = NULL, 260 .dai_name = "snd-soc-dummy-dai", 261 .name = "snd-soc-dummy", 262 }; 263 EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc); 264 265 int snd_soc_dlc_is_dummy(struct snd_soc_dai_link_component *dlc) 266 { 267 if (dlc == &snd_soc_dummy_dlc) 268 return true; 269 270 if ((dlc->name && strcmp(dlc->name, snd_soc_dummy_dlc.name) == 0) || 271 (dlc->dai_name && strcmp(dlc->dai_name, snd_soc_dummy_dlc.dai_name) == 0)) 272 return true; 273 274 return false; 275 } 276 EXPORT_SYMBOL_GPL(snd_soc_dlc_is_dummy); 277 278 static int snd_soc_dummy_probe(struct faux_device *fdev) 279 { 280 int ret; 281 282 ret = devm_snd_soc_register_component(&fdev->dev, 283 &dummy_codec, &dummy_dai, 1); 284 if (ret < 0) 285 return ret; 286 287 ret = devm_snd_soc_register_component(&fdev->dev, &dummy_platform, 288 NULL, 0); 289 290 return ret; 291 } 292 293 static struct faux_device_ops soc_dummy_ops = { 294 .probe = snd_soc_dummy_probe, 295 }; 296 297 static struct faux_device *soc_dummy_dev; 298 299 int __init snd_soc_util_init(void) 300 { 301 soc_dummy_dev = faux_device_create("snd-soc-dummy", NULL, 302 &soc_dummy_ops); 303 if (!soc_dummy_dev) 304 return -ENODEV; 305 306 return 0; 307 } 308 309 void snd_soc_util_exit(void) 310 { 311 faux_device_destroy(soc_dummy_dev); 312 } 313