]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - sound/firewire/bebob/bebob_pcm.c
Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[mirror_ubuntu-jammy-kernel.git] / sound / firewire / bebob / bebob_pcm.c
CommitLineData
da607e19 1// SPDX-License-Identifier: GPL-2.0-only
fbbebd2c
TS
2/*
3 * bebob_pcm.c - a part of driver for BeBoB based devices
4 *
5 * Copyright (c) 2013-2014 Takashi Sakamoto
fbbebd2c
TS
6 */
7
8#include "./bebob.h"
9
10static int
11hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
12{
13 struct snd_bebob_stream_formation *formations = rule->private;
14 struct snd_interval *r =
15 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
16 const struct snd_interval *c =
17 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
18 struct snd_interval t = {
19 .min = UINT_MAX, .max = 0, .integer = 1
20 };
21 unsigned int i;
22
23 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
24 /* entry is invalid */
25 if (formations[i].pcm == 0)
26 continue;
27
28 if (!snd_interval_test(c, formations[i].pcm))
29 continue;
30
31 t.min = min(t.min, snd_bebob_rate_table[i]);
32 t.max = max(t.max, snd_bebob_rate_table[i]);
33
34 }
35 return snd_interval_refine(r, &t);
36}
37
38static int
39hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
40{
41 struct snd_bebob_stream_formation *formations = rule->private;
42 struct snd_interval *c =
43 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
44 const struct snd_interval *r =
45 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
46 struct snd_interval t = {
47 .min = UINT_MAX, .max = 0, .integer = 1
48 };
49
50 unsigned int i;
51
52 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
53 /* entry is invalid */
54 if (formations[i].pcm == 0)
55 continue;
56
57 if (!snd_interval_test(r, snd_bebob_rate_table[i]))
58 continue;
59
60 t.min = min(t.min, formations[i].pcm);
61 t.max = max(t.max, formations[i].pcm);
62 }
63
64 return snd_interval_refine(c, &t);
65}
66
67static void
68limit_channels_and_rates(struct snd_pcm_hardware *hw,
69 struct snd_bebob_stream_formation *formations)
70{
71 unsigned int i;
72
73 hw->channels_min = UINT_MAX;
74 hw->channels_max = 0;
75
76 hw->rate_min = UINT_MAX;
77 hw->rate_max = 0;
78 hw->rates = 0;
79
80 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
81 /* entry has no PCM channels */
82 if (formations[i].pcm == 0)
83 continue;
84
85 hw->channels_min = min(hw->channels_min, formations[i].pcm);
86 hw->channels_max = max(hw->channels_max, formations[i].pcm);
87
88 hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
89 hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
90 hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
91 }
92}
93
fbbebd2c
TS
94static int
95pcm_init_hw_params(struct snd_bebob *bebob,
96 struct snd_pcm_substream *substream)
97{
98 struct snd_pcm_runtime *runtime = substream->runtime;
99 struct amdtp_stream *s;
100 struct snd_bebob_stream_formation *formations;
101 int err;
102
fbbebd2c 103 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
49c7b3fc 104 runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
fbbebd2c
TS
105 s = &bebob->tx_stream;
106 formations = bebob->tx_stream_formations;
107 } else {
49c7b3fc 108 runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
fbbebd2c
TS
109 s = &bebob->rx_stream;
110 formations = bebob->rx_stream_formations;
111 }
112
113 limit_channels_and_rates(&runtime->hw, formations);
fbbebd2c
TS
114
115 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
116 hw_rule_channels, formations,
117 SNDRV_PCM_HW_PARAM_RATE, -1);
118 if (err < 0)
119 goto end;
120
121 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
122 hw_rule_rate, formations,
123 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
124 if (err < 0)
125 goto end;
126
bc8500da 127 err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
fbbebd2c
TS
128end:
129 return err;
130}
131
132static int
133pcm_open(struct snd_pcm_substream *substream)
134{
135 struct snd_bebob *bebob = substream->private_data;
6b9866c8 136 const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
fbbebd2c 137 unsigned int sampling_rate;
3e254b16 138 enum snd_bebob_clock_type src;
fbbebd2c
TS
139 int err;
140
618eabea 141 err = snd_bebob_stream_lock_try(bebob);
fbbebd2c
TS
142 if (err < 0)
143 goto end;
144
618eabea
TS
145 err = pcm_init_hw_params(bebob, substream);
146 if (err < 0)
147 goto err_locked;
148
3e254b16 149 err = snd_bebob_stream_get_clock_src(bebob, &src);
fbbebd2c 150 if (err < 0)
618eabea 151 goto err_locked;
fbbebd2c
TS
152
153 /*
154 * When source of clock is internal or any PCM stream are running,
155 * the available sampling rate is limited at current sampling rate.
156 */
3e254b16 157 if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
fbbebd2c
TS
158 amdtp_stream_pcm_running(&bebob->tx_stream) ||
159 amdtp_stream_pcm_running(&bebob->rx_stream)) {
1fc9522a 160 err = spec->get(bebob, &sampling_rate);
fbbebd2c
TS
161 if (err < 0) {
162 dev_err(&bebob->unit->device,
163 "fail to get sampling rate: %d\n", err);
618eabea 164 goto err_locked;
fbbebd2c
TS
165 }
166
167 substream->runtime->hw.rate_min = sampling_rate;
168 substream->runtime->hw.rate_max = sampling_rate;
169 }
170
171 snd_pcm_set_sync(substream);
fbbebd2c
TS
172end:
173 return err;
618eabea
TS
174err_locked:
175 snd_bebob_stream_lock_release(bebob);
176 return err;
fbbebd2c
TS
177}
178
179static int
180pcm_close(struct snd_pcm_substream *substream)
181{
618eabea
TS
182 struct snd_bebob *bebob = substream->private_data;
183 snd_bebob_stream_lock_release(bebob);
fbbebd2c
TS
184 return 0;
185}
186
187static int
188pcm_capture_hw_params(struct snd_pcm_substream *substream,
189 struct snd_pcm_hw_params *hw_params)
190{
191 struct snd_bebob *bebob = substream->private_data;
22c103cd
TS
192 int err;
193
194 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
195 params_buffer_bytes(hw_params));
196 if (err < 0)
197 return err;
fbbebd2c 198
2a71e701
TS
199 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
200 mutex_lock(&bebob->mutex);
4fd6c6c7 201 bebob->substreams_counter++;
2a71e701
TS
202 mutex_unlock(&bebob->mutex);
203 }
85130cb4 204
22c103cd 205 return 0;
fbbebd2c
TS
206}
207static int
208pcm_playback_hw_params(struct snd_pcm_substream *substream,
209 struct snd_pcm_hw_params *hw_params)
210{
211 struct snd_bebob *bebob = substream->private_data;
22c103cd
TS
212 int err;
213
214 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
215 params_buffer_bytes(hw_params));
216 if (err < 0)
217 return err;
fbbebd2c 218
2a71e701
TS
219 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
220 mutex_lock(&bebob->mutex);
4fd6c6c7 221 bebob->substreams_counter++;
2a71e701
TS
222 mutex_unlock(&bebob->mutex);
223 }
85130cb4 224
22c103cd 225 return 0;
fbbebd2c
TS
226}
227
228static int
229pcm_capture_hw_free(struct snd_pcm_substream *substream)
230{
231 struct snd_bebob *bebob = substream->private_data;
232
2a71e701
TS
233 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
234 mutex_lock(&bebob->mutex);
4fd6c6c7 235 bebob->substreams_counter--;
2a71e701
TS
236 mutex_unlock(&bebob->mutex);
237 }
fbbebd2c
TS
238
239 snd_bebob_stream_stop_duplex(bebob);
240
241 return snd_pcm_lib_free_vmalloc_buffer(substream);
242}
243static int
244pcm_playback_hw_free(struct snd_pcm_substream *substream)
245{
246 struct snd_bebob *bebob = substream->private_data;
247
2a71e701
TS
248 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
249 mutex_lock(&bebob->mutex);
4fd6c6c7 250 bebob->substreams_counter--;
2a71e701
TS
251 mutex_unlock(&bebob->mutex);
252 }
fbbebd2c
TS
253
254 snd_bebob_stream_stop_duplex(bebob);
255
256 return snd_pcm_lib_free_vmalloc_buffer(substream);
257}
258
259static int
260pcm_capture_prepare(struct snd_pcm_substream *substream)
261{
262 struct snd_bebob *bebob = substream->private_data;
263 struct snd_pcm_runtime *runtime = substream->runtime;
264 int err;
265
266 err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
267 if (err >= 0)
268 amdtp_stream_pcm_prepare(&bebob->tx_stream);
269
270 return err;
271}
272static int
273pcm_playback_prepare(struct snd_pcm_substream *substream)
274{
275 struct snd_bebob *bebob = substream->private_data;
276 struct snd_pcm_runtime *runtime = substream->runtime;
277 int err;
278
279 err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
280 if (err >= 0)
281 amdtp_stream_pcm_prepare(&bebob->rx_stream);
282
283 return err;
284}
285
286static int
287pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
288{
289 struct snd_bebob *bebob = substream->private_data;
290
291 switch (cmd) {
292 case SNDRV_PCM_TRIGGER_START:
293 amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
294 break;
295 case SNDRV_PCM_TRIGGER_STOP:
296 amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
297 break;
298 default:
299 return -EINVAL;
300 }
301
302 return 0;
303}
304static int
305pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
306{
307 struct snd_bebob *bebob = substream->private_data;
308
309 switch (cmd) {
310 case SNDRV_PCM_TRIGGER_START:
311 amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
312 break;
313 case SNDRV_PCM_TRIGGER_STOP:
314 amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
315 break;
316 default:
317 return -EINVAL;
318 }
319
320 return 0;
321}
322
323static snd_pcm_uframes_t
324pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
325{
326 struct snd_bebob *bebob = sbstrm->private_data;
327 return amdtp_stream_pcm_pointer(&bebob->tx_stream);
328}
329static snd_pcm_uframes_t
330pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
331{
332 struct snd_bebob *bebob = sbstrm->private_data;
333 return amdtp_stream_pcm_pointer(&bebob->rx_stream);
334}
335
875becf8
TS
336static int pcm_capture_ack(struct snd_pcm_substream *substream)
337{
338 struct snd_bebob *bebob = substream->private_data;
339
340 return amdtp_stream_pcm_ack(&bebob->tx_stream);
341}
342
343static int pcm_playback_ack(struct snd_pcm_substream *substream)
344{
345 struct snd_bebob *bebob = substream->private_data;
346
347 return amdtp_stream_pcm_ack(&bebob->rx_stream);
348}
349
fbbebd2c
TS
350int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
351{
4780f774
TS
352 static const struct snd_pcm_ops capture_ops = {
353 .open = pcm_open,
354 .close = pcm_close,
355 .ioctl = snd_pcm_lib_ioctl,
356 .hw_params = pcm_capture_hw_params,
357 .hw_free = pcm_capture_hw_free,
358 .prepare = pcm_capture_prepare,
359 .trigger = pcm_capture_trigger,
360 .pointer = pcm_capture_pointer,
875becf8 361 .ack = pcm_capture_ack,
4780f774
TS
362 .page = snd_pcm_lib_get_vmalloc_page,
363 };
364 static const struct snd_pcm_ops playback_ops = {
365 .open = pcm_open,
366 .close = pcm_close,
367 .ioctl = snd_pcm_lib_ioctl,
368 .hw_params = pcm_playback_hw_params,
369 .hw_free = pcm_playback_hw_free,
370 .prepare = pcm_playback_prepare,
371 .trigger = pcm_playback_trigger,
372 .pointer = pcm_playback_pointer,
875becf8 373 .ack = pcm_playback_ack,
4780f774 374 .page = snd_pcm_lib_get_vmalloc_page,
4780f774 375 };
fbbebd2c
TS
376 struct snd_pcm *pcm;
377 int err;
378
379 err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
380 if (err < 0)
381 goto end;
382
383 pcm->private_data = bebob;
384 snprintf(pcm->name, sizeof(pcm->name),
385 "%s PCM", bebob->card->shortname);
4780f774
TS
386 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
387 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
fbbebd2c
TS
388end:
389 return err;
390}