File Index
Files and directories
Sign in
FILE

motu-pcm.c

File preview with syntax highlighting.

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