1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family |
4 | * |
5 | * Copyright (c) 2014-2015 Takashi Sakamoto |
6 | */ |
7 | |
8 | #include "digi00x.h" |
9 | |
10 | static int hw_rule_rate(struct snd_pcm_hw_params *params, |
11 | struct snd_pcm_hw_rule *rule) |
12 | { |
13 | struct snd_interval *r = |
14 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); |
15 | const struct snd_interval *c = |
16 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
17 | struct snd_interval t = { |
18 | .min = UINT_MAX, .max = 0, .integer = 1, |
19 | }; |
20 | unsigned int i; |
21 | |
22 | for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { |
23 | if (!snd_interval_test(i: c, |
24 | val: snd_dg00x_stream_pcm_channels[i])) |
25 | continue; |
26 | |
27 | t.min = min(t.min, snd_dg00x_stream_rates[i]); |
28 | t.max = max(t.max, snd_dg00x_stream_rates[i]); |
29 | } |
30 | |
31 | return snd_interval_refine(i: r, v: &t); |
32 | } |
33 | |
34 | static int hw_rule_channels(struct snd_pcm_hw_params *params, |
35 | struct snd_pcm_hw_rule *rule) |
36 | { |
37 | struct snd_interval *c = |
38 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
39 | const struct snd_interval *r = |
40 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); |
41 | struct snd_interval t = { |
42 | .min = UINT_MAX, .max = 0, .integer = 1, |
43 | }; |
44 | unsigned int i; |
45 | |
46 | for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { |
47 | if (!snd_interval_test(i: r, val: snd_dg00x_stream_rates[i])) |
48 | continue; |
49 | |
50 | t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); |
51 | t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); |
52 | } |
53 | |
54 | return snd_interval_refine(i: c, v: &t); |
55 | } |
56 | |
57 | static int pcm_init_hw_params(struct snd_dg00x *dg00x, |
58 | struct snd_pcm_substream *substream) |
59 | { |
60 | struct snd_pcm_runtime *runtime = substream->runtime; |
61 | struct snd_pcm_hardware *hw = &runtime->hw; |
62 | struct amdtp_stream *s; |
63 | int err; |
64 | |
65 | |
66 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
67 | substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; |
68 | s = &dg00x->tx_stream; |
69 | } else { |
70 | substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; |
71 | s = &dg00x->rx_stream; |
72 | } |
73 | |
74 | hw->channels_min = 10; |
75 | hw->channels_max = 18; |
76 | |
77 | hw->rates = SNDRV_PCM_RATE_44100 | |
78 | SNDRV_PCM_RATE_48000 | |
79 | SNDRV_PCM_RATE_88200 | |
80 | SNDRV_PCM_RATE_96000; |
81 | snd_pcm_limit_hw_rates(runtime); |
82 | |
83 | err = snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, |
84 | SNDRV_PCM_HW_PARAM_CHANNELS, |
85 | func: hw_rule_channels, NULL, |
86 | SNDRV_PCM_HW_PARAM_RATE, -1); |
87 | if (err < 0) |
88 | return err; |
89 | |
90 | err = snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, |
91 | SNDRV_PCM_HW_PARAM_RATE, |
92 | func: hw_rule_rate, NULL, |
93 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); |
94 | if (err < 0) |
95 | return err; |
96 | |
97 | return amdtp_dot_add_pcm_hw_constraints(s, runtime: substream->runtime); |
98 | } |
99 | |
100 | static int pcm_open(struct snd_pcm_substream *substream) |
101 | { |
102 | struct snd_dg00x *dg00x = substream->private_data; |
103 | struct amdtp_domain *d = &dg00x->domain; |
104 | enum snd_dg00x_clock clock; |
105 | bool detect; |
106 | int err; |
107 | |
108 | err = snd_dg00x_stream_lock_try(dg00x); |
109 | if (err < 0) |
110 | return err; |
111 | |
112 | err = pcm_init_hw_params(dg00x, substream); |
113 | if (err < 0) |
114 | goto err_locked; |
115 | |
116 | /* Check current clock source. */ |
117 | err = snd_dg00x_stream_get_clock(dg00x, clock: &clock); |
118 | if (err < 0) |
119 | goto err_locked; |
120 | if (clock != SND_DG00X_CLOCK_INTERNAL) { |
121 | err = snd_dg00x_stream_check_external_clock(dg00x, detect: &detect); |
122 | if (err < 0) |
123 | goto err_locked; |
124 | if (!detect) { |
125 | err = -EBUSY; |
126 | goto err_locked; |
127 | } |
128 | } |
129 | |
130 | mutex_lock(&dg00x->mutex); |
131 | |
132 | // When source of clock is not internal or any stream is reserved for |
133 | // transmission of PCM frames, the available sampling rate is limited |
134 | // at current one. |
135 | if ((clock != SND_DG00X_CLOCK_INTERNAL) || |
136 | (dg00x->substreams_counter > 0 && d->events_per_period > 0)) { |
137 | unsigned int frames_per_period = d->events_per_period; |
138 | unsigned int frames_per_buffer = d->events_per_buffer; |
139 | unsigned int rate; |
140 | |
141 | err = snd_dg00x_stream_get_external_rate(dg00x, rate: &rate); |
142 | if (err < 0) { |
143 | mutex_unlock(lock: &dg00x->mutex); |
144 | goto err_locked; |
145 | } |
146 | substream->runtime->hw.rate_min = rate; |
147 | substream->runtime->hw.rate_max = rate; |
148 | |
149 | if (frames_per_period > 0) { |
150 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
151 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, |
152 | min: frames_per_period, max: frames_per_period); |
153 | if (err < 0) { |
154 | mutex_unlock(lock: &dg00x->mutex); |
155 | goto err_locked; |
156 | } |
157 | |
158 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
159 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, |
160 | min: frames_per_buffer, max: frames_per_buffer); |
161 | if (err < 0) { |
162 | mutex_unlock(lock: &dg00x->mutex); |
163 | goto err_locked; |
164 | } |
165 | } |
166 | } |
167 | |
168 | mutex_unlock(lock: &dg00x->mutex); |
169 | |
170 | snd_pcm_set_sync(substream); |
171 | |
172 | return 0; |
173 | err_locked: |
174 | snd_dg00x_stream_lock_release(dg00x); |
175 | return err; |
176 | } |
177 | |
178 | static int pcm_close(struct snd_pcm_substream *substream) |
179 | { |
180 | struct snd_dg00x *dg00x = substream->private_data; |
181 | |
182 | snd_dg00x_stream_lock_release(dg00x); |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static int pcm_hw_params(struct snd_pcm_substream *substream, |
188 | struct snd_pcm_hw_params *hw_params) |
189 | { |
190 | struct snd_dg00x *dg00x = substream->private_data; |
191 | int err = 0; |
192 | |
193 | if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
194 | unsigned int rate = params_rate(p: hw_params); |
195 | unsigned int frames_per_period = params_period_size(p: hw_params); |
196 | unsigned int frames_per_buffer = params_buffer_size(p: hw_params); |
197 | |
198 | mutex_lock(&dg00x->mutex); |
199 | err = snd_dg00x_stream_reserve_duplex(dg00x, rate, |
200 | frames_per_period, frames_per_buffer); |
201 | if (err >= 0) |
202 | ++dg00x->substreams_counter; |
203 | mutex_unlock(lock: &dg00x->mutex); |
204 | } |
205 | |
206 | return err; |
207 | } |
208 | |
209 | static int pcm_hw_free(struct snd_pcm_substream *substream) |
210 | { |
211 | struct snd_dg00x *dg00x = substream->private_data; |
212 | |
213 | mutex_lock(&dg00x->mutex); |
214 | |
215 | if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) |
216 | --dg00x->substreams_counter; |
217 | |
218 | snd_dg00x_stream_stop_duplex(dg00x); |
219 | |
220 | mutex_unlock(lock: &dg00x->mutex); |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | static int pcm_capture_prepare(struct snd_pcm_substream *substream) |
226 | { |
227 | struct snd_dg00x *dg00x = substream->private_data; |
228 | int err; |
229 | |
230 | mutex_lock(&dg00x->mutex); |
231 | |
232 | err = snd_dg00x_stream_start_duplex(dg00x); |
233 | if (err >= 0) |
234 | amdtp_stream_pcm_prepare(s: &dg00x->tx_stream); |
235 | |
236 | mutex_unlock(lock: &dg00x->mutex); |
237 | |
238 | return err; |
239 | } |
240 | |
241 | static int pcm_playback_prepare(struct snd_pcm_substream *substream) |
242 | { |
243 | struct snd_dg00x *dg00x = substream->private_data; |
244 | int err; |
245 | |
246 | mutex_lock(&dg00x->mutex); |
247 | |
248 | err = snd_dg00x_stream_start_duplex(dg00x); |
249 | if (err >= 0) { |
250 | amdtp_stream_pcm_prepare(s: &dg00x->rx_stream); |
251 | amdtp_dot_reset(s: &dg00x->rx_stream); |
252 | } |
253 | |
254 | mutex_unlock(lock: &dg00x->mutex); |
255 | |
256 | return err; |
257 | } |
258 | |
259 | static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) |
260 | { |
261 | struct snd_dg00x *dg00x = substream->private_data; |
262 | |
263 | switch (cmd) { |
264 | case SNDRV_PCM_TRIGGER_START: |
265 | amdtp_stream_pcm_trigger(s: &dg00x->tx_stream, pcm: substream); |
266 | break; |
267 | case SNDRV_PCM_TRIGGER_STOP: |
268 | amdtp_stream_pcm_trigger(s: &dg00x->tx_stream, NULL); |
269 | break; |
270 | default: |
271 | return -EINVAL; |
272 | } |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) |
278 | { |
279 | struct snd_dg00x *dg00x = substream->private_data; |
280 | |
281 | switch (cmd) { |
282 | case SNDRV_PCM_TRIGGER_START: |
283 | amdtp_stream_pcm_trigger(s: &dg00x->rx_stream, pcm: substream); |
284 | break; |
285 | case SNDRV_PCM_TRIGGER_STOP: |
286 | amdtp_stream_pcm_trigger(s: &dg00x->rx_stream, NULL); |
287 | break; |
288 | default: |
289 | return -EINVAL; |
290 | } |
291 | |
292 | return 0; |
293 | } |
294 | |
295 | static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) |
296 | { |
297 | struct snd_dg00x *dg00x = sbstrm->private_data; |
298 | |
299 | return amdtp_domain_stream_pcm_pointer(d: &dg00x->domain, s: &dg00x->tx_stream); |
300 | } |
301 | |
302 | static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) |
303 | { |
304 | struct snd_dg00x *dg00x = sbstrm->private_data; |
305 | |
306 | return amdtp_domain_stream_pcm_pointer(d: &dg00x->domain, s: &dg00x->rx_stream); |
307 | } |
308 | |
309 | static int pcm_capture_ack(struct snd_pcm_substream *substream) |
310 | { |
311 | struct snd_dg00x *dg00x = substream->private_data; |
312 | |
313 | return amdtp_domain_stream_pcm_ack(d: &dg00x->domain, s: &dg00x->tx_stream); |
314 | } |
315 | |
316 | static int pcm_playback_ack(struct snd_pcm_substream *substream) |
317 | { |
318 | struct snd_dg00x *dg00x = substream->private_data; |
319 | |
320 | return amdtp_domain_stream_pcm_ack(d: &dg00x->domain, s: &dg00x->rx_stream); |
321 | } |
322 | |
323 | int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) |
324 | { |
325 | static const struct snd_pcm_ops capture_ops = { |
326 | .open = pcm_open, |
327 | .close = pcm_close, |
328 | .hw_params = pcm_hw_params, |
329 | .hw_free = pcm_hw_free, |
330 | .prepare = pcm_capture_prepare, |
331 | .trigger = pcm_capture_trigger, |
332 | .pointer = pcm_capture_pointer, |
333 | .ack = pcm_capture_ack, |
334 | }; |
335 | static const struct snd_pcm_ops playback_ops = { |
336 | .open = pcm_open, |
337 | .close = pcm_close, |
338 | .hw_params = pcm_hw_params, |
339 | .hw_free = pcm_hw_free, |
340 | .prepare = pcm_playback_prepare, |
341 | .trigger = pcm_playback_trigger, |
342 | .pointer = pcm_playback_pointer, |
343 | .ack = pcm_playback_ack, |
344 | }; |
345 | struct snd_pcm *pcm; |
346 | int err; |
347 | |
348 | err = snd_pcm_new(card: dg00x->card, id: dg00x->card->driver, device: 0, playback_count: 1, capture_count: 1, rpcm: &pcm); |
349 | if (err < 0) |
350 | return err; |
351 | |
352 | pcm->private_data = dg00x; |
353 | snprintf(buf: pcm->name, size: sizeof(pcm->name), |
354 | fmt: "%s PCM" , dg00x->card->shortname); |
355 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &playback_ops); |
356 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &capture_ops); |
357 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, size: 0, max: 0); |
358 | |
359 | return 0; |
360 | } |
361 | |