/*
* bebob_stream.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"
#define CALLBACK_TIMEOUT 2000
#define FW_ISO_RESOURCE_DELAY 1000
/*
* NOTE;
* For BeBoB streams, Both of input and output CMP connection are important.
*
* For most devices, each CMP connection starts to transmit/receive a
* corresponding stream. But for a few devices, both of CMP connection needs
* to start transmitting stream. An example is 'M-Audio Firewire 410'.
*/
/* 128 is an arbitrary length but it seems to be enough */
#define FORMAT_MAXIMUM_LENGTH 128
const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
[0] = 32000,
[1] = 44100,
[2] = 48000,
[3] = 88200,
[4] = 96000,
[5] = 176400,
[6] = 192000,
};
/*
* See: Table 51: Extended Stream Format Info ‘Sampling Frequency’
* in Additional AVC commands (Nov 2003, BridgeCo)
*/
static const unsigned int bridgeco_freq_table[] = {
[0] = 0x02,
[1] = 0x03,
[2] = 0x04,
[3] = 0x0a,
[4] = 0x05,
[5] = 0x06,
[6] = 0x07,
};
static int
get_formation_index(unsigned int rate, unsigned int *index)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
if (snd_bebob_rate_table[i] == rate) {
*index = i;
return 0;
}
}
return -EINVAL;
}
int
snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
{
unsigned int tx_rate, rx_rate, trials;
int err;
trials = 0;
do {
err = avc_general_get_sig_fmt(bebob->unit, &tx_rate,
AVC_GENERAL_PLUG_DIR_OUT, 0);
} while (err == -EAGAIN && ++trials < 3);
if (err < 0)
goto end;
trials = 0;
do {
err = avc_general_get_sig_fmt(bebob->unit, &rx_rate,
AVC_GENERAL_PLUG_DIR_IN, 0);
} while (err == -EAGAIN && ++trials < 3);
if (err < 0)
goto end;
*curr_rate = rx_rate;
if (rx_rate == tx_rate)
goto end;
/* synchronize receive stream rate to transmit stream rate */
err = avc_general_set_sig_fmt(bebob->unit, rx_rate,
AVC_GENERAL_PLUG_DIR_IN, 0);
end:
return err;
}
int
snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
{
int err;
err = avc_general_set_sig_fmt(bebob->unit, rate,
AVC_GENERAL_PLUG_DIR_OUT, 0);
if (err < 0)
goto end;
err = avc_general_set_sig_fmt(bebob->unit, rate,
AVC_GENERAL_PLUG_DIR_IN, 0);
if (err < 0)
goto end;
/*
* Some devices need a bit time for transition.
* 300msec is got by some experiments.
*/
msleep(300);
end:
return err;
}
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src)
{
const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
enum avc_bridgeco_plug_type type;
int err = 0;
/* 1.The device has its own operation to switch source of clock */
if (clk_spec) {
err = clk_spec->get(bebob, &id);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get clock source: %d\n", err);
goto end;
}
if (id >= clk_spec->num) {
dev_err(&bebob->unit->device,
"clock source %d out of range 0..%d\n",
id, clk_spec->num - 1);
err = -EIO;
goto end;
}
*src = clk_spec->types[id];
goto end;
}
/*
* 2.The device don't support to switch source of clock then assumed
* to use internal clock always
*/
if (bebob->sync_input_plug < 0) {
*src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
/*
* 3.The device supports to switch source of clock by an usual way.
* Let's check input for 'Music Sub Unit Sync Input' plug.
*/
avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
bebob->sync_input_plug);
err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get an input for MSU in plug %d: %d\n",
b