xref: /linux/sound/firewire/oxfw/oxfw-stream.c (revision 8985f4ac1c42bd25799f294f4e87fa73064673c7)
1e2786ca6STakashi Sakamoto /*
2e2786ca6STakashi Sakamoto  * oxfw_stream.c - a part of driver for OXFW970/971 based devices
3e2786ca6STakashi Sakamoto  *
4e2786ca6STakashi Sakamoto  * Copyright (c) 2014 Takashi Sakamoto
5e2786ca6STakashi Sakamoto  *
6e2786ca6STakashi Sakamoto  * Licensed under the terms of the GNU General Public License, version 2.
7e2786ca6STakashi Sakamoto  */
8e2786ca6STakashi Sakamoto 
9e2786ca6STakashi Sakamoto #include "oxfw.h"
10f3699e2cSTakashi Sakamoto #include <linux/delay.h>
11e2786ca6STakashi Sakamoto 
125cd1d3f4STakashi Sakamoto #define AVC_GENERIC_FRAME_MAXIMUM_BYTES	512
13f3699e2cSTakashi Sakamoto #define CALLBACK_TIMEOUT	200
145cd1d3f4STakashi Sakamoto 
155cd1d3f4STakashi Sakamoto /*
165cd1d3f4STakashi Sakamoto  * According to datasheet of Oxford Semiconductor:
175cd1d3f4STakashi Sakamoto  *  OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
185cd1d3f4STakashi Sakamoto  *  OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
195cd1d3f4STakashi Sakamoto  */
205cd1d3f4STakashi Sakamoto static const unsigned int oxfw_rate_table[] = {
215cd1d3f4STakashi Sakamoto 	[0] = 32000,
225cd1d3f4STakashi Sakamoto 	[1] = 44100,
235cd1d3f4STakashi Sakamoto 	[2] = 48000,
245cd1d3f4STakashi Sakamoto 	[3] = 88200,
255cd1d3f4STakashi Sakamoto 	[4] = 96000,
265cd1d3f4STakashi Sakamoto 	[5] = 192000,
275cd1d3f4STakashi Sakamoto };
285cd1d3f4STakashi Sakamoto 
295cd1d3f4STakashi Sakamoto /*
305cd1d3f4STakashi Sakamoto  * See Table 5.7 – Sampling frequency for Multi-bit Audio
315cd1d3f4STakashi Sakamoto  * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
325cd1d3f4STakashi Sakamoto  */
335cd1d3f4STakashi Sakamoto static const unsigned int avc_stream_rate_table[] = {
345cd1d3f4STakashi Sakamoto 	[0] = 0x02,
355cd1d3f4STakashi Sakamoto 	[1] = 0x03,
365cd1d3f4STakashi Sakamoto 	[2] = 0x04,
375cd1d3f4STakashi Sakamoto 	[3] = 0x0a,
385cd1d3f4STakashi Sakamoto 	[4] = 0x05,
395cd1d3f4STakashi Sakamoto 	[5] = 0x07,
405cd1d3f4STakashi Sakamoto };
415cd1d3f4STakashi Sakamoto 
42b0ac0009STakashi Sakamoto static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
43b0ac0009STakashi Sakamoto {
44b0ac0009STakashi Sakamoto 	int err;
45b0ac0009STakashi Sakamoto 
46b0ac0009STakashi Sakamoto 	err = avc_general_set_sig_fmt(oxfw->unit, rate,
47b0ac0009STakashi Sakamoto 				      AVC_GENERAL_PLUG_DIR_IN, 0);
48b0ac0009STakashi Sakamoto 	if (err < 0)
49b0ac0009STakashi Sakamoto 		goto end;
50b0ac0009STakashi Sakamoto 
51b0ac0009STakashi Sakamoto 	if (oxfw->has_output)
52b0ac0009STakashi Sakamoto 		err = avc_general_set_sig_fmt(oxfw->unit, rate,
53b0ac0009STakashi Sakamoto 					      AVC_GENERAL_PLUG_DIR_OUT, 0);
54b0ac0009STakashi Sakamoto end:
55b0ac0009STakashi Sakamoto 	return err;
56b0ac0009STakashi Sakamoto }
57b0ac0009STakashi Sakamoto 
58f3699e2cSTakashi Sakamoto static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
59f3699e2cSTakashi Sakamoto 			     unsigned int rate, unsigned int pcm_channels)
60f3699e2cSTakashi Sakamoto {
61f3699e2cSTakashi Sakamoto 	u8 **formats;
62f3699e2cSTakashi Sakamoto 	struct snd_oxfw_stream_formation formation;
63f3699e2cSTakashi Sakamoto 	enum avc_general_plug_dir dir;
64f3699e2cSTakashi Sakamoto 	unsigned int i, err, len;
65f3699e2cSTakashi Sakamoto 
66b0ac0009STakashi Sakamoto 	if (s == &oxfw->tx_stream) {
67b0ac0009STakashi Sakamoto 		formats = oxfw->tx_stream_formats;
68b0ac0009STakashi Sakamoto 		dir = AVC_GENERAL_PLUG_DIR_OUT;
69b0ac0009STakashi Sakamoto 	} else {
70f3699e2cSTakashi Sakamoto 		formats = oxfw->rx_stream_formats;
71f3699e2cSTakashi Sakamoto 		dir = AVC_GENERAL_PLUG_DIR_IN;
72b0ac0009STakashi Sakamoto 	}
73f3699e2cSTakashi Sakamoto 
74f3699e2cSTakashi Sakamoto 	/* Seek stream format for requirements. */
75f3699e2cSTakashi Sakamoto 	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
76f3699e2cSTakashi Sakamoto 		err = snd_oxfw_stream_parse_format(formats[i], &formation);
77f3699e2cSTakashi Sakamoto 		if (err < 0)
78f3699e2cSTakashi Sakamoto 			return err;
79f3699e2cSTakashi Sakamoto 
80f3699e2cSTakashi Sakamoto 		if ((formation.rate == rate) && (formation.pcm == pcm_channels))
81f3699e2cSTakashi Sakamoto 			break;
82f3699e2cSTakashi Sakamoto 	}
83f3699e2cSTakashi Sakamoto 	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
84f3699e2cSTakashi Sakamoto 		return -EINVAL;
85f3699e2cSTakashi Sakamoto 
86f3699e2cSTakashi Sakamoto 	/* If assumed, just change rate. */
87f3699e2cSTakashi Sakamoto 	if (oxfw->assumed)
88b0ac0009STakashi Sakamoto 		return set_rate(oxfw, rate);
89f3699e2cSTakashi Sakamoto 
90f3699e2cSTakashi Sakamoto 	/* Calculate format length. */
91f3699e2cSTakashi Sakamoto 	len = 5 + formats[i][4] * 2;
92f3699e2cSTakashi Sakamoto 
93f3699e2cSTakashi Sakamoto 	err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
94f3699e2cSTakashi Sakamoto 	if (err < 0)
95f3699e2cSTakashi Sakamoto 		return err;
96f3699e2cSTakashi Sakamoto 
97f3699e2cSTakashi Sakamoto 	/* Some requests just after changing format causes freezing. */
98f3699e2cSTakashi Sakamoto 	msleep(100);
99f3699e2cSTakashi Sakamoto 
100f3699e2cSTakashi Sakamoto 	return 0;
101f3699e2cSTakashi Sakamoto }
102f3699e2cSTakashi Sakamoto 
103b0ac0009STakashi Sakamoto static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
104e2786ca6STakashi Sakamoto {
105b0ac0009STakashi Sakamoto 	amdtp_stream_pcm_abort(stream);
106b0ac0009STakashi Sakamoto 	amdtp_stream_stop(stream);
107e2786ca6STakashi Sakamoto 
108b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream)
109b0ac0009STakashi Sakamoto 		cmp_connection_break(&oxfw->out_conn);
110b0ac0009STakashi Sakamoto 	else
111e2786ca6STakashi Sakamoto 		cmp_connection_break(&oxfw->in_conn);
112e2786ca6STakashi Sakamoto }
113e2786ca6STakashi Sakamoto 
114b0ac0009STakashi Sakamoto static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
115b0ac0009STakashi Sakamoto 			unsigned int rate, unsigned int pcm_channels)
116e2786ca6STakashi Sakamoto {
117f3699e2cSTakashi Sakamoto 	u8 **formats;
118f3699e2cSTakashi Sakamoto 	struct cmp_connection *conn;
119f3699e2cSTakashi Sakamoto 	struct snd_oxfw_stream_formation formation;
120f3699e2cSTakashi Sakamoto 	unsigned int i, midi_ports;
121f3699e2cSTakashi Sakamoto 	int err;
122f3699e2cSTakashi Sakamoto 
123b0ac0009STakashi Sakamoto 	if (stream == &oxfw->rx_stream) {
124f3699e2cSTakashi Sakamoto 		formats = oxfw->rx_stream_formats;
125f3699e2cSTakashi Sakamoto 		conn = &oxfw->in_conn;
126b0ac0009STakashi Sakamoto 	} else {
127b0ac0009STakashi Sakamoto 		formats = oxfw->tx_stream_formats;
128b0ac0009STakashi Sakamoto 		conn = &oxfw->out_conn;
129b0ac0009STakashi Sakamoto 	}
130f3699e2cSTakashi Sakamoto 
131b0ac0009STakashi Sakamoto 	/* Get stream format */
132f3699e2cSTakashi Sakamoto 	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
133f3699e2cSTakashi Sakamoto 		if (formats[i] == NULL)
134f3699e2cSTakashi Sakamoto 			break;
135f3699e2cSTakashi Sakamoto 
136f3699e2cSTakashi Sakamoto 		err = snd_oxfw_stream_parse_format(formats[i], &formation);
137f3699e2cSTakashi Sakamoto 		if (err < 0)
138f3699e2cSTakashi Sakamoto 			goto end;
139f3699e2cSTakashi Sakamoto 		if (rate != formation.rate)
140f3699e2cSTakashi Sakamoto 			continue;
141f3699e2cSTakashi Sakamoto 		if (pcm_channels == 0 ||  pcm_channels == formation.pcm)
142f3699e2cSTakashi Sakamoto 			break;
143f3699e2cSTakashi Sakamoto 	}
144f3699e2cSTakashi Sakamoto 	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
145f3699e2cSTakashi Sakamoto 		err = -EINVAL;
146f3699e2cSTakashi Sakamoto 		goto end;
147f3699e2cSTakashi Sakamoto 	}
148f3699e2cSTakashi Sakamoto 
149f3699e2cSTakashi Sakamoto 	pcm_channels = formation.pcm;
150f3699e2cSTakashi Sakamoto 	midi_ports = DIV_ROUND_UP(formation.midi, 8);
151f3699e2cSTakashi Sakamoto 
152f3699e2cSTakashi Sakamoto 	/* The stream should have one pcm channels at least */
153f3699e2cSTakashi Sakamoto 	if (pcm_channels == 0) {
154f3699e2cSTakashi Sakamoto 		err = -EINVAL;
155f3699e2cSTakashi Sakamoto 		goto end;
156f3699e2cSTakashi Sakamoto 	}
157f3699e2cSTakashi Sakamoto 	amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
158f3699e2cSTakashi Sakamoto 
159f3699e2cSTakashi Sakamoto 	err = cmp_connection_establish(conn,
160f3699e2cSTakashi Sakamoto 				       amdtp_stream_get_max_payload(stream));
161f3699e2cSTakashi Sakamoto 	if (err < 0)
162f3699e2cSTakashi Sakamoto 		goto end;
163f3699e2cSTakashi Sakamoto 
164f3699e2cSTakashi Sakamoto 	err = amdtp_stream_start(stream,
165f3699e2cSTakashi Sakamoto 				 conn->resources.channel,
166f3699e2cSTakashi Sakamoto 				 conn->speed);
167f3699e2cSTakashi Sakamoto 	if (err < 0) {
168f3699e2cSTakashi Sakamoto 		cmp_connection_break(conn);
169f3699e2cSTakashi Sakamoto 		goto end;
170f3699e2cSTakashi Sakamoto 	}
171f3699e2cSTakashi Sakamoto 
172f3699e2cSTakashi Sakamoto 	/* Wait first packet */
173f3699e2cSTakashi Sakamoto 	err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
174f3699e2cSTakashi Sakamoto 	if (err < 0)
175b0ac0009STakashi Sakamoto 		stop_stream(oxfw, stream);
176f3699e2cSTakashi Sakamoto end:
177f3699e2cSTakashi Sakamoto 	return err;
178f3699e2cSTakashi Sakamoto }
179f3699e2cSTakashi Sakamoto 
180b0ac0009STakashi Sakamoto static int check_connection_used_by_others(struct snd_oxfw *oxfw,
181b0ac0009STakashi Sakamoto 					   struct amdtp_stream *stream)
182f3699e2cSTakashi Sakamoto {
183b0ac0009STakashi Sakamoto 	struct cmp_connection *conn;
184b0ac0009STakashi Sakamoto 	bool used;
185b0ac0009STakashi Sakamoto 	int err;
186b0ac0009STakashi Sakamoto 
187b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream)
188b0ac0009STakashi Sakamoto 		conn = &oxfw->out_conn;
189b0ac0009STakashi Sakamoto 	else
190b0ac0009STakashi Sakamoto 		conn = &oxfw->in_conn;
191b0ac0009STakashi Sakamoto 
192b0ac0009STakashi Sakamoto 	err = cmp_connection_check_used(conn, &used);
193b0ac0009STakashi Sakamoto 	if ((err >= 0) && used && !amdtp_stream_running(stream)) {
194b0ac0009STakashi Sakamoto 		dev_err(&oxfw->unit->device,
195b0ac0009STakashi Sakamoto 			"Connection established by others: %cPCR[%d]\n",
196b0ac0009STakashi Sakamoto 			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
197b0ac0009STakashi Sakamoto 			conn->pcr_index);
198b0ac0009STakashi Sakamoto 		err = -EBUSY;
199b0ac0009STakashi Sakamoto 	}
200b0ac0009STakashi Sakamoto 
201b0ac0009STakashi Sakamoto 	return err;
202b0ac0009STakashi Sakamoto }
203b0ac0009STakashi Sakamoto 
204b0ac0009STakashi Sakamoto int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
205b0ac0009STakashi Sakamoto 				 struct amdtp_stream *stream)
206b0ac0009STakashi Sakamoto {
207b0ac0009STakashi Sakamoto 	struct cmp_connection *conn;
208b0ac0009STakashi Sakamoto 	enum cmp_direction c_dir;
209b0ac0009STakashi Sakamoto 	enum amdtp_stream_direction s_dir;
210b0ac0009STakashi Sakamoto 	int err;
211b0ac0009STakashi Sakamoto 
212b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream) {
213b0ac0009STakashi Sakamoto 		conn = &oxfw->out_conn;
214b0ac0009STakashi Sakamoto 		c_dir = CMP_OUTPUT;
215b0ac0009STakashi Sakamoto 		s_dir = AMDTP_IN_STREAM;
216b0ac0009STakashi Sakamoto 	} else {
217b0ac0009STakashi Sakamoto 		conn = &oxfw->in_conn;
218b0ac0009STakashi Sakamoto 		c_dir = CMP_INPUT;
219b0ac0009STakashi Sakamoto 		s_dir = AMDTP_OUT_STREAM;
220b0ac0009STakashi Sakamoto 	}
221b0ac0009STakashi Sakamoto 
222b0ac0009STakashi Sakamoto 	err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
223b0ac0009STakashi Sakamoto 	if (err < 0)
224b0ac0009STakashi Sakamoto 		goto end;
225b0ac0009STakashi Sakamoto 
226b0ac0009STakashi Sakamoto 	err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
227b0ac0009STakashi Sakamoto 	if (err < 0) {
228b0ac0009STakashi Sakamoto 		amdtp_stream_destroy(stream);
229b0ac0009STakashi Sakamoto 		cmp_connection_destroy(conn);
230b0ac0009STakashi Sakamoto 		goto end;
231b0ac0009STakashi Sakamoto 	}
232b0ac0009STakashi Sakamoto 
233b0ac0009STakashi Sakamoto 	/* OXFW starts to transmit packets with non-zero dbc. */
234b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream)
235b0ac0009STakashi Sakamoto 		oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
236b0ac0009STakashi Sakamoto end:
237b0ac0009STakashi Sakamoto 	return err;
238b0ac0009STakashi Sakamoto }
239b0ac0009STakashi Sakamoto 
240b0ac0009STakashi Sakamoto int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
241b0ac0009STakashi Sakamoto 				  struct amdtp_stream *stream,
242b0ac0009STakashi Sakamoto 				  unsigned int rate, unsigned int pcm_channels)
243b0ac0009STakashi Sakamoto {
244b0ac0009STakashi Sakamoto 	struct amdtp_stream *opposite;
245f3699e2cSTakashi Sakamoto 	struct snd_oxfw_stream_formation formation;
246b0ac0009STakashi Sakamoto 	enum avc_general_plug_dir dir;
247b0ac0009STakashi Sakamoto 	unsigned int substreams, opposite_substreams;
248e2786ca6STakashi Sakamoto 	int err = 0;
249e2786ca6STakashi Sakamoto 
250b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream) {
251b0ac0009STakashi Sakamoto 		substreams = oxfw->capture_substreams;
252b0ac0009STakashi Sakamoto 		opposite = &oxfw->rx_stream;
253b0ac0009STakashi Sakamoto 		opposite_substreams = oxfw->playback_substreams;
254b0ac0009STakashi Sakamoto 		dir = AVC_GENERAL_PLUG_DIR_OUT;
255b0ac0009STakashi Sakamoto 	} else {
256b0ac0009STakashi Sakamoto 		substreams = oxfw->playback_substreams;
257b0ac0009STakashi Sakamoto 		opposite_substreams = oxfw->capture_substreams;
258e2786ca6STakashi Sakamoto 
259b0ac0009STakashi Sakamoto 		if (oxfw->has_output)
260b0ac0009STakashi Sakamoto 			opposite = &oxfw->rx_stream;
261b0ac0009STakashi Sakamoto 		else
262b0ac0009STakashi Sakamoto 			opposite = NULL;
263b0ac0009STakashi Sakamoto 
264b0ac0009STakashi Sakamoto 		dir = AVC_GENERAL_PLUG_DIR_IN;
265b0ac0009STakashi Sakamoto 	}
266b0ac0009STakashi Sakamoto 
267b0ac0009STakashi Sakamoto 	if (substreams == 0)
268b0ac0009STakashi Sakamoto 		goto end;
269b0ac0009STakashi Sakamoto 
270b0ac0009STakashi Sakamoto 	/*
271b0ac0009STakashi Sakamoto 	 * Considering JACK/FFADO streaming:
272b0ac0009STakashi Sakamoto 	 * TODO: This can be removed hwdep functionality becomes popular.
273b0ac0009STakashi Sakamoto 	 */
274b0ac0009STakashi Sakamoto 	err = check_connection_used_by_others(oxfw, stream);
275b0ac0009STakashi Sakamoto 	if (err < 0)
276b0ac0009STakashi Sakamoto 		goto end;
277b0ac0009STakashi Sakamoto 
278b0ac0009STakashi Sakamoto 	/* packet queueing error */
279b0ac0009STakashi Sakamoto 	if (amdtp_streaming_error(stream))
280b0ac0009STakashi Sakamoto 		stop_stream(oxfw, stream);
281b0ac0009STakashi Sakamoto 
282b0ac0009STakashi Sakamoto 	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
283e2786ca6STakashi Sakamoto 	if (err < 0)
284e2786ca6STakashi Sakamoto 		goto end;
28505588d34STakashi Sakamoto 	if (rate == 0)
28605588d34STakashi Sakamoto 		rate = formation.rate;
28705588d34STakashi Sakamoto 	if (pcm_channels == 0)
28805588d34STakashi Sakamoto 		pcm_channels = formation.pcm;
289e2786ca6STakashi Sakamoto 
290f3699e2cSTakashi Sakamoto 	if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
291b0ac0009STakashi Sakamoto 		if (opposite != NULL) {
292b0ac0009STakashi Sakamoto 			err = check_connection_used_by_others(oxfw, opposite);
293b0ac0009STakashi Sakamoto 			if (err < 0)
294b0ac0009STakashi Sakamoto 				goto end;
295b0ac0009STakashi Sakamoto 			stop_stream(oxfw, opposite);
296b0ac0009STakashi Sakamoto 		}
297b0ac0009STakashi Sakamoto 		stop_stream(oxfw, stream);
298f3699e2cSTakashi Sakamoto 
299b0ac0009STakashi Sakamoto 		err = set_stream_format(oxfw, stream, rate, pcm_channels);
300f3699e2cSTakashi Sakamoto 		if (err < 0) {
301f3699e2cSTakashi Sakamoto 			dev_err(&oxfw->unit->device,
302f3699e2cSTakashi Sakamoto 				"fail to set stream format: %d\n", err);
303f3699e2cSTakashi Sakamoto 			goto end;
304f3699e2cSTakashi Sakamoto 		}
305b0ac0009STakashi Sakamoto 
306b0ac0009STakashi Sakamoto 		/* Start opposite stream if needed. */
307b0ac0009STakashi Sakamoto 		if (opposite && !amdtp_stream_running(opposite) &&
308b0ac0009STakashi Sakamoto 		    (opposite_substreams > 0)) {
309b0ac0009STakashi Sakamoto 			err = start_stream(oxfw, opposite, rate, 0);
310b0ac0009STakashi Sakamoto 			if (err < 0) {
311b0ac0009STakashi Sakamoto 				dev_err(&oxfw->unit->device,
312b0ac0009STakashi Sakamoto 					"fail to restart stream: %d\n", err);
313b0ac0009STakashi Sakamoto 				goto end;
314b0ac0009STakashi Sakamoto 			}
315b0ac0009STakashi Sakamoto 		}
316f3699e2cSTakashi Sakamoto 	}
317f3699e2cSTakashi Sakamoto 
318b0ac0009STakashi Sakamoto 	/* Start requested stream. */
319b0ac0009STakashi Sakamoto 	if (!amdtp_stream_running(stream)) {
320b0ac0009STakashi Sakamoto 		err = start_stream(oxfw, stream, rate, pcm_channels);
321f3699e2cSTakashi Sakamoto 		if (err < 0)
322f3699e2cSTakashi Sakamoto 			dev_err(&oxfw->unit->device,
323f3699e2cSTakashi Sakamoto 				"fail to start stream: %d\n", err);
324b0ac0009STakashi Sakamoto 	}
325e2786ca6STakashi Sakamoto end:
326e2786ca6STakashi Sakamoto 	return err;
327e2786ca6STakashi Sakamoto }
328e2786ca6STakashi Sakamoto 
329b0ac0009STakashi Sakamoto void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
330b0ac0009STakashi Sakamoto 				  struct amdtp_stream *stream)
331e2786ca6STakashi Sakamoto {
332b0ac0009STakashi Sakamoto 	if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
333b0ac0009STakashi Sakamoto 	    ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
334b0ac0009STakashi Sakamoto 		return;
335b0ac0009STakashi Sakamoto 
336b0ac0009STakashi Sakamoto 	stop_stream(oxfw, stream);
337e2786ca6STakashi Sakamoto }
338e2786ca6STakashi Sakamoto 
339b0ac0009STakashi Sakamoto void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
340b0ac0009STakashi Sakamoto 				     struct amdtp_stream *stream)
341e2786ca6STakashi Sakamoto {
342b0ac0009STakashi Sakamoto 	struct cmp_connection *conn;
343e2786ca6STakashi Sakamoto 
344b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream)
345b0ac0009STakashi Sakamoto 		conn = &oxfw->out_conn;
346e2786ca6STakashi Sakamoto 	else
347b0ac0009STakashi Sakamoto 		conn = &oxfw->in_conn;
348b0ac0009STakashi Sakamoto 
349b0ac0009STakashi Sakamoto 	stop_stream(oxfw, stream);
350b0ac0009STakashi Sakamoto 
351b0ac0009STakashi Sakamoto 	amdtp_stream_destroy(stream);
352b0ac0009STakashi Sakamoto 	cmp_connection_destroy(conn);
353b0ac0009STakashi Sakamoto }
354b0ac0009STakashi Sakamoto 
355b0ac0009STakashi Sakamoto void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
356b0ac0009STakashi Sakamoto 				    struct amdtp_stream *stream)
357b0ac0009STakashi Sakamoto {
358b0ac0009STakashi Sakamoto 	struct cmp_connection *conn;
359b0ac0009STakashi Sakamoto 
360b0ac0009STakashi Sakamoto 	if (stream == &oxfw->tx_stream)
361b0ac0009STakashi Sakamoto 		conn = &oxfw->out_conn;
362b0ac0009STakashi Sakamoto 	else
363b0ac0009STakashi Sakamoto 		conn = &oxfw->in_conn;
364b0ac0009STakashi Sakamoto 
365b0ac0009STakashi Sakamoto 	if (cmp_connection_update(conn) < 0)
366b0ac0009STakashi Sakamoto 		stop_stream(oxfw, stream);
367b0ac0009STakashi Sakamoto 	else
368b0ac0009STakashi Sakamoto 		amdtp_stream_update(stream);
369e2786ca6STakashi Sakamoto }
3705cd1d3f4STakashi Sakamoto 
3713c96101fSTakashi Sakamoto int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
3723c96101fSTakashi Sakamoto 				enum avc_general_plug_dir dir,
3733c96101fSTakashi Sakamoto 				struct snd_oxfw_stream_formation *formation)
3743c96101fSTakashi Sakamoto {
3753c96101fSTakashi Sakamoto 	u8 *format;
3763c96101fSTakashi Sakamoto 	unsigned int len;
3773c96101fSTakashi Sakamoto 	int err;
3783c96101fSTakashi Sakamoto 
3793c96101fSTakashi Sakamoto 	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
3803c96101fSTakashi Sakamoto 	format = kmalloc(len, GFP_KERNEL);
3813c96101fSTakashi Sakamoto 	if (format == NULL)
3823c96101fSTakashi Sakamoto 		return -ENOMEM;
3833c96101fSTakashi Sakamoto 
3843c96101fSTakashi Sakamoto 	err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
3853c96101fSTakashi Sakamoto 	if (err < 0)
3863c96101fSTakashi Sakamoto 		goto end;
3873c96101fSTakashi Sakamoto 	if (len < 3) {
3883c96101fSTakashi Sakamoto 		err = -EIO;
3893c96101fSTakashi Sakamoto 		goto end;
3903c96101fSTakashi Sakamoto 	}
3913c96101fSTakashi Sakamoto 
3923c96101fSTakashi Sakamoto 	err = snd_oxfw_stream_parse_format(format, formation);
3933c96101fSTakashi Sakamoto end:
3943c96101fSTakashi Sakamoto 	kfree(format);
3953c96101fSTakashi Sakamoto 	return err;
3963c96101fSTakashi Sakamoto }
3973c96101fSTakashi Sakamoto 
3985cd1d3f4STakashi Sakamoto /*
3995cd1d3f4STakashi Sakamoto  * See Table 6.16 - AM824 Stream Format
4005cd1d3f4STakashi Sakamoto  *     Figure 6.19 - format_information field for AM824 Compound
4015cd1d3f4STakashi Sakamoto  * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
4025cd1d3f4STakashi Sakamoto  * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
4035cd1d3f4STakashi Sakamoto  */
4045cd1d3f4STakashi Sakamoto int snd_oxfw_stream_parse_format(u8 *format,
4055cd1d3f4STakashi Sakamoto 				 struct snd_oxfw_stream_formation *formation)
4065cd1d3f4STakashi Sakamoto {
4075cd1d3f4STakashi Sakamoto 	unsigned int i, e, channels, type;
4085cd1d3f4STakashi Sakamoto 
4095cd1d3f4STakashi Sakamoto 	memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
4105cd1d3f4STakashi Sakamoto 
4115cd1d3f4STakashi Sakamoto 	/*
4125cd1d3f4STakashi Sakamoto 	 * this module can support a hierarchy combination that:
4135cd1d3f4STakashi Sakamoto 	 *  Root:	Audio and Music (0x90)
4145cd1d3f4STakashi Sakamoto 	 *  Level 1:	AM824 Compound  (0x40)
4155cd1d3f4STakashi Sakamoto 	 */
4165cd1d3f4STakashi Sakamoto 	if ((format[0] != 0x90) || (format[1] != 0x40))
4175cd1d3f4STakashi Sakamoto 		return -ENOSYS;
4185cd1d3f4STakashi Sakamoto 
4195cd1d3f4STakashi Sakamoto 	/* check the sampling rate */
4205cd1d3f4STakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
4215cd1d3f4STakashi Sakamoto 		if (format[2] == avc_stream_rate_table[i])
4225cd1d3f4STakashi Sakamoto 			break;
4235cd1d3f4STakashi Sakamoto 	}
4245cd1d3f4STakashi Sakamoto 	if (i == ARRAY_SIZE(avc_stream_rate_table))
4255cd1d3f4STakashi Sakamoto 		return -ENOSYS;
4265cd1d3f4STakashi Sakamoto 
4275cd1d3f4STakashi Sakamoto 	formation->rate = oxfw_rate_table[i];
4285cd1d3f4STakashi Sakamoto 
4295cd1d3f4STakashi Sakamoto 	for (e = 0; e < format[4]; e++) {
4305cd1d3f4STakashi Sakamoto 		channels = format[5 + e * 2];
4315cd1d3f4STakashi Sakamoto 		type = format[6 + e * 2];
4325cd1d3f4STakashi Sakamoto 
4335cd1d3f4STakashi Sakamoto 		switch (type) {
4345cd1d3f4STakashi Sakamoto 		/* IEC 60958 Conformant, currently handled as MBLA */
4355cd1d3f4STakashi Sakamoto 		case 0x00:
4365cd1d3f4STakashi Sakamoto 		/* Multi Bit Linear Audio (Raw) */
4375cd1d3f4STakashi Sakamoto 		case 0x06:
4385cd1d3f4STakashi Sakamoto 			formation->pcm += channels;
4395cd1d3f4STakashi Sakamoto 			break;
4405cd1d3f4STakashi Sakamoto 		/* MIDI Conformant */
4415cd1d3f4STakashi Sakamoto 		case 0x0d:
4425cd1d3f4STakashi Sakamoto 			formation->midi = channels;
4435cd1d3f4STakashi Sakamoto 			break;
4445cd1d3f4STakashi Sakamoto 		/* IEC 61937-3 to 7 */
4455cd1d3f4STakashi Sakamoto 		case 0x01:
4465cd1d3f4STakashi Sakamoto 		case 0x02:
4475cd1d3f4STakashi Sakamoto 		case 0x03:
4485cd1d3f4STakashi Sakamoto 		case 0x04:
4495cd1d3f4STakashi Sakamoto 		case 0x05:
4505cd1d3f4STakashi Sakamoto 		/* Multi Bit Linear Audio */
4515cd1d3f4STakashi Sakamoto 		case 0x07:	/* DVD-Audio */
4525cd1d3f4STakashi Sakamoto 		case 0x0c:	/* High Precision */
4535cd1d3f4STakashi Sakamoto 		/* One Bit Audio */
4545cd1d3f4STakashi Sakamoto 		case 0x08:	/* (Plain) Raw */
4555cd1d3f4STakashi Sakamoto 		case 0x09:	/* (Plain) SACD */
4565cd1d3f4STakashi Sakamoto 		case 0x0a:	/* (Encoded) Raw */
4575cd1d3f4STakashi Sakamoto 		case 0x0b:	/* (Encoded) SACD */
4585cd1d3f4STakashi Sakamoto 		/* SMPTE Time-Code conformant */
4595cd1d3f4STakashi Sakamoto 		case 0x0e:
4605cd1d3f4STakashi Sakamoto 		/* Sample Count */
4615cd1d3f4STakashi Sakamoto 		case 0x0f:
4625cd1d3f4STakashi Sakamoto 		/* Anciliary Data */
4635cd1d3f4STakashi Sakamoto 		case 0x10:
4645cd1d3f4STakashi Sakamoto 		/* Synchronization Stream (Stereo Raw audio) */
4655cd1d3f4STakashi Sakamoto 		case 0x40:
4665cd1d3f4STakashi Sakamoto 		/* Don't care */
4675cd1d3f4STakashi Sakamoto 		case 0xff:
4685cd1d3f4STakashi Sakamoto 		default:
4695cd1d3f4STakashi Sakamoto 			return -ENOSYS;	/* not supported */
4705cd1d3f4STakashi Sakamoto 		}
4715cd1d3f4STakashi Sakamoto 	}
4725cd1d3f4STakashi Sakamoto 
4735cd1d3f4STakashi Sakamoto 	if (formation->pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
4745cd1d3f4STakashi Sakamoto 	    formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
4755cd1d3f4STakashi Sakamoto 		return -ENOSYS;
4765cd1d3f4STakashi Sakamoto 
4775cd1d3f4STakashi Sakamoto 	return 0;
4785cd1d3f4STakashi Sakamoto }
4795cd1d3f4STakashi Sakamoto 
4805cd1d3f4STakashi Sakamoto static int
4815cd1d3f4STakashi Sakamoto assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
4825cd1d3f4STakashi Sakamoto 		      unsigned int pid, u8 *buf, unsigned int *len,
4835cd1d3f4STakashi Sakamoto 		      u8 **formats)
4845cd1d3f4STakashi Sakamoto {
4855cd1d3f4STakashi Sakamoto 	struct snd_oxfw_stream_formation formation;
4865cd1d3f4STakashi Sakamoto 	unsigned int i, eid;
4875cd1d3f4STakashi Sakamoto 	int err;
4885cd1d3f4STakashi Sakamoto 
4895cd1d3f4STakashi Sakamoto 	/* get format at current sampling rate */
4905cd1d3f4STakashi Sakamoto 	err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
4915cd1d3f4STakashi Sakamoto 	if (err < 0) {
4925cd1d3f4STakashi Sakamoto 		dev_err(&oxfw->unit->device,
4935cd1d3f4STakashi Sakamoto 		"fail to get current stream format for isoc %s plug %d:%d\n",
4945cd1d3f4STakashi Sakamoto 			(dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
4955cd1d3f4STakashi Sakamoto 			pid, err);
4965cd1d3f4STakashi Sakamoto 		goto end;
4975cd1d3f4STakashi Sakamoto 	}
4985cd1d3f4STakashi Sakamoto 
4995cd1d3f4STakashi Sakamoto 	/* parse and set stream format */
5005cd1d3f4STakashi Sakamoto 	eid = 0;
5015cd1d3f4STakashi Sakamoto 	err = snd_oxfw_stream_parse_format(buf, &formation);
5025cd1d3f4STakashi Sakamoto 	if (err < 0)
5035cd1d3f4STakashi Sakamoto 		goto end;
5045cd1d3f4STakashi Sakamoto 
5055cd1d3f4STakashi Sakamoto 	formats[eid] = kmalloc(*len, GFP_KERNEL);
5065cd1d3f4STakashi Sakamoto 	if (formats[eid] == NULL) {
5075cd1d3f4STakashi Sakamoto 		err = -ENOMEM;
5085cd1d3f4STakashi Sakamoto 		goto end;
5095cd1d3f4STakashi Sakamoto 	}
5105cd1d3f4STakashi Sakamoto 	memcpy(formats[eid], buf, *len);
5115cd1d3f4STakashi Sakamoto 
5125cd1d3f4STakashi Sakamoto 	/* apply the format for each available sampling rate */
5135cd1d3f4STakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
5145cd1d3f4STakashi Sakamoto 		if (formation.rate == oxfw_rate_table[i])
5155cd1d3f4STakashi Sakamoto 			continue;
5165cd1d3f4STakashi Sakamoto 
5175cd1d3f4STakashi Sakamoto 		err = avc_general_inquiry_sig_fmt(oxfw->unit,
5185cd1d3f4STakashi Sakamoto 						  oxfw_rate_table[i],
5195cd1d3f4STakashi Sakamoto 						  dir, pid);
5205cd1d3f4STakashi Sakamoto 		if (err < 0)
5215cd1d3f4STakashi Sakamoto 			continue;
5225cd1d3f4STakashi Sakamoto 
5235cd1d3f4STakashi Sakamoto 		eid++;
5245cd1d3f4STakashi Sakamoto 		formats[eid] = kmalloc(*len, GFP_KERNEL);
5255cd1d3f4STakashi Sakamoto 		if (formats[eid] == NULL) {
5265cd1d3f4STakashi Sakamoto 			err = -ENOMEM;
5275cd1d3f4STakashi Sakamoto 			goto end;
5285cd1d3f4STakashi Sakamoto 		}
5295cd1d3f4STakashi Sakamoto 		memcpy(formats[eid], buf, *len);
5305cd1d3f4STakashi Sakamoto 		formats[eid][2] = avc_stream_rate_table[i];
5315cd1d3f4STakashi Sakamoto 	}
5325cd1d3f4STakashi Sakamoto 
5335cd1d3f4STakashi Sakamoto 	err = 0;
5345cd1d3f4STakashi Sakamoto 	oxfw->assumed = true;
5355cd1d3f4STakashi Sakamoto end:
5365cd1d3f4STakashi Sakamoto 	return err;
5375cd1d3f4STakashi Sakamoto }
5385cd1d3f4STakashi Sakamoto 
5395cd1d3f4STakashi Sakamoto static int fill_stream_formats(struct snd_oxfw *oxfw,
5405cd1d3f4STakashi Sakamoto 			       enum avc_general_plug_dir dir,
5415cd1d3f4STakashi Sakamoto 			       unsigned short pid)
5425cd1d3f4STakashi Sakamoto {
5435cd1d3f4STakashi Sakamoto 	u8 *buf, **formats;
5445cd1d3f4STakashi Sakamoto 	unsigned int len, eid = 0;
5455cd1d3f4STakashi Sakamoto 	struct snd_oxfw_stream_formation dummy;
5465cd1d3f4STakashi Sakamoto 	int err;
5475cd1d3f4STakashi Sakamoto 
5485cd1d3f4STakashi Sakamoto 	buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
5495cd1d3f4STakashi Sakamoto 	if (buf == NULL)
5505cd1d3f4STakashi Sakamoto 		return -ENOMEM;
5515cd1d3f4STakashi Sakamoto 
552b0ac0009STakashi Sakamoto 	if (dir == AVC_GENERAL_PLUG_DIR_OUT)
553b0ac0009STakashi Sakamoto 		formats = oxfw->tx_stream_formats;
554b0ac0009STakashi Sakamoto 	else
5555cd1d3f4STakashi Sakamoto 		formats = oxfw->rx_stream_formats;
5565cd1d3f4STakashi Sakamoto 
5575cd1d3f4STakashi Sakamoto 	/* get first entry */
5585cd1d3f4STakashi Sakamoto 	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
5595cd1d3f4STakashi Sakamoto 	err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
5605cd1d3f4STakashi Sakamoto 	if (err == -ENOSYS) {
5615cd1d3f4STakashi Sakamoto 		/* LIST subfunction is not implemented */
5625cd1d3f4STakashi Sakamoto 		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
5635cd1d3f4STakashi Sakamoto 		err = assume_stream_formats(oxfw, dir, pid, buf, &len,
5645cd1d3f4STakashi Sakamoto 					    formats);
5655cd1d3f4STakashi Sakamoto 		goto end;
5665cd1d3f4STakashi Sakamoto 	} else if (err < 0) {
5675cd1d3f4STakashi Sakamoto 		dev_err(&oxfw->unit->device,
5685cd1d3f4STakashi Sakamoto 			"fail to get stream format %d for isoc %s plug %d:%d\n",
5695cd1d3f4STakashi Sakamoto 			eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
5705cd1d3f4STakashi Sakamoto 			pid, err);
5715cd1d3f4STakashi Sakamoto 		goto end;
5725cd1d3f4STakashi Sakamoto 	}
5735cd1d3f4STakashi Sakamoto 
5745cd1d3f4STakashi Sakamoto 	/* LIST subfunction is implemented */
5755cd1d3f4STakashi Sakamoto 	while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
5765cd1d3f4STakashi Sakamoto 		/* The format is too short. */
5775cd1d3f4STakashi Sakamoto 		if (len < 3) {
5785cd1d3f4STakashi Sakamoto 			err = -EIO;
5795cd1d3f4STakashi Sakamoto 			break;
5805cd1d3f4STakashi Sakamoto 		}
5815cd1d3f4STakashi Sakamoto 
5825cd1d3f4STakashi Sakamoto 		/* parse and set stream format */
5835cd1d3f4STakashi Sakamoto 		err = snd_oxfw_stream_parse_format(buf, &dummy);
5845cd1d3f4STakashi Sakamoto 		if (err < 0)
5855cd1d3f4STakashi Sakamoto 			break;
5865cd1d3f4STakashi Sakamoto 
5875cd1d3f4STakashi Sakamoto 		formats[eid] = kmalloc(len, GFP_KERNEL);
5885cd1d3f4STakashi Sakamoto 		if (formats[eid] == NULL) {
5895cd1d3f4STakashi Sakamoto 			err = -ENOMEM;
5905cd1d3f4STakashi Sakamoto 			break;
5915cd1d3f4STakashi Sakamoto 		}
5925cd1d3f4STakashi Sakamoto 		memcpy(formats[eid], buf, len);
5935cd1d3f4STakashi Sakamoto 
5945cd1d3f4STakashi Sakamoto 		/* get next entry */
5955cd1d3f4STakashi Sakamoto 		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
5965cd1d3f4STakashi Sakamoto 		err = avc_stream_get_format_list(oxfw->unit, dir, 0,
5975cd1d3f4STakashi Sakamoto 						 buf, &len, ++eid);
5985cd1d3f4STakashi Sakamoto 		/* No entries remained. */
5995cd1d3f4STakashi Sakamoto 		if (err == -EINVAL) {
6005cd1d3f4STakashi Sakamoto 			err = 0;
6015cd1d3f4STakashi Sakamoto 			break;
6025cd1d3f4STakashi Sakamoto 		} else if (err < 0) {
6035cd1d3f4STakashi Sakamoto 			dev_err(&oxfw->unit->device,
6045cd1d3f4STakashi Sakamoto 			"fail to get stream format %d for isoc %s plug %d:%d\n",
6055cd1d3f4STakashi Sakamoto 				eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
6065cd1d3f4STakashi Sakamoto 									"out",
6075cd1d3f4STakashi Sakamoto 				pid, err);
6085cd1d3f4STakashi Sakamoto 			break;
6095cd1d3f4STakashi Sakamoto 		}
6105cd1d3f4STakashi Sakamoto 	}
6115cd1d3f4STakashi Sakamoto end:
6125cd1d3f4STakashi Sakamoto 	kfree(buf);
6135cd1d3f4STakashi Sakamoto 	return err;
6145cd1d3f4STakashi Sakamoto }
6155cd1d3f4STakashi Sakamoto 
6165cd1d3f4STakashi Sakamoto int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
6175cd1d3f4STakashi Sakamoto {
6185cd1d3f4STakashi Sakamoto 	u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
6195cd1d3f4STakashi Sakamoto 	int err;
6205cd1d3f4STakashi Sakamoto 
6215cd1d3f4STakashi Sakamoto 	/* the number of plugs for isoc in/out, ext in/out  */
6225cd1d3f4STakashi Sakamoto 	err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
6235cd1d3f4STakashi Sakamoto 	if (err < 0) {
6245cd1d3f4STakashi Sakamoto 		dev_err(&oxfw->unit->device,
6255cd1d3f4STakashi Sakamoto 		"fail to get info for isoc/external in/out plugs: %d\n",
6265cd1d3f4STakashi Sakamoto 			err);
6275cd1d3f4STakashi Sakamoto 		goto end;
628b0ac0009STakashi Sakamoto 	} else if ((plugs[0] == 0) && (plugs[1] == 0)) {
6295cd1d3f4STakashi Sakamoto 		err = -ENOSYS;
6305cd1d3f4STakashi Sakamoto 		goto end;
6315cd1d3f4STakashi Sakamoto 	}
6325cd1d3f4STakashi Sakamoto 
633b0ac0009STakashi Sakamoto 	/* use oPCR[0] if exists */
634b0ac0009STakashi Sakamoto 	if (plugs[1] > 0) {
635b0ac0009STakashi Sakamoto 		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
636b0ac0009STakashi Sakamoto 		if (err < 0)
637b0ac0009STakashi Sakamoto 			goto end;
638b0ac0009STakashi Sakamoto 		oxfw->has_output = true;
639b0ac0009STakashi Sakamoto 	}
640b0ac0009STakashi Sakamoto 
6415cd1d3f4STakashi Sakamoto 	/* use iPCR[0] if exists */
6425cd1d3f4STakashi Sakamoto 	if (plugs[0] > 0)
6435cd1d3f4STakashi Sakamoto 		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
6445cd1d3f4STakashi Sakamoto end:
6455cd1d3f4STakashi Sakamoto 	return err;
6465cd1d3f4STakashi Sakamoto }
647*8985f4acSTakashi Sakamoto 
648*8985f4acSTakashi Sakamoto void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
649*8985f4acSTakashi Sakamoto {
650*8985f4acSTakashi Sakamoto 	oxfw->dev_lock_changed = true;
651*8985f4acSTakashi Sakamoto 	wake_up(&oxfw->hwdep_wait);
652*8985f4acSTakashi Sakamoto }
653*8985f4acSTakashi Sakamoto 
654*8985f4acSTakashi Sakamoto int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
655*8985f4acSTakashi Sakamoto {
656*8985f4acSTakashi Sakamoto 	int err;
657*8985f4acSTakashi Sakamoto 
658*8985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
659*8985f4acSTakashi Sakamoto 
660*8985f4acSTakashi Sakamoto 	/* user land lock this */
661*8985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_count < 0) {
662*8985f4acSTakashi Sakamoto 		err = -EBUSY;
663*8985f4acSTakashi Sakamoto 		goto end;
664*8985f4acSTakashi Sakamoto 	}
665*8985f4acSTakashi Sakamoto 
666*8985f4acSTakashi Sakamoto 	/* this is the first time */
667*8985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_count++ == 0)
668*8985f4acSTakashi Sakamoto 		snd_oxfw_stream_lock_changed(oxfw);
669*8985f4acSTakashi Sakamoto 	err = 0;
670*8985f4acSTakashi Sakamoto end:
671*8985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
672*8985f4acSTakashi Sakamoto 	return err;
673*8985f4acSTakashi Sakamoto }
674*8985f4acSTakashi Sakamoto 
675*8985f4acSTakashi Sakamoto void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
676*8985f4acSTakashi Sakamoto {
677*8985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
678*8985f4acSTakashi Sakamoto 
679*8985f4acSTakashi Sakamoto 	if (WARN_ON(oxfw->dev_lock_count <= 0))
680*8985f4acSTakashi Sakamoto 		goto end;
681*8985f4acSTakashi Sakamoto 	if (--oxfw->dev_lock_count == 0)
682*8985f4acSTakashi Sakamoto 		snd_oxfw_stream_lock_changed(oxfw);
683*8985f4acSTakashi Sakamoto end:
684*8985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
685*8985f4acSTakashi Sakamoto }
686