mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 23:25:54 +00:00
c50fb91f53
This commit adds a file and move some codes related to PCM functionality. Currently PCM playback is supported. PCM capture will be supported in followed commits. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
208 lines
4.4 KiB
C
208 lines
4.4 KiB
C
/*
|
|
* 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);
|
|
}
|