/* * dice_stream.c - a part of driver for DICE based devices * * Copyright (c) Clemens Ladisch * Copyright (c) 2014 Takashi Sakamoto * * 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); }