2 * ff-stream.c - a part of driver for RME Fireface series
4 * Copyright (c) 2015-2017 Takashi Sakamoto
6 * Licensed under the terms of the GNU General Public License, version 2.
11 #define CALLBACK_TIMEOUT_MS 200
13 int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc
,
14 enum snd_ff_stream_mode
*mode
)
16 static const enum snd_ff_stream_mode modes
[] = {
17 [CIP_SFC_32000
] = SND_FF_STREAM_MODE_LOW
,
18 [CIP_SFC_44100
] = SND_FF_STREAM_MODE_LOW
,
19 [CIP_SFC_48000
] = SND_FF_STREAM_MODE_LOW
,
20 [CIP_SFC_88200
] = SND_FF_STREAM_MODE_MID
,
21 [CIP_SFC_96000
] = SND_FF_STREAM_MODE_MID
,
22 [CIP_SFC_176400
] = SND_FF_STREAM_MODE_HIGH
,
23 [CIP_SFC_192000
] = SND_FF_STREAM_MODE_HIGH
,
26 if (sfc
>= CIP_SFC_COUNT
)
34 static void release_resources(struct snd_ff
*ff
)
36 fw_iso_resources_free(&ff
->tx_resources
);
37 fw_iso_resources_free(&ff
->rx_resources
);
40 static inline void finish_session(struct snd_ff
*ff
)
42 ff
->spec
->protocol
->finish_session(ff
);
43 ff
->spec
->protocol
->switch_fetching_mode(ff
, false);
46 static int init_stream(struct snd_ff
*ff
, enum amdtp_stream_direction dir
)
49 struct fw_iso_resources
*resources
;
50 struct amdtp_stream
*stream
;
52 if (dir
== AMDTP_IN_STREAM
) {
53 resources
= &ff
->tx_resources
;
54 stream
= &ff
->tx_stream
;
56 resources
= &ff
->rx_resources
;
57 stream
= &ff
->rx_stream
;
60 err
= fw_iso_resources_init(resources
, ff
->unit
);
64 err
= amdtp_ff_init(stream
, ff
->unit
, dir
);
66 fw_iso_resources_destroy(resources
);
71 static void destroy_stream(struct snd_ff
*ff
, enum amdtp_stream_direction dir
)
73 if (dir
== AMDTP_IN_STREAM
) {
74 amdtp_stream_destroy(&ff
->tx_stream
);
75 fw_iso_resources_destroy(&ff
->tx_resources
);
77 amdtp_stream_destroy(&ff
->rx_stream
);
78 fw_iso_resources_destroy(&ff
->rx_resources
);
82 int snd_ff_stream_init_duplex(struct snd_ff
*ff
)
86 err
= init_stream(ff
, AMDTP_OUT_STREAM
);
90 err
= init_stream(ff
, AMDTP_IN_STREAM
);
92 destroy_stream(ff
, AMDTP_OUT_STREAM
);
98 * This function should be called before starting streams or after stopping
101 void snd_ff_stream_destroy_duplex(struct snd_ff
*ff
)
103 destroy_stream(ff
, AMDTP_IN_STREAM
);
104 destroy_stream(ff
, AMDTP_OUT_STREAM
);
107 int snd_ff_stream_start_duplex(struct snd_ff
*ff
, unsigned int rate
)
109 unsigned int curr_rate
;
110 enum snd_ff_clock_src src
;
113 if (ff
->substreams_counter
== 0)
116 err
= ff
->spec
->protocol
->get_clock(ff
, &curr_rate
, &src
);
119 if (curr_rate
!= rate
||
120 amdtp_streaming_error(&ff
->tx_stream
) ||
121 amdtp_streaming_error(&ff
->rx_stream
)) {
124 amdtp_stream_stop(&ff
->tx_stream
);
125 amdtp_stream_stop(&ff
->rx_stream
);
127 release_resources(ff
);
131 * Regardless of current source of clock signal, drivers transfer some
132 * packets. Then, the device transfers packets.
134 if (!amdtp_stream_running(&ff
->rx_stream
)) {
135 enum snd_ff_stream_mode mode
;
138 for (i
= 0; i
< CIP_SFC_COUNT
; ++i
) {
139 if (amdtp_rate_table
[i
] == rate
)
142 if (i
>= CIP_SFC_COUNT
)
145 err
= snd_ff_stream_get_multiplier_mode(i
, &mode
);
149 err
= amdtp_ff_set_parameters(&ff
->tx_stream
, rate
,
150 ff
->spec
->pcm_capture_channels
[mode
]);
154 err
= amdtp_ff_set_parameters(&ff
->rx_stream
, rate
,
155 ff
->spec
->pcm_playback_channels
[mode
]);
159 err
= ff
->spec
->protocol
->begin_session(ff
, rate
);
163 err
= amdtp_stream_start(&ff
->rx_stream
,
164 ff
->rx_resources
.channel
,
165 fw_parent_device(ff
->unit
)->max_speed
);
169 if (!amdtp_stream_wait_callback(&ff
->rx_stream
,
170 CALLBACK_TIMEOUT_MS
)) {
175 err
= ff
->spec
->protocol
->switch_fetching_mode(ff
, true);
180 if (!amdtp_stream_running(&ff
->tx_stream
)) {
181 err
= amdtp_stream_start(&ff
->tx_stream
,
182 ff
->tx_resources
.channel
,
183 fw_parent_device(ff
->unit
)->max_speed
);
187 if (!amdtp_stream_wait_callback(&ff
->tx_stream
,
188 CALLBACK_TIMEOUT_MS
)) {
196 amdtp_stream_stop(&ff
->tx_stream
);
197 amdtp_stream_stop(&ff
->rx_stream
);
200 release_resources(ff
);
205 void snd_ff_stream_stop_duplex(struct snd_ff
*ff
)
207 if (ff
->substreams_counter
> 0)
210 amdtp_stream_stop(&ff
->tx_stream
);
211 amdtp_stream_stop(&ff
->rx_stream
);
213 release_resources(ff
);
216 void snd_ff_stream_update_duplex(struct snd_ff
*ff
)
218 /* The device discontinue to transfer packets. */
219 amdtp_stream_pcm_abort(&ff
->tx_stream
);
220 amdtp_stream_stop(&ff
->tx_stream
);
222 amdtp_stream_pcm_abort(&ff
->rx_stream
);
223 amdtp_stream_stop(&ff
->rx_stream
);
225 fw_iso_resources_update(&ff
->tx_resources
);
226 fw_iso_resources_update(&ff
->rx_resources
);
229 void snd_ff_stream_lock_changed(struct snd_ff
*ff
)
231 ff
->dev_lock_changed
= true;
232 wake_up(&ff
->hwdep_wait
);
235 int snd_ff_stream_lock_try(struct snd_ff
*ff
)
239 spin_lock_irq(&ff
->lock
);
241 /* user land lock this */
242 if (ff
->dev_lock_count
< 0) {
247 /* this is the first time */
248 if (ff
->dev_lock_count
++ == 0)
249 snd_ff_stream_lock_changed(ff
);
252 spin_unlock_irq(&ff
->lock
);
256 void snd_ff_stream_lock_release(struct snd_ff
*ff
)
258 spin_lock_irq(&ff
->lock
);
260 if (WARN_ON(ff
->dev_lock_count
<= 0))
262 if (--ff
->dev_lock_count
== 0)
263 snd_ff_stream_lock_changed(ff
);
265 spin_unlock_irq(&ff
->lock
);