18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tascam-pcm.c - a part of driver for TASCAM FireWire series 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "tascam.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic int pcm_init_hw_params(struct snd_tscm *tscm, 118c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 148c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw = &runtime->hw; 158c2ecf20Sopenharmony_ci struct amdtp_stream *stream; 168c2ecf20Sopenharmony_ci unsigned int pcm_channels; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 198c2ecf20Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; 208c2ecf20Sopenharmony_ci stream = &tscm->tx_stream; 218c2ecf20Sopenharmony_ci pcm_channels = tscm->spec->pcm_capture_analog_channels; 228c2ecf20Sopenharmony_ci } else { 238c2ecf20Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; 248c2ecf20Sopenharmony_ci stream = &tscm->rx_stream; 258c2ecf20Sopenharmony_ci pcm_channels = tscm->spec->pcm_playback_analog_channels; 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (tscm->spec->has_adat) 298c2ecf20Sopenharmony_ci pcm_channels += 8; 308c2ecf20Sopenharmony_ci if (tscm->spec->has_spdif) 318c2ecf20Sopenharmony_ci pcm_channels += 2; 328c2ecf20Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci hw->rates = SNDRV_PCM_RATE_44100 | 358c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | 368c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | 378c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000; 388c2ecf20Sopenharmony_ci snd_pcm_limit_hw_rates(runtime); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return amdtp_tscm_add_pcm_hw_constraints(stream, runtime); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 468c2ecf20Sopenharmony_ci struct amdtp_domain *d = &tscm->domain; 478c2ecf20Sopenharmony_ci enum snd_tscm_clock clock; 488c2ecf20Sopenharmony_ci int err; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci err = snd_tscm_stream_lock_try(tscm); 518c2ecf20Sopenharmony_ci if (err < 0) 528c2ecf20Sopenharmony_ci return err; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci err = pcm_init_hw_params(tscm, substream); 558c2ecf20Sopenharmony_ci if (err < 0) 568c2ecf20Sopenharmony_ci goto err_locked; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci err = snd_tscm_stream_get_clock(tscm, &clock); 598c2ecf20Sopenharmony_ci if (err < 0) 608c2ecf20Sopenharmony_ci goto err_locked; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci mutex_lock(&tscm->mutex); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci // When source of clock is not internal or any stream is reserved for 658c2ecf20Sopenharmony_ci // transmission of PCM frames, the available sampling rate is limited 668c2ecf20Sopenharmony_ci // at current one. 678c2ecf20Sopenharmony_ci if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) { 688c2ecf20Sopenharmony_ci unsigned int frames_per_period = d->events_per_period; 698c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = d->events_per_buffer; 708c2ecf20Sopenharmony_ci unsigned int rate; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci err = snd_tscm_stream_get_rate(tscm, &rate); 738c2ecf20Sopenharmony_ci if (err < 0) { 748c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 758c2ecf20Sopenharmony_ci goto err_locked; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci substream->runtime->hw.rate_min = rate; 788c2ecf20Sopenharmony_ci substream->runtime->hw.rate_max = rate; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 818c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 828c2ecf20Sopenharmony_ci frames_per_period, frames_per_period); 838c2ecf20Sopenharmony_ci if (err < 0) { 848c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 858c2ecf20Sopenharmony_ci goto err_locked; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 898c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 908c2ecf20Sopenharmony_ci frames_per_buffer, frames_per_buffer); 918c2ecf20Sopenharmony_ci if (err < 0) { 928c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 938c2ecf20Sopenharmony_ci goto err_locked; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_cierr_locked: 1038c2ecf20Sopenharmony_ci snd_tscm_stream_lock_release(tscm); 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci snd_tscm_stream_lock_release(tscm); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int pcm_hw_params(struct snd_pcm_substream *substream, 1178c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1208c2ecf20Sopenharmony_ci int err = 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 1238c2ecf20Sopenharmony_ci unsigned int rate = params_rate(hw_params); 1248c2ecf20Sopenharmony_ci unsigned int frames_per_period = params_period_size(hw_params); 1258c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = params_buffer_size(hw_params); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mutex_lock(&tscm->mutex); 1288c2ecf20Sopenharmony_ci err = snd_tscm_stream_reserve_duplex(tscm, rate, 1298c2ecf20Sopenharmony_ci frames_per_period, frames_per_buffer); 1308c2ecf20Sopenharmony_ci if (err >= 0) 1318c2ecf20Sopenharmony_ci ++tscm->substreams_counter; 1328c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int pcm_hw_free(struct snd_pcm_substream *substream) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mutex_lock(&tscm->mutex); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 1458c2ecf20Sopenharmony_ci --tscm->substreams_counter; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci snd_tscm_stream_stop_duplex(tscm); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int pcm_capture_prepare(struct snd_pcm_substream *substream) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1578c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1588c2ecf20Sopenharmony_ci int err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci mutex_lock(&tscm->mutex); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = snd_tscm_stream_start_duplex(tscm, runtime->rate); 1638c2ecf20Sopenharmony_ci if (err >= 0) 1648c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&tscm->tx_stream); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return err; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int pcm_playback_prepare(struct snd_pcm_substream *substream) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1748c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1758c2ecf20Sopenharmony_ci int err; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci mutex_lock(&tscm->mutex); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci err = snd_tscm_stream_start_duplex(tscm, runtime->rate); 1808c2ecf20Sopenharmony_ci if (err >= 0) 1818c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&tscm->rx_stream); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mutex_unlock(&tscm->mutex); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return err; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (cmd) { 1938c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1948c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&tscm->tx_stream, substream); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1978c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci switch (cmd) { 2118c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2128c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&tscm->rx_stream, substream); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2158c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL); 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci default: 2188c2ecf20Sopenharmony_ci return -EINVAL; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct snd_tscm *tscm = sbstrm->private_data; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct snd_tscm *tscm = sbstrm->private_data; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int pcm_capture_ack(struct snd_pcm_substream *substream) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int pcm_playback_ack(struct snd_pcm_substream *substream) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct snd_tscm *tscm = substream->private_data; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciint snd_tscm_create_pcm_devices(struct snd_tscm *tscm) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci static const struct snd_pcm_ops capture_ops = { 2558c2ecf20Sopenharmony_ci .open = pcm_open, 2568c2ecf20Sopenharmony_ci .close = pcm_close, 2578c2ecf20Sopenharmony_ci .hw_params = pcm_hw_params, 2588c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 2598c2ecf20Sopenharmony_ci .prepare = pcm_capture_prepare, 2608c2ecf20Sopenharmony_ci .trigger = pcm_capture_trigger, 2618c2ecf20Sopenharmony_ci .pointer = pcm_capture_pointer, 2628c2ecf20Sopenharmony_ci .ack = pcm_capture_ack, 2638c2ecf20Sopenharmony_ci }; 2648c2ecf20Sopenharmony_ci static const struct snd_pcm_ops playback_ops = { 2658c2ecf20Sopenharmony_ci .open = pcm_open, 2668c2ecf20Sopenharmony_ci .close = pcm_close, 2678c2ecf20Sopenharmony_ci .hw_params = pcm_hw_params, 2688c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 2698c2ecf20Sopenharmony_ci .prepare = pcm_playback_prepare, 2708c2ecf20Sopenharmony_ci .trigger = pcm_playback_trigger, 2718c2ecf20Sopenharmony_ci .pointer = pcm_playback_pointer, 2728c2ecf20Sopenharmony_ci .ack = pcm_playback_ack, 2738c2ecf20Sopenharmony_ci }; 2748c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2758c2ecf20Sopenharmony_ci int err; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm); 2788c2ecf20Sopenharmony_ci if (err < 0) 2798c2ecf20Sopenharmony_ci return err; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pcm->private_data = tscm; 2828c2ecf20Sopenharmony_ci snprintf(pcm->name, sizeof(pcm->name), 2838c2ecf20Sopenharmony_ci "%s PCM", tscm->card->shortname); 2848c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 2858c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 2868c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 290