1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * tascam-pcm.c - a part of driver for TASCAM FireWire series 4 * 5 * Copyright (c) 2015 Takashi Sakamoto 6 */ 7 8 #include "tascam.h" 9 10 static 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 43 static 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 for 65 // transmission of PCM frames, the available sampling rate is limited 66 // 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; 102 err_locked: 103 snd_tscm_stream_lock_release(tscm); 104 return err; 105 } 106 107 static 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 116 static 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 138 static 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 154 static 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 171 static 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 188 static 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 206 static 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 224 static 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 231 static 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 238 static 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 245 static 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 252 int 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 snprintf(pcm->name, sizeof(pcm->name), 283 "%s PCM", tscm->card->shortname); 284 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 285 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 286 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 287 288 return 0; 289 } 290
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.