brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · dfe783d Raw
560 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tascam-stream.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8#include <linux/delay.h>9#include "tascam.h"10 11#define CLOCK_STATUS_MASK      0xffff000012#define CLOCK_CONFIG_MASK      0x0000ffff13 14#define READY_TIMEOUT_MS	400015 16static int get_clock(struct snd_tscm *tscm, u32 *data)17{18	int trial = 0;19	__be32 reg;20	int err;21 22	while (trial++ < 5) {23		err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,24				TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,25				&reg, sizeof(reg), 0);26		if (err < 0)27			return err;28 29		*data = be32_to_cpu(reg);30		if (*data & CLOCK_STATUS_MASK)31			break;32 33		// In intermediate state after changing clock status.34		msleep(50);35	}36 37	// Still in the intermediate state.38	if (trial >= 5)39		return -EAGAIN;40 41	return 0;42}43 44static int set_clock(struct snd_tscm *tscm, unsigned int rate,45		     enum snd_tscm_clock clock)46{47	u32 data;48	__be32 reg;49	int err;50 51	err = get_clock(tscm, &data);52	if (err < 0)53		return err;54	data &= CLOCK_CONFIG_MASK;55 56	if (rate > 0) {57		data &= 0x000000ff;58		/* Base rate. */59		if ((rate % 44100) == 0) {60			data |= 0x00000100;61			/* Multiplier. */62			if (rate / 44100 == 2)63				data |= 0x00008000;64		} else if ((rate % 48000) == 0) {65			data |= 0x00000200;66			/* Multiplier. */67			if (rate / 48000 == 2)68				data |= 0x00008000;69		} else {70			return -EAGAIN;71		}72	}73 74	if (clock != INT_MAX) {75		data &= 0x0000ff00;76		data |= clock + 1;77	}78 79	reg = cpu_to_be32(data);80 81	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,82				 TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,83				 &reg, sizeof(reg), 0);84	if (err < 0)85		return err;86 87	if (data & 0x00008000)88		reg = cpu_to_be32(0x0000001a);89	else90		reg = cpu_to_be32(0x0000000d);91 92	return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,93				  TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,94				  &reg, sizeof(reg), 0);95}96 97int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)98{99	u32 data;100	int err;101 102	err = get_clock(tscm, &data);103	if (err < 0)104		return err;105 106	data = (data & 0xff000000) >> 24;107 108	/* Check base rate. */109	if ((data & 0x0f) == 0x01)110		*rate = 44100;111	else if ((data & 0x0f) == 0x02)112		*rate = 48000;113	else114		return -EAGAIN;115 116	/* Check multiplier. */117	if ((data & 0xf0) == 0x80)118		*rate *= 2;119	else if ((data & 0xf0) != 0x00)120		return -EAGAIN;121 122	return err;123}124 125int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)126{127	u32 data;128	int err;129 130	err = get_clock(tscm, &data);131	if (err < 0)132		return err;133 134	*clock = ((data & 0x00ff0000) >> 16) - 1;135	if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)136		return -EIO;137 138	return 0;139}140 141static int enable_data_channels(struct snd_tscm *tscm)142{143	__be32 reg;144	u32 data;145	unsigned int i;146	int err;147 148	data = 0;149	for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)150		data |= BIT(i);151	if (tscm->spec->has_adat)152		data |= 0x0000ff00;153	if (tscm->spec->has_spdif)154		data |= 0x00030000;155 156	reg = cpu_to_be32(data);157	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,158				 TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,159				 &reg, sizeof(reg), 0);160	if (err < 0)161		return err;162 163	data = 0;164	for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)165		data |= BIT(i);166	if (tscm->spec->has_adat)167		data |= 0x0000ff00;168	if (tscm->spec->has_spdif)169		data |= 0x00030000;170 171	reg = cpu_to_be32(data);172	return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,173				  TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,174				  &reg, sizeof(reg), 0);175}176 177static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)178{179	__be32 reg;180	int err;181 182	// Set an option for unknown purpose.183	reg = cpu_to_be32(0x00200000);184	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,185				 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,186				 &reg, sizeof(reg), 0);187	if (err < 0)188		return err;189 190	return enable_data_channels(tscm);191}192 193static void finish_session(struct snd_tscm *tscm)194{195	__be32 reg;196 197	reg = 0;198	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,199			   TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,200			   &reg, sizeof(reg), 0);201 202	reg = 0;203	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,204			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,205			   &reg, sizeof(reg), 0);206 207	// Unregister channels.208	reg = cpu_to_be32(0x00000000);209	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,210			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,211			   &reg, sizeof(reg), 0);212	reg = cpu_to_be32(0x00000000);213	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,214			   TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,215			   &reg, sizeof(reg), 0);216	reg = cpu_to_be32(0x00000000);217	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,218			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,219			   &reg, sizeof(reg), 0);220}221 222static int begin_session(struct snd_tscm *tscm)223{224	__be32 reg;225	int err;226 227	// Register the isochronous channel for transmitting stream.228	reg = cpu_to_be32(tscm->tx_resources.channel);229	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,230				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,231				 &reg, sizeof(reg), 0);232	if (err < 0)233		return err;234 235	// Unknown.236	reg = cpu_to_be32(0x00000002);237	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,238				 TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,239				 &reg, sizeof(reg), 0);240	if (err < 0)241		return err;242 243	// Register the isochronous channel for receiving stream.244	reg = cpu_to_be32(tscm->rx_resources.channel);245	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,246				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,247				 &reg, sizeof(reg), 0);248	if (err < 0)249		return err;250 251	reg = cpu_to_be32(0x00000001);252	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,253				 TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,254				 &reg, sizeof(reg), 0);255	if (err < 0)256		return err;257 258	reg = cpu_to_be32(0x00000001);259	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,260				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,261				 &reg, sizeof(reg), 0);262	if (err < 0)263		return err;264 265	// Set an option for unknown purpose.266	reg = cpu_to_be32(0x00002000);267	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,268				 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,269				 &reg, sizeof(reg), 0);270	if (err < 0)271		return err;272 273	// Start multiplexing PCM samples on packets.274	reg = cpu_to_be32(0x00000001);275	return snd_fw_transaction(tscm->unit,276				  TCODE_WRITE_QUADLET_REQUEST,277				  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,278				  &reg, sizeof(reg), 0);279}280 281static int keep_resources(struct snd_tscm *tscm, unsigned int rate,282			  struct amdtp_stream *stream)283{284	struct fw_iso_resources *resources;285	int err;286 287	if (stream == &tscm->tx_stream)288		resources = &tscm->tx_resources;289	else290		resources = &tscm->rx_resources;291 292	err = amdtp_tscm_set_parameters(stream, rate);293	if (err < 0)294		return err;295 296	return fw_iso_resources_allocate(resources,297				amdtp_stream_get_max_payload(stream),298				fw_parent_device(tscm->unit)->max_speed);299}300 301static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)302{303	struct fw_iso_resources *resources;304	enum amdtp_stream_direction dir;305	unsigned int pcm_channels;306	int err;307 308	if (s == &tscm->tx_stream) {309		resources = &tscm->tx_resources;310		dir = AMDTP_IN_STREAM;311		pcm_channels = tscm->spec->pcm_capture_analog_channels;312	} else {313		resources = &tscm->rx_resources;314		dir = AMDTP_OUT_STREAM;315		pcm_channels = tscm->spec->pcm_playback_analog_channels;316	}317 318	if (tscm->spec->has_adat)319		pcm_channels += 8;320	if (tscm->spec->has_spdif)321		pcm_channels += 2;322 323	err = fw_iso_resources_init(resources, tscm->unit);324	if (err < 0)325		return err;326 327	err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);328	if (err < 0)329		fw_iso_resources_free(resources);330 331	return err;332}333 334static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)335{336	amdtp_stream_destroy(s);337 338	if (s == &tscm->tx_stream)339		fw_iso_resources_destroy(&tscm->tx_resources);340	else341		fw_iso_resources_destroy(&tscm->rx_resources);342}343 344int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)345{346	int err;347 348	err = init_stream(tscm, &tscm->tx_stream);349	if (err < 0)350		return err;351 352	err = init_stream(tscm, &tscm->rx_stream);353	if (err < 0) {354		destroy_stream(tscm, &tscm->tx_stream);355		return err;356	}357 358	err = amdtp_domain_init(&tscm->domain);359	if (err < 0) {360		destroy_stream(tscm, &tscm->tx_stream);361		destroy_stream(tscm, &tscm->rx_stream);362	}363 364	return err;365}366 367// At bus reset, streaming is stopped and some registers are clear.368void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)369{370	amdtp_domain_stop(&tscm->domain);371 372	amdtp_stream_pcm_abort(&tscm->tx_stream);373	amdtp_stream_pcm_abort(&tscm->rx_stream);374}375 376// This function should be called before starting streams or after stopping377// streams.378void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)379{380	amdtp_domain_destroy(&tscm->domain);381 382	destroy_stream(tscm, &tscm->rx_stream);383	destroy_stream(tscm, &tscm->tx_stream);384}385 386int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,387				   unsigned int frames_per_period,388				   unsigned int frames_per_buffer)389{390	unsigned int curr_rate;391	int err;392 393	err = snd_tscm_stream_get_rate(tscm, &curr_rate);394	if (err < 0)395		return err;396 397	if (tscm->substreams_counter == 0 || rate != curr_rate) {398		amdtp_domain_stop(&tscm->domain);399 400		finish_session(tscm);401 402		fw_iso_resources_free(&tscm->tx_resources);403		fw_iso_resources_free(&tscm->rx_resources);404 405		err = set_clock(tscm, rate, INT_MAX);406		if (err < 0)407			return err;408 409		err = keep_resources(tscm, rate, &tscm->tx_stream);410		if (err < 0)411			return err;412 413		err = keep_resources(tscm, rate, &tscm->rx_stream);414		if (err < 0) {415			fw_iso_resources_free(&tscm->tx_resources);416			return err;417		}418 419		err = amdtp_domain_set_events_per_period(&tscm->domain,420					frames_per_period, frames_per_buffer);421		if (err < 0) {422			fw_iso_resources_free(&tscm->tx_resources);423			fw_iso_resources_free(&tscm->rx_resources);424			return err;425		}426 427		tscm->need_long_tx_init_skip = (rate != curr_rate);428	}429 430	return 0;431}432 433int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)434{435	unsigned int generation = tscm->rx_resources.generation;436	int err;437 438	if (tscm->substreams_counter == 0)439		return 0;440 441	if (amdtp_streaming_error(&tscm->rx_stream) ||442	    amdtp_streaming_error(&tscm->tx_stream)) {443		amdtp_domain_stop(&tscm->domain);444		finish_session(tscm);445	}446 447	if (generation != fw_parent_device(tscm->unit)->card->generation) {448		err = fw_iso_resources_update(&tscm->tx_resources);449		if (err < 0)450			goto error;451 452		err = fw_iso_resources_update(&tscm->rx_resources);453		if (err < 0)454			goto error;455	}456 457	if (!amdtp_stream_running(&tscm->rx_stream)) {458		int spd = fw_parent_device(tscm->unit)->max_speed;459		unsigned int tx_init_skip_cycles;460 461		err = set_stream_formats(tscm, rate);462		if (err < 0)463			goto error;464 465		err = begin_session(tscm);466		if (err < 0)467			goto error;468 469		err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,470					      tscm->rx_resources.channel, spd);471		if (err < 0)472			goto error;473 474		err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,475					      tscm->tx_resources.channel, spd);476		if (err < 0)477			goto error;478 479		if (tscm->need_long_tx_init_skip)480			tx_init_skip_cycles = 16000;481		else482			tx_init_skip_cycles = 0;483 484		// MEMO: Just after starting packet streaming, it transfers packets without any485		// event. Enough after receiving the sequence of packets, it multiplexes events into486		// the packet. However, just after changing sampling transfer frequency, it stops487		// multiplexing during packet transmission. Enough after, it restarts multiplexing488		// again. The device ignores presentation time expressed by the value of syt field489		// of CIP header in received packets. The sequence of the number of data blocks per490		// packet is important for media clock recovery.491		err = amdtp_domain_start(&tscm->domain, tx_init_skip_cycles, true, true);492		if (err < 0)493			goto error;494 495		if (!amdtp_domain_wait_ready(&tscm->domain, READY_TIMEOUT_MS)) {496			err = -ETIMEDOUT;497			goto error;498		}499	}500 501	return 0;502error:503	amdtp_domain_stop(&tscm->domain);504	finish_session(tscm);505 506	return err;507}508 509void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)510{511	if (tscm->substreams_counter == 0) {512		amdtp_domain_stop(&tscm->domain);513		finish_session(tscm);514 515		fw_iso_resources_free(&tscm->tx_resources);516		fw_iso_resources_free(&tscm->rx_resources);517 518		tscm->need_long_tx_init_skip = false;519	}520}521 522void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)523{524	tscm->dev_lock_changed = true;525	wake_up(&tscm->hwdep_wait);526}527 528int snd_tscm_stream_lock_try(struct snd_tscm *tscm)529{530	int err;531 532	spin_lock_irq(&tscm->lock);533 534	/* user land lock this */535	if (tscm->dev_lock_count < 0) {536		err = -EBUSY;537		goto end;538	}539 540	/* this is the first time */541	if (tscm->dev_lock_count++ == 0)542		snd_tscm_stream_lock_changed(tscm);543	err = 0;544end:545	spin_unlock_irq(&tscm->lock);546	return err;547}548 549void snd_tscm_stream_lock_release(struct snd_tscm *tscm)550{551	spin_lock_irq(&tscm->lock);552 553	if (WARN_ON(tscm->dev_lock_count <= 0))554		goto end;555	if (--tscm->dev_lock_count == 0)556		snd_tscm_stream_lock_changed(tscm);557end:558	spin_unlock_irq(&tscm->lock);559}560