162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bebob_stream.c - a part of driver for BeBoB based devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "./bebob.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define READY_TIMEOUT_MS 4000 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * NOTE; 1462306a36Sopenharmony_ci * For BeBoB streams, Both of input and output CMP connection are important. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * For most devices, each CMP connection starts to transmit/receive a 1762306a36Sopenharmony_ci * corresponding stream. But for a few devices, both of CMP connection needs 1862306a36Sopenharmony_ci * to start transmitting stream. An example is 'M-Audio Firewire 410'. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 128 is an arbitrary length but it seems to be enough */ 2262306a36Sopenharmony_ci#define FORMAT_MAXIMUM_LENGTH 128 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciconst unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = { 2562306a36Sopenharmony_ci [0] = 32000, 2662306a36Sopenharmony_ci [1] = 44100, 2762306a36Sopenharmony_ci [2] = 48000, 2862306a36Sopenharmony_ci [3] = 88200, 2962306a36Sopenharmony_ci [4] = 96000, 3062306a36Sopenharmony_ci [5] = 176400, 3162306a36Sopenharmony_ci [6] = 192000, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * See: Table 51: Extended Stream Format Info ‘Sampling Frequency’ 3662306a36Sopenharmony_ci * in Additional AVC commands (Nov 2003, BridgeCo) 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic const unsigned int bridgeco_freq_table[] = { 3962306a36Sopenharmony_ci [0] = 0x02, 4062306a36Sopenharmony_ci [1] = 0x03, 4162306a36Sopenharmony_ci [2] = 0x04, 4262306a36Sopenharmony_ci [3] = 0x0a, 4362306a36Sopenharmony_ci [4] = 0x05, 4462306a36Sopenharmony_ci [5] = 0x06, 4562306a36Sopenharmony_ci [6] = 0x07, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int 4962306a36Sopenharmony_ciget_formation_index(unsigned int rate, unsigned int *index) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci unsigned int i; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) { 5462306a36Sopenharmony_ci if (snd_bebob_rate_table[i] == rate) { 5562306a36Sopenharmony_ci *index = i; 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint 6362306a36Sopenharmony_cisnd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned int tx_rate, rx_rate, trials; 6662306a36Sopenharmony_ci int err; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci trials = 0; 6962306a36Sopenharmony_ci do { 7062306a36Sopenharmony_ci err = avc_general_get_sig_fmt(bebob->unit, &tx_rate, 7162306a36Sopenharmony_ci AVC_GENERAL_PLUG_DIR_OUT, 0); 7262306a36Sopenharmony_ci } while (err == -EAGAIN && ++trials < 3); 7362306a36Sopenharmony_ci if (err < 0) 7462306a36Sopenharmony_ci goto end; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci trials = 0; 7762306a36Sopenharmony_ci do { 7862306a36Sopenharmony_ci err = avc_general_get_sig_fmt(bebob->unit, &rx_rate, 7962306a36Sopenharmony_ci AVC_GENERAL_PLUG_DIR_IN, 0); 8062306a36Sopenharmony_ci } while (err == -EAGAIN && ++trials < 3); 8162306a36Sopenharmony_ci if (err < 0) 8262306a36Sopenharmony_ci goto end; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci *curr_rate = rx_rate; 8562306a36Sopenharmony_ci if (rx_rate == tx_rate) 8662306a36Sopenharmony_ci goto end; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* synchronize receive stream rate to transmit stream rate */ 8962306a36Sopenharmony_ci err = avc_general_set_sig_fmt(bebob->unit, rx_rate, 9062306a36Sopenharmony_ci AVC_GENERAL_PLUG_DIR_IN, 0); 9162306a36Sopenharmony_ciend: 9262306a36Sopenharmony_ci return err; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciint 9662306a36Sopenharmony_cisnd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int err; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = avc_general_set_sig_fmt(bebob->unit, rate, 10162306a36Sopenharmony_ci AVC_GENERAL_PLUG_DIR_OUT, 0); 10262306a36Sopenharmony_ci if (err < 0) 10362306a36Sopenharmony_ci goto end; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci err = avc_general_set_sig_fmt(bebob->unit, rate, 10662306a36Sopenharmony_ci AVC_GENERAL_PLUG_DIR_IN, 0); 10762306a36Sopenharmony_ci if (err < 0) 10862306a36Sopenharmony_ci goto end; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* 11162306a36Sopenharmony_ci * Some devices need a bit time for transition. 11262306a36Sopenharmony_ci * 300msec is got by some experiments. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci msleep(300); 11562306a36Sopenharmony_ciend: 11662306a36Sopenharmony_ci return err; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciint snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, 12062306a36Sopenharmony_ci enum snd_bebob_clock_type *src) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; 12362306a36Sopenharmony_ci u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7]; 12462306a36Sopenharmony_ci unsigned int id; 12562306a36Sopenharmony_ci enum avc_bridgeco_plug_type type; 12662306a36Sopenharmony_ci int err = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 1.The device has its own operation to switch source of clock */ 12962306a36Sopenharmony_ci if (clk_spec) { 13062306a36Sopenharmony_ci err = clk_spec->get(bebob, &id); 13162306a36Sopenharmony_ci if (err < 0) { 13262306a36Sopenharmony_ci dev_err(&bebob->unit->device, 13362306a36Sopenharmony_ci "fail to get clock source: %d\n", err); 13462306a36Sopenharmony_ci goto end; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (id >= clk_spec->num) { 13862306a36Sopenharmony_ci dev_err(&bebob->unit->device, 13962306a36Sopenharmony_ci "clock source %d out of range 0..%d\n", 14062306a36Sopenharmony_ci id, clk_spec->num - 1); 14162306a36Sopenharmony_ci err = -EIO; 14262306a36Sopenharmony_ci goto end; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci *src = clk_spec->types[id]; 14662306a36Sopenharmony_ci goto end; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * 2.The device don't support to switch source of clock then assumed 15162306a36Sopenharmony_ci * to use internal clock always 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci if (bebob->sync_input_plug < 0) { 15462306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; 15562306a36Sopenharmony_ci goto end; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * 3.The device supports to switch source of clock by an usual way. 16062306a36Sopenharmony_ci * Let's check input for 'Music Sub Unit Sync Input' plug. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, 16362306a36Sopenharmony_ci bebob->sync_input_plug); 16462306a36Sopenharmony_ci err = avc_bridgeco_get_plug_input(bebob->unit, addr, input); 16562306a36Sopenharmony_ci if (err < 0) { 16662306a36Sopenharmony_ci dev_err(&bebob->unit->device, 16762306a36Sopenharmony_ci "fail to get an input for MSU in plug %d: %d\n", 16862306a36Sopenharmony_ci bebob->sync_input_plug, err); 16962306a36Sopenharmony_ci goto end; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * If there are no input plugs, all of fields are 0xff. 17462306a36Sopenharmony_ci * Here check the first field. This field is used for direction. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci if (input[0] == 0xff) { 17762306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; 17862306a36Sopenharmony_ci goto end; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* The source from any output plugs is for one purpose only. */ 18262306a36Sopenharmony_ci if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) { 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * In BeBoB architecture, the source from music subunit may 18562306a36Sopenharmony_ci * bypass from oPCR[0]. This means that this source gives 18662306a36Sopenharmony_ci * synchronization to IEEE 1394 cycle start packet. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT && 18962306a36Sopenharmony_ci input[2] == 0x0c) { 19062306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; 19162306a36Sopenharmony_ci goto end; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci /* The source from any input units is for several purposes. */ 19462306a36Sopenharmony_ci } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) { 19562306a36Sopenharmony_ci if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) { 19662306a36Sopenharmony_ci if (input[3] == 0x00) { 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * This source comes from iPCR[0]. This means 19962306a36Sopenharmony_ci * that presentation timestamp calculated by 20062306a36Sopenharmony_ci * SYT series of the received packets. In 20162306a36Sopenharmony_ci * short, this driver is the master of 20262306a36Sopenharmony_ci * synchronization. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_SYT; 20562306a36Sopenharmony_ci goto end; 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * This source comes from iPCR[1-29]. This 20962306a36Sopenharmony_ci * means that the synchronization stream is not 21062306a36Sopenharmony_ci * the Audio/MIDI compound stream. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; 21362306a36Sopenharmony_ci goto end; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) { 21662306a36Sopenharmony_ci /* Check type of this plug. */ 21762306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, 21862306a36Sopenharmony_ci AVC_BRIDGECO_PLUG_DIR_IN, 21962306a36Sopenharmony_ci AVC_BRIDGECO_PLUG_UNIT_EXT, 22062306a36Sopenharmony_ci input[3]); 22162306a36Sopenharmony_ci err = avc_bridgeco_get_plug_type(bebob->unit, addr, 22262306a36Sopenharmony_ci &type); 22362306a36Sopenharmony_ci if (err < 0) 22462306a36Sopenharmony_ci goto end; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) { 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * SPDIF/ADAT or sometimes (not always) word 22962306a36Sopenharmony_ci * clock. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; 23262306a36Sopenharmony_ci goto end; 23362306a36Sopenharmony_ci } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) { 23462306a36Sopenharmony_ci /* Often word clock. */ 23562306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; 23662306a36Sopenharmony_ci goto end; 23762306a36Sopenharmony_ci } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) { 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * Not standard. 24062306a36Sopenharmony_ci * Mostly, additional internal clock. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; 24362306a36Sopenharmony_ci goto end; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Not supported. */ 24962306a36Sopenharmony_ci err = -EIO; 25062306a36Sopenharmony_ciend: 25162306a36Sopenharmony_ci return err; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unsigned int sec, sections, ch, channels; 25762306a36Sopenharmony_ci unsigned int pcm, midi, location; 25862306a36Sopenharmony_ci unsigned int stm_pos, sec_loc, pos; 25962306a36Sopenharmony_ci u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type; 26062306a36Sopenharmony_ci enum avc_bridgeco_plug_dir dir; 26162306a36Sopenharmony_ci int err; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * The length of return value of this command cannot be expected. Here 26562306a36Sopenharmony_ci * use the maximum length of FCP. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci buf = kzalloc(256, GFP_KERNEL); 26862306a36Sopenharmony_ci if (buf == NULL) 26962306a36Sopenharmony_ci return -ENOMEM; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (s == &bebob->tx_stream) 27262306a36Sopenharmony_ci dir = AVC_BRIDGECO_PLUG_DIR_OUT; 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci dir = AVC_BRIDGECO_PLUG_DIR_IN; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0); 27762306a36Sopenharmony_ci err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256); 27862306a36Sopenharmony_ci if (err < 0) { 27962306a36Sopenharmony_ci dev_err(&bebob->unit->device, 28062306a36Sopenharmony_ci "fail to get channel position for isoc %s plug 0: %d\n", 28162306a36Sopenharmony_ci (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : "out", 28262306a36Sopenharmony_ci err); 28362306a36Sopenharmony_ci goto end; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci pos = 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* positions in I/O buffer */ 28862306a36Sopenharmony_ci pcm = 0; 28962306a36Sopenharmony_ci midi = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* the number of sections in AMDTP packet */ 29262306a36Sopenharmony_ci sections = buf[pos++]; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci for (sec = 0; sec < sections; sec++) { 29562306a36Sopenharmony_ci /* type of this section */ 29662306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, dir, 29762306a36Sopenharmony_ci AVC_BRIDGECO_PLUG_UNIT_ISOC, 0); 29862306a36Sopenharmony_ci err = avc_bridgeco_get_plug_section_type(bebob->unit, addr, 29962306a36Sopenharmony_ci sec, &type); 30062306a36Sopenharmony_ci if (err < 0) { 30162306a36Sopenharmony_ci dev_err(&bebob->unit->device, 30262306a36Sopenharmony_ci "fail to get section type for isoc %s plug 0: %d\n", 30362306a36Sopenharmony_ci (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : 30462306a36Sopenharmony_ci "out", 30562306a36Sopenharmony_ci err); 30662306a36Sopenharmony_ci goto end; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci /* NoType */ 30962306a36Sopenharmony_ci if (type == 0xff) { 31062306a36Sopenharmony_ci err = -ENOSYS; 31162306a36Sopenharmony_ci goto end; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* the number of channels in this section */ 31562306a36Sopenharmony_ci channels = buf[pos++]; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for (ch = 0; ch < channels; ch++) { 31862306a36Sopenharmony_ci /* position of this channel in AMDTP packet */ 31962306a36Sopenharmony_ci stm_pos = buf[pos++] - 1; 32062306a36Sopenharmony_ci /* location of this channel in this section */ 32162306a36Sopenharmony_ci sec_loc = buf[pos++] - 1; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * Basically the number of location is within the 32562306a36Sopenharmony_ci * number of channels in this section. But some models 32662306a36Sopenharmony_ci * of M-Audio don't follow this. Its location for MIDI 32762306a36Sopenharmony_ci * is the position of MIDI channels in AMDTP packet. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci if (sec_loc >= channels) 33062306a36Sopenharmony_ci sec_loc = ch; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci switch (type) { 33362306a36Sopenharmony_ci /* for MIDI conformant data channel */ 33462306a36Sopenharmony_ci case 0x0a: 33562306a36Sopenharmony_ci /* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */ 33662306a36Sopenharmony_ci if ((midi > 0) && (stm_pos != midi)) { 33762306a36Sopenharmony_ci err = -ENOSYS; 33862306a36Sopenharmony_ci goto end; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci amdtp_am824_set_midi_position(s, stm_pos); 34162306a36Sopenharmony_ci midi = stm_pos; 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci /* for PCM data channel */ 34462306a36Sopenharmony_ci case 0x01: /* Headphone */ 34562306a36Sopenharmony_ci case 0x02: /* Microphone */ 34662306a36Sopenharmony_ci case 0x03: /* Line */ 34762306a36Sopenharmony_ci case 0x04: /* SPDIF */ 34862306a36Sopenharmony_ci case 0x05: /* ADAT */ 34962306a36Sopenharmony_ci case 0x06: /* TDIF */ 35062306a36Sopenharmony_ci case 0x07: /* MADI */ 35162306a36Sopenharmony_ci /* for undefined/changeable signal */ 35262306a36Sopenharmony_ci case 0x08: /* Analog */ 35362306a36Sopenharmony_ci case 0x09: /* Digital */ 35462306a36Sopenharmony_ci default: 35562306a36Sopenharmony_ci location = pcm + sec_loc; 35662306a36Sopenharmony_ci if (location >= AM824_MAX_CHANNELS_FOR_PCM) { 35762306a36Sopenharmony_ci err = -ENOSYS; 35862306a36Sopenharmony_ci goto end; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci amdtp_am824_set_pcm_position(s, location, 36162306a36Sopenharmony_ci stm_pos); 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (type != 0x0a) 36762306a36Sopenharmony_ci pcm += channels; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci midi += channels; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ciend: 37262306a36Sopenharmony_ci kfree(buf); 37362306a36Sopenharmony_ci return err; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int 37762306a36Sopenharmony_cicheck_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct cmp_connection *conn; 38062306a36Sopenharmony_ci bool used; 38162306a36Sopenharmony_ci int err; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (s == &bebob->tx_stream) 38462306a36Sopenharmony_ci conn = &bebob->out_conn; 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci conn = &bebob->in_conn; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci err = cmp_connection_check_used(conn, &used); 38962306a36Sopenharmony_ci if ((err >= 0) && used && !amdtp_stream_running(s)) { 39062306a36Sopenharmony_ci dev_err(&bebob->unit->device, 39162306a36Sopenharmony_ci "Connection established by others: %cPCR[%d]\n", 39262306a36Sopenharmony_ci (conn->direction == CMP_OUTPUT) ? 'o' : 'i', 39362306a36Sopenharmony_ci conn->pcr_index); 39462306a36Sopenharmony_ci err = -EBUSY; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return err; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void break_both_connections(struct snd_bebob *bebob) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci cmp_connection_break(&bebob->in_conn); 40362306a36Sopenharmony_ci cmp_connection_break(&bebob->out_conn); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct cmp_connection *conn; 40962306a36Sopenharmony_ci int err = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (stream == &bebob->rx_stream) 41262306a36Sopenharmony_ci conn = &bebob->in_conn; 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci conn = &bebob->out_conn; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci // channel mapping. 41762306a36Sopenharmony_ci if (bebob->maudio_special_quirk == NULL) { 41862306a36Sopenharmony_ci err = map_data_channels(bebob, stream); 41962306a36Sopenharmony_ci if (err < 0) 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci err = cmp_connection_establish(conn); 42462306a36Sopenharmony_ci if (err < 0) 42562306a36Sopenharmony_ci return err; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return amdtp_domain_add_stream(&bebob->domain, stream, 42862306a36Sopenharmony_ci conn->resources.channel, conn->speed); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci unsigned int flags = CIP_BLOCKING; 43462306a36Sopenharmony_ci enum amdtp_stream_direction dir_stream; 43562306a36Sopenharmony_ci struct cmp_connection *conn; 43662306a36Sopenharmony_ci enum cmp_direction dir_conn; 43762306a36Sopenharmony_ci int err; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (stream == &bebob->tx_stream) { 44062306a36Sopenharmony_ci dir_stream = AMDTP_IN_STREAM; 44162306a36Sopenharmony_ci conn = &bebob->out_conn; 44262306a36Sopenharmony_ci dir_conn = CMP_OUTPUT; 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci dir_stream = AMDTP_OUT_STREAM; 44562306a36Sopenharmony_ci conn = &bebob->in_conn; 44662306a36Sopenharmony_ci dir_conn = CMP_INPUT; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (stream == &bebob->tx_stream) { 45062306a36Sopenharmony_ci if (bebob->quirks & SND_BEBOB_QUIRK_WRONG_DBC) 45162306a36Sopenharmony_ci flags |= CIP_EMPTY_HAS_WRONG_DBC; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci err = cmp_connection_init(conn, bebob->unit, dir_conn, 0); 45562306a36Sopenharmony_ci if (err < 0) 45662306a36Sopenharmony_ci return err; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci err = amdtp_am824_init(stream, bebob->unit, dir_stream, flags); 45962306a36Sopenharmony_ci if (err < 0) { 46062306a36Sopenharmony_ci cmp_connection_destroy(conn); 46162306a36Sopenharmony_ci return err; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci amdtp_stream_destroy(stream); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (stream == &bebob->tx_stream) 47262306a36Sopenharmony_ci cmp_connection_destroy(&bebob->out_conn); 47362306a36Sopenharmony_ci else 47462306a36Sopenharmony_ci cmp_connection_destroy(&bebob->in_conn); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciint snd_bebob_stream_init_duplex(struct snd_bebob *bebob) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci int err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci err = init_stream(bebob, &bebob->tx_stream); 48262306a36Sopenharmony_ci if (err < 0) 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = init_stream(bebob, &bebob->rx_stream); 48662306a36Sopenharmony_ci if (err < 0) { 48762306a36Sopenharmony_ci destroy_stream(bebob, &bebob->tx_stream); 48862306a36Sopenharmony_ci return err; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci err = amdtp_domain_init(&bebob->domain); 49262306a36Sopenharmony_ci if (err < 0) { 49362306a36Sopenharmony_ci destroy_stream(bebob, &bebob->tx_stream); 49462306a36Sopenharmony_ci destroy_stream(bebob, &bebob->rx_stream); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return err; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, 50162306a36Sopenharmony_ci unsigned int rate, unsigned int index) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci unsigned int pcm_channels; 50462306a36Sopenharmony_ci unsigned int midi_ports; 50562306a36Sopenharmony_ci struct cmp_connection *conn; 50662306a36Sopenharmony_ci int err; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (stream == &bebob->tx_stream) { 50962306a36Sopenharmony_ci pcm_channels = bebob->tx_stream_formations[index].pcm; 51062306a36Sopenharmony_ci midi_ports = bebob->midi_input_ports; 51162306a36Sopenharmony_ci conn = &bebob->out_conn; 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci pcm_channels = bebob->rx_stream_formations[index].pcm; 51462306a36Sopenharmony_ci midi_ports = bebob->midi_output_ports; 51562306a36Sopenharmony_ci conn = &bebob->in_conn; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, false); 51962306a36Sopenharmony_ci if (err < 0) 52062306a36Sopenharmony_ci return err; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciint snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, 52662306a36Sopenharmony_ci unsigned int frames_per_period, 52762306a36Sopenharmony_ci unsigned int frames_per_buffer) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci unsigned int curr_rate; 53062306a36Sopenharmony_ci int err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci // Considering JACK/FFADO streaming: 53362306a36Sopenharmony_ci // TODO: This can be removed hwdep functionality becomes popular. 53462306a36Sopenharmony_ci err = check_connection_used_by_others(bebob, &bebob->rx_stream); 53562306a36Sopenharmony_ci if (err < 0) 53662306a36Sopenharmony_ci return err; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci err = bebob->spec->rate->get(bebob, &curr_rate); 53962306a36Sopenharmony_ci if (err < 0) 54062306a36Sopenharmony_ci return err; 54162306a36Sopenharmony_ci if (rate == 0) 54262306a36Sopenharmony_ci rate = curr_rate; 54362306a36Sopenharmony_ci if (curr_rate != rate) { 54462306a36Sopenharmony_ci amdtp_domain_stop(&bebob->domain); 54562306a36Sopenharmony_ci break_both_connections(bebob); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci cmp_connection_release(&bebob->out_conn); 54862306a36Sopenharmony_ci cmp_connection_release(&bebob->in_conn); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (bebob->substreams_counter == 0 || curr_rate != rate) { 55262306a36Sopenharmony_ci unsigned int index; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci // NOTE: 55562306a36Sopenharmony_ci // If establishing connections at first, Yamaha GO46 55662306a36Sopenharmony_ci // (and maybe Terratec X24) don't generate sound. 55762306a36Sopenharmony_ci // 55862306a36Sopenharmony_ci // For firmware customized by M-Audio, refer to next NOTE. 55962306a36Sopenharmony_ci err = bebob->spec->rate->set(bebob, rate); 56062306a36Sopenharmony_ci if (err < 0) { 56162306a36Sopenharmony_ci dev_err(&bebob->unit->device, 56262306a36Sopenharmony_ci "fail to set sampling rate: %d\n", 56362306a36Sopenharmony_ci err); 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err = get_formation_index(rate, &index); 56862306a36Sopenharmony_ci if (err < 0) 56962306a36Sopenharmony_ci return err; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci err = keep_resources(bebob, &bebob->tx_stream, rate, index); 57262306a36Sopenharmony_ci if (err < 0) 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci err = keep_resources(bebob, &bebob->rx_stream, rate, index); 57662306a36Sopenharmony_ci if (err < 0) { 57762306a36Sopenharmony_ci cmp_connection_release(&bebob->out_conn); 57862306a36Sopenharmony_ci return err; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci err = amdtp_domain_set_events_per_period(&bebob->domain, 58262306a36Sopenharmony_ci frames_per_period, frames_per_buffer); 58362306a36Sopenharmony_ci if (err < 0) { 58462306a36Sopenharmony_ci cmp_connection_release(&bebob->out_conn); 58562306a36Sopenharmony_ci cmp_connection_release(&bebob->in_conn); 58662306a36Sopenharmony_ci return err; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciint snd_bebob_stream_start_duplex(struct snd_bebob *bebob) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci int err; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci // Need no substreams. 59862306a36Sopenharmony_ci if (bebob->substreams_counter == 0) 59962306a36Sopenharmony_ci return -EIO; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci // packet queueing error or detecting discontinuity 60262306a36Sopenharmony_ci if (amdtp_streaming_error(&bebob->rx_stream) || 60362306a36Sopenharmony_ci amdtp_streaming_error(&bebob->tx_stream)) { 60462306a36Sopenharmony_ci amdtp_domain_stop(&bebob->domain); 60562306a36Sopenharmony_ci break_both_connections(bebob); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!amdtp_stream_running(&bebob->rx_stream)) { 60962306a36Sopenharmony_ci enum snd_bebob_clock_type src; 61062306a36Sopenharmony_ci unsigned int curr_rate; 61162306a36Sopenharmony_ci unsigned int tx_init_skip_cycles; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (bebob->maudio_special_quirk) { 61462306a36Sopenharmony_ci err = bebob->spec->rate->get(bebob, &curr_rate); 61562306a36Sopenharmony_ci if (err < 0) 61662306a36Sopenharmony_ci return err; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci err = snd_bebob_stream_get_clock_src(bebob, &src); 62062306a36Sopenharmony_ci if (err < 0) 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = start_stream(bebob, &bebob->rx_stream); 62462306a36Sopenharmony_ci if (err < 0) 62562306a36Sopenharmony_ci goto error; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci err = start_stream(bebob, &bebob->tx_stream); 62862306a36Sopenharmony_ci if (err < 0) 62962306a36Sopenharmony_ci goto error; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!(bebob->quirks & SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC)) 63262306a36Sopenharmony_ci tx_init_skip_cycles = 0; 63362306a36Sopenharmony_ci else 63462306a36Sopenharmony_ci tx_init_skip_cycles = 16000; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci // MEMO: Some devices start packet transmission long enough after establishment of 63762306a36Sopenharmony_ci // CMP connection. In the early stage of packet streaming, any device transfers 63862306a36Sopenharmony_ci // NODATA packets. After several hundred cycles, it begins to multiplex event into 63962306a36Sopenharmony_ci // the packet with adequate value of syt field in CIP header. Some devices are 64062306a36Sopenharmony_ci // strictly to generate any discontinuity in the sequence of tx packet when they 64162306a36Sopenharmony_ci // receives inadequate sequence of value in syt field of CIP header. In the case, 64262306a36Sopenharmony_ci // the request to break CMP connection is often corrupted, then any transaction 64362306a36Sopenharmony_ci // results in unrecoverable error, sometimes generate bus-reset. 64462306a36Sopenharmony_ci err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles, true, false); 64562306a36Sopenharmony_ci if (err < 0) 64662306a36Sopenharmony_ci goto error; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci // NOTE: 64962306a36Sopenharmony_ci // The firmware customized by M-Audio uses these commands to 65062306a36Sopenharmony_ci // start transmitting stream. This is not usual way. 65162306a36Sopenharmony_ci if (bebob->maudio_special_quirk) { 65262306a36Sopenharmony_ci err = bebob->spec->rate->set(bebob, curr_rate); 65362306a36Sopenharmony_ci if (err < 0) { 65462306a36Sopenharmony_ci dev_err(&bebob->unit->device, 65562306a36Sopenharmony_ci "fail to ensure sampling rate: %d\n", 65662306a36Sopenharmony_ci err); 65762306a36Sopenharmony_ci goto error; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci // Some devices postpone start of transmission mostly for 1 sec after receives 66262306a36Sopenharmony_ci // packets firstly. 66362306a36Sopenharmony_ci if (!amdtp_domain_wait_ready(&bebob->domain, READY_TIMEOUT_MS)) { 66462306a36Sopenharmony_ci err = -ETIMEDOUT; 66562306a36Sopenharmony_ci goto error; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_cierror: 67162306a36Sopenharmony_ci amdtp_domain_stop(&bebob->domain); 67262306a36Sopenharmony_ci break_both_connections(bebob); 67362306a36Sopenharmony_ci return err; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_civoid snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci if (bebob->substreams_counter == 0) { 67962306a36Sopenharmony_ci amdtp_domain_stop(&bebob->domain); 68062306a36Sopenharmony_ci break_both_connections(bebob); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci cmp_connection_release(&bebob->out_conn); 68362306a36Sopenharmony_ci cmp_connection_release(&bebob->in_conn); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/* 68862306a36Sopenharmony_ci * This function should be called before starting streams or after stopping 68962306a36Sopenharmony_ci * streams. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_civoid snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci amdtp_domain_destroy(&bebob->domain); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci destroy_stream(bebob, &bebob->tx_stream); 69662306a36Sopenharmony_ci destroy_stream(bebob, &bebob->rx_stream); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * See: Table 50: Extended Stream Format Info Format Hierarchy Level 2’ 70162306a36Sopenharmony_ci * in Additional AVC commands (Nov 2003, BridgeCo) 70262306a36Sopenharmony_ci * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_cistatic int 70562306a36Sopenharmony_ciparse_stream_formation(u8 *buf, unsigned int len, 70662306a36Sopenharmony_ci struct snd_bebob_stream_formation *formation) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci unsigned int i, e, channels, format; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* 71162306a36Sopenharmony_ci * this module can support a hierarchy combination that: 71262306a36Sopenharmony_ci * Root: Audio and Music (0x90) 71362306a36Sopenharmony_ci * Level 1: AM824 Compound (0x40) 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci if ((buf[0] != 0x90) || (buf[1] != 0x40)) 71662306a36Sopenharmony_ci return -ENOSYS; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* check sampling rate */ 71962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bridgeco_freq_table); i++) { 72062306a36Sopenharmony_ci if (buf[2] == bridgeco_freq_table[i]) 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci if (i == ARRAY_SIZE(bridgeco_freq_table)) 72462306a36Sopenharmony_ci return -ENOSYS; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Avoid double count by different entries for the same rate. */ 72762306a36Sopenharmony_ci memset(&formation[i], 0, sizeof(struct snd_bebob_stream_formation)); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci for (e = 0; e < buf[4]; e++) { 73062306a36Sopenharmony_ci channels = buf[5 + e * 2]; 73162306a36Sopenharmony_ci format = buf[6 + e * 2]; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci switch (format) { 73462306a36Sopenharmony_ci /* IEC 60958 Conformant, currently handled as MBLA */ 73562306a36Sopenharmony_ci case 0x00: 73662306a36Sopenharmony_ci /* Multi bit linear audio */ 73762306a36Sopenharmony_ci case 0x06: /* Raw */ 73862306a36Sopenharmony_ci formation[i].pcm += channels; 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci /* MIDI Conformant */ 74162306a36Sopenharmony_ci case 0x0d: 74262306a36Sopenharmony_ci formation[i].midi += channels; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci /* IEC 61937-3 to 7 */ 74562306a36Sopenharmony_ci case 0x01: 74662306a36Sopenharmony_ci case 0x02: 74762306a36Sopenharmony_ci case 0x03: 74862306a36Sopenharmony_ci case 0x04: 74962306a36Sopenharmony_ci case 0x05: 75062306a36Sopenharmony_ci /* Multi bit linear audio */ 75162306a36Sopenharmony_ci case 0x07: /* DVD-Audio */ 75262306a36Sopenharmony_ci case 0x0c: /* High Precision */ 75362306a36Sopenharmony_ci /* One Bit Audio */ 75462306a36Sopenharmony_ci case 0x08: /* (Plain) Raw */ 75562306a36Sopenharmony_ci case 0x09: /* (Plain) SACD */ 75662306a36Sopenharmony_ci case 0x0a: /* (Encoded) Raw */ 75762306a36Sopenharmony_ci case 0x0b: /* (Encoded) SACD */ 75862306a36Sopenharmony_ci /* Synchronization Stream (Stereo Raw audio) */ 75962306a36Sopenharmony_ci case 0x40: 76062306a36Sopenharmony_ci /* Don't care */ 76162306a36Sopenharmony_ci case 0xff: 76262306a36Sopenharmony_ci default: 76362306a36Sopenharmony_ci return -ENOSYS; /* not supported */ 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM || 76862306a36Sopenharmony_ci formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI) 76962306a36Sopenharmony_ci return -ENOSYS; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int fill_stream_formations(struct snd_bebob *bebob, u8 addr[AVC_BRIDGECO_ADDR_BYTES], 77562306a36Sopenharmony_ci enum avc_bridgeco_plug_dir plug_dir, unsigned int plug_id, 77662306a36Sopenharmony_ci struct snd_bebob_stream_formation *formations) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci enum avc_bridgeco_plug_type plug_type; 77962306a36Sopenharmony_ci u8 *buf; 78062306a36Sopenharmony_ci unsigned int len, eid; 78162306a36Sopenharmony_ci int err; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type); 78662306a36Sopenharmony_ci if (err < 0) { 78762306a36Sopenharmony_ci dev_err(&bebob->unit->device, 78862306a36Sopenharmony_ci "Fail to get type for isoc %d plug 0: %d\n", plug_dir, err); 78962306a36Sopenharmony_ci return err; 79062306a36Sopenharmony_ci } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_ISOC) 79162306a36Sopenharmony_ci return -ENXIO; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL); 79462306a36Sopenharmony_ci if (buf == NULL) 79562306a36Sopenharmony_ci return -ENOMEM; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; ++eid) { 79862306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci len = FORMAT_MAXIMUM_LENGTH; 80162306a36Sopenharmony_ci err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf, &len, eid); 80262306a36Sopenharmony_ci // No entries remained. 80362306a36Sopenharmony_ci if (err == -EINVAL && eid > 0) { 80462306a36Sopenharmony_ci err = 0; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci } else if (err < 0) { 80762306a36Sopenharmony_ci dev_err(&bebob->unit->device, 80862306a36Sopenharmony_ci "fail to get stream format %d for isoc %d plug %d:%d\n", 80962306a36Sopenharmony_ci eid, plug_dir, plug_id, err); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci err = parse_stream_formation(buf, len, formations); 81462306a36Sopenharmony_ci if (err < 0) 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci kfree(buf); 81962306a36Sopenharmony_ci return err; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int detect_midi_ports(struct snd_bebob *bebob, 82362306a36Sopenharmony_ci const struct snd_bebob_stream_formation *formats, 82462306a36Sopenharmony_ci u8 addr[AVC_BRIDGECO_ADDR_BYTES], enum avc_bridgeco_plug_dir plug_dir, 82562306a36Sopenharmony_ci unsigned int plug_count, unsigned int *midi_ports) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int i; 82862306a36Sopenharmony_ci int err = 0; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci *midi_ports = 0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /// Detect the number of available MIDI ports when packet has MIDI conformant data channel. 83362306a36Sopenharmony_ci for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; ++i) { 83462306a36Sopenharmony_ci if (formats[i].midi > 0) 83562306a36Sopenharmony_ci break; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci if (i >= SND_BEBOB_STRM_FMT_ENTRIES) 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < plug_count; ++i) { 84162306a36Sopenharmony_ci enum avc_bridgeco_plug_type plug_type; 84262306a36Sopenharmony_ci unsigned int ch_count; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_EXT, i); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type); 84762306a36Sopenharmony_ci if (err < 0) { 84862306a36Sopenharmony_ci dev_err(&bebob->unit->device, 84962306a36Sopenharmony_ci "fail to get type for external %d plug %d: %d\n", 85062306a36Sopenharmony_ci plug_dir, i, err); 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_MIDI) { 85362306a36Sopenharmony_ci continue; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci err = avc_bridgeco_get_plug_ch_count(bebob->unit, addr, &ch_count); 85762306a36Sopenharmony_ci if (err < 0) 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci // Yamaha GO44, GO46, Terratec Phase 24, Phase x24 reports 0 for the number of 86062306a36Sopenharmony_ci // channels in external output plug 3 (MIDI type) even if it has a pair of physical 86162306a36Sopenharmony_ci // MIDI jacks. As a workaround, assume it as one. 86262306a36Sopenharmony_ci if (ch_count == 0) 86362306a36Sopenharmony_ci ch_count = 1; 86462306a36Sopenharmony_ci *midi_ports += ch_count; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return err; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int 87162306a36Sopenharmony_ciseek_msu_sync_input_plug(struct snd_bebob *bebob) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES]; 87462306a36Sopenharmony_ci unsigned int i; 87562306a36Sopenharmony_ci enum avc_bridgeco_plug_type type; 87662306a36Sopenharmony_ci int err; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Get the number of Music Sub Unit for both direction. */ 87962306a36Sopenharmony_ci err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs); 88062306a36Sopenharmony_ci if (err < 0) { 88162306a36Sopenharmony_ci dev_err(&bebob->unit->device, 88262306a36Sopenharmony_ci "fail to get info for MSU in/out plugs: %d\n", 88362306a36Sopenharmony_ci err); 88462306a36Sopenharmony_ci goto end; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* seek destination plugs for 'MSU sync input' */ 88862306a36Sopenharmony_ci bebob->sync_input_plug = -1; 88962306a36Sopenharmony_ci for (i = 0; i < plugs[0]; i++) { 89062306a36Sopenharmony_ci avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, i); 89162306a36Sopenharmony_ci err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type); 89262306a36Sopenharmony_ci if (err < 0) { 89362306a36Sopenharmony_ci dev_err(&bebob->unit->device, 89462306a36Sopenharmony_ci "fail to get type for MSU in plug %d: %d\n", 89562306a36Sopenharmony_ci i, err); 89662306a36Sopenharmony_ci goto end; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) { 90062306a36Sopenharmony_ci bebob->sync_input_plug = i; 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ciend: 90562306a36Sopenharmony_ci return err; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ciint snd_bebob_stream_discover(struct snd_bebob *bebob) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; 91162306a36Sopenharmony_ci u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES]; 91262306a36Sopenharmony_ci int err; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* the number of plugs for isoc in/out, ext in/out */ 91562306a36Sopenharmony_ci err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs); 91662306a36Sopenharmony_ci if (err < 0) { 91762306a36Sopenharmony_ci dev_err(&bebob->unit->device, 91862306a36Sopenharmony_ci "fail to get info for isoc/external in/out plugs: %d\n", 91962306a36Sopenharmony_ci err); 92062306a36Sopenharmony_ci goto end; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * This module supports at least one isoc input plug and one isoc 92562306a36Sopenharmony_ci * output plug. 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_ci if ((plugs[0] == 0) || (plugs[1] == 0)) { 92862306a36Sopenharmony_ci err = -ENOSYS; 92962306a36Sopenharmony_ci goto end; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_IN, 0, 93362306a36Sopenharmony_ci bebob->rx_stream_formations); 93462306a36Sopenharmony_ci if (err < 0) 93562306a36Sopenharmony_ci goto end; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_OUT, 0, 93862306a36Sopenharmony_ci bebob->tx_stream_formations); 93962306a36Sopenharmony_ci if (err < 0) 94062306a36Sopenharmony_ci goto end; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN, 94362306a36Sopenharmony_ci plugs[2], &bebob->midi_input_ports); 94462306a36Sopenharmony_ci if (err < 0) 94562306a36Sopenharmony_ci goto end; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT, 94862306a36Sopenharmony_ci plugs[3], &bebob->midi_output_ports); 94962306a36Sopenharmony_ci if (err < 0) 95062306a36Sopenharmony_ci goto end; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* for check source of clock later */ 95362306a36Sopenharmony_ci if (!clk_spec) 95462306a36Sopenharmony_ci err = seek_msu_sync_input_plug(bebob); 95562306a36Sopenharmony_ciend: 95662306a36Sopenharmony_ci return err; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_civoid snd_bebob_stream_lock_changed(struct snd_bebob *bebob) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci bebob->dev_lock_changed = true; 96262306a36Sopenharmony_ci wake_up(&bebob->hwdep_wait); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ciint snd_bebob_stream_lock_try(struct snd_bebob *bebob) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci int err; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci spin_lock_irq(&bebob->lock); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* user land lock this */ 97262306a36Sopenharmony_ci if (bebob->dev_lock_count < 0) { 97362306a36Sopenharmony_ci err = -EBUSY; 97462306a36Sopenharmony_ci goto end; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* this is the first time */ 97862306a36Sopenharmony_ci if (bebob->dev_lock_count++ == 0) 97962306a36Sopenharmony_ci snd_bebob_stream_lock_changed(bebob); 98062306a36Sopenharmony_ci err = 0; 98162306a36Sopenharmony_ciend: 98262306a36Sopenharmony_ci spin_unlock_irq(&bebob->lock); 98362306a36Sopenharmony_ci return err; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_civoid snd_bebob_stream_lock_release(struct snd_bebob *bebob) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci spin_lock_irq(&bebob->lock); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (WARN_ON(bebob->dev_lock_count <= 0)) 99162306a36Sopenharmony_ci goto end; 99262306a36Sopenharmony_ci if (--bebob->dev_lock_count == 0) 99362306a36Sopenharmony_ci snd_bebob_stream_lock_changed(bebob); 99462306a36Sopenharmony_ciend: 99562306a36Sopenharmony_ci spin_unlock_irq(&bebob->lock); 99662306a36Sopenharmony_ci} 997