Skip to content

Commit acfedcb

Browse files
takaswietiwai
authored andcommitted
ALSA: firewire-lib: postpone to start IR context
Some devices have a quirk to postpone transmission of isoc packet for several dozen or hundred isoc cycles since configured to transmit. Furthermore, some devices have a quirk to transmit isoc packet with discontinued data of its header. In 1394 OHCI specification, software allows to start isoc context with certain isoc cycle. Linux firewire subsystem has kernel API to use it as well. This commit uses the functionality of 1394 OHCI controller to handle the quirks. At present, this feature is convenient to ALSA bebob and fireface driver. As a result, some devices can be safely handled, as long as I know: - MAudio FireWire solo - MAudio ProFire Lightbridge - MAudio FireWire 410 - Roland FA-66 Signed-off-by: Takashi Sakamoto <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 60dd492 commit acfedcb

File tree

10 files changed

+98
-15
lines changed

10 files changed

+98
-15
lines changed

sound/firewire/amdtp-stream.c

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/device.h>
1010
#include <linux/err.h>
1111
#include <linux/firewire.h>
12+
#include <linux/firewire-constants.h>
1213
#include <linux/module.h>
1314
#include <linux/slab.h>
1415
#include <sound/pcm.h>
@@ -983,13 +984,16 @@ static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
983984
* @d: the AMDTP domain to which the AMDTP stream belongs
984985
* @is_irq_target: whether isoc context for the AMDTP stream is used to generate
985986
* hardware IRQ.
987+
* @start_cycle: the isochronous cycle to start the context. Start immediately
988+
* if negative value is given.
986989
*
987990
* The stream cannot be started until it has been configured with
988991
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
989992
* device can be started.
990993
*/
991994
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
992-
struct amdtp_domain *d, bool is_irq_target)
995+
struct amdtp_domain *d, bool is_irq_target,
996+
int start_cycle)
993997
{
994998
static const struct {
995999
unsigned int data_block;
@@ -1146,7 +1150,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
11461150
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
11471151

11481152
s->callbacked = false;
1149-
err = fw_iso_context_start(s->context, -1, 0, tag);
1153+
err = fw_iso_context_start(s->context, start_cycle, 0, tag);
11501154
if (err < 0)
11511155
goto err_pkt_descs;
11521156

@@ -1339,14 +1343,40 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
13391343
}
13401344
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
13411345

1346+
static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle)
1347+
{
1348+
int generation;
1349+
int rcode;
1350+
__be32 reg;
1351+
u32 data;
1352+
1353+
// This is a request to local 1394 OHCI controller and expected to
1354+
// complete without any event waiting.
1355+
generation = fw_card->generation;
1356+
smp_rmb(); // node_id vs. generation.
1357+
rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST,
1358+
fw_card->node_id, generation, SCODE_100,
1359+
CSR_REGISTER_BASE + CSR_CYCLE_TIME,
1360+
&reg, sizeof(reg));
1361+
if (rcode != RCODE_COMPLETE)
1362+
return -EIO;
1363+
1364+
data = be32_to_cpu(reg);
1365+
*cur_cycle = data >> 12;
1366+
1367+
return 0;
1368+
}
1369+
13421370
/**
13431371
* amdtp_domain_start - start sending packets for isoc context in the domain.
13441372
* @d: the AMDTP domain.
1373+
* @ir_delay_cycle: the cycle delay to start all IR contexts.
13451374
*/
1346-
int amdtp_domain_start(struct amdtp_domain *d)
1375+
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
13471376
{
13481377
struct amdtp_stream *s;
1349-
int err = 0;
1378+
int cycle;
1379+
int err;
13501380

13511381
// Select an IT context as IRQ target.
13521382
list_for_each_entry(s, &d->streams, list) {
@@ -1357,17 +1387,54 @@ int amdtp_domain_start(struct amdtp_domain *d)
13571387
return -ENXIO;
13581388
d->irq_target = s;
13591389

1390+
if (ir_delay_cycle > 0) {
1391+
struct fw_card *fw_card = fw_parent_device(s->unit)->card;
1392+
1393+
err = get_current_cycle_time(fw_card, &cycle);
1394+
if (err < 0)
1395+
return err;
1396+
1397+
// No need to care overflow in cycle field because of enough
1398+
// width.
1399+
cycle += ir_delay_cycle;
1400+
1401+
// Round up to sec field.
1402+
if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) {
1403+
unsigned int sec;
1404+
1405+
// The sec field can overflow.
1406+
sec = (cycle & 0xffffe000) >> 13;
1407+
cycle = (++sec << 13) |
1408+
((cycle & 0x00001fff) / CYCLES_PER_SECOND);
1409+
}
1410+
1411+
// In OHCI 1394 specification, lower 2 bits are available for
1412+
// sec field.
1413+
cycle &= 0x00007fff;
1414+
} else {
1415+
cycle = -1;
1416+
}
1417+
13601418
list_for_each_entry(s, &d->streams, list) {
1419+
int cycle_match;
1420+
1421+
if (s->direction == AMDTP_IN_STREAM) {
1422+
cycle_match = cycle;
1423+
} else {
1424+
// IT context starts immediately.
1425+
cycle_match = -1;
1426+
}
1427+
13611428
if (s != d->irq_target) {
13621429
err = amdtp_stream_start(s, s->channel, s->speed, d,
1363-
false);
1430+
false, cycle_match);
13641431
if (err < 0)
13651432
goto error;
13661433
}
13671434
}
13681435

13691436
s = d->irq_target;
1370-
err = amdtp_stream_start(s, s->channel, s->speed, d, true);
1437+
err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1);
13711438
if (err < 0)
13721439
goto error;
13731440

sound/firewire/amdtp-stream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ void amdtp_domain_destroy(struct amdtp_domain *d);
288288
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
289289
int channel, int speed);
290290

291-
int amdtp_domain_start(struct amdtp_domain *d);
291+
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle);
292292
void amdtp_domain_stop(struct amdtp_domain *d);
293293

294294
static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,

sound/firewire/bebob/bebob_stream.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,15 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
658658
if (err < 0)
659659
goto error;
660660

661-
err = amdtp_domain_start(&bebob->domain);
661+
// The device postpones start of transmission mostly for 1 sec
662+
// after receives packets firstly. For safe, IR context starts
663+
// 1.5 sec (=12000 cycles) later. This is within 2.0 sec
664+
// (=CALLBACK_TIMEOUT).
665+
// Furthermore, some devices transfer isoc packets with
666+
// discontinuous counter in the beginning of packet streaming.
667+
// The delay has an effect to avoid detection of this
668+
// discontinuity.
669+
err = amdtp_domain_start(&bebob->domain, 12000);
662670
if (err < 0)
663671
goto error;
664672

sound/firewire/dice/dice-stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
462462
goto error;
463463
}
464464

465-
err = amdtp_domain_start(&dice->domain);
465+
err = amdtp_domain_start(&dice->domain, 0);
466466
if (err < 0)
467467
goto error;
468468

sound/firewire/digi00x/digi00x-stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
375375
if (err < 0)
376376
goto error;
377377

378-
err = amdtp_domain_start(&dg00x->domain);
378+
err = amdtp_domain_start(&dg00x->domain, 0);
379379
if (err < 0)
380380
goto error;
381381

sound/firewire/fireface/ff-stream.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
184184
*/
185185
if (!amdtp_stream_running(&ff->rx_stream)) {
186186
int spd = fw_parent_device(ff->unit)->max_speed;
187+
unsigned int ir_delay_cycle;
187188

188189
err = ff->spec->protocol->begin_session(ff, rate);
189190
if (err < 0)
@@ -199,7 +200,14 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
199200
if (err < 0)
200201
goto error;
201202

202-
err = amdtp_domain_start(&ff->domain);
203+
// The device postpones start of transmission mostly for several
204+
// cycles after receiving packets firstly.
205+
if (ff->spec->protocol == &snd_ff_protocol_ff800)
206+
ir_delay_cycle = 800; // = 100 msec
207+
else
208+
ir_delay_cycle = 16; // = 2 msec
209+
210+
err = amdtp_domain_start(&ff->domain, ir_delay_cycle);
203211
if (err < 0)
204212
goto error;
205213

sound/firewire/fireworks/fireworks_stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
272272
if (err < 0)
273273
goto error;
274274

275-
err = amdtp_domain_start(&efw->domain);
275+
err = amdtp_domain_start(&efw->domain, 0);
276276
if (err < 0)
277277
goto error;
278278

sound/firewire/motu/motu-stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
260260
if (err < 0)
261261
goto stop_streams;
262262

263-
err = amdtp_domain_start(&motu->domain);
263+
err = amdtp_domain_start(&motu->domain, 0);
264264
if (err < 0)
265265
goto stop_streams;
266266

sound/firewire/oxfw/oxfw-stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
355355
}
356356
}
357357

358-
err = amdtp_domain_start(&oxfw->domain);
358+
err = amdtp_domain_start(&oxfw->domain, 0);
359359
if (err < 0)
360360
goto error;
361361

sound/firewire/tascam/tascam-stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
473473
if (err < 0)
474474
goto error;
475475

476-
err = amdtp_domain_start(&tscm->domain);
476+
err = amdtp_domain_start(&tscm->domain, 0);
477477
if (err < 0)
478478
return err;
479479

0 commit comments

Comments
 (0)