File Index
Files and directories
Sign in
FILE

ff-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/fireface/ff-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * ff-pcm.c - a part of driver for RME Fireface series
  4 *
  5 * Copyright (c) 2015-2017 Takashi Sakamoto
  6 */
  7
  8#include "ff.h"
  9
 10static int hw_rule_rate(struct snd_pcm_hw_params *params,
 11			struct snd_pcm_hw_rule *rule)
 12{
 13	const unsigned int *pcm_channels = rule->private;
 14	struct snd_interval *r =
 15		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 16	const struct snd_interval *c =
 17		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 18	struct snd_interval t = {
 19		.min = UINT_MAX, .max = 0, .integer = 1
 20	};
 21	unsigned int i;
 22
 23	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
 24		enum snd_ff_stream_mode mode;
 25		int err;
 26
 27		err = snd_ff_stream_get_multiplier_mode(i, &mode);
 28		if (err < 0)
 29			continue;
 30
 31		if (!snd_interval_test(c, pcm_channels[mode]))
 32			continue;
 33
 34		t.min = min(t.min, amdtp_rate_table[i]);
 35		t.max = max(t.max, amdtp_rate_table[i]);
 36	}
 37
 38	return snd_interval_refine(r, &t);
 39}
 40
 41static int hw_rule_channels(struct snd_pcm_hw_params *params,
 42			    struct snd_pcm_hw_rule *rule)
 43{
 44	const unsigned int *pcm_channels = rule->private;
 45	struct snd_interval *c =
 46		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 47	const struct snd_interval *r =
 48		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
 49	struct snd_interval t = {
 50		.min = UINT_MAX, .max = 0, .integer = 1
 51	};
 52	unsigned int i;
 53
 54	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
 55		enum snd_ff_stream_mode mode;
 56		int err;
 57
 58		err = snd_ff_stream_get_multiplier_mode(i, &mode);
 59		if (err < 0)
 60			continue;
 61
 62		if (!snd_interval_test(r, amdtp_rate_table[i]))
 63			continue;
 64
 65		t.min = min(t.min, pcm_channels[mode]);
 66		t.max = max(t.max, pcm_channels[mode]);
 67	}
 68
 69	return snd_interval_refine(c, &t);
 70}
 71
 72static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
 73				     const unsigned int *pcm_channels)
 74{
 75	unsigned int rate, channels;
 76	int i;
 77
 78	hw->channels_min = UINT_MAX;
 79	hw->channels_max = 0;
 80	hw->rate_min = UINT_MAX;
 81	hw->rate_max = 0;
 82
 83	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
 84		enum snd_ff_stream_mode mode;
 85		int err;
 86
 87		err = snd_ff_stream_get_multiplier_mode(i, &mode);
 88		if (err < 0)
 89			continue;
 90
 91		channels = pcm_channels[mode];
 92		if (pcm_channels[mode] == 0)
 93			continue;
 94		hw->channels_min = min(hw->channels_min, channels);
 95		hw->channels_max = max(hw->channels_max, channels);
 96
 97		rate = amdtp_rate_table[i];
 98		hw->rates |= snd_pcm_rate_to_rate_bit(rate);
 99		hw->rate_min = min(hw->rate_min, rate);
100		hw->rate_max = max(hw->rate_max, rate);
101	}
102}
103
104static int pcm_init_hw_params(struct snd_ff *ff,
105			      struct snd_pcm_substream *substream)
106{
107	struct snd_pcm_runtime *runtime = substream->runtime;
108	struct amdtp_stream *s;
109	const unsigned int *pcm_channels;
110	int err;
111
112	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
113		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
114		s = &ff->tx_stream;
115		pcm_channels = ff->spec->pcm_capture_channels;
116	} else {
117		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
118		s = &ff->rx_stream;
119		pcm_channels = ff->spec->pcm_playback_channels;
120	}
121
122	limit_channels_and_rates(&runtime->hw, pcm_channels);
123
124	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
125				  hw_rule_channels, (void *)pcm_channels,
126				  SNDRV_PCM_HW_PARAM_RATE, -1);
127	if (err < 0)
128		return err;
129
130	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
131				  hw_rule_rate, (void *)pcm_channels,
132				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
133	if (err < 0)
134		return err;
135
136	return amdtp_ff_add_pcm_hw_constraints(s, runtime);
137}
138
139static int pcm_open(struct snd_pcm_substream *substream)
140{
141	struct snd_ff *ff = substream->private_data;
142	struct amdtp_domain *d = &ff->domain;
143	unsigned int rate;
144	enum snd_ff_clock_src src;
145	int i, err;
146
147	err = snd_ff_stream_lock_try(ff);
148	if (err < 0)
149		return err;
150
151	err = pcm_init_hw_params(ff, substream);
152	if (err < 0)
153		goto release_lock;
154
155	err = ff->spec->protocol->get_clock(ff, &rate, &src);
156	if (err < 0)
157		goto release_lock;
158
159	scoped_guard(mutex, &ff->mutex) {
160		// When source of clock is not internal or any stream is reserved for
161		// transmission of PCM frames, the available sampling rate is limited
162		// at current one.
163		if (src != SND_FF_CLOCK_SRC_INTERNAL) {
164			for (i = 0; i < CIP_SFC_COUNT; ++i) {
165				if (amdtp_rate_table[i] == rate)
166					break;
167			}
168
169			// The unit is configured at sampling frequency which packet
170			// streaming engine can't support.
171			if (i >= CIP_SFC_COUNT) {
172				err = -EIO;
173				goto release_lock;
174			}
175
176			substream->runtime->hw.rate_min = rate;
177			substream->runtime->hw.rate_max = rate;
178		} else {
179			if (ff->substreams_counter > 0) {
180				unsigned int frames_per_period = d->events_per_period;
181				unsigned int frames_per_buffer = d->events_per_buffer;
182
183				rate = amdtp_rate_table[ff->rx_stream.sfc];
184				substream->runtime->hw.rate_min = rate;
185				substream->runtime->hw.rate_max = rate;
186
187				err = snd_pcm_hw_constraint_minmax(substream->runtime,
188								   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
189								   frames_per_period, frames_per_period);
190				if (err < 0)
191					goto release_lock;
192
193				err = snd_pcm_hw_constraint_minmax(substream->runtime,
194								   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
195								   frames_per_buffer, frames_per_buffer);
196				if (err < 0)
197					goto release_lock;
198			}
199		}
200	}
201
202	snd_pcm_set_sync(substream);
203
204	return 0;
205
206release_lock:
207	snd_ff_stream_lock_release(ff);
208	return err;
209}
210
211static int pcm_close(struct snd_pcm_substream *substream)
212{
213	struct snd_ff *ff = substream->private_data;
214
215	snd_ff_stream_lock_release(ff);
216
217	return 0;
218}
219
220static int pcm_hw_params(struct snd_pcm_substream *substream,
221			 struct snd_pcm_hw_params *hw_params)
222{
223	struct snd_ff *ff = substream->private_data;
224	int err = 0;
225
226	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
227		unsigned int rate = params_rate(hw_params);
228		unsigned int frames_per_period = params_period_size(hw_params);
229		unsigned int frames_per_buffer = params_buffer_size(hw_params);
230
231		guard(mutex)(&ff->mutex);
232		err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
233						   frames_per_buffer);
234		if (err >= 0)
235			++ff->substreams_counter;
236	}
237
238	return err;
239}
240
241static int pcm_hw_free(struct snd_pcm_substream *substream)
242{
243	struct snd_ff *ff = substream->private_data;
244
245	guard(mutex)(&ff->mutex);
246
247	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
248		--ff->substreams_counter;
249
250	snd_ff_stream_stop_duplex(ff);
251
252	return 0;
253}
254
255static int pcm_capture_prepare(struct snd_pcm_substream *substream)
256{
257	struct snd_ff *ff = substream->private_data;
258	struct snd_pcm_runtime *runtime = substream->runtime;
259	int err;
260
261	guard(mutex)(&ff->mutex);
262
263	err = snd_ff_stream_start_duplex(ff, runtime->rate);
264	if (err >= 0)
265		amdtp_stream_pcm_prepare(&ff->tx_stream);
266
267	return err;
268}
269
270static int pcm_playback_prepare(struct snd_pcm_substream *substream)
271{
272	struct snd_ff *ff = substream->private_data;
273	struct snd_pcm_runtime *runtime = substream->runtime;
274	int err;
275
276	guard(mutex)(&ff->mutex);
277
278	err = snd_ff_stream_start_duplex(ff, runtime->rate);
279	if (err >= 0)
280		amdtp_stream_pcm_prepare(&ff->rx_stream);
281
282	return err;
283}
284
285static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
286{
287	struct snd_ff *ff = substream->private_data;
288
289	switch (cmd) {
290	case SNDRV_PCM_TRIGGER_START:
291		amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
292		break;
293	case SNDRV_PCM_TRIGGER_STOP:
294		amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
295		break;
296	default:
297		return -EINVAL;
298	}
299
300	return 0;
301}
302
303static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
304{
305	struct snd_ff *ff = substream->private_data;
306
307	switch (cmd) {
308	case SNDRV_PCM_TRIGGER_START:
309		amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
310		break;
311	case SNDRV_PCM_TRIGGER_STOP:
312		amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
313		break;
314	default:
315		return -EINVAL;
316	}
317
318	return 0;
319}
320
321static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
322{
323	struct snd_ff *ff = sbstrm->private_data;
324
325	return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);
326}
327
328static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
329{
330	struct snd_ff *ff = sbstrm->private_data;
331
332	return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);
333}
334
335static int pcm_capture_ack(struct snd_pcm_substream *substream)
336{
337	struct snd_ff *ff = substream->private_data;
338
339	return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);
340}
341
342static int pcm_playback_ack(struct snd_pcm_substream *substream)
343{
344	struct snd_ff *ff = substream->private_data;
345
346	return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
347}
348
349int snd_ff_create_pcm_devices(struct snd_ff *ff)
350{
351	static const struct snd_pcm_ops pcm_capture_ops = {
352		.open		= pcm_open,
353		.close		= pcm_close,
354		.hw_params	= pcm_hw_params,
355		.hw_free	= pcm_hw_free,
356		.prepare	= pcm_capture_prepare,
357		.trigger	= pcm_capture_trigger,
358		.pointer	= pcm_capture_pointer,
359		.ack		= pcm_capture_ack,
360	};
361	static const struct snd_pcm_ops pcm_playback_ops = {
362		.open		= pcm_open,
363		.close		= pcm_close,
364		.hw_params	= pcm_hw_params,
365		.hw_free	= pcm_hw_free,
366		.prepare	= pcm_playback_prepare,
367		.trigger	= pcm_playback_trigger,
368		.pointer	= pcm_playback_pointer,
369		.ack		= pcm_playback_ack,
370	};
371	struct snd_pcm *pcm;
372	int err;
373
374	err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
375	if (err < 0)
376		return err;
377
378	pcm->private_data = ff;
379	pcm->nonatomic = true;
380	snprintf(pcm->name, sizeof(pcm->name),
381		 "%s PCM", ff->card->shortname);
382	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
383	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
384	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
385
386	return 0;
387}
~/ Read-only file preview.