Path: blob/master/sound/firewire/digi00x/digi00x-stream.c
26451 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family3*4* Copyright (c) 2014-2015 Takashi Sakamoto5*/67#include "digi00x.h"89#define READY_TIMEOUT_MS 2001011const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {12[SND_DG00X_RATE_44100] = 44100,13[SND_DG00X_RATE_48000] = 48000,14[SND_DG00X_RATE_88200] = 88200,15[SND_DG00X_RATE_96000] = 96000,16};1718/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */19const unsigned int20snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {21/* Analog/ADAT/SPDIF */22[SND_DG00X_RATE_44100] = (8 + 8 + 2),23[SND_DG00X_RATE_48000] = (8 + 8 + 2),24/* Analog/SPDIF */25[SND_DG00X_RATE_88200] = (8 + 2),26[SND_DG00X_RATE_96000] = (8 + 2),27};2829int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)30{31u32 data;32__be32 reg;33int err;3435err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,36DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,37®, sizeof(reg), 0);38if (err < 0)39return err;4041data = be32_to_cpu(reg) & 0x0f;42if (data < ARRAY_SIZE(snd_dg00x_stream_rates))43*rate = snd_dg00x_stream_rates[data];44else45err = -EIO;4647return err;48}4950int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)51{52__be32 reg;53unsigned int i;5455for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {56if (rate == snd_dg00x_stream_rates[i])57break;58}59if (i == ARRAY_SIZE(snd_dg00x_stream_rates))60return -EINVAL;6162reg = cpu_to_be32(i);63return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,64DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,65®, sizeof(reg), 0);66}6768int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,69enum snd_dg00x_clock *clock)70{71__be32 reg;72int err;7374err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,75DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,76®, sizeof(reg), 0);77if (err < 0)78return err;7980*clock = be32_to_cpu(reg) & 0x0f;81if (*clock >= SND_DG00X_CLOCK_COUNT)82err = -EIO;8384return err;85}8687int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)88{89__be32 reg;90int err;9192err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,93DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,94®, sizeof(reg), 0);95if (err >= 0)96*detect = be32_to_cpu(reg) > 0;9798return err;99}100101int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,102unsigned int *rate)103{104u32 data;105__be32 reg;106int err;107108err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,109DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,110®, sizeof(reg), 0);111if (err < 0)112return err;113114data = be32_to_cpu(reg) & 0x0f;115if (data < ARRAY_SIZE(snd_dg00x_stream_rates))116*rate = snd_dg00x_stream_rates[data];117/* This means desync. */118else119err = -EBUSY;120121return err;122}123124static void finish_session(struct snd_dg00x *dg00x)125{126__be32 data;127128data = cpu_to_be32(0x00000003);129snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,130DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,131&data, sizeof(data), 0);132133// Unregister isochronous channels for both direction.134data = 0;135snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,136DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,137&data, sizeof(data), 0);138139// Just after finishing the session, the device may lost transmitting140// functionality for a short time.141msleep(50);142}143144static int begin_session(struct snd_dg00x *dg00x)145{146__be32 data;147u32 curr;148int err;149150// Register isochronous channels for both direction.151data = cpu_to_be32((dg00x->tx_resources.channel << 16) |152dg00x->rx_resources.channel);153err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,154DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,155&data, sizeof(data), 0);156if (err < 0)157return err;158159err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,160DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,161&data, sizeof(data), 0);162if (err < 0)163return err;164curr = be32_to_cpu(data);165166if (curr == 0)167curr = 2;168169curr--;170while (curr > 0) {171data = cpu_to_be32(curr);172err = snd_fw_transaction(dg00x->unit,173TCODE_WRITE_QUADLET_REQUEST,174DG00X_ADDR_BASE +175DG00X_OFFSET_STREAMING_SET,176&data, sizeof(data), 0);177if (err < 0)178break;179180msleep(20);181curr--;182}183184return err;185}186187static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,188unsigned int rate)189{190struct fw_iso_resources *resources;191int i;192int err;193194// Check sampling rate.195for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {196if (snd_dg00x_stream_rates[i] == rate)197break;198}199if (i == SND_DG00X_RATE_COUNT)200return -EINVAL;201202if (stream == &dg00x->tx_stream)203resources = &dg00x->tx_resources;204else205resources = &dg00x->rx_resources;206207err = amdtp_dot_set_parameters(stream, rate,208snd_dg00x_stream_pcm_channels[i]);209if (err < 0)210return err;211212return fw_iso_resources_allocate(resources,213amdtp_stream_get_max_payload(stream),214fw_parent_device(dg00x->unit)->max_speed);215}216217static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)218{219struct fw_iso_resources *resources;220enum amdtp_stream_direction dir;221int err;222223if (s == &dg00x->tx_stream) {224resources = &dg00x->tx_resources;225dir = AMDTP_IN_STREAM;226} else {227resources = &dg00x->rx_resources;228dir = AMDTP_OUT_STREAM;229}230231err = fw_iso_resources_init(resources, dg00x->unit);232if (err < 0)233return err;234235err = amdtp_dot_init(s, dg00x->unit, dir);236if (err < 0)237fw_iso_resources_destroy(resources);238239return err;240}241242static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)243{244amdtp_stream_destroy(s);245246if (s == &dg00x->tx_stream)247fw_iso_resources_destroy(&dg00x->tx_resources);248else249fw_iso_resources_destroy(&dg00x->rx_resources);250}251252int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)253{254int err;255256err = init_stream(dg00x, &dg00x->rx_stream);257if (err < 0)258return err;259260err = init_stream(dg00x, &dg00x->tx_stream);261if (err < 0) {262destroy_stream(dg00x, &dg00x->rx_stream);263return err;264}265266err = amdtp_domain_init(&dg00x->domain);267if (err < 0) {268destroy_stream(dg00x, &dg00x->rx_stream);269destroy_stream(dg00x, &dg00x->tx_stream);270}271272return err;273}274275/*276* This function should be called before starting streams or after stopping277* streams.278*/279void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)280{281amdtp_domain_destroy(&dg00x->domain);282283destroy_stream(dg00x, &dg00x->rx_stream);284destroy_stream(dg00x, &dg00x->tx_stream);285}286287int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,288unsigned int frames_per_period,289unsigned int frames_per_buffer)290{291unsigned int curr_rate;292int err;293294err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);295if (err < 0)296return err;297if (rate == 0)298rate = curr_rate;299300if (dg00x->substreams_counter == 0 || curr_rate != rate) {301amdtp_domain_stop(&dg00x->domain);302303finish_session(dg00x);304305fw_iso_resources_free(&dg00x->tx_resources);306fw_iso_resources_free(&dg00x->rx_resources);307308err = snd_dg00x_stream_set_local_rate(dg00x, rate);309if (err < 0)310return err;311312err = keep_resources(dg00x, &dg00x->rx_stream, rate);313if (err < 0)314return err;315316err = keep_resources(dg00x, &dg00x->tx_stream, rate);317if (err < 0) {318fw_iso_resources_free(&dg00x->rx_resources);319return err;320}321322err = amdtp_domain_set_events_per_period(&dg00x->domain,323frames_per_period, frames_per_buffer);324if (err < 0) {325fw_iso_resources_free(&dg00x->rx_resources);326fw_iso_resources_free(&dg00x->tx_resources);327return err;328}329}330331return 0;332}333334int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)335{336unsigned int generation = dg00x->rx_resources.generation;337int err = 0;338339if (dg00x->substreams_counter == 0)340return 0;341342if (amdtp_streaming_error(&dg00x->tx_stream) ||343amdtp_streaming_error(&dg00x->rx_stream)) {344amdtp_domain_stop(&dg00x->domain);345finish_session(dg00x);346}347348if (generation != fw_parent_device(dg00x->unit)->card->generation) {349err = fw_iso_resources_update(&dg00x->tx_resources);350if (err < 0)351goto error;352353err = fw_iso_resources_update(&dg00x->rx_resources);354if (err < 0)355goto error;356}357358/*359* No packets are transmitted without receiving packets, reagardless of360* which source of clock is used.361*/362if (!amdtp_stream_running(&dg00x->rx_stream)) {363int spd = fw_parent_device(dg00x->unit)->max_speed;364365err = begin_session(dg00x);366if (err < 0)367goto error;368369err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,370dg00x->rx_resources.channel, spd);371if (err < 0)372goto error;373374err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,375dg00x->tx_resources.channel, spd);376if (err < 0)377goto error;378379// NOTE: The device doesn't start packet transmission till receiving any packet.380// It ignores presentation time expressed by the value of syt field of CIP header381// in received packets. The sequence of the number of data blocks per packet is382// important for media clock recovery.383err = amdtp_domain_start(&dg00x->domain, 0, true, true);384if (err < 0)385goto error;386387if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {388err = -ETIMEDOUT;389goto error;390}391}392393return 0;394error:395amdtp_domain_stop(&dg00x->domain);396finish_session(dg00x);397398return err;399}400401void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)402{403if (dg00x->substreams_counter == 0) {404amdtp_domain_stop(&dg00x->domain);405finish_session(dg00x);406407fw_iso_resources_free(&dg00x->tx_resources);408fw_iso_resources_free(&dg00x->rx_resources);409}410}411412void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)413{414fw_iso_resources_update(&dg00x->tx_resources);415fw_iso_resources_update(&dg00x->rx_resources);416417amdtp_stream_update(&dg00x->tx_stream);418amdtp_stream_update(&dg00x->rx_stream);419}420421void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)422{423dg00x->dev_lock_changed = true;424wake_up(&dg00x->hwdep_wait);425}426427int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)428{429int err;430431spin_lock_irq(&dg00x->lock);432433/* user land lock this */434if (dg00x->dev_lock_count < 0) {435err = -EBUSY;436goto end;437}438439/* this is the first time */440if (dg00x->dev_lock_count++ == 0)441snd_dg00x_stream_lock_changed(dg00x);442err = 0;443end:444spin_unlock_irq(&dg00x->lock);445return err;446}447448void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)449{450spin_lock_irq(&dg00x->lock);451452if (WARN_ON(dg00x->dev_lock_count <= 0))453goto end;454if (--dg00x->dev_lock_count == 0)455snd_dg00x_stream_lock_changed(dg00x);456end:457spin_unlock_irq(&dg00x->lock);458}459460461