mirror of https://gitee.com/openkylin/linux.git
181 lines
4.7 KiB
C
181 lines
4.7 KiB
C
|
/*
|
||
|
* 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
|
||
|
};
|