18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * oxfw_stream.c - a part of driver for OXFW970/971 based devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "oxfw.h"
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define AVC_GENERIC_FRAME_MAXIMUM_BYTES	512
128c2ecf20Sopenharmony_ci#define CALLBACK_TIMEOUT	200
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * According to datasheet of Oxford Semiconductor:
168c2ecf20Sopenharmony_ci *  OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
178c2ecf20Sopenharmony_ci *  OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_cistatic const unsigned int oxfw_rate_table[] = {
208c2ecf20Sopenharmony_ci	[0] = 32000,
218c2ecf20Sopenharmony_ci	[1] = 44100,
228c2ecf20Sopenharmony_ci	[2] = 48000,
238c2ecf20Sopenharmony_ci	[3] = 88200,
248c2ecf20Sopenharmony_ci	[4] = 96000,
258c2ecf20Sopenharmony_ci	[5] = 192000,
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * See Table 5.7 – Sampling frequency for Multi-bit Audio
308c2ecf20Sopenharmony_ci * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistatic const unsigned int avc_stream_rate_table[] = {
338c2ecf20Sopenharmony_ci	[0] = 0x02,
348c2ecf20Sopenharmony_ci	[1] = 0x03,
358c2ecf20Sopenharmony_ci	[2] = 0x04,
368c2ecf20Sopenharmony_ci	[3] = 0x0a,
378c2ecf20Sopenharmony_ci	[4] = 0x05,
388c2ecf20Sopenharmony_ci	[5] = 0x07,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	int err;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	err = avc_general_set_sig_fmt(oxfw->unit, rate,
468c2ecf20Sopenharmony_ci				      AVC_GENERAL_PLUG_DIR_IN, 0);
478c2ecf20Sopenharmony_ci	if (err < 0)
488c2ecf20Sopenharmony_ci		goto end;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (oxfw->has_output)
518c2ecf20Sopenharmony_ci		err = avc_general_set_sig_fmt(oxfw->unit, rate,
528c2ecf20Sopenharmony_ci					      AVC_GENERAL_PLUG_DIR_OUT, 0);
538c2ecf20Sopenharmony_ciend:
548c2ecf20Sopenharmony_ci	return err;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
588c2ecf20Sopenharmony_ci			     unsigned int rate, unsigned int pcm_channels)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	u8 **formats;
618c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
628c2ecf20Sopenharmony_ci	enum avc_general_plug_dir dir;
638c2ecf20Sopenharmony_ci	unsigned int len;
648c2ecf20Sopenharmony_ci	int i, err;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (s == &oxfw->tx_stream) {
678c2ecf20Sopenharmony_ci		formats = oxfw->tx_stream_formats;
688c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_OUT;
698c2ecf20Sopenharmony_ci	} else {
708c2ecf20Sopenharmony_ci		formats = oxfw->rx_stream_formats;
718c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_IN;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Seek stream format for requirements. */
758c2ecf20Sopenharmony_ci	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
768c2ecf20Sopenharmony_ci		err = snd_oxfw_stream_parse_format(formats[i], &formation);
778c2ecf20Sopenharmony_ci		if (err < 0)
788c2ecf20Sopenharmony_ci			return err;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		if ((formation.rate == rate) && (formation.pcm == pcm_channels))
818c2ecf20Sopenharmony_ci			break;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
848c2ecf20Sopenharmony_ci		return -EINVAL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* If assumed, just change rate. */
878c2ecf20Sopenharmony_ci	if (oxfw->assumed)
888c2ecf20Sopenharmony_ci		return set_rate(oxfw, rate);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Calculate format length. */
918c2ecf20Sopenharmony_ci	len = 5 + formats[i][4] * 2;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
948c2ecf20Sopenharmony_ci	if (err < 0)
958c2ecf20Sopenharmony_ci		return err;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Some requests just after changing format causes freezing. */
988c2ecf20Sopenharmony_ci	msleep(100);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct cmp_connection *conn;
1068c2ecf20Sopenharmony_ci	int err;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (stream == &oxfw->rx_stream)
1098c2ecf20Sopenharmony_ci		conn = &oxfw->in_conn;
1108c2ecf20Sopenharmony_ci	else
1118c2ecf20Sopenharmony_ci		conn = &oxfw->out_conn;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	err = cmp_connection_establish(conn);
1148c2ecf20Sopenharmony_ci	if (err < 0)
1158c2ecf20Sopenharmony_ci		return err;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	err = amdtp_domain_add_stream(&oxfw->domain, stream,
1188c2ecf20Sopenharmony_ci				      conn->resources.channel, conn->speed);
1198c2ecf20Sopenharmony_ci	if (err < 0) {
1208c2ecf20Sopenharmony_ci		cmp_connection_break(conn);
1218c2ecf20Sopenharmony_ci		return err;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int check_connection_used_by_others(struct snd_oxfw *oxfw,
1288c2ecf20Sopenharmony_ci					   struct amdtp_stream *stream)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct cmp_connection *conn;
1318c2ecf20Sopenharmony_ci	bool used;
1328c2ecf20Sopenharmony_ci	int err;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (stream == &oxfw->tx_stream)
1358c2ecf20Sopenharmony_ci		conn = &oxfw->out_conn;
1368c2ecf20Sopenharmony_ci	else
1378c2ecf20Sopenharmony_ci		conn = &oxfw->in_conn;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	err = cmp_connection_check_used(conn, &used);
1408c2ecf20Sopenharmony_ci	if ((err >= 0) && used && !amdtp_stream_running(stream)) {
1418c2ecf20Sopenharmony_ci		dev_err(&oxfw->unit->device,
1428c2ecf20Sopenharmony_ci			"Connection established by others: %cPCR[%d]\n",
1438c2ecf20Sopenharmony_ci			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
1448c2ecf20Sopenharmony_ci			conn->pcr_index);
1458c2ecf20Sopenharmony_ci		err = -EBUSY;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return err;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct cmp_connection *conn;
1548c2ecf20Sopenharmony_ci	enum cmp_direction c_dir;
1558c2ecf20Sopenharmony_ci	enum amdtp_stream_direction s_dir;
1568c2ecf20Sopenharmony_ci	int err;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (stream == &oxfw->tx_stream) {
1598c2ecf20Sopenharmony_ci		conn = &oxfw->out_conn;
1608c2ecf20Sopenharmony_ci		c_dir = CMP_OUTPUT;
1618c2ecf20Sopenharmony_ci		s_dir = AMDTP_IN_STREAM;
1628c2ecf20Sopenharmony_ci	} else {
1638c2ecf20Sopenharmony_ci		conn = &oxfw->in_conn;
1648c2ecf20Sopenharmony_ci		c_dir = CMP_INPUT;
1658c2ecf20Sopenharmony_ci		s_dir = AMDTP_OUT_STREAM;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
1698c2ecf20Sopenharmony_ci	if (err < 0)
1708c2ecf20Sopenharmony_ci		return err;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
1738c2ecf20Sopenharmony_ci	if (err < 0) {
1748c2ecf20Sopenharmony_ci		cmp_connection_destroy(conn);
1758c2ecf20Sopenharmony_ci		return err;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/*
1798c2ecf20Sopenharmony_ci	 * OXFW starts to transmit packets with non-zero dbc.
1808c2ecf20Sopenharmony_ci	 * OXFW postpone transferring packets till handling any asynchronous
1818c2ecf20Sopenharmony_ci	 * packets. As a result, next isochronous packet includes more data
1828c2ecf20Sopenharmony_ci	 * blocks than IEC 61883-6 defines.
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci	if (stream == &oxfw->tx_stream) {
1858c2ecf20Sopenharmony_ci		oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
1868c2ecf20Sopenharmony_ci		if (oxfw->wrong_dbs)
1878c2ecf20Sopenharmony_ci			oxfw->tx_stream.flags |= CIP_WRONG_DBS;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	enum avc_general_plug_dir dir;
1968c2ecf20Sopenharmony_ci	u8 **formats;
1978c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
1988c2ecf20Sopenharmony_ci	struct cmp_connection *conn;
1998c2ecf20Sopenharmony_ci	int i;
2008c2ecf20Sopenharmony_ci	int err;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (stream == &oxfw->rx_stream) {
2038c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_IN;
2048c2ecf20Sopenharmony_ci		formats = oxfw->rx_stream_formats;
2058c2ecf20Sopenharmony_ci		conn = &oxfw->in_conn;
2068c2ecf20Sopenharmony_ci	} else {
2078c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_OUT;
2088c2ecf20Sopenharmony_ci		formats = oxfw->tx_stream_formats;
2098c2ecf20Sopenharmony_ci		conn = &oxfw->out_conn;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
2138c2ecf20Sopenharmony_ci	if (err < 0)
2148c2ecf20Sopenharmony_ci		return err;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
2178c2ecf20Sopenharmony_ci		struct snd_oxfw_stream_formation fmt;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		if (formats[i] == NULL)
2208c2ecf20Sopenharmony_ci			break;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		err = snd_oxfw_stream_parse_format(formats[i], &fmt);
2238c2ecf20Sopenharmony_ci		if (err < 0)
2248c2ecf20Sopenharmony_ci			return err;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
2278c2ecf20Sopenharmony_ci		    fmt.midi == formation.midi)
2288c2ecf20Sopenharmony_ci			break;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
2318c2ecf20Sopenharmony_ci		return -EINVAL;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	// The stream should have one pcm channels at least.
2348c2ecf20Sopenharmony_ci	if (formation.pcm == 0)
2358c2ecf20Sopenharmony_ci		return -EINVAL;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
2388c2ecf20Sopenharmony_ci					 formation.midi * 8, false);
2398c2ecf20Sopenharmony_ci	if (err < 0)
2408c2ecf20Sopenharmony_ci		return err;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ciint snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
2468c2ecf20Sopenharmony_ci				   struct amdtp_stream *stream,
2478c2ecf20Sopenharmony_ci				   unsigned int rate, unsigned int pcm_channels,
2488c2ecf20Sopenharmony_ci				   unsigned int frames_per_period,
2498c2ecf20Sopenharmony_ci				   unsigned int frames_per_buffer)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
2528c2ecf20Sopenharmony_ci	enum avc_general_plug_dir dir;
2538c2ecf20Sopenharmony_ci	int err;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	// Considering JACK/FFADO streaming:
2568c2ecf20Sopenharmony_ci	// TODO: This can be removed hwdep functionality becomes popular.
2578c2ecf20Sopenharmony_ci	err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
2588c2ecf20Sopenharmony_ci	if (err < 0)
2598c2ecf20Sopenharmony_ci		return err;
2608c2ecf20Sopenharmony_ci	if (oxfw->has_output) {
2618c2ecf20Sopenharmony_ci		err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
2628c2ecf20Sopenharmony_ci		if (err < 0)
2638c2ecf20Sopenharmony_ci			return err;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (stream == &oxfw->tx_stream)
2678c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_OUT;
2688c2ecf20Sopenharmony_ci	else
2698c2ecf20Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_IN;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
2728c2ecf20Sopenharmony_ci	if (err < 0)
2738c2ecf20Sopenharmony_ci		return err;
2748c2ecf20Sopenharmony_ci	if (rate == 0) {
2758c2ecf20Sopenharmony_ci		rate = formation.rate;
2768c2ecf20Sopenharmony_ci		pcm_channels = formation.pcm;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	if (formation.rate != rate || formation.pcm != pcm_channels) {
2798c2ecf20Sopenharmony_ci		amdtp_domain_stop(&oxfw->domain);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		cmp_connection_break(&oxfw->in_conn);
2828c2ecf20Sopenharmony_ci		cmp_connection_release(&oxfw->in_conn);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		if (oxfw->has_output) {
2858c2ecf20Sopenharmony_ci			cmp_connection_break(&oxfw->out_conn);
2868c2ecf20Sopenharmony_ci			cmp_connection_release(&oxfw->out_conn);
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (oxfw->substreams_count == 0 ||
2918c2ecf20Sopenharmony_ci	    formation.rate != rate || formation.pcm != pcm_channels) {
2928c2ecf20Sopenharmony_ci		err = set_stream_format(oxfw, stream, rate, pcm_channels);
2938c2ecf20Sopenharmony_ci		if (err < 0) {
2948c2ecf20Sopenharmony_ci			dev_err(&oxfw->unit->device,
2958c2ecf20Sopenharmony_ci				"fail to set stream format: %d\n", err);
2968c2ecf20Sopenharmony_ci			return err;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		err = keep_resources(oxfw, &oxfw->rx_stream);
3008c2ecf20Sopenharmony_ci		if (err < 0)
3018c2ecf20Sopenharmony_ci			return err;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		if (oxfw->has_output) {
3048c2ecf20Sopenharmony_ci			err = keep_resources(oxfw, &oxfw->tx_stream);
3058c2ecf20Sopenharmony_ci			if (err < 0) {
3068c2ecf20Sopenharmony_ci				cmp_connection_release(&oxfw->in_conn);
3078c2ecf20Sopenharmony_ci				return err;
3088c2ecf20Sopenharmony_ci			}
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		err = amdtp_domain_set_events_per_period(&oxfw->domain,
3128c2ecf20Sopenharmony_ci					frames_per_period, frames_per_buffer);
3138c2ecf20Sopenharmony_ci		if (err < 0) {
3148c2ecf20Sopenharmony_ci			cmp_connection_release(&oxfw->in_conn);
3158c2ecf20Sopenharmony_ci			if (oxfw->has_output)
3168c2ecf20Sopenharmony_ci				cmp_connection_release(&oxfw->out_conn);
3178c2ecf20Sopenharmony_ci			return err;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return 0;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ciint snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int err;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (oxfw->substreams_count == 0)
3298c2ecf20Sopenharmony_ci		return -EIO;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (amdtp_streaming_error(&oxfw->rx_stream) ||
3328c2ecf20Sopenharmony_ci	    amdtp_streaming_error(&oxfw->tx_stream)) {
3338c2ecf20Sopenharmony_ci		amdtp_domain_stop(&oxfw->domain);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		cmp_connection_break(&oxfw->in_conn);
3368c2ecf20Sopenharmony_ci		if (oxfw->has_output)
3378c2ecf20Sopenharmony_ci			cmp_connection_break(&oxfw->out_conn);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (!amdtp_stream_running(&oxfw->rx_stream)) {
3418c2ecf20Sopenharmony_ci		err = start_stream(oxfw, &oxfw->rx_stream);
3428c2ecf20Sopenharmony_ci		if (err < 0) {
3438c2ecf20Sopenharmony_ci			dev_err(&oxfw->unit->device,
3448c2ecf20Sopenharmony_ci				"fail to prepare rx stream: %d\n", err);
3458c2ecf20Sopenharmony_ci			goto error;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		if (oxfw->has_output &&
3498c2ecf20Sopenharmony_ci		    !amdtp_stream_running(&oxfw->tx_stream)) {
3508c2ecf20Sopenharmony_ci			err = start_stream(oxfw, &oxfw->tx_stream);
3518c2ecf20Sopenharmony_ci			if (err < 0) {
3528c2ecf20Sopenharmony_ci				dev_err(&oxfw->unit->device,
3538c2ecf20Sopenharmony_ci					"fail to prepare tx stream: %d\n", err);
3548c2ecf20Sopenharmony_ci				goto error;
3558c2ecf20Sopenharmony_ci			}
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		err = amdtp_domain_start(&oxfw->domain, 0);
3598c2ecf20Sopenharmony_ci		if (err < 0)
3608c2ecf20Sopenharmony_ci			goto error;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		// Wait first packet.
3638c2ecf20Sopenharmony_ci		if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
3648c2ecf20Sopenharmony_ci						CALLBACK_TIMEOUT)) {
3658c2ecf20Sopenharmony_ci			err = -ETIMEDOUT;
3668c2ecf20Sopenharmony_ci			goto error;
3678c2ecf20Sopenharmony_ci		}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		if (oxfw->has_output) {
3708c2ecf20Sopenharmony_ci			if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
3718c2ecf20Sopenharmony_ci							CALLBACK_TIMEOUT)) {
3728c2ecf20Sopenharmony_ci				err = -ETIMEDOUT;
3738c2ecf20Sopenharmony_ci				goto error;
3748c2ecf20Sopenharmony_ci			}
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_cierror:
3808c2ecf20Sopenharmony_ci	amdtp_domain_stop(&oxfw->domain);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	cmp_connection_break(&oxfw->in_conn);
3838c2ecf20Sopenharmony_ci	if (oxfw->has_output)
3848c2ecf20Sopenharmony_ci		cmp_connection_break(&oxfw->out_conn);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return err;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_civoid snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	if (oxfw->substreams_count == 0) {
3928c2ecf20Sopenharmony_ci		amdtp_domain_stop(&oxfw->domain);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		cmp_connection_break(&oxfw->in_conn);
3958c2ecf20Sopenharmony_ci		cmp_connection_release(&oxfw->in_conn);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		if (oxfw->has_output) {
3988c2ecf20Sopenharmony_ci			cmp_connection_break(&oxfw->out_conn);
3998c2ecf20Sopenharmony_ci			cmp_connection_release(&oxfw->out_conn);
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct cmp_connection *conn;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (stream == &oxfw->tx_stream)
4098c2ecf20Sopenharmony_ci		conn = &oxfw->out_conn;
4108c2ecf20Sopenharmony_ci	else
4118c2ecf20Sopenharmony_ci		conn = &oxfw->in_conn;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	amdtp_stream_destroy(stream);
4148c2ecf20Sopenharmony_ci	cmp_connection_destroy(conn);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciint snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	int err;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	err = init_stream(oxfw, &oxfw->rx_stream);
4228c2ecf20Sopenharmony_ci	if (err < 0)
4238c2ecf20Sopenharmony_ci		return err;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (oxfw->has_output) {
4268c2ecf20Sopenharmony_ci		err = init_stream(oxfw, &oxfw->tx_stream);
4278c2ecf20Sopenharmony_ci		if (err < 0) {
4288c2ecf20Sopenharmony_ci			destroy_stream(oxfw, &oxfw->rx_stream);
4298c2ecf20Sopenharmony_ci			return err;
4308c2ecf20Sopenharmony_ci		}
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	err = amdtp_domain_init(&oxfw->domain);
4348c2ecf20Sopenharmony_ci	if (err < 0) {
4358c2ecf20Sopenharmony_ci		destroy_stream(oxfw, &oxfw->rx_stream);
4368c2ecf20Sopenharmony_ci		if (oxfw->has_output)
4378c2ecf20Sopenharmony_ci			destroy_stream(oxfw, &oxfw->tx_stream);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return err;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci// This function should be called before starting the stream or after stopping
4448c2ecf20Sopenharmony_ci// the streams.
4458c2ecf20Sopenharmony_civoid snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	amdtp_domain_destroy(&oxfw->domain);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	destroy_stream(oxfw, &oxfw->rx_stream);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (oxfw->has_output)
4528c2ecf20Sopenharmony_ci		destroy_stream(oxfw, &oxfw->tx_stream);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_civoid snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	amdtp_domain_stop(&oxfw->domain);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	cmp_connection_break(&oxfw->in_conn);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	amdtp_stream_pcm_abort(&oxfw->rx_stream);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (oxfw->has_output) {
4648c2ecf20Sopenharmony_ci		cmp_connection_break(&oxfw->out_conn);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		amdtp_stream_pcm_abort(&oxfw->tx_stream);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ciint snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
4718c2ecf20Sopenharmony_ci				enum avc_general_plug_dir dir,
4728c2ecf20Sopenharmony_ci				struct snd_oxfw_stream_formation *formation)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	u8 *format;
4758c2ecf20Sopenharmony_ci	unsigned int len;
4768c2ecf20Sopenharmony_ci	int err;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
4798c2ecf20Sopenharmony_ci	format = kmalloc(len, GFP_KERNEL);
4808c2ecf20Sopenharmony_ci	if (format == NULL)
4818c2ecf20Sopenharmony_ci		return -ENOMEM;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
4848c2ecf20Sopenharmony_ci	if (err < 0)
4858c2ecf20Sopenharmony_ci		goto end;
4868c2ecf20Sopenharmony_ci	if (len < 3) {
4878c2ecf20Sopenharmony_ci		err = -EIO;
4888c2ecf20Sopenharmony_ci		goto end;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	err = snd_oxfw_stream_parse_format(format, formation);
4928c2ecf20Sopenharmony_ciend:
4938c2ecf20Sopenharmony_ci	kfree(format);
4948c2ecf20Sopenharmony_ci	return err;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/*
4988c2ecf20Sopenharmony_ci * See Table 6.16 - AM824 Stream Format
4998c2ecf20Sopenharmony_ci *     Figure 6.19 - format_information field for AM824 Compound
5008c2ecf20Sopenharmony_ci * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
5018c2ecf20Sopenharmony_ci * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_ciint snd_oxfw_stream_parse_format(u8 *format,
5048c2ecf20Sopenharmony_ci				 struct snd_oxfw_stream_formation *formation)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	unsigned int i, e, channels, type;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/*
5118c2ecf20Sopenharmony_ci	 * this module can support a hierarchy combination that:
5128c2ecf20Sopenharmony_ci	 *  Root:	Audio and Music (0x90)
5138c2ecf20Sopenharmony_ci	 *  Level 1:	AM824 Compound  (0x40)
5148c2ecf20Sopenharmony_ci	 */
5158c2ecf20Sopenharmony_ci	if ((format[0] != 0x90) || (format[1] != 0x40))
5168c2ecf20Sopenharmony_ci		return -ENXIO;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* check the sampling rate */
5198c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
5208c2ecf20Sopenharmony_ci		if (format[2] == avc_stream_rate_table[i])
5218c2ecf20Sopenharmony_ci			break;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(avc_stream_rate_table))
5248c2ecf20Sopenharmony_ci		return -ENXIO;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	formation->rate = oxfw_rate_table[i];
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	for (e = 0; e < format[4]; e++) {
5298c2ecf20Sopenharmony_ci		channels = format[5 + e * 2];
5308c2ecf20Sopenharmony_ci		type = format[6 + e * 2];
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		switch (type) {
5338c2ecf20Sopenharmony_ci		/* IEC 60958 Conformant, currently handled as MBLA */
5348c2ecf20Sopenharmony_ci		case 0x00:
5358c2ecf20Sopenharmony_ci		/* Multi Bit Linear Audio (Raw) */
5368c2ecf20Sopenharmony_ci		case 0x06:
5378c2ecf20Sopenharmony_ci			formation->pcm += channels;
5388c2ecf20Sopenharmony_ci			break;
5398c2ecf20Sopenharmony_ci		/* MIDI Conformant */
5408c2ecf20Sopenharmony_ci		case 0x0d:
5418c2ecf20Sopenharmony_ci			formation->midi = channels;
5428c2ecf20Sopenharmony_ci			break;
5438c2ecf20Sopenharmony_ci		/* IEC 61937-3 to 7 */
5448c2ecf20Sopenharmony_ci		case 0x01:
5458c2ecf20Sopenharmony_ci		case 0x02:
5468c2ecf20Sopenharmony_ci		case 0x03:
5478c2ecf20Sopenharmony_ci		case 0x04:
5488c2ecf20Sopenharmony_ci		case 0x05:
5498c2ecf20Sopenharmony_ci		/* Multi Bit Linear Audio */
5508c2ecf20Sopenharmony_ci		case 0x07:	/* DVD-Audio */
5518c2ecf20Sopenharmony_ci		case 0x0c:	/* High Precision */
5528c2ecf20Sopenharmony_ci		/* One Bit Audio */
5538c2ecf20Sopenharmony_ci		case 0x08:	/* (Plain) Raw */
5548c2ecf20Sopenharmony_ci		case 0x09:	/* (Plain) SACD */
5558c2ecf20Sopenharmony_ci		case 0x0a:	/* (Encoded) Raw */
5568c2ecf20Sopenharmony_ci		case 0x0b:	/* (Encoded) SACD */
5578c2ecf20Sopenharmony_ci		/* SMPTE Time-Code conformant */
5588c2ecf20Sopenharmony_ci		case 0x0e:
5598c2ecf20Sopenharmony_ci		/* Sample Count */
5608c2ecf20Sopenharmony_ci		case 0x0f:
5618c2ecf20Sopenharmony_ci		/* Anciliary Data */
5628c2ecf20Sopenharmony_ci		case 0x10:
5638c2ecf20Sopenharmony_ci		/* Synchronization Stream (Stereo Raw audio) */
5648c2ecf20Sopenharmony_ci		case 0x40:
5658c2ecf20Sopenharmony_ci		/* Don't care */
5668c2ecf20Sopenharmony_ci		case 0xff:
5678c2ecf20Sopenharmony_ci		default:
5688c2ecf20Sopenharmony_ci			return -ENXIO;	/* not supported */
5698c2ecf20Sopenharmony_ci		}
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (formation->pcm  > AM824_MAX_CHANNELS_FOR_PCM ||
5738c2ecf20Sopenharmony_ci	    formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
5748c2ecf20Sopenharmony_ci		return -ENXIO;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int
5808c2ecf20Sopenharmony_ciassume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
5818c2ecf20Sopenharmony_ci		      unsigned int pid, u8 *buf, unsigned int *len,
5828c2ecf20Sopenharmony_ci		      u8 **formats)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
5858c2ecf20Sopenharmony_ci	unsigned int i, eid;
5868c2ecf20Sopenharmony_ci	int err;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* get format at current sampling rate */
5898c2ecf20Sopenharmony_ci	err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
5908c2ecf20Sopenharmony_ci	if (err < 0) {
5918c2ecf20Sopenharmony_ci		dev_err(&oxfw->unit->device,
5928c2ecf20Sopenharmony_ci		"fail to get current stream format for isoc %s plug %d:%d\n",
5938c2ecf20Sopenharmony_ci			(dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
5948c2ecf20Sopenharmony_ci			pid, err);
5958c2ecf20Sopenharmony_ci		goto end;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* parse and set stream format */
5998c2ecf20Sopenharmony_ci	eid = 0;
6008c2ecf20Sopenharmony_ci	err = snd_oxfw_stream_parse_format(buf, &formation);
6018c2ecf20Sopenharmony_ci	if (err < 0)
6028c2ecf20Sopenharmony_ci		goto end;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
6058c2ecf20Sopenharmony_ci				    GFP_KERNEL);
6068c2ecf20Sopenharmony_ci	if (!formats[eid]) {
6078c2ecf20Sopenharmony_ci		err = -ENOMEM;
6088c2ecf20Sopenharmony_ci		goto end;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* apply the format for each available sampling rate */
6128c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
6138c2ecf20Sopenharmony_ci		if (formation.rate == oxfw_rate_table[i])
6148c2ecf20Sopenharmony_ci			continue;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		err = avc_general_inquiry_sig_fmt(oxfw->unit,
6178c2ecf20Sopenharmony_ci						  oxfw_rate_table[i],
6188c2ecf20Sopenharmony_ci						  dir, pid);
6198c2ecf20Sopenharmony_ci		if (err < 0)
6208c2ecf20Sopenharmony_ci			continue;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		eid++;
6238c2ecf20Sopenharmony_ci		formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
6248c2ecf20Sopenharmony_ci					    GFP_KERNEL);
6258c2ecf20Sopenharmony_ci		if (formats[eid] == NULL) {
6268c2ecf20Sopenharmony_ci			err = -ENOMEM;
6278c2ecf20Sopenharmony_ci			goto end;
6288c2ecf20Sopenharmony_ci		}
6298c2ecf20Sopenharmony_ci		formats[eid][2] = avc_stream_rate_table[i];
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	err = 0;
6338c2ecf20Sopenharmony_ci	oxfw->assumed = true;
6348c2ecf20Sopenharmony_ciend:
6358c2ecf20Sopenharmony_ci	return err;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic int fill_stream_formats(struct snd_oxfw *oxfw,
6398c2ecf20Sopenharmony_ci			       enum avc_general_plug_dir dir,
6408c2ecf20Sopenharmony_ci			       unsigned short pid)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	u8 *buf, **formats;
6438c2ecf20Sopenharmony_ci	unsigned int len, eid = 0;
6448c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation dummy;
6458c2ecf20Sopenharmony_ci	int err;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
6488c2ecf20Sopenharmony_ci	if (buf == NULL)
6498c2ecf20Sopenharmony_ci		return -ENOMEM;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (dir == AVC_GENERAL_PLUG_DIR_OUT)
6528c2ecf20Sopenharmony_ci		formats = oxfw->tx_stream_formats;
6538c2ecf20Sopenharmony_ci	else
6548c2ecf20Sopenharmony_ci		formats = oxfw->rx_stream_formats;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* get first entry */
6578c2ecf20Sopenharmony_ci	len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
6588c2ecf20Sopenharmony_ci	err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
6598c2ecf20Sopenharmony_ci	if (err == -ENXIO) {
6608c2ecf20Sopenharmony_ci		/* LIST subfunction is not implemented */
6618c2ecf20Sopenharmony_ci		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
6628c2ecf20Sopenharmony_ci		err = assume_stream_formats(oxfw, dir, pid, buf, &len,
6638c2ecf20Sopenharmony_ci					    formats);
6648c2ecf20Sopenharmony_ci		goto end;
6658c2ecf20Sopenharmony_ci	} else if (err < 0) {
6668c2ecf20Sopenharmony_ci		dev_err(&oxfw->unit->device,
6678c2ecf20Sopenharmony_ci			"fail to get stream format %d for isoc %s plug %d:%d\n",
6688c2ecf20Sopenharmony_ci			eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
6698c2ecf20Sopenharmony_ci			pid, err);
6708c2ecf20Sopenharmony_ci		goto end;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* LIST subfunction is implemented */
6748c2ecf20Sopenharmony_ci	while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
6758c2ecf20Sopenharmony_ci		/* The format is too short. */
6768c2ecf20Sopenharmony_ci		if (len < 3) {
6778c2ecf20Sopenharmony_ci			err = -EIO;
6788c2ecf20Sopenharmony_ci			break;
6798c2ecf20Sopenharmony_ci		}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		/* parse and set stream format */
6828c2ecf20Sopenharmony_ci		err = snd_oxfw_stream_parse_format(buf, &dummy);
6838c2ecf20Sopenharmony_ci		if (err < 0)
6848c2ecf20Sopenharmony_ci			break;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len,
6878c2ecf20Sopenharmony_ci					    GFP_KERNEL);
6888c2ecf20Sopenharmony_ci		if (!formats[eid]) {
6898c2ecf20Sopenharmony_ci			err = -ENOMEM;
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		/* get next entry */
6948c2ecf20Sopenharmony_ci		len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
6958c2ecf20Sopenharmony_ci		err = avc_stream_get_format_list(oxfw->unit, dir, 0,
6968c2ecf20Sopenharmony_ci						 buf, &len, ++eid);
6978c2ecf20Sopenharmony_ci		/* No entries remained. */
6988c2ecf20Sopenharmony_ci		if (err == -EINVAL) {
6998c2ecf20Sopenharmony_ci			err = 0;
7008c2ecf20Sopenharmony_ci			break;
7018c2ecf20Sopenharmony_ci		} else if (err < 0) {
7028c2ecf20Sopenharmony_ci			dev_err(&oxfw->unit->device,
7038c2ecf20Sopenharmony_ci			"fail to get stream format %d for isoc %s plug %d:%d\n",
7048c2ecf20Sopenharmony_ci				eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
7058c2ecf20Sopenharmony_ci									"out",
7068c2ecf20Sopenharmony_ci				pid, err);
7078c2ecf20Sopenharmony_ci			break;
7088c2ecf20Sopenharmony_ci		}
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ciend:
7118c2ecf20Sopenharmony_ci	kfree(buf);
7128c2ecf20Sopenharmony_ci	return err;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ciint snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
7188c2ecf20Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
7198c2ecf20Sopenharmony_ci	u8 *format;
7208c2ecf20Sopenharmony_ci	unsigned int i;
7218c2ecf20Sopenharmony_ci	int err;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* the number of plugs for isoc in/out, ext in/out  */
7248c2ecf20Sopenharmony_ci	err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
7258c2ecf20Sopenharmony_ci	if (err < 0) {
7268c2ecf20Sopenharmony_ci		dev_err(&oxfw->unit->device,
7278c2ecf20Sopenharmony_ci		"fail to get info for isoc/external in/out plugs: %d\n",
7288c2ecf20Sopenharmony_ci			err);
7298c2ecf20Sopenharmony_ci		goto end;
7308c2ecf20Sopenharmony_ci	} else if ((plugs[0] == 0) && (plugs[1] == 0)) {
7318c2ecf20Sopenharmony_ci		err = -ENXIO;
7328c2ecf20Sopenharmony_ci		goto end;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/* use oPCR[0] if exists */
7368c2ecf20Sopenharmony_ci	if (plugs[1] > 0) {
7378c2ecf20Sopenharmony_ci		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
7388c2ecf20Sopenharmony_ci		if (err < 0) {
7398c2ecf20Sopenharmony_ci			if (err != -ENXIO)
7408c2ecf20Sopenharmony_ci				return err;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci			// The oPCR is not available for isoc communication.
7438c2ecf20Sopenharmony_ci			err = 0;
7448c2ecf20Sopenharmony_ci		} else {
7458c2ecf20Sopenharmony_ci			for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
7468c2ecf20Sopenharmony_ci				format = oxfw->tx_stream_formats[i];
7478c2ecf20Sopenharmony_ci				if (format == NULL)
7488c2ecf20Sopenharmony_ci					continue;
7498c2ecf20Sopenharmony_ci				err = snd_oxfw_stream_parse_format(format,
7508c2ecf20Sopenharmony_ci								   &formation);
7518c2ecf20Sopenharmony_ci				if (err < 0)
7528c2ecf20Sopenharmony_ci					continue;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci				/* Add one MIDI port. */
7558c2ecf20Sopenharmony_ci				if (formation.midi > 0)
7568c2ecf20Sopenharmony_ci					oxfw->midi_input_ports = 1;
7578c2ecf20Sopenharmony_ci			}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci			oxfw->has_output = true;
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	/* use iPCR[0] if exists */
7648c2ecf20Sopenharmony_ci	if (plugs[0] > 0) {
7658c2ecf20Sopenharmony_ci		err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
7668c2ecf20Sopenharmony_ci		if (err < 0) {
7678c2ecf20Sopenharmony_ci			if (err != -ENXIO)
7688c2ecf20Sopenharmony_ci				return err;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci			// The iPCR is not available for isoc communication.
7718c2ecf20Sopenharmony_ci			err = 0;
7728c2ecf20Sopenharmony_ci		} else {
7738c2ecf20Sopenharmony_ci			for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
7748c2ecf20Sopenharmony_ci				format = oxfw->rx_stream_formats[i];
7758c2ecf20Sopenharmony_ci				if (format == NULL)
7768c2ecf20Sopenharmony_ci					continue;
7778c2ecf20Sopenharmony_ci				err = snd_oxfw_stream_parse_format(format,
7788c2ecf20Sopenharmony_ci								   &formation);
7798c2ecf20Sopenharmony_ci				if (err < 0)
7808c2ecf20Sopenharmony_ci					continue;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci				/* Add one MIDI port. */
7838c2ecf20Sopenharmony_ci				if (formation.midi > 0)
7848c2ecf20Sopenharmony_ci					oxfw->midi_output_ports = 1;
7858c2ecf20Sopenharmony_ci			}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci			oxfw->has_input = true;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ciend:
7918c2ecf20Sopenharmony_ci	return err;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_civoid snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	oxfw->dev_lock_changed = true;
7978c2ecf20Sopenharmony_ci	wake_up(&oxfw->hwdep_wait);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ciint snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	int err;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	spin_lock_irq(&oxfw->lock);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	/* user land lock this */
8078c2ecf20Sopenharmony_ci	if (oxfw->dev_lock_count < 0) {
8088c2ecf20Sopenharmony_ci		err = -EBUSY;
8098c2ecf20Sopenharmony_ci		goto end;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* this is the first time */
8138c2ecf20Sopenharmony_ci	if (oxfw->dev_lock_count++ == 0)
8148c2ecf20Sopenharmony_ci		snd_oxfw_stream_lock_changed(oxfw);
8158c2ecf20Sopenharmony_ci	err = 0;
8168c2ecf20Sopenharmony_ciend:
8178c2ecf20Sopenharmony_ci	spin_unlock_irq(&oxfw->lock);
8188c2ecf20Sopenharmony_ci	return err;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_civoid snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	spin_lock_irq(&oxfw->lock);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	if (WARN_ON(oxfw->dev_lock_count <= 0))
8268c2ecf20Sopenharmony_ci		goto end;
8278c2ecf20Sopenharmony_ci	if (--oxfw->dev_lock_count == 0)
8288c2ecf20Sopenharmony_ci		snd_oxfw_stream_lock_changed(oxfw);
8298c2ecf20Sopenharmony_ciend:
8308c2ecf20Sopenharmony_ci	spin_unlock_irq(&oxfw->lock);
8318c2ecf20Sopenharmony_ci}
832