brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 64aec9c Raw
439 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * motu-stream.c - a part of driver for MOTU FireWire series4 *5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>6 */7 8#include "motu.h"9 10#define	READY_TIMEOUT_MS	20011 12#define ISOC_COMM_CONTROL_OFFSET		0x0b0013#define  ISOC_COMM_CONTROL_MASK			0xffff000014#define  CHANGE_RX_ISOC_COMM_STATE		0x8000000015#define  RX_ISOC_COMM_IS_ACTIVATED		0x4000000016#define  RX_ISOC_COMM_CHANNEL_MASK		0x3f00000017#define  RX_ISOC_COMM_CHANNEL_SHIFT		2418#define  CHANGE_TX_ISOC_COMM_STATE		0x0080000019#define  TX_ISOC_COMM_IS_ACTIVATED		0x0040000020#define  TX_ISOC_COMM_CHANNEL_MASK		0x003f000021#define  TX_ISOC_COMM_CHANNEL_SHIFT		1622 23#define PACKET_FORMAT_OFFSET			0x0b1024#define  TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS	0x0000008025#define  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS	0x0000004026#define  TX_PACKET_TRANSMISSION_SPEED_MASK	0x0000000f27 28static int keep_resources(struct snd_motu *motu, unsigned int rate,29			  struct amdtp_stream *stream)30{31	struct fw_iso_resources *resources;32	struct snd_motu_packet_format *packet_format;33	unsigned int midi_ports = 0;34	int err;35 36	if (stream == &motu->rx_stream) {37		resources = &motu->rx_resources;38		packet_format = &motu->rx_packet_formats;39 40		if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||41		    (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))42			midi_ports = 1;43	} else {44		resources = &motu->tx_resources;45		packet_format = &motu->tx_packet_formats;46 47		if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||48		    (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))49			midi_ports = 1;50	}51 52	err = amdtp_motu_set_parameters(stream, rate, midi_ports,53					packet_format);54	if (err < 0)55		return err;56 57	return fw_iso_resources_allocate(resources,58				amdtp_stream_get_max_payload(stream),59				fw_parent_device(motu->unit)->max_speed);60}61 62static int begin_session(struct snd_motu *motu)63{64	__be32 reg;65	u32 data;66	int err;67 68	// Configure the unit to start isochronous communication.69	err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,70					sizeof(reg));71	if (err < 0)72		return err;73	data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;74 75	data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |76		(motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |77		CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |78		(motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);79 80	reg = cpu_to_be32(data);81	return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,82					  sizeof(reg));83}84 85static void finish_session(struct snd_motu *motu)86{87	__be32 reg;88	u32 data;89	int err;90 91	err = snd_motu_protocol_switch_fetching_mode(motu, false);92	if (err < 0)93		return;94 95	err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,96					sizeof(reg));97	if (err < 0)98		return;99	data = be32_to_cpu(reg);100 101	data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);102	data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;103 104	reg = cpu_to_be32(data);105	snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,106				   sizeof(reg));107}108 109int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)110{111	int err;112 113	err = snd_motu_protocol_cache_packet_formats(motu);114	if (err < 0)115		return err;116 117	if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) {118		motu->tx_packet_formats.midi_flag_offset = 4;119		motu->tx_packet_formats.midi_byte_offset = 6;120	} else if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q) {121		motu->tx_packet_formats.midi_flag_offset = 8;122		motu->tx_packet_formats.midi_byte_offset = 7;123	}124 125	if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) {126		motu->rx_packet_formats.midi_flag_offset = 4;127		motu->rx_packet_formats.midi_byte_offset = 6;128	} else if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) {129		motu->rx_packet_formats.midi_flag_offset = 8;130		motu->rx_packet_formats.midi_byte_offset = 7;131	}132 133	return 0;134}135 136int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,137				   unsigned int frames_per_period,138				   unsigned int frames_per_buffer)139{140	unsigned int curr_rate;141	int err;142 143	err = snd_motu_protocol_get_clock_rate(motu, &curr_rate);144	if (err < 0)145		return err;146	if (rate == 0)147		rate = curr_rate;148 149	if (motu->substreams_counter == 0 || curr_rate != rate) {150		amdtp_domain_stop(&motu->domain);151		finish_session(motu);152 153		fw_iso_resources_free(&motu->tx_resources);154		fw_iso_resources_free(&motu->rx_resources);155 156		kfree(motu->cache.event_offsets);157		motu->cache.event_offsets = NULL;158 159		err = snd_motu_protocol_set_clock_rate(motu, rate);160		if (err < 0) {161			dev_err(&motu->unit->device,162				"fail to set sampling rate: %d\n", err);163			return err;164		}165 166		err = snd_motu_stream_cache_packet_formats(motu);167		if (err < 0)168			return err;169 170		err = keep_resources(motu, rate, &motu->tx_stream);171		if (err < 0)172			return err;173 174		err = keep_resources(motu, rate, &motu->rx_stream);175		if (err < 0) {176			fw_iso_resources_free(&motu->tx_resources);177			return err;178		}179 180		err = amdtp_domain_set_events_per_period(&motu->domain,181					frames_per_period, frames_per_buffer);182		if (err < 0) {183			fw_iso_resources_free(&motu->tx_resources);184			fw_iso_resources_free(&motu->rx_resources);185			return err;186		}187 188		motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer;189		motu->cache.event_offsets = kcalloc(motu->cache.size, sizeof(*motu->cache.event_offsets),190						  GFP_KERNEL);191		if (!motu->cache.event_offsets) {192			fw_iso_resources_free(&motu->tx_resources);193			fw_iso_resources_free(&motu->rx_resources);194			return -ENOMEM;195		}196	}197 198	return 0;199}200 201static int ensure_packet_formats(struct snd_motu *motu)202{203	__be32 reg;204	u32 data;205	int err;206 207	err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,208					sizeof(reg));209	if (err < 0)210		return err;211	data = be32_to_cpu(reg);212 213	data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |214		  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|215		  TX_PACKET_TRANSMISSION_SPEED_MASK);216	if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])217		data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;218	if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0])219		data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;220	data |= fw_parent_device(motu->unit)->max_speed;221 222	reg = cpu_to_be32(data);223	return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,224					  sizeof(reg));225}226 227int snd_motu_stream_start_duplex(struct snd_motu *motu)228{229	unsigned int generation = motu->rx_resources.generation;230	int err = 0;231 232	if (motu->substreams_counter == 0)233		return 0;234 235	if (amdtp_streaming_error(&motu->rx_stream) ||236	    amdtp_streaming_error(&motu->tx_stream)) {237		amdtp_domain_stop(&motu->domain);238		finish_session(motu);239	}240 241	if (generation != fw_parent_device(motu->unit)->card->generation) {242		err = fw_iso_resources_update(&motu->rx_resources);243		if (err < 0)244			return err;245 246		err = fw_iso_resources_update(&motu->tx_resources);247		if (err < 0)248			return err;249	}250 251	if (!amdtp_stream_running(&motu->rx_stream)) {252		int spd = fw_parent_device(motu->unit)->max_speed;253 254		err = ensure_packet_formats(motu);255		if (err < 0)256			return err;257 258		if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {259			err = snd_motu_register_dsp_message_parser_init(motu);260			if (err < 0)261				return err;262		} else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {263			err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);264			if (err < 0)265				return err;266		}267 268		err = begin_session(motu);269		if (err < 0) {270			dev_err(&motu->unit->device,271				"fail to start isochronous comm: %d\n", err);272			goto stop_streams;273		}274 275		err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,276					      motu->tx_resources.channel, spd);277		if (err < 0)278			goto stop_streams;279 280		err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,281					      motu->rx_resources.channel, spd);282		if (err < 0)283			goto stop_streams;284 285		motu->cache.tail = 0;286		motu->cache.tx_cycle_count = UINT_MAX;287		motu->cache.head = 0;288		motu->cache.rx_cycle_count = UINT_MAX;289 290		// NOTE: The device requires both of replay; the sequence of the number of data291		// blocks per packet, and the sequence of source packet header per data block as292		// presentation time.293		err = amdtp_domain_start(&motu->domain, 0, true, false);294		if (err < 0)295			goto stop_streams;296 297		if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) {298			err = -ETIMEDOUT;299			goto stop_streams;300		}301 302		err = snd_motu_protocol_switch_fetching_mode(motu, true);303		if (err < 0) {304			dev_err(&motu->unit->device,305				"fail to enable frame fetching: %d\n", err);306			goto stop_streams;307		}308	}309 310	return 0;311 312stop_streams:313	amdtp_domain_stop(&motu->domain);314	finish_session(motu);315	return err;316}317 318void snd_motu_stream_stop_duplex(struct snd_motu *motu)319{320	if (motu->substreams_counter == 0) {321		amdtp_domain_stop(&motu->domain);322		finish_session(motu);323 324		fw_iso_resources_free(&motu->tx_resources);325		fw_iso_resources_free(&motu->rx_resources);326 327		kfree(motu->cache.event_offsets);328		motu->cache.event_offsets = NULL;329	}330}331 332static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)333{334	struct fw_iso_resources *resources;335	enum amdtp_stream_direction dir;336	int err;337 338	if (s == &motu->tx_stream) {339		resources = &motu->tx_resources;340		dir = AMDTP_IN_STREAM;341	} else {342		resources = &motu->rx_resources;343		dir = AMDTP_OUT_STREAM;344	}345 346	err = fw_iso_resources_init(resources, motu->unit);347	if (err < 0)348		return err;349 350	err = amdtp_motu_init(s, motu->unit, dir, motu->spec, &motu->cache);351	if (err < 0)352		fw_iso_resources_destroy(resources);353 354	return err;355}356 357static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)358{359	amdtp_stream_destroy(s);360 361	if (s == &motu->tx_stream)362		fw_iso_resources_destroy(&motu->tx_resources);363	else364		fw_iso_resources_destroy(&motu->rx_resources);365}366 367int snd_motu_stream_init_duplex(struct snd_motu *motu)368{369	int err;370 371	err = init_stream(motu, &motu->tx_stream);372	if (err < 0)373		return err;374 375	err = init_stream(motu, &motu->rx_stream);376	if (err < 0) {377		destroy_stream(motu, &motu->tx_stream);378		return err;379	}380 381	err = amdtp_domain_init(&motu->domain);382	if (err < 0) {383		destroy_stream(motu, &motu->tx_stream);384		destroy_stream(motu, &motu->rx_stream);385	}386 387	return err;388}389 390// This function should be called before starting streams or after stopping391// streams.392void snd_motu_stream_destroy_duplex(struct snd_motu *motu)393{394	amdtp_domain_destroy(&motu->domain);395 396	destroy_stream(motu, &motu->rx_stream);397	destroy_stream(motu, &motu->tx_stream);398 399	motu->substreams_counter = 0;400}401 402static void motu_lock_changed(struct snd_motu *motu)403{404	motu->dev_lock_changed = true;405	wake_up(&motu->hwdep_wait);406}407 408int snd_motu_stream_lock_try(struct snd_motu *motu)409{410	int err;411 412	spin_lock_irq(&motu->lock);413 414	if (motu->dev_lock_count < 0) {415		err = -EBUSY;416		goto out;417	}418 419	if (motu->dev_lock_count++ == 0)420		motu_lock_changed(motu);421	err = 0;422out:423	spin_unlock_irq(&motu->lock);424	return err;425}426 427void snd_motu_stream_lock_release(struct snd_motu *motu)428{429	spin_lock_irq(&motu->lock);430 431	if (WARN_ON(motu->dev_lock_count <= 0))432		goto out;433 434	if (--motu->dev_lock_count == 0)435		motu_lock_changed(motu);436out:437	spin_unlock_irq(&motu->lock);438}439