1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26eb6c81eSTakashi Sakamoto /*
36eb6c81eSTakashi Sakamoto * dice_stream.c - a part of driver for DICE based devices
46eb6c81eSTakashi Sakamoto *
56eb6c81eSTakashi Sakamoto * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
66eb6c81eSTakashi Sakamoto * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
76eb6c81eSTakashi Sakamoto */
86eb6c81eSTakashi Sakamoto
96eb6c81eSTakashi Sakamoto #include "dice.h"
106eb6c81eSTakashi Sakamoto
11bdaedca7STakashi Sakamoto #define READY_TIMEOUT_MS 200
1241319eb5STakashi Sakamoto #define NOTIFICATION_TIMEOUT_MS 100
13288a8d0cSTakashi Sakamoto
148cc1a8abSTakashi Sakamoto struct reg_params {
158cc1a8abSTakashi Sakamoto unsigned int count;
168cc1a8abSTakashi Sakamoto unsigned int size;
178cc1a8abSTakashi Sakamoto };
188cc1a8abSTakashi Sakamoto
196eb6c81eSTakashi Sakamoto const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
206eb6c81eSTakashi Sakamoto /* mode 0 */
216eb6c81eSTakashi Sakamoto [0] = 32000,
226eb6c81eSTakashi Sakamoto [1] = 44100,
236eb6c81eSTakashi Sakamoto [2] = 48000,
246eb6c81eSTakashi Sakamoto /* mode 1 */
256eb6c81eSTakashi Sakamoto [3] = 88200,
266eb6c81eSTakashi Sakamoto [4] = 96000,
276eb6c81eSTakashi Sakamoto /* mode 2 */
286eb6c81eSTakashi Sakamoto [5] = 176400,
296eb6c81eSTakashi Sakamoto [6] = 192000,
306eb6c81eSTakashi Sakamoto };
316eb6c81eSTakashi Sakamoto
snd_dice_stream_get_rate_mode(struct snd_dice * dice,unsigned int rate,enum snd_dice_rate_mode * mode)32b60152f7STakashi Sakamoto int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
33b60152f7STakashi Sakamoto enum snd_dice_rate_mode *mode)
34b60152f7STakashi Sakamoto {
35b60152f7STakashi Sakamoto /* Corresponding to each entry in snd_dice_rates. */
36b60152f7STakashi Sakamoto static const enum snd_dice_rate_mode modes[] = {
37b60152f7STakashi Sakamoto [0] = SND_DICE_RATE_MODE_LOW,
38b60152f7STakashi Sakamoto [1] = SND_DICE_RATE_MODE_LOW,
39b60152f7STakashi Sakamoto [2] = SND_DICE_RATE_MODE_LOW,
40b60152f7STakashi Sakamoto [3] = SND_DICE_RATE_MODE_MIDDLE,
41b60152f7STakashi Sakamoto [4] = SND_DICE_RATE_MODE_MIDDLE,
42b60152f7STakashi Sakamoto [5] = SND_DICE_RATE_MODE_HIGH,
43b60152f7STakashi Sakamoto [6] = SND_DICE_RATE_MODE_HIGH,
44b60152f7STakashi Sakamoto };
45b60152f7STakashi Sakamoto int i;
46b60152f7STakashi Sakamoto
47b60152f7STakashi Sakamoto for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
48b60152f7STakashi Sakamoto if (!(dice->clock_caps & BIT(i)))
49b60152f7STakashi Sakamoto continue;
50b60152f7STakashi Sakamoto if (snd_dice_rates[i] != rate)
51b60152f7STakashi Sakamoto continue;
52b60152f7STakashi Sakamoto
53b60152f7STakashi Sakamoto *mode = modes[i];
54b60152f7STakashi Sakamoto return 0;
55b60152f7STakashi Sakamoto }
56b60152f7STakashi Sakamoto
57b60152f7STakashi Sakamoto return -EINVAL;
58b60152f7STakashi Sakamoto }
59b60152f7STakashi Sakamoto
select_clock(struct snd_dice * dice,unsigned int rate)6041319eb5STakashi Sakamoto static int select_clock(struct snd_dice *dice, unsigned int rate)
61dfabc0eeSTakashi Sakamoto {
62*9b84f0f7STakashi Sakamoto __be32 reg, new;
63afa617f2STakashi Sakamoto u32 data;
64afa617f2STakashi Sakamoto int i;
65dfabc0eeSTakashi Sakamoto int err;
66dfabc0eeSTakashi Sakamoto
67dfabc0eeSTakashi Sakamoto err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
68dfabc0eeSTakashi Sakamoto ®, sizeof(reg));
69dfabc0eeSTakashi Sakamoto if (err < 0)
70dfabc0eeSTakashi Sakamoto return err;
71dfabc0eeSTakashi Sakamoto
72afa617f2STakashi Sakamoto data = be32_to_cpu(reg);
73afa617f2STakashi Sakamoto
74afa617f2STakashi Sakamoto data &= ~CLOCK_RATE_MASK;
75afa617f2STakashi Sakamoto for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
76afa617f2STakashi Sakamoto if (snd_dice_rates[i] == rate)
77afa617f2STakashi Sakamoto break;
78afa617f2STakashi Sakamoto }
79afa617f2STakashi Sakamoto if (i == ARRAY_SIZE(snd_dice_rates))
80afa617f2STakashi Sakamoto return -EINVAL;
81afa617f2STakashi Sakamoto data |= i << CLOCK_RATE_SHIFT;
82afa617f2STakashi Sakamoto
83dfabc0eeSTakashi Sakamoto if (completion_done(&dice->clock_accepted))
84dfabc0eeSTakashi Sakamoto reinit_completion(&dice->clock_accepted);
85dfabc0eeSTakashi Sakamoto
86*9b84f0f7STakashi Sakamoto new = cpu_to_be32(data);
87dfabc0eeSTakashi Sakamoto err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
88*9b84f0f7STakashi Sakamoto &new, sizeof(new));
89dfabc0eeSTakashi Sakamoto if (err < 0)
90dfabc0eeSTakashi Sakamoto return err;
91dfabc0eeSTakashi Sakamoto
92dfabc0eeSTakashi Sakamoto if (wait_for_completion_timeout(&dice->clock_accepted,
93*9b84f0f7STakashi Sakamoto msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
94*9b84f0f7STakashi Sakamoto if (reg != new)
95dfabc0eeSTakashi Sakamoto return -ETIMEDOUT;
96*9b84f0f7STakashi Sakamoto }
97dfabc0eeSTakashi Sakamoto
98dfabc0eeSTakashi Sakamoto return 0;
99dfabc0eeSTakashi Sakamoto }
100dfabc0eeSTakashi Sakamoto
get_register_params(struct snd_dice * dice,struct reg_params * tx_params,struct reg_params * rx_params)1018cc1a8abSTakashi Sakamoto static int get_register_params(struct snd_dice *dice,
1028cc1a8abSTakashi Sakamoto struct reg_params *tx_params,
1038cc1a8abSTakashi Sakamoto struct reg_params *rx_params)
1046eb6c81eSTakashi Sakamoto {
105436b5abeSTakashi Sakamoto __be32 reg[2];
106436b5abeSTakashi Sakamoto int err;
1076eb6c81eSTakashi Sakamoto
108436b5abeSTakashi Sakamoto err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
109436b5abeSTakashi Sakamoto if (err < 0)
110436b5abeSTakashi Sakamoto return err;
1118cc1a8abSTakashi Sakamoto tx_params->count =
1128cc1a8abSTakashi Sakamoto min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
1138cc1a8abSTakashi Sakamoto tx_params->size = be32_to_cpu(reg[1]) * 4;
114288a8d0cSTakashi Sakamoto
115436b5abeSTakashi Sakamoto err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
116436b5abeSTakashi Sakamoto if (err < 0)
117436b5abeSTakashi Sakamoto return err;
1188cc1a8abSTakashi Sakamoto rx_params->count =
1198cc1a8abSTakashi Sakamoto min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
1208cc1a8abSTakashi Sakamoto rx_params->size = be32_to_cpu(reg[1]) * 4;
121436b5abeSTakashi Sakamoto
122436b5abeSTakashi Sakamoto return 0;
123436b5abeSTakashi Sakamoto }
124436b5abeSTakashi Sakamoto
release_resources(struct snd_dice * dice)125436b5abeSTakashi Sakamoto static void release_resources(struct snd_dice *dice)
126436b5abeSTakashi Sakamoto {
1273cd2c2d7STakashi Sakamoto int i;
128436b5abeSTakashi Sakamoto
1293cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) {
130436b5abeSTakashi Sakamoto fw_iso_resources_free(&dice->tx_resources[i]);
131436b5abeSTakashi Sakamoto fw_iso_resources_free(&dice->rx_resources[i]);
132436b5abeSTakashi Sakamoto }
133436b5abeSTakashi Sakamoto }
134436b5abeSTakashi Sakamoto
stop_streams(struct snd_dice * dice,enum amdtp_stream_direction dir,struct reg_params * params)135436b5abeSTakashi Sakamoto static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
1368cc1a8abSTakashi Sakamoto struct reg_params *params)
137436b5abeSTakashi Sakamoto {
138436b5abeSTakashi Sakamoto __be32 reg;
139436b5abeSTakashi Sakamoto unsigned int i;
140436b5abeSTakashi Sakamoto
1418cc1a8abSTakashi Sakamoto for (i = 0; i < params->count; i++) {
142436b5abeSTakashi Sakamoto reg = cpu_to_be32((u32)-1);
143436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
144436b5abeSTakashi Sakamoto snd_dice_transaction_write_tx(dice,
1458cc1a8abSTakashi Sakamoto params->size * i + TX_ISOCHRONOUS,
146436b5abeSTakashi Sakamoto ®, sizeof(reg));
147436b5abeSTakashi Sakamoto } else {
148436b5abeSTakashi Sakamoto snd_dice_transaction_write_rx(dice,
1498cc1a8abSTakashi Sakamoto params->size * i + RX_ISOCHRONOUS,
150436b5abeSTakashi Sakamoto ®, sizeof(reg));
151436b5abeSTakashi Sakamoto }
152436b5abeSTakashi Sakamoto }
153288a8d0cSTakashi Sakamoto }
154288a8d0cSTakashi Sakamoto
keep_resources(struct snd_dice * dice,struct amdtp_stream * stream,struct fw_iso_resources * resources,unsigned int rate,unsigned int pcm_chs,unsigned int midi_ports)155c738aed1STakashi Sakamoto static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
156c738aed1STakashi Sakamoto struct fw_iso_resources *resources, unsigned int rate,
157c738aed1STakashi Sakamoto unsigned int pcm_chs, unsigned int midi_ports)
158288a8d0cSTakashi Sakamoto {
15927ec83b5STakashi Sakamoto bool double_pcm_frames;
160436b5abeSTakashi Sakamoto unsigned int i;
161288a8d0cSTakashi Sakamoto int err;
162288a8d0cSTakashi Sakamoto
163c738aed1STakashi Sakamoto // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
164c738aed1STakashi Sakamoto // one data block of AMDTP packet. Thus sampling transfer frequency is
165c738aed1STakashi Sakamoto // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
166c738aed1STakashi Sakamoto // transferred on AMDTP packets at 96 kHz. Two successive samples of a
167c738aed1STakashi Sakamoto // channel are stored consecutively in the packet. This quirk is called
168c738aed1STakashi Sakamoto // as 'Dual Wire'.
169c738aed1STakashi Sakamoto // For this quirk, blocking mode is required and PCM buffer size should
170c738aed1STakashi Sakamoto // be aligned to SYT_INTERVAL.
1719f079c1bSTakashi Sakamoto double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames);
17227ec83b5STakashi Sakamoto if (double_pcm_frames) {
173288a8d0cSTakashi Sakamoto rate /= 2;
174288a8d0cSTakashi Sakamoto pcm_chs *= 2;
175288a8d0cSTakashi Sakamoto }
176288a8d0cSTakashi Sakamoto
17751c29fd2STakashi Sakamoto err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
17851c29fd2STakashi Sakamoto double_pcm_frames);
179547e631cSTakashi Sakamoto if (err < 0)
180436b5abeSTakashi Sakamoto return err;
181547e631cSTakashi Sakamoto
18227ec83b5STakashi Sakamoto if (double_pcm_frames) {
183288a8d0cSTakashi Sakamoto pcm_chs /= 2;
184288a8d0cSTakashi Sakamoto
185288a8d0cSTakashi Sakamoto for (i = 0; i < pcm_chs; i++) {
186f65be911STakashi Sakamoto amdtp_am824_set_pcm_position(stream, i, i * 2);
187f65be911STakashi Sakamoto amdtp_am824_set_pcm_position(stream, i + pcm_chs,
188f65be911STakashi Sakamoto i * 2 + 1);
189288a8d0cSTakashi Sakamoto }
190288a8d0cSTakashi Sakamoto }
191288a8d0cSTakashi Sakamoto
192436b5abeSTakashi Sakamoto return fw_iso_resources_allocate(resources,
193436b5abeSTakashi Sakamoto amdtp_stream_get_max_payload(stream),
194436b5abeSTakashi Sakamoto fw_parent_device(dice->unit)->max_speed);
195288a8d0cSTakashi Sakamoto }
196288a8d0cSTakashi Sakamoto
keep_dual_resources(struct snd_dice * dice,unsigned int rate,enum amdtp_stream_direction dir,struct reg_params * params)197c738aed1STakashi Sakamoto static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
198c738aed1STakashi Sakamoto enum amdtp_stream_direction dir,
199c738aed1STakashi Sakamoto struct reg_params *params)
200c738aed1STakashi Sakamoto {
201c738aed1STakashi Sakamoto enum snd_dice_rate_mode mode;
202c738aed1STakashi Sakamoto int i;
203c738aed1STakashi Sakamoto int err;
204c738aed1STakashi Sakamoto
205c738aed1STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
206c738aed1STakashi Sakamoto if (err < 0)
207c738aed1STakashi Sakamoto return err;
208c738aed1STakashi Sakamoto
209c738aed1STakashi Sakamoto for (i = 0; i < params->count; ++i) {
210c738aed1STakashi Sakamoto __be32 reg[2];
211c738aed1STakashi Sakamoto struct amdtp_stream *stream;
212c738aed1STakashi Sakamoto struct fw_iso_resources *resources;
213c738aed1STakashi Sakamoto unsigned int pcm_cache;
214c738aed1STakashi Sakamoto unsigned int pcm_chs;
215c738aed1STakashi Sakamoto unsigned int midi_ports;
216c738aed1STakashi Sakamoto
217c738aed1STakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
218c738aed1STakashi Sakamoto stream = &dice->tx_stream[i];
219c738aed1STakashi Sakamoto resources = &dice->tx_resources[i];
220c738aed1STakashi Sakamoto
221c738aed1STakashi Sakamoto pcm_cache = dice->tx_pcm_chs[i][mode];
222c738aed1STakashi Sakamoto err = snd_dice_transaction_read_tx(dice,
223c738aed1STakashi Sakamoto params->size * i + TX_NUMBER_AUDIO,
224c738aed1STakashi Sakamoto reg, sizeof(reg));
225c738aed1STakashi Sakamoto } else {
226c738aed1STakashi Sakamoto stream = &dice->rx_stream[i];
227c738aed1STakashi Sakamoto resources = &dice->rx_resources[i];
228c738aed1STakashi Sakamoto
229c738aed1STakashi Sakamoto pcm_cache = dice->rx_pcm_chs[i][mode];
230c738aed1STakashi Sakamoto err = snd_dice_transaction_read_rx(dice,
231c738aed1STakashi Sakamoto params->size * i + RX_NUMBER_AUDIO,
232c738aed1STakashi Sakamoto reg, sizeof(reg));
233c738aed1STakashi Sakamoto }
234c738aed1STakashi Sakamoto if (err < 0)
235c738aed1STakashi Sakamoto return err;
236c738aed1STakashi Sakamoto pcm_chs = be32_to_cpu(reg[0]);
237c738aed1STakashi Sakamoto midi_ports = be32_to_cpu(reg[1]);
238c738aed1STakashi Sakamoto
239c738aed1STakashi Sakamoto // These are important for developer of this driver.
240a9f47fcbSTakashi Sakamoto if (pcm_chs != pcm_cache) {
241c738aed1STakashi Sakamoto dev_info(&dice->unit->device,
242a9f47fcbSTakashi Sakamoto "cache mismatch: pcm: %u:%u, midi: %u\n",
243a9f47fcbSTakashi Sakamoto pcm_chs, pcm_cache, midi_ports);
244c738aed1STakashi Sakamoto return -EPROTO;
245c738aed1STakashi Sakamoto }
246c738aed1STakashi Sakamoto
247c738aed1STakashi Sakamoto err = keep_resources(dice, stream, resources, rate, pcm_chs,
248c738aed1STakashi Sakamoto midi_ports);
249c738aed1STakashi Sakamoto if (err < 0)
250c738aed1STakashi Sakamoto return err;
251c738aed1STakashi Sakamoto }
252c738aed1STakashi Sakamoto
253c738aed1STakashi Sakamoto return 0;
254c738aed1STakashi Sakamoto }
255c738aed1STakashi Sakamoto
finish_session(struct snd_dice * dice,struct reg_params * tx_params,struct reg_params * rx_params)256b3480638STakashi Sakamoto static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
257b3480638STakashi Sakamoto struct reg_params *rx_params)
258b3480638STakashi Sakamoto {
259b3480638STakashi Sakamoto stop_streams(dice, AMDTP_IN_STREAM, tx_params);
260b3480638STakashi Sakamoto stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
261b3480638STakashi Sakamoto
262b3480638STakashi Sakamoto snd_dice_transaction_clear_enable(dice);
263b3480638STakashi Sakamoto }
264b3480638STakashi Sakamoto
snd_dice_stream_reserve_duplex(struct snd_dice * dice,unsigned int rate,unsigned int events_per_period,unsigned int events_per_buffer)26594c8101aSTakashi Sakamoto int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
266ecb40fd2STakashi Sakamoto unsigned int events_per_period,
267ecb40fd2STakashi Sakamoto unsigned int events_per_buffer)
2683cd2c2d7STakashi Sakamoto {
2693cd2c2d7STakashi Sakamoto unsigned int curr_rate;
2703cd2c2d7STakashi Sakamoto int err;
2713cd2c2d7STakashi Sakamoto
2723cd2c2d7STakashi Sakamoto // Check sampling transmission frequency.
2733cd2c2d7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &curr_rate);
2743cd2c2d7STakashi Sakamoto if (err < 0)
2753cd2c2d7STakashi Sakamoto return err;
2763cd2c2d7STakashi Sakamoto if (rate == 0)
2773cd2c2d7STakashi Sakamoto rate = curr_rate;
2783cd2c2d7STakashi Sakamoto
2793cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0 || curr_rate != rate) {
2803cd2c2d7STakashi Sakamoto struct reg_params tx_params, rx_params;
2813cd2c2d7STakashi Sakamoto
282e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain);
283e9f21129STakashi Sakamoto
2843cd2c2d7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params);
2853cd2c2d7STakashi Sakamoto if (err < 0)
2863cd2c2d7STakashi Sakamoto return err;
2873cd2c2d7STakashi Sakamoto finish_session(dice, &tx_params, &rx_params);
2883cd2c2d7STakashi Sakamoto
2893cd2c2d7STakashi Sakamoto release_resources(dice);
2903cd2c2d7STakashi Sakamoto
2913cd2c2d7STakashi Sakamoto // Just after owning the unit (GLOBAL_OWNER), the unit can
2923cd2c2d7STakashi Sakamoto // return invalid stream formats. Selecting clock parameters
2933cd2c2d7STakashi Sakamoto // have an effect for the unit to refine it.
29441319eb5STakashi Sakamoto err = select_clock(dice, rate);
2953cd2c2d7STakashi Sakamoto if (err < 0)
2963cd2c2d7STakashi Sakamoto return err;
2973cd2c2d7STakashi Sakamoto
2983cd2c2d7STakashi Sakamoto // After changing sampling transfer frequency, the value of
2993cd2c2d7STakashi Sakamoto // register can be changed.
3003cd2c2d7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params);
3013cd2c2d7STakashi Sakamoto if (err < 0)
3023cd2c2d7STakashi Sakamoto return err;
3033cd2c2d7STakashi Sakamoto
3043cd2c2d7STakashi Sakamoto err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
3053cd2c2d7STakashi Sakamoto &tx_params);
3063cd2c2d7STakashi Sakamoto if (err < 0)
3073cd2c2d7STakashi Sakamoto goto error;
3083cd2c2d7STakashi Sakamoto
3093cd2c2d7STakashi Sakamoto err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
3103cd2c2d7STakashi Sakamoto &rx_params);
3113cd2c2d7STakashi Sakamoto if (err < 0)
3123cd2c2d7STakashi Sakamoto goto error;
31394c8101aSTakashi Sakamoto
31494c8101aSTakashi Sakamoto err = amdtp_domain_set_events_per_period(&dice->domain,
315ecb40fd2STakashi Sakamoto events_per_period, events_per_buffer);
31694c8101aSTakashi Sakamoto if (err < 0)
31794c8101aSTakashi Sakamoto goto error;
3183cd2c2d7STakashi Sakamoto }
3193cd2c2d7STakashi Sakamoto
3203cd2c2d7STakashi Sakamoto return 0;
3213cd2c2d7STakashi Sakamoto error:
3223cd2c2d7STakashi Sakamoto release_resources(dice);
3233cd2c2d7STakashi Sakamoto return err;
3243cd2c2d7STakashi Sakamoto }
3253cd2c2d7STakashi Sakamoto
start_streams(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int rate,struct reg_params * params)326436b5abeSTakashi Sakamoto static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
3278cc1a8abSTakashi Sakamoto unsigned int rate, struct reg_params *params)
328436b5abeSTakashi Sakamoto {
329c738aed1STakashi Sakamoto unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
330c738aed1STakashi Sakamoto int i;
331c738aed1STakashi Sakamoto int err;
332436b5abeSTakashi Sakamoto
3338cc1a8abSTakashi Sakamoto for (i = 0; i < params->count; i++) {
334c738aed1STakashi Sakamoto struct amdtp_stream *stream;
335c738aed1STakashi Sakamoto struct fw_iso_resources *resources;
336c738aed1STakashi Sakamoto __be32 reg;
337afa617f2STakashi Sakamoto
338436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
339c738aed1STakashi Sakamoto stream = dice->tx_stream + i;
340c738aed1STakashi Sakamoto resources = dice->tx_resources + i;
341436b5abeSTakashi Sakamoto } else {
342c738aed1STakashi Sakamoto stream = dice->rx_stream + i;
343c738aed1STakashi Sakamoto resources = dice->rx_resources + i;
344afa617f2STakashi Sakamoto }
345afa617f2STakashi Sakamoto
346c738aed1STakashi Sakamoto reg = cpu_to_be32(resources->channel);
347436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
348436b5abeSTakashi Sakamoto err = snd_dice_transaction_write_tx(dice,
3498cc1a8abSTakashi Sakamoto params->size * i + TX_ISOCHRONOUS,
350c738aed1STakashi Sakamoto ®, sizeof(reg));
351436b5abeSTakashi Sakamoto } else {
352436b5abeSTakashi Sakamoto err = snd_dice_transaction_write_rx(dice,
3538cc1a8abSTakashi Sakamoto params->size * i + RX_ISOCHRONOUS,
354c738aed1STakashi Sakamoto ®, sizeof(reg));
355436b5abeSTakashi Sakamoto }
356436b5abeSTakashi Sakamoto if (err < 0)
357436b5abeSTakashi Sakamoto return err;
358436b5abeSTakashi Sakamoto
359b0e159feSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
360c738aed1STakashi Sakamoto reg = cpu_to_be32(max_speed);
361b0e159feSTakashi Sakamoto err = snd_dice_transaction_write_tx(dice,
362b0e159feSTakashi Sakamoto params->size * i + TX_SPEED,
363c738aed1STakashi Sakamoto ®, sizeof(reg));
364b0e159feSTakashi Sakamoto if (err < 0)
365b0e159feSTakashi Sakamoto return err;
366b0e159feSTakashi Sakamoto }
367b0e159feSTakashi Sakamoto
368e9f21129STakashi Sakamoto err = amdtp_domain_add_stream(&dice->domain, stream,
369e9f21129STakashi Sakamoto resources->channel, max_speed);
370288a8d0cSTakashi Sakamoto if (err < 0)
371288a8d0cSTakashi Sakamoto return err;
372288a8d0cSTakashi Sakamoto }
373288a8d0cSTakashi Sakamoto
374c72d3a0aSTakashi Sakamoto return 0;
375436b5abeSTakashi Sakamoto }
376436b5abeSTakashi Sakamoto
3773cd2c2d7STakashi Sakamoto /*
3783cd2c2d7STakashi Sakamoto * MEMO: After this function, there're two states of streams:
3793cd2c2d7STakashi Sakamoto * - None streams are running.
3803cd2c2d7STakashi Sakamoto * - All streams are running.
3813cd2c2d7STakashi Sakamoto */
snd_dice_stream_start_duplex(struct snd_dice * dice)3823cd2c2d7STakashi Sakamoto int snd_dice_stream_start_duplex(struct snd_dice *dice)
38320b94544STakashi Sakamoto {
384d5553026STakashi Sakamoto unsigned int generation = dice->rx_resources[0].generation;
38520b94544STakashi Sakamoto struct reg_params tx_params, rx_params;
3863cd2c2d7STakashi Sakamoto unsigned int i;
3873cd2c2d7STakashi Sakamoto unsigned int rate;
3883cd2c2d7STakashi Sakamoto enum snd_dice_rate_mode mode;
38920b94544STakashi Sakamoto int err;
39020b94544STakashi Sakamoto
3913cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0)
3923cd2c2d7STakashi Sakamoto return -EIO;
3933cd2c2d7STakashi Sakamoto
39420b94544STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params);
39520b94544STakashi Sakamoto if (err < 0)
39620b94544STakashi Sakamoto return err;
39720b94544STakashi Sakamoto
3983cd2c2d7STakashi Sakamoto // Check error of packet streaming.
3993cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) {
4003cd2c2d7STakashi Sakamoto if (amdtp_streaming_error(&dice->tx_stream[i]) ||
4013cd2c2d7STakashi Sakamoto amdtp_streaming_error(&dice->rx_stream[i])) {
402e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain);
403b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params);
4043cd2c2d7STakashi Sakamoto break;
4053cd2c2d7STakashi Sakamoto }
40620b94544STakashi Sakamoto }
40720b94544STakashi Sakamoto
408d5553026STakashi Sakamoto if (generation != fw_parent_device(dice->unit)->card->generation) {
409d5553026STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) {
410d5553026STakashi Sakamoto if (i < tx_params.count)
411d5553026STakashi Sakamoto fw_iso_resources_update(dice->tx_resources + i);
412d5553026STakashi Sakamoto if (i < rx_params.count)
413d5553026STakashi Sakamoto fw_iso_resources_update(dice->rx_resources + i);
414d5553026STakashi Sakamoto }
415d5553026STakashi Sakamoto }
416d5553026STakashi Sakamoto
4173cd2c2d7STakashi Sakamoto // Check required streams are running or not.
4183cd2c2d7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &rate);
419afa617f2STakashi Sakamoto if (err < 0)
420afa617f2STakashi Sakamoto return err;
4213cd2c2d7STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
4223cd2c2d7STakashi Sakamoto if (err < 0)
4233cd2c2d7STakashi Sakamoto return err;
4243cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) {
4253cd2c2d7STakashi Sakamoto if (dice->tx_pcm_chs[i][mode] > 0 &&
4263cd2c2d7STakashi Sakamoto !amdtp_stream_running(&dice->tx_stream[i]))
4273cd2c2d7STakashi Sakamoto break;
4283cd2c2d7STakashi Sakamoto if (dice->rx_pcm_chs[i][mode] > 0 &&
4293cd2c2d7STakashi Sakamoto !amdtp_stream_running(&dice->rx_stream[i]))
4303cd2c2d7STakashi Sakamoto break;
4313cd2c2d7STakashi Sakamoto }
4323cd2c2d7STakashi Sakamoto if (i < MAX_STREAMS) {
4333cd2c2d7STakashi Sakamoto // Start both streams.
43420b94544STakashi Sakamoto err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
43520b94544STakashi Sakamoto if (err < 0)
43620b94544STakashi Sakamoto goto error;
4373cd2c2d7STakashi Sakamoto
43820b94544STakashi Sakamoto err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
43920b94544STakashi Sakamoto if (err < 0)
44020b94544STakashi Sakamoto goto error;
44120b94544STakashi Sakamoto
44220b94544STakashi Sakamoto err = snd_dice_transaction_set_enable(dice);
44320b94544STakashi Sakamoto if (err < 0) {
4443cd2c2d7STakashi Sakamoto dev_err(&dice->unit->device,
4453cd2c2d7STakashi Sakamoto "fail to enable interface\n");
44620b94544STakashi Sakamoto goto error;
44720b94544STakashi Sakamoto }
44820b94544STakashi Sakamoto
4494121f626STakashi Sakamoto // MEMO: The device immediately starts packet transmission when enabled. Some
4504121f626STakashi Sakamoto // devices are strictly to generate any discontinuity in the sequence of tx packet
4514121f626STakashi Sakamoto // when they receives invalid sequence of presentation time in CIP header. The
4524121f626STakashi Sakamoto // sequence replay for media clock recovery can suppress the behaviour.
4534121f626STakashi Sakamoto err = amdtp_domain_start(&dice->domain, 0, true, false);
454e9f21129STakashi Sakamoto if (err < 0)
455e9f21129STakashi Sakamoto goto error;
456e9f21129STakashi Sakamoto
457bdaedca7STakashi Sakamoto if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
45820b94544STakashi Sakamoto err = -ETIMEDOUT;
45920b94544STakashi Sakamoto goto error;
46020b94544STakashi Sakamoto }
46120b94544STakashi Sakamoto }
46220b94544STakashi Sakamoto
46320b94544STakashi Sakamoto return 0;
46420b94544STakashi Sakamoto error:
465e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain);
466b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params);
46720b94544STakashi Sakamoto return err;
46820b94544STakashi Sakamoto }
46920b94544STakashi Sakamoto
470436b5abeSTakashi Sakamoto /*
471436b5abeSTakashi Sakamoto * MEMO: After this function, there're two states of streams:
472436b5abeSTakashi Sakamoto * - None streams are running.
473436b5abeSTakashi Sakamoto * - All streams are running.
474436b5abeSTakashi Sakamoto */
snd_dice_stream_stop_duplex(struct snd_dice * dice)4759a02843cSTakashi Sakamoto void snd_dice_stream_stop_duplex(struct snd_dice *dice)
4766eb6c81eSTakashi Sakamoto {
4778cc1a8abSTakashi Sakamoto struct reg_params tx_params, rx_params;
478436b5abeSTakashi Sakamoto
4793cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0) {
480dd7b836dSTakashi Sakamoto if (get_register_params(dice, &tx_params, &rx_params) >= 0)
481b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params);
482740680f2STakashi Sakamoto
483dd7b836dSTakashi Sakamoto amdtp_domain_stop(&dice->domain);
484740680f2STakashi Sakamoto release_resources(dice);
4853cd2c2d7STakashi Sakamoto }
486436b5abeSTakashi Sakamoto }
4879a02843cSTakashi Sakamoto
init_stream(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int index)488436b5abeSTakashi Sakamoto static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
489436b5abeSTakashi Sakamoto unsigned int index)
490436b5abeSTakashi Sakamoto {
491436b5abeSTakashi Sakamoto struct amdtp_stream *stream;
492436b5abeSTakashi Sakamoto struct fw_iso_resources *resources;
493436b5abeSTakashi Sakamoto int err;
494436b5abeSTakashi Sakamoto
495436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
496436b5abeSTakashi Sakamoto stream = &dice->tx_stream[index];
497436b5abeSTakashi Sakamoto resources = &dice->tx_resources[index];
4989a02843cSTakashi Sakamoto } else {
499436b5abeSTakashi Sakamoto stream = &dice->rx_stream[index];
500436b5abeSTakashi Sakamoto resources = &dice->rx_resources[index];
5019a02843cSTakashi Sakamoto }
5029a02843cSTakashi Sakamoto
5039a02843cSTakashi Sakamoto err = fw_iso_resources_init(resources, dice->unit);
5049a02843cSTakashi Sakamoto if (err < 0)
5059a02843cSTakashi Sakamoto goto end;
5069a02843cSTakashi Sakamoto resources->channels_mask = 0x00000000ffffffffuLL;
5079a02843cSTakashi Sakamoto
5085955815eSTakashi Sakamoto err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
5099a02843cSTakashi Sakamoto if (err < 0) {
5109a02843cSTakashi Sakamoto amdtp_stream_destroy(stream);
5119a02843cSTakashi Sakamoto fw_iso_resources_destroy(resources);
5129a02843cSTakashi Sakamoto }
5139a02843cSTakashi Sakamoto end:
5149a02843cSTakashi Sakamoto return err;
5159a02843cSTakashi Sakamoto }
5169a02843cSTakashi Sakamoto
517d23c2cc4STakashi Sakamoto /*
518d23c2cc4STakashi Sakamoto * This function should be called before starting streams or after stopping
519d23c2cc4STakashi Sakamoto * streams.
520d23c2cc4STakashi Sakamoto */
destroy_stream(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int index)521436b5abeSTakashi Sakamoto static void destroy_stream(struct snd_dice *dice,
522436b5abeSTakashi Sakamoto enum amdtp_stream_direction dir,
523436b5abeSTakashi Sakamoto unsigned int index)
5249a02843cSTakashi Sakamoto {
525436b5abeSTakashi Sakamoto struct amdtp_stream *stream;
526d23c2cc4STakashi Sakamoto struct fw_iso_resources *resources;
5279a02843cSTakashi Sakamoto
528436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) {
529436b5abeSTakashi Sakamoto stream = &dice->tx_stream[index];
530436b5abeSTakashi Sakamoto resources = &dice->tx_resources[index];
531436b5abeSTakashi Sakamoto } else {
532436b5abeSTakashi Sakamoto stream = &dice->rx_stream[index];
533436b5abeSTakashi Sakamoto resources = &dice->rx_resources[index];
534436b5abeSTakashi Sakamoto }
535d23c2cc4STakashi Sakamoto
536d23c2cc4STakashi Sakamoto amdtp_stream_destroy(stream);
537d23c2cc4STakashi Sakamoto fw_iso_resources_destroy(resources);
5389a02843cSTakashi Sakamoto }
5399a02843cSTakashi Sakamoto
snd_dice_stream_init_duplex(struct snd_dice * dice)5409a02843cSTakashi Sakamoto int snd_dice_stream_init_duplex(struct snd_dice *dice)
5416eb6c81eSTakashi Sakamoto {
542436b5abeSTakashi Sakamoto int i, err;
5436eb6c81eSTakashi Sakamoto
544436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) {
545436b5abeSTakashi Sakamoto err = init_stream(dice, AMDTP_IN_STREAM, i);
546436b5abeSTakashi Sakamoto if (err < 0) {
547436b5abeSTakashi Sakamoto for (; i >= 0; i--)
5480f925660STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i);
5496eb6c81eSTakashi Sakamoto goto end;
550436b5abeSTakashi Sakamoto }
551436b5abeSTakashi Sakamoto }
5526eb6c81eSTakashi Sakamoto
553436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) {
554436b5abeSTakashi Sakamoto err = init_stream(dice, AMDTP_OUT_STREAM, i);
555436b5abeSTakashi Sakamoto if (err < 0) {
556436b5abeSTakashi Sakamoto for (; i >= 0; i--)
557436b5abeSTakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i);
558436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++)
559436b5abeSTakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i);
5606e26d193STakashi Sakamoto goto end;
561436b5abeSTakashi Sakamoto }
562436b5abeSTakashi Sakamoto }
563e9f21129STakashi Sakamoto
564e9f21129STakashi Sakamoto err = amdtp_domain_init(&dice->domain);
565e9f21129STakashi Sakamoto if (err < 0) {
566e9f21129STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) {
567e9f21129STakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i);
568e9f21129STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i);
569e9f21129STakashi Sakamoto }
570e9f21129STakashi Sakamoto }
5716eb6c81eSTakashi Sakamoto end:
5726eb6c81eSTakashi Sakamoto return err;
5736eb6c81eSTakashi Sakamoto }
5746eb6c81eSTakashi Sakamoto
snd_dice_stream_destroy_duplex(struct snd_dice * dice)5759a02843cSTakashi Sakamoto void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
5766eb6c81eSTakashi Sakamoto {
5776b94fb14STakashi Sakamoto unsigned int i;
578436b5abeSTakashi Sakamoto
5796b94fb14STakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) {
5806b94fb14STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i);
5816b94fb14STakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i);
582436b5abeSTakashi Sakamoto }
583e9f21129STakashi Sakamoto
584e9f21129STakashi Sakamoto amdtp_domain_destroy(&dice->domain);
5856eb6c81eSTakashi Sakamoto }
5866eb6c81eSTakashi Sakamoto
snd_dice_stream_update_duplex(struct snd_dice * dice)5879a02843cSTakashi Sakamoto void snd_dice_stream_update_duplex(struct snd_dice *dice)
5886eb6c81eSTakashi Sakamoto {
5898cc1a8abSTakashi Sakamoto struct reg_params tx_params, rx_params;
590436b5abeSTakashi Sakamoto
5916eb6c81eSTakashi Sakamoto /*
5926eb6c81eSTakashi Sakamoto * On a bus reset, the DICE firmware disables streaming and then goes
5936eb6c81eSTakashi Sakamoto * off contemplating its own navel for hundreds of milliseconds before
5946eb6c81eSTakashi Sakamoto * it can react to any of our attempts to reenable streaming. This
5956eb6c81eSTakashi Sakamoto * means that we lose synchronization anyway, so we force our streams
5966eb6c81eSTakashi Sakamoto * to stop so that the application can restart them in an orderly
5976eb6c81eSTakashi Sakamoto * manner.
5986eb6c81eSTakashi Sakamoto */
5996eb6c81eSTakashi Sakamoto dice->global_enabled = false;
6006eb6c81eSTakashi Sakamoto
6018cc1a8abSTakashi Sakamoto if (get_register_params(dice, &tx_params, &rx_params) == 0) {
602e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain);
603e9f21129STakashi Sakamoto
6048cc1a8abSTakashi Sakamoto stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
6058cc1a8abSTakashi Sakamoto stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
606436b5abeSTakashi Sakamoto }
6076eb6c81eSTakashi Sakamoto }
6086eb6c81eSTakashi Sakamoto
snd_dice_stream_detect_current_formats(struct snd_dice * dice)609b60152f7STakashi Sakamoto int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
610b60152f7STakashi Sakamoto {
611b60152f7STakashi Sakamoto unsigned int rate;
612b60152f7STakashi Sakamoto enum snd_dice_rate_mode mode;
613b60152f7STakashi Sakamoto __be32 reg[2];
614b60152f7STakashi Sakamoto struct reg_params tx_params, rx_params;
615b60152f7STakashi Sakamoto int i;
616b60152f7STakashi Sakamoto int err;
617b60152f7STakashi Sakamoto
61858579c05STakashi Sakamoto /* If extended protocol is available, detect detail spec. */
61958579c05STakashi Sakamoto err = snd_dice_detect_extension_formats(dice);
62058579c05STakashi Sakamoto if (err >= 0)
62158579c05STakashi Sakamoto return err;
62258579c05STakashi Sakamoto
623b60152f7STakashi Sakamoto /*
624b60152f7STakashi Sakamoto * Available stream format is restricted at current mode of sampling
625b60152f7STakashi Sakamoto * clock.
626b60152f7STakashi Sakamoto */
627b60152f7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &rate);
628b60152f7STakashi Sakamoto if (err < 0)
629b60152f7STakashi Sakamoto return err;
630b60152f7STakashi Sakamoto
631b60152f7STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
632b60152f7STakashi Sakamoto if (err < 0)
633b60152f7STakashi Sakamoto return err;
634b60152f7STakashi Sakamoto
635b60152f7STakashi Sakamoto /*
636b60152f7STakashi Sakamoto * Just after owning the unit (GLOBAL_OWNER), the unit can return
637b60152f7STakashi Sakamoto * invalid stream formats. Selecting clock parameters have an effect
638b60152f7STakashi Sakamoto * for the unit to refine it.
639b60152f7STakashi Sakamoto */
64041319eb5STakashi Sakamoto err = select_clock(dice, rate);
641b60152f7STakashi Sakamoto if (err < 0)
642b60152f7STakashi Sakamoto return err;
643b60152f7STakashi Sakamoto
644b60152f7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params);
645b60152f7STakashi Sakamoto if (err < 0)
646b60152f7STakashi Sakamoto return err;
647b60152f7STakashi Sakamoto
648b60152f7STakashi Sakamoto for (i = 0; i < tx_params.count; ++i) {
649b60152f7STakashi Sakamoto err = snd_dice_transaction_read_tx(dice,
650b60152f7STakashi Sakamoto tx_params.size * i + TX_NUMBER_AUDIO,
651b60152f7STakashi Sakamoto reg, sizeof(reg));
652b60152f7STakashi Sakamoto if (err < 0)
653b60152f7STakashi Sakamoto return err;
654b60152f7STakashi Sakamoto dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
655b60152f7STakashi Sakamoto dice->tx_midi_ports[i] = max_t(unsigned int,
656b60152f7STakashi Sakamoto be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
657b60152f7STakashi Sakamoto }
658b60152f7STakashi Sakamoto for (i = 0; i < rx_params.count; ++i) {
659b60152f7STakashi Sakamoto err = snd_dice_transaction_read_rx(dice,
660b60152f7STakashi Sakamoto rx_params.size * i + RX_NUMBER_AUDIO,
661b60152f7STakashi Sakamoto reg, sizeof(reg));
662b60152f7STakashi Sakamoto if (err < 0)
663b60152f7STakashi Sakamoto return err;
664b60152f7STakashi Sakamoto dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
665b60152f7STakashi Sakamoto dice->rx_midi_ports[i] = max_t(unsigned int,
666b60152f7STakashi Sakamoto be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
667b60152f7STakashi Sakamoto }
668b60152f7STakashi Sakamoto
669b60152f7STakashi Sakamoto return 0;
670b60152f7STakashi Sakamoto }
671b60152f7STakashi Sakamoto
dice_lock_changed(struct snd_dice * dice)6726eb6c81eSTakashi Sakamoto static void dice_lock_changed(struct snd_dice *dice)
6736eb6c81eSTakashi Sakamoto {
6746eb6c81eSTakashi Sakamoto dice->dev_lock_changed = true;
6756eb6c81eSTakashi Sakamoto wake_up(&dice->hwdep_wait);
6766eb6c81eSTakashi Sakamoto }
6776eb6c81eSTakashi Sakamoto
snd_dice_stream_lock_try(struct snd_dice * dice)6786eb6c81eSTakashi Sakamoto int snd_dice_stream_lock_try(struct snd_dice *dice)
6796eb6c81eSTakashi Sakamoto {
6806eb6c81eSTakashi Sakamoto int err;
6816eb6c81eSTakashi Sakamoto
6826eb6c81eSTakashi Sakamoto spin_lock_irq(&dice->lock);
6836eb6c81eSTakashi Sakamoto
6846eb6c81eSTakashi Sakamoto if (dice->dev_lock_count < 0) {
6856eb6c81eSTakashi Sakamoto err = -EBUSY;
6866eb6c81eSTakashi Sakamoto goto out;
6876eb6c81eSTakashi Sakamoto }
6886eb6c81eSTakashi Sakamoto
6896eb6c81eSTakashi Sakamoto if (dice->dev_lock_count++ == 0)
6906eb6c81eSTakashi Sakamoto dice_lock_changed(dice);
6916eb6c81eSTakashi Sakamoto err = 0;
6926eb6c81eSTakashi Sakamoto out:
6936eb6c81eSTakashi Sakamoto spin_unlock_irq(&dice->lock);
6946eb6c81eSTakashi Sakamoto return err;
6956eb6c81eSTakashi Sakamoto }
6966eb6c81eSTakashi Sakamoto
snd_dice_stream_lock_release(struct snd_dice * dice)6976eb6c81eSTakashi Sakamoto void snd_dice_stream_lock_release(struct snd_dice *dice)
6986eb6c81eSTakashi Sakamoto {
6996eb6c81eSTakashi Sakamoto spin_lock_irq(&dice->lock);
7006eb6c81eSTakashi Sakamoto
7016eb6c81eSTakashi Sakamoto if (WARN_ON(dice->dev_lock_count <= 0))
7026eb6c81eSTakashi Sakamoto goto out;
7036eb6c81eSTakashi Sakamoto
7046eb6c81eSTakashi Sakamoto if (--dice->dev_lock_count == 0)
7056eb6c81eSTakashi Sakamoto dice_lock_changed(dice);
7066eb6c81eSTakashi Sakamoto out:
7076eb6c81eSTakashi Sakamoto spin_unlock_irq(&dice->lock);
7086eb6c81eSTakashi Sakamoto }
709