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 | |
10 | static 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(s: stream, runtime); |
41 | } |
42 | |
43 | static 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: &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: &rate); |
73 | if (err < 0) { |
74 | mutex_unlock(lock: &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(runtime: substream->runtime, |
81 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, |
82 | min: frames_per_period, max: frames_per_period); |
83 | if (err < 0) { |
84 | mutex_unlock(lock: &tscm->mutex); |
85 | goto err_locked; |
86 | } |
87 | |
88 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
89 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, |
90 | min: frames_per_buffer, max: frames_per_buffer); |
91 | if (err < 0) { |
92 | mutex_unlock(lock: &tscm->mutex); |
93 | goto err_locked; |
94 | } |
95 | } |
96 | |
97 | mutex_unlock(lock: &tscm->mutex); |
98 | |
99 | snd_pcm_set_sync(substream); |
100 | |
101 | return 0; |
102 | err_locked: |
103 | snd_tscm_stream_lock_release(tscm); |
104 | return err; |
105 | } |
106 | |
107 | static 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 | |
116 | static 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(p: hw_params); |
124 | unsigned int frames_per_period = params_period_size(p: hw_params); |
125 | unsigned int frames_per_buffer = params_buffer_size(p: 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(lock: &tscm->mutex); |
133 | } |
134 | |
135 | return err; |
136 | } |
137 | |
138 | static 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(lock: &tscm->mutex); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static 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, rate: runtime->rate); |
163 | if (err >= 0) |
164 | amdtp_stream_pcm_prepare(s: &tscm->tx_stream); |
165 | |
166 | mutex_unlock(lock: &tscm->mutex); |
167 | |
168 | return err; |
169 | } |
170 | |
171 | static 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, rate: runtime->rate); |
180 | if (err >= 0) |
181 | amdtp_stream_pcm_prepare(s: &tscm->rx_stream); |
182 | |
183 | mutex_unlock(lock: &tscm->mutex); |
184 | |
185 | return err; |
186 | } |
187 | |
188 | static 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(s: &tscm->tx_stream, pcm: substream); |
195 | break; |
196 | case SNDRV_PCM_TRIGGER_STOP: |
197 | amdtp_stream_pcm_trigger(s: &tscm->tx_stream, NULL); |
198 | break; |
199 | default: |
200 | return -EINVAL; |
201 | } |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static 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(s: &tscm->rx_stream, pcm: substream); |
213 | break; |
214 | case SNDRV_PCM_TRIGGER_STOP: |
215 | amdtp_stream_pcm_trigger(s: &tscm->rx_stream, NULL); |
216 | break; |
217 | default: |
218 | return -EINVAL; |
219 | } |
220 | |
221 | return 0; |
222 | } |
223 | |
224 | static 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(d: &tscm->domain, s: &tscm->tx_stream); |
229 | } |
230 | |
231 | static 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(d: &tscm->domain, s: &tscm->rx_stream); |
236 | } |
237 | |
238 | static 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(d: &tscm->domain, s: &tscm->tx_stream); |
243 | } |
244 | |
245 | static 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(d: &tscm->domain, s: &tscm->rx_stream); |
250 | } |
251 | |
252 | int 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(card: tscm->card, id: tscm->card->driver, device: 0, playback_count: 1, capture_count: 1, rpcm: &pcm); |
278 | if (err < 0) |
279 | return err; |
280 | |
281 | pcm->private_data = tscm; |
282 | snprintf(buf: pcm->name, size: sizeof(pcm->name), |
283 | fmt: "%s PCM" , tscm->card->shortname); |
284 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &playback_ops); |
285 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &capture_ops); |
286 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, size: 0, max: 0); |
287 | |
288 | return 0; |
289 | } |
290 | |