162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// ff-protocol-former.c - a part of driver for RME Fireface series 362306a36Sopenharmony_ci// 462306a36Sopenharmony_ci// Copyright (c) 2019 Takashi Sakamoto 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "ff.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull 1162306a36Sopenharmony_ci/* For block write request. */ 1262306a36Sopenharmony_ci#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull 1362306a36Sopenharmony_ci#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int parse_clock_bits(u32 data, unsigned int *rate, 1662306a36Sopenharmony_ci enum snd_ff_clock_src *src) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci static const struct { 1962306a36Sopenharmony_ci unsigned int rate; 2062306a36Sopenharmony_ci u32 mask; 2162306a36Sopenharmony_ci } *rate_entry, rate_entries[] = { 2262306a36Sopenharmony_ci { 32000, 0x00000002, }, 2362306a36Sopenharmony_ci { 44100, 0x00000000, }, 2462306a36Sopenharmony_ci { 48000, 0x00000006, }, 2562306a36Sopenharmony_ci { 64000, 0x0000000a, }, 2662306a36Sopenharmony_ci { 88200, 0x00000008, }, 2762306a36Sopenharmony_ci { 96000, 0x0000000e, }, 2862306a36Sopenharmony_ci { 128000, 0x00000012, }, 2962306a36Sopenharmony_ci { 176400, 0x00000010, }, 3062306a36Sopenharmony_ci { 192000, 0x00000016, }, 3162306a36Sopenharmony_ci }; 3262306a36Sopenharmony_ci static const struct { 3362306a36Sopenharmony_ci enum snd_ff_clock_src src; 3462306a36Sopenharmony_ci u32 mask; 3562306a36Sopenharmony_ci } *clk_entry, clk_entries[] = { 3662306a36Sopenharmony_ci { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, }, 3762306a36Sopenharmony_ci { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, }, 3862306a36Sopenharmony_ci { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, }, 3962306a36Sopenharmony_ci { SND_FF_CLOCK_SRC_WORD, 0x00001000, }, 4062306a36Sopenharmony_ci { SND_FF_CLOCK_SRC_LTC, 0x00001800, }, 4162306a36Sopenharmony_ci }; 4262306a36Sopenharmony_ci int i; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { 4562306a36Sopenharmony_ci rate_entry = rate_entries + i; 4662306a36Sopenharmony_ci if ((data & 0x0000001e) == rate_entry->mask) { 4762306a36Sopenharmony_ci *rate = rate_entry->rate; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci if (i == ARRAY_SIZE(rate_entries)) 5262306a36Sopenharmony_ci return -EIO; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (data & 0x00000001) { 5562306a36Sopenharmony_ci *src = SND_FF_CLOCK_SRC_INTERNAL; 5662306a36Sopenharmony_ci } else { 5762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { 5862306a36Sopenharmony_ci clk_entry = clk_entries + i; 5962306a36Sopenharmony_ci if ((data & 0x00001c00) == clk_entry->mask) { 6062306a36Sopenharmony_ci *src = clk_entry->src; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci if (i == ARRAY_SIZE(clk_entries)) 6562306a36Sopenharmony_ci return -EIO; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int former_get_clock(struct snd_ff *ff, unsigned int *rate, 7262306a36Sopenharmony_ci enum snd_ff_clock_src *src) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci __le32 reg; 7562306a36Sopenharmony_ci u32 data; 7662306a36Sopenharmony_ci int err; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, 7962306a36Sopenharmony_ci FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); 8062306a36Sopenharmony_ci if (err < 0) 8162306a36Sopenharmony_ci return err; 8262306a36Sopenharmony_ci data = le32_to_cpu(reg); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return parse_clock_bits(data, rate, src); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int former_switch_fetching_mode(struct snd_ff *ff, bool enable) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci unsigned int count; 9062306a36Sopenharmony_ci __le32 *reg; 9162306a36Sopenharmony_ci int i; 9262306a36Sopenharmony_ci int err; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci count = 0; 9562306a36Sopenharmony_ci for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) 9662306a36Sopenharmony_ci count = max(count, ff->spec->pcm_playback_channels[i]); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); 9962306a36Sopenharmony_ci if (!reg) 10062306a36Sopenharmony_ci return -ENOMEM; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!enable) { 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Each quadlet is corresponding to data channels in a data 10562306a36Sopenharmony_ci * blocks in reverse order. Precisely, quadlets for available 10662306a36Sopenharmony_ci * data channels should be enabled. Here, I take second best 10762306a36Sopenharmony_ci * to fetch PCM frames from all of data channels regardless of 10862306a36Sopenharmony_ci * stf. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci for (i = 0; i < count; ++i) 11162306a36Sopenharmony_ci reg[i] = cpu_to_le32(0x00000001); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, 11562306a36Sopenharmony_ci FORMER_REG_FETCH_PCM_FRAMES, reg, 11662306a36Sopenharmony_ci sizeof(__le32) * count, 0); 11762306a36Sopenharmony_ci kfree(reg); 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci __le32 reg; 12462306a36Sopenharmony_ci u32 data; 12562306a36Sopenharmony_ci unsigned int rate; 12662306a36Sopenharmony_ci enum snd_ff_clock_src src; 12762306a36Sopenharmony_ci const char *label; 12862306a36Sopenharmony_ci int err; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, 13162306a36Sopenharmony_ci FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); 13262306a36Sopenharmony_ci if (err < 0) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci data = le32_to_cpu(reg); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", 13762306a36Sopenharmony_ci (data & 0x00000020) ? "Professional" : "Consumer", 13862306a36Sopenharmony_ci (data & 0x00000040) ? "on" : "off"); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci snd_iprintf(buffer, "Optical output interface format: %s\n", 14162306a36Sopenharmony_ci (data & 0x00000100) ? "S/PDIF" : "ADAT"); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci snd_iprintf(buffer, "Word output single speed: %s\n", 14462306a36Sopenharmony_ci (data & 0x00002000) ? "on" : "off"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci snd_iprintf(buffer, "S/PDIF input interface: %s\n", 14762306a36Sopenharmony_ci (data & 0x00000200) ? "Optical" : "Coaxial"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci err = parse_clock_bits(data, &rate, &src); 15062306a36Sopenharmony_ci if (err < 0) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci label = snd_ff_proc_get_clk_label(src); 15362306a36Sopenharmony_ci if (!label) 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci static const struct { 16262306a36Sopenharmony_ci char *const label; 16362306a36Sopenharmony_ci u32 locked_mask; 16462306a36Sopenharmony_ci u32 synced_mask; 16562306a36Sopenharmony_ci } *clk_entry, clk_entries[] = { 16662306a36Sopenharmony_ci { "WDClk", 0x40000000, 0x20000000, }, 16762306a36Sopenharmony_ci { "S/PDIF", 0x00080000, 0x00040000, }, 16862306a36Sopenharmony_ci { "ADAT1", 0x00000400, 0x00001000, }, 16962306a36Sopenharmony_ci { "ADAT2", 0x00000800, 0x00002000, }, 17062306a36Sopenharmony_ci }; 17162306a36Sopenharmony_ci static const struct { 17262306a36Sopenharmony_ci char *const label; 17362306a36Sopenharmony_ci u32 mask; 17462306a36Sopenharmony_ci } *referred_entry, referred_entries[] = { 17562306a36Sopenharmony_ci { "ADAT1", 0x00000000, }, 17662306a36Sopenharmony_ci { "ADAT2", 0x00400000, }, 17762306a36Sopenharmony_ci { "S/PDIF", 0x00c00000, }, 17862306a36Sopenharmony_ci { "WDclk", 0x01000000, }, 17962306a36Sopenharmony_ci { "TCO", 0x01400000, }, 18062306a36Sopenharmony_ci }; 18162306a36Sopenharmony_ci static const struct { 18262306a36Sopenharmony_ci unsigned int rate; 18362306a36Sopenharmony_ci u32 mask; 18462306a36Sopenharmony_ci } *rate_entry, rate_entries[] = { 18562306a36Sopenharmony_ci { 32000, 0x02000000, }, 18662306a36Sopenharmony_ci { 44100, 0x04000000, }, 18762306a36Sopenharmony_ci { 48000, 0x06000000, }, 18862306a36Sopenharmony_ci { 64000, 0x08000000, }, 18962306a36Sopenharmony_ci { 88200, 0x0a000000, }, 19062306a36Sopenharmony_ci { 96000, 0x0c000000, }, 19162306a36Sopenharmony_ci { 128000, 0x0e000000, }, 19262306a36Sopenharmony_ci { 176400, 0x10000000, }, 19362306a36Sopenharmony_ci { 192000, 0x12000000, }, 19462306a36Sopenharmony_ci }; 19562306a36Sopenharmony_ci __le32 reg[2]; 19662306a36Sopenharmony_ci u32 data[2]; 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci int err; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, 20162306a36Sopenharmony_ci FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0); 20262306a36Sopenharmony_ci if (err < 0) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci data[0] = le32_to_cpu(reg[0]); 20562306a36Sopenharmony_ci data[1] = le32_to_cpu(reg[1]); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci snd_iprintf(buffer, "External source detection:\n"); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { 21062306a36Sopenharmony_ci const char *state; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci clk_entry = clk_entries + i; 21362306a36Sopenharmony_ci if (data[0] & clk_entry->locked_mask) { 21462306a36Sopenharmony_ci if (data[0] & clk_entry->synced_mask) 21562306a36Sopenharmony_ci state = "sync"; 21662306a36Sopenharmony_ci else 21762306a36Sopenharmony_ci state = "lock"; 21862306a36Sopenharmony_ci } else { 21962306a36Sopenharmony_ci state = "none"; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci snd_iprintf(buffer, "Referred clock:\n"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (data[1] & 0x00000001) { 22862306a36Sopenharmony_ci snd_iprintf(buffer, "Internal\n"); 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci unsigned int rate; 23162306a36Sopenharmony_ci const char *label; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) { 23462306a36Sopenharmony_ci referred_entry = referred_entries + i; 23562306a36Sopenharmony_ci if ((data[0] & 0x1e0000) == referred_entry->mask) { 23662306a36Sopenharmony_ci label = referred_entry->label; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (i == ARRAY_SIZE(referred_entries)) 24162306a36Sopenharmony_ci label = "none"; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { 24462306a36Sopenharmony_ci rate_entry = rate_entries + i; 24562306a36Sopenharmony_ci if ((data[0] & 0x1e000000) == rate_entry->mask) { 24662306a36Sopenharmony_ci rate = rate_entry->rate; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci if (i == ARRAY_SIZE(rate_entries)) 25162306a36Sopenharmony_ci rate = 0; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci snd_iprintf(buffer, "%s %d\n", label, rate); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void former_dump_status(struct snd_ff *ff, 25862306a36Sopenharmony_ci struct snd_info_buffer *buffer) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci dump_clock_config(ff, buffer); 26162306a36Sopenharmony_ci dump_sync_status(ff, buffer); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int former_fill_midi_msg(struct snd_ff *ff, 26562306a36Sopenharmony_ci struct snd_rawmidi_substream *substream, 26662306a36Sopenharmony_ci unsigned int port) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u8 *buf = (u8 *)ff->msg_buf[port]; 26962306a36Sopenharmony_ci int len; 27062306a36Sopenharmony_ci int i; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci len = snd_rawmidi_transmit_peek(substream, buf, 27362306a36Sopenharmony_ci SND_FF_MAXIMIM_MIDI_QUADS); 27462306a36Sopenharmony_ci if (len <= 0) 27562306a36Sopenharmony_ci return len; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci // One quadlet includes one byte. 27862306a36Sopenharmony_ci for (i = len - 1; i >= 0; --i) 27962306a36Sopenharmony_ci ff->msg_buf[port][i] = cpu_to_le32(buf[i]); 28062306a36Sopenharmony_ci ff->rx_bytes[port] = len; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return len; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci#define FF800_STF 0x0000fc88f000 28662306a36Sopenharmony_ci#define FF800_RX_PACKET_FORMAT 0x0000fc88f004 28762306a36Sopenharmony_ci#define FF800_ALLOC_TX_STREAM 0x0000fc88f008 28862306a36Sopenharmony_ci#define FF800_ISOC_COMM_START 0x0000fc88f00c 28962306a36Sopenharmony_ci#define FF800_TX_S800_FLAG 0x00000800 29062306a36Sopenharmony_ci#define FF800_ISOC_COMM_STOP 0x0000fc88f010 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int allocate_tx_resources(struct snd_ff *ff) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci __le32 reg; 29762306a36Sopenharmony_ci unsigned int count; 29862306a36Sopenharmony_ci unsigned int tx_isoc_channel; 29962306a36Sopenharmony_ci int err; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci reg = cpu_to_le32(ff->tx_stream.data_block_quadlets); 30262306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 30362306a36Sopenharmony_ci FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0); 30462306a36Sopenharmony_ci if (err < 0) 30562306a36Sopenharmony_ci return err; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci // Wait till the format of tx packet is available. 30862306a36Sopenharmony_ci count = 0; 30962306a36Sopenharmony_ci while (count++ < 10) { 31062306a36Sopenharmony_ci u32 data; 31162306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, 31262306a36Sopenharmony_ci FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0); 31362306a36Sopenharmony_ci if (err < 0) 31462306a36Sopenharmony_ci return err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci data = le32_to_cpu(reg); 31762306a36Sopenharmony_ci if (data != 0xffffffff) { 31862306a36Sopenharmony_ci tx_isoc_channel = data; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci msleep(50); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci if (count >= 10) 32562306a36Sopenharmony_ci return -ETIMEDOUT; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci // NOTE: this is a makeshift to start OHCI 1394 IR context in the 32862306a36Sopenharmony_ci // channel. On the other hand, 'struct fw_iso_resources.allocated' is 32962306a36Sopenharmony_ci // not true and it's not deallocated at stop. 33062306a36Sopenharmony_ci ff->tx_resources.channel = tx_isoc_channel; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci u32 data; 33862306a36Sopenharmony_ci __le32 reg; 33962306a36Sopenharmony_ci int err; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci reg = cpu_to_le32(rate); 34262306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 34362306a36Sopenharmony_ci FF800_STF, ®, sizeof(reg), 0); 34462306a36Sopenharmony_ci if (err < 0) 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci // If starting isochronous communication immediately, change of STF has 34862306a36Sopenharmony_ci // no effect. In this case, the communication runs based on former STF. 34962306a36Sopenharmony_ci // Let's sleep for a bit. 35062306a36Sopenharmony_ci msleep(100); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci // Controllers should allocate isochronous resources for rx stream. 35362306a36Sopenharmony_ci err = fw_iso_resources_allocate(&ff->rx_resources, 35462306a36Sopenharmony_ci amdtp_stream_get_max_payload(&ff->rx_stream), 35562306a36Sopenharmony_ci fw_parent_device(ff->unit)->max_speed); 35662306a36Sopenharmony_ci if (err < 0) 35762306a36Sopenharmony_ci return err; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci // Set isochronous channel and the number of quadlets of rx packets. 36062306a36Sopenharmony_ci // This should be done before the allocation of tx resources to avoid 36162306a36Sopenharmony_ci // periodical noise. 36262306a36Sopenharmony_ci data = ff->rx_stream.data_block_quadlets << 3; 36362306a36Sopenharmony_ci data = (data << 8) | ff->rx_resources.channel; 36462306a36Sopenharmony_ci reg = cpu_to_le32(data); 36562306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 36662306a36Sopenharmony_ci FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); 36762306a36Sopenharmony_ci if (err < 0) 36862306a36Sopenharmony_ci return err; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return allocate_tx_resources(ff); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int ff800_begin_session(struct snd_ff *ff, unsigned int rate) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci unsigned int generation = ff->rx_resources.generation; 37662306a36Sopenharmony_ci __le32 reg; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (generation != fw_parent_device(ff->unit)->card->generation) { 37962306a36Sopenharmony_ci int err = fw_iso_resources_update(&ff->rx_resources); 38062306a36Sopenharmony_ci if (err < 0) 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci reg = cpu_to_le32(0x80000000); 38562306a36Sopenharmony_ci reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); 38662306a36Sopenharmony_ci if (fw_parent_device(ff->unit)->max_speed == SCODE_800) 38762306a36Sopenharmony_ci reg |= cpu_to_le32(FF800_TX_S800_FLAG); 38862306a36Sopenharmony_ci return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 38962306a36Sopenharmony_ci FF800_ISOC_COMM_START, ®, sizeof(reg), 0); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void ff800_finish_session(struct snd_ff *ff) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci __le32 reg; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci reg = cpu_to_le32(0x80000000); 39762306a36Sopenharmony_ci snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 39862306a36Sopenharmony_ci FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination 40262306a36Sopenharmony_ci// address. 40362306a36Sopenharmony_ci// A write transaction to clear registered higher 4 bytes of destination address 40462306a36Sopenharmony_ci// has an effect to suppress asynchronous transaction from device. 40562306a36Sopenharmony_cistatic void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, 40662306a36Sopenharmony_ci size_t length, u32 tstamp) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int i; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci for (i = 0; i < length / 4; i++) { 41162306a36Sopenharmony_ci u8 byte = le32_to_cpu(buf[i]) & 0xff; 41262306a36Sopenharmony_ci struct snd_rawmidi_substream *substream; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci substream = READ_ONCE(ff->tx_midi_substreams[0]); 41562306a36Sopenharmony_ci if (substream) 41662306a36Sopenharmony_ci snd_rawmidi_receive(substream, &byte, 1); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ciconst struct snd_ff_protocol snd_ff_protocol_ff800 = { 42162306a36Sopenharmony_ci .handle_msg = ff800_handle_midi_msg, 42262306a36Sopenharmony_ci .fill_midi_msg = former_fill_midi_msg, 42362306a36Sopenharmony_ci .get_clock = former_get_clock, 42462306a36Sopenharmony_ci .switch_fetching_mode = former_switch_fetching_mode, 42562306a36Sopenharmony_ci .allocate_resources = ff800_allocate_resources, 42662306a36Sopenharmony_ci .begin_session = ff800_begin_session, 42762306a36Sopenharmony_ci .finish_session = ff800_finish_session, 42862306a36Sopenharmony_ci .dump_status = former_dump_status, 42962306a36Sopenharmony_ci}; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci#define FF400_STF 0x000080100500ull 43262306a36Sopenharmony_ci#define FF400_RX_PACKET_FORMAT 0x000080100504ull 43362306a36Sopenharmony_ci#define FF400_ISOC_COMM_START 0x000080100508ull 43462306a36Sopenharmony_ci#define FF400_TX_PACKET_FORMAT 0x00008010050cull 43562306a36Sopenharmony_ci#define FF400_ISOC_COMM_STOP 0x000080100510ull 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci// Fireface 400 manages isochronous channel number in 3 bit field. Therefore, 43862306a36Sopenharmony_ci// we can allocate between 0 and 7 channel. 43962306a36Sopenharmony_cistatic int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci __le32 reg; 44262306a36Sopenharmony_ci enum snd_ff_stream_mode mode; 44362306a36Sopenharmony_ci int i; 44462306a36Sopenharmony_ci int err; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci // Check whether the given value is supported or not. 44762306a36Sopenharmony_ci for (i = 0; i < CIP_SFC_COUNT; i++) { 44862306a36Sopenharmony_ci if (amdtp_rate_table[i] == rate) 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci if (i >= CIP_SFC_COUNT) 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci // Set the number of data blocks transferred in a second. 45562306a36Sopenharmony_ci reg = cpu_to_le32(rate); 45662306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 45762306a36Sopenharmony_ci FF400_STF, ®, sizeof(reg), 0); 45862306a36Sopenharmony_ci if (err < 0) 45962306a36Sopenharmony_ci return err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci msleep(100); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci err = snd_ff_stream_get_multiplier_mode(i, &mode); 46462306a36Sopenharmony_ci if (err < 0) 46562306a36Sopenharmony_ci return err; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci // Keep resources for in-stream. 46862306a36Sopenharmony_ci ff->tx_resources.channels_mask = 0x00000000000000ffuLL; 46962306a36Sopenharmony_ci err = fw_iso_resources_allocate(&ff->tx_resources, 47062306a36Sopenharmony_ci amdtp_stream_get_max_payload(&ff->tx_stream), 47162306a36Sopenharmony_ci fw_parent_device(ff->unit)->max_speed); 47262306a36Sopenharmony_ci if (err < 0) 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci // Keep resources for out-stream. 47662306a36Sopenharmony_ci ff->rx_resources.channels_mask = 0x00000000000000ffuLL; 47762306a36Sopenharmony_ci err = fw_iso_resources_allocate(&ff->rx_resources, 47862306a36Sopenharmony_ci amdtp_stream_get_max_payload(&ff->rx_stream), 47962306a36Sopenharmony_ci fw_parent_device(ff->unit)->max_speed); 48062306a36Sopenharmony_ci if (err < 0) 48162306a36Sopenharmony_ci fw_iso_resources_free(&ff->tx_resources); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int ff400_begin_session(struct snd_ff *ff, unsigned int rate) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci unsigned int generation = ff->rx_resources.generation; 48962306a36Sopenharmony_ci __le32 reg; 49062306a36Sopenharmony_ci int err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (generation != fw_parent_device(ff->unit)->card->generation) { 49362306a36Sopenharmony_ci err = fw_iso_resources_update(&ff->tx_resources); 49462306a36Sopenharmony_ci if (err < 0) 49562306a36Sopenharmony_ci return err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci err = fw_iso_resources_update(&ff->rx_resources); 49862306a36Sopenharmony_ci if (err < 0) 49962306a36Sopenharmony_ci return err; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci // Set isochronous channel and the number of quadlets of received 50362306a36Sopenharmony_ci // packets. 50462306a36Sopenharmony_ci reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | 50562306a36Sopenharmony_ci ff->rx_resources.channel); 50662306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 50762306a36Sopenharmony_ci FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); 50862306a36Sopenharmony_ci if (err < 0) 50962306a36Sopenharmony_ci return err; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci // Set isochronous channel and the number of quadlets of transmitted 51262306a36Sopenharmony_ci // packet. 51362306a36Sopenharmony_ci // TODO: investigate the purpose of this 0x80. 51462306a36Sopenharmony_ci reg = cpu_to_le32((0x80 << 24) | 51562306a36Sopenharmony_ci (ff->tx_resources.channel << 5) | 51662306a36Sopenharmony_ci (ff->tx_stream.data_block_quadlets)); 51762306a36Sopenharmony_ci err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 51862306a36Sopenharmony_ci FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); 51962306a36Sopenharmony_ci if (err < 0) 52062306a36Sopenharmony_ci return err; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci // Allow to transmit packets. 52362306a36Sopenharmony_ci reg = cpu_to_le32(0x00000001); 52462306a36Sopenharmony_ci return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 52562306a36Sopenharmony_ci FF400_ISOC_COMM_START, ®, sizeof(reg), 0); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void ff400_finish_session(struct snd_ff *ff) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci __le32 reg; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci reg = cpu_to_le32(0x80000000); 53362306a36Sopenharmony_ci snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, 53462306a36Sopenharmony_ci FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (substream != NULL) { 54262306a36Sopenharmony_ci u8 byte = (quad >> (16 * port)) & 0x000000ff; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci snd_rawmidi_receive(substream, &byte, 1); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci#define FF400_QUEUE_SIZE 32 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistruct ff400_msg_parser { 55162306a36Sopenharmony_ci struct { 55262306a36Sopenharmony_ci u32 msg; 55362306a36Sopenharmony_ci u32 tstamp; 55462306a36Sopenharmony_ci } msgs[FF400_QUEUE_SIZE]; 55562306a36Sopenharmony_ci size_t push_pos; 55662306a36Sopenharmony_ci size_t pull_pos; 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic bool ff400_has_msg(struct snd_ff *ff) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct ff400_msg_parser *parser = ff->msg_parser; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return (parser->push_pos != parser->pull_pos); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci// For Fireface 400, lower 4 bytes of destination address is configured by bit 56762306a36Sopenharmony_ci// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can 56862306a36Sopenharmony_ci// select one of 4 options: 56962306a36Sopenharmony_ci// 57062306a36Sopenharmony_ci// bit flags: offset of destination address 57162306a36Sopenharmony_ci// - 0x04000000: 0x'....'....'0000'0000 57262306a36Sopenharmony_ci// - 0x08000000: 0x'....'....'0000'0080 57362306a36Sopenharmony_ci// - 0x10000000: 0x'....'....'0000'0100 57462306a36Sopenharmony_ci// - 0x20000000: 0x'....'....'0000'0180 57562306a36Sopenharmony_ci// 57662306a36Sopenharmony_ci// Drivers can suppress the device to transfer asynchronous transactions by 57762306a36Sopenharmony_ci// using below 2 bits. 57862306a36Sopenharmony_ci// - 0x01000000: suppress transmission 57962306a36Sopenharmony_ci// - 0x02000000: suppress transmission 58062306a36Sopenharmony_ci// 58162306a36Sopenharmony_ci// Actually, the register is write-only and includes the other options such as 58262306a36Sopenharmony_ci// input attenuation. This driver allocates destination address with '0000'0000 58362306a36Sopenharmony_ci// in its lower offset and expects userspace application to configure the 58462306a36Sopenharmony_ci// register for it. 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of 58762306a36Sopenharmony_ci// stereo physical port. 58862306a36Sopenharmony_ci// - 0: Microphone input 0/1 58962306a36Sopenharmony_ci// - 1: Line input 0/1 59062306a36Sopenharmony_ci// - [2-4]: Line output 0-5 59162306a36Sopenharmony_ci// - 5: Headphone output 0/1 59262306a36Sopenharmony_ci// - 6: S/PDIF output 0/1 59362306a36Sopenharmony_ci// - [7-10]: ADAT output 0-7 59462306a36Sopenharmony_ci// 59562306a36Sopenharmony_ci// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone 59662306a36Sopenharmony_ci// input: 59762306a36Sopenharmony_ci// 59862306a36Sopenharmony_ci// - 0: 0.0 dB 59962306a36Sopenharmony_ci// - 10: +10.0 dB 60062306a36Sopenharmony_ci// - 11: +11.0 dB 60162306a36Sopenharmony_ci// - 12: +12.0 dB 60262306a36Sopenharmony_ci// - ... 60362306a36Sopenharmony_ci// - 63: +63.0 dB: 60462306a36Sopenharmony_ci// - 64: +64.0 dB: 60562306a36Sopenharmony_ci// - 65: +65.0 dB: 60662306a36Sopenharmony_ci// 60762306a36Sopenharmony_ci// For signal level of line input: 60862306a36Sopenharmony_ci// 60962306a36Sopenharmony_ci// - 0: 0.0 dB 61062306a36Sopenharmony_ci// - 1: +0.5 dB 61162306a36Sopenharmony_ci// - 2: +1.0 dB 61262306a36Sopenharmony_ci// - 3: +1.5 dB 61362306a36Sopenharmony_ci// - ... 61462306a36Sopenharmony_ci// - 34: +17.0 dB: 61562306a36Sopenharmony_ci// - 35: +17.5 dB: 61662306a36Sopenharmony_ci// - 36: +18.0 dB: 61762306a36Sopenharmony_ci// 61862306a36Sopenharmony_ci// For signal level of any type of output: 61962306a36Sopenharmony_ci// 62062306a36Sopenharmony_ci// - 63: -infinite 62162306a36Sopenharmony_ci// - 62: -58.0 dB 62262306a36Sopenharmony_ci// - 61: -56.0 dB 62362306a36Sopenharmony_ci// - 60: -54.0 dB 62462306a36Sopenharmony_ci// - 59: -53.0 dB 62562306a36Sopenharmony_ci// - 58: -52.0 dB 62662306a36Sopenharmony_ci// - ... 62762306a36Sopenharmony_ci// - 7: -1.0 dB 62862306a36Sopenharmony_ci// - 6: 0.0 dB 62962306a36Sopenharmony_ci// - 5: +1.0 dB 63062306a36Sopenharmony_ci// - ... 63162306a36Sopenharmony_ci// - 2: +4.0 dB 63262306a36Sopenharmony_ci// - 1: +5.0 dB 63362306a36Sopenharmony_ci// - 0: +6.0 dB 63462306a36Sopenharmony_ci// 63562306a36Sopenharmony_ci// When the message is not for signal level operation, it's for MIDI bytes. When matching to 63662306a36Sopenharmony_ci// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When 63762306a36Sopenharmony_ci// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000. 63862306a36Sopenharmony_ci#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000 63962306a36Sopenharmony_ci#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000 64062306a36Sopenharmony_ci#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000 64162306a36Sopenharmony_ci#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000 64262306a36Sopenharmony_ci#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00 64362306a36Sopenharmony_ci#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 64462306a36Sopenharmony_ci#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff 64562306a36Sopenharmony_ci#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 64662306a36Sopenharmony_ci#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, 64962306a36Sopenharmony_ci size_t length, u32 tstamp) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci bool need_hwdep_wake_up = false; 65262306a36Sopenharmony_ci int i; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci for (i = 0; i < length / 4; i++) { 65562306a36Sopenharmony_ci u32 quad = le32_to_cpu(buf[i]); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) { 65862306a36Sopenharmony_ci struct ff400_msg_parser *parser = ff->msg_parser; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci parser->msgs[parser->push_pos].msg = quad; 66162306a36Sopenharmony_ci parser->msgs[parser->push_pos].tstamp = tstamp; 66262306a36Sopenharmony_ci ++parser->push_pos; 66362306a36Sopenharmony_ci if (parser->push_pos >= FF400_QUEUE_SIZE) 66462306a36Sopenharmony_ci parser->push_pos = 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci need_hwdep_wake_up = true; 66762306a36Sopenharmony_ci } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) { 66862306a36Sopenharmony_ci parse_midi_msg(ff, quad, 0); 66962306a36Sopenharmony_ci } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) { 67062306a36Sopenharmony_ci parse_midi_msg(ff, quad, 1); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (need_hwdep_wake_up) 67562306a36Sopenharmony_ci wake_up(&ff->hwdep_wait); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct snd_firewire_event_ff400_message ev = { 68162306a36Sopenharmony_ci .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE, 68262306a36Sopenharmony_ci .message_count = 0, 68362306a36Sopenharmony_ci }; 68462306a36Sopenharmony_ci struct ff400_msg_parser *parser = ff->msg_parser; 68562306a36Sopenharmony_ci long consumed = 0; 68662306a36Sopenharmony_ci long ret = 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (count < sizeof(ev) || parser->pull_pos == parser->push_pos) 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci count -= sizeof(ev); 69262306a36Sopenharmony_ci consumed += sizeof(ev); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) { 69562306a36Sopenharmony_ci spin_unlock_irq(&ff->lock); 69662306a36Sopenharmony_ci if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos, 69762306a36Sopenharmony_ci sizeof(*parser->msgs))) 69862306a36Sopenharmony_ci ret = -EFAULT; 69962306a36Sopenharmony_ci spin_lock_irq(&ff->lock); 70062306a36Sopenharmony_ci if (ret) 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ++parser->pull_pos; 70462306a36Sopenharmony_ci if (parser->pull_pos >= FF400_QUEUE_SIZE) 70562306a36Sopenharmony_ci parser->pull_pos = 0; 70662306a36Sopenharmony_ci ++ev.message_count; 70762306a36Sopenharmony_ci count -= sizeof(*parser->msgs); 70862306a36Sopenharmony_ci consumed += sizeof(*parser->msgs); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_unlock_irq(&ff->lock); 71262306a36Sopenharmony_ci if (copy_to_user(buf, &ev, sizeof(ev))) 71362306a36Sopenharmony_ci ret = -EFAULT; 71462306a36Sopenharmony_ci spin_lock_irq(&ff->lock); 71562306a36Sopenharmony_ci if (ret) 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return consumed; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ciconst struct snd_ff_protocol snd_ff_protocol_ff400 = { 72262306a36Sopenharmony_ci .msg_parser_size = sizeof(struct ff400_msg_parser), 72362306a36Sopenharmony_ci .has_msg = ff400_has_msg, 72462306a36Sopenharmony_ci .copy_msg_to_user = ff400_copy_msg_to_user, 72562306a36Sopenharmony_ci .handle_msg = ff400_handle_msg, 72662306a36Sopenharmony_ci .fill_midi_msg = former_fill_midi_msg, 72762306a36Sopenharmony_ci .get_clock = former_get_clock, 72862306a36Sopenharmony_ci .switch_fetching_mode = former_switch_fetching_mode, 72962306a36Sopenharmony_ci .allocate_resources = ff400_allocate_resources, 73062306a36Sopenharmony_ci .begin_session = ff400_begin_session, 73162306a36Sopenharmony_ci .finish_session = ff400_finish_session, 73262306a36Sopenharmony_ci .dump_status = former_dump_status, 73362306a36Sopenharmony_ci}; 734