|
@@ -0,0 +1,180 @@
|
|
|
+/*
|
|
|
+ * bebob_maudio.c - a part of driver for BeBoB based devices
|
|
|
+ *
|
|
|
+ * Copyright (c) 2013-2014 Takashi Sakamoto
|
|
|
+ *
|
|
|
+ * Licensed under the terms of the GNU General Public License, version 2.
|
|
|
+ */
|
|
|
+
|
|
|
+#include "./bebob.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * Just powering on, Firewire 410/Audiophile wait to
|
|
|
+ * download firmware blob. To enable these devices, drivers should upload
|
|
|
+ * firmware blob and send a command to initialize configuration to factory
|
|
|
+ * settings when completing uploading. Then these devices generate bus reset
|
|
|
+ * and are recognized as new devices with the firmware.
|
|
|
+ *
|
|
|
+ * For streaming, both of output and input streams are needed for Firewire 410
|
|
|
+ * and Ozonic. The single stream is OK for the other devices even if the clock
|
|
|
+ * source is not SYT-Match (I note no devices use SYT-Match).
|
|
|
+ *
|
|
|
+ * Without streaming, the devices except for Firewire Audiophile can mix any
|
|
|
+ * input and output. For this reason, Audiophile cannot be used as standalone
|
|
|
+ * mixer.
|
|
|
+ */
|
|
|
+
|
|
|
+#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000
|
|
|
+
|
|
|
+#define METER_OFFSET 0x00600000
|
|
|
+
|
|
|
+/* some device has sync info after metering data */
|
|
|
+#define METER_SIZE_FW410 76 /* with sync info */
|
|
|
+#define METER_SIZE_AUDIOPHILE 60 /* with sync info */
|
|
|
+#define METER_SIZE_SOLO 52 /* with sync info */
|
|
|
+#define METER_SIZE_OZONIC 48
|
|
|
+#define METER_SIZE_NRV10 80
|
|
|
+
|
|
|
+/* labels for metering */
|
|
|
+#define ANA_IN "Analog In"
|
|
|
+#define ANA_OUT "Analog Out"
|
|
|
+#define DIG_IN "Digital In"
|
|
|
+#define SPDIF_IN "S/PDIF In"
|
|
|
+#define ADAT_IN "ADAT In"
|
|
|
+#define DIG_OUT "Digital Out"
|
|
|
+#define SPDIF_OUT "S/PDIF Out"
|
|
|
+#define ADAT_OUT "ADAT Out"
|
|
|
+#define STRM_IN "Stream In"
|
|
|
+#define AUX_OUT "Aux Out"
|
|
|
+#define HP_OUT "HP Out"
|
|
|
+/* for NRV */
|
|
|
+#define UNKNOWN_METER "Unknown"
|
|
|
+
|
|
|
+static inline int
|
|
|
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
|
|
|
+{
|
|
|
+ return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
|
|
|
+ MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
|
|
|
+ buf, size, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/* last 4 bytes are omitted because it's clock info. */
|
|
|
+static char *const fw410_meter_labels[] = {
|
|
|
+ ANA_IN, DIG_IN,
|
|
|
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
|
|
|
+ HP_OUT
|
|
|
+};
|
|
|
+static char *const audiophile_meter_labels[] = {
|
|
|
+ ANA_IN, DIG_IN,
|
|
|
+ ANA_OUT, ANA_OUT, DIG_OUT,
|
|
|
+ HP_OUT, AUX_OUT,
|
|
|
+};
|
|
|
+static char *const solo_meter_labels[] = {
|
|
|
+ ANA_IN, DIG_IN,
|
|
|
+ STRM_IN, STRM_IN,
|
|
|
+ ANA_OUT, DIG_OUT
|
|
|
+};
|
|
|
+
|
|
|
+/* no clock info */
|
|
|
+static char *const ozonic_meter_labels[] = {
|
|
|
+ ANA_IN, ANA_IN,
|
|
|
+ STRM_IN, STRM_IN,
|
|
|
+ ANA_OUT, ANA_OUT
|
|
|
+};
|
|
|
+/* TODO: need testers. these positions are based on authour's assumption */
|
|
|
+static char *const nrv10_meter_labels[] = {
|
|
|
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
|
|
|
+ DIG_IN,
|
|
|
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
|
|
|
+ DIG_IN
|
|
|
+};
|
|
|
+static int
|
|
|
+normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
|
|
|
+{
|
|
|
+ struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
|
|
+ unsigned int c, channels;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ channels = spec->num * 2;
|
|
|
+ if (size < channels * sizeof(u32))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ err = get_meter(bebob, (void *)buf, size);
|
|
|
+ if (err < 0)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ for (c = 0; c < channels; c++)
|
|
|
+ be32_to_cpus(&buf[c]);
|
|
|
+
|
|
|
+ /* swap stream channels because inverted */
|
|
|
+ if (spec->labels == solo_meter_labels) {
|
|
|
+ swap(buf[4], buf[6]);
|
|
|
+ swap(buf[5], buf[7]);
|
|
|
+ }
|
|
|
+end:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/* Firewire 410 specification */
|
|
|
+static struct snd_bebob_rate_spec usual_rate_spec = {
|
|
|
+ .get = &snd_bebob_stream_get_rate,
|
|
|
+ .set = &snd_bebob_stream_set_rate,
|
|
|
+};
|
|
|
+static struct snd_bebob_meter_spec fw410_meter_spec = {
|
|
|
+ .num = ARRAY_SIZE(fw410_meter_labels),
|
|
|
+ .labels = fw410_meter_labels,
|
|
|
+ .get = &normal_meter_get
|
|
|
+};
|
|
|
+struct snd_bebob_spec maudio_fw410_spec = {
|
|
|
+ .clock = NULL,
|
|
|
+ .rate = &usual_rate_spec,
|
|
|
+ .meter = &fw410_meter_spec
|
|
|
+};
|
|
|
+
|
|
|
+/* Firewire Audiophile specification */
|
|
|
+static struct snd_bebob_meter_spec audiophile_meter_spec = {
|
|
|
+ .num = ARRAY_SIZE(audiophile_meter_labels),
|
|
|
+ .labels = audiophile_meter_labels,
|
|
|
+ .get = &normal_meter_get
|
|
|
+};
|
|
|
+struct snd_bebob_spec maudio_audiophile_spec = {
|
|
|
+ .clock = NULL,
|
|
|
+ .rate = &usual_rate_spec,
|
|
|
+ .meter = &audiophile_meter_spec
|
|
|
+};
|
|
|
+
|
|
|
+/* Firewire Solo specification */
|
|
|
+static struct snd_bebob_meter_spec solo_meter_spec = {
|
|
|
+ .num = ARRAY_SIZE(solo_meter_labels),
|
|
|
+ .labels = solo_meter_labels,
|
|
|
+ .get = &normal_meter_get
|
|
|
+};
|
|
|
+struct snd_bebob_spec maudio_solo_spec = {
|
|
|
+ .clock = NULL,
|
|
|
+ .rate = &usual_rate_spec,
|
|
|
+ .meter = &solo_meter_spec
|
|
|
+};
|
|
|
+
|
|
|
+/* Ozonic specification */
|
|
|
+static struct snd_bebob_meter_spec ozonic_meter_spec = {
|
|
|
+ .num = ARRAY_SIZE(ozonic_meter_labels),
|
|
|
+ .labels = ozonic_meter_labels,
|
|
|
+ .get = &normal_meter_get
|
|
|
+};
|
|
|
+struct snd_bebob_spec maudio_ozonic_spec = {
|
|
|
+ .clock = NULL,
|
|
|
+ .rate = &usual_rate_spec,
|
|
|
+ .meter = &ozonic_meter_spec
|
|
|
+};
|
|
|
+
|
|
|
+/* NRV10 specification */
|
|
|
+static struct snd_bebob_meter_spec nrv10_meter_spec = {
|
|
|
+ .num = ARRAY_SIZE(nrv10_meter_labels),
|
|
|
+ .labels = nrv10_meter_labels,
|
|
|
+ .get = &normal_meter_get
|
|
|
+};
|
|
|
+struct snd_bebob_spec maudio_nrv10_spec = {
|
|
|
+ .clock = NULL,
|
|
|
+ .rate = &usual_rate_spec,
|
|
|
+ .meter = &nrv10_meter_spec
|
|
|
+};
|