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 and the tdm slot count and 60 * tdm slot width. Either or both of tdm_width and tdm_slots can be 0. 61 * 62 * If tdm_width == 0 and tdm_slots > 0: the params_width will be used. 63 * If tdm_width > 0 and tdm_slots == 0: the params_channels will be used 64 * as the slot count. 65 * Both tdm_width and tdm_slots are 0: this is equivalent to calling 66 * snd_soc_params_to_bclk(). 67 * 68 * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0) 69 * will be rounded up to a multiple of this value. This is mainly useful for 70 * I2S mode, which has a left and right phase so the number of slots is always 71 * a multiple of 2. 72 * 73 * @params: Pointer to struct_pcm_hw_params. 74 * @tdm_width: Width in bits of the tdm slots. 75 * @tdm_slots: Number of tdm slots per frame. 76 * @slot_multiple: If >1 roundup slot count to a multiple of this value. 77 * 78 * Return: bclk frequency in Hz, else a negative error code if params format 79 * is invalid. 80 */ 81 int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, 82 int tdm_width, int tdm_slots, int slot_multiple) 83 { 84 if (!tdm_slots) 85 tdm_slots = params_channels(params); 86 87 if (slot_multiple > 1) 88 tdm_slots = roundup(tdm_slots, slot_multiple); 89 90 if (!tdm_width) { 91 tdm_width = snd_pcm_format_width(params_format(params)); 92 if (tdm_width < 0) 93 return tdm_width; 94 } 95 96 return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); 97 } 98 EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); 99 100 static const struct snd_pcm_hardware dummy_dma_hardware = { 101 /* Random values to keep userspace happy when checking constraints */ 102 .info = SNDRV_PCM_INFO_INTERLEAVED | 103 SNDRV_PCM_INFO_BLOCK_TRANSFER, 104 .buffer_bytes_max = 128*1024, 105 .period_bytes_min = PAGE_SIZE, 106 .period_bytes_max = PAGE_SIZE*2, 107 .periods_min = 2, 108 .periods_max = 128, 109 }; 110 111 112 static const struct snd_soc_component_driver dummy_platform; 113 114 static int dummy_dma_open(struct snd_soc_component *component, 115 struct snd_pcm_substream *substream) 116 { 117 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 118 int i; 119 120 /* 121 * If there are other components associated with rtd, we shouldn't 122 * override their hwparams 123 */ 124 for_each_rtd_components(rtd, i, component) { 125 if (component->driver == &dummy_platform) 126 return 0; 127 } 128 129 /* BE's dont need dummy params */ 130 if (!rtd->dai_link->no_pcm) 131 snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); 132 133 return 0; 134 } 135 136 static const struct snd_soc_component_driver dummy_platform = { 137 .open = dummy_dma_open, 138 }; 139 140 static const struct snd_soc_component_driver dummy_codec = { 141 .idle_bias_on = 1, 142 .use_pmdown_time = 1, 143 .endianness = 1, 144 }; 145 146 #define STUB_RATES SNDRV_PCM_RATE_8000_384000 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 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 = STUB_RATES, 201 .formats = STUB_FORMATS, 202 }, 203 .capture = { 204 .stream_name = "Capture", 205 .channels_min = 1, 206 .channels_max = 384, 207 .rates = STUB_RATES, 208 .formats = STUB_FORMATS, 209 }, 210 .ops = &dummy_dai_ops, 211 }; 212 213 int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) 214 { 215 if (dai->driver == &dummy_dai) 216 return 1; 217 return 0; 218 } 219 220 int snd_soc_component_is_dummy(struct snd_soc_component *component) 221 { 222 return ((component->driver == &dummy_platform) || 223 (component->driver == &dummy_codec)); 224 } 225 226 static int snd_soc_dummy_probe(struct platform_device *pdev) 227 { 228 int ret; 229 230 ret = devm_snd_soc_register_component(&pdev->dev, 231 &dummy_codec, &dummy_dai, 1); 232 if (ret < 0) 233 return ret; 234 235 ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 236 NULL, 0); 237 238 return ret; 239 } 240 241 static struct platform_driver soc_dummy_driver = { 242 .driver = { 243 .name = "snd-soc-dummy", 244 }, 245 .probe = snd_soc_dummy_probe, 246 }; 247 248 static struct platform_device *soc_dummy_dev; 249 250 int __init snd_soc_util_init(void) 251 { 252 int ret; 253 254 soc_dummy_dev = 255 platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 256 if (IS_ERR(soc_dummy_dev)) 257 return PTR_ERR(soc_dummy_dev); 258 259 ret = platform_driver_register(&soc_dummy_driver); 260 if (ret != 0) 261 platform_device_unregister(soc_dummy_dev); 262 263 return ret; 264 } 265 266 void __exit snd_soc_util_exit(void) 267 { 268 platform_driver_unregister(&soc_dummy_driver); 269 platform_device_unregister(soc_dummy_dev); 270 } 271