Commit ac2888b9 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent c7e68a69
...@@ -218,7 +218,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) ...@@ -555,132 +533,154 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
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)
{ {
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; struct snd_bebob_stream_formation *formation;
unsigned int curr_rate;
int err = 0;
/* Need no substreams */ if (stream == &bebob->tx_stream)
if (bebob->substreams_counter == 0) formation = bebob->tx_stream_formations + index;
goto end; else
formation = bebob->rx_stream_formations + index;
/* return amdtp_am824_set_parameters(stream, rate, formation->pcm,
* Considering JACK/FFADO streaming: formation->midi, false);
* TODO: This can be removed hwdep functionality becomes popular. }
*/
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
{
unsigned int curr_rate;
int err;
// 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 if (rate == 0)
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. rate = curr_rate;
*/ if (curr_rate != rate) {
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); amdtp_stream_stop(&bebob->tx_stream);
if (!amdtp_stream_running(&bebob->rx_stream) && amdtp_stream_stop(&bebob->rx_stream);
!amdtp_stream_running(&bebob->tx_stream))
break_both_connections(bebob); break_both_connections(bebob);
}
/* stop streams if rate is different */ if (bebob->substreams_counter == 0 || curr_rate != rate) {
err = rate_spec->get(bebob, &curr_rate); unsigned int index;
if (err < 0) {
dev_err(&bebob->unit->device, // NOTE:
"fail to get sampling rate: %d\n", err); // If establishing connections at first, Yamaha GO46
goto end; // (and maybe Terratec X24) don't generate sound.
//
// For firmware customized by M-Audio, refer to next NOTE.
err = bebob->spec->rate->set(bebob, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to set sampling rate: %d\n",
err);
return err;
}
err = get_formation_index(rate, &index);
if (err < 0)
return err;
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;
} }
if (rate == 0)
rate = curr_rate; return 0;
if (rate != curr_rate) { }
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->rx_stream);
amdtp_stream_stop(&bebob->tx_stream); amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob); break_both_connections(bebob);
} }
/* master should be always running */
if (!amdtp_stream_running(&bebob->rx_stream)) { if (!amdtp_stream_running(&bebob->rx_stream)) {
/* unsigned int curr_rate;
* NOTE:
* If establishing connections at first, Yamaha GO46 if (bebob->maudio_special_quirk) {
* (and maybe Terratec X24) don't generate sound. err = bebob->spec->rate->get(bebob, &curr_rate);
* if (err < 0)
* For firmware customized by M-Audio, refer to next NOTE. return err;
*/
if (bebob->maudio_special_quirk == NULL) {
err = rate_spec->set(bebob, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to set sampling rate: %d\n",
err);
goto end;
}
} }
err = make_both_connections(bebob, rate); err = make_both_connections(bebob);
if (err < 0) if (err < 0)
goto end; return err;
err = start_stream(bebob, &bebob->rx_stream, rate); 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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment