xref: /linux/sound/firewire/motu/motu-command-dsp-message-parser.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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 
19 enum msg_parser_state {
20 	INITIALIZED,
21 	FRAGMENT_DETECTED,
22 	AVAILABLE,
23 };
24 
25 struct 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 
snd_motu_command_dsp_message_parser_new(struct snd_motu * motu)36 int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
37 {
38 	struct msg_parser *parser;
39 
40 	parser = devm_kzalloc(&motu->card->card_dev, 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 
snd_motu_command_dsp_message_parser_init(struct snd_motu * motu,enum cip_sfc sfc)49 int 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 
snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream * s,const struct pkt_desc * desc,unsigned int count)83 void 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(&parser->lock, flags);
173 }
174 
snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu * motu,struct snd_firewire_motu_command_dsp_meter * meter)175 void 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(&parser->lock, flags);
184 }
185