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