123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
- *
- * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
- */
- #include "digi00x.h"
- #define CALLBACK_TIMEOUT 500
- const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
- [SND_DG00X_RATE_44100] = 44100,
- [SND_DG00X_RATE_48000] = 48000,
- [SND_DG00X_RATE_88200] = 88200,
- [SND_DG00X_RATE_96000] = 96000,
- };
- /* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
- const unsigned int
- snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
- /* Analog/ADAT/SPDIF */
- [SND_DG00X_RATE_44100] = (8 + 8 + 2),
- [SND_DG00X_RATE_48000] = (8 + 8 + 2),
- /* Analog/SPDIF */
- [SND_DG00X_RATE_88200] = (8 + 2),
- [SND_DG00X_RATE_96000] = (8 + 2),
- };
- int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
- {
- u32 data;
- __be32 reg;
- int err;
- err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
- ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- data = be32_to_cpu(reg) & 0x0f;
- if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
- *rate = snd_dg00x_stream_rates[data];
- else
- err = -EIO;
- return err;
- }
- int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
- {
- __be32 reg;
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
- if (rate == snd_dg00x_stream_rates[i])
- break;
- }
- if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
- return -EINVAL;
- reg = cpu_to_be32(i);
- return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
- ®, sizeof(reg), 0);
- }
- int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
- enum snd_dg00x_clock *clock)
- {
- __be32 reg;
- int err;
- err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
- ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- *clock = be32_to_cpu(reg) & 0x0f;
- if (*clock >= SND_DG00X_CLOCK_COUNT)
- err = -EIO;
- return err;
- }
- int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
- {
- __be32 reg;
- int err;
- err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
- ®, sizeof(reg), 0);
- if (err >= 0)
- *detect = be32_to_cpu(reg) > 0;
- return err;
- }
- int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
- unsigned int *rate)
- {
- u32 data;
- __be32 reg;
- int err;
- err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
- ®, sizeof(reg), 0);
- if (err < 0)
- return err;
- data = be32_to_cpu(reg) & 0x0f;
- if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
- *rate = snd_dg00x_stream_rates[data];
- /* This means desync. */
- else
- err = -EBUSY;
- return err;
- }
- static void finish_session(struct snd_dg00x *dg00x)
- {
- __be32 data = cpu_to_be32(0x00000003);
- snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
- &data, sizeof(data), 0);
- }
- static int begin_session(struct snd_dg00x *dg00x)
- {
- __be32 data;
- u32 curr;
- int err;
- err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
- &data, sizeof(data), 0);
- if (err < 0)
- goto error;
- curr = be32_to_cpu(data);
- if (curr == 0)
- curr = 2;
- curr--;
- while (curr > 0) {
- data = cpu_to_be32(curr);
- err = snd_fw_transaction(dg00x->unit,
- TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE +
- DG00X_OFFSET_STREAMING_SET,
- &data, sizeof(data), 0);
- if (err < 0)
- goto error;
- msleep(20);
- curr--;
- }
- return 0;
- error:
- finish_session(dg00x);
- return err;
- }
- static void release_resources(struct snd_dg00x *dg00x)
- {
- __be32 data = 0;
- /* Unregister isochronous channels for both direction. */
- snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
- &data, sizeof(data), 0);
- /* Release isochronous resources. */
- fw_iso_resources_free(&dg00x->tx_resources);
- fw_iso_resources_free(&dg00x->rx_resources);
- }
- static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
- {
- unsigned int i;
- __be32 data;
- int err;
- /* Check sampling rate. */
- for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
- if (snd_dg00x_stream_rates[i] == rate)
- break;
- }
- if (i == SND_DG00X_RATE_COUNT)
- return -EINVAL;
- /* Keep resources for out-stream. */
- err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
- snd_dg00x_stream_pcm_channels[i]);
- if (err < 0)
- return err;
- err = fw_iso_resources_allocate(&dg00x->rx_resources,
- amdtp_stream_get_max_payload(&dg00x->rx_stream),
- fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- return err;
- /* Keep resources for in-stream. */
- err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
- snd_dg00x_stream_pcm_channels[i]);
- if (err < 0)
- return err;
- err = fw_iso_resources_allocate(&dg00x->tx_resources,
- amdtp_stream_get_max_payload(&dg00x->tx_stream),
- fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- goto error;
- /* Register isochronous channels for both direction. */
- data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
- dg00x->rx_resources.channel);
- err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
- &data, sizeof(data), 0);
- if (err < 0)
- goto error;
- return 0;
- error:
- release_resources(dg00x);
- return err;
- }
- int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
- {
- int err;
- /* For out-stream. */
- err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
- if (err < 0)
- goto error;
- err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
- if (err < 0)
- goto error;
- /* For in-stream. */
- err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
- if (err < 0)
- goto error;
- err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
- if (err < 0)
- goto error;
- return 0;
- error:
- snd_dg00x_stream_destroy_duplex(dg00x);
- return err;
- }
- /*
- * This function should be called before starting streams or after stopping
- * streams.
- */
- void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
- {
- amdtp_stream_destroy(&dg00x->rx_stream);
- fw_iso_resources_destroy(&dg00x->rx_resources);
- amdtp_stream_destroy(&dg00x->tx_stream);
- fw_iso_resources_destroy(&dg00x->tx_resources);
- }
- int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
- {
- unsigned int curr_rate;
- int err = 0;
- if (dg00x->substreams_counter == 0)
- goto end;
- /* Check current sampling rate. */
- err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
- if (err < 0)
- goto error;
- if (rate == 0)
- rate = curr_rate;
- if (curr_rate != rate ||
- amdtp_streaming_error(&dg00x->tx_stream) ||
- amdtp_streaming_error(&dg00x->rx_stream)) {
- finish_session(dg00x);
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- release_resources(dg00x);
- }
- /*
- * No packets are transmitted without receiving packets, reagardless of
- * which source of clock is used.
- */
- if (!amdtp_stream_running(&dg00x->rx_stream)) {
- err = snd_dg00x_stream_set_local_rate(dg00x, rate);
- if (err < 0)
- goto error;
- err = keep_resources(dg00x, rate);
- if (err < 0)
- goto error;
- err = begin_session(dg00x);
- if (err < 0)
- goto error;
- err = amdtp_stream_start(&dg00x->rx_stream,
- dg00x->rx_resources.channel,
- fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- goto error;
- if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
- CALLBACK_TIMEOUT)) {
- err = -ETIMEDOUT;
- goto error;
- }
- }
- /*
- * The value of SYT field in transmitted packets is always 0x0000. Thus,
- * duplex streams with timestamp synchronization cannot be built.
- */
- if (!amdtp_stream_running(&dg00x->tx_stream)) {
- err = amdtp_stream_start(&dg00x->tx_stream,
- dg00x->tx_resources.channel,
- fw_parent_device(dg00x->unit)->max_speed);
- if (err < 0)
- goto error;
- if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
- CALLBACK_TIMEOUT)) {
- err = -ETIMEDOUT;
- goto error;
- }
- }
- end:
- return err;
- error:
- finish_session(dg00x);
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- release_resources(dg00x);
- return err;
- }
- void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
- {
- if (dg00x->substreams_counter > 0)
- return;
- amdtp_stream_stop(&dg00x->tx_stream);
- amdtp_stream_stop(&dg00x->rx_stream);
- finish_session(dg00x);
- release_resources(dg00x);
- /*
- * Just after finishing the session, the device may lost transmitting
- * functionality for a short time.
- */
- msleep(50);
- }
- void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
- {
- fw_iso_resources_update(&dg00x->tx_resources);
- fw_iso_resources_update(&dg00x->rx_resources);
- amdtp_stream_update(&dg00x->tx_stream);
- amdtp_stream_update(&dg00x->rx_stream);
- }
- void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
- {
- dg00x->dev_lock_changed = true;
- wake_up(&dg00x->hwdep_wait);
- }
- int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
- {
- int err;
- spin_lock_irq(&dg00x->lock);
- /* user land lock this */
- if (dg00x->dev_lock_count < 0) {
- err = -EBUSY;
- goto end;
- }
- /* this is the first time */
- if (dg00x->dev_lock_count++ == 0)
- snd_dg00x_stream_lock_changed(dg00x);
- err = 0;
- end:
- spin_unlock_irq(&dg00x->lock);
- return err;
- }
- void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
- {
- spin_lock_irq(&dg00x->lock);
- if (WARN_ON(dg00x->dev_lock_count <= 0))
- goto end;
- if (--dg00x->dev_lock_count == 0)
- snd_dg00x_stream_lock_changed(dg00x);
- end:
- spin_unlock_irq(&dg00x->lock);
- }
|