File Index
Files and directories
Sign in
FILE

tascam-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/tascam/tascam-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * tascam-pcm.c - a part of driver for TASCAM FireWire series
  4 *
  5 * Copyright (c) 2015 Takashi Sakamoto
  6 */
  7
  8#include "tascam.h"
  9
 10static int pcm_init_hw_params(struct snd_tscm *tscm,
 11			      struct snd_pcm_substream *substream)
 12{
 13	struct snd_pcm_runtime *runtime = substream->runtime;
 14	struct snd_pcm_hardware *hw = &runtime->hw;
 15	struct amdtp_stream *stream;
 16	unsigned int pcm_channels;
 17
 18	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 19		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
 20		stream = &tscm->tx_stream;
 21		pcm_channels = tscm->spec->pcm_capture_analog_channels;
 22	} else {
 23		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
 24		stream = &tscm->rx_stream;
 25		pcm_channels = tscm->spec->pcm_playback_analog_channels;
 26	}
 27
 28	if (tscm->spec->has_adat)
 29		pcm_channels += 8;
 30	if (tscm->spec->has_spdif)
 31		pcm_channels += 2;
 32	runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
 33
 34	hw->rates = SNDRV_PCM_RATE_44100 |
 35		    SNDRV_PCM_RATE_48000 |
 36		    SNDRV_PCM_RATE_88200 |
 37		    SNDRV_PCM_RATE_96000;
 38	snd_pcm_limit_hw_rates(runtime);
 39
 40	return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
 41}
 42
 43static int pcm_open(struct snd_pcm_substream *substream)
 44{
 45	struct snd_tscm *tscm = substream->private_data;
 46	struct amdtp_domain *d = &tscm->domain;
 47	enum snd_tscm_clock clock;
 48	int err;
 49
 50	err = snd_tscm_stream_lock_try(tscm);
 51	if (err < 0)
 52		return err;
 53
 54	err = pcm_init_hw_params(tscm, substream);
 55	if (err < 0)
 56		goto err_locked;
 57
 58	err = snd_tscm_stream_get_clock(tscm, &clock);
 59	if (err < 0)
 60		goto err_locked;
 61
 62	scoped_guard(mutex, &tscm->mutex) {
 63		// When source of clock is not internal or any stream is reserved for
 64		// transmission of PCM frames, the available sampling rate is limited
 65		// at current one.
 66		if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
 67			unsigned int frames_per_period = d->events_per_period;
 68			unsigned int frames_per_buffer = d->events_per_buffer;
 69			unsigned int rate;
 70
 71			err = snd_tscm_stream_get_rate(tscm, &rate);
 72			if (err < 0)
 73				goto err_locked;
 74			substream->runtime->hw.rate_min = rate;
 75			substream->runtime->hw.rate_max = rate;
 76
 77			err = snd_pcm_hw_constraint_minmax(substream->runtime,
 78							   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
 79							   frames_per_period, frames_per_period);
 80			if (err < 0)
 81				goto err_locked;
 82
 83			err = snd_pcm_hw_constraint_minmax(substream->runtime,
 84							   SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
 85							   frames_per_buffer, frames_per_buffer);
 86			if (err < 0)
 87				goto err_locked;
 88		}
 89	}
 90
 91	snd_pcm_set_sync(substream);
 92
 93	return 0;
 94err_locked:
 95	snd_tscm_stream_lock_release(tscm);
 96	return err;
 97}
 98
 99static int pcm_close(struct snd_pcm_substream *substream)
100{
101	struct snd_tscm *tscm = substream->private_data;
102
103	snd_tscm_stream_lock_release(tscm);
104
105	return 0;
106}
107
108static int pcm_hw_params(struct snd_pcm_substream *substream,
109			 struct snd_pcm_hw_params *hw_params)
110{
111	struct snd_tscm *tscm = substream->private_data;
112	int err = 0;
113
114	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
115		unsigned int rate = params_rate(hw_params);
116		unsigned int frames_per_period = params_period_size(hw_params);
117		unsigned int frames_per_buffer = params_buffer_size(hw_params);
118
119		guard(mutex)(&tscm->mutex);
120		err = snd_tscm_stream_reserve_duplex(tscm, rate,
121					frames_per_period, frames_per_buffer);
122		if (err >= 0)
123			++tscm->substreams_counter;
124	}
125
126	return err;
127}
128
129static int pcm_hw_free(struct snd_pcm_substream *substream)
130{
131	struct snd_tscm *tscm = substream->private_data;
132
133	guard(mutex)(&tscm->mutex);
134
135	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
136		--tscm->substreams_counter;
137
138	snd_tscm_stream_stop_duplex(tscm);
139
140	return 0;
141}
142
143static int pcm_capture_prepare(struct snd_pcm_substream *substream)
144{
145	struct snd_tscm *tscm = substream->private_data;
146	struct snd_pcm_runtime *runtime = substream->runtime;
147	int err;
148
149	guard(mutex)(&tscm->mutex);
150
151	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
152	if (err >= 0)
153		amdtp_stream_pcm_prepare(&tscm->tx_stream);
154
155	return err;
156}
157
158static int pcm_playback_prepare(struct snd_pcm_substream *substream)
159{
160	struct snd_tscm *tscm = substream->private_data;
161	struct snd_pcm_runtime *runtime = substream->runtime;
162	int err;
163
164	guard(mutex)(&tscm->mutex);
165
166	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
167	if (err >= 0)
168		amdtp_stream_pcm_prepare(&tscm->rx_stream);
169
170	return err;
171}
172
173static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
174{
175	struct snd_tscm *tscm = substream->private_data;
176
177	switch (cmd) {
178	case SNDRV_PCM_TRIGGER_START:
179		amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
180		break;
181	case SNDRV_PCM_TRIGGER_STOP:
182		amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
183		break;
184	default:
185		return -EINVAL;
186	}
187
188	return 0;
189}
190
191static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
192{
193	struct snd_tscm *tscm = substream->private_data;
194
195	switch (cmd) {
196	case SNDRV_PCM_TRIGGER_START:
197		amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
198		break;
199	case SNDRV_PCM_TRIGGER_STOP:
200		amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
201		break;
202	default:
203		return -EINVAL;
204	}
205
206	return 0;
207}
208
209static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
210{
211	struct snd_tscm *tscm = sbstrm->private_data;
212
213	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
214}
215
216static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
217{
218	struct snd_tscm *tscm = sbstrm->private_data;
219
220	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
221}
222
223static int pcm_capture_ack(struct snd_pcm_substream *substream)
224{
225	struct snd_tscm *tscm = substream->private_data;
226
227	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
228}
229
230static int pcm_playback_ack(struct snd_pcm_substream *substream)
231{
232	struct snd_tscm *tscm = substream->private_data;
233
234	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
235}
236
237int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
238{
239	static const struct snd_pcm_ops capture_ops = {
240		.open		= pcm_open,
241		.close		= pcm_close,
242		.hw_params	= pcm_hw_params,
243		.hw_free	= pcm_hw_free,
244		.prepare	= pcm_capture_prepare,
245		.trigger	= pcm_capture_trigger,
246		.pointer	= pcm_capture_pointer,
247		.ack		= pcm_capture_ack,
248	};
249	static const struct snd_pcm_ops playback_ops = {
250		.open		= pcm_open,
251		.close		= pcm_close,
252		.hw_params	= pcm_hw_params,
253		.hw_free	= pcm_hw_free,
254		.prepare	= pcm_playback_prepare,
255		.trigger	= pcm_playback_trigger,
256		.pointer	= pcm_playback_pointer,
257		.ack		= pcm_playback_ack,
258	};
259	struct snd_pcm *pcm;
260	int err;
261
262	err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
263	if (err < 0)
264		return err;
265
266	pcm->private_data = tscm;
267	pcm->nonatomic = true;
268	snprintf(pcm->name, sizeof(pcm->name),
269		 "%s PCM", tscm->card->shortname);
270	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
271	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
272	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
273
274	return 0;
275}
~/ Read-only file preview.