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	mutex_lock(&tscm->mutex);
63
64	// When source of clock is not internal or any stream is reserved for
65	// transmission of PCM frames, the available sampling rate is limited
66	// at current one.
67	if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
68		unsigned int frames_per_period = d->events_per_period;
69		unsigned int frames_per_buffer = d->events_per_buffer;
70		unsigned int rate;
71
72		err = snd_tscm_stream_get_rate(tscm, &rate);
73		if (err < 0) {
74			mutex_unlock(&tscm->mutex);
75			goto err_locked;
76		}
77		substream->runtime->hw.rate_min = rate;
78		substream->runtime->hw.rate_max = rate;
79
80		err = snd_pcm_hw_constraint_minmax(substream->runtime,
81					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
82					frames_per_period, frames_per_period);
83		if (err < 0) {
84			mutex_unlock(&tscm->mutex);
85			goto err_locked;
86		}
87
88		err = snd_pcm_hw_constraint_minmax(substream->runtime,
89					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
90					frames_per_buffer, frames_per_buffer);
91		if (err < 0) {
92			mutex_unlock(&tscm->mutex);
93			goto err_locked;
94		}
95	}
96
97	mutex_unlock(&tscm->mutex);
98
99	snd_pcm_set_sync(substream);
100
101	return 0;
102err_locked:
103	snd_tscm_stream_lock_release(tscm);
104	return err;
105}
106
107static int pcm_close(struct snd_pcm_substream *substream)
108{
109	struct snd_tscm *tscm = substream->private_data;
110
111	snd_tscm_stream_lock_release(tscm);
112
113	return 0;
114}
115
116static int pcm_hw_params(struct snd_pcm_substream *substream,
117			 struct snd_pcm_hw_params *hw_params)
118{
119	struct snd_tscm *tscm = substream->private_data;
120	int err = 0;
121
122	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
123		unsigned int rate = params_rate(hw_params);
124		unsigned int frames_per_period = params_period_size(hw_params);
125		unsigned int frames_per_buffer = params_buffer_size(hw_params);
126
127		mutex_lock(&tscm->mutex);
128		err = snd_tscm_stream_reserve_duplex(tscm, rate,
129					frames_per_period, frames_per_buffer);
130		if (err >= 0)
131			++tscm->substreams_counter;
132		mutex_unlock(&tscm->mutex);
133	}
134
135	return err;
136}
137
138static int pcm_hw_free(struct snd_pcm_substream *substream)
139{
140	struct snd_tscm *tscm = substream->private_data;
141
142	mutex_lock(&tscm->mutex);
143
144	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
145		--tscm->substreams_counter;
146
147	snd_tscm_stream_stop_duplex(tscm);
148
149	mutex_unlock(&tscm->mutex);
150
151	return 0;
152}
153
154static int pcm_capture_prepare(struct snd_pcm_substream *substream)
155{
156	struct snd_tscm *tscm = substream->private_data;
157	struct snd_pcm_runtime *runtime = substream->runtime;
158	int err;
159
160	mutex_lock(&tscm->mutex);
161
162	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
163	if (err >= 0)
164		amdtp_stream_pcm_prepare(&tscm->tx_stream);
165
166	mutex_unlock(&tscm->mutex);
167
168	return err;
169}
170
171static int pcm_playback_prepare(struct snd_pcm_substream *substream)
172{
173	struct snd_tscm *tscm = substream->private_data;
174	struct snd_pcm_runtime *runtime = substream->runtime;
175	int err;
176
177	mutex_lock(&tscm->mutex);
178
179	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
180	if (err >= 0)
181		amdtp_stream_pcm_prepare(&tscm->rx_stream);
182
183	mutex_unlock(&tscm->mutex);
184
185	return err;
186}
187
188static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
189{
190	struct snd_tscm *tscm = substream->private_data;
191
192	switch (cmd) {
193	case SNDRV_PCM_TRIGGER_START:
194		amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
195		break;
196	case SNDRV_PCM_TRIGGER_STOP:
197		amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
198		break;
199	default:
200		return -EINVAL;
201	}
202
203	return 0;
204}
205
206static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
207{
208	struct snd_tscm *tscm = substream->private_data;
209
210	switch (cmd) {
211	case SNDRV_PCM_TRIGGER_START:
212		amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
213		break;
214	case SNDRV_PCM_TRIGGER_STOP:
215		amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
216		break;
217	default:
218		return -EINVAL;
219	}
220
221	return 0;
222}
223
224static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
225{
226	struct snd_tscm *tscm = sbstrm->private_data;
227
228	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
229}
230
231static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
232{
233	struct snd_tscm *tscm = sbstrm->private_data;
234
235	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
236}
237
238static int pcm_capture_ack(struct snd_pcm_substream *substream)
239{
240	struct snd_tscm *tscm = substream->private_data;
241
242	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
243}
244
245static int pcm_playback_ack(struct snd_pcm_substream *substream)
246{
247	struct snd_tscm *tscm = substream->private_data;
248
249	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
250}
251
252int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
253{
254	static const struct snd_pcm_ops capture_ops = {
255		.open		= pcm_open,
256		.close		= pcm_close,
257		.hw_params	= pcm_hw_params,
258		.hw_free	= pcm_hw_free,
259		.prepare	= pcm_capture_prepare,
260		.trigger	= pcm_capture_trigger,
261		.pointer	= pcm_capture_pointer,
262		.ack		= pcm_capture_ack,
263	};
264	static const struct snd_pcm_ops playback_ops = {
265		.open		= pcm_open,
266		.close		= pcm_close,
267		.hw_params	= pcm_hw_params,
268		.hw_free	= pcm_hw_free,
269		.prepare	= pcm_playback_prepare,
270		.trigger	= pcm_playback_trigger,
271		.pointer	= pcm_playback_pointer,
272		.ack		= pcm_playback_ack,
273	};
274	struct snd_pcm *pcm;
275	int err;
276
277	err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
278	if (err < 0)
279		return err;
280
281	pcm->private_data = tscm;
282	snprintf(pcm->name, sizeof(pcm->name),
283		 "%s PCM", tscm->card->shortname);
284	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
285	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
286	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
287
288	return 0;
289}
290