int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw) { int err = 0; if (amdtp_streaming_error(&oxfw->rx_stream)) stop_stream(oxfw); if (amdtp_stream_running(&oxfw->rx_stream)) goto end; err = cmp_connection_establish(&oxfw->in_conn, amdtp_stream_get_max_payload(&oxfw->rx_stream)); if (err < 0) goto end; err = amdtp_stream_start(&oxfw->rx_stream, oxfw->in_conn.resources.channel, oxfw->in_conn.speed); if (err < 0) stop_stream(oxfw); end: return err; }
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) { struct amdtp_stream *master, *slave; atomic_t *slave_substreams; enum cip_flags sync_mode; unsigned int curr_rate; int err = 0; mutex_lock(&efw->mutex); /* Need no substreams */ if ((atomic_read(&efw->playback_substreams) == 0) && (atomic_read(&efw->capture_substreams) == 0)) goto end; err = get_sync_mode(efw, &sync_mode); if (err < 0) goto end; if (sync_mode == CIP_SYNC_TO_DEVICE) { master = &efw->tx_stream; slave = &efw->rx_stream; slave_substreams = &efw->playback_substreams; } else { master = &efw->rx_stream; slave = &efw->tx_stream; slave_substreams = &efw->capture_substreams; } /* * Considering JACK/FFADO streaming: * TODO: This can be removed hwdep functionality becomes popular. */ err = check_connection_used_by_others(efw, master); if (err < 0) goto end; /* packet queueing error */ if (amdtp_streaming_error(slave)) stop_stream(efw, slave); if (amdtp_streaming_error(master)) stop_stream(efw, master); /* stop streams if rate is different */ err = snd_efw_command_get_sampling_rate(efw, &curr_rate); if (err < 0) goto end; if (rate == 0) rate = curr_rate; if (rate != curr_rate) { stop_stream(efw, slave); stop_stream(efw, master); } /* master should be always running */ if (!amdtp_stream_running(master)) { amdtp_stream_set_sync(sync_mode, master, slave); efw->master = master; err = snd_efw_command_set_sampling_rate(efw, rate); if (err < 0) goto end; err = start_stream(efw, master, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP master stream:%d\n", err); goto end; } } /* start slave if needed */ if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) { err = start_stream(efw, slave, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP slave stream:%d\n", err); stop_stream(efw, master); } } end: mutex_unlock(&efw->mutex); return err; }
static int firewave_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static const struct snd_interval all_channels = { .min = 6, .max = 6 }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* 32/44.1 kHz work only with all six channels */ if (snd_interval_max(rate) < 48000) return snd_interval_refine(channels, &all_channels); return 0; } static int firewave_constraints(struct snd_pcm_runtime *runtime) { static unsigned int channels_list[] = { 2, 6 }; static struct snd_pcm_hw_constraint_list channels_list_constraint = { .count = 2, .list = channels_list, }; int err; runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000; runtime->hw.channels_max = 6; err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &channels_list_constraint); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, firewave_rate_constraint, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, firewave_channels_constraint, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; return 0; } static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) { runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; return 0; } static int fwspk_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, .periods_min = 1, .periods_max = UINT_MAX, }; struct fwspk *fwspk = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; runtime->hw = hardware; err = fwspk->device_info->pcm_constraints(runtime); if (err < 0) return err; err = snd_pcm_limit_hw_rates(runtime); if (err < 0) return err; err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime); if (err < 0) return err; return 0; } static int fwspk_close(struct snd_pcm_substream *substream) { return 0; } static void fwspk_stop_stream(struct fwspk *fwspk) { if (amdtp_stream_running(&fwspk->stream)) { amdtp_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); } } static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) goto error; amdtp_stream_set_parameters(&fwspk->stream, params_rate(hw_params), params_channels(hw_params), 0); amdtp_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params), AVC_GENERAL_PLUG_DIR_IN, 0); if (err < 0) { dev_err(&fwspk->unit->device, "failed to set sample rate\n"); goto err_buffer; } return 0; err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: return err; } static int fwspk_hw_free(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int fwspk_prepare(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); if (amdtp_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); if (!amdtp_stream_running(&fwspk->stream)) { err = cmp_connection_establish(&fwspk->connection, amdtp_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; err = amdtp_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) goto err_connection; } mutex_unlock(&fwspk->mutex); amdtp_stream_pcm_prepare(&fwspk->stream); return 0; err_connection: cmp_connection_break(&fwspk->connection); err_mutex: mutex_unlock(&fwspk->mutex); return err; } static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) { struct fwspk *fwspk = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { case SNDRV_PCM_TRIGGER_START: pcm = substream; break; case SNDRV_PCM_TRIGGER_STOP: pcm = NULL; break; default: return -EINVAL; } amdtp_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; return amdtp_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) { static struct snd_pcm_ops ops = { .open = fwspk_open, .close = fwspk_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = fwspk_hw_params, .hw_free = fwspk_hw_free, .prepare = fwspk_prepare, .trigger = fwspk_trigger, .pointer = fwspk_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); if (err < 0) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); return 0; } enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, CTL_MAX = 0x03, CTL_CURRENT = 0x10, }; static int fwspk_mute_command(struct fwspk *fwspk, bool *value, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(11, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ buf[5] = 0x10; /* control attribute: current */ buf[6] = 0x02; /* selector length */ buf[7] = 0x00; /* audio channel number */ buf[8] = 0x01; /* control selector: mute */ buf[9] = 0x01; /* control data length */ if (action == CTL_READ) buf[10] = 0xff; else buf[10] = *value ? 0x70 : 0x60; err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); if (err < 0) goto error; if (err < 11) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "mute command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = buf[10] == 0x70; err = 0; error: kfree(buf); return err; } static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, unsigned int channel, enum control_attribute attribute, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(12, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ buf[5] = attribute; /* control attribute */ buf[6] = 0x02; /* selector length */ buf[7] = channel; /* audio channel number */ buf[8] = 0x02; /* control selector: volume */ buf[9] = 0x02; /* control data length */ if (action == CTL_READ) { buf[10] = 0xff; buf[11] = 0xff; } else { buf[10] = *value >> 8; buf[11] = *value; } err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); if (err < 0) goto error; if (err < 12) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "volume command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = (buf[10] << 8) | buf[11]; err = 0; error: kfree(buf); return err; }
int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream, unsigned int rate, unsigned int pcm_channels) { struct amdtp_stream *opposite; struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; unsigned int substreams, opposite_substreams; int err = 0; if (stream == &oxfw->tx_stream) { substreams = oxfw->capture_substreams; opposite = &oxfw->rx_stream; opposite_substreams = oxfw->playback_substreams; dir = AVC_GENERAL_PLUG_DIR_OUT; } else { substreams = oxfw->playback_substreams; opposite_substreams = oxfw->capture_substreams; if (oxfw->has_output) opposite = &oxfw->rx_stream; else opposite = NULL; dir = AVC_GENERAL_PLUG_DIR_IN; } if (substreams == 0) goto end; /* * Considering JACK/FFADO streaming: * TODO: This can be removed hwdep functionality becomes popular. */ err = check_connection_used_by_others(oxfw, stream); if (err < 0) goto end; /* packet queueing error */ if (amdtp_streaming_error(stream)) stop_stream(oxfw, stream); err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); if (err < 0) goto end; if (rate == 0) rate = formation.rate; if (pcm_channels == 0) pcm_channels = formation.pcm; if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { if (opposite != NULL) { err = check_connection_used_by_others(oxfw, opposite); if (err < 0) goto end; stop_stream(oxfw, opposite); } stop_stream(oxfw, stream); err = set_stream_format(oxfw, stream, rate, pcm_channels); if (err < 0) { dev_err(&oxfw->unit->device, "fail to set stream format: %d\n", err); goto end; } /* Start opposite stream if needed. */ if (opposite && !amdtp_stream_running(opposite) && (opposite_substreams > 0)) { err = start_stream(oxfw, opposite, rate, 0); if (err < 0) { dev_err(&oxfw->unit->device, "fail to restart stream: %d\n", err); goto end; } } } /* Start requested stream. */ if (!amdtp_stream_running(stream)) { err = start_stream(oxfw, stream, rate, pcm_channels); if (err < 0) dev_err(&oxfw->unit->device, "fail to start stream: %d\n", err); } end: return err; }
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) { const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; unsigned int curr_rate; int err = 0; /* Need no substreams */ if (bebob->substreams_counter == 0) goto end; /* * Considering JACK/FFADO streaming: * TODO: This can be removed hwdep functionality becomes popular. */ err = check_connection_used_by_others(bebob, &bebob->rx_stream); if (err < 0) goto end; /* * packet queueing error or detecting discontinuity * * At bus reset, connections should not be broken here. So streams need * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. */ if (amdtp_streaming_error(&bebob->rx_stream)) amdtp_stream_stop(&bebob->rx_stream); if (amdtp_streaming_error(&bebob->tx_stream)) amdtp_stream_stop(&bebob->tx_stream); if (!amdtp_stream_running(&bebob->rx_stream) && !amdtp_stream_running(&bebob->tx_stream)) break_both_connections(bebob); /* stop streams if rate is different */ err = rate_spec->get(bebob, &curr_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto end; } if (rate == 0) rate = curr_rate; if (rate != curr_rate) { amdtp_stream_stop(&bebob->rx_stream); amdtp_stream_stop(&bebob->tx_stream); break_both_connections(bebob); } /* master should be always running */ if (!amdtp_stream_running(&bebob->rx_stream)) { /* * NOTE: * If establishing connections at first, Yamaha GO46 * (and maybe Terratec X24) don't generate sound. * * For firmware customized by M-Audio, refer to next NOTE. */ if (bebob->maudio_special_quirk == NULL) { err = rate_spec->set(bebob, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to set sampling rate: %d\n", err); goto end; } } err = make_both_connections(bebob, rate); if (err < 0) goto end; err = start_stream(bebob, &bebob->rx_stream, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP master stream:%d\n", err); break_both_connections(bebob); goto end; } /* * NOTE: * The firmware customized by M-Audio uses these commands to * start transmitting stream. This is not usual way. */ if (bebob->maudio_special_quirk != NULL) { err = rate_spec->set(bebob, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to ensure sampling rate: %d\n", err); amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); goto end; } } /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->rx_stream, CALLBACK_TIMEOUT)) { amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); err = -ETIMEDOUT; goto end; } } /* start slave if needed */ if (!amdtp_stream_running(&bebob->tx_stream)) { err = start_stream(bebob, &bebob->tx_stream, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP slave stream:%d\n", err); amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); goto end; } /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->tx_stream, CALLBACK_TIMEOUT)) { amdtp_stream_stop(&bebob->tx_stream); amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); err = -ETIMEDOUT; } } end: return err; }