]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - sound/firewire/digi00x/digi00x-pcm.c
Merge remote-tracking branch 'sound-current/for-linus'
[karo-tx-linux.git] / sound / firewire / digi00x / digi00x-pcm.c
1 /*
2  * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
3  *
4  * Copyright (c) 2014-2015 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8
9 #include "digi00x.h"
10
11 static int hw_rule_rate(struct snd_pcm_hw_params *params,
12                         struct snd_pcm_hw_rule *rule)
13 {
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_DG00X_RATE_COUNT; i++) {
24                 if (!snd_interval_test(c,
25                                        snd_dg00x_stream_pcm_channels[i]))
26                         continue;
27
28                 t.min = min(t.min, snd_dg00x_stream_rates[i]);
29                 t.max = max(t.max, snd_dg00x_stream_rates[i]);
30         }
31
32         return snd_interval_refine(r, &t);
33 }
34
35 static int hw_rule_channels(struct snd_pcm_hw_params *params,
36                             struct snd_pcm_hw_rule *rule)
37 {
38         struct snd_interval *c =
39                 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
40         const struct snd_interval *r =
41                 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
42         struct snd_interval t = {
43                 .min = UINT_MAX, .max = 0, .integer = 1,
44         };
45         unsigned int i;
46
47         for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
48                 if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
49                         continue;
50
51                 t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
52                 t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
53         }
54
55         return snd_interval_refine(c, &t);
56 }
57
58 static int pcm_init_hw_params(struct snd_dg00x *dg00x,
59                               struct snd_pcm_substream *substream)
60 {
61         static const struct snd_pcm_hardware hardware = {
62                 .info = SNDRV_PCM_INFO_BATCH |
63                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
64                         SNDRV_PCM_INFO_INTERLEAVED |
65                         SNDRV_PCM_INFO_JOINT_DUPLEX |
66                         SNDRV_PCM_INFO_MMAP |
67                         SNDRV_PCM_INFO_MMAP_VALID,
68                 .rates = SNDRV_PCM_RATE_44100 |
69                          SNDRV_PCM_RATE_48000 |
70                          SNDRV_PCM_RATE_88200 |
71                          SNDRV_PCM_RATE_96000,
72                 .rate_min = 44100,
73                 .rate_max = 96000,
74                 .channels_min = 10,
75                 .channels_max = 18,
76                 .period_bytes_min = 4 * 18,
77                 .period_bytes_max = 4 * 18 * 2048,
78                 .buffer_bytes_max = 4 * 18 * 2048 * 2,
79                 .periods_min = 2,
80                 .periods_max = UINT_MAX,
81         };
82         struct amdtp_stream *s;
83         int err;
84
85         substream->runtime->hw = hardware;
86
87         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
88                 substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
89                 s = &dg00x->tx_stream;
90         } else {
91                 substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
92                                                  SNDRV_PCM_FMTBIT_S32;
93                 s = &dg00x->rx_stream;
94         }
95
96         err = snd_pcm_hw_rule_add(substream->runtime, 0,
97                                   SNDRV_PCM_HW_PARAM_CHANNELS,
98                                   hw_rule_channels, NULL,
99                                   SNDRV_PCM_HW_PARAM_RATE, -1);
100         if (err < 0)
101                 return err;
102
103         err = snd_pcm_hw_rule_add(substream->runtime, 0,
104                                   SNDRV_PCM_HW_PARAM_RATE,
105                                   hw_rule_rate, NULL,
106                                   SNDRV_PCM_HW_PARAM_CHANNELS, -1);
107         if (err < 0)
108                 return err;
109
110         return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
111 }
112
113 static int pcm_open(struct snd_pcm_substream *substream)
114 {
115         struct snd_dg00x *dg00x = substream->private_data;
116         enum snd_dg00x_clock clock;
117         bool detect;
118         unsigned int rate;
119         int err;
120
121         err = snd_dg00x_stream_lock_try(dg00x);
122         if (err < 0)
123                 goto end;
124
125         err = pcm_init_hw_params(dg00x, substream);
126         if (err < 0)
127                 goto err_locked;
128
129         /* Check current clock source. */
130         err = snd_dg00x_stream_get_clock(dg00x, &clock);
131         if (err < 0)
132                 goto err_locked;
133         if (clock != SND_DG00X_CLOCK_INTERNAL) {
134                 err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
135                 if (err < 0)
136                         goto err_locked;
137                 if (!detect) {
138                         err = -EBUSY;
139                         goto err_locked;
140                 }
141         }
142
143         if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
144             amdtp_stream_pcm_running(&dg00x->rx_stream) ||
145             amdtp_stream_pcm_running(&dg00x->tx_stream)) {
146                 err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
147                 if (err < 0)
148                         goto err_locked;
149                 substream->runtime->hw.rate_min = rate;
150                 substream->runtime->hw.rate_max = rate;
151         }
152
153         snd_pcm_set_sync(substream);
154 end:
155         return err;
156 err_locked:
157         snd_dg00x_stream_lock_release(dg00x);
158         return err;
159 }
160
161 static int pcm_close(struct snd_pcm_substream *substream)
162 {
163         struct snd_dg00x *dg00x = substream->private_data;
164
165         snd_dg00x_stream_lock_release(dg00x);
166
167         return 0;
168 }
169
170 static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
171                                  struct snd_pcm_hw_params *hw_params)
172 {
173         struct snd_dg00x *dg00x = substream->private_data;
174         int err;
175
176         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
177                                                params_buffer_bytes(hw_params));
178         if (err < 0)
179                 return err;
180
181         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
182                 mutex_lock(&dg00x->mutex);
183                 dg00x->substreams_counter++;
184                 mutex_unlock(&dg00x->mutex);
185         }
186
187         amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
188
189         return 0;
190 }
191
192 static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
193                                   struct snd_pcm_hw_params *hw_params)
194 {
195         struct snd_dg00x *dg00x = substream->private_data;
196         int err;
197
198         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
199                                                params_buffer_bytes(hw_params));
200         if (err < 0)
201                 return err;
202
203         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
204                 mutex_lock(&dg00x->mutex);
205                 dg00x->substreams_counter++;
206                 mutex_unlock(&dg00x->mutex);
207         }
208
209         amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
210
211         return 0;
212 }
213
214 static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
215 {
216         struct snd_dg00x *dg00x = substream->private_data;
217
218         mutex_lock(&dg00x->mutex);
219
220         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
221                 dg00x->substreams_counter--;
222
223         snd_dg00x_stream_stop_duplex(dg00x);
224
225         mutex_unlock(&dg00x->mutex);
226
227         return snd_pcm_lib_free_vmalloc_buffer(substream);
228 }
229
230 static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
231 {
232         struct snd_dg00x *dg00x = substream->private_data;
233
234         mutex_lock(&dg00x->mutex);
235
236         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
237                 dg00x->substreams_counter--;
238
239         snd_dg00x_stream_stop_duplex(dg00x);
240
241         mutex_unlock(&dg00x->mutex);
242
243         return snd_pcm_lib_free_vmalloc_buffer(substream);
244 }
245
246 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
247 {
248         struct snd_dg00x *dg00x = substream->private_data;
249         struct snd_pcm_runtime *runtime = substream->runtime;
250         int err;
251
252         mutex_lock(&dg00x->mutex);
253
254         err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
255         if (err >= 0)
256                 amdtp_stream_pcm_prepare(&dg00x->tx_stream);
257
258         mutex_unlock(&dg00x->mutex);
259
260         return err;
261 }
262
263 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
264 {
265         struct snd_dg00x *dg00x = substream->private_data;
266         struct snd_pcm_runtime *runtime = substream->runtime;
267         int err;
268
269         mutex_lock(&dg00x->mutex);
270
271         err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
272         if (err >= 0) {
273                 amdtp_stream_pcm_prepare(&dg00x->rx_stream);
274                 amdtp_dot_reset(&dg00x->rx_stream);
275         }
276
277         mutex_unlock(&dg00x->mutex);
278
279         return err;
280 }
281
282 static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
283 {
284         struct snd_dg00x *dg00x = substream->private_data;
285
286         switch (cmd) {
287         case SNDRV_PCM_TRIGGER_START:
288                 amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
289                 break;
290         case SNDRV_PCM_TRIGGER_STOP:
291                 amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
292                 break;
293         default:
294                 return -EINVAL;
295         }
296
297         return 0;
298 }
299
300 static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
301 {
302         struct snd_dg00x *dg00x = substream->private_data;
303
304         switch (cmd) {
305         case SNDRV_PCM_TRIGGER_START:
306                 amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
307                 break;
308         case SNDRV_PCM_TRIGGER_STOP:
309                 amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
310                 break;
311         default:
312                 return -EINVAL;
313         }
314
315         return 0;
316 }
317
318 static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
319 {
320         struct snd_dg00x *dg00x = sbstrm->private_data;
321
322         return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
323 }
324
325 static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
326 {
327         struct snd_dg00x *dg00x = sbstrm->private_data;
328
329         return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
330 }
331
332 static struct snd_pcm_ops pcm_capture_ops = {
333         .open           = pcm_open,
334         .close          = pcm_close,
335         .ioctl          = snd_pcm_lib_ioctl,
336         .hw_params      = pcm_capture_hw_params,
337         .hw_free        = pcm_capture_hw_free,
338         .prepare        = pcm_capture_prepare,
339         .trigger        = pcm_capture_trigger,
340         .pointer        = pcm_capture_pointer,
341         .page           = snd_pcm_lib_get_vmalloc_page,
342 };
343
344 static struct snd_pcm_ops pcm_playback_ops = {
345         .open           = pcm_open,
346         .close          = pcm_close,
347         .ioctl          = snd_pcm_lib_ioctl,
348         .hw_params      = pcm_playback_hw_params,
349         .hw_free        = pcm_playback_hw_free,
350         .prepare        = pcm_playback_prepare,
351         .trigger        = pcm_playback_trigger,
352         .pointer        = pcm_playback_pointer,
353         .page           = snd_pcm_lib_get_vmalloc_page,
354         .mmap           = snd_pcm_lib_mmap_vmalloc,
355 };
356
357 int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
358 {
359         struct snd_pcm *pcm;
360         int err;
361
362         err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
363         if (err < 0)
364                 return err;
365
366         pcm->private_data = dg00x;
367         snprintf(pcm->name, sizeof(pcm->name),
368                  "%s PCM", dg00x->card->shortname);
369         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
370         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
371
372         return 0;
373 }