1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * bebob_pcm.c - a part of driver for BeBoB based devices |
4 | * |
5 | * Copyright (c) 2013-2014 Takashi Sakamoto |
6 | */ |
7 | |
8 | #include "./bebob.h" |
9 | |
10 | static int |
11 | hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) |
12 | { |
13 | struct snd_bebob_stream_formation *formations = rule->private; |
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_BEBOB_STRM_FMT_ENTRIES; i++) { |
24 | /* entry is invalid */ |
25 | if (formations[i].pcm == 0) |
26 | continue; |
27 | |
28 | if (!snd_interval_test(i: c, val: formations[i].pcm)) |
29 | continue; |
30 | |
31 | t.min = min(t.min, snd_bebob_rate_table[i]); |
32 | t.max = max(t.max, snd_bebob_rate_table[i]); |
33 | |
34 | } |
35 | return snd_interval_refine(i: r, v: &t); |
36 | } |
37 | |
38 | static int |
39 | hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) |
40 | { |
41 | struct snd_bebob_stream_formation *formations = rule->private; |
42 | struct snd_interval *c = |
43 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
44 | const struct snd_interval *r = |
45 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); |
46 | struct snd_interval t = { |
47 | .min = UINT_MAX, .max = 0, .integer = 1 |
48 | }; |
49 | |
50 | unsigned int i; |
51 | |
52 | for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { |
53 | /* entry is invalid */ |
54 | if (formations[i].pcm == 0) |
55 | continue; |
56 | |
57 | if (!snd_interval_test(i: r, val: snd_bebob_rate_table[i])) |
58 | continue; |
59 | |
60 | t.min = min(t.min, formations[i].pcm); |
61 | t.max = max(t.max, formations[i].pcm); |
62 | } |
63 | |
64 | return snd_interval_refine(i: c, v: &t); |
65 | } |
66 | |
67 | static void |
68 | limit_channels_and_rates(struct snd_pcm_hardware *hw, |
69 | struct snd_bebob_stream_formation *formations) |
70 | { |
71 | unsigned int i; |
72 | |
73 | hw->channels_min = UINT_MAX; |
74 | hw->channels_max = 0; |
75 | |
76 | hw->rate_min = UINT_MAX; |
77 | hw->rate_max = 0; |
78 | hw->rates = 0; |
79 | |
80 | for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { |
81 | /* entry has no PCM channels */ |
82 | if (formations[i].pcm == 0) |
83 | continue; |
84 | |
85 | hw->channels_min = min(hw->channels_min, formations[i].pcm); |
86 | hw->channels_max = max(hw->channels_max, formations[i].pcm); |
87 | |
88 | hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); |
89 | hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); |
90 | hw->rates |= snd_pcm_rate_to_rate_bit(rate: snd_bebob_rate_table[i]); |
91 | } |
92 | } |
93 | |
94 | static int |
95 | pcm_init_hw_params(struct snd_bebob *bebob, |
96 | struct snd_pcm_substream *substream) |
97 | { |
98 | struct snd_pcm_runtime *runtime = substream->runtime; |
99 | struct amdtp_stream *s; |
100 | struct snd_bebob_stream_formation *formations; |
101 | int err; |
102 | |
103 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
104 | runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; |
105 | s = &bebob->tx_stream; |
106 | formations = bebob->tx_stream_formations; |
107 | } else { |
108 | runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; |
109 | s = &bebob->rx_stream; |
110 | formations = bebob->rx_stream_formations; |
111 | } |
112 | |
113 | limit_channels_and_rates(hw: &runtime->hw, formations); |
114 | |
115 | err = snd_pcm_hw_rule_add(runtime, cond: 0, SNDRV_PCM_HW_PARAM_CHANNELS, |
116 | func: hw_rule_channels, private: formations, |
117 | SNDRV_PCM_HW_PARAM_RATE, -1); |
118 | if (err < 0) |
119 | goto end; |
120 | |
121 | err = snd_pcm_hw_rule_add(runtime, cond: 0, SNDRV_PCM_HW_PARAM_RATE, |
122 | func: hw_rule_rate, private: formations, |
123 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); |
124 | if (err < 0) |
125 | goto end; |
126 | |
127 | err = amdtp_am824_add_pcm_hw_constraints(s, runtime); |
128 | end: |
129 | return err; |
130 | } |
131 | |
132 | static int pcm_open(struct snd_pcm_substream *substream) |
133 | { |
134 | struct snd_bebob *bebob = substream->private_data; |
135 | const struct snd_bebob_rate_spec *spec = bebob->spec->rate; |
136 | struct amdtp_domain *d = &bebob->domain; |
137 | enum snd_bebob_clock_type src; |
138 | int err; |
139 | |
140 | err = snd_bebob_stream_lock_try(bebob); |
141 | if (err < 0) |
142 | return err; |
143 | |
144 | err = pcm_init_hw_params(bebob, substream); |
145 | if (err < 0) |
146 | goto err_locked; |
147 | |
148 | err = snd_bebob_stream_get_clock_src(bebob, src: &src); |
149 | if (err < 0) |
150 | goto err_locked; |
151 | |
152 | mutex_lock(&bebob->mutex); |
153 | |
154 | // When source of clock is not internal or any stream is reserved for |
155 | // transmission of PCM frames, the available sampling rate is limited |
156 | // at current one. |
157 | if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || |
158 | (bebob->substreams_counter > 0 && d->events_per_period > 0)) { |
159 | unsigned int frames_per_period = d->events_per_period; |
160 | unsigned int frames_per_buffer = d->events_per_buffer; |
161 | unsigned int sampling_rate; |
162 | |
163 | err = spec->get(bebob, &sampling_rate); |
164 | if (err < 0) { |
165 | mutex_unlock(lock: &bebob->mutex); |
166 | dev_err(&bebob->unit->device, |
167 | "fail to get sampling rate: %d\n" , err); |
168 | goto err_locked; |
169 | } |
170 | |
171 | substream->runtime->hw.rate_min = sampling_rate; |
172 | substream->runtime->hw.rate_max = sampling_rate; |
173 | |
174 | if (frames_per_period > 0) { |
175 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
176 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, |
177 | min: frames_per_period, max: frames_per_period); |
178 | if (err < 0) { |
179 | mutex_unlock(lock: &bebob->mutex); |
180 | goto err_locked; |
181 | } |
182 | |
183 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
184 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, |
185 | min: frames_per_buffer, max: frames_per_buffer); |
186 | if (err < 0) { |
187 | mutex_unlock(lock: &bebob->mutex); |
188 | goto err_locked; |
189 | } |
190 | } |
191 | } |
192 | |
193 | mutex_unlock(lock: &bebob->mutex); |
194 | |
195 | snd_pcm_set_sync(substream); |
196 | |
197 | return 0; |
198 | err_locked: |
199 | snd_bebob_stream_lock_release(bebob); |
200 | return err; |
201 | } |
202 | |
203 | static int |
204 | pcm_close(struct snd_pcm_substream *substream) |
205 | { |
206 | struct snd_bebob *bebob = substream->private_data; |
207 | snd_bebob_stream_lock_release(bebob); |
208 | return 0; |
209 | } |
210 | |
211 | static int pcm_hw_params(struct snd_pcm_substream *substream, |
212 | struct snd_pcm_hw_params *hw_params) |
213 | { |
214 | struct snd_bebob *bebob = substream->private_data; |
215 | int err = 0; |
216 | |
217 | if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
218 | unsigned int rate = params_rate(p: hw_params); |
219 | unsigned int frames_per_period = params_period_size(p: hw_params); |
220 | unsigned int frames_per_buffer = params_buffer_size(p: hw_params); |
221 | |
222 | mutex_lock(&bebob->mutex); |
223 | err = snd_bebob_stream_reserve_duplex(bebob, rate, |
224 | frames_per_period, frames_per_buffer); |
225 | if (err >= 0) |
226 | ++bebob->substreams_counter; |
227 | mutex_unlock(lock: &bebob->mutex); |
228 | } |
229 | |
230 | return err; |
231 | } |
232 | |
233 | static int pcm_hw_free(struct snd_pcm_substream *substream) |
234 | { |
235 | struct snd_bebob *bebob = substream->private_data; |
236 | |
237 | mutex_lock(&bebob->mutex); |
238 | |
239 | if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) |
240 | bebob->substreams_counter--; |
241 | |
242 | snd_bebob_stream_stop_duplex(bebob); |
243 | |
244 | mutex_unlock(lock: &bebob->mutex); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int |
250 | pcm_capture_prepare(struct snd_pcm_substream *substream) |
251 | { |
252 | struct snd_bebob *bebob = substream->private_data; |
253 | int err; |
254 | |
255 | err = snd_bebob_stream_start_duplex(bebob); |
256 | if (err >= 0) |
257 | amdtp_stream_pcm_prepare(s: &bebob->tx_stream); |
258 | |
259 | return err; |
260 | } |
261 | static int |
262 | pcm_playback_prepare(struct snd_pcm_substream *substream) |
263 | { |
264 | struct snd_bebob *bebob = substream->private_data; |
265 | int err; |
266 | |
267 | err = snd_bebob_stream_start_duplex(bebob); |
268 | if (err >= 0) |
269 | amdtp_stream_pcm_prepare(s: &bebob->rx_stream); |
270 | |
271 | return err; |
272 | } |
273 | |
274 | static int |
275 | pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) |
276 | { |
277 | struct snd_bebob *bebob = substream->private_data; |
278 | |
279 | switch (cmd) { |
280 | case SNDRV_PCM_TRIGGER_START: |
281 | amdtp_stream_pcm_trigger(s: &bebob->tx_stream, pcm: substream); |
282 | break; |
283 | case SNDRV_PCM_TRIGGER_STOP: |
284 | amdtp_stream_pcm_trigger(s: &bebob->tx_stream, NULL); |
285 | break; |
286 | default: |
287 | return -EINVAL; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | static int |
293 | pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) |
294 | { |
295 | struct snd_bebob *bebob = substream->private_data; |
296 | |
297 | switch (cmd) { |
298 | case SNDRV_PCM_TRIGGER_START: |
299 | amdtp_stream_pcm_trigger(s: &bebob->rx_stream, pcm: substream); |
300 | break; |
301 | case SNDRV_PCM_TRIGGER_STOP: |
302 | amdtp_stream_pcm_trigger(s: &bebob->rx_stream, NULL); |
303 | break; |
304 | default: |
305 | return -EINVAL; |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) |
312 | { |
313 | struct snd_bebob *bebob = sbstrm->private_data; |
314 | |
315 | return amdtp_domain_stream_pcm_pointer(d: &bebob->domain, |
316 | s: &bebob->tx_stream); |
317 | } |
318 | static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) |
319 | { |
320 | struct snd_bebob *bebob = sbstrm->private_data; |
321 | |
322 | return amdtp_domain_stream_pcm_pointer(d: &bebob->domain, |
323 | s: &bebob->rx_stream); |
324 | } |
325 | |
326 | static int pcm_capture_ack(struct snd_pcm_substream *substream) |
327 | { |
328 | struct snd_bebob *bebob = substream->private_data; |
329 | |
330 | return amdtp_domain_stream_pcm_ack(d: &bebob->domain, s: &bebob->tx_stream); |
331 | } |
332 | |
333 | static int pcm_playback_ack(struct snd_pcm_substream *substream) |
334 | { |
335 | struct snd_bebob *bebob = substream->private_data; |
336 | |
337 | return amdtp_domain_stream_pcm_ack(d: &bebob->domain, s: &bebob->rx_stream); |
338 | } |
339 | |
340 | int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) |
341 | { |
342 | static const struct snd_pcm_ops capture_ops = { |
343 | .open = pcm_open, |
344 | .close = pcm_close, |
345 | .hw_params = pcm_hw_params, |
346 | .hw_free = pcm_hw_free, |
347 | .prepare = pcm_capture_prepare, |
348 | .trigger = pcm_capture_trigger, |
349 | .pointer = pcm_capture_pointer, |
350 | .ack = pcm_capture_ack, |
351 | }; |
352 | static const struct snd_pcm_ops playback_ops = { |
353 | .open = pcm_open, |
354 | .close = pcm_close, |
355 | .hw_params = pcm_hw_params, |
356 | .hw_free = pcm_hw_free, |
357 | .prepare = pcm_playback_prepare, |
358 | .trigger = pcm_playback_trigger, |
359 | .pointer = pcm_playback_pointer, |
360 | .ack = pcm_playback_ack, |
361 | }; |
362 | struct snd_pcm *pcm; |
363 | int err; |
364 | |
365 | err = snd_pcm_new(card: bebob->card, id: bebob->card->driver, device: 0, playback_count: 1, capture_count: 1, rpcm: &pcm); |
366 | if (err < 0) |
367 | goto end; |
368 | |
369 | pcm->private_data = bebob; |
370 | snprintf(buf: pcm->name, size: sizeof(pcm->name), |
371 | fmt: "%s PCM" , bebob->card->shortname); |
372 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &playback_ops); |
373 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &capture_ops); |
374 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, size: 0, max: 0); |
375 | end: |
376 | return err; |
377 | } |
378 | |