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(const 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(const 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(const 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 = 4096, 107 .period_bytes_max = 4096*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 = snd_soc_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_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 148 SNDRV_PCM_FMTBIT_U8 | \ 149 SNDRV_PCM_FMTBIT_S16_LE | \ 150 SNDRV_PCM_FMTBIT_U16_LE | \ 151 SNDRV_PCM_FMTBIT_S24_LE | \ 152 SNDRV_PCM_FMTBIT_S24_3LE | \ 153 SNDRV_PCM_FMTBIT_U24_LE | \ 154 SNDRV_PCM_FMTBIT_S32_LE | \ 155 SNDRV_PCM_FMTBIT_U32_LE | \ 156 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 157 158 /* 159 * Select these from Sound Card Manually 160 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP 161 * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC 162 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP 163 * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC 164 */ 165 static const u64 dummy_dai_formats = 166 SND_SOC_POSSIBLE_DAIFMT_I2S | 167 SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 168 SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 169 SND_SOC_POSSIBLE_DAIFMT_DSP_A | 170 SND_SOC_POSSIBLE_DAIFMT_DSP_B | 171 SND_SOC_POSSIBLE_DAIFMT_AC97 | 172 SND_SOC_POSSIBLE_DAIFMT_PDM | 173 SND_SOC_POSSIBLE_DAIFMT_GATED | 174 SND_SOC_POSSIBLE_DAIFMT_CONT | 175 SND_SOC_POSSIBLE_DAIFMT_NB_NF | 176 SND_SOC_POSSIBLE_DAIFMT_NB_IF | 177 SND_SOC_POSSIBLE_DAIFMT_IB_NF | 178 SND_SOC_POSSIBLE_DAIFMT_IB_IF; 179 180 static const struct snd_soc_dai_ops dummy_dai_ops = { 181 .auto_selectable_formats = &dummy_dai_formats, 182 .num_auto_selectable_formats = 1, 183 }; 184 185 /* 186 * The dummy CODEC is only meant to be used in situations where there is no 187 * actual hardware. 188 * 189 * If there is actual hardware even if it does not have a control bus 190 * the hardware will still have constraints like supported samplerates, etc. 191 * which should be modelled. And the data flow graph also should be modelled 192 * using DAPM. 193 */ 194 static struct snd_soc_dai_driver dummy_dai = { 195 .name = "snd-soc-dummy-dai", 196 .playback = { 197 .stream_name = "Playback", 198 .channels_min = 1, 199 .channels_max = 384, 200 .rates = SNDRV_PCM_RATE_CONTINUOUS, 201 .rate_min = 5512, 202 .rate_max = 768000, 203 .formats = STUB_FORMATS, 204 }, 205 .capture = { 206 .stream_name = "Capture", 207 .channels_min = 1, 208 .channels_max = 384, 209 .rates = SNDRV_PCM_RATE_CONTINUOUS, 210 .rate_min = 5512, 211 .rate_max = 768000, 212 .formats = STUB_FORMATS, 213 }, 214 .ops = &dummy_dai_ops, 215 }; 216 217 int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai) 218 { 219 if (dai->driver == &dummy_dai) 220 return 1; 221 return 0; 222 } 223 EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy); 224 225 int snd_soc_component_is_dummy(struct snd_soc_component *component) 226 { 227 return ((component->driver == &dummy_platform) || 228 (component->driver == &dummy_codec)); 229 } 230 231 struct snd_soc_dai_link_component snd_soc_dummy_dlc = { 232 .of_node = NULL, 233 .dai_name = "snd-soc-dummy-dai", 234 .name = "snd-soc-dummy", 235 }; 236 EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc); 237 238 static int snd_soc_dummy_probe(struct platform_device *pdev) 239 { 240 int ret; 241 242 ret = devm_snd_soc_register_component(&pdev->dev, 243 &dummy_codec, &dummy_dai, 1); 244 if (ret < 0) 245 return ret; 246 247 ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 248 NULL, 0); 249 250 return ret; 251 } 252 253 static struct platform_driver soc_dummy_driver = { 254 .driver = { 255 .name = "snd-soc-dummy", 256 }, 257 .probe = snd_soc_dummy_probe, 258 }; 259 260 static struct platform_device *soc_dummy_dev; 261 262 int __init snd_soc_util_init(void) 263 { 264 int ret; 265 266 soc_dummy_dev = 267 platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 268 if (IS_ERR(soc_dummy_dev)) 269 return PTR_ERR(soc_dummy_dev); 270 271 ret = platform_driver_register(&soc_dummy_driver); 272 if (ret != 0) 273 platform_device_unregister(soc_dummy_dev); 274 275 return ret; 276 } 277 278 void snd_soc_util_exit(void) 279 { 280 platform_driver_unregister(&soc_dummy_driver); 281 platform_device_unregister(soc_dummy_dev); 282 } 283