18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * motu-pcm.c - a part of driver for MOTU FireWire series 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 98c2ecf20Sopenharmony_ci#include "motu.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int motu_rate_constraint(struct snd_pcm_hw_params *params, 128c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct snd_motu_packet_format *formats = rule->private; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci const struct snd_interval *c = 178c2ecf20Sopenharmony_ci hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); 188c2ecf20Sopenharmony_ci struct snd_interval *r = 198c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 208c2ecf20Sopenharmony_ci struct snd_interval rates = { 218c2ecf20Sopenharmony_ci .min = UINT_MAX, .max = 0, .integer = 1 228c2ecf20Sopenharmony_ci }; 238c2ecf20Sopenharmony_ci unsigned int i, pcm_channels, rate, mode; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { 268c2ecf20Sopenharmony_ci rate = snd_motu_clock_rates[i]; 278c2ecf20Sopenharmony_ci mode = i / 2; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci pcm_channels = formats->pcm_chunks[mode]; 308c2ecf20Sopenharmony_ci if (!snd_interval_test(c, pcm_channels)) 318c2ecf20Sopenharmony_ci continue; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci rates.min = min(rates.min, rate); 348c2ecf20Sopenharmony_ci rates.max = max(rates.max, rate); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return snd_interval_refine(r, &rates); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int motu_channels_constraint(struct snd_pcm_hw_params *params, 418c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct snd_motu_packet_format *formats = rule->private; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci const struct snd_interval *r = 468c2ecf20Sopenharmony_ci hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); 478c2ecf20Sopenharmony_ci struct snd_interval *c = 488c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 498c2ecf20Sopenharmony_ci struct snd_interval channels = { 508c2ecf20Sopenharmony_ci .min = UINT_MAX, .max = 0, .integer = 1 518c2ecf20Sopenharmony_ci }; 528c2ecf20Sopenharmony_ci unsigned int i, pcm_channels, rate, mode; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { 558c2ecf20Sopenharmony_ci rate = snd_motu_clock_rates[i]; 568c2ecf20Sopenharmony_ci mode = i / 2; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (!snd_interval_test(r, rate)) 598c2ecf20Sopenharmony_ci continue; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci pcm_channels = formats->pcm_chunks[mode]; 628c2ecf20Sopenharmony_ci channels.min = min(channels.min, pcm_channels); 638c2ecf20Sopenharmony_ci channels.max = max(channels.max, pcm_channels); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return snd_interval_refine(c, &channels); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void limit_channels_and_rates(struct snd_motu *motu, 708c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime, 718c2ecf20Sopenharmony_ci struct snd_motu_packet_format *formats) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw = &runtime->hw; 748c2ecf20Sopenharmony_ci unsigned int i, pcm_channels, rate, mode; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci hw->channels_min = UINT_MAX; 778c2ecf20Sopenharmony_ci hw->channels_max = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { 808c2ecf20Sopenharmony_ci rate = snd_motu_clock_rates[i]; 818c2ecf20Sopenharmony_ci mode = i / 2; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci pcm_channels = formats->pcm_chunks[mode]; 848c2ecf20Sopenharmony_ci if (pcm_channels == 0) 858c2ecf20Sopenharmony_ci continue; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci hw->rates |= snd_pcm_rate_to_rate_bit(rate); 888c2ecf20Sopenharmony_ci hw->channels_min = min(hw->channels_min, pcm_channels); 898c2ecf20Sopenharmony_ci hw->channels_max = max(hw->channels_max, pcm_channels); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci snd_pcm_limit_hw_rates(runtime); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int init_hw_info(struct snd_motu *motu, 968c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 998c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw = &runtime->hw; 1008c2ecf20Sopenharmony_ci struct amdtp_stream *stream; 1018c2ecf20Sopenharmony_ci struct snd_motu_packet_format *formats; 1028c2ecf20Sopenharmony_ci int err; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 1058c2ecf20Sopenharmony_ci hw->formats = SNDRV_PCM_FMTBIT_S32; 1068c2ecf20Sopenharmony_ci stream = &motu->tx_stream; 1078c2ecf20Sopenharmony_ci formats = &motu->tx_packet_formats; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci hw->formats = SNDRV_PCM_FMTBIT_S32; 1108c2ecf20Sopenharmony_ci stream = &motu->rx_stream; 1118c2ecf20Sopenharmony_ci formats = &motu->rx_packet_formats; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci limit_channels_and_rates(motu, runtime, formats); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 1178c2ecf20Sopenharmony_ci motu_rate_constraint, formats, 1188c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 1198c2ecf20Sopenharmony_ci if (err < 0) 1208c2ecf20Sopenharmony_ci return err; 1218c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 1228c2ecf20Sopenharmony_ci motu_channels_constraint, formats, 1238c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 1248c2ecf20Sopenharmony_ci if (err < 0) 1258c2ecf20Sopenharmony_ci return err; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return amdtp_motu_add_pcm_hw_constraints(stream, runtime); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 1338c2ecf20Sopenharmony_ci struct amdtp_domain *d = &motu->domain; 1348c2ecf20Sopenharmony_ci enum snd_motu_clock_source src; 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = snd_motu_stream_lock_try(motu); 1388c2ecf20Sopenharmony_ci if (err < 0) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci mutex_lock(&motu->mutex); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = snd_motu_stream_cache_packet_formats(motu); 1448c2ecf20Sopenharmony_ci if (err < 0) 1458c2ecf20Sopenharmony_ci goto err_locked; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci err = init_hw_info(motu, substream); 1488c2ecf20Sopenharmony_ci if (err < 0) 1498c2ecf20Sopenharmony_ci goto err_locked; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci err = snd_motu_protocol_get_clock_source(motu, &src); 1528c2ecf20Sopenharmony_ci if (err < 0) 1538c2ecf20Sopenharmony_ci goto err_locked; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci // When source of clock is not internal or any stream is reserved for 1568c2ecf20Sopenharmony_ci // transmission of PCM frames, the available sampling rate is limited 1578c2ecf20Sopenharmony_ci // at current one. 1588c2ecf20Sopenharmony_ci if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL && 1598c2ecf20Sopenharmony_ci src != SND_MOTU_CLOCK_SOURCE_SPH) || 1608c2ecf20Sopenharmony_ci (motu->substreams_counter > 0 && d->events_per_period > 0)) { 1618c2ecf20Sopenharmony_ci unsigned int frames_per_period = d->events_per_period; 1628c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = d->events_per_buffer; 1638c2ecf20Sopenharmony_ci unsigned int rate; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = snd_motu_protocol_get_clock_rate(motu, &rate); 1668c2ecf20Sopenharmony_ci if (err < 0) 1678c2ecf20Sopenharmony_ci goto err_locked; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci substream->runtime->hw.rate_min = rate; 1708c2ecf20Sopenharmony_ci substream->runtime->hw.rate_max = rate; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (frames_per_period > 0) { 1738c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 1748c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 1758c2ecf20Sopenharmony_ci frames_per_period, frames_per_period); 1768c2ecf20Sopenharmony_ci if (err < 0) 1778c2ecf20Sopenharmony_ci goto err_locked; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 1808c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 1818c2ecf20Sopenharmony_ci frames_per_buffer, frames_per_buffer); 1828c2ecf20Sopenharmony_ci if (err < 0) 1838c2ecf20Sopenharmony_ci goto err_locked; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_cierr_locked: 1938c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 1948c2ecf20Sopenharmony_ci snd_motu_stream_lock_release(motu); 1958c2ecf20Sopenharmony_ci return err; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci snd_motu_stream_lock_release(motu); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int pcm_hw_params(struct snd_pcm_substream *substream, 2088c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2118c2ecf20Sopenharmony_ci int err = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 2148c2ecf20Sopenharmony_ci unsigned int rate = params_rate(hw_params); 2158c2ecf20Sopenharmony_ci unsigned int frames_per_period = params_period_size(hw_params); 2168c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = params_buffer_size(hw_params); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mutex_lock(&motu->mutex); 2198c2ecf20Sopenharmony_ci err = snd_motu_stream_reserve_duplex(motu, rate, 2208c2ecf20Sopenharmony_ci frames_per_period, frames_per_buffer); 2218c2ecf20Sopenharmony_ci if (err >= 0) 2228c2ecf20Sopenharmony_ci ++motu->substreams_counter; 2238c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return err; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int pcm_hw_free(struct snd_pcm_substream *substream) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&motu->mutex); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 2368c2ecf20Sopenharmony_ci --motu->substreams_counter; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci snd_motu_stream_stop_duplex(motu); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int capture_prepare(struct snd_pcm_substream *substream) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2488c2ecf20Sopenharmony_ci int err; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci mutex_lock(&motu->mutex); 2518c2ecf20Sopenharmony_ci err = snd_motu_stream_start_duplex(motu); 2528c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 2538c2ecf20Sopenharmony_ci if (err >= 0) 2548c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&motu->tx_stream); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_cistatic int playback_prepare(struct snd_pcm_substream *substream) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2618c2ecf20Sopenharmony_ci int err; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci mutex_lock(&motu->mutex); 2648c2ecf20Sopenharmony_ci err = snd_motu_stream_start_duplex(motu); 2658c2ecf20Sopenharmony_ci mutex_unlock(&motu->mutex); 2668c2ecf20Sopenharmony_ci if (err >= 0) 2678c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&motu->rx_stream); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return err; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int capture_trigger(struct snd_pcm_substream *substream, int cmd) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci switch (cmd) { 2778c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2788c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&motu->tx_stream, substream); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2818c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&motu->tx_stream, NULL); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci default: 2848c2ecf20Sopenharmony_ci return -EINVAL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_cistatic int playback_trigger(struct snd_pcm_substream *substream, int cmd) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (cmd) { 2948c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2958c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&motu->rx_stream, substream); 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2988c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&motu->rx_stream, NULL); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int capture_ack(struct snd_pcm_substream *substream) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int playback_ack(struct snd_pcm_substream *substream) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct snd_motu *motu = substream->private_data; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciint snd_motu_create_pcm_devices(struct snd_motu *motu) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci static const struct snd_pcm_ops capture_ops = { 3378c2ecf20Sopenharmony_ci .open = pcm_open, 3388c2ecf20Sopenharmony_ci .close = pcm_close, 3398c2ecf20Sopenharmony_ci .hw_params = pcm_hw_params, 3408c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 3418c2ecf20Sopenharmony_ci .prepare = capture_prepare, 3428c2ecf20Sopenharmony_ci .trigger = capture_trigger, 3438c2ecf20Sopenharmony_ci .pointer = capture_pointer, 3448c2ecf20Sopenharmony_ci .ack = capture_ack, 3458c2ecf20Sopenharmony_ci }; 3468c2ecf20Sopenharmony_ci static const struct snd_pcm_ops playback_ops = { 3478c2ecf20Sopenharmony_ci .open = pcm_open, 3488c2ecf20Sopenharmony_ci .close = pcm_close, 3498c2ecf20Sopenharmony_ci .hw_params = pcm_hw_params, 3508c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 3518c2ecf20Sopenharmony_ci .prepare = playback_prepare, 3528c2ecf20Sopenharmony_ci .trigger = playback_trigger, 3538c2ecf20Sopenharmony_ci .pointer = playback_pointer, 3548c2ecf20Sopenharmony_ci .ack = playback_ack, 3558c2ecf20Sopenharmony_ci }; 3568c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 3578c2ecf20Sopenharmony_ci int err; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm); 3608c2ecf20Sopenharmony_ci if (err < 0) 3618c2ecf20Sopenharmony_ci return err; 3628c2ecf20Sopenharmony_ci pcm->private_data = motu; 3638c2ecf20Sopenharmony_ci strcpy(pcm->name, motu->card->shortname); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 3668c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 3678c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 371