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