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