18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * oxfw_pcm.c - a part of driver for OXFW970/971 based devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "oxfw.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic int hw_rule_rate(struct snd_pcm_hw_params *params, 118c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci u8 **formats = rule->private; 148c2ecf20Sopenharmony_ci struct snd_interval *r = 158c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 168c2ecf20Sopenharmony_ci const struct snd_interval *c = 178c2ecf20Sopenharmony_ci hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); 188c2ecf20Sopenharmony_ci struct snd_interval t = { 198c2ecf20Sopenharmony_ci .min = UINT_MAX, .max = 0, .integer = 1 208c2ecf20Sopenharmony_ci }; 218c2ecf20Sopenharmony_ci struct snd_oxfw_stream_formation formation; 228c2ecf20Sopenharmony_ci int i, err; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 258c2ecf20Sopenharmony_ci if (formats[i] == NULL) 268c2ecf20Sopenharmony_ci continue; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci err = snd_oxfw_stream_parse_format(formats[i], &formation); 298c2ecf20Sopenharmony_ci if (err < 0) 308c2ecf20Sopenharmony_ci continue; 318c2ecf20Sopenharmony_ci if (!snd_interval_test(c, formation.pcm)) 328c2ecf20Sopenharmony_ci continue; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci t.min = min(t.min, formation.rate); 358c2ecf20Sopenharmony_ci t.max = max(t.max, formation.rate); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci return snd_interval_refine(r, &t); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int hw_rule_channels(struct snd_pcm_hw_params *params, 428c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci u8 **formats = rule->private; 458c2ecf20Sopenharmony_ci struct snd_interval *c = 468c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 478c2ecf20Sopenharmony_ci const struct snd_interval *r = 488c2ecf20Sopenharmony_ci hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); 498c2ecf20Sopenharmony_ci struct snd_oxfw_stream_formation formation; 508c2ecf20Sopenharmony_ci int i, j, err; 518c2ecf20Sopenharmony_ci unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci count = 0; 548c2ecf20Sopenharmony_ci for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 558c2ecf20Sopenharmony_ci if (formats[i] == NULL) 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci err = snd_oxfw_stream_parse_format(formats[i], &formation); 598c2ecf20Sopenharmony_ci if (err < 0) 608c2ecf20Sopenharmony_ci continue; 618c2ecf20Sopenharmony_ci if (!snd_interval_test(r, formation.rate)) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci if (list[count] == formation.pcm) 648c2ecf20Sopenharmony_ci continue; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(list); j++) { 678c2ecf20Sopenharmony_ci if (list[j] == formation.pcm) 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci if (j == ARRAY_SIZE(list)) { 718c2ecf20Sopenharmony_ci list[count] = formation.pcm; 728c2ecf20Sopenharmony_ci if (++count == ARRAY_SIZE(list)) 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return snd_interval_list(c, count, list, 0); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct snd_oxfw_stream_formation formation; 838c2ecf20Sopenharmony_ci int i, err; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci hw->channels_min = UINT_MAX; 868c2ecf20Sopenharmony_ci hw->channels_max = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci hw->rate_min = UINT_MAX; 898c2ecf20Sopenharmony_ci hw->rate_max = 0; 908c2ecf20Sopenharmony_ci hw->rates = 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 938c2ecf20Sopenharmony_ci if (formats[i] == NULL) 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci err = snd_oxfw_stream_parse_format(formats[i], &formation); 978c2ecf20Sopenharmony_ci if (err < 0) 988c2ecf20Sopenharmony_ci continue; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci hw->channels_min = min(hw->channels_min, formation.pcm); 1018c2ecf20Sopenharmony_ci hw->channels_max = max(hw->channels_max, formation.pcm); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci hw->rate_min = min(hw->rate_min, formation.rate); 1048c2ecf20Sopenharmony_ci hw->rate_max = max(hw->rate_max, formation.rate); 1058c2ecf20Sopenharmony_ci hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int init_hw_params(struct snd_oxfw *oxfw, 1108c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1138c2ecf20Sopenharmony_ci u8 **formats; 1148c2ecf20Sopenharmony_ci struct amdtp_stream *stream; 1158c2ecf20Sopenharmony_ci int err; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 1188c2ecf20Sopenharmony_ci runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; 1198c2ecf20Sopenharmony_ci stream = &oxfw->tx_stream; 1208c2ecf20Sopenharmony_ci formats = oxfw->tx_stream_formats; 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; 1238c2ecf20Sopenharmony_ci stream = &oxfw->rx_stream; 1248c2ecf20Sopenharmony_ci formats = oxfw->rx_stream_formats; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci limit_channels_and_rates(&runtime->hw, formats); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 1308c2ecf20Sopenharmony_ci hw_rule_channels, formats, 1318c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 1328c2ecf20Sopenharmony_ci if (err < 0) 1338c2ecf20Sopenharmony_ci goto end; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 1368c2ecf20Sopenharmony_ci hw_rule_rate, formats, 1378c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 1388c2ecf20Sopenharmony_ci if (err < 0) 1398c2ecf20Sopenharmony_ci goto end; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); 1428c2ecf20Sopenharmony_ciend: 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int limit_to_current_params(struct snd_pcm_substream *substream) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 1498c2ecf20Sopenharmony_ci struct snd_oxfw_stream_formation formation; 1508c2ecf20Sopenharmony_ci enum avc_general_plug_dir dir; 1518c2ecf20Sopenharmony_ci int err; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 1548c2ecf20Sopenharmony_ci dir = AVC_GENERAL_PLUG_DIR_OUT; 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci dir = AVC_GENERAL_PLUG_DIR_IN; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); 1598c2ecf20Sopenharmony_ci if (err < 0) 1608c2ecf20Sopenharmony_ci goto end; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci substream->runtime->hw.channels_min = formation.pcm; 1638c2ecf20Sopenharmony_ci substream->runtime->hw.channels_max = formation.pcm; 1648c2ecf20Sopenharmony_ci substream->runtime->hw.rate_min = formation.rate; 1658c2ecf20Sopenharmony_ci substream->runtime->hw.rate_max = formation.rate; 1668c2ecf20Sopenharmony_ciend: 1678c2ecf20Sopenharmony_ci return err; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 1738c2ecf20Sopenharmony_ci struct amdtp_domain *d = &oxfw->domain; 1748c2ecf20Sopenharmony_ci int err; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = snd_oxfw_stream_lock_try(oxfw); 1778c2ecf20Sopenharmony_ci if (err < 0) 1788c2ecf20Sopenharmony_ci return err; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci err = init_hw_params(oxfw, substream); 1818c2ecf20Sopenharmony_ci if (err < 0) 1828c2ecf20Sopenharmony_ci goto err_locked; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci // When source of clock is not internal or any stream is reserved for 1878c2ecf20Sopenharmony_ci // transmission of PCM frames, the available sampling rate is limited 1888c2ecf20Sopenharmony_ci // at current one. 1898c2ecf20Sopenharmony_ci if (oxfw->substreams_count > 0 && d->events_per_period > 0) { 1908c2ecf20Sopenharmony_ci unsigned int frames_per_period = d->events_per_period; 1918c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = d->events_per_buffer; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err = limit_to_current_params(substream); 1948c2ecf20Sopenharmony_ci if (err < 0) { 1958c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 1968c2ecf20Sopenharmony_ci goto err_locked; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (frames_per_period > 0) { 2008c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 2018c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2028c2ecf20Sopenharmony_ci frames_per_period, frames_per_period); 2038c2ecf20Sopenharmony_ci if (err < 0) { 2048c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2058c2ecf20Sopenharmony_ci goto err_locked; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(substream->runtime, 2098c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 2108c2ecf20Sopenharmony_ci frames_per_buffer, frames_per_buffer); 2118c2ecf20Sopenharmony_ci if (err < 0) { 2128c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2138c2ecf20Sopenharmony_ci goto err_locked; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_cierr_locked: 2248c2ecf20Sopenharmony_ci snd_oxfw_stream_lock_release(oxfw); 2258c2ecf20Sopenharmony_ci return err; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci snd_oxfw_stream_lock_release(oxfw); 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int pcm_capture_hw_params(struct snd_pcm_substream *substream, 2378c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 2408c2ecf20Sopenharmony_ci int err = 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 2438c2ecf20Sopenharmony_ci unsigned int rate = params_rate(hw_params); 2448c2ecf20Sopenharmony_ci unsigned int channels = params_channels(hw_params); 2458c2ecf20Sopenharmony_ci unsigned int frames_per_period = params_period_size(hw_params); 2468c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = params_buffer_size(hw_params); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 2498c2ecf20Sopenharmony_ci err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 2508c2ecf20Sopenharmony_ci rate, channels, frames_per_period, 2518c2ecf20Sopenharmony_ci frames_per_buffer); 2528c2ecf20Sopenharmony_ci if (err >= 0) 2538c2ecf20Sopenharmony_ci ++oxfw->substreams_count; 2548c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return err; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_cistatic int pcm_playback_hw_params(struct snd_pcm_substream *substream, 2608c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 2638c2ecf20Sopenharmony_ci int err = 0; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 2668c2ecf20Sopenharmony_ci unsigned int rate = params_rate(hw_params); 2678c2ecf20Sopenharmony_ci unsigned int channels = params_channels(hw_params); 2688c2ecf20Sopenharmony_ci unsigned int frames_per_period = params_period_size(hw_params); 2698c2ecf20Sopenharmony_ci unsigned int frames_per_buffer = params_buffer_size(hw_params); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 2728c2ecf20Sopenharmony_ci err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 2738c2ecf20Sopenharmony_ci rate, channels, frames_per_period, 2748c2ecf20Sopenharmony_ci frames_per_buffer); 2758c2ecf20Sopenharmony_ci if (err >= 0) 2768c2ecf20Sopenharmony_ci ++oxfw->substreams_count; 2778c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return err; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int pcm_capture_hw_free(struct snd_pcm_substream *substream) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 2908c2ecf20Sopenharmony_ci --oxfw->substreams_count; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci snd_oxfw_stream_stop_duplex(oxfw); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_cistatic int pcm_playback_hw_free(struct snd_pcm_substream *substream) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 3058c2ecf20Sopenharmony_ci --oxfw->substreams_count; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci snd_oxfw_stream_stop_duplex(oxfw); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int pcm_capture_prepare(struct snd_pcm_substream *substream) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3178c2ecf20Sopenharmony_ci int err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 3208c2ecf20Sopenharmony_ci err = snd_oxfw_stream_start_duplex(oxfw); 3218c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 3228c2ecf20Sopenharmony_ci if (err < 0) 3238c2ecf20Sopenharmony_ci goto end; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&oxfw->tx_stream); 3268c2ecf20Sopenharmony_ciend: 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_cistatic int pcm_playback_prepare(struct snd_pcm_substream *substream) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3328c2ecf20Sopenharmony_ci int err; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci mutex_lock(&oxfw->mutex); 3358c2ecf20Sopenharmony_ci err = snd_oxfw_stream_start_duplex(oxfw); 3368c2ecf20Sopenharmony_ci mutex_unlock(&oxfw->mutex); 3378c2ecf20Sopenharmony_ci if (err < 0) 3388c2ecf20Sopenharmony_ci goto end; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci amdtp_stream_pcm_prepare(&oxfw->rx_stream); 3418c2ecf20Sopenharmony_ciend: 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3488c2ecf20Sopenharmony_ci struct snd_pcm_substream *pcm; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci switch (cmd) { 3518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3528c2ecf20Sopenharmony_ci pcm = substream; 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3558c2ecf20Sopenharmony_ci pcm = NULL; 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci default: 3588c2ecf20Sopenharmony_ci return -EINVAL; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm); 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_cistatic int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3668c2ecf20Sopenharmony_ci struct snd_pcm_substream *pcm; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci switch (cmd) { 3698c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3708c2ecf20Sopenharmony_ci pcm = substream; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3738c2ecf20Sopenharmony_ci pcm = NULL; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = sbstm->private_data; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = sbstm->private_data; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int pcm_capture_ack(struct snd_pcm_substream *substream) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int pcm_playback_ack(struct snd_pcm_substream *substream) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct snd_oxfw *oxfw = substream->private_data; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ciint snd_oxfw_create_pcm(struct snd_oxfw *oxfw) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci static const struct snd_pcm_ops capture_ops = { 4128c2ecf20Sopenharmony_ci .open = pcm_open, 4138c2ecf20Sopenharmony_ci .close = pcm_close, 4148c2ecf20Sopenharmony_ci .hw_params = pcm_capture_hw_params, 4158c2ecf20Sopenharmony_ci .hw_free = pcm_capture_hw_free, 4168c2ecf20Sopenharmony_ci .prepare = pcm_capture_prepare, 4178c2ecf20Sopenharmony_ci .trigger = pcm_capture_trigger, 4188c2ecf20Sopenharmony_ci .pointer = pcm_capture_pointer, 4198c2ecf20Sopenharmony_ci .ack = pcm_capture_ack, 4208c2ecf20Sopenharmony_ci }; 4218c2ecf20Sopenharmony_ci static const struct snd_pcm_ops playback_ops = { 4228c2ecf20Sopenharmony_ci .open = pcm_open, 4238c2ecf20Sopenharmony_ci .close = pcm_close, 4248c2ecf20Sopenharmony_ci .hw_params = pcm_playback_hw_params, 4258c2ecf20Sopenharmony_ci .hw_free = pcm_playback_hw_free, 4268c2ecf20Sopenharmony_ci .prepare = pcm_playback_prepare, 4278c2ecf20Sopenharmony_ci .trigger = pcm_playback_trigger, 4288c2ecf20Sopenharmony_ci .pointer = pcm_playback_pointer, 4298c2ecf20Sopenharmony_ci .ack = pcm_playback_ack, 4308c2ecf20Sopenharmony_ci }; 4318c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 4328c2ecf20Sopenharmony_ci unsigned int cap = 0; 4338c2ecf20Sopenharmony_ci int err; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (oxfw->has_output) 4368c2ecf20Sopenharmony_ci cap = 1; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm); 4398c2ecf20Sopenharmony_ci if (err < 0) 4408c2ecf20Sopenharmony_ci return err; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pcm->private_data = oxfw; 4438c2ecf20Sopenharmony_ci strcpy(pcm->name, oxfw->card->shortname); 4448c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 4458c2ecf20Sopenharmony_ci if (cap > 0) 4468c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 4478c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 451