1/*
2 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
3 *
4 * Copyright (c) 2014-2015 Takashi Sakamoto
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8
9#include "digi00x.h"
10
11#define CALLBACK_TIMEOUT 500
12
13const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
14	[SND_DG00X_RATE_44100] = 44100,
15	[SND_DG00X_RATE_48000] = 48000,
16	[SND_DG00X_RATE_88200] = 88200,
17	[SND_DG00X_RATE_96000] = 96000,
18};
19
20/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
21const unsigned int
22snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
23	/* Analog/ADAT/SPDIF */
24	[SND_DG00X_RATE_44100] = (8 + 8 + 2),
25	[SND_DG00X_RATE_48000] = (8 + 8 + 2),
26	/* Analog/SPDIF */
27	[SND_DG00X_RATE_88200] = (8 + 2),
28	[SND_DG00X_RATE_96000] = (8 + 2),
29};
30
31int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
32{
33	u32 data;
34	__be32 reg;
35	int err;
36
37	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
38				 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
39				 &reg, sizeof(reg), 0);
40	if (err < 0)
41		return err;
42
43	data = be32_to_cpu(reg) & 0x0f;
44	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
45		*rate = snd_dg00x_stream_rates[data];
46	else
47		err = -EIO;
48
49	return err;
50}
51
52int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
53{
54	__be32 reg;
55	unsigned int i;
56
57	for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
58		if (rate == snd_dg00x_stream_rates[i])
59			break;
60	}
61	if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
62		return -EINVAL;
63
64	reg = cpu_to_be32(i);
65	return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
66				  DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
67				  &reg, sizeof(reg), 0);
68}
69
70int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
71			       enum snd_dg00x_clock *clock)
72{
73	__be32 reg;
74	int err;
75
76	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
77				 DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
78				 &reg, sizeof(reg), 0);
79	if (err < 0)
80		return err;
81
82	*clock = be32_to_cpu(reg) & 0x0f;
83	if (*clock >= SND_DG00X_CLOCK_COUNT)
84		err = -EIO;
85
86	return err;
87}
88
89int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
90{
91	__be32 reg;
92	int err;
93
94	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
95				 DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
96				 &reg, sizeof(reg), 0);
97	if (err >= 0)
98		*detect = be32_to_cpu(reg) > 0;
99
100	return err;
101}
102
103int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
104				       unsigned int *rate)
105{
106	u32 data;
107	__be32 reg;
108	int err;
109
110	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
111				 DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
112				 &reg, sizeof(reg), 0);
113	if (err < 0)
114		return err;
115
116	data = be32_to_cpu(reg) & 0x0f;
117	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
118		*rate = snd_dg00x_stream_rates[data];
119	/* This means desync. */
120	else
121		err = -EBUSY;
122
123	return err;
124}
125
126static void finish_session(struct snd_dg00x *dg00x)
127{
128	__be32 data = cpu_to_be32(0x00000003);
129
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
135static int begin_session(struct snd_dg00x *dg00x)
136{
137	__be32 data;
138	u32 curr;
139	int err;
140
141	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
142				 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
143				 &data, sizeof(data), 0);
144	if (err < 0)
145		goto error;
146	curr = be32_to_cpu(data);
147
148	if (curr == 0)
149		curr = 2;
150
151	curr--;
152	while (curr > 0) {
153		data = cpu_to_be32(curr);
154		err = snd_fw_transaction(dg00x->unit,
155					 TCODE_WRITE_QUADLET_REQUEST,
156					 DG00X_ADDR_BASE +
157					 DG00X_OFFSET_STREAMING_SET,
158					 &data, sizeof(data), 0);
159		if (err < 0)
160			goto error;
161
162		msleep(20);
163		curr--;
164	}
165
166	return 0;
167error:
168	finish_session(dg00x);
169	return err;
170}
171
172static void release_resources(struct snd_dg00x *dg00x)
173{
174	__be32 data = 0;
175
176	/* Unregister isochronous channels for both direction. */
177	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
178			   DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
179			   &data, sizeof(data), 0);
180
181	/* Release isochronous resources. */
182	fw_iso_resources_free(&dg00x->tx_resources);
183	fw_iso_resources_free(&dg00x->rx_resources);
184}
185
186static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
187{
188	unsigned int i;
189	__be32 data;
190	int err;
191
192	/* Check sampling rate. */
193	for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
194		if (snd_dg00x_stream_rates[i] == rate)
195			break;
196	}
197	if (i == SND_DG00X_RATE_COUNT)
198		return -EINVAL;
199
200	/* Keep resources for out-stream. */
201	err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
202				       snd_dg00x_stream_pcm_channels[i]);
203	if (err < 0)
204		return err;
205	err = fw_iso_resources_allocate(&dg00x->rx_resources,
206				amdtp_stream_get_max_payload(&dg00x->rx_stream),
207				fw_parent_device(dg00x->unit)->max_speed);
208	if (err < 0)
209		return err;
210
211	/* Keep resources for in-stream. */
212	err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
213				       snd_dg00x_stream_pcm_channels[i]);
214	if (err < 0)
215		return err;
216	err = fw_iso_resources_allocate(&dg00x->tx_resources,
217				amdtp_stream_get_max_payload(&dg00x->tx_stream),
218				fw_parent_device(dg00x->unit)->max_speed);
219	if (err < 0)
220		goto error;
221
222	/* Register isochronous channels for both direction. */
223	data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
224			   dg00x->rx_resources.channel);
225	err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
226				 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
227				 &data, sizeof(data), 0);
228	if (err < 0)
229		goto error;
230
231	return 0;
232error:
233	release_resources(dg00x);
234	return err;
235}
236
237int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
238{
239	int err;
240
241	/* For out-stream. */
242	err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
243	if (err < 0)
244		goto error;
245	err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
246	if (err < 0)
247		goto error;
248
249	/* For in-stream. */
250	err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
251	if (err < 0)
252		goto error;
253	err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
254	if (err < 0)
255		goto error;
256
257	return 0;
258error:
259	snd_dg00x_stream_destroy_duplex(dg00x);
260	return err;
261}
262
263/*
264 * This function should be called before starting streams or after stopping
265 * streams.
266 */
267void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
268{
269	amdtp_stream_destroy(&dg00x->rx_stream);
270	fw_iso_resources_destroy(&dg00x->rx_resources);
271
272	amdtp_stream_destroy(&dg00x->tx_stream);
273	fw_iso_resources_destroy(&dg00x->tx_resources);
274}
275
276int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
277{
278	unsigned int curr_rate;
279	int err = 0;
280
281	if (dg00x->substreams_counter == 0)
282		goto end;
283
284	/* Check current sampling rate. */
285	err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
286	if (err < 0)
287		goto error;
288	if (rate == 0)
289		rate = curr_rate;
290	if (curr_rate != rate ||
291	    amdtp_streaming_error(&dg00x->tx_stream) ||
292	    amdtp_streaming_error(&dg00x->rx_stream)) {
293		finish_session(dg00x);
294
295		amdtp_stream_stop(&dg00x->tx_stream);
296		amdtp_stream_stop(&dg00x->rx_stream);
297		release_resources(dg00x);
298	}
299
300	/*
301	 * No packets are transmitted without receiving packets, reagardless of
302	 * which source of clock is used.
303	 */
304	if (!amdtp_stream_running(&dg00x->rx_stream)) {
305		err = snd_dg00x_stream_set_local_rate(dg00x, rate);
306		if (err < 0)
307			goto error;
308
309		err = keep_resources(dg00x, rate);
310		if (err < 0)
311			goto error;
312
313		err = begin_session(dg00x);
314		if (err < 0)
315			goto error;
316
317		err = amdtp_stream_start(&dg00x->rx_stream,
318				dg00x->rx_resources.channel,
319				fw_parent_device(dg00x->unit)->max_speed);
320		if (err < 0)
321			goto error;
322
323		if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
324					      CALLBACK_TIMEOUT)) {
325			err = -ETIMEDOUT;
326			goto error;
327		}
328	}
329
330	/*
331	 * The value of SYT field in transmitted packets is always 0x0000. Thus,
332	 * duplex streams with timestamp synchronization cannot be built.
333	 */
334	if (!amdtp_stream_running(&dg00x->tx_stream)) {
335		err = amdtp_stream_start(&dg00x->tx_stream,
336				dg00x->tx_resources.channel,
337				fw_parent_device(dg00x->unit)->max_speed);
338		if (err < 0)
339			goto error;
340
341		if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
342					      CALLBACK_TIMEOUT)) {
343			err = -ETIMEDOUT;
344			goto error;
345		}
346	}
347end:
348	return err;
349error:
350	finish_session(dg00x);
351
352	amdtp_stream_stop(&dg00x->tx_stream);
353	amdtp_stream_stop(&dg00x->rx_stream);
354	release_resources(dg00x);
355
356	return err;
357}
358
359void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
360{
361	if (dg00x->substreams_counter > 0)
362		return;
363
364	amdtp_stream_stop(&dg00x->tx_stream);
365	amdtp_stream_stop(&dg00x->rx_stream);
366	finish_session(dg00x);
367	release_resources(dg00x);
368
369	/*
370	 * Just after finishing the session, the device may lost transmitting
371	 * functionality for a short time.
372	 */
373	msleep(50);
374}
375
376void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
377{
378	fw_iso_resources_update(&dg00x->tx_resources);
379	fw_iso_resources_update(&dg00x->rx_resources);
380
381	amdtp_stream_update(&dg00x->tx_stream);
382	amdtp_stream_update(&dg00x->rx_stream);
383}
384
385void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
386{
387	dg00x->dev_lock_changed = true;
388	wake_up(&dg00x->hwdep_wait);
389}
390
391int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
392{
393	int err;
394
395	spin_lock_irq(&dg00x->lock);
396
397	/* user land lock this */
398	if (dg00x->dev_lock_count < 0) {
399		err = -EBUSY;
400		goto end;
401	}
402
403	/* this is the first time */
404	if (dg00x->dev_lock_count++ == 0)
405		snd_dg00x_stream_lock_changed(dg00x);
406	err = 0;
407end:
408	spin_unlock_irq(&dg00x->lock);
409	return err;
410}
411
412void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
413{
414	spin_lock_irq(&dg00x->lock);
415
416	if (WARN_ON(dg00x->dev_lock_count <= 0))
417		goto end;
418	if (--dg00x->dev_lock_count == 0)
419		snd_dg00x_stream_lock_changed(dg00x);
420end:
421	spin_unlock_irq(&dg00x->lock);
422}
423