Commit 44b73088 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 660dd3d5
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE 0x746e736c
struct snd_firewire_event_common { struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
...@@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response { ...@@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response {
__be32 response[0]; /* some responses */ __be32 response[0]; /* some responses */
}; };
struct snd_firewire_event_digi00x_message {
unsigned int type;
__u32 message; /* Digi00x-specific message */
};
union snd_firewire_event { union snd_firewire_event {
struct snd_firewire_event_common common; struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status; struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification; struct snd_firewire_event_dice_notification dice_notification;
struct snd_firewire_event_efw_response efw_response; struct snd_firewire_event_efw_response efw_response;
struct snd_firewire_event_digi00x_message digi00x_message;
}; };
......
snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ 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 obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* 1.get firewire node information * 1.get firewire node information
* 2.get notification about starting/stopping stream * 2.get notification about starting/stopping stream
* 3.lock/unlock stream * 3.lock/unlock stream
* 4.get asynchronous messaging
*/ */
#include "digi00x.h" #include "digi00x.h"
...@@ -25,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, ...@@ -25,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&dg00x->lock); 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); prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&dg00x->lock); spin_unlock_irq(&dg00x->lock);
schedule(); schedule();
...@@ -42,6 +43,13 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, ...@@ -42,6 +43,13 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
dg00x->dev_lock_changed = false; dg00x->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status)); 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); spin_unlock_irq(&dg00x->lock);
...@@ -61,7 +69,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, ...@@ -61,7 +69,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &dg00x->hwdep_wait, wait); poll_wait(file, &dg00x->hwdep_wait, wait);
spin_lock_irq(&dg00x->lock); spin_lock_irq(&dg00x->lock);
if (dg00x->dev_lock_changed) if (dg00x->dev_lock_changed || dg00x->msg)
events = POLLIN | POLLRDNORM; events = POLLIN | POLLRDNORM;
else else
events = 0; events = 0;
......
/*
* 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);
}
...@@ -46,6 +46,7 @@ static void dg00x_card_free(struct snd_card *card) ...@@ -46,6 +46,7 @@ static void dg00x_card_free(struct snd_card *card)
struct snd_dg00x *dg00x = card->private_data; struct snd_dg00x *dg00x = card->private_data;
snd_dg00x_stream_destroy_duplex(dg00x); snd_dg00x_stream_destroy_duplex(dg00x);
snd_dg00x_transaction_unregister(dg00x);
fw_unit_put(dg00x->unit); fw_unit_put(dg00x->unit);
...@@ -93,6 +94,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, ...@@ -93,6 +94,10 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_dg00x_transaction_register(dg00x);
if (err < 0)
goto error;
err = snd_card_register(card); err = snd_card_register(card);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -109,6 +114,8 @@ static void snd_dg00x_update(struct fw_unit *unit) ...@@ -109,6 +114,8 @@ static void snd_dg00x_update(struct fw_unit *unit)
{ {
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
snd_dg00x_transaction_reregister(dg00x);
mutex_lock(&dg00x->mutex); mutex_lock(&dg00x->mutex);
snd_dg00x_stream_update_duplex(dg00x); snd_dg00x_stream_update_duplex(dg00x);
mutex_unlock(&dg00x->mutex); mutex_unlock(&dg00x->mutex);
......
...@@ -49,6 +49,9 @@ struct snd_dg00x { ...@@ -49,6 +49,9 @@ struct snd_dg00x {
bool dev_lock_changed; bool dev_lock_changed;
wait_queue_head_t hwdep_wait; wait_queue_head_t hwdep_wait;
/* For asynchronous messages. */
struct fw_address_handler async_handler;
u32 msg;
}; };
#define DG00X_ADDR_BASE 0xffffe0000000ull #define DG00X_ADDR_BASE 0xffffe0000000ull
...@@ -110,6 +113,10 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, ...@@ -110,6 +113,10 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime); struct snd_pcm_runtime *runtime);
void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); 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_rates[SND_DG00X_RATE_COUNT];
extern const unsigned int snd_dg00x_stream_pcm_channels[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, int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment