blob: 4c4c4fff62726be04b55a598d36b9bec7fee0549 [file] [log] [blame]
/*
* dice_stream.c - a part of driver for DICE based devices
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "dice.h"
const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
/* mode 0 */
[0] = 32000,
[1] = 44100,
[2] = 48000,
/* mode 1 */
[3] = 88200,
[4] = 96000,
/* mode 2 */
[5] = 176400,
[6] = 192000,
};
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
unsigned int *mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
if (!(dice->clock_caps & BIT(i)))
continue;
if (snd_dice_rates[i] != rate)
continue;
*mode = (i - 1) / 2;
return 0;
}
return -EINVAL;
}
int snd_dice_stream_start_packets(struct snd_dice *dice)
{
int err;
if (amdtp_stream_running(&dice->rx_stream))
return 0;
err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
return err;
err = snd_dice_transaction_set_enable(dice);
if (err < 0) {
amdtp_stream_stop(&dice->rx_stream);
return err;
}
return 0;
}
int snd_dice_stream_start(struct snd_dice *dice)
{
__be32 channel;
int err;
if (!dice->rx_resources.allocated) {
err = fw_iso_resources_allocate(&dice->rx_resources,
amdtp_stream_get_max_payload(&dice->rx_stream),
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
goto error;
channel = cpu_to_be32(dice->rx_resources.channel);
err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS,
&channel, 4);
if (err < 0)
goto err_resources;
}
err = snd_dice_stream_start_packets(dice);
if (err < 0)
goto err_rx_channel;
return 0;
err_rx_channel:
channel = cpu_to_be32((u32)-1);
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
err_resources:
fw_iso_resources_free(&dice->rx_resources);
error:
return err;
}
void snd_dice_stream_stop_packets(struct snd_dice *dice)
{
if (!amdtp_stream_running(&dice->rx_stream))
return;
snd_dice_transaction_clear_enable(dice);
amdtp_stream_stop(&dice->rx_stream);
}
void snd_dice_stream_stop(struct snd_dice *dice)
{
__be32 channel;
snd_dice_stream_stop_packets(dice);
if (!dice->rx_resources.allocated)
return;
channel = cpu_to_be32((u32)-1);
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
fw_iso_resources_free(&dice->rx_resources);
}
int snd_dice_stream_init(struct snd_dice *dice)
{
int err;
err = fw_iso_resources_init(&dice->rx_resources, dice->unit);
if (err < 0)
goto end;
dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM,
CIP_BLOCKING);
if (err < 0)
goto error;
err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
if (err < 0)
goto error;
end:
return err;
error:
amdtp_stream_destroy(&dice->rx_stream);
fw_iso_resources_destroy(&dice->rx_resources);
return err;
}
void snd_dice_stream_destroy(struct snd_dice *dice)
{
amdtp_stream_pcm_abort(&dice->rx_stream);
snd_dice_stream_stop(dice);
amdtp_stream_destroy(&dice->rx_stream);
fw_iso_resources_destroy(&dice->rx_resources);
}
void snd_dice_stream_update(struct snd_dice *dice)
{
/*
* On a bus reset, the DICE firmware disables streaming and then goes
* off contemplating its own navel for hundreds of milliseconds before
* it can react to any of our attempts to reenable streaming. This
* means that we lose synchronization anyway, so we force our streams
* to stop so that the application can restart them in an orderly
* manner.
*/
dice->global_enabled = false;
amdtp_stream_pcm_abort(&dice->rx_stream);
snd_dice_stream_stop_packets(dice);
fw_iso_resources_update(&dice->rx_resources);
}
static void dice_lock_changed(struct snd_dice *dice)
{
dice->dev_lock_changed = true;
wake_up(&dice->hwdep_wait);
}
int snd_dice_stream_lock_try(struct snd_dice *dice)
{
int err;
spin_lock_irq(&dice->lock);
if (dice->dev_lock_count < 0) {
err = -EBUSY;
goto out;
}
if (dice->dev_lock_count++ == 0)
dice_lock_changed(dice);
err = 0;
out:
spin_unlock_irq(&dice->lock);
return err;
}
void snd_dice_stream_lock_release(struct snd_dice *dice)
{
spin_lock_irq(&dice->lock);
if (WARN_ON(dice->dev_lock_count <= 0))
goto out;
if (--dice->dev_lock_count == 0)
dice_lock_changed(dice);
out:
spin_unlock_irq(&dice->lock);
}