ALSA: firewire-digi00x: add support for asynchronous messaging

Digi 002/003 family uses asynchronous transaction for messaging.
The address to transmit this message is stored on a certain register.

This commit allocates a range of address on OHCI 1394 host controller
to handle the messaging. As long as I know, the purpose of this message
seems to notify lost of synchronization. While, the meaning of content
of the message is not clear.

Actual examples of this messaging:
 * When clock source is set as internal:
 - 0x00007051
 - 0x00007052
 - 0x00007054
 - 0x00007057
 - 0x00007058
 * When clock source is set as somewhat external:
 - 0x00009000
 - 0x00009010
 - 0x00009020
 - 0x00009021
 - 0x00009022

The lost often occurs when using internal clock source. In this case,
users hear sounds with quite short gap every several minutes. In fact,
the lost is recovered temporarily.

When using with external clock source, the lost seems not to occur. The
mechanism is not clear yet.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2015-09-30 09:39:22 +09:00 committed by Takashi Iwai
parent 660dd3d52e
commit 44b7308871
6 changed files with 114 additions and 3 deletions

View File

@ -9,6 +9,7 @@
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE 0x746e736c
struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response {
__be32 response[0]; /* some responses */
};
struct snd_firewire_event_digi00x_message {
unsigned int type;
__u32 message; /* Digi00x-specific message */
};
union snd_firewire_event {
struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification;
struct snd_firewire_event_efw_response efw_response;
struct snd_firewire_event_digi00x_message digi00x_message;
};

View File

@ -1,3 +1,4 @@
snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
digi00x-pcm.o digi00x-hwdep.o digi00x.o
digi00x-pcm.o digi00x-hwdep.o \
digi00x-transaction.o digi00x.o
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o

View File

@ -12,6 +12,7 @@
* 1.get firewire node information
* 2.get notification about starting/stopping stream
* 3.lock/unlock stream
* 4.get asynchronous messaging
*/
#include "digi00x.h"
@ -25,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&dg00x->lock);
while (!dg00x->dev_lock_changed) {
while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&dg00x->lock);
schedule();
@ -42,6 +43,13 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
dg00x->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status));
} else {
event.digi00x_message.type =
SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
event.digi00x_message.message = dg00x->msg;
dg00x->msg = 0;
count = min_t(long, count, sizeof(event.digi00x_message));
}
spin_unlock_irq(&dg00x->lock);
@ -61,7 +69,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &dg00x->hwdep_wait, wait);
spin_lock_irq(&dg00x->lock);
if (dg00x->dev_lock_changed)
if (dg00x->dev_lock_changed || dg00x->msg)
events = POLLIN | POLLRDNORM;
else
events = 0;

View File

@ -0,0 +1,81 @@
/*
* digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
*
* Copyright (c) 2014-2015 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/asound.h>
#include "digi00x.h"
static void handle_unknown_message(struct snd_dg00x *dg00x,
unsigned long long offset, __be32 *buf)
{
unsigned long flags;
spin_lock_irqsave(&dg00x->lock, flags);
dg00x->msg = be32_to_cpu(*buf);
spin_unlock_irqrestore(&dg00x->lock, flags);
wake_up(&dg00x->hwdep_wait);
}
static void handle_message(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
void *data, size_t length, void *callback_data)
{
struct snd_dg00x *dg00x = callback_data;
__be32 *buf = (__be32 *)data;
if (offset == dg00x->async_handler.offset)
handle_unknown_message(dg00x, offset, buf);
fw_send_response(card, request, RCODE_COMPLETE);
}
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
{
struct fw_device *device = fw_parent_device(dg00x->unit);
__be32 data[2];
/* Unknown. 4bytes. */
data[0] = cpu_to_be32((device->card->node_id << 16) |
(dg00x->async_handler.offset >> 32));
data[1] = cpu_to_be32(dg00x->async_handler.offset);
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
&data, sizeof(data), 0);
}
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
{
static const struct fw_address_region resp_register_region = {
.start = 0xffffe0000000ull,
.end = 0xffffe000ffffull,
};
int err;
dg00x->async_handler.length = 4;
dg00x->async_handler.address_callback = handle_message;
dg00x->async_handler.callback_data = dg00x;
err = fw_core_add_address_handler(&dg00x->async_handler,
&resp_register_region);
if (err < 0)
return err;
err = snd_dg00x_transaction_reregister(dg00x);
if (err < 0) {
fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.address_callback = NULL;
}
return err;
}
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{
fw_core_remove_address_handler(&dg00x->async_handler);
}

View File

@ -46,6 +46,7 @@ static void dg00x_card_free(struct snd_card *card)
struct snd_dg00x *dg00x = card->private_data;
snd_dg00x_stream_destroy_duplex(dg00x);
snd_dg00x_transaction_unregister(dg00x);
fw_unit_put(dg00x->unit);
@ -93,6 +94,10 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0)
goto error;
err = snd_dg00x_transaction_register(dg00x);
if (err < 0)
goto error;
err = snd_card_register(card);
if (err < 0)
goto error;
@ -109,6 +114,8 @@ static void snd_dg00x_update(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
snd_dg00x_transaction_reregister(dg00x);
mutex_lock(&dg00x->mutex);
snd_dg00x_stream_update_duplex(dg00x);
mutex_unlock(&dg00x->mutex);

View File

@ -49,6 +49,9 @@ struct snd_dg00x {
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
/* For asynchronous messages. */
struct fw_address_handler async_handler;
u32 msg;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
@ -110,6 +113,10 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,