Commit 6eb6c81e authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: dice: Split stream functionality into a file

This commit adds a file with some helper functions for streaming, and move some
codes into the file with some arrangements.

Well-known CMP is not used to start/stop streams for Dice chipset. It's
achieved by writing to specific address. We call this way as 'enable'.
When devices are 'enabled', streaming starts in registered isochronous channel.
Some helper functions are already implemented in previous commit.

Basically, the stream is compliant to IEC 61883-6, so-called as AMDTP. But Dice
has a specific quirk, so called-as 'Dual Wire'. This quirk is applied at
176.4/192.0kHz. In this mode, each packet includes double number of events than
number in the specification, and stream runs at a half of sampling rate.

There is another quirk at bus reset. Dice chipset handles drivers' request but
don't re-enable streaming. So stream should be stopped.
Signed-off-by: 's avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: 's avatarTakashi Iwai <tiwai@suse.de>
parent 7c2d4c0c
snd-dice-objs := dice-transaction.o dice.o
snd-dice-objs := dice-transaction.o dice-stream.o dice.o
obj-m += snd-dice.o
/*
* 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)) {
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);
}
This diff is collapsed.
......@@ -157,4 +157,18 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
#define SND_DICE_RATES_COUNT 7
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, unsigned int *mode);
int snd_dice_stream_start_packets(struct snd_dice *dice);
int snd_dice_stream_start(struct snd_dice *dice);
void snd_dice_stream_stop_packets(struct snd_dice *dice);
void snd_dice_stream_stop(struct snd_dice *dice);
int snd_dice_stream_init(struct snd_dice *dice);
void snd_dice_stream_destroy(struct snd_dice *dice);
void snd_dice_stream_update(struct snd_dice *dice);
int snd_dice_stream_lock_try(struct snd_dice *dice);
void snd_dice_stream_lock_release(struct snd_dice *dice);
#endif
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