brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 53dbd4d Raw
378 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * fireworks_stream.c - a part of driver for Fireworks based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7#include "./fireworks.h"8 9#define READY_TIMEOUT_MS	100010 11static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)12{13	struct cmp_connection *conn;14	enum cmp_direction c_dir;15	enum amdtp_stream_direction s_dir;16	int err;17 18	if (stream == &efw->tx_stream) {19		conn = &efw->out_conn;20		c_dir = CMP_OUTPUT;21		s_dir = AMDTP_IN_STREAM;22	} else {23		conn = &efw->in_conn;24		c_dir = CMP_INPUT;25		s_dir = AMDTP_OUT_STREAM;26	}27 28	err = cmp_connection_init(conn, efw->unit, c_dir, 0);29	if (err < 0)30		return err;31 32	err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT);33	if (err < 0) {34		amdtp_stream_destroy(stream);35		cmp_connection_destroy(conn);36		return err;37	}38 39	if (stream == &efw->tx_stream) {40		// Fireworks transmits NODATA packets with TAG0.41		efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;42		// Fireworks has its own meaning for dbc.43		efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;44		// Fireworks reset dbc at bus reset.45		efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;46		// But Recent firmwares starts packets with non-zero dbc.47		// Driver version 5.7.6 installs firmware version 5.7.3.48		if (efw->is_fireworks3 &&49		    (efw->firmware_version == 0x5070000 ||50		     efw->firmware_version == 0x5070300 ||51		     efw->firmware_version == 0x5080000))52			efw->tx_stream.flags |= CIP_UNALIGHED_DBC;53		// AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0)54		// also report wrong dbs at 88.2 kHz or greater.55		if (efw->is_af9 || efw->firmware_version == 0x4060000)56			efw->tx_stream.flags |= CIP_WRONG_DBS;57		// Firmware version 5.5 reports fixed interval for dbc.58		if (efw->firmware_version == 0x5050000)59			efw->tx_stream.ctx_data.tx.dbc_interval = 8;60	}61 62	return err;63}64 65static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,66			unsigned int rate)67{68	struct cmp_connection *conn;69	int err;70 71	if (stream == &efw->tx_stream)72		conn = &efw->out_conn;73	else74		conn = &efw->in_conn;75 76	// Establish connection via CMP.77	err = cmp_connection_establish(conn);78	if (err < 0)79		return err;80 81	// Start amdtp stream.82	err = amdtp_domain_add_stream(&efw->domain, stream,83				      conn->resources.channel, conn->speed);84	if (err < 0) {85		cmp_connection_break(conn);86		return err;87	}88 89	return 0;90}91 92// This function should be called before starting the stream or after stopping93// the streams.94static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)95{96	amdtp_stream_destroy(stream);97 98	if (stream == &efw->tx_stream)99		cmp_connection_destroy(&efw->out_conn);100	else101		cmp_connection_destroy(&efw->in_conn);102}103 104static int105check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)106{107	struct cmp_connection *conn;108	bool used;109	int err;110 111	if (s == &efw->tx_stream)112		conn = &efw->out_conn;113	else114		conn = &efw->in_conn;115 116	err = cmp_connection_check_used(conn, &used);117	if ((err >= 0) && used && !amdtp_stream_running(s)) {118		dev_err(&efw->unit->device,119			"Connection established by others: %cPCR[%d]\n",120			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',121			conn->pcr_index);122		err = -EBUSY;123	}124 125	return err;126}127 128int snd_efw_stream_init_duplex(struct snd_efw *efw)129{130	int err;131 132	err = init_stream(efw, &efw->tx_stream);133	if (err < 0)134		return err;135 136	err = init_stream(efw, &efw->rx_stream);137	if (err < 0) {138		destroy_stream(efw, &efw->tx_stream);139		return err;140	}141 142	err = amdtp_domain_init(&efw->domain);143	if (err < 0) {144		destroy_stream(efw, &efw->tx_stream);145		destroy_stream(efw, &efw->rx_stream);146		return err;147	}148 149	// set IEC61883 compliant mode (actually not fully compliant...).150	err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);151	if (err < 0) {152		destroy_stream(efw, &efw->tx_stream);153		destroy_stream(efw, &efw->rx_stream);154	}155 156	return err;157}158 159static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,160			  unsigned int rate, unsigned int mode)161{162	unsigned int pcm_channels;163	unsigned int midi_ports;164	struct cmp_connection *conn;165	int err;166 167	if (stream == &efw->tx_stream) {168		pcm_channels = efw->pcm_capture_channels[mode];169		midi_ports = efw->midi_out_ports;170		conn = &efw->out_conn;171	} else {172		pcm_channels = efw->pcm_playback_channels[mode];173		midi_ports = efw->midi_in_ports;174		conn = &efw->in_conn;175	}176 177	err = amdtp_am824_set_parameters(stream, rate, pcm_channels,178					 midi_ports, false);179	if (err < 0)180		return err;181 182	return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));183}184 185int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,186				  unsigned int frames_per_period,187				  unsigned int frames_per_buffer)188{189	unsigned int curr_rate;190	int err;191 192	// Considering JACK/FFADO streaming:193	// TODO: This can be removed hwdep functionality becomes popular.194	err = check_connection_used_by_others(efw, &efw->rx_stream);195	if (err < 0)196		return err;197 198	// stop streams if rate is different.199	err = snd_efw_command_get_sampling_rate(efw, &curr_rate);200	if (err < 0)201		return err;202	if (rate == 0)203		rate = curr_rate;204	if (rate != curr_rate) {205		amdtp_domain_stop(&efw->domain);206 207		cmp_connection_break(&efw->out_conn);208		cmp_connection_break(&efw->in_conn);209 210		cmp_connection_release(&efw->out_conn);211		cmp_connection_release(&efw->in_conn);212	}213 214	if (efw->substreams_counter == 0 || rate != curr_rate) {215		unsigned int mode;216 217		err = snd_efw_command_set_sampling_rate(efw, rate);218		if (err < 0)219			return err;220 221		err = snd_efw_get_multiplier_mode(rate, &mode);222		if (err < 0)223			return err;224 225		err = keep_resources(efw, &efw->tx_stream, rate, mode);226		if (err < 0)227			return err;228 229		err = keep_resources(efw, &efw->rx_stream, rate, mode);230		if (err < 0) {231			cmp_connection_release(&efw->in_conn);232			return err;233		}234 235		err = amdtp_domain_set_events_per_period(&efw->domain,236					frames_per_period, frames_per_buffer);237		if (err < 0) {238			cmp_connection_release(&efw->in_conn);239			cmp_connection_release(&efw->out_conn);240			return err;241		}242	}243 244	return 0;245}246 247int snd_efw_stream_start_duplex(struct snd_efw *efw)248{249	unsigned int rate;250	int err = 0;251 252	// Need no substreams.253	if (efw->substreams_counter == 0)254		return -EIO;255 256	if (amdtp_streaming_error(&efw->rx_stream) ||257	    amdtp_streaming_error(&efw->tx_stream)) {258		amdtp_domain_stop(&efw->domain);259		cmp_connection_break(&efw->out_conn);260		cmp_connection_break(&efw->in_conn);261	}262 263	err = snd_efw_command_get_sampling_rate(efw, &rate);264	if (err < 0)265		return err;266 267	if (!amdtp_stream_running(&efw->rx_stream)) {268		unsigned int tx_init_skip_cycles;269 270		// Audiofire 2/4 skip an isochronous cycle several thousands after starting271		// packet transmission.272		if (efw->is_fireworks3 && !efw->is_af9)273			tx_init_skip_cycles = 6000;274		else275			tx_init_skip_cycles = 0;276 277		err = start_stream(efw, &efw->rx_stream, rate);278		if (err < 0)279			goto error;280 281		err = start_stream(efw, &efw->tx_stream, rate);282		if (err < 0)283			goto error;284 285		// NOTE: The device ignores presentation time expressed by the value of syt field286		// of CIP header in received packets. The sequence of the number of data blocks per287		// packet is important for media clock recovery.288		err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false);289		if (err < 0)290			goto error;291 292		if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) {293			err = -ETIMEDOUT;294			goto error;295		}296	}297 298	return 0;299error:300	amdtp_domain_stop(&efw->domain);301 302	cmp_connection_break(&efw->out_conn);303	cmp_connection_break(&efw->in_conn);304 305	return err;306}307 308void snd_efw_stream_stop_duplex(struct snd_efw *efw)309{310	if (efw->substreams_counter == 0) {311		amdtp_domain_stop(&efw->domain);312 313		cmp_connection_break(&efw->out_conn);314		cmp_connection_break(&efw->in_conn);315 316		cmp_connection_release(&efw->out_conn);317		cmp_connection_release(&efw->in_conn);318	}319}320 321void snd_efw_stream_update_duplex(struct snd_efw *efw)322{323	amdtp_domain_stop(&efw->domain);324 325	cmp_connection_break(&efw->out_conn);326	cmp_connection_break(&efw->in_conn);327 328	amdtp_stream_pcm_abort(&efw->rx_stream);329	amdtp_stream_pcm_abort(&efw->tx_stream);330}331 332void snd_efw_stream_destroy_duplex(struct snd_efw *efw)333{334	amdtp_domain_destroy(&efw->domain);335 336	destroy_stream(efw, &efw->rx_stream);337	destroy_stream(efw, &efw->tx_stream);338}339 340void snd_efw_stream_lock_changed(struct snd_efw *efw)341{342	efw->dev_lock_changed = true;343	wake_up(&efw->hwdep_wait);344}345 346int snd_efw_stream_lock_try(struct snd_efw *efw)347{348	int err;349 350	spin_lock_irq(&efw->lock);351 352	/* user land lock this */353	if (efw->dev_lock_count < 0) {354		err = -EBUSY;355		goto end;356	}357 358	/* this is the first time */359	if (efw->dev_lock_count++ == 0)360		snd_efw_stream_lock_changed(efw);361	err = 0;362end:363	spin_unlock_irq(&efw->lock);364	return err;365}366 367void snd_efw_stream_lock_release(struct snd_efw *efw)368{369	spin_lock_irq(&efw->lock);370 371	if (WARN_ON(efw->dev_lock_count <= 0))372		goto end;373	if (--efw->dev_lock_count == 0)374		snd_efw_stream_lock_changed(efw);375end:376	spin_unlock_irq(&efw->lock);377}378