]> asedeno.scripts.mit.edu Git - linux.git/blob - sound/firewire/tascam/tascam-pcm.c
Merge tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
[linux.git] / sound / firewire / tascam / tascam-pcm.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * tascam-pcm.c - a part of driver for TASCAM FireWire series
4  *
5  * Copyright (c) 2015 Takashi Sakamoto
6  */
7
8 #include "tascam.h"
9
10 static int pcm_init_hw_params(struct snd_tscm *tscm,
11                               struct snd_pcm_substream *substream)
12 {
13         struct snd_pcm_runtime *runtime = substream->runtime;
14         struct snd_pcm_hardware *hw = &runtime->hw;
15         struct amdtp_stream *stream;
16         unsigned int pcm_channels;
17
18         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
19                 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
20                 stream = &tscm->tx_stream;
21                 pcm_channels = tscm->spec->pcm_capture_analog_channels;
22         } else {
23                 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
24                 stream = &tscm->rx_stream;
25                 pcm_channels = tscm->spec->pcm_playback_analog_channels;
26         }
27
28         if (tscm->spec->has_adat)
29                 pcm_channels += 8;
30         if (tscm->spec->has_spdif)
31                 pcm_channels += 2;
32         runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
33
34         hw->rates = SNDRV_PCM_RATE_44100 |
35                     SNDRV_PCM_RATE_48000 |
36                     SNDRV_PCM_RATE_88200 |
37                     SNDRV_PCM_RATE_96000;
38         snd_pcm_limit_hw_rates(runtime);
39
40         return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
41 }
42
43 static int pcm_open(struct snd_pcm_substream *substream)
44 {
45         struct snd_tscm *tscm = substream->private_data;
46         enum snd_tscm_clock clock;
47         unsigned int rate;
48         int err;
49
50         err = snd_tscm_stream_lock_try(tscm);
51         if (err < 0)
52                 goto end;
53
54         err = pcm_init_hw_params(tscm, substream);
55         if (err < 0)
56                 goto err_locked;
57
58         err = snd_tscm_stream_get_clock(tscm, &clock);
59         if (clock != SND_TSCM_CLOCK_INTERNAL ||
60             amdtp_stream_pcm_running(&tscm->rx_stream) ||
61             amdtp_stream_pcm_running(&tscm->tx_stream)) {
62                 err = snd_tscm_stream_get_rate(tscm, &rate);
63                 if (err < 0)
64                         goto err_locked;
65                 substream->runtime->hw.rate_min = rate;
66                 substream->runtime->hw.rate_max = rate;
67         }
68
69         snd_pcm_set_sync(substream);
70 end:
71         return err;
72 err_locked:
73         snd_tscm_stream_lock_release(tscm);
74         return err;
75 }
76
77 static int pcm_close(struct snd_pcm_substream *substream)
78 {
79         struct snd_tscm *tscm = substream->private_data;
80
81         snd_tscm_stream_lock_release(tscm);
82
83         return 0;
84 }
85
86 static int pcm_hw_params(struct snd_pcm_substream *substream,
87                          struct snd_pcm_hw_params *hw_params)
88 {
89         struct snd_tscm *tscm = substream->private_data;
90         int err;
91
92         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
93                                                params_buffer_bytes(hw_params));
94         if (err < 0)
95                 return err;
96
97         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
98                 unsigned int rate = params_rate(hw_params);
99
100                 mutex_lock(&tscm->mutex);
101                 err = snd_tscm_stream_reserve_duplex(tscm, rate);
102                 if (err >= 0)
103                         ++tscm->substreams_counter;
104                 mutex_unlock(&tscm->mutex);
105         }
106
107         return err;
108 }
109
110 static int pcm_hw_free(struct snd_pcm_substream *substream)
111 {
112         struct snd_tscm *tscm = substream->private_data;
113
114         mutex_lock(&tscm->mutex);
115
116         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
117                 --tscm->substreams_counter;
118
119         snd_tscm_stream_stop_duplex(tscm);
120
121         mutex_unlock(&tscm->mutex);
122
123         return snd_pcm_lib_free_vmalloc_buffer(substream);
124 }
125
126 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
127 {
128         struct snd_tscm *tscm = substream->private_data;
129         struct snd_pcm_runtime *runtime = substream->runtime;
130         int err;
131
132         mutex_lock(&tscm->mutex);
133
134         err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
135         if (err >= 0)
136                 amdtp_stream_pcm_prepare(&tscm->tx_stream);
137
138         mutex_unlock(&tscm->mutex);
139
140         return err;
141 }
142
143 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
144 {
145         struct snd_tscm *tscm = substream->private_data;
146         struct snd_pcm_runtime *runtime = substream->runtime;
147         int err;
148
149         mutex_lock(&tscm->mutex);
150
151         err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
152         if (err >= 0)
153                 amdtp_stream_pcm_prepare(&tscm->rx_stream);
154
155         mutex_unlock(&tscm->mutex);
156
157         return err;
158 }
159
160 static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
161 {
162         struct snd_tscm *tscm = substream->private_data;
163
164         switch (cmd) {
165         case SNDRV_PCM_TRIGGER_START:
166                 amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
167                 break;
168         case SNDRV_PCM_TRIGGER_STOP:
169                 amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
170                 break;
171         default:
172                 return -EINVAL;
173         }
174
175         return 0;
176 }
177
178 static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
179 {
180         struct snd_tscm *tscm = substream->private_data;
181
182         switch (cmd) {
183         case SNDRV_PCM_TRIGGER_START:
184                 amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
185                 break;
186         case SNDRV_PCM_TRIGGER_STOP:
187                 amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
188                 break;
189         default:
190                 return -EINVAL;
191         }
192
193         return 0;
194 }
195
196 static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
197 {
198         struct snd_tscm *tscm = sbstrm->private_data;
199
200         return amdtp_stream_pcm_pointer(&tscm->tx_stream);
201 }
202
203 static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
204 {
205         struct snd_tscm *tscm = sbstrm->private_data;
206
207         return amdtp_stream_pcm_pointer(&tscm->rx_stream);
208 }
209
210 static int pcm_capture_ack(struct snd_pcm_substream *substream)
211 {
212         struct snd_tscm *tscm = substream->private_data;
213
214         return amdtp_stream_pcm_ack(&tscm->tx_stream);
215 }
216
217 static int pcm_playback_ack(struct snd_pcm_substream *substream)
218 {
219         struct snd_tscm *tscm = substream->private_data;
220
221         return amdtp_stream_pcm_ack(&tscm->rx_stream);
222 }
223
224 int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
225 {
226         static const struct snd_pcm_ops capture_ops = {
227                 .open           = pcm_open,
228                 .close          = pcm_close,
229                 .ioctl          = snd_pcm_lib_ioctl,
230                 .hw_params      = pcm_hw_params,
231                 .hw_free        = pcm_hw_free,
232                 .prepare        = pcm_capture_prepare,
233                 .trigger        = pcm_capture_trigger,
234                 .pointer        = pcm_capture_pointer,
235                 .ack            = pcm_capture_ack,
236                 .page           = snd_pcm_lib_get_vmalloc_page,
237         };
238         static const struct snd_pcm_ops playback_ops = {
239                 .open           = pcm_open,
240                 .close          = pcm_close,
241                 .ioctl          = snd_pcm_lib_ioctl,
242                 .hw_params      = pcm_hw_params,
243                 .hw_free        = pcm_hw_free,
244                 .prepare        = pcm_playback_prepare,
245                 .trigger        = pcm_playback_trigger,
246                 .pointer        = pcm_playback_pointer,
247                 .ack            = pcm_playback_ack,
248                 .page           = snd_pcm_lib_get_vmalloc_page,
249         };
250         struct snd_pcm *pcm;
251         int err;
252
253         err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
254         if (err < 0)
255                 return err;
256
257         pcm->private_data = tscm;
258         snprintf(pcm->name, sizeof(pcm->name),
259                  "%s PCM", tscm->card->shortname);
260         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
261         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
262
263         return 0;
264 }