static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; }
static int pcm_open(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; unsigned int sampling_rate; enum snd_efw_clock_source clock_source; int err; err = snd_efw_stream_lock_try(efw); if (err < 0) goto end; err = pcm_init_hw_params(efw, substream); if (err < 0) goto err_locked; err = snd_efw_command_get_clock_source(efw, &clock_source); if (err < 0) goto err_locked; /* * When source of clock is not internal or any PCM streams are running, * available sampling rate is limited at current sampling rate. */ if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) || amdtp_stream_pcm_running(&efw->tx_stream) || amdtp_stream_pcm_running(&efw->rx_stream)) { err = snd_efw_command_get_sampling_rate(efw, &sampling_rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_efw_stream_lock_release(efw); return err; }
static int pcm_init_hw_params(struct snd_tscm *tscm, struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 44100, .rate_max = 96000, .channels_min = 10, .channels_max = 18, }; struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *stream; unsigned int pcm_channels; runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; stream = &tscm->tx_stream; pcm_channels = tscm->spec->pcm_capture_analog_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; stream = &tscm->rx_stream; pcm_channels = tscm->spec->pcm_playback_analog_channels; } if (tscm->spec->has_adat) pcm_channels += 8; if (tscm->spec->has_spdif) pcm_channels += 2; runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels; set_buffer_params(&runtime->hw); return amdtp_tscm_add_pcm_hw_constraints(stream, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; enum snd_tscm_clock clock; unsigned int rate; int err; err = snd_tscm_stream_lock_try(tscm); if (err < 0) goto end; err = pcm_init_hw_params(tscm, substream); if (err < 0) goto err_locked; err = snd_tscm_stream_get_clock(tscm, &clock); if (clock != SND_TSCM_CLOCK_INTERNAL || amdtp_stream_pcm_running(&tscm->rx_stream) || amdtp_stream_pcm_running(&tscm->tx_stream)) { err = snd_tscm_stream_get_rate(tscm, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_tscm_stream_lock_release(tscm); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; snd_tscm_stream_lock_release(tscm); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_tscm *tscm = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&tscm->mutex); tscm->substreams_counter++; mutex_unlock(&tscm->mutex); } amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params)); return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_tscm *tscm = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&tscm->mutex); tscm->substreams_counter++; mutex_unlock(&tscm->mutex); } amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params)); return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; mutex_lock(&tscm->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) tscm->substreams_counter--; snd_tscm_stream_stop_duplex(tscm); mutex_unlock(&tscm->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; mutex_lock(&tscm->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) tscm->substreams_counter--; snd_tscm_stream_stop_duplex(tscm); mutex_unlock(&tscm->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&tscm->mutex); err = snd_tscm_stream_start_duplex(tscm, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&tscm->tx_stream); mutex_unlock(&tscm->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&tscm->mutex); err = snd_tscm_stream_start_duplex(tscm, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&tscm->rx_stream); mutex_unlock(&tscm->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_tscm *tscm = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&tscm->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_tscm *tscm = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&tscm->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; return amdtp_stream_pcm_pointer(&tscm->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; return amdtp_stream_pcm_pointer(&tscm->rx_stream); } static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) { struct snd_pcm *pcm; int err; err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = tscm; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", tscm->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; struct snd_bebob_stream_formation *formations; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } limit_channels_and_rates(&runtime->hw, formations); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, formations, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; }
static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { int err; static const struct snd_pcm_hardware hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_FIFO_IN_FRAMES | SNDRV_PCM_INFO_JOINT_DUPLEX | /* for Open Sound System compatibility */ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, /* set up later */ .rates = 0, .rate_min = UINT_MAX, .rate_max = 0, /* set up later */ .channels_min = UINT_MAX, .channels_max = 0, .buffer_bytes_max = 1024 * 1024 * 1024, .period_bytes_min = 256, .period_bytes_max = 1024 * 1024 * 1024 / 2, .periods_min = 2, .periods_max = 32, .fifo_size = 0, }; substream->runtime->hw = hw; substream->runtime->delay = substream->runtime->hw.fifo_size; /* add rule between channels and sampling rate */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { prepare_rates(&substream->runtime->hw, bebob->tx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->tx_stream_formations); substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_capture_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_capture_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } else { prepare_rates(&substream->runtime->hw, bebob->rx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->rx_stream_formations); substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_playback_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_playback_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } /* AM824 in IEC 61883-6 can deliver 24bit data */ err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); if (err < 0) goto end; /* * AMDTP functionality in firewire-lib require periods to be aligned to * 16 bit, or 24bit inner 32bit. */ err = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (err < 0) goto end; /* time for period constraint */ err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 500, UINT_MAX); if (err < 0) goto end; err = 0; end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; bool internal; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_check_internal_clock(bebob, &internal); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (!internal || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->tx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->tx_stream); end: return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->rx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->rx_stream); end: return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); end: return err; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, amdtp_rate_table[i]); t.max = max(t.max, amdtp_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(r, amdtp_rate_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { unsigned int rate, channels; int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, channels); hw->channels_max = max(hw->channels_max, channels); rate = amdtp_rate_table[i]; hw->rates |= snd_pcm_rate_to_rate_bit(rate); hw->rate_min = min(hw->rate_min, rate); hw->rate_max = max(hw->rate_max, rate); } } static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; const unsigned int *pcm_channels; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; pcm_channels = ff->spec->pcm_capture_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->rx_stream; pcm_channels = ff->spec->pcm_playback_channels; } limit_channels_and_rates(&runtime->hw, pcm_channels); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_ff_add_pcm_hw_constraints(s, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; unsigned int rate; enum snd_ff_clock_src src; int i, err; err = snd_ff_stream_lock_try(ff); if (err < 0) return err; err = pcm_init_hw_params(ff, substream); if (err < 0) goto release_lock; err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } /* * The unit is configured at sampling frequency which packet * streaming engine can't support. */ if (i >= CIP_SFC_COUNT) { err = -EIO; goto release_lock; } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { if (amdtp_stream_pcm_running(&ff->rx_stream) || amdtp_stream_pcm_running(&ff->tx_stream)) { rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } } snd_pcm_set_sync(substream); return 0; release_lock: snd_ff_stream_lock_release(ff); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; snd_ff_stream_lock_release(ff); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->tx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->rx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) { static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = ff; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(c, snd_dg00x_stream_pcm_channels[i])) continue; t.min = min(t.min, snd_dg00x_stream_rates[i]); t.max = max(t.max, snd_dg00x_stream_rates[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(r, snd_dg00x_stream_rates[i])) continue; t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); } return snd_interval_refine(c, &t); } static int pcm_init_hw_params(struct snd_dg00x *dg00x, struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 44100, .rate_max = 96000, .channels_min = 10, .channels_max = 18, .period_bytes_min = 4 * 18, .period_bytes_max = 4 * 18 * 2048, .buffer_bytes_max = 4 * 18 * 2048 * 2, .periods_min = 2, .periods_max = UINT_MAX, }; struct amdtp_stream *s; int err; substream->runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->tx_stream; } else { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; s = &dg00x->rx_stream; } err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; enum snd_dg00x_clock clock; bool detect; unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) goto end; err = pcm_init_hw_params(dg00x, substream); if (err < 0) goto err_locked; /* Check current clock source. */ err = snd_dg00x_stream_get_clock(dg00x, &clock); if (err < 0) goto err_locked; if (clock != SND_DG00X_CLOCK_INTERNAL) { err = snd_dg00x_stream_check_external_clock(dg00x, &detect); if (err < 0) goto err_locked; if (!detect) { err = -EBUSY; goto err_locked; } } if ((clock != SND_DG00X_CLOCK_INTERNAL) || amdtp_stream_pcm_running(&dg00x->rx_stream) || amdtp_stream_pcm_running(&dg00x->tx_stream)) { err = snd_dg00x_stream_get_external_rate(dg00x, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_dg00x_stream_lock_release(dg00x); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; snd_dg00x_stream_lock_release(dg00x); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dg00x->tx_stream); mutex_unlock(&dg00x->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) { amdtp_stream_pcm_prepare(&dg00x->rx_stream); amdtp_dot_reset(&dg00x->rx_stream); } mutex_unlock(&dg00x->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; }