summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-22 08:30:22 -0400
committerTakashi Iwai <tiwai@suse.de>2017-03-28 06:33:53 -0400
commitdd49b2d1f04af9b1f44e9fe82c85f374f662c61b (patch)
tree3d24378eea1b2dfb16d3fd808679aea886c1018a /sound/firewire/motu
parent4638ec6ede0847c75bd943d54237efb118f4abae (diff)
ALSA: firewire-motu: add PCM functionality
This commit adds PCM functionality to transmit/receive PCM samples. When one of PCM substreams are running or external clock source is selected, current sampling rate is used. Else, the sampling rate is changed according to requests from a userspace application. Available number of samples in a frame of PCM substream is determined at open(2) to corresponding PCM character device. Later, packet streaming starts by ioctl(2) with SNDRV_PCM_IOCTL_PREPARE. In theory, between them, applications can change state of the unit by any write transaction to change the number. In this case, this driver may fail packet streaming due to wrong data format. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire/motu')
-rw-r--r--sound/firewire/motu/Makefile2
-rw-r--r--sound/firewire/motu/motu-pcm.c386
-rw-r--r--sound/firewire/motu/motu.c4
-rw-r--r--sound/firewire/motu/motu.h2
4 files changed, 393 insertions, 1 deletions
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 0eccbe215f5e..508b6894826a 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,3 @@
1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ 1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
2 motu-proc.o 2 motu-proc.o motu-pcm.o
3obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o 3obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
new file mode 100644
index 000000000000..a50bcd6f4a63
--- /dev/null
+++ b/sound/firewire/motu/motu-pcm.c
@@ -0,0 +1,386 @@
1/*
2 * motu-pcm.c - a part of driver for MOTU FireWire series
3 *
4 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8
9#include <sound/pcm_params.h>
10#include "motu.h"
11
12static int motu_rate_constraint(struct snd_pcm_hw_params *params,
13 struct snd_pcm_hw_rule *rule)
14{
15 struct snd_motu_packet_format *formats = rule->private;
16
17 const struct snd_interval *c =
18 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
19 struct snd_interval *r =
20 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
21 struct snd_interval rates = {
22 .min = UINT_MAX, .max = 0, .integer = 1
23 };
24 unsigned int i, pcm_channels, rate, mode;
25
26 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
27 rate = snd_motu_clock_rates[i];
28 mode = i / 2;
29
30 pcm_channels = formats->fixed_part_pcm_chunks[mode] +
31 formats->differed_part_pcm_chunks[mode];
32 if (!snd_interval_test(c, pcm_channels))
33 continue;
34
35 rates.min = min(rates.min, rate);
36 rates.max = max(rates.max, rate);
37 }
38
39 return snd_interval_refine(r, &rates);
40}
41
42static int motu_channels_constraint(struct snd_pcm_hw_params *params,
43 struct snd_pcm_hw_rule *rule)
44{
45 struct snd_motu_packet_format *formats = rule->private;
46
47 const struct snd_interval *r =
48 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
49 struct snd_interval *c =
50 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
51 struct snd_interval channels = {
52 .min = UINT_MAX, .max = 0, .integer = 1
53 };
54 unsigned int i, pcm_channels, rate, mode;
55
56 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
57 rate = snd_motu_clock_rates[i];
58 mode = i / 2;
59
60 if (!snd_interval_test(r, rate))
61 continue;
62
63 pcm_channels = formats->fixed_part_pcm_chunks[mode] +
64 formats->differed_part_pcm_chunks[mode];
65 channels.min = min(channels.min, pcm_channels);
66 channels.max = max(channels.max, pcm_channels);
67 }
68
69 return snd_interval_refine(c, &channels);
70}
71
72static void limit_channels_and_rates(struct snd_motu *motu,
73 struct snd_pcm_runtime *runtime,
74 struct snd_motu_packet_format *formats)
75{
76 struct snd_pcm_hardware *hw = &runtime->hw;
77 unsigned int i, pcm_channels, rate, mode;
78
79 hw->channels_min = UINT_MAX;
80 hw->channels_max = 0;
81
82 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
83 rate = snd_motu_clock_rates[i];
84 mode = i / 2;
85
86 pcm_channels = formats->fixed_part_pcm_chunks[mode] +
87 formats->differed_part_pcm_chunks[mode];
88 if (pcm_channels == 0)
89 continue;
90
91 hw->rates |= snd_pcm_rate_to_rate_bit(rate);
92 hw->channels_min = min(hw->channels_min, pcm_channels);
93 hw->channels_max = max(hw->channels_max, pcm_channels);
94 }
95
96 snd_pcm_limit_hw_rates(runtime);
97}
98
99static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
100{
101 hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
102 hw->periods_max = UINT_MAX;
103
104 hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
105
106 /* Just to prevent from allocating much pages. */
107 hw->period_bytes_max = hw->period_bytes_min * 2048;
108 hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
109}
110
111static int init_hw_info(struct snd_motu *motu,
112 struct snd_pcm_substream *substream)
113{
114 struct snd_pcm_runtime *runtime = substream->runtime;
115 struct snd_pcm_hardware *hw = &runtime->hw;
116 struct amdtp_stream *stream;
117 struct snd_motu_packet_format *formats;
118 int err;
119
120 hw->info = SNDRV_PCM_INFO_MMAP |
121 SNDRV_PCM_INFO_MMAP_VALID |
122 SNDRV_PCM_INFO_BATCH |
123 SNDRV_PCM_INFO_INTERLEAVED |
124 SNDRV_PCM_INFO_JOINT_DUPLEX |
125 SNDRV_PCM_INFO_BLOCK_TRANSFER;
126
127 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
128 hw->formats = SNDRV_PCM_FMTBIT_S32;
129 stream = &motu->tx_stream;
130 formats = &motu->tx_packet_formats;
131 } else {
132 hw->formats = SNDRV_PCM_FMTBIT_S32;
133 stream = &motu->rx_stream;
134 formats = &motu->rx_packet_formats;
135 }
136
137 limit_channels_and_rates(motu, runtime, formats);
138 limit_period_and_buffer(hw);
139
140 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
141 motu_rate_constraint, formats,
142 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
143 if (err < 0)
144 return err;
145 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
146 motu_channels_constraint, formats,
147 SNDRV_PCM_HW_PARAM_RATE, -1);
148 if (err < 0)
149 return err;
150
151 return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
152}
153
154static int pcm_open(struct snd_pcm_substream *substream)
155{
156 struct snd_motu *motu = substream->private_data;
157 const struct snd_motu_protocol *const protocol = motu->spec->protocol;
158 enum snd_motu_clock_source src;
159 unsigned int rate;
160 int err;
161
162 mutex_lock(&motu->mutex);
163
164 err = protocol->cache_packet_formats(motu);
165 if (err < 0)
166 return err;
167
168 err = init_hw_info(motu, substream);
169 if (err < 0)
170 return err;
171
172 /*
173 * When source of clock is not internal or any PCM streams are running,
174 * available sampling rate is limited at current sampling rate.
175 */
176 err = protocol->get_clock_source(motu, &src);
177 if (err < 0)
178 return err;
179 if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
180 amdtp_stream_pcm_running(&motu->tx_stream) ||
181 amdtp_stream_pcm_running(&motu->rx_stream)) {
182 err = protocol->get_clock_rate(motu, &rate);
183 if (err < 0)
184 return err;
185 substream->runtime->hw.rate_min = rate;
186 substream->runtime->hw.rate_max = rate;
187 }
188
189 snd_pcm_set_sync(substream);
190
191 mutex_unlock(&motu->mutex);
192
193 return err;
194}
195
196static int pcm_close(struct snd_pcm_substream *substream)
197{
198 return 0;
199}
200
201static int capture_hw_params(struct snd_pcm_substream *substream,
202 struct snd_pcm_hw_params *hw_params)
203{
204 struct snd_motu *motu = substream->private_data;
205 int err;
206
207 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
208 params_buffer_bytes(hw_params));
209 if (err < 0)
210 return err;
211
212 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
213 mutex_lock(&motu->mutex);
214 motu->capture_substreams++;
215 mutex_unlock(&motu->mutex);
216 }
217
218 return 0;
219}
220static int playback_hw_params(struct snd_pcm_substream *substream,
221 struct snd_pcm_hw_params *hw_params)
222{
223 struct snd_motu *motu = substream->private_data;
224 int err;
225
226 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
227 params_buffer_bytes(hw_params));
228 if (err < 0)
229 return err;
230
231 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
232 mutex_lock(&motu->mutex);
233 motu->playback_substreams++;
234 mutex_unlock(&motu->mutex);
235 }
236
237 return 0;
238}
239
240static int capture_hw_free(struct snd_pcm_substream *substream)
241{
242 struct snd_motu *motu = substream->private_data;
243
244 mutex_lock(&motu->mutex);
245
246 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
247 motu->capture_substreams--;
248
249 snd_motu_stream_stop_duplex(motu);
250
251 mutex_unlock(&motu->mutex);
252
253 return snd_pcm_lib_free_vmalloc_buffer(substream);
254}
255
256static int playback_hw_free(struct snd_pcm_substream *substream)
257{
258 struct snd_motu *motu = substream->private_data;
259
260 mutex_lock(&motu->mutex);
261
262 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
263 motu->playback_substreams--;
264
265 snd_motu_stream_stop_duplex(motu);
266
267 mutex_unlock(&motu->mutex);
268
269 return snd_pcm_lib_free_vmalloc_buffer(substream);
270}
271
272static int capture_prepare(struct snd_pcm_substream *substream)
273{
274 struct snd_motu *motu = substream->private_data;
275 int err;
276
277 mutex_lock(&motu->mutex);
278 err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
279 mutex_unlock(&motu->mutex);
280 if (err >= 0)
281 amdtp_stream_pcm_prepare(&motu->tx_stream);
282
283 return 0;
284}
285static int playback_prepare(struct snd_pcm_substream *substream)
286{
287 struct snd_motu *motu = substream->private_data;
288 int err;
289
290 mutex_lock(&motu->mutex);
291 err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
292 mutex_unlock(&motu->mutex);
293 if (err >= 0)
294 amdtp_stream_pcm_prepare(&motu->rx_stream);
295
296 return err;
297}
298
299static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
300{
301 struct snd_motu *motu = substream->private_data;
302
303 switch (cmd) {
304 case SNDRV_PCM_TRIGGER_START:
305 amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
306 break;
307 case SNDRV_PCM_TRIGGER_STOP:
308 amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
309 break;
310 default:
311 return -EINVAL;
312 }
313
314 return 0;
315}
316static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
317{
318 struct snd_motu *motu = substream->private_data;
319
320 switch (cmd) {
321 case SNDRV_PCM_TRIGGER_START:
322 amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
323 break;
324 case SNDRV_PCM_TRIGGER_STOP:
325 amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
326 break;
327 default:
328 return -EINVAL;
329 }
330
331 return 0;
332}
333
334static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
335{
336 struct snd_motu *motu = substream->private_data;
337
338 return amdtp_stream_pcm_pointer(&motu->tx_stream);
339}
340static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
341{
342 struct snd_motu *motu = substream->private_data;
343
344 return amdtp_stream_pcm_pointer(&motu->rx_stream);
345}
346
347int snd_motu_create_pcm_devices(struct snd_motu *motu)
348{
349 static struct snd_pcm_ops capture_ops = {
350 .open = pcm_open,
351 .close = pcm_close,
352 .ioctl = snd_pcm_lib_ioctl,
353 .hw_params = capture_hw_params,
354 .hw_free = capture_hw_free,
355 .prepare = capture_prepare,
356 .trigger = capture_trigger,
357 .pointer = capture_pointer,
358 .page = snd_pcm_lib_get_vmalloc_page,
359 .mmap = snd_pcm_lib_mmap_vmalloc,
360 };
361 static struct snd_pcm_ops playback_ops = {
362 .open = pcm_open,
363 .close = pcm_close,
364 .ioctl = snd_pcm_lib_ioctl,
365 .hw_params = playback_hw_params,
366 .hw_free = playback_hw_free,
367 .prepare = playback_prepare,
368 .trigger = playback_trigger,
369 .pointer = playback_pointer,
370 .page = snd_pcm_lib_get_vmalloc_page,
371 .mmap = snd_pcm_lib_mmap_vmalloc,
372 };
373 struct snd_pcm *pcm;
374 int err;
375
376 err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
377 if (err < 0)
378 return err;
379 pcm->private_data = motu;
380 strcpy(pcm->name, motu->card->shortname);
381
382 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
383 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
384
385 return 0;
386}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index cbf4ed0f5234..801d6a73b0f3 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -99,6 +99,10 @@ static void do_registration(struct work_struct *work)
99 99
100 snd_motu_proc_init(motu); 100 snd_motu_proc_init(motu);
101 101
102 err = snd_motu_create_pcm_devices(motu);
103 if (err < 0)
104 goto error;
105
102 err = snd_card_register(motu->card); 106 err = snd_card_register(motu->card);
103 if (err < 0) 107 if (err < 0)
104 goto error; 108 goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 4d079d66cf77..afc6de654daa 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -131,4 +131,6 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
131void snd_motu_stream_stop_duplex(struct snd_motu *motu); 131void snd_motu_stream_stop_duplex(struct snd_motu *motu);
132 132
133void snd_motu_proc_init(struct snd_motu *motu); 133void snd_motu_proc_init(struct snd_motu *motu);
134
135int snd_motu_create_pcm_devices(struct snd_motu *motu);
134#endif 136#endif