1// SPDX-License-Identifier: GPL-2.0-only
2//
3// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
4//
5// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6
7// Below models allow software to configure their DSP function by command transferred in
8// asynchronous transaction:
9// * 828 mk3 (FireWire only and Hybrid)
10// * 896 mk3 (FireWire only and Hybrid)
11// * Ultralite mk3 (FireWire only and Hybrid)
12// * Traveler mk3
13// * Track 16
14//
15// Isochronous packets from the above models includes messages to report state of hardware meter.
16
17#include "motu.h"
18
19enum msg_parser_state {
20 INITIALIZED,
21 FRAGMENT_DETECTED,
22 AVAILABLE,
23};
24
25struct msg_parser {
26 spinlock_t lock;
27 enum msg_parser_state state;
28 unsigned int interval;
29 unsigned int message_count;
30 unsigned int fragment_pos;
31 unsigned int value_index;
32 u64 value;
33 struct snd_firewire_motu_command_dsp_meter meter;
34};
35
36int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
37{
38 struct msg_parser *parser;
39
40 parser = devm_kzalloc(dev: &motu->card->card_dev, size: sizeof(*parser), GFP_KERNEL);
41 if (!parser)
42 return -ENOMEM;
43 spin_lock_init(&parser->lock);
44 motu->message_parser = parser;
45
46 return 0;
47}
48
49int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
50{
51 struct msg_parser *parser = motu->message_parser;
52
53 parser->state = INITIALIZED;
54
55 // All of data blocks don't have messages with meaningful information.
56 switch (sfc) {
57 case CIP_SFC_176400:
58 case CIP_SFC_192000:
59 parser->interval = 4;
60 break;
61 case CIP_SFC_88200:
62 case CIP_SFC_96000:
63 parser->interval = 2;
64 break;
65 case CIP_SFC_32000:
66 case CIP_SFC_44100:
67 case CIP_SFC_48000:
68 default:
69 parser->interval = 1;
70 break;
71 }
72
73 return 0;
74}
75
76#define FRAGMENT_POS 6
77#define MIDI_BYTE_POS 7
78#define MIDI_FLAG_POS 8
79// One value of hardware meter consists of 4 messages.
80#define FRAGMENTS_PER_VALUE 4
81#define VALUES_AT_IMAGE_END 0xffffffffffffffff
82
83void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
84 const struct pkt_desc *desc, unsigned int count)
85{
86 struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
87 unsigned int data_block_quadlets = s->data_block_quadlets;
88 struct msg_parser *parser = motu->message_parser;
89 unsigned int interval = parser->interval;
90 unsigned long flags;
91 int i;
92
93 spin_lock_irqsave(&parser->lock, flags);
94
95 for (i = 0; i < count; ++i) {
96 __be32 *buffer = desc->ctx_payload;
97 unsigned int data_blocks = desc->data_blocks;
98 int j;
99
100 desc = amdtp_stream_next_packet_desc(s, desc);
101
102 for (j = 0; j < data_blocks; ++j) {
103 u8 *b = (u8 *)buffer;
104 buffer += data_block_quadlets;
105
106 switch (parser->state) {
107 case INITIALIZED:
108 {
109 u8 fragment = b[FRAGMENT_POS];
110
111 if (fragment > 0) {
112 parser->value = fragment;
113 parser->message_count = 1;
114 parser->state = FRAGMENT_DETECTED;
115 }
116 break;
117 }
118 case FRAGMENT_DETECTED:
119 {
120 if (parser->message_count % interval == 0) {
121 u8 fragment = b[FRAGMENT_POS];
122
123 parser->value >>= 8;
124 parser->value |= (u64)fragment << 56;
125
126 if (parser->value == VALUES_AT_IMAGE_END) {
127 parser->state = AVAILABLE;
128 parser->fragment_pos = 0;
129 parser->value_index = 0;
130 parser->message_count = 0;
131 }
132 }
133 ++parser->message_count;
134 break;
135 }
136 case AVAILABLE:
137 default:
138 {
139 if (parser->message_count % interval == 0) {
140 u8 fragment = b[FRAGMENT_POS];
141
142 parser->value >>= 8;
143 parser->value |= (u64)fragment << 56;
144 ++parser->fragment_pos;
145
146 if (parser->fragment_pos == 4) {
147 // Skip the last two quadlets since they could be
148 // invalid value (0xffffffff) as floating point
149 // number.
150 if (parser->value_index <
151 SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
152 u32 val = (u32)(parser->value >> 32);
153 parser->meter.data[parser->value_index] = val;
154 }
155 ++parser->value_index;
156 parser->fragment_pos = 0;
157 }
158
159 if (parser->value == VALUES_AT_IMAGE_END) {
160 parser->value_index = 0;
161 parser->fragment_pos = 0;
162 parser->message_count = 0;
163 }
164 }
165 ++parser->message_count;
166 break;
167 }
168 }
169 }
170 }
171
172 spin_unlock_irqrestore(lock: &parser->lock, flags);
173}
174
175void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
176 struct snd_firewire_motu_command_dsp_meter *meter)
177{
178 struct msg_parser *parser = motu->message_parser;
179 unsigned long flags;
180
181 spin_lock_irqsave(&parser->lock, flags);
182 memcpy(meter, &parser->meter, sizeof(*meter));
183 spin_unlock_irqrestore(lock: &parser->lock, flags);
184}
185

source code of linux/sound/firewire/motu/motu-command-dsp-message-parser.c