ALSA: bebob: configure sampling transfer frequency in pcm.hw_params callback
This commit is a part of preparation to perform allocation/release of isochronous resources in pcm.hw_params/hw_free callbacks. At present, several operations are done in pcm.prepare callback. To reduce load of the callback, This commit splits out an operation to set sampling transfer frequency in pcm.hw_params callback. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
c7e68a697c
commit
ac2888b958
sound/firewire/bebob
@ -218,7 +218,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
|
|||||||
enum snd_bebob_clock_type *src);
|
enum snd_bebob_clock_type *src);
|
||||||
int snd_bebob_stream_discover(struct snd_bebob *bebob);
|
int snd_bebob_stream_discover(struct snd_bebob *bebob);
|
||||||
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
|
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
|
||||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
|
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate);
|
||||||
|
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
|
||||||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
|
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
|
||||||
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
|
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
|
||||||
|
|
||||||
|
@ -15,15 +15,18 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||||||
|
|
||||||
err = snd_bebob_stream_lock_try(bebob);
|
err = snd_bebob_stream_lock_try(bebob);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto end;
|
return err;
|
||||||
|
|
||||||
mutex_lock(&bebob->mutex);
|
mutex_lock(&bebob->mutex);
|
||||||
bebob->substreams_counter++;
|
err = snd_bebob_stream_reserve_duplex(bebob, 0);
|
||||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
if (err >= 0) {
|
||||||
|
++bebob->substreams_counter;
|
||||||
|
err = snd_bebob_stream_start_duplex(bebob);
|
||||||
|
}
|
||||||
mutex_unlock(&bebob->mutex);
|
mutex_unlock(&bebob->mutex);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
snd_bebob_stream_lock_release(bebob);
|
snd_bebob_stream_lock_release(bebob);
|
||||||
end:
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,15 +37,18 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
|||||||
|
|
||||||
err = snd_bebob_stream_lock_try(bebob);
|
err = snd_bebob_stream_lock_try(bebob);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto end;
|
return err;
|
||||||
|
|
||||||
mutex_lock(&bebob->mutex);
|
mutex_lock(&bebob->mutex);
|
||||||
bebob->substreams_counter++;
|
err = snd_bebob_stream_reserve_duplex(bebob, 0);
|
||||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
if (err >= 0) {
|
||||||
|
++bebob->substreams_counter;
|
||||||
|
err = snd_bebob_stream_start_duplex(bebob);
|
||||||
|
}
|
||||||
mutex_unlock(&bebob->mutex);
|
mutex_unlock(&bebob->mutex);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
snd_bebob_stream_lock_release(bebob);
|
snd_bebob_stream_lock_release(bebob);
|
||||||
end:
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,12 +198,16 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||||
|
unsigned int rate = params_rate(hw_params);
|
||||||
|
|
||||||
mutex_lock(&bebob->mutex);
|
mutex_lock(&bebob->mutex);
|
||||||
bebob->substreams_counter++;
|
err = snd_bebob_stream_reserve_duplex(bebob, rate);
|
||||||
|
if (err >= 0)
|
||||||
|
++bebob->substreams_counter;
|
||||||
mutex_unlock(&bebob->mutex);
|
mutex_unlock(&bebob->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
static int
|
static int
|
||||||
pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||||
@ -218,12 +222,16 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||||
|
unsigned int rate = params_rate(hw_params);
|
||||||
|
|
||||||
mutex_lock(&bebob->mutex);
|
mutex_lock(&bebob->mutex);
|
||||||
bebob->substreams_counter++;
|
err = snd_bebob_stream_reserve_duplex(bebob, rate);
|
||||||
|
if (err >= 0)
|
||||||
|
++bebob->substreams_counter;
|
||||||
mutex_unlock(&bebob->mutex);
|
mutex_unlock(&bebob->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -261,10 +269,9 @@ static int
|
|||||||
pcm_capture_prepare(struct snd_pcm_substream *substream)
|
pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_bebob *bebob = substream->private_data;
|
struct snd_bebob *bebob = substream->private_data;
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
|
err = snd_bebob_stream_start_duplex(bebob);
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
amdtp_stream_pcm_prepare(&bebob->tx_stream);
|
amdtp_stream_pcm_prepare(&bebob->tx_stream);
|
||||||
|
|
||||||
@ -274,10 +281,9 @@ static int
|
|||||||
pcm_playback_prepare(struct snd_pcm_substream *substream)
|
pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_bebob *bebob = substream->private_data;
|
struct snd_bebob *bebob = substream->private_data;
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
|
err = snd_bebob_stream_start_duplex(bebob);
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
amdtp_stream_pcm_prepare(&bebob->rx_stream);
|
amdtp_stream_pcm_prepare(&bebob->rx_stream);
|
||||||
|
|
||||||
|
@ -418,49 +418,28 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int make_both_connections(struct snd_bebob *bebob)
|
||||||
make_both_connections(struct snd_bebob *bebob, unsigned int rate)
|
|
||||||
{
|
{
|
||||||
int index, pcm_channels, midi_channels, err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (bebob->connected)
|
if (bebob->connected)
|
||||||
goto end;
|
return 0;
|
||||||
|
|
||||||
/* confirm params for both streams */
|
|
||||||
err = get_formation_index(rate, &index);
|
|
||||||
if (err < 0)
|
|
||||||
goto end;
|
|
||||||
pcm_channels = bebob->tx_stream_formations[index].pcm;
|
|
||||||
midi_channels = bebob->tx_stream_formations[index].midi;
|
|
||||||
err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
|
|
||||||
pcm_channels, midi_channels * 8,
|
|
||||||
false);
|
|
||||||
if (err < 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
pcm_channels = bebob->rx_stream_formations[index].pcm;
|
|
||||||
midi_channels = bebob->rx_stream_formations[index].midi;
|
|
||||||
err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
|
|
||||||
pcm_channels, midi_channels * 8,
|
|
||||||
false);
|
|
||||||
if (err < 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* establish connections for both streams */
|
|
||||||
err = cmp_connection_establish(&bebob->out_conn,
|
err = cmp_connection_establish(&bebob->out_conn,
|
||||||
amdtp_stream_get_max_payload(&bebob->tx_stream));
|
amdtp_stream_get_max_payload(&bebob->tx_stream));
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto end;
|
return err;
|
||||||
|
|
||||||
err = cmp_connection_establish(&bebob->in_conn,
|
err = cmp_connection_establish(&bebob->in_conn,
|
||||||
amdtp_stream_get_max_payload(&bebob->rx_stream));
|
amdtp_stream_get_max_payload(&bebob->rx_stream));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
cmp_connection_break(&bebob->out_conn);
|
cmp_connection_break(&bebob->out_conn);
|
||||||
goto end;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bebob->connected = true;
|
bebob->connected = true;
|
||||||
end:
|
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -484,8 +463,7 @@ destroy_both_connections(struct snd_bebob *bebob)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||||
unsigned int rate)
|
|
||||||
{
|
{
|
||||||
struct cmp_connection *conn;
|
struct cmp_connection *conn;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@ -555,132 +533,154 @@ end:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||||
|
unsigned int rate, unsigned int index)
|
||||||
|
{
|
||||||
|
struct snd_bebob_stream_formation *formation;
|
||||||
|
|
||||||
|
if (stream == &bebob->tx_stream)
|
||||||
|
formation = bebob->tx_stream_formations + index;
|
||||||
|
else
|
||||||
|
formation = bebob->rx_stream_formations + index;
|
||||||
|
|
||||||
|
return amdtp_am824_set_parameters(stream, rate, formation->pcm,
|
||||||
|
formation->midi, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||||
{
|
{
|
||||||
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
|
||||||
unsigned int curr_rate;
|
unsigned int curr_rate;
|
||||||
int err = 0;
|
int err;
|
||||||
|
|
||||||
/* Need no substreams */
|
// Considering JACK/FFADO streaming:
|
||||||
if (bebob->substreams_counter == 0)
|
// TODO: This can be removed hwdep functionality becomes popular.
|
||||||
goto end;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Considering JACK/FFADO streaming:
|
|
||||||
* TODO: This can be removed hwdep functionality becomes popular.
|
|
||||||
*/
|
|
||||||
err = check_connection_used_by_others(bebob, &bebob->rx_stream);
|
err = check_connection_used_by_others(bebob, &bebob->rx_stream);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto end;
|
return err;
|
||||||
|
|
||||||
/*
|
err = bebob->spec->rate->get(bebob, &curr_rate);
|
||||||
* packet queueing error or detecting discontinuity
|
if (err < 0)
|
||||||
*
|
return err;
|
||||||
* At bus reset, connections should not be broken here. So streams need
|
|
||||||
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
|
|
||||||
*/
|
|
||||||
if (amdtp_streaming_error(&bebob->rx_stream))
|
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
|
||||||
if (amdtp_streaming_error(&bebob->tx_stream))
|
|
||||||
amdtp_stream_stop(&bebob->tx_stream);
|
|
||||||
if (!amdtp_stream_running(&bebob->rx_stream) &&
|
|
||||||
!amdtp_stream_running(&bebob->tx_stream))
|
|
||||||
break_both_connections(bebob);
|
|
||||||
|
|
||||||
/* stop streams if rate is different */
|
|
||||||
err = rate_spec->get(bebob, &curr_rate);
|
|
||||||
if (err < 0) {
|
|
||||||
dev_err(&bebob->unit->device,
|
|
||||||
"fail to get sampling rate: %d\n", err);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (rate == 0)
|
if (rate == 0)
|
||||||
rate = curr_rate;
|
rate = curr_rate;
|
||||||
if (rate != curr_rate) {
|
if (curr_rate != rate) {
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
|
||||||
amdtp_stream_stop(&bebob->tx_stream);
|
amdtp_stream_stop(&bebob->tx_stream);
|
||||||
|
amdtp_stream_stop(&bebob->rx_stream);
|
||||||
|
|
||||||
break_both_connections(bebob);
|
break_both_connections(bebob);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* master should be always running */
|
if (bebob->substreams_counter == 0 || curr_rate != rate) {
|
||||||
if (!amdtp_stream_running(&bebob->rx_stream)) {
|
unsigned int index;
|
||||||
/*
|
|
||||||
* NOTE:
|
// NOTE:
|
||||||
* If establishing connections at first, Yamaha GO46
|
// If establishing connections at first, Yamaha GO46
|
||||||
* (and maybe Terratec X24) don't generate sound.
|
// (and maybe Terratec X24) don't generate sound.
|
||||||
*
|
//
|
||||||
* For firmware customized by M-Audio, refer to next NOTE.
|
// For firmware customized by M-Audio, refer to next NOTE.
|
||||||
*/
|
err = bebob->spec->rate->set(bebob, rate);
|
||||||
if (bebob->maudio_special_quirk == NULL) {
|
if (err < 0) {
|
||||||
err = rate_spec->set(bebob, rate);
|
dev_err(&bebob->unit->device,
|
||||||
if (err < 0) {
|
"fail to set sampling rate: %d\n",
|
||||||
dev_err(&bebob->unit->device,
|
err);
|
||||||
"fail to set sampling rate: %d\n",
|
return err;
|
||||||
err);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = make_both_connections(bebob, rate);
|
err = get_formation_index(rate, &index);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto end;
|
return err;
|
||||||
|
|
||||||
err = start_stream(bebob, &bebob->rx_stream, rate);
|
err = keep_resources(bebob, &bebob->tx_stream, rate, index);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = keep_resources(bebob, &bebob->rx_stream, rate, index);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
// Need no substreams.
|
||||||
|
if (bebob->substreams_counter == 0)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
// packet queueing error or detecting discontinuity
|
||||||
|
if (amdtp_streaming_error(&bebob->rx_stream) ||
|
||||||
|
amdtp_streaming_error(&bebob->tx_stream)) {
|
||||||
|
amdtp_stream_stop(&bebob->rx_stream);
|
||||||
|
amdtp_stream_stop(&bebob->tx_stream);
|
||||||
|
|
||||||
|
break_both_connections(bebob);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amdtp_stream_running(&bebob->rx_stream)) {
|
||||||
|
unsigned int curr_rate;
|
||||||
|
|
||||||
|
if (bebob->maudio_special_quirk) {
|
||||||
|
err = bebob->spec->rate->get(bebob, &curr_rate);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = make_both_connections(bebob);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = start_stream(bebob, &bebob->rx_stream);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&bebob->unit->device,
|
dev_err(&bebob->unit->device,
|
||||||
"fail to run AMDTP master stream:%d\n", err);
|
"fail to run AMDTP master stream:%d\n", err);
|
||||||
break_both_connections(bebob);
|
goto error;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// NOTE:
|
||||||
* NOTE:
|
// The firmware customized by M-Audio uses these commands to
|
||||||
* The firmware customized by M-Audio uses these commands to
|
// start transmitting stream. This is not usual way.
|
||||||
* start transmitting stream. This is not usual way.
|
if (bebob->maudio_special_quirk) {
|
||||||
*/
|
err = bebob->spec->rate->set(bebob, curr_rate);
|
||||||
if (bebob->maudio_special_quirk != NULL) {
|
|
||||||
err = rate_spec->set(bebob, rate);
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&bebob->unit->device,
|
dev_err(&bebob->unit->device,
|
||||||
"fail to ensure sampling rate: %d\n",
|
"fail to ensure sampling rate: %d\n",
|
||||||
err);
|
err);
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
goto error;
|
||||||
break_both_connections(bebob);
|
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait first callback */
|
|
||||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||||
CALLBACK_TIMEOUT)) {
|
CALLBACK_TIMEOUT)) {
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
amdtp_stream_stop(&bebob->rx_stream);
|
||||||
break_both_connections(bebob);
|
break_both_connections(bebob);
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
goto end;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start slave if needed */
|
|
||||||
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
||||||
err = start_stream(bebob, &bebob->tx_stream, rate);
|
err = start_stream(bebob, &bebob->tx_stream);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&bebob->unit->device,
|
dev_err(&bebob->unit->device,
|
||||||
"fail to run AMDTP slave stream:%d\n", err);
|
"fail to run AMDTP slave stream:%d\n", err);
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
goto error;
|
||||||
break_both_connections(bebob);
|
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait first callback */
|
|
||||||
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||||
CALLBACK_TIMEOUT)) {
|
CALLBACK_TIMEOUT)) {
|
||||||
amdtp_stream_stop(&bebob->tx_stream);
|
|
||||||
amdtp_stream_stop(&bebob->rx_stream);
|
|
||||||
break_both_connections(bebob);
|
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end:
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
amdtp_stream_stop(&bebob->tx_stream);
|
||||||
|
amdtp_stream_stop(&bebob->rx_stream);
|
||||||
|
break_both_connections(bebob);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user