1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * fireworks_pcm.c - a part of driver for Fireworks based devices |
4 | * |
5 | * Copyright (c) 2009-2010 Clemens Ladisch |
6 | * Copyright (c) 2013-2014 Takashi Sakamoto |
7 | */ |
8 | #include "./fireworks.h" |
9 | |
10 | /* |
11 | * NOTE: |
12 | * Fireworks changes its AMDTP channels for PCM data according to its sampling |
13 | * rate. There are three modes. Here _XX is either _rx or _tx. |
14 | * 0: 32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied |
15 | * 1: 88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied |
16 | * 2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied |
17 | * |
18 | * The number of PCM channels for analog input and output are always fixed but |
19 | * the number of PCM channels for digital input and output are differed. |
20 | * |
21 | * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some |
22 | * model, the number of PCM channels for digital input has more restriction |
23 | * depending on which digital interface is selected. |
24 | * - S/PDIF coaxial and optical : use input 1-2 |
25 | * - ADAT optical at 32.0-48.0 kHz : use input 1-8 |
26 | * - ADAT optical at 88.2-96.0 kHz : use input 1-4 (S/MUX format) |
27 | * |
28 | * The data in AMDTP channels for blank PCM channels are zero. |
29 | */ |
30 | static const unsigned int freq_table[] = { |
31 | /* multiplier mode 0 */ |
32 | [0] = 32000, |
33 | [1] = 44100, |
34 | [2] = 48000, |
35 | /* multiplier mode 1 */ |
36 | [3] = 88200, |
37 | [4] = 96000, |
38 | /* multiplier mode 2 */ |
39 | [5] = 176400, |
40 | [6] = 192000, |
41 | }; |
42 | |
43 | static inline unsigned int |
44 | get_multiplier_mode_with_index(unsigned int index) |
45 | { |
46 | return ((int)index - 1) / 2; |
47 | } |
48 | |
49 | int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode) |
50 | { |
51 | unsigned int i; |
52 | |
53 | for (i = 0; i < ARRAY_SIZE(freq_table); i++) { |
54 | if (freq_table[i] == sampling_rate) { |
55 | *mode = get_multiplier_mode_with_index(index: i); |
56 | return 0; |
57 | } |
58 | } |
59 | |
60 | return -EINVAL; |
61 | } |
62 | |
63 | static int |
64 | hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) |
65 | { |
66 | unsigned int *pcm_channels = rule->private; |
67 | struct snd_interval *r = |
68 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); |
69 | const struct snd_interval *c = |
70 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
71 | struct snd_interval t = { |
72 | .min = UINT_MAX, .max = 0, .integer = 1 |
73 | }; |
74 | unsigned int i, mode; |
75 | |
76 | for (i = 0; i < ARRAY_SIZE(freq_table); i++) { |
77 | mode = get_multiplier_mode_with_index(index: i); |
78 | if (!snd_interval_test(i: c, val: pcm_channels[mode])) |
79 | continue; |
80 | |
81 | t.min = min(t.min, freq_table[i]); |
82 | t.max = max(t.max, freq_table[i]); |
83 | } |
84 | |
85 | return snd_interval_refine(i: r, v: &t); |
86 | } |
87 | |
88 | static int |
89 | hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) |
90 | { |
91 | unsigned int *pcm_channels = rule->private; |
92 | struct snd_interval *c = |
93 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); |
94 | const struct snd_interval *r = |
95 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); |
96 | struct snd_interval t = { |
97 | .min = UINT_MAX, .max = 0, .integer = 1 |
98 | }; |
99 | unsigned int i, mode; |
100 | |
101 | for (i = 0; i < ARRAY_SIZE(freq_table); i++) { |
102 | mode = get_multiplier_mode_with_index(index: i); |
103 | if (!snd_interval_test(i: r, val: freq_table[i])) |
104 | continue; |
105 | |
106 | t.min = min(t.min, pcm_channels[mode]); |
107 | t.max = max(t.max, pcm_channels[mode]); |
108 | } |
109 | |
110 | return snd_interval_refine(i: c, v: &t); |
111 | } |
112 | |
113 | static void |
114 | limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels) |
115 | { |
116 | unsigned int i, mode; |
117 | |
118 | hw->channels_min = UINT_MAX; |
119 | hw->channels_max = 0; |
120 | |
121 | for (i = 0; i < ARRAY_SIZE(freq_table); i++) { |
122 | mode = get_multiplier_mode_with_index(index: i); |
123 | if (pcm_channels[mode] == 0) |
124 | continue; |
125 | |
126 | hw->channels_min = min(hw->channels_min, pcm_channels[mode]); |
127 | hw->channels_max = max(hw->channels_max, pcm_channels[mode]); |
128 | } |
129 | } |
130 | |
131 | static int |
132 | pcm_init_hw_params(struct snd_efw *efw, |
133 | struct snd_pcm_substream *substream) |
134 | { |
135 | struct snd_pcm_runtime *runtime = substream->runtime; |
136 | struct amdtp_stream *s; |
137 | unsigned int *pcm_channels; |
138 | int err; |
139 | |
140 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
141 | runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; |
142 | s = &efw->tx_stream; |
143 | pcm_channels = efw->pcm_capture_channels; |
144 | } else { |
145 | runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; |
146 | s = &efw->rx_stream; |
147 | pcm_channels = efw->pcm_playback_channels; |
148 | } |
149 | |
150 | /* limit rates */ |
151 | runtime->hw.rates = efw->supported_sampling_rate; |
152 | snd_pcm_limit_hw_rates(runtime); |
153 | |
154 | limit_channels(hw: &runtime->hw, pcm_channels); |
155 | |
156 | err = snd_pcm_hw_rule_add(runtime, cond: 0, SNDRV_PCM_HW_PARAM_CHANNELS, |
157 | func: hw_rule_channels, private: pcm_channels, |
158 | SNDRV_PCM_HW_PARAM_RATE, -1); |
159 | if (err < 0) |
160 | goto end; |
161 | |
162 | err = snd_pcm_hw_rule_add(runtime, cond: 0, SNDRV_PCM_HW_PARAM_RATE, |
163 | func: hw_rule_rate, private: pcm_channels, |
164 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); |
165 | if (err < 0) |
166 | goto end; |
167 | |
168 | err = amdtp_am824_add_pcm_hw_constraints(s, runtime); |
169 | end: |
170 | return err; |
171 | } |
172 | |
173 | static int pcm_open(struct snd_pcm_substream *substream) |
174 | { |
175 | struct snd_efw *efw = substream->private_data; |
176 | struct amdtp_domain *d = &efw->domain; |
177 | enum snd_efw_clock_source clock_source; |
178 | int err; |
179 | |
180 | err = snd_efw_stream_lock_try(efw); |
181 | if (err < 0) |
182 | return err; |
183 | |
184 | err = pcm_init_hw_params(efw, substream); |
185 | if (err < 0) |
186 | goto err_locked; |
187 | |
188 | err = snd_efw_command_get_clock_source(efw, source: &clock_source); |
189 | if (err < 0) |
190 | goto err_locked; |
191 | |
192 | mutex_lock(&efw->mutex); |
193 | |
194 | // When source of clock is not internal or any stream is reserved for |
195 | // transmission of PCM frames, the available sampling rate is limited |
196 | // at current one. |
197 | if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) || |
198 | (efw->substreams_counter > 0 && d->events_per_period > 0)) { |
199 | unsigned int frames_per_period = d->events_per_period; |
200 | unsigned int frames_per_buffer = d->events_per_buffer; |
201 | unsigned int sampling_rate; |
202 | |
203 | err = snd_efw_command_get_sampling_rate(efw, rate: &sampling_rate); |
204 | if (err < 0) { |
205 | mutex_unlock(lock: &efw->mutex); |
206 | goto err_locked; |
207 | } |
208 | substream->runtime->hw.rate_min = sampling_rate; |
209 | substream->runtime->hw.rate_max = sampling_rate; |
210 | |
211 | if (frames_per_period > 0) { |
212 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
213 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, |
214 | min: frames_per_period, max: frames_per_period); |
215 | if (err < 0) { |
216 | mutex_unlock(lock: &efw->mutex); |
217 | goto err_locked; |
218 | } |
219 | |
220 | err = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
221 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, |
222 | min: frames_per_buffer, max: frames_per_buffer); |
223 | if (err < 0) { |
224 | mutex_unlock(lock: &efw->mutex); |
225 | goto err_locked; |
226 | } |
227 | } |
228 | } |
229 | |
230 | mutex_unlock(lock: &efw->mutex); |
231 | |
232 | snd_pcm_set_sync(substream); |
233 | |
234 | return 0; |
235 | err_locked: |
236 | snd_efw_stream_lock_release(efw); |
237 | return err; |
238 | } |
239 | |
240 | static int pcm_close(struct snd_pcm_substream *substream) |
241 | { |
242 | struct snd_efw *efw = substream->private_data; |
243 | snd_efw_stream_lock_release(efw); |
244 | return 0; |
245 | } |
246 | |
247 | static int pcm_hw_params(struct snd_pcm_substream *substream, |
248 | struct snd_pcm_hw_params *hw_params) |
249 | { |
250 | struct snd_efw *efw = substream->private_data; |
251 | int err = 0; |
252 | |
253 | if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
254 | unsigned int rate = params_rate(p: hw_params); |
255 | unsigned int frames_per_period = params_period_size(p: hw_params); |
256 | unsigned int frames_per_buffer = params_buffer_size(p: hw_params); |
257 | |
258 | mutex_lock(&efw->mutex); |
259 | err = snd_efw_stream_reserve_duplex(efw, rate, |
260 | frames_per_period, frames_per_buffer); |
261 | if (err >= 0) |
262 | ++efw->substreams_counter; |
263 | mutex_unlock(lock: &efw->mutex); |
264 | } |
265 | |
266 | return err; |
267 | } |
268 | |
269 | static int pcm_hw_free(struct snd_pcm_substream *substream) |
270 | { |
271 | struct snd_efw *efw = substream->private_data; |
272 | |
273 | mutex_lock(&efw->mutex); |
274 | |
275 | if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) |
276 | --efw->substreams_counter; |
277 | |
278 | snd_efw_stream_stop_duplex(efw); |
279 | |
280 | mutex_unlock(lock: &efw->mutex); |
281 | |
282 | return 0; |
283 | } |
284 | |
285 | static int pcm_capture_prepare(struct snd_pcm_substream *substream) |
286 | { |
287 | struct snd_efw *efw = substream->private_data; |
288 | int err; |
289 | |
290 | err = snd_efw_stream_start_duplex(efw); |
291 | if (err >= 0) |
292 | amdtp_stream_pcm_prepare(s: &efw->tx_stream); |
293 | |
294 | return err; |
295 | } |
296 | static int pcm_playback_prepare(struct snd_pcm_substream *substream) |
297 | { |
298 | struct snd_efw *efw = substream->private_data; |
299 | int err; |
300 | |
301 | err = snd_efw_stream_start_duplex(efw); |
302 | if (err >= 0) |
303 | amdtp_stream_pcm_prepare(s: &efw->rx_stream); |
304 | |
305 | return err; |
306 | } |
307 | |
308 | static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) |
309 | { |
310 | struct snd_efw *efw = substream->private_data; |
311 | |
312 | switch (cmd) { |
313 | case SNDRV_PCM_TRIGGER_START: |
314 | amdtp_stream_pcm_trigger(s: &efw->tx_stream, pcm: substream); |
315 | break; |
316 | case SNDRV_PCM_TRIGGER_STOP: |
317 | amdtp_stream_pcm_trigger(s: &efw->tx_stream, NULL); |
318 | break; |
319 | default: |
320 | return -EINVAL; |
321 | } |
322 | |
323 | return 0; |
324 | } |
325 | static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) |
326 | { |
327 | struct snd_efw *efw = substream->private_data; |
328 | |
329 | switch (cmd) { |
330 | case SNDRV_PCM_TRIGGER_START: |
331 | amdtp_stream_pcm_trigger(s: &efw->rx_stream, pcm: substream); |
332 | break; |
333 | case SNDRV_PCM_TRIGGER_STOP: |
334 | amdtp_stream_pcm_trigger(s: &efw->rx_stream, NULL); |
335 | break; |
336 | default: |
337 | return -EINVAL; |
338 | } |
339 | |
340 | return 0; |
341 | } |
342 | |
343 | static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) |
344 | { |
345 | struct snd_efw *efw = sbstrm->private_data; |
346 | |
347 | return amdtp_domain_stream_pcm_pointer(d: &efw->domain, s: &efw->tx_stream); |
348 | } |
349 | static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) |
350 | { |
351 | struct snd_efw *efw = sbstrm->private_data; |
352 | |
353 | return amdtp_domain_stream_pcm_pointer(d: &efw->domain, s: &efw->rx_stream); |
354 | } |
355 | |
356 | static int pcm_capture_ack(struct snd_pcm_substream *substream) |
357 | { |
358 | struct snd_efw *efw = substream->private_data; |
359 | |
360 | return amdtp_domain_stream_pcm_ack(d: &efw->domain, s: &efw->tx_stream); |
361 | } |
362 | |
363 | static int pcm_playback_ack(struct snd_pcm_substream *substream) |
364 | { |
365 | struct snd_efw *efw = substream->private_data; |
366 | |
367 | return amdtp_domain_stream_pcm_ack(d: &efw->domain, s: &efw->rx_stream); |
368 | } |
369 | |
370 | int snd_efw_create_pcm_devices(struct snd_efw *efw) |
371 | { |
372 | static const struct snd_pcm_ops capture_ops = { |
373 | .open = pcm_open, |
374 | .close = pcm_close, |
375 | .hw_params = pcm_hw_params, |
376 | .hw_free = pcm_hw_free, |
377 | .prepare = pcm_capture_prepare, |
378 | .trigger = pcm_capture_trigger, |
379 | .pointer = pcm_capture_pointer, |
380 | .ack = pcm_capture_ack, |
381 | }; |
382 | static const struct snd_pcm_ops playback_ops = { |
383 | .open = pcm_open, |
384 | .close = pcm_close, |
385 | .hw_params = pcm_hw_params, |
386 | .hw_free = pcm_hw_free, |
387 | .prepare = pcm_playback_prepare, |
388 | .trigger = pcm_playback_trigger, |
389 | .pointer = pcm_playback_pointer, |
390 | .ack = pcm_playback_ack, |
391 | }; |
392 | struct snd_pcm *pcm; |
393 | int err; |
394 | |
395 | err = snd_pcm_new(card: efw->card, id: efw->card->driver, device: 0, playback_count: 1, capture_count: 1, rpcm: &pcm); |
396 | if (err < 0) |
397 | goto end; |
398 | |
399 | pcm->private_data = efw; |
400 | snprintf(buf: pcm->name, size: sizeof(pcm->name), fmt: "%s PCM" , efw->card->shortname); |
401 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &playback_ops); |
402 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &capture_ops); |
403 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, size: 0, max: 0); |
404 | end: |
405 | return err; |
406 | } |
407 | |
408 | |