291 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tascam-pcm.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8#include "tascam.h"9 10static int pcm_init_hw_params(struct snd_tscm *tscm,11 struct snd_pcm_substream *substream)12{13 struct snd_pcm_runtime *runtime = substream->runtime;14 struct snd_pcm_hardware *hw = &runtime->hw;15 struct amdtp_stream *stream;16 unsigned int pcm_channels;17 18 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {19 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;20 stream = &tscm->tx_stream;21 pcm_channels = tscm->spec->pcm_capture_analog_channels;22 } else {23 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;24 stream = &tscm->rx_stream;25 pcm_channels = tscm->spec->pcm_playback_analog_channels;26 }27 28 if (tscm->spec->has_adat)29 pcm_channels += 8;30 if (tscm->spec->has_spdif)31 pcm_channels += 2;32 runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;33 34 hw->rates = SNDRV_PCM_RATE_44100 |35 SNDRV_PCM_RATE_48000 |36 SNDRV_PCM_RATE_88200 |37 SNDRV_PCM_RATE_96000;38 snd_pcm_limit_hw_rates(runtime);39 40 return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);41}42 43static int pcm_open(struct snd_pcm_substream *substream)44{45 struct snd_tscm *tscm = substream->private_data;46 struct amdtp_domain *d = &tscm->domain;47 enum snd_tscm_clock clock;48 int err;49 50 err = snd_tscm_stream_lock_try(tscm);51 if (err < 0)52 return err;53 54 err = pcm_init_hw_params(tscm, substream);55 if (err < 0)56 goto err_locked;57 58 err = snd_tscm_stream_get_clock(tscm, &clock);59 if (err < 0)60 goto err_locked;61 62 mutex_lock(&tscm->mutex);63 64 // When source of clock is not internal or any stream is reserved for65 // transmission of PCM frames, the available sampling rate is limited66 // at current one.67 if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {68 unsigned int frames_per_period = d->events_per_period;69 unsigned int frames_per_buffer = d->events_per_buffer;70 unsigned int rate;71 72 err = snd_tscm_stream_get_rate(tscm, &rate);73 if (err < 0) {74 mutex_unlock(&tscm->mutex);75 goto err_locked;76 }77 substream->runtime->hw.rate_min = rate;78 substream->runtime->hw.rate_max = rate;79 80 err = snd_pcm_hw_constraint_minmax(substream->runtime,81 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,82 frames_per_period, frames_per_period);83 if (err < 0) {84 mutex_unlock(&tscm->mutex);85 goto err_locked;86 }87 88 err = snd_pcm_hw_constraint_minmax(substream->runtime,89 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,90 frames_per_buffer, frames_per_buffer);91 if (err < 0) {92 mutex_unlock(&tscm->mutex);93 goto err_locked;94 }95 }96 97 mutex_unlock(&tscm->mutex);98 99 snd_pcm_set_sync(substream);100 101 return 0;102err_locked:103 snd_tscm_stream_lock_release(tscm);104 return err;105}106 107static int pcm_close(struct snd_pcm_substream *substream)108{109 struct snd_tscm *tscm = substream->private_data;110 111 snd_tscm_stream_lock_release(tscm);112 113 return 0;114}115 116static int pcm_hw_params(struct snd_pcm_substream *substream,117 struct snd_pcm_hw_params *hw_params)118{119 struct snd_tscm *tscm = substream->private_data;120 int err = 0;121 122 if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {123 unsigned int rate = params_rate(hw_params);124 unsigned int frames_per_period = params_period_size(hw_params);125 unsigned int frames_per_buffer = params_buffer_size(hw_params);126 127 mutex_lock(&tscm->mutex);128 err = snd_tscm_stream_reserve_duplex(tscm, rate,129 frames_per_period, frames_per_buffer);130 if (err >= 0)131 ++tscm->substreams_counter;132 mutex_unlock(&tscm->mutex);133 }134 135 return err;136}137 138static int pcm_hw_free(struct snd_pcm_substream *substream)139{140 struct snd_tscm *tscm = substream->private_data;141 142 mutex_lock(&tscm->mutex);143 144 if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)145 --tscm->substreams_counter;146 147 snd_tscm_stream_stop_duplex(tscm);148 149 mutex_unlock(&tscm->mutex);150 151 return 0;152}153 154static int pcm_capture_prepare(struct snd_pcm_substream *substream)155{156 struct snd_tscm *tscm = substream->private_data;157 struct snd_pcm_runtime *runtime = substream->runtime;158 int err;159 160 mutex_lock(&tscm->mutex);161 162 err = snd_tscm_stream_start_duplex(tscm, runtime->rate);163 if (err >= 0)164 amdtp_stream_pcm_prepare(&tscm->tx_stream);165 166 mutex_unlock(&tscm->mutex);167 168 return err;169}170 171static int pcm_playback_prepare(struct snd_pcm_substream *substream)172{173 struct snd_tscm *tscm = substream->private_data;174 struct snd_pcm_runtime *runtime = substream->runtime;175 int err;176 177 mutex_lock(&tscm->mutex);178 179 err = snd_tscm_stream_start_duplex(tscm, runtime->rate);180 if (err >= 0)181 amdtp_stream_pcm_prepare(&tscm->rx_stream);182 183 mutex_unlock(&tscm->mutex);184 185 return err;186}187 188static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)189{190 struct snd_tscm *tscm = substream->private_data;191 192 switch (cmd) {193 case SNDRV_PCM_TRIGGER_START:194 amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);195 break;196 case SNDRV_PCM_TRIGGER_STOP:197 amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);198 break;199 default:200 return -EINVAL;201 }202 203 return 0;204}205 206static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)207{208 struct snd_tscm *tscm = substream->private_data;209 210 switch (cmd) {211 case SNDRV_PCM_TRIGGER_START:212 amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);213 break;214 case SNDRV_PCM_TRIGGER_STOP:215 amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);216 break;217 default:218 return -EINVAL;219 }220 221 return 0;222}223 224static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)225{226 struct snd_tscm *tscm = sbstrm->private_data;227 228 return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);229}230 231static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)232{233 struct snd_tscm *tscm = sbstrm->private_data;234 235 return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);236}237 238static int pcm_capture_ack(struct snd_pcm_substream *substream)239{240 struct snd_tscm *tscm = substream->private_data;241 242 return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);243}244 245static int pcm_playback_ack(struct snd_pcm_substream *substream)246{247 struct snd_tscm *tscm = substream->private_data;248 249 return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);250}251 252int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)253{254 static const struct snd_pcm_ops capture_ops = {255 .open = pcm_open,256 .close = pcm_close,257 .hw_params = pcm_hw_params,258 .hw_free = pcm_hw_free,259 .prepare = pcm_capture_prepare,260 .trigger = pcm_capture_trigger,261 .pointer = pcm_capture_pointer,262 .ack = pcm_capture_ack,263 };264 static const struct snd_pcm_ops playback_ops = {265 .open = pcm_open,266 .close = pcm_close,267 .hw_params = pcm_hw_params,268 .hw_free = pcm_hw_free,269 .prepare = pcm_playback_prepare,270 .trigger = pcm_playback_trigger,271 .pointer = pcm_playback_pointer,272 .ack = pcm_playback_ack,273 };274 struct snd_pcm *pcm;275 int err;276 277 err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);278 if (err < 0)279 return err;280 281 pcm->private_data = tscm;282 pcm->nonatomic = true;283 snprintf(pcm->name, sizeof(pcm->name),284 "%s PCM", tscm->card->shortname);285 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);286 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);287 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);288 289 return 0;290}291