]> git.karo-electronics.de Git - linux-beck.git/blob - sound/firewire/digi00x/digi00x-pcm.c
87c9aa23489044aaf6bff468df933804477d5a51
[linux-beck.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 = pcm_init_hw_params(dg00x, substream);
122         if (err < 0)
123                 return err;
124
125         /* Check current clock source. */
126         err = snd_dg00x_stream_get_clock(dg00x, &clock);
127         if (err < 0)
128                 return err;
129         if (clock != SND_DG00X_CLOCK_INTERNAL) {
130                 err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
131                 if (err < 0)
132                         return err;
133                 if (!detect) {
134                         err = -EBUSY;
135                         return err;
136                 }
137         }
138
139         if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
140             amdtp_stream_pcm_running(&dg00x->rx_stream) ||
141             amdtp_stream_pcm_running(&dg00x->tx_stream)) {
142                 err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
143                 if (err < 0)
144                         return err;
145                 substream->runtime->hw.rate_min = rate;
146                 substream->runtime->hw.rate_max = rate;
147         }
148
149         snd_pcm_set_sync(substream);
150
151         return err;
152 }
153
154 static int pcm_close(struct snd_pcm_substream *substream)
155 {
156         return 0;
157 }
158
159 static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
160                                  struct snd_pcm_hw_params *hw_params)
161 {
162         struct snd_dg00x *dg00x = substream->private_data;
163         int err;
164
165         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
166                                                params_buffer_bytes(hw_params));
167         if (err < 0)
168                 return err;
169
170         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
171                 mutex_lock(&dg00x->mutex);
172                 dg00x->substreams_counter++;
173                 mutex_unlock(&dg00x->mutex);
174         }
175
176         amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
177
178         return 0;
179 }
180
181 static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
182                                   struct snd_pcm_hw_params *hw_params)
183 {
184         struct snd_dg00x *dg00x = substream->private_data;
185         int err;
186
187         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
188                                                params_buffer_bytes(hw_params));
189         if (err < 0)
190                 return err;
191
192         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
193                 mutex_lock(&dg00x->mutex);
194                 dg00x->substreams_counter++;
195                 mutex_unlock(&dg00x->mutex);
196         }
197
198         amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
199
200         return 0;
201 }
202
203 static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
204 {
205         struct snd_dg00x *dg00x = substream->private_data;
206
207         mutex_lock(&dg00x->mutex);
208
209         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
210                 dg00x->substreams_counter--;
211
212         snd_dg00x_stream_stop_duplex(dg00x);
213
214         mutex_unlock(&dg00x->mutex);
215
216         return snd_pcm_lib_free_vmalloc_buffer(substream);
217 }
218
219 static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
220 {
221         struct snd_dg00x *dg00x = substream->private_data;
222
223         mutex_lock(&dg00x->mutex);
224
225         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
226                 dg00x->substreams_counter--;
227
228         snd_dg00x_stream_stop_duplex(dg00x);
229
230         mutex_unlock(&dg00x->mutex);
231
232         return snd_pcm_lib_free_vmalloc_buffer(substream);
233 }
234
235 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
236 {
237         struct snd_dg00x *dg00x = substream->private_data;
238         struct snd_pcm_runtime *runtime = substream->runtime;
239         int err;
240
241         mutex_lock(&dg00x->mutex);
242
243         err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
244         if (err >= 0)
245                 amdtp_stream_pcm_prepare(&dg00x->tx_stream);
246
247         mutex_unlock(&dg00x->mutex);
248
249         return err;
250 }
251
252 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
253 {
254         struct snd_dg00x *dg00x = substream->private_data;
255         struct snd_pcm_runtime *runtime = substream->runtime;
256         int err;
257
258         mutex_lock(&dg00x->mutex);
259
260         err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
261         if (err >= 0) {
262                 amdtp_stream_pcm_prepare(&dg00x->rx_stream);
263                 amdtp_dot_reset(&dg00x->rx_stream);
264         }
265
266         mutex_unlock(&dg00x->mutex);
267
268         return err;
269 }
270
271 static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
272 {
273         struct snd_dg00x *dg00x = substream->private_data;
274
275         switch (cmd) {
276         case SNDRV_PCM_TRIGGER_START:
277                 amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
278                 break;
279         case SNDRV_PCM_TRIGGER_STOP:
280                 amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
281                 break;
282         default:
283                 return -EINVAL;
284         }
285
286         return 0;
287 }
288
289 static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
290 {
291         struct snd_dg00x *dg00x = substream->private_data;
292
293         switch (cmd) {
294         case SNDRV_PCM_TRIGGER_START:
295                 amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
296                 break;
297         case SNDRV_PCM_TRIGGER_STOP:
298                 amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
299                 break;
300         default:
301                 return -EINVAL;
302         }
303
304         return 0;
305 }
306
307 static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
308 {
309         struct snd_dg00x *dg00x = sbstrm->private_data;
310
311         return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
312 }
313
314 static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
315 {
316         struct snd_dg00x *dg00x = sbstrm->private_data;
317
318         return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
319 }
320
321 static struct snd_pcm_ops pcm_capture_ops = {
322         .open           = pcm_open,
323         .close          = pcm_close,
324         .ioctl          = snd_pcm_lib_ioctl,
325         .hw_params      = pcm_capture_hw_params,
326         .hw_free        = pcm_capture_hw_free,
327         .prepare        = pcm_capture_prepare,
328         .trigger        = pcm_capture_trigger,
329         .pointer        = pcm_capture_pointer,
330         .page           = snd_pcm_lib_get_vmalloc_page,
331 };
332
333 static struct snd_pcm_ops pcm_playback_ops = {
334         .open           = pcm_open,
335         .close          = pcm_close,
336         .ioctl          = snd_pcm_lib_ioctl,
337         .hw_params      = pcm_playback_hw_params,
338         .hw_free        = pcm_playback_hw_free,
339         .prepare        = pcm_playback_prepare,
340         .trigger        = pcm_playback_trigger,
341         .pointer        = pcm_playback_pointer,
342         .page           = snd_pcm_lib_get_vmalloc_page,
343         .mmap           = snd_pcm_lib_mmap_vmalloc,
344 };
345
346 int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
347 {
348         struct snd_pcm *pcm;
349         int err;
350
351         err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
352         if (err < 0)
353                 return err;
354
355         pcm->private_data = dg00x;
356         snprintf(pcm->name, sizeof(pcm->name),
357                  "%s PCM", dg00x->card->shortname);
358         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
359         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
360
361         return 0;
362 }