Path: blob/master/sound/firewire/motu/motu-protocol-v2.c
26424 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* motu-protocol-v2.c - a part of driver for MOTU FireWire series3*4* Copyright (c) 2015-2017 Takashi Sakamoto <[email protected]>5*/67#include "motu.h"89#define V2_CLOCK_STATUS_OFFSET 0x0b1410#define V2_CLOCK_RATE_MASK 0x0000003811#define V2_CLOCK_RATE_SHIFT 312#define V2_CLOCK_SRC_MASK 0x0000000713#define V2_CLOCK_SRC_SHIFT 014#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler.15#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x0516#define V2_CLOCK_SRC_WORD_ON_BNC 0x0417#define V2_CLOCK_SRC_SPH 0x0318#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD.19#define V2_CLOCK_SRC_ADAT_ON_OPT 0x0120#define V2_CLOCK_SRC_INTERNAL 0x0021#define V2_CLOCK_FETCH_ENABLE 0x0200000022#define V2_CLOCK_MODEL_SPECIFIC 0x040000002324#define V2_IN_OUT_CONF_OFFSET 0x0c0425#define V2_OPT_OUT_IFACE_MASK 0x00000c0026#define V2_OPT_OUT_IFACE_SHIFT 1027#define V2_OPT_IN_IFACE_MASK 0x0000030028#define V2_OPT_IN_IFACE_SHIFT 829#define V2_OPT_IFACE_MODE_NONE 030#define V2_OPT_IFACE_MODE_ADAT 131#define V2_OPT_IFACE_MODE_SPDIF 23233static int get_clock_rate(u32 data, unsigned int *rate)34{35unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;36if (index >= ARRAY_SIZE(snd_motu_clock_rates))37return -EIO;3839*rate = snd_motu_clock_rates[index];4041return 0;42}4344int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,45unsigned int *rate)46{47__be32 reg;48int err;4950err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®,51sizeof(reg));52if (err < 0)53return err;5455return get_clock_rate(be32_to_cpu(reg), rate);56}5758int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,59unsigned int rate)60{61__be32 reg;62u32 data;63int i;64int err;6566for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {67if (snd_motu_clock_rates[i] == rate)68break;69}70if (i == ARRAY_SIZE(snd_motu_clock_rates))71return -EINVAL;7273err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®,74sizeof(reg));75if (err < 0)76return err;77data = be32_to_cpu(reg);7879data &= ~V2_CLOCK_RATE_MASK;80data |= i << V2_CLOCK_RATE_SHIFT;8182reg = cpu_to_be32(data);83return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®,84sizeof(reg));85}8687static int get_clock_source(struct snd_motu *motu, u32 data,88enum snd_motu_clock_source *src)89{90switch (data & V2_CLOCK_SRC_MASK) {91case V2_CLOCK_SRC_INTERNAL:92*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;93break;94case V2_CLOCK_SRC_ADAT_ON_OPT:95*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;96break;97case V2_CLOCK_SRC_SPDIF:98{99bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||100motu->spec == &snd_motu_spec_traveler);101102if (motu->spec == &snd_motu_spec_896hd) {103*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;104} else if (!support_iec60958_on_opt) {105*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;106} else {107__be32 reg;108109// To check the configuration of optical interface.110int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®,111sizeof(reg));112if (err < 0)113return err;114115if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==116V2_OPT_IFACE_MODE_SPDIF)117*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;118else119*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;120}121break;122}123case V2_CLOCK_SRC_SPH:124*src = SND_MOTU_CLOCK_SOURCE_SPH;125break;126case V2_CLOCK_SRC_WORD_ON_BNC:127*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;128break;129case V2_CLOCK_SRC_ADAT_ON_DSUB:130*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;131break;132case V2_CLOCK_SRC_AESEBU_ON_XLR:133// For Traveler.134*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;135break;136default:137*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;138break;139}140141return 0;142}143144int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,145enum snd_motu_clock_source *src)146{147__be32 reg;148int err;149150err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®,151sizeof(reg));152if (err < 0)153return err;154155return get_clock_source(motu, be32_to_cpu(reg), src);156}157158// Expected for Traveler, which implements Altera Cyclone EP1C3.159static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,160bool enable)161{162*data |= V2_CLOCK_MODEL_SPECIFIC;163164return 0;165}166167// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.168static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,169bool enable)170{171unsigned int rate;172enum snd_motu_clock_source src;173int err;174175err = get_clock_source(motu, *data, &src);176if (err < 0)177return err;178179err = get_clock_rate(*data, &rate);180if (err < 0)181return err;182183if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)184*data |= V2_CLOCK_MODEL_SPECIFIC;185186return 0;187}188189int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,190bool enable)191{192if (motu->spec == &snd_motu_spec_828mk2) {193// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.194return 0;195} else if (motu->spec == &snd_motu_spec_896hd) {196// 896HD implements Altera Cyclone EP1C3 but nothing to do.197return 0;198} else {199__be32 reg;200u32 data;201int err;202203err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,204®, sizeof(reg));205if (err < 0)206return err;207data = be32_to_cpu(reg);208209data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);210if (enable)211data |= V2_CLOCK_FETCH_ENABLE;212213if (motu->spec == &snd_motu_spec_traveler)214err = switch_fetching_mode_cyclone(motu, &data, enable);215else216err = switch_fetching_mode_spartan(motu, &data, enable);217if (err < 0)218return err;219220reg = cpu_to_be32(data);221return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,222®, sizeof(reg));223}224}225226int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)227{228bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);229__be32 reg;230u32 data;231int err;232233motu->tx_packet_formats.pcm_byte_offset = 10;234motu->rx_packet_formats.pcm_byte_offset = 10;235236motu->tx_packet_formats.msg_chunks = 2;237motu->rx_packet_formats.msg_chunks = 2;238239err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®,240sizeof(reg));241if (err < 0)242return err;243data = be32_to_cpu(reg);244245memcpy(motu->tx_packet_formats.pcm_chunks,246motu->spec->tx_fixed_pcm_chunks,247sizeof(motu->tx_packet_formats.pcm_chunks));248memcpy(motu->rx_packet_formats.pcm_chunks,249motu->spec->rx_fixed_pcm_chunks,250sizeof(motu->rx_packet_formats.pcm_chunks));251252if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {253motu->tx_packet_formats.pcm_chunks[0] += 8;254255if (!has_two_opt_ifaces)256motu->tx_packet_formats.pcm_chunks[1] += 4;257else258motu->tx_packet_formats.pcm_chunks[1] += 8;259}260261if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {262motu->rx_packet_formats.pcm_chunks[0] += 8;263264if (!has_two_opt_ifaces)265motu->rx_packet_formats.pcm_chunks[1] += 4;266else267motu->rx_packet_formats.pcm_chunks[1] += 8;268}269270return 0;271}272273const struct snd_motu_spec snd_motu_spec_828mk2 = {274.name = "828mk2",275.protocol_version = SND_MOTU_PROTOCOL_V2,276.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |277SND_MOTU_SPEC_TX_MIDI_2ND_Q |278SND_MOTU_SPEC_REGISTER_DSP,279.tx_fixed_pcm_chunks = {14, 14, 0},280.rx_fixed_pcm_chunks = {14, 14, 0},281};282283const struct snd_motu_spec snd_motu_spec_896hd = {284.name = "896HD",285.protocol_version = SND_MOTU_PROTOCOL_V2,286.flags = SND_MOTU_SPEC_REGISTER_DSP,287.tx_fixed_pcm_chunks = {14, 14, 8},288.rx_fixed_pcm_chunks = {14, 14, 8},289};290291const struct snd_motu_spec snd_motu_spec_traveler = {292.name = "Traveler",293.protocol_version = SND_MOTU_PROTOCOL_V2,294.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |295SND_MOTU_SPEC_TX_MIDI_2ND_Q |296SND_MOTU_SPEC_REGISTER_DSP,297.tx_fixed_pcm_chunks = {14, 14, 8},298.rx_fixed_pcm_chunks = {14, 14, 8},299};300301const struct snd_motu_spec snd_motu_spec_ultralite = {302.name = "UltraLite",303.protocol_version = SND_MOTU_PROTOCOL_V2,304.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |305SND_MOTU_SPEC_TX_MIDI_2ND_Q |306SND_MOTU_SPEC_REGISTER_DSP,307.tx_fixed_pcm_chunks = {14, 14, 0},308.rx_fixed_pcm_chunks = {14, 14, 0},309};310311const struct snd_motu_spec snd_motu_spec_8pre = {312.name = "8pre",313.protocol_version = SND_MOTU_PROTOCOL_V2,314.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |315SND_MOTU_SPEC_TX_MIDI_2ND_Q |316SND_MOTU_SPEC_REGISTER_DSP,317// Two dummy chunks always in the end of data block.318.tx_fixed_pcm_chunks = {10, 10, 0},319.rx_fixed_pcm_chunks = {6, 6, 0},320};321322323