mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
ALSA: dice: reserve/release isochronous resources in pcm.hw_params/hw_free callbacks
Once allocated, isochronous resources are available for packet streaming, even if the streaming is cancelled. For this reason, current implementation handles allocation of the resources and starting packet streaming at the same time. However, this brings complicated procedure to start packet streaming. This commit separates the allocation and starting. The allocation is done in pcm.hw_params callback and available till pcm.hw_free callback. Even if any XRUN occurs, pcm.prepare callback is done to restart packet streaming without releasing/allocating the resources. There are two points to stop packet streaming; in pcm.hw_params and pcm.prepare callbacks. The former point is a case that packet streaming is already started for any MIDI substream then packet streaming is requested with different sampling transfer frequency for any PCM substream. The latter point is cases of any XRUN or packet queueing error. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
c738aed136
commit
3cd2c2d780
4 changed files with 143 additions and 115 deletions
|
@ -18,8 +18,11 @@ static int midi_open(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
dice->substreams_counter++;
|
||||
err = snd_dice_stream_start_duplex(dice, 0);
|
||||
err = snd_dice_stream_reserve_duplex(dice, 0);
|
||||
if (err >= 0) {
|
||||
++dice->substreams_counter;
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
}
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
|
@ -35,8 +38,9 @@ static int midi_close(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
dice->substreams_counter--;
|
||||
--dice->substreams_counter;
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
snd_dice_stream_release_duplex(dice);
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
|
|
|
@ -243,12 +243,16 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
dice->substreams_counter++;
|
||||
err = snd_dice_stream_reserve_duplex(dice, rate);
|
||||
if (err >= 0)
|
||||
++dice->substreams_counter;
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
static int playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
|
@ -262,12 +266,16 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
dice->substreams_counter++;
|
||||
err = snd_dice_stream_reserve_duplex(dice, rate);
|
||||
if (err >= 0)
|
||||
++dice->substreams_counter;
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int capture_hw_free(struct snd_pcm_substream *substream)
|
||||
|
@ -277,9 +285,10 @@ static int capture_hw_free(struct snd_pcm_substream *substream)
|
|||
mutex_lock(&dice->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dice->substreams_counter--;
|
||||
--dice->substreams_counter;
|
||||
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
snd_dice_stream_release_duplex(dice);
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
|
@ -293,9 +302,10 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
|
|||
mutex_lock(&dice->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dice->substreams_counter--;
|
||||
--dice->substreams_counter;
|
||||
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
snd_dice_stream_release_duplex(dice);
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
|
@ -309,7 +319,7 @@ static int capture_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
@ -323,7 +333,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
|
|
@ -138,18 +138,9 @@ static int get_register_params(struct snd_dice *dice,
|
|||
|
||||
static void release_resources(struct snd_dice *dice)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if (amdtp_stream_running(&dice->tx_stream[i])) {
|
||||
amdtp_stream_pcm_abort(&dice->tx_stream[i]);
|
||||
amdtp_stream_stop(&dice->tx_stream[i]);
|
||||
}
|
||||
if (amdtp_stream_running(&dice->rx_stream[i])) {
|
||||
amdtp_stream_pcm_abort(&dice->rx_stream[i]);
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
}
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
fw_iso_resources_free(&dice->tx_resources[i]);
|
||||
fw_iso_resources_free(&dice->rx_resources[i]);
|
||||
}
|
||||
|
@ -164,10 +155,14 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
for (i = 0; i < params->count; i++) {
|
||||
reg = cpu_to_be32((u32)-1);
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
amdtp_stream_stop(&dice->tx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
|
@ -288,6 +283,65 @@ static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
|
|||
snd_dice_transaction_clear_enable(dice);
|
||||
}
|
||||
|
||||
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
// Check sampling transmission frequency.
|
||||
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
|
||||
if (dice->substreams_counter == 0 || curr_rate != rate) {
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
release_resources(dice);
|
||||
|
||||
// Just after owning the unit (GLOBAL_OWNER), the unit can
|
||||
// return invalid stream formats. Selecting clock parameters
|
||||
// have an effect for the unit to refine it.
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// After changing sampling transfer frequency, the value of
|
||||
// register can be changed.
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
|
||||
&tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
|
||||
&rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
release_resources(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dice_stream_release_duplex(struct snd_dice *dice)
|
||||
{
|
||||
if (dice->substreams_counter == 0)
|
||||
release_resources(dice);
|
||||
}
|
||||
|
||||
static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
unsigned int rate, struct reg_params *params)
|
||||
{
|
||||
|
@ -295,10 +349,6 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
int i;
|
||||
int err;
|
||||
|
||||
err = keep_dual_resources(dice, rate, dir, params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < params->count; i++) {
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
|
@ -342,102 +392,39 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int start_duplex_streams(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Stop transmission.
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
release_resources(dice);
|
||||
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to ensure phase lock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Likely to have changed stream formats. */
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Start both streams. */
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
release_resources(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* MEMO: After this function, there're two states of streams:
|
||||
* - None streams are running.
|
||||
* - All streams are running.
|
||||
*/
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
struct reg_params tx_params, rx_params;
|
||||
unsigned int i;
|
||||
unsigned int rate;
|
||||
enum snd_dice_rate_mode mode;
|
||||
int err;
|
||||
|
||||
if (dice->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
/* Check sampling transmission frequency. */
|
||||
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to get sampling rate\n");
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate)
|
||||
goto restart;
|
||||
|
||||
/* Check error of packet streaming. */
|
||||
// Check error of packet streaming.
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]))
|
||||
break;
|
||||
if (amdtp_streaming_error(&dice->rx_stream[i]))
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
|
||||
amdtp_streaming_error(&dice->rx_stream[i])) {
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
|
||||
/* Check required streams are running or not. */
|
||||
// Check required streams are running or not.
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -449,12 +436,40 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
|||
!amdtp_stream_running(&dice->rx_stream[i]))
|
||||
break;
|
||||
}
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
if (i < MAX_STREAMS) {
|
||||
// Start both streams.
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
restart:
|
||||
return start_duplex_streams(dice, rate);
|
||||
error:
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -466,13 +481,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
|
|||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
if (dice->substreams_counter > 0)
|
||||
return;
|
||||
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
release_resources(dice);
|
||||
if (dice->substreams_counter == 0) {
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
}
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
|
|
|
@ -205,10 +205,12 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
|
|||
|
||||
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
enum snd_dice_rate_mode *mode);
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_init_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate);
|
||||
void snd_dice_stream_release_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_update_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
|
||||
|
||||
|
|
Loading…
Reference in a new issue