mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 23:51:46 +00:00
407359d44e
In command DSP models, one meter information consists of 4 bytes for IEEE 764 floating point (binary32). In previous patch, it is exported to userspace as 32 bit storage since the storage is also handled in ALSA firewire-motu driver as well in kernel space in which floating point arithmetic is not preferable. On the other hand, ALSA firewire-motu driver doesn't perform floating point calculation. The driver just gather meter information from isochronous packets and fill structure fields for userspace. In 'header' target of Kbuild, UAPI headers are processed before installed. In this timing, #ifdef macro with __KERNEL__ is removed. This mechanism is useful in the case so that the 32 bit storage can be accessible as u32 type in kernel space and float type in user space. We can see the same usage in ''struct acct_v3' in 'include/uapi/linux/acct.h'. This commit is for the above idea. Additionally, due to message protocol, meter information is filled with 0xffffffff in the end of period but 0xffffffff is invalid as binary32. To avoid confusion in userspace application, the last two elements are left without any assignment. Suggested-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Link: https://lore.kernel.org/r/20211027125529.54295-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai <tiwai@suse.de>
182 lines
4.4 KiB
C
182 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
//
|
|
// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
|
|
//
|
|
// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
|
|
|
// Below models allow software to configure their DSP function by command transferred in
|
|
// asynchronous transaction:
|
|
// * 828 mk3 (FireWire only and Hybrid)
|
|
// * 896 mk3 (FireWire only and Hybrid)
|
|
// * Ultralite mk3 (FireWire only and Hybrid)
|
|
// * Traveler mk3
|
|
// * Track 16
|
|
//
|
|
// Isochronous packets from the above models includes messages to report state of hardware meter.
|
|
|
|
#include "motu.h"
|
|
|
|
enum msg_parser_state {
|
|
INITIALIZED,
|
|
FRAGMENT_DETECTED,
|
|
AVAILABLE,
|
|
};
|
|
|
|
struct msg_parser {
|
|
spinlock_t lock;
|
|
enum msg_parser_state state;
|
|
unsigned int interval;
|
|
unsigned int message_count;
|
|
unsigned int fragment_pos;
|
|
unsigned int value_index;
|
|
u64 value;
|
|
struct snd_firewire_motu_command_dsp_meter meter;
|
|
};
|
|
|
|
int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
|
|
{
|
|
struct msg_parser *parser;
|
|
|
|
parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
|
|
if (!parser)
|
|
return -ENOMEM;
|
|
spin_lock_init(&parser->lock);
|
|
motu->message_parser = parser;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
|
|
{
|
|
struct msg_parser *parser = motu->message_parser;
|
|
|
|
parser->state = INITIALIZED;
|
|
|
|
// All of data blocks don't have messages with meaningful information.
|
|
switch (sfc) {
|
|
case CIP_SFC_176400:
|
|
case CIP_SFC_192000:
|
|
parser->interval = 4;
|
|
break;
|
|
case CIP_SFC_88200:
|
|
case CIP_SFC_96000:
|
|
parser->interval = 2;
|
|
break;
|
|
case CIP_SFC_32000:
|
|
case CIP_SFC_44100:
|
|
case CIP_SFC_48000:
|
|
default:
|
|
parser->interval = 1;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define FRAGMENT_POS 6
|
|
#define MIDI_BYTE_POS 7
|
|
#define MIDI_FLAG_POS 8
|
|
// One value of hardware meter consists of 4 messages.
|
|
#define FRAGMENTS_PER_VALUE 4
|
|
#define VALUES_AT_IMAGE_END 0xffffffffffffffff
|
|
|
|
void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
|
|
unsigned int desc_count, unsigned int data_block_quadlets)
|
|
{
|
|
struct msg_parser *parser = motu->message_parser;
|
|
unsigned int interval = parser->interval;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
spin_lock_irqsave(&parser->lock, flags);
|
|
|
|
for (i = 0; i < desc_count; ++i) {
|
|
const struct pkt_desc *desc = descs + i;
|
|
__be32 *buffer = desc->ctx_payload;
|
|
unsigned int data_blocks = desc->data_blocks;
|
|
int j;
|
|
|
|
for (j = 0; j < data_blocks; ++j) {
|
|
u8 *b = (u8 *)buffer;
|
|
buffer += data_block_quadlets;
|
|
|
|
switch (parser->state) {
|
|
case INITIALIZED:
|
|
{
|
|
u8 fragment = b[FRAGMENT_POS];
|
|
|
|
if (fragment > 0) {
|
|
parser->value = fragment;
|
|
parser->message_count = 1;
|
|
parser->state = FRAGMENT_DETECTED;
|
|
}
|
|
break;
|
|
}
|
|
case FRAGMENT_DETECTED:
|
|
{
|
|
if (parser->message_count % interval == 0) {
|
|
u8 fragment = b[FRAGMENT_POS];
|
|
|
|
parser->value >>= 8;
|
|
parser->value |= (u64)fragment << 56;
|
|
|
|
if (parser->value == VALUES_AT_IMAGE_END) {
|
|
parser->state = AVAILABLE;
|
|
parser->fragment_pos = 0;
|
|
parser->value_index = 0;
|
|
parser->message_count = 0;
|
|
}
|
|
}
|
|
++parser->message_count;
|
|
break;
|
|
}
|
|
case AVAILABLE:
|
|
default:
|
|
{
|
|
if (parser->message_count % interval == 0) {
|
|
u8 fragment = b[FRAGMENT_POS];
|
|
|
|
parser->value >>= 8;
|
|
parser->value |= (u64)fragment << 56;
|
|
++parser->fragment_pos;
|
|
|
|
if (parser->fragment_pos == 4) {
|
|
// Skip the last two quadlets since they could be
|
|
// invalid value (0xffffffff) as floating point
|
|
// number.
|
|
if (parser->value_index <
|
|
SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
|
|
u32 val = (u32)(parser->value >> 32);
|
|
parser->meter.data[parser->value_index] = val;
|
|
}
|
|
++parser->value_index;
|
|
parser->fragment_pos = 0;
|
|
}
|
|
|
|
if (parser->value == VALUES_AT_IMAGE_END) {
|
|
parser->value_index = 0;
|
|
parser->fragment_pos = 0;
|
|
parser->message_count = 0;
|
|
}
|
|
}
|
|
++parser->message_count;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&parser->lock, flags);
|
|
}
|
|
|
|
void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
|
|
struct snd_firewire_motu_command_dsp_meter *meter)
|
|
{
|
|
struct msg_parser *parser = motu->message_parser;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&parser->lock, flags);
|
|
memcpy(meter, &parser->meter, sizeof(*meter));
|
|
spin_unlock_irqrestore(&parser->lock, flags);
|
|
}
|