1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
275d6d898STakashi Sakamoto /*
375d6d898STakashi Sakamoto  * ff-stream.c - a part of driver for RME Fireface series
475d6d898STakashi Sakamoto  *
575d6d898STakashi Sakamoto  * Copyright (c) 2015-2017 Takashi Sakamoto
675d6d898STakashi Sakamoto  */
775d6d898STakashi Sakamoto 
875d6d898STakashi Sakamoto #include "ff.h"
975d6d898STakashi Sakamoto 
10bdaedca7STakashi Sakamoto #define READY_TIMEOUT_MS	200
1175d6d898STakashi Sakamoto 
snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,enum snd_ff_stream_mode * mode)1276ea4688STakashi Sakamoto int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
1376ea4688STakashi Sakamoto 				      enum snd_ff_stream_mode *mode)
1475d6d898STakashi Sakamoto {
1576ea4688STakashi Sakamoto 	static const enum snd_ff_stream_mode modes[] = {
1676ea4688STakashi Sakamoto 		[CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
1776ea4688STakashi Sakamoto 		[CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
1876ea4688STakashi Sakamoto 		[CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
1976ea4688STakashi Sakamoto 		[CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
2076ea4688STakashi Sakamoto 		[CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
2176ea4688STakashi Sakamoto 		[CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
2276ea4688STakashi Sakamoto 		[CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
2376ea4688STakashi Sakamoto 	};
2475d6d898STakashi Sakamoto 
2576ea4688STakashi Sakamoto 	if (sfc >= CIP_SFC_COUNT)
2675d6d898STakashi Sakamoto 		return -EINVAL;
2775d6d898STakashi Sakamoto 
2876ea4688STakashi Sakamoto 	*mode = modes[sfc];
2975d6d898STakashi Sakamoto 
3075d6d898STakashi Sakamoto 	return 0;
3175d6d898STakashi Sakamoto }
3275d6d898STakashi Sakamoto 
finish_session(struct snd_ff * ff)3375d6d898STakashi Sakamoto static inline void finish_session(struct snd_ff *ff)
3475d6d898STakashi Sakamoto {
3575d6d898STakashi Sakamoto 	ff->spec->protocol->finish_session(ff);
36ae3053c2STakashi Sakamoto 	ff->spec->protocol->switch_fetching_mode(ff, false);
3775d6d898STakashi Sakamoto }
3875d6d898STakashi Sakamoto 
init_stream(struct snd_ff * ff,struct amdtp_stream * s)3942355abbSTakashi Sakamoto static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
4075d6d898STakashi Sakamoto {
4175d6d898STakashi Sakamoto 	struct fw_iso_resources *resources;
4242355abbSTakashi Sakamoto 	enum amdtp_stream_direction dir;
4342355abbSTakashi Sakamoto 	int err;
4475d6d898STakashi Sakamoto 
4542355abbSTakashi Sakamoto 	if (s == &ff->tx_stream) {
4675d6d898STakashi Sakamoto 		resources = &ff->tx_resources;
4742355abbSTakashi Sakamoto 		dir = AMDTP_IN_STREAM;
4875d6d898STakashi Sakamoto 	} else {
4975d6d898STakashi Sakamoto 		resources = &ff->rx_resources;
5042355abbSTakashi Sakamoto 		dir = AMDTP_OUT_STREAM;
5175d6d898STakashi Sakamoto 	}
5275d6d898STakashi Sakamoto 
5375d6d898STakashi Sakamoto 	err = fw_iso_resources_init(resources, ff->unit);
5475d6d898STakashi Sakamoto 	if (err < 0)
5575d6d898STakashi Sakamoto 		return err;
5675d6d898STakashi Sakamoto 
5742355abbSTakashi Sakamoto 	err = amdtp_ff_init(s, ff->unit, dir);
5875d6d898STakashi Sakamoto 	if (err < 0)
5975d6d898STakashi Sakamoto 		fw_iso_resources_destroy(resources);
6075d6d898STakashi Sakamoto 
6175d6d898STakashi Sakamoto 	return err;
6275d6d898STakashi Sakamoto }
6375d6d898STakashi Sakamoto 
destroy_stream(struct snd_ff * ff,struct amdtp_stream * s)6442355abbSTakashi Sakamoto static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
6575d6d898STakashi Sakamoto {
6642355abbSTakashi Sakamoto 	amdtp_stream_destroy(s);
6742355abbSTakashi Sakamoto 
6842355abbSTakashi Sakamoto 	if (s == &ff->tx_stream)
6975d6d898STakashi Sakamoto 		fw_iso_resources_destroy(&ff->tx_resources);
7042355abbSTakashi Sakamoto 	else
7175d6d898STakashi Sakamoto 		fw_iso_resources_destroy(&ff->rx_resources);
7275d6d898STakashi Sakamoto }
7375d6d898STakashi Sakamoto 
snd_ff_stream_init_duplex(struct snd_ff * ff)7475d6d898STakashi Sakamoto int snd_ff_stream_init_duplex(struct snd_ff *ff)
7575d6d898STakashi Sakamoto {
7675d6d898STakashi Sakamoto 	int err;
7775d6d898STakashi Sakamoto 
7842355abbSTakashi Sakamoto 	err = init_stream(ff, &ff->rx_stream);
7975d6d898STakashi Sakamoto 	if (err < 0)
8042355abbSTakashi Sakamoto 		return err;
8175d6d898STakashi Sakamoto 
8242355abbSTakashi Sakamoto 	err = init_stream(ff, &ff->tx_stream);
83c9a9ce89STakashi Sakamoto 	if (err < 0) {
8442355abbSTakashi Sakamoto 		destroy_stream(ff, &ff->rx_stream);
85c9a9ce89STakashi Sakamoto 		return err;
86c9a9ce89STakashi Sakamoto 	}
87c9a9ce89STakashi Sakamoto 
88c9a9ce89STakashi Sakamoto 	err = amdtp_domain_init(&ff->domain);
89c9a9ce89STakashi Sakamoto 	if (err < 0) {
90c9a9ce89STakashi Sakamoto 		destroy_stream(ff, &ff->rx_stream);
91c9a9ce89STakashi Sakamoto 		destroy_stream(ff, &ff->tx_stream);
92c9a9ce89STakashi Sakamoto 	}
9342355abbSTakashi Sakamoto 
9475d6d898STakashi Sakamoto 	return err;
9575d6d898STakashi Sakamoto }
9675d6d898STakashi Sakamoto 
9775d6d898STakashi Sakamoto /*
9875d6d898STakashi Sakamoto  * This function should be called before starting streams or after stopping
9975d6d898STakashi Sakamoto  * streams.
10075d6d898STakashi Sakamoto  */
snd_ff_stream_destroy_duplex(struct snd_ff * ff)10175d6d898STakashi Sakamoto void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
10275d6d898STakashi Sakamoto {
103c9a9ce89STakashi Sakamoto 	amdtp_domain_destroy(&ff->domain);
104c9a9ce89STakashi Sakamoto 
10542355abbSTakashi Sakamoto 	destroy_stream(ff, &ff->rx_stream);
10642355abbSTakashi Sakamoto 	destroy_stream(ff, &ff->tx_stream);
10775d6d898STakashi Sakamoto }
10875d6d898STakashi Sakamoto 
snd_ff_stream_reserve_duplex(struct snd_ff * ff,unsigned int rate,unsigned int frames_per_period,unsigned int frames_per_buffer)1099d9ff58cSTakashi Sakamoto int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
1104de3eb06STakashi Sakamoto 				 unsigned int frames_per_period,
1114de3eb06STakashi Sakamoto 				 unsigned int frames_per_buffer)
11275d6d898STakashi Sakamoto {
11375d6d898STakashi Sakamoto 	unsigned int curr_rate;
11475d6d898STakashi Sakamoto 	enum snd_ff_clock_src src;
11575d6d898STakashi Sakamoto 	int err;
11675d6d898STakashi Sakamoto 
117b1d0cb0aSTakashi Sakamoto 	err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
11875d6d898STakashi Sakamoto 	if (err < 0)
11975d6d898STakashi Sakamoto 		return err;
12055162d2bSTakashi Sakamoto 
12155162d2bSTakashi Sakamoto 	if (ff->substreams_counter == 0 || curr_rate != rate) {
12255162d2bSTakashi Sakamoto 		enum snd_ff_stream_mode mode;
12355162d2bSTakashi Sakamoto 		int i;
12475d6d898STakashi Sakamoto 
125c9a9ce89STakashi Sakamoto 		amdtp_domain_stop(&ff->domain);
12655162d2bSTakashi Sakamoto 		finish_session(ff);
12775d6d898STakashi Sakamoto 
12855162d2bSTakashi Sakamoto 		fw_iso_resources_free(&ff->tx_resources);
12955162d2bSTakashi Sakamoto 		fw_iso_resources_free(&ff->rx_resources);
130365c00d0STakashi Sakamoto 
131365c00d0STakashi Sakamoto 		for (i = 0; i < CIP_SFC_COUNT; ++i) {
132365c00d0STakashi Sakamoto 			if (amdtp_rate_table[i] == rate)
133365c00d0STakashi Sakamoto 				break;
134365c00d0STakashi Sakamoto 		}
135365c00d0STakashi Sakamoto 		if (i >= CIP_SFC_COUNT)
136365c00d0STakashi Sakamoto 			return -EINVAL;
137365c00d0STakashi Sakamoto 
138365c00d0STakashi Sakamoto 		err = snd_ff_stream_get_multiplier_mode(i, &mode);
13975d6d898STakashi Sakamoto 		if (err < 0)
140365c00d0STakashi Sakamoto 			return err;
141365c00d0STakashi Sakamoto 
142365c00d0STakashi Sakamoto 		err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
143365c00d0STakashi Sakamoto 					ff->spec->pcm_capture_channels[mode]);
144365c00d0STakashi Sakamoto 		if (err < 0)
145365c00d0STakashi Sakamoto 			return err;
146365c00d0STakashi Sakamoto 
147365c00d0STakashi Sakamoto 		err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
148365c00d0STakashi Sakamoto 					ff->spec->pcm_playback_channels[mode]);
149365c00d0STakashi Sakamoto 		if (err < 0)
150365c00d0STakashi Sakamoto 			return err;
15175d6d898STakashi Sakamoto 
15247b87c8eSTakashi Sakamoto 		err = ff->spec->protocol->allocate_resources(ff, rate);
15347b87c8eSTakashi Sakamoto 		if (err < 0)
15455162d2bSTakashi Sakamoto 			return err;
1559d9ff58cSTakashi Sakamoto 
1569d9ff58cSTakashi Sakamoto 		err = amdtp_domain_set_events_per_period(&ff->domain,
1574de3eb06STakashi Sakamoto 					frames_per_period, frames_per_buffer);
1589d9ff58cSTakashi Sakamoto 		if (err < 0) {
1599d9ff58cSTakashi Sakamoto 			fw_iso_resources_free(&ff->tx_resources);
1609d9ff58cSTakashi Sakamoto 			fw_iso_resources_free(&ff->rx_resources);
1619d9ff58cSTakashi Sakamoto 			return err;
1629d9ff58cSTakashi Sakamoto 		}
16355162d2bSTakashi Sakamoto 	}
16447b87c8eSTakashi Sakamoto 
16555162d2bSTakashi Sakamoto 	return 0;
16655162d2bSTakashi Sakamoto }
16755162d2bSTakashi Sakamoto 
snd_ff_stream_start_duplex(struct snd_ff * ff,unsigned int rate)16855162d2bSTakashi Sakamoto int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
16955162d2bSTakashi Sakamoto {
17055162d2bSTakashi Sakamoto 	int err;
17155162d2bSTakashi Sakamoto 
17255162d2bSTakashi Sakamoto 	if (ff->substreams_counter == 0)
17355162d2bSTakashi Sakamoto 		return 0;
17455162d2bSTakashi Sakamoto 
17555162d2bSTakashi Sakamoto 	if (amdtp_streaming_error(&ff->tx_stream) ||
176c9a9ce89STakashi Sakamoto 	    amdtp_streaming_error(&ff->rx_stream)) {
177c9a9ce89STakashi Sakamoto 		amdtp_domain_stop(&ff->domain);
17855162d2bSTakashi Sakamoto 		finish_session(ff);
179c9a9ce89STakashi Sakamoto 	}
18055162d2bSTakashi Sakamoto 
18155162d2bSTakashi Sakamoto 	/*
18255162d2bSTakashi Sakamoto 	 * Regardless of current source of clock signal, drivers transfer some
18355162d2bSTakashi Sakamoto 	 * packets. Then, the device transfers packets.
18455162d2bSTakashi Sakamoto 	 */
18555162d2bSTakashi Sakamoto 	if (!amdtp_stream_running(&ff->rx_stream)) {
186c9a9ce89STakashi Sakamoto 		int spd = fw_parent_device(ff->unit)->max_speed;
187c9a9ce89STakashi Sakamoto 
18875d6d898STakashi Sakamoto 		err = ff->spec->protocol->begin_session(ff, rate);
18975d6d898STakashi Sakamoto 		if (err < 0)
19075d6d898STakashi Sakamoto 			goto error;
19175d6d898STakashi Sakamoto 
192c9a9ce89STakashi Sakamoto 		err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
193c9a9ce89STakashi Sakamoto 					      ff->rx_resources.channel, spd);
194c9a9ce89STakashi Sakamoto 		if (err < 0)
195c9a9ce89STakashi Sakamoto 			goto error;
196c9a9ce89STakashi Sakamoto 
197c9a9ce89STakashi Sakamoto 		err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
198c9a9ce89STakashi Sakamoto 					      ff->tx_resources.channel, spd);
199c9a9ce89STakashi Sakamoto 		if (err < 0)
200c9a9ce89STakashi Sakamoto 			goto error;
201c9a9ce89STakashi Sakamoto 
202*dfacca39STakashi Sakamoto 		// NOTE: The device doesn't transfer packets unless receiving any packet. The
203*dfacca39STakashi Sakamoto 		// sequence of tx packets includes cycle skip corresponding to empty packet or
204*dfacca39STakashi Sakamoto 		// NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
205*dfacca39STakashi Sakamoto 		// packet is important for media clock recovery.
206*dfacca39STakashi Sakamoto 		err = amdtp_domain_start(&ff->domain, 0, true, true);
20775d6d898STakashi Sakamoto 		if (err < 0)
20875d6d898STakashi Sakamoto 			goto error;
20975d6d898STakashi Sakamoto 
210bdaedca7STakashi Sakamoto 		if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
21175d6d898STakashi Sakamoto 			err = -ETIMEDOUT;
21275d6d898STakashi Sakamoto 			goto error;
21375d6d898STakashi Sakamoto 		}
21475d6d898STakashi Sakamoto 
215ae3053c2STakashi Sakamoto 		err = ff->spec->protocol->switch_fetching_mode(ff, true);
21675d6d898STakashi Sakamoto 		if (err < 0)
21775d6d898STakashi Sakamoto 			goto error;
21875d6d898STakashi Sakamoto 	}
21975d6d898STakashi Sakamoto 
22075d6d898STakashi Sakamoto 	return 0;
22175d6d898STakashi Sakamoto error:
222c9a9ce89STakashi Sakamoto 	amdtp_domain_stop(&ff->domain);
22375d6d898STakashi Sakamoto 	finish_session(ff);
22475d6d898STakashi Sakamoto 
22575d6d898STakashi Sakamoto 	return err;
22675d6d898STakashi Sakamoto }
22775d6d898STakashi Sakamoto 
snd_ff_stream_stop_duplex(struct snd_ff * ff)22875d6d898STakashi Sakamoto void snd_ff_stream_stop_duplex(struct snd_ff *ff)
22975d6d898STakashi Sakamoto {
230af26baccSTakashi Sakamoto 	if (ff->substreams_counter == 0) {
231c9a9ce89STakashi Sakamoto 		amdtp_domain_stop(&ff->domain);
23275d6d898STakashi Sakamoto 		finish_session(ff);
233af26baccSTakashi Sakamoto 
234af26baccSTakashi Sakamoto 		fw_iso_resources_free(&ff->tx_resources);
235af26baccSTakashi Sakamoto 		fw_iso_resources_free(&ff->rx_resources);
236af26baccSTakashi Sakamoto 	}
23775d6d898STakashi Sakamoto }
23875d6d898STakashi Sakamoto 
snd_ff_stream_update_duplex(struct snd_ff * ff)23975d6d898STakashi Sakamoto void snd_ff_stream_update_duplex(struct snd_ff *ff)
24075d6d898STakashi Sakamoto {
241c9a9ce89STakashi Sakamoto 	amdtp_domain_stop(&ff->domain);
242c9a9ce89STakashi Sakamoto 
243b88f4d7cSTakashi Sakamoto 	// The device discontinue to transfer packets.
24475d6d898STakashi Sakamoto 	amdtp_stream_pcm_abort(&ff->tx_stream);
24575d6d898STakashi Sakamoto 	amdtp_stream_pcm_abort(&ff->rx_stream);
24675d6d898STakashi Sakamoto }
247f656edd5STakashi Sakamoto 
snd_ff_stream_lock_changed(struct snd_ff * ff)248f656edd5STakashi Sakamoto void snd_ff_stream_lock_changed(struct snd_ff *ff)
249f656edd5STakashi Sakamoto {
250f656edd5STakashi Sakamoto 	ff->dev_lock_changed = true;
251f656edd5STakashi Sakamoto 	wake_up(&ff->hwdep_wait);
252f656edd5STakashi Sakamoto }
253f656edd5STakashi Sakamoto 
snd_ff_stream_lock_try(struct snd_ff * ff)254f656edd5STakashi Sakamoto int snd_ff_stream_lock_try(struct snd_ff *ff)
255f656edd5STakashi Sakamoto {
256f656edd5STakashi Sakamoto 	int err;
257f656edd5STakashi Sakamoto 
258f656edd5STakashi Sakamoto 	spin_lock_irq(&ff->lock);
259f656edd5STakashi Sakamoto 
260f656edd5STakashi Sakamoto 	/* user land lock this */
261f656edd5STakashi Sakamoto 	if (ff->dev_lock_count < 0) {
262f656edd5STakashi Sakamoto 		err = -EBUSY;
263f656edd5STakashi Sakamoto 		goto end;
264f656edd5STakashi Sakamoto 	}
265f656edd5STakashi Sakamoto 
266f656edd5STakashi Sakamoto 	/* this is the first time */
267f656edd5STakashi Sakamoto 	if (ff->dev_lock_count++ == 0)
268f656edd5STakashi Sakamoto 		snd_ff_stream_lock_changed(ff);
269f656edd5STakashi Sakamoto 	err = 0;
270f656edd5STakashi Sakamoto end:
271f656edd5STakashi Sakamoto 	spin_unlock_irq(&ff->lock);
272f656edd5STakashi Sakamoto 	return err;
273f656edd5STakashi Sakamoto }
274f656edd5STakashi Sakamoto 
snd_ff_stream_lock_release(struct snd_ff * ff)275f656edd5STakashi Sakamoto void snd_ff_stream_lock_release(struct snd_ff *ff)
276f656edd5STakashi Sakamoto {
277f656edd5STakashi Sakamoto 	spin_lock_irq(&ff->lock);
278f656edd5STakashi Sakamoto 
279f656edd5STakashi Sakamoto 	if (WARN_ON(ff->dev_lock_count <= 0))
280f656edd5STakashi Sakamoto 		goto end;
281f656edd5STakashi Sakamoto 	if (--ff->dev_lock_count == 0)
282f656edd5STakashi Sakamoto 		snd_ff_stream_lock_changed(ff);
283f656edd5STakashi Sakamoto end:
284f656edd5STakashi Sakamoto 	spin_unlock_irq(&ff->lock);
285f656edd5STakashi Sakamoto }
286