18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fireworks_stream.c - a part of driver for Fireworks based devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "./fireworks.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define CALLBACK_TIMEOUT 100 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int init_stream(struct snd_efw *efw, struct amdtp_stream *stream) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct cmp_connection *conn; 148c2ecf20Sopenharmony_ci enum cmp_direction c_dir; 158c2ecf20Sopenharmony_ci enum amdtp_stream_direction s_dir; 168c2ecf20Sopenharmony_ci int err; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci if (stream == &efw->tx_stream) { 198c2ecf20Sopenharmony_ci conn = &efw->out_conn; 208c2ecf20Sopenharmony_ci c_dir = CMP_OUTPUT; 218c2ecf20Sopenharmony_ci s_dir = AMDTP_IN_STREAM; 228c2ecf20Sopenharmony_ci } else { 238c2ecf20Sopenharmony_ci conn = &efw->in_conn; 248c2ecf20Sopenharmony_ci c_dir = CMP_INPUT; 258c2ecf20Sopenharmony_ci s_dir = AMDTP_OUT_STREAM; 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci err = cmp_connection_init(conn, efw->unit, c_dir, 0); 298c2ecf20Sopenharmony_ci if (err < 0) 308c2ecf20Sopenharmony_ci return err; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); 338c2ecf20Sopenharmony_ci if (err < 0) { 348c2ecf20Sopenharmony_ci amdtp_stream_destroy(stream); 358c2ecf20Sopenharmony_ci cmp_connection_destroy(conn); 368c2ecf20Sopenharmony_ci return err; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (stream == &efw->tx_stream) { 408c2ecf20Sopenharmony_ci // Fireworks transmits NODATA packets with TAG0. 418c2ecf20Sopenharmony_ci efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0; 428c2ecf20Sopenharmony_ci // Fireworks has its own meaning for dbc. 438c2ecf20Sopenharmony_ci efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT; 448c2ecf20Sopenharmony_ci // Fireworks reset dbc at bus reset. 458c2ecf20Sopenharmony_ci efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK; 468c2ecf20Sopenharmony_ci // But Recent firmwares starts packets with non-zero dbc. 478c2ecf20Sopenharmony_ci // Driver version 5.7.6 installs firmware version 5.7.3. 488c2ecf20Sopenharmony_ci if (efw->is_fireworks3 && 498c2ecf20Sopenharmony_ci (efw->firmware_version == 0x5070000 || 508c2ecf20Sopenharmony_ci efw->firmware_version == 0x5070300 || 518c2ecf20Sopenharmony_ci efw->firmware_version == 0x5080000)) 528c2ecf20Sopenharmony_ci efw->tx_stream.flags |= CIP_UNALIGHED_DBC; 538c2ecf20Sopenharmony_ci // AudioFire9 always reports wrong dbs. 548c2ecf20Sopenharmony_ci if (efw->is_af9) 558c2ecf20Sopenharmony_ci efw->tx_stream.flags |= CIP_WRONG_DBS; 568c2ecf20Sopenharmony_ci // Firmware version 5.5 reports fixed interval for dbc. 578c2ecf20Sopenharmony_ci if (efw->firmware_version == 0x5050000) 588c2ecf20Sopenharmony_ci efw->tx_stream.ctx_data.tx.dbc_interval = 8; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return err; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int start_stream(struct snd_efw *efw, struct amdtp_stream *stream, 658c2ecf20Sopenharmony_ci unsigned int rate) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct cmp_connection *conn; 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (stream == &efw->tx_stream) 718c2ecf20Sopenharmony_ci conn = &efw->out_conn; 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci conn = &efw->in_conn; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci // Establish connection via CMP. 768c2ecf20Sopenharmony_ci err = cmp_connection_establish(conn); 778c2ecf20Sopenharmony_ci if (err < 0) 788c2ecf20Sopenharmony_ci return err; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci // Start amdtp stream. 818c2ecf20Sopenharmony_ci err = amdtp_domain_add_stream(&efw->domain, stream, 828c2ecf20Sopenharmony_ci conn->resources.channel, conn->speed); 838c2ecf20Sopenharmony_ci if (err < 0) { 848c2ecf20Sopenharmony_ci cmp_connection_break(conn); 858c2ecf20Sopenharmony_ci return err; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci// This function should be called before starting the stream or after stopping 928c2ecf20Sopenharmony_ci// the streams. 938c2ecf20Sopenharmony_cistatic void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci amdtp_stream_destroy(stream); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (stream == &efw->tx_stream) 988c2ecf20Sopenharmony_ci cmp_connection_destroy(&efw->out_conn); 998c2ecf20Sopenharmony_ci else 1008c2ecf20Sopenharmony_ci cmp_connection_destroy(&efw->in_conn); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int 1048c2ecf20Sopenharmony_cicheck_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct cmp_connection *conn; 1078c2ecf20Sopenharmony_ci bool used; 1088c2ecf20Sopenharmony_ci int err; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (s == &efw->tx_stream) 1118c2ecf20Sopenharmony_ci conn = &efw->out_conn; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci conn = &efw->in_conn; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci err = cmp_connection_check_used(conn, &used); 1168c2ecf20Sopenharmony_ci if ((err >= 0) && used && !amdtp_stream_running(s)) { 1178c2ecf20Sopenharmony_ci dev_err(&efw->unit->device, 1188c2ecf20Sopenharmony_ci "Connection established by others: %cPCR[%d]\n", 1198c2ecf20Sopenharmony_ci (conn->direction == CMP_OUTPUT) ? 'o' : 'i', 1208c2ecf20Sopenharmony_ci conn->pcr_index); 1218c2ecf20Sopenharmony_ci err = -EBUSY; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return err; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciint snd_efw_stream_init_duplex(struct snd_efw *efw) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int err; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci err = init_stream(efw, &efw->tx_stream); 1328c2ecf20Sopenharmony_ci if (err < 0) 1338c2ecf20Sopenharmony_ci return err; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci err = init_stream(efw, &efw->rx_stream); 1368c2ecf20Sopenharmony_ci if (err < 0) { 1378c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->tx_stream); 1388c2ecf20Sopenharmony_ci return err; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = amdtp_domain_init(&efw->domain); 1428c2ecf20Sopenharmony_ci if (err < 0) { 1438c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->tx_stream); 1448c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->rx_stream); 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci // set IEC61883 compliant mode (actually not fully compliant...). 1498c2ecf20Sopenharmony_ci err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883); 1508c2ecf20Sopenharmony_ci if (err < 0) { 1518c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->tx_stream); 1528c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->rx_stream); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, 1598c2ecf20Sopenharmony_ci unsigned int rate, unsigned int mode) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned int pcm_channels; 1628c2ecf20Sopenharmony_ci unsigned int midi_ports; 1638c2ecf20Sopenharmony_ci struct cmp_connection *conn; 1648c2ecf20Sopenharmony_ci int err; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (stream == &efw->tx_stream) { 1678c2ecf20Sopenharmony_ci pcm_channels = efw->pcm_capture_channels[mode]; 1688c2ecf20Sopenharmony_ci midi_ports = efw->midi_out_ports; 1698c2ecf20Sopenharmony_ci conn = &efw->out_conn; 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci pcm_channels = efw->pcm_playback_channels[mode]; 1728c2ecf20Sopenharmony_ci midi_ports = efw->midi_in_ports; 1738c2ecf20Sopenharmony_ci conn = &efw->in_conn; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = amdtp_am824_set_parameters(stream, rate, pcm_channels, 1778c2ecf20Sopenharmony_ci midi_ports, false); 1788c2ecf20Sopenharmony_ci if (err < 0) 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciint snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, 1858c2ecf20Sopenharmony_ci unsigned int frames_per_period, 1868c2ecf20Sopenharmony_ci unsigned int frames_per_buffer) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned int curr_rate; 1898c2ecf20Sopenharmony_ci int err; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci // Considering JACK/FFADO streaming: 1928c2ecf20Sopenharmony_ci // TODO: This can be removed hwdep functionality becomes popular. 1938c2ecf20Sopenharmony_ci err = check_connection_used_by_others(efw, &efw->rx_stream); 1948c2ecf20Sopenharmony_ci if (err < 0) 1958c2ecf20Sopenharmony_ci return err; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci // stop streams if rate is different. 1988c2ecf20Sopenharmony_ci err = snd_efw_command_get_sampling_rate(efw, &curr_rate); 1998c2ecf20Sopenharmony_ci if (err < 0) 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci if (rate == 0) 2028c2ecf20Sopenharmony_ci rate = curr_rate; 2038c2ecf20Sopenharmony_ci if (rate != curr_rate) { 2048c2ecf20Sopenharmony_ci amdtp_domain_stop(&efw->domain); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci cmp_connection_break(&efw->out_conn); 2078c2ecf20Sopenharmony_ci cmp_connection_break(&efw->in_conn); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci cmp_connection_release(&efw->out_conn); 2108c2ecf20Sopenharmony_ci cmp_connection_release(&efw->in_conn); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (efw->substreams_counter == 0 || rate != curr_rate) { 2148c2ecf20Sopenharmony_ci unsigned int mode; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci err = snd_efw_command_set_sampling_rate(efw, rate); 2178c2ecf20Sopenharmony_ci if (err < 0) 2188c2ecf20Sopenharmony_ci return err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci err = snd_efw_get_multiplier_mode(rate, &mode); 2218c2ecf20Sopenharmony_ci if (err < 0) 2228c2ecf20Sopenharmony_ci return err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci err = keep_resources(efw, &efw->tx_stream, rate, mode); 2258c2ecf20Sopenharmony_ci if (err < 0) 2268c2ecf20Sopenharmony_ci return err; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci err = keep_resources(efw, &efw->rx_stream, rate, mode); 2298c2ecf20Sopenharmony_ci if (err < 0) { 2308c2ecf20Sopenharmony_ci cmp_connection_release(&efw->in_conn); 2318c2ecf20Sopenharmony_ci return err; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci err = amdtp_domain_set_events_per_period(&efw->domain, 2358c2ecf20Sopenharmony_ci frames_per_period, frames_per_buffer); 2368c2ecf20Sopenharmony_ci if (err < 0) { 2378c2ecf20Sopenharmony_ci cmp_connection_release(&efw->in_conn); 2388c2ecf20Sopenharmony_ci cmp_connection_release(&efw->out_conn); 2398c2ecf20Sopenharmony_ci return err; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciint snd_efw_stream_start_duplex(struct snd_efw *efw) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci unsigned int rate; 2498c2ecf20Sopenharmony_ci int err = 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci // Need no substreams. 2528c2ecf20Sopenharmony_ci if (efw->substreams_counter == 0) 2538c2ecf20Sopenharmony_ci return -EIO; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (amdtp_streaming_error(&efw->rx_stream) || 2568c2ecf20Sopenharmony_ci amdtp_streaming_error(&efw->tx_stream)) { 2578c2ecf20Sopenharmony_ci amdtp_domain_stop(&efw->domain); 2588c2ecf20Sopenharmony_ci cmp_connection_break(&efw->out_conn); 2598c2ecf20Sopenharmony_ci cmp_connection_break(&efw->in_conn); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = snd_efw_command_get_sampling_rate(efw, &rate); 2638c2ecf20Sopenharmony_ci if (err < 0) 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (!amdtp_stream_running(&efw->rx_stream)) { 2678c2ecf20Sopenharmony_ci err = start_stream(efw, &efw->rx_stream, rate); 2688c2ecf20Sopenharmony_ci if (err < 0) 2698c2ecf20Sopenharmony_ci goto error; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = start_stream(efw, &efw->tx_stream, rate); 2728c2ecf20Sopenharmony_ci if (err < 0) 2738c2ecf20Sopenharmony_ci goto error; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci err = amdtp_domain_start(&efw->domain, 0); 2768c2ecf20Sopenharmony_ci if (err < 0) 2778c2ecf20Sopenharmony_ci goto error; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci // Wait first callback. 2808c2ecf20Sopenharmony_ci if (!amdtp_stream_wait_callback(&efw->rx_stream, 2818c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT) || 2828c2ecf20Sopenharmony_ci !amdtp_stream_wait_callback(&efw->tx_stream, 2838c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT)) { 2848c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 2858c2ecf20Sopenharmony_ci goto error; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_cierror: 2918c2ecf20Sopenharmony_ci amdtp_domain_stop(&efw->domain); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci cmp_connection_break(&efw->out_conn); 2948c2ecf20Sopenharmony_ci cmp_connection_break(&efw->in_conn); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return err; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid snd_efw_stream_stop_duplex(struct snd_efw *efw) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci if (efw->substreams_counter == 0) { 3028c2ecf20Sopenharmony_ci amdtp_domain_stop(&efw->domain); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cmp_connection_break(&efw->out_conn); 3058c2ecf20Sopenharmony_ci cmp_connection_break(&efw->in_conn); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci cmp_connection_release(&efw->out_conn); 3088c2ecf20Sopenharmony_ci cmp_connection_release(&efw->in_conn); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_civoid snd_efw_stream_update_duplex(struct snd_efw *efw) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci amdtp_domain_stop(&efw->domain); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci cmp_connection_break(&efw->out_conn); 3178c2ecf20Sopenharmony_ci cmp_connection_break(&efw->in_conn); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci amdtp_stream_pcm_abort(&efw->rx_stream); 3208c2ecf20Sopenharmony_ci amdtp_stream_pcm_abort(&efw->tx_stream); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_civoid snd_efw_stream_destroy_duplex(struct snd_efw *efw) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci amdtp_domain_destroy(&efw->domain); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->rx_stream); 3288c2ecf20Sopenharmony_ci destroy_stream(efw, &efw->tx_stream); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_civoid snd_efw_stream_lock_changed(struct snd_efw *efw) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci efw->dev_lock_changed = true; 3348c2ecf20Sopenharmony_ci wake_up(&efw->hwdep_wait); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciint snd_efw_stream_lock_try(struct snd_efw *efw) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock_irq(&efw->lock); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* user land lock this */ 3448c2ecf20Sopenharmony_ci if (efw->dev_lock_count < 0) { 3458c2ecf20Sopenharmony_ci err = -EBUSY; 3468c2ecf20Sopenharmony_ci goto end; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* this is the first time */ 3508c2ecf20Sopenharmony_ci if (efw->dev_lock_count++ == 0) 3518c2ecf20Sopenharmony_ci snd_efw_stream_lock_changed(efw); 3528c2ecf20Sopenharmony_ci err = 0; 3538c2ecf20Sopenharmony_ciend: 3548c2ecf20Sopenharmony_ci spin_unlock_irq(&efw->lock); 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_civoid snd_efw_stream_lock_release(struct snd_efw *efw) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci spin_lock_irq(&efw->lock); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (WARN_ON(efw->dev_lock_count <= 0)) 3638c2ecf20Sopenharmony_ci goto end; 3648c2ecf20Sopenharmony_ci if (--efw->dev_lock_count == 0) 3658c2ecf20Sopenharmony_ci snd_efw_stream_lock_changed(efw); 3668c2ecf20Sopenharmony_ciend: 3678c2ecf20Sopenharmony_ci spin_unlock_irq(&efw->lock); 3688c2ecf20Sopenharmony_ci} 369