brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · 295163b Raw
460 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family4 *5 * Copyright (c) 2014-2015 Takashi Sakamoto6 */7 8#include "digi00x.h"9 10#define READY_TIMEOUT_MS	20011 12const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {13	[SND_DG00X_RATE_44100] = 44100,14	[SND_DG00X_RATE_48000] = 48000,15	[SND_DG00X_RATE_88200] = 88200,16	[SND_DG00X_RATE_96000] = 96000,17};18 19/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */20const unsigned int21snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {22	/* Analog/ADAT/SPDIF */23	[SND_DG00X_RATE_44100] = (8 + 8 + 2),24	[SND_DG00X_RATE_48000] = (8 + 8 + 2),25	/* Analog/SPDIF */26	[SND_DG00X_RATE_88200] = (8 + 2),27	[SND_DG00X_RATE_96000] = (8 + 2),28};29 30int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)31{32	u32 data;33	__be32 reg;34	int err;35 36	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,37				 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,38				 &reg, sizeof(reg), 0);39	if (err < 0)40		return err;41 42	data = be32_to_cpu(reg) & 0x0f;43	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))44		*rate = snd_dg00x_stream_rates[data];45	else46		err = -EIO;47 48	return err;49}50 51int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)52{53	__be32 reg;54	unsigned int i;55 56	for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {57		if (rate == snd_dg00x_stream_rates[i])58			break;59	}60	if (i == ARRAY_SIZE(snd_dg00x_stream_rates))61		return -EINVAL;62 63	reg = cpu_to_be32(i);64	return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,65				  DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,66				  &reg, sizeof(reg), 0);67}68 69int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,70			       enum snd_dg00x_clock *clock)71{72	__be32 reg;73	int err;74 75	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,76				 DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,77				 &reg, sizeof(reg), 0);78	if (err < 0)79		return err;80 81	*clock = be32_to_cpu(reg) & 0x0f;82	if (*clock >= SND_DG00X_CLOCK_COUNT)83		err = -EIO;84 85	return err;86}87 88int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)89{90	__be32 reg;91	int err;92 93	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,94				 DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,95				 &reg, sizeof(reg), 0);96	if (err >= 0)97		*detect = be32_to_cpu(reg) > 0;98 99	return err;100}101 102int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,103				       unsigned int *rate)104{105	u32 data;106	__be32 reg;107	int err;108 109	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,110				 DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,111				 &reg, sizeof(reg), 0);112	if (err < 0)113		return err;114 115	data = be32_to_cpu(reg) & 0x0f;116	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))117		*rate = snd_dg00x_stream_rates[data];118	/* This means desync. */119	else120		err = -EBUSY;121 122	return err;123}124 125static void finish_session(struct snd_dg00x *dg00x)126{127	__be32 data;128 129	data = cpu_to_be32(0x00000003);130	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,131			   DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,132			   &data, sizeof(data), 0);133 134	// Unregister isochronous channels for both direction.135	data = 0;136	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,137			   DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,138			   &data, sizeof(data), 0);139 140	// Just after finishing the session, the device may lost transmitting141	// functionality for a short time.142	msleep(50);143}144 145static int begin_session(struct snd_dg00x *dg00x)146{147	__be32 data;148	u32 curr;149	int err;150 151	// Register isochronous channels for both direction.152	data = cpu_to_be32((dg00x->tx_resources.channel << 16) |153			   dg00x->rx_resources.channel);154	err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,155				 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,156				 &data, sizeof(data), 0);157	if (err < 0)158		return err;159 160	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,161				 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,162				 &data, sizeof(data), 0);163	if (err < 0)164		return err;165	curr = be32_to_cpu(data);166 167	if (curr == 0)168		curr = 2;169 170	curr--;171	while (curr > 0) {172		data = cpu_to_be32(curr);173		err = snd_fw_transaction(dg00x->unit,174					 TCODE_WRITE_QUADLET_REQUEST,175					 DG00X_ADDR_BASE +176					 DG00X_OFFSET_STREAMING_SET,177					 &data, sizeof(data), 0);178		if (err < 0)179			break;180 181		msleep(20);182		curr--;183	}184 185	return err;186}187 188static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,189			  unsigned int rate)190{191	struct fw_iso_resources *resources;192	int i;193	int err;194 195	// Check sampling rate.196	for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {197		if (snd_dg00x_stream_rates[i] == rate)198			break;199	}200	if (i == SND_DG00X_RATE_COUNT)201		return -EINVAL;202 203	if (stream == &dg00x->tx_stream)204		resources = &dg00x->tx_resources;205	else206		resources = &dg00x->rx_resources;207 208	err = amdtp_dot_set_parameters(stream, rate,209				       snd_dg00x_stream_pcm_channels[i]);210	if (err < 0)211		return err;212 213	return fw_iso_resources_allocate(resources,214				amdtp_stream_get_max_payload(stream),215				fw_parent_device(dg00x->unit)->max_speed);216}217 218static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)219{220	struct fw_iso_resources *resources;221	enum amdtp_stream_direction dir;222	int err;223 224	if (s == &dg00x->tx_stream) {225		resources = &dg00x->tx_resources;226		dir = AMDTP_IN_STREAM;227	} else {228		resources = &dg00x->rx_resources;229		dir = AMDTP_OUT_STREAM;230	}231 232	err = fw_iso_resources_init(resources, dg00x->unit);233	if (err < 0)234		return err;235 236	err = amdtp_dot_init(s, dg00x->unit, dir);237	if (err < 0)238		fw_iso_resources_destroy(resources);239 240	return err;241}242 243static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)244{245	amdtp_stream_destroy(s);246 247	if (s == &dg00x->tx_stream)248		fw_iso_resources_destroy(&dg00x->tx_resources);249	else250		fw_iso_resources_destroy(&dg00x->rx_resources);251}252 253int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)254{255	int err;256 257	err = init_stream(dg00x, &dg00x->rx_stream);258	if (err < 0)259		return err;260 261	err = init_stream(dg00x, &dg00x->tx_stream);262	if (err < 0) {263		destroy_stream(dg00x, &dg00x->rx_stream);264		return err;265	}266 267	err = amdtp_domain_init(&dg00x->domain);268	if (err < 0) {269		destroy_stream(dg00x, &dg00x->rx_stream);270		destroy_stream(dg00x, &dg00x->tx_stream);271	}272 273	return err;274}275 276/*277 * This function should be called before starting streams or after stopping278 * streams.279 */280void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)281{282	amdtp_domain_destroy(&dg00x->domain);283 284	destroy_stream(dg00x, &dg00x->rx_stream);285	destroy_stream(dg00x, &dg00x->tx_stream);286}287 288int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,289				    unsigned int frames_per_period,290				    unsigned int frames_per_buffer)291{292	unsigned int curr_rate;293	int err;294 295	err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);296	if (err < 0)297		return err;298	if (rate == 0)299		rate = curr_rate;300 301	if (dg00x->substreams_counter == 0 || curr_rate != rate) {302		amdtp_domain_stop(&dg00x->domain);303 304		finish_session(dg00x);305 306		fw_iso_resources_free(&dg00x->tx_resources);307		fw_iso_resources_free(&dg00x->rx_resources);308 309		err = snd_dg00x_stream_set_local_rate(dg00x, rate);310		if (err < 0)311			return err;312 313		err = keep_resources(dg00x, &dg00x->rx_stream, rate);314		if (err < 0)315			return err;316 317		err = keep_resources(dg00x, &dg00x->tx_stream, rate);318		if (err < 0) {319			fw_iso_resources_free(&dg00x->rx_resources);320			return err;321		}322 323		err = amdtp_domain_set_events_per_period(&dg00x->domain,324					frames_per_period, frames_per_buffer);325		if (err < 0) {326			fw_iso_resources_free(&dg00x->rx_resources);327			fw_iso_resources_free(&dg00x->tx_resources);328			return err;329		}330	}331 332	return 0;333}334 335int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)336{337	unsigned int generation = dg00x->rx_resources.generation;338	int err = 0;339 340	if (dg00x->substreams_counter == 0)341		return 0;342 343	if (amdtp_streaming_error(&dg00x->tx_stream) ||344	    amdtp_streaming_error(&dg00x->rx_stream)) {345		amdtp_domain_stop(&dg00x->domain);346		finish_session(dg00x);347	}348 349	if (generation != fw_parent_device(dg00x->unit)->card->generation) {350		err = fw_iso_resources_update(&dg00x->tx_resources);351		if (err < 0)352			goto error;353 354		err = fw_iso_resources_update(&dg00x->rx_resources);355		if (err < 0)356			goto error;357	}358 359	/*360	 * No packets are transmitted without receiving packets, reagardless of361	 * which source of clock is used.362	 */363	if (!amdtp_stream_running(&dg00x->rx_stream)) {364		int spd = fw_parent_device(dg00x->unit)->max_speed;365 366		err = begin_session(dg00x);367		if (err < 0)368			goto error;369 370		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,371					      dg00x->rx_resources.channel, spd);372		if (err < 0)373			goto error;374 375		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,376					      dg00x->tx_resources.channel, spd);377		if (err < 0)378			goto error;379 380		// NOTE: The device doesn't start packet transmission till receiving any packet.381		// It ignores presentation time expressed by the value of syt field of CIP header382		// in received packets. The sequence of the number of data blocks per packet is383		// important for media clock recovery.384		err = amdtp_domain_start(&dg00x->domain, 0, true, true);385		if (err < 0)386			goto error;387 388		if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {389			err = -ETIMEDOUT;390			goto error;391		}392	}393 394	return 0;395error:396	amdtp_domain_stop(&dg00x->domain);397	finish_session(dg00x);398 399	return err;400}401 402void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)403{404	if (dg00x->substreams_counter == 0) {405		amdtp_domain_stop(&dg00x->domain);406		finish_session(dg00x);407 408		fw_iso_resources_free(&dg00x->tx_resources);409		fw_iso_resources_free(&dg00x->rx_resources);410	}411}412 413void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)414{415	fw_iso_resources_update(&dg00x->tx_resources);416	fw_iso_resources_update(&dg00x->rx_resources);417 418	amdtp_stream_update(&dg00x->tx_stream);419	amdtp_stream_update(&dg00x->rx_stream);420}421 422void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)423{424	dg00x->dev_lock_changed = true;425	wake_up(&dg00x->hwdep_wait);426}427 428int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)429{430	int err;431 432	spin_lock_irq(&dg00x->lock);433 434	/* user land lock this */435	if (dg00x->dev_lock_count < 0) {436		err = -EBUSY;437		goto end;438	}439 440	/* this is the first time */441	if (dg00x->dev_lock_count++ == 0)442		snd_dg00x_stream_lock_changed(dg00x);443	err = 0;444end:445	spin_unlock_irq(&dg00x->lock);446	return err;447}448 449void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)450{451	spin_lock_irq(&dg00x->lock);452 453	if (WARN_ON(dg00x->dev_lock_count <= 0))454		goto end;455	if (--dg00x->dev_lock_count == 0)456		snd_dg00x_stream_lock_changed(dg00x);457end:458	spin_unlock_irq(&dg00x->lock);459}460