summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu/motu-command-dsp-message-parser.c
blob: 9efe4d364baf5cc2de602d4be8b9c9c4f03156ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// 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);
}