File Index
Files and directories
Sign in
FILE

soc-utils.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/soc-utils.c C
  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
 18int 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}
 43EXPORT_SYMBOL_GPL(snd_soc_ret);
 44
 45int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots)
 46{
 47	return sample_size * channels * tdm_slots;
 48}
 49EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size);
 50
 51int 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}
 62EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size);
 63
 64int 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}
 68EXPORT_SYMBOL_GPL(snd_soc_calc_bclk);
 69
 70int 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}
 81EXPORT_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 */
109int 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}
126EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk);
127
128static 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
140static const struct snd_soc_component_driver dummy_platform;
141
142static 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
164static const struct snd_soc_component_driver dummy_platform = {
165	.open		= dummy_dma_open,
166};
167
168static 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 */
192static 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
207static 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 */
221static 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
244int 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}
250EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy);
251
252int snd_soc_component_is_dummy(struct snd_soc_component *component)
253{
254	return ((component->driver == &dummy_platform) ||
255		(component->driver == &dummy_codec));
256}
257
258struct 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};
263EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc);
264
265int 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}
276EXPORT_SYMBOL_GPL(snd_soc_dlc_is_dummy);
277
278static 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
293static struct faux_device_ops soc_dummy_ops = {
294	.probe = snd_soc_dummy_probe,
295};
296
297static struct faux_device *soc_dummy_dev;
298
299int __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
309void snd_soc_util_exit(void)
310{
311	faux_device_destroy(soc_dummy_dev);
312}
~/ Read-only file preview.