]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/firewire/tascam/tascam-pcm.c
ALSA: firewire: process packets in 'struct snd_pcm_ops.ack' callback
[karo-tx-linux.git] / sound / firewire / tascam / tascam-pcm.c
1 /*
2  * tascam-pcm.c - a part of driver for TASCAM FireWire series
3  *
4  * Copyright (c) 2015 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8
9 #include "tascam.h"
10
11 static void set_buffer_params(struct snd_pcm_hardware *hw)
12 {
13         hw->period_bytes_min = 4 * hw->channels_min;
14         hw->period_bytes_max = hw->period_bytes_min * 2048;
15         hw->buffer_bytes_max = hw->period_bytes_max * 2;
16
17         hw->periods_min = 2;
18         hw->periods_max = UINT_MAX;
19 }
20
21 static int pcm_init_hw_params(struct snd_tscm *tscm,
22                               struct snd_pcm_substream *substream)
23 {
24         static const struct snd_pcm_hardware hardware = {
25                 .info = SNDRV_PCM_INFO_BATCH |
26                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
27                         SNDRV_PCM_INFO_INTERLEAVED |
28                         SNDRV_PCM_INFO_JOINT_DUPLEX |
29                         SNDRV_PCM_INFO_MMAP |
30                         SNDRV_PCM_INFO_MMAP_VALID,
31                 .rates = SNDRV_PCM_RATE_44100 |
32                          SNDRV_PCM_RATE_48000 |
33                          SNDRV_PCM_RATE_88200 |
34                          SNDRV_PCM_RATE_96000,
35                 .rate_min = 44100,
36                 .rate_max = 96000,
37                 .channels_min = 10,
38                 .channels_max = 18,
39         };
40         struct snd_pcm_runtime *runtime = substream->runtime;
41         struct amdtp_stream *stream;
42         unsigned int pcm_channels;
43
44         runtime->hw = hardware;
45
46         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
47                 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
48                 stream = &tscm->tx_stream;
49                 pcm_channels = tscm->spec->pcm_capture_analog_channels;
50         } else {
51                 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
52                 stream = &tscm->rx_stream;
53                 pcm_channels = tscm->spec->pcm_playback_analog_channels;
54         }
55
56         if (tscm->spec->has_adat)
57                 pcm_channels += 8;
58         if (tscm->spec->has_spdif)
59                 pcm_channels += 2;
60         runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
61
62         set_buffer_params(&runtime->hw);
63
64         return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
65 }
66
67 static int pcm_open(struct snd_pcm_substream *substream)
68 {
69         struct snd_tscm *tscm = substream->private_data;
70         enum snd_tscm_clock clock;
71         unsigned int rate;
72         int err;
73
74         err = snd_tscm_stream_lock_try(tscm);
75         if (err < 0)
76                 goto end;
77
78         err = pcm_init_hw_params(tscm, substream);
79         if (err < 0)
80                 goto err_locked;
81
82         err = snd_tscm_stream_get_clock(tscm, &clock);
83         if (clock != SND_TSCM_CLOCK_INTERNAL ||
84             amdtp_stream_pcm_running(&tscm->rx_stream) ||
85             amdtp_stream_pcm_running(&tscm->tx_stream)) {
86                 err = snd_tscm_stream_get_rate(tscm, &rate);
87                 if (err < 0)
88                         goto err_locked;
89                 substream->runtime->hw.rate_min = rate;
90                 substream->runtime->hw.rate_max = rate;
91         }
92
93         snd_pcm_set_sync(substream);
94 end:
95         return err;
96 err_locked:
97         snd_tscm_stream_lock_release(tscm);
98         return err;
99 }
100
101 static int pcm_close(struct snd_pcm_substream *substream)
102 {
103         struct snd_tscm *tscm = substream->private_data;
104
105         snd_tscm_stream_lock_release(tscm);
106
107         return 0;
108 }
109
110 static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
111                                  struct snd_pcm_hw_params *hw_params)
112 {
113         struct snd_tscm *tscm = substream->private_data;
114         int err;
115
116         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
117                                                params_buffer_bytes(hw_params));
118         if (err < 0)
119                 return err;
120
121         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
122                 mutex_lock(&tscm->mutex);
123                 tscm->substreams_counter++;
124                 mutex_unlock(&tscm->mutex);
125         }
126
127         return 0;
128 }
129
130 static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
131                                   struct snd_pcm_hw_params *hw_params)
132 {
133         struct snd_tscm *tscm = substream->private_data;
134         int err;
135
136         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
137                                                params_buffer_bytes(hw_params));
138         if (err < 0)
139                 return err;
140
141         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
142                 mutex_lock(&tscm->mutex);
143                 tscm->substreams_counter++;
144                 mutex_unlock(&tscm->mutex);
145         }
146
147         return 0;
148 }
149
150 static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
151 {
152         struct snd_tscm *tscm = substream->private_data;
153
154         mutex_lock(&tscm->mutex);
155
156         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
157                 tscm->substreams_counter--;
158
159         snd_tscm_stream_stop_duplex(tscm);
160
161         mutex_unlock(&tscm->mutex);
162
163         return snd_pcm_lib_free_vmalloc_buffer(substream);
164 }
165
166 static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
167 {
168         struct snd_tscm *tscm = substream->private_data;
169
170         mutex_lock(&tscm->mutex);
171
172         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
173                 tscm->substreams_counter--;
174
175         snd_tscm_stream_stop_duplex(tscm);
176
177         mutex_unlock(&tscm->mutex);
178
179         return snd_pcm_lib_free_vmalloc_buffer(substream);
180 }
181
182 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
183 {
184         struct snd_tscm *tscm = substream->private_data;
185         struct snd_pcm_runtime *runtime = substream->runtime;
186         int err;
187
188         mutex_lock(&tscm->mutex);
189
190         err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
191         if (err >= 0)
192                 amdtp_stream_pcm_prepare(&tscm->tx_stream);
193
194         mutex_unlock(&tscm->mutex);
195
196         return err;
197 }
198
199 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
200 {
201         struct snd_tscm *tscm = substream->private_data;
202         struct snd_pcm_runtime *runtime = substream->runtime;
203         int err;
204
205         mutex_lock(&tscm->mutex);
206
207         err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
208         if (err >= 0)
209                 amdtp_stream_pcm_prepare(&tscm->rx_stream);
210
211         mutex_unlock(&tscm->mutex);
212
213         return err;
214 }
215
216 static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
217 {
218         struct snd_tscm *tscm = substream->private_data;
219
220         switch (cmd) {
221         case SNDRV_PCM_TRIGGER_START:
222                 amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
223                 break;
224         case SNDRV_PCM_TRIGGER_STOP:
225                 amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
226                 break;
227         default:
228                 return -EINVAL;
229         }
230
231         return 0;
232 }
233
234 static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
235 {
236         struct snd_tscm *tscm = substream->private_data;
237
238         switch (cmd) {
239         case SNDRV_PCM_TRIGGER_START:
240                 amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
241                 break;
242         case SNDRV_PCM_TRIGGER_STOP:
243                 amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
244                 break;
245         default:
246                 return -EINVAL;
247         }
248
249         return 0;
250 }
251
252 static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
253 {
254         struct snd_tscm *tscm = sbstrm->private_data;
255
256         return amdtp_stream_pcm_pointer(&tscm->tx_stream);
257 }
258
259 static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
260 {
261         struct snd_tscm *tscm = sbstrm->private_data;
262
263         return amdtp_stream_pcm_pointer(&tscm->rx_stream);
264 }
265
266 static int pcm_capture_ack(struct snd_pcm_substream *substream)
267 {
268         struct snd_tscm *tscm = substream->private_data;
269
270         return amdtp_stream_pcm_ack(&tscm->tx_stream);
271 }
272
273 static int pcm_playback_ack(struct snd_pcm_substream *substream)
274 {
275         struct snd_tscm *tscm = substream->private_data;
276
277         return amdtp_stream_pcm_ack(&tscm->rx_stream);
278 }
279
280 int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
281 {
282         static const struct snd_pcm_ops capture_ops = {
283                 .open           = pcm_open,
284                 .close          = pcm_close,
285                 .ioctl          = snd_pcm_lib_ioctl,
286                 .hw_params      = pcm_capture_hw_params,
287                 .hw_free        = pcm_capture_hw_free,
288                 .prepare        = pcm_capture_prepare,
289                 .trigger        = pcm_capture_trigger,
290                 .pointer        = pcm_capture_pointer,
291                 .ack            = pcm_capture_ack,
292                 .page           = snd_pcm_lib_get_vmalloc_page,
293         };
294         static const struct snd_pcm_ops playback_ops = {
295                 .open           = pcm_open,
296                 .close          = pcm_close,
297                 .ioctl          = snd_pcm_lib_ioctl,
298                 .hw_params      = pcm_playback_hw_params,
299                 .hw_free        = pcm_playback_hw_free,
300                 .prepare        = pcm_playback_prepare,
301                 .trigger        = pcm_playback_trigger,
302                 .pointer        = pcm_playback_pointer,
303                 .ack            = pcm_playback_ack,
304                 .page           = snd_pcm_lib_get_vmalloc_page,
305                 .mmap           = snd_pcm_lib_mmap_vmalloc,
306         };
307         struct snd_pcm *pcm;
308         int err;
309
310         err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
311         if (err < 0)
312                 return err;
313
314         pcm->private_data = tscm;
315         snprintf(pcm->name, sizeof(pcm->name),
316                  "%s PCM", tscm->card->shortname);
317         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
318         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
319
320         return 0;
321 }