1
2
3
4
5
6
7
8
9#include "tascam.h"
10
11static 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
21static 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 =
52 SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
53 stream = &tscm->rx_stream;
54 pcm_channels = tscm->spec->pcm_playback_analog_channels;
55 }
56
57 if (tscm->spec->has_adat)
58 pcm_channels += 8;
59 if (tscm->spec->has_spdif)
60 pcm_channels += 2;
61 runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
62
63 set_buffer_params(&runtime->hw);
64
65 return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
66}
67
68static int pcm_open(struct snd_pcm_substream *substream)
69{
70 struct snd_tscm *tscm = substream->private_data;
71 enum snd_tscm_clock clock;
72 unsigned int rate;
73 int err;
74
75 err = snd_tscm_stream_lock_try(tscm);
76 if (err < 0)
77 goto end;
78
79 err = pcm_init_hw_params(tscm, substream);
80 if (err < 0)
81 goto err_locked;
82
83 err = snd_tscm_stream_get_clock(tscm, &clock);
84 if (clock != SND_TSCM_CLOCK_INTERNAL ||
85 amdtp_stream_pcm_running(&tscm->rx_stream) ||
86 amdtp_stream_pcm_running(&tscm->tx_stream)) {
87 err = snd_tscm_stream_get_rate(tscm, &rate);
88 if (err < 0)
89 goto err_locked;
90 substream->runtime->hw.rate_min = rate;
91 substream->runtime->hw.rate_max = rate;
92 }
93
94 snd_pcm_set_sync(substream);
95end:
96 return err;
97err_locked:
98 snd_tscm_stream_lock_release(tscm);
99 return err;
100}
101
102static int pcm_close(struct snd_pcm_substream *substream)
103{
104 struct snd_tscm *tscm = substream->private_data;
105
106 snd_tscm_stream_lock_release(tscm);
107
108 return 0;
109}
110
111static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
112 struct snd_pcm_hw_params *hw_params)
113{
114 struct snd_tscm *tscm = substream->private_data;
115 int err;
116
117 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
118 params_buffer_bytes(hw_params));
119 if (err < 0)
120 return err;
121
122 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
123 mutex_lock(&tscm->mutex);
124 tscm->substreams_counter++;
125 mutex_unlock(&tscm->mutex);
126 }
127
128 amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
129
130 return 0;
131}
132
133static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
134 struct snd_pcm_hw_params *hw_params)
135{
136 struct snd_tscm *tscm = substream->private_data;
137 int err;
138
139 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
140 params_buffer_bytes(hw_params));
141 if (err < 0)
142 return err;
143
144 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
145 mutex_lock(&tscm->mutex);
146 tscm->substreams_counter++;
147 mutex_unlock(&tscm->mutex);
148 }
149
150 amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
151
152 return 0;
153}
154
155static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
156{
157 struct snd_tscm *tscm = substream->private_data;
158
159 mutex_lock(&tscm->mutex);
160
161 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
162 tscm->substreams_counter--;
163
164 snd_tscm_stream_stop_duplex(tscm);
165
166 mutex_unlock(&tscm->mutex);
167
168 return snd_pcm_lib_free_vmalloc_buffer(substream);
169}
170
171static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
172{
173 struct snd_tscm *tscm = substream->private_data;
174
175 mutex_lock(&tscm->mutex);
176
177 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
178 tscm->substreams_counter--;
179
180 snd_tscm_stream_stop_duplex(tscm);
181
182 mutex_unlock(&tscm->mutex);
183
184 return snd_pcm_lib_free_vmalloc_buffer(substream);
185}
186
187static int pcm_capture_prepare(struct snd_pcm_substream *substream)
188{
189 struct snd_tscm *tscm = substream->private_data;
190 struct snd_pcm_runtime *runtime = substream->runtime;
191 int err;
192
193 mutex_lock(&tscm->mutex);
194
195 err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
196 if (err >= 0)
197 amdtp_stream_pcm_prepare(&tscm->tx_stream);
198
199 mutex_unlock(&tscm->mutex);
200
201 return err;
202}
203
204static int pcm_playback_prepare(struct snd_pcm_substream *substream)
205{
206 struct snd_tscm *tscm = substream->private_data;
207 struct snd_pcm_runtime *runtime = substream->runtime;
208 int err;
209
210 mutex_lock(&tscm->mutex);
211
212 err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
213 if (err >= 0)
214 amdtp_stream_pcm_prepare(&tscm->rx_stream);
215
216 mutex_unlock(&tscm->mutex);
217
218 return err;
219}
220
221static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
222{
223 struct snd_tscm *tscm = substream->private_data;
224
225 switch (cmd) {
226 case SNDRV_PCM_TRIGGER_START:
227 amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
228 break;
229 case SNDRV_PCM_TRIGGER_STOP:
230 amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
231 break;
232 default:
233 return -EINVAL;
234 }
235
236 return 0;
237}
238
239static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
240{
241 struct snd_tscm *tscm = substream->private_data;
242
243 switch (cmd) {
244 case SNDRV_PCM_TRIGGER_START:
245 amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
246 break;
247 case SNDRV_PCM_TRIGGER_STOP:
248 amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
249 break;
250 default:
251 return -EINVAL;
252 }
253
254 return 0;
255}
256
257static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
258{
259 struct snd_tscm *tscm = sbstrm->private_data;
260
261 return amdtp_stream_pcm_pointer(&tscm->tx_stream);
262}
263
264static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
265{
266 struct snd_tscm *tscm = sbstrm->private_data;
267
268 return amdtp_stream_pcm_pointer(&tscm->rx_stream);
269}
270
271static const struct snd_pcm_ops pcm_capture_ops = {
272 .open = pcm_open,
273 .close = pcm_close,
274 .ioctl = snd_pcm_lib_ioctl,
275 .hw_params = pcm_capture_hw_params,
276 .hw_free = pcm_capture_hw_free,
277 .prepare = pcm_capture_prepare,
278 .trigger = pcm_capture_trigger,
279 .pointer = pcm_capture_pointer,
280 .page = snd_pcm_lib_get_vmalloc_page,
281};
282
283static const struct snd_pcm_ops pcm_playback_ops = {
284 .open = pcm_open,
285 .close = pcm_close,
286 .ioctl = snd_pcm_lib_ioctl,
287 .hw_params = pcm_playback_hw_params,
288 .hw_free = pcm_playback_hw_free,
289 .prepare = pcm_playback_prepare,
290 .trigger = pcm_playback_trigger,
291 .pointer = pcm_playback_pointer,
292 .page = snd_pcm_lib_get_vmalloc_page,
293 .mmap = snd_pcm_lib_mmap_vmalloc,
294};
295
296int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
297{
298 struct snd_pcm *pcm;
299 int err;
300
301 err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
302 if (err < 0)
303 return err;
304
305 pcm->private_data = tscm;
306 snprintf(pcm->name, sizeof(pcm->name),
307 "%s PCM", tscm->card->shortname);
308 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
309 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
310
311 return 0;
312}
313