firewire_dice_protocols/maudio.rs
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! Hardware specification and application protocol specific to M-Audio ProFire series.
5//!
6//! The modules includes structure, enumeration, and trait and its implementation for hardware
7//! specification and application protocol specific to M-Audio ProFire series.
8//!
9//! ## Diagram of internal signal flow for Profire 2626
10//!
11//! ```text
12//!
13//! XLR input 1 -------+-------+
14//! Phone input 1 -----+ |
15//! |
16//! XLR input 2 -------+-------+
17//! Phone input 2 -----+ |
18//! +----------------> analog-input-1/2
19//! XLR input 3/4 ------------------------------> analog-input-3/4
20//! XLR input 5/6 ------------------------------> analog-input-5/6
21//! XLR input 7/8 ------------------------------> analog-input-7/8
22//! Coaxial input ------------------------------> spdif-input-1/2
23//! Optical input A ----------------------------> adat-input-1..8
24//! Optical input B -----------or---------------> adat-input-8..16
25//! +---------------> spdif-input-3/4
26//!
27//! ++=============++
28//! analog-input-1/2 ------> || 70 x 68 || --> analog-output-1/2
29//! analog-input-3/4 ------> || router || --> analog-output-3/4
30//! analog-input-5/6 ------> || up to || --> analog-output-5/6
31//! analog-input-7/8 ------> || 128 entries || --> analog-output-7/8
32//! || ||
33//! spdif-input-1/2 -------> || || --> spdif-output-1/2
34//! spdif-input-3/4 -------> || || --> spdif-output-3/4
35//! || ||
36//! adat-input-1/2 --------> || || --> adat-output-1/2
37//! adat-input-3/4 --------> || || --> adat-output-3/4
38//! adat-input-5/6 --------> || || --> adat-output-5/6
39//! adat-input-7/8 --------> || || --> adat-output-7/8
40//! || ||
41//! adat-input-9/10 -------> || || --> adat-output-9/10
42//! adat-input-11/12 ------> || || --> adat-output-11/12
43//! adat-input-13/14 ------> || || --> adat-output-13/14
44//! adat-input-15/16 ------> || || --> adat-output-15/16
45//! || ||
46//! stream-input-A-1/2 ----> || || --> stream-output-A-1/2
47//! stream-input-A-3/4 ----> || || --> stream-output-A-3/4
48//! stream-input-A-5/6 ----> || || --> stream-output-A-5/6
49//! stream-input-A-7/8 ----> || || --> stream-output-A-7/8
50//! stream-input-A-9/10 ---> || || --> stream-output-A-9/10
51//! || ||
52//! stream-input-B-1/2 ----> || || --> stream-output-B-1/2
53//! stream-input-B-3/4 ----> || || --> stream-output-B-3/4
54//! stream-input-B-5/6 ----> || || --> stream-output-B-5/6
55//! stream-input-B-7/8 ----> || || --> stream-output-B-7/8
56//! stream-input-B-9/10 ---> || || --> stream-output-B-9/10
57//! stream-input-B-11/12 --> || || --> stream-output-B-11/12
58//! stream-input-B-13/14 --> || || --> stream-output-B-13/14
59//! stream-input-B-15/16 --> || || --> stream-output-B-15/16
60//! || ||
61//! mixer-output-1/2 ------> || || --> mixer-input-1/2
62//! mixer-output-3/4 ------> || || --> mixer-input-3/4
63//! mixer-output-5/6 ------> || || --> mixer-input-5/6
64//! mixer-output-7/8 ------> || || --> mixer-input-7/8
65//! mixer-output-9/10 -----> || || --> mixer-input-9/10
66//! mixer-output-11/12 ----> || || --> mixer-input-11/12
67//! mixer-output-13/14 ----> || || --> mixer-input-13/14
68//! mixer-output-15/16 ----> || || --> mixer-input-15/16
69//! || || --> mixer-input-17/18
70//! ++=============++
71//!
72//! ++============++
73//! mixer-input-1/2 -----> || || --> mixer-output-1/2
74//! mixer-input-3/4 -----> || || --> mixer-output-3/4
75//! mixer-input-5/6 -----> || || --> mixer-output-5/6
76//! mixer-input-7/8 -----> || 18 x 16 || --> mixer-output-7/8
77//! mixer-input-9/10 ----> || || --> mixer-output-9/10
78//! mixer-input-11/11 ---> || mixer || --> mixer-output-11/12
79//! mixer-input-13/14 ---> || || --> mixer-output-13/14
80//! mixer-input-15/16 ---> || || --> mixer-output-15/16
81//! mixer-input-17/18 ---> || ||
82//! ++============++
83//!
84//! analog-output-1/2 ------------+-------------> Phone output 1/2
85//! +-------------> Headphone output 1/2
86//! analog-output-3/4 ------------+-------------> Phone output 1/2
87//! +-------------> Headphone output 3/4
88//! analog-output-5/6 --------------------------> Phone output 1/2
89//! analog-output-7/8 --------------------------> Phone output 1/2
90//!
91//! spdif-output-1/2 ---------------------------> Coaxial output
92//!
93//! adat-output-1..8 ---------------------------> Optical A output
94//!
95//! adat-output-9..16 ------------or------------> Optical B output
96//! spdif-output-3/4 -------------+
97//!
98//! ```
99//!
100//! ## Diagram of internal signal flow for Profire 610
101//!
102//! At 176.4/192.0 kHz, both stream-input-A and stream-input-B are available for
103//! analog-output-1..6 and spdif-output-1/2 per each.
104//!
105//! ```text
106//!
107//! XLR input 1/2 ------------------------------> analog-input-1/2
108//! Phone input 3/4 ----------------------------> analog-input-3/4
109//! coaxial input 1/2 --------------------------> spdif-input-1/2
110//!
111//! ++=============++
112//! analog-input-1/2 ------> || 32 x 30 || --> analog-output-1/2
113//! analog-input-3/4 ------> || router || --> analog-output-3/4
114//! || up to ||
115//! spdif-input-1/2 -------> || 128 entries || --> spdif-output-1/2
116//! || ||
117//! stream-input-1/2 ------> || || --> stream-output-1/2
118//! stream-input-3/4 ------> || || --> stream-output-3/4
119//! stream-input-5/6 ------> || || --> stream-output-5/6
120//! stream-input-7/8 ------> || ||
121//! stream-input-9/10 -----> || ||
122//! || ||
123//! mixer-output-1/2 ------> || || --> mixer-input-1/2
124//! mixer-output-3/4 ------> || || --> mixer-input-3/4
125//! mixer-output-5/6 ------> || || --> mixer-input-5/6
126//! mixer-output-7/8 ------> || || --> mixer-input-7/8
127//! mixer-output-9/10 -----> || || --> mixer-input-9/10
128//! mixer-output-11/12 ----> || || --> mixer-input-11/12
129//! mixer-output-13/14 ----> || || --> mixer-input-13/14
130//! mixer-output-15/16 ----> || || --> mixer-input-15/16
131//! || || --> mixer-input-17/18
132//! ++=============++
133//!
134//! ++============++
135//! mixer-input-1/2 -----> || || --> mixer-output-1/2
136//! mixer-input-3/4 -----> || || --> mixer-output-3/4
137//! mixer-input-5/6 -----> || || --> mixer-output-5/6
138//! mixer-input-7/8 -----> || 18 x 16 || --> mixer-output-7/8
139//! mixer-input-9/10 ----> || || --> mixer-output-9/10
140//! mixer-input-11/11 ---> || mixer || --> mixer-output-11/12
141//! mixer-input-13/14 ---> || || --> mixer-output-13/14
142//! mixer-input-15/16 ---> || || --> mixer-output-15/16
143//! mixer-input-17/18 ---> || ||
144//! ++============++
145//!
146//! analog-output-1/2 ------------+-------------> Phone output 1/2
147//! +-------------> Headphone output 1/2
148//! analog-output-3/4 ------------+-------------> Phone output 3/4
149//! +-------------> Headphone output 1/2
150//!
151//! spdif-output-1/2 ---------------------------> Coaxial output
152//!
153
154use super::{
155 tcat::{extension::*, global_section::*, tcd22xx_spec::*, *},
156 *,
157};
158
159/// Protocol implementation specific to ProFire 2626.
160#[derive(Default, Debug)]
161pub struct Pfire2626Protocol;
162
163impl TcatOperation for Pfire2626Protocol {}
164
165impl TcatGlobalSectionSpecification for Pfire2626Protocol {
166 // NOTE: ClockSource::Tdif is used for second optical interface as 'ADAT_AUX'.
167 const AVAILABLE_CLOCK_SOURCE_OVERRIDE: Option<&'static [ClockSource]> = Some(&[
168 ClockSource::Aes1,
169 ClockSource::Aes4,
170 ClockSource::Adat,
171 ClockSource::Tdif,
172 ClockSource::WordClock,
173 ClockSource::Internal,
174 ]);
175}
176
177impl TcatExtensionOperation for Pfire2626Protocol {}
178
179impl PfireSpecificSpecification for Pfire2626Protocol {
180 const HAS_OPT_IFACE_B: bool = true;
181 const SUPPORT_STANDALONE_CONVERTER: bool = true;
182}
183
184impl Tcd22xxSpecification for Pfire2626Protocol {
185 const INPUTS: &'static [Input] = &[
186 Input {
187 id: SrcBlkId::Ins1,
188 offset: 0,
189 count: 8,
190 label: None,
191 },
192 Input {
193 id: SrcBlkId::Aes,
194 offset: 0,
195 count: 2,
196 label: None,
197 },
198 Input {
199 id: SrcBlkId::Adat,
200 offset: 0,
201 count: 8,
202 label: None,
203 },
204 // NOTE: share the same optical interface.
205 Input {
206 id: SrcBlkId::Adat,
207 offset: 8,
208 count: 8,
209 label: None,
210 },
211 Input {
212 id: SrcBlkId::Aes,
213 offset: 6,
214 count: 2,
215 label: None,
216 },
217 ];
218 const OUTPUTS: &'static [Output] = &[
219 Output {
220 id: DstBlkId::Ins1,
221 offset: 0,
222 count: 8,
223 label: None,
224 },
225 Output {
226 id: DstBlkId::Aes,
227 offset: 0,
228 count: 2,
229 label: None,
230 },
231 Output {
232 id: DstBlkId::Adat,
233 offset: 0,
234 count: 8,
235 label: None,
236 },
237 // NOTE: share the same optical interface.
238 Output {
239 id: DstBlkId::Adat,
240 offset: 8,
241 count: 8,
242 label: None,
243 },
244 Output {
245 id: DstBlkId::Aes,
246 offset: 6,
247 count: 2,
248 label: None,
249 },
250 ];
251 const FIXED: &'static [SrcBlk] = &[
252 SrcBlk {
253 id: SrcBlkId::Ins1,
254 ch: 0,
255 },
256 SrcBlk {
257 id: SrcBlkId::Ins1,
258 ch: 1,
259 },
260 SrcBlk {
261 id: SrcBlkId::Ins1,
262 ch: 2,
263 },
264 SrcBlk {
265 id: SrcBlkId::Ins1,
266 ch: 3,
267 },
268 SrcBlk {
269 id: SrcBlkId::Ins1,
270 ch: 4,
271 },
272 SrcBlk {
273 id: SrcBlkId::Ins1,
274 ch: 5,
275 },
276 SrcBlk {
277 id: SrcBlkId::Ins1,
278 ch: 6,
279 },
280 SrcBlk {
281 id: SrcBlkId::Ins1,
282 ch: 7,
283 },
284 ];
285}
286
287/// Protocol implementation specific to ProFire 610.
288#[derive(Default, Debug)]
289pub struct Pfire610Protocol;
290
291impl TcatOperation for Pfire610Protocol {}
292
293impl TcatGlobalSectionSpecification for Pfire610Protocol {
294 const AVAILABLE_CLOCK_SOURCE_OVERRIDE: Option<&'static [ClockSource]> =
295 Some(&[ClockSource::Aes1, ClockSource::Internal]);
296}
297
298impl TcatExtensionOperation for Pfire610Protocol {}
299
300impl PfireSpecificSpecification for Pfire610Protocol {
301 const HAS_OPT_IFACE_B: bool = false;
302 const SUPPORT_STANDALONE_CONVERTER: bool = false;
303}
304
305// NOTE: the second rx stream is firstly available at higher sampling rate.
306impl Tcd22xxSpecification for Pfire610Protocol {
307 const INPUTS: &'static [Input] = &[
308 Input {
309 id: SrcBlkId::Ins0,
310 offset: 0,
311 count: 4,
312 label: None,
313 },
314 Input {
315 id: SrcBlkId::Aes,
316 offset: 0,
317 count: 2,
318 label: None,
319 },
320 ];
321 const OUTPUTS: &'static [Output] = &[
322 Output {
323 id: DstBlkId::Ins0,
324 offset: 0,
325 count: 8,
326 label: None,
327 },
328 Output {
329 id: DstBlkId::Aes,
330 offset: 0,
331 count: 2,
332 label: None,
333 },
334 ];
335 const FIXED: &'static [SrcBlk] = &[
336 SrcBlk {
337 id: SrcBlkId::Ins0,
338 ch: 0,
339 },
340 SrcBlk {
341 id: SrcBlkId::Ins0,
342 ch: 1,
343 },
344 ];
345}
346
347/// Mode of optical interface.
348#[derive(Debug, Copy, Clone, PartialEq, Eq)]
349pub enum OptIfaceMode {
350 /// For S/PDIF signal.
351 Spdif,
352 /// For ADAT signal.
353 Adat,
354}
355
356impl Default for OptIfaceMode {
357 fn default() -> Self {
358 Self::Spdif
359 }
360}
361
362/// Mode of standalone converter.
363#[derive(Debug, Copy, Clone, PartialEq, Eq)]
364pub enum StandaloneConverterMode {
365 /// For A/D and D/A conversion.
366 AdDa,
367 /// For A/D conversion only.
368 AdOnly,
369}
370
371impl Default for StandaloneConverterMode {
372 fn default() -> Self {
373 Self::AdDa
374 }
375}
376
377/// Mode of standalone converter.
378#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
379pub struct PfireSpecificParams {
380 /// Whether volumes of 4 analog output pairs have assignment to hardware knob.
381 pub knob_assigns: [bool; 4],
382 /// Mode of optical interface B.
383 pub opt_iface_b_mode: OptIfaceMode,
384 /// Mode of converter at standalone.
385 pub standalone_mode: StandaloneConverterMode,
386}
387
388// const KNOB_ASSIGN_OFFSET: usize = 0x00;
389// const STANDALONE_MODE_OFFSET: usize = 0x04;
390
391const KNOB_ASSIGN_MASK: u32 = 0x0f;
392const OPT_IFACE_B_IS_SPDIF_FLAG: u32 = 0x10;
393const STANDALONE_CONVERTER_IS_AD_ONLY_FLAG: u32 = 0x02;
394
395const MIN_SIZE: usize = 8;
396
397fn serialize(params: &PfireSpecificParams, raw: &mut [u8]) -> Result<(), String> {
398 assert!(raw.len() >= MIN_SIZE);
399
400 let mut val = 0u32;
401 params
402 .knob_assigns
403 .iter()
404 .enumerate()
405 .filter(|(_, &v)| v)
406 .for_each(|(i, _)| val |= 1 << i);
407
408 if params.opt_iface_b_mode == OptIfaceMode::Spdif {
409 val |= OPT_IFACE_B_IS_SPDIF_FLAG;
410 }
411 serialize_u32(&val, &mut raw[..4]);
412
413 let mut val = 0u32;
414 if params.standalone_mode == StandaloneConverterMode::AdOnly {
415 val |= STANDALONE_CONVERTER_IS_AD_ONLY_FLAG;
416 }
417 serialize_u32(&val, &mut raw[4..8]);
418
419 Ok(())
420}
421
422fn deserialize(params: &mut PfireSpecificParams, raw: &[u8]) -> Result<(), String> {
423 assert!(raw.len() >= MIN_SIZE);
424
425 let mut val = 0u32;
426 deserialize_u32(&mut val, &raw[..4]);
427 params
428 .knob_assigns
429 .iter_mut()
430 .enumerate()
431 .for_each(|(i, v)| *v = (val & KNOB_ASSIGN_MASK) & (1 << i) > 0);
432
433 params.opt_iface_b_mode = if val & OPT_IFACE_B_IS_SPDIF_FLAG > 0 {
434 OptIfaceMode::Spdif
435 } else {
436 OptIfaceMode::Adat
437 };
438
439 deserialize_u32(&mut val, &raw[4..8]);
440 params.standalone_mode = if val & STANDALONE_CONVERTER_IS_AD_ONLY_FLAG > 0 {
441 StandaloneConverterMode::AdOnly
442 } else {
443 StandaloneConverterMode::AdDa
444 };
445
446 Ok(())
447}
448
449/// Protocol implementation specific to ProFire series.
450pub trait PfireSpecificSpecification {
451 const HAS_OPT_IFACE_B: bool;
452 const SUPPORT_STANDALONE_CONVERTER: bool;
453
454 const KNOB_COUNT: usize = 4;
455}
456
457impl<O: TcatExtensionOperation + PfireSpecificSpecification>
458 TcatExtensionSectionParamsOperation<PfireSpecificParams> for O
459{
460 fn cache_extension_whole_params(
461 req: &FwReq,
462 node: &FwNode,
463 sections: &ExtensionSections,
464 _: &ExtensionCaps,
465 params: &mut PfireSpecificParams,
466 timeout_ms: u32,
467 ) -> Result<(), Error> {
468 let mut raw = vec![0u8; MIN_SIZE];
469 Self::read_extension(req, node, §ions.application, 0, &mut raw, timeout_ms)?;
470 deserialize(params, &raw).map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
471 }
472}
473
474impl<O: TcatExtensionOperation + PfireSpecificSpecification>
475 TcatExtensionSectionPartialMutableParamsOperation<PfireSpecificParams> for O
476{
477 fn update_extension_partial_params(
478 req: &FwReq,
479 node: &FwNode,
480 sections: &ExtensionSections,
481 _: &ExtensionCaps,
482 params: &PfireSpecificParams,
483 prev: &mut PfireSpecificParams,
484 timeout_ms: u32,
485 ) -> Result<(), Error> {
486 let mut new = vec![0u8; MIN_SIZE];
487 serialize(params, &mut new)
488 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
489
490 let mut old = vec![0u8; MIN_SIZE];
491 serialize(prev, &mut old)
492 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
493
494 (0..MIN_SIZE).step_by(4).try_for_each(|pos| {
495 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
496 Self::write_extension(
497 req,
498 node,
499 §ions.application,
500 pos,
501 &mut new[pos..(pos + 4)],
502 timeout_ms,
503 )
504 } else {
505 Ok(())
506 }
507 })?;
508
509 deserialize(prev, &new).map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
510 }
511}
512
513#[cfg(test)]
514mod test {
515 use super::*;
516
517 #[test]
518 fn pfire_specific_params_serdes() {
519 let params = PfireSpecificParams {
520 knob_assigns: [false, true, true, false],
521 opt_iface_b_mode: OptIfaceMode::Spdif,
522 standalone_mode: StandaloneConverterMode::AdDa,
523 };
524
525 let mut raw = [0; MIN_SIZE];
526 serialize(¶ms, &mut raw).unwrap();
527
528 let mut p = PfireSpecificParams::default();
529 deserialize(&mut p, &raw).unwrap();
530
531 assert_eq!(params, p);
532 }
533}