162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci// Below models allow software to configure their DSP function by command transferred in
862306a36Sopenharmony_ci// asynchronous transaction:
962306a36Sopenharmony_ci//  * 828 mk3 (FireWire only and Hybrid)
1062306a36Sopenharmony_ci//  * 896 mk3 (FireWire only and Hybrid)
1162306a36Sopenharmony_ci//  * Ultralite mk3 (FireWire only and Hybrid)
1262306a36Sopenharmony_ci//  * Traveler mk3
1362306a36Sopenharmony_ci//  * Track 16
1462306a36Sopenharmony_ci//
1562306a36Sopenharmony_ci// Isochronous packets from the above models includes messages to report state of hardware meter.
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "motu.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cienum msg_parser_state {
2062306a36Sopenharmony_ci	INITIALIZED,
2162306a36Sopenharmony_ci	FRAGMENT_DETECTED,
2262306a36Sopenharmony_ci	AVAILABLE,
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct msg_parser {
2662306a36Sopenharmony_ci	spinlock_t lock;
2762306a36Sopenharmony_ci	enum msg_parser_state state;
2862306a36Sopenharmony_ci	unsigned int interval;
2962306a36Sopenharmony_ci	unsigned int message_count;
3062306a36Sopenharmony_ci	unsigned int fragment_pos;
3162306a36Sopenharmony_ci	unsigned int value_index;
3262306a36Sopenharmony_ci	u64 value;
3362306a36Sopenharmony_ci	struct snd_firewire_motu_command_dsp_meter meter;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciint snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct msg_parser *parser;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
4162306a36Sopenharmony_ci	if (!parser)
4262306a36Sopenharmony_ci		return -ENOMEM;
4362306a36Sopenharmony_ci	spin_lock_init(&parser->lock);
4462306a36Sopenharmony_ci	motu->message_parser = parser;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciint snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct msg_parser *parser = motu->message_parser;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	parser->state = INITIALIZED;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	// All of data blocks don't have messages with meaningful information.
5662306a36Sopenharmony_ci	switch (sfc) {
5762306a36Sopenharmony_ci	case CIP_SFC_176400:
5862306a36Sopenharmony_ci	case CIP_SFC_192000:
5962306a36Sopenharmony_ci		parser->interval = 4;
6062306a36Sopenharmony_ci		break;
6162306a36Sopenharmony_ci	case CIP_SFC_88200:
6262306a36Sopenharmony_ci	case CIP_SFC_96000:
6362306a36Sopenharmony_ci		parser->interval = 2;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	case CIP_SFC_32000:
6662306a36Sopenharmony_ci	case CIP_SFC_44100:
6762306a36Sopenharmony_ci	case CIP_SFC_48000:
6862306a36Sopenharmony_ci	default:
6962306a36Sopenharmony_ci		parser->interval = 1;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define FRAGMENT_POS			6
7762306a36Sopenharmony_ci#define MIDI_BYTE_POS			7
7862306a36Sopenharmony_ci#define MIDI_FLAG_POS			8
7962306a36Sopenharmony_ci// One value of hardware meter consists of 4 messages.
8062306a36Sopenharmony_ci#define FRAGMENTS_PER_VALUE		4
8162306a36Sopenharmony_ci#define VALUES_AT_IMAGE_END		0xffffffffffffffff
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
8462306a36Sopenharmony_ci					const struct pkt_desc *desc, unsigned int count)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
8762306a36Sopenharmony_ci	unsigned int data_block_quadlets = s->data_block_quadlets;
8862306a36Sopenharmony_ci	struct msg_parser *parser = motu->message_parser;
8962306a36Sopenharmony_ci	unsigned int interval = parser->interval;
9062306a36Sopenharmony_ci	unsigned long flags;
9162306a36Sopenharmony_ci	int i;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	spin_lock_irqsave(&parser->lock, flags);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
9662306a36Sopenharmony_ci		__be32 *buffer = desc->ctx_payload;
9762306a36Sopenharmony_ci		unsigned int data_blocks = desc->data_blocks;
9862306a36Sopenharmony_ci		int j;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		desc = amdtp_stream_next_packet_desc(s, desc);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		for (j = 0; j < data_blocks; ++j) {
10362306a36Sopenharmony_ci			u8 *b = (u8 *)buffer;
10462306a36Sopenharmony_ci			buffer += data_block_quadlets;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			switch (parser->state) {
10762306a36Sopenharmony_ci			case INITIALIZED:
10862306a36Sopenharmony_ci			{
10962306a36Sopenharmony_ci				u8 fragment = b[FRAGMENT_POS];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci				if (fragment > 0) {
11262306a36Sopenharmony_ci					parser->value = fragment;
11362306a36Sopenharmony_ci					parser->message_count = 1;
11462306a36Sopenharmony_ci					parser->state = FRAGMENT_DETECTED;
11562306a36Sopenharmony_ci				}
11662306a36Sopenharmony_ci				break;
11762306a36Sopenharmony_ci			}
11862306a36Sopenharmony_ci			case FRAGMENT_DETECTED:
11962306a36Sopenharmony_ci			{
12062306a36Sopenharmony_ci				if (parser->message_count % interval == 0) {
12162306a36Sopenharmony_ci					u8 fragment = b[FRAGMENT_POS];
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci					parser->value >>= 8;
12462306a36Sopenharmony_ci					parser->value |= (u64)fragment << 56;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci					if (parser->value == VALUES_AT_IMAGE_END) {
12762306a36Sopenharmony_ci						parser->state = AVAILABLE;
12862306a36Sopenharmony_ci						parser->fragment_pos = 0;
12962306a36Sopenharmony_ci						parser->value_index = 0;
13062306a36Sopenharmony_ci						parser->message_count = 0;
13162306a36Sopenharmony_ci					}
13262306a36Sopenharmony_ci				}
13362306a36Sopenharmony_ci				++parser->message_count;
13462306a36Sopenharmony_ci				break;
13562306a36Sopenharmony_ci			}
13662306a36Sopenharmony_ci			case AVAILABLE:
13762306a36Sopenharmony_ci			default:
13862306a36Sopenharmony_ci			{
13962306a36Sopenharmony_ci				if (parser->message_count % interval == 0) {
14062306a36Sopenharmony_ci					u8 fragment = b[FRAGMENT_POS];
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci					parser->value >>= 8;
14362306a36Sopenharmony_ci					parser->value |= (u64)fragment << 56;
14462306a36Sopenharmony_ci					++parser->fragment_pos;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci					if (parser->fragment_pos == 4) {
14762306a36Sopenharmony_ci						// Skip the last two quadlets since they could be
14862306a36Sopenharmony_ci						// invalid value (0xffffffff) as floating point
14962306a36Sopenharmony_ci						// number.
15062306a36Sopenharmony_ci						if (parser->value_index <
15162306a36Sopenharmony_ci						    SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
15262306a36Sopenharmony_ci							u32 val = (u32)(parser->value >> 32);
15362306a36Sopenharmony_ci							parser->meter.data[parser->value_index] = val;
15462306a36Sopenharmony_ci						}
15562306a36Sopenharmony_ci						++parser->value_index;
15662306a36Sopenharmony_ci						parser->fragment_pos = 0;
15762306a36Sopenharmony_ci					}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci					if (parser->value == VALUES_AT_IMAGE_END) {
16062306a36Sopenharmony_ci						parser->value_index = 0;
16162306a36Sopenharmony_ci						parser->fragment_pos = 0;
16262306a36Sopenharmony_ci						parser->message_count = 0;
16362306a36Sopenharmony_ci					}
16462306a36Sopenharmony_ci				}
16562306a36Sopenharmony_ci				++parser->message_count;
16662306a36Sopenharmony_ci				break;
16762306a36Sopenharmony_ci			}
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	spin_unlock_irqrestore(&parser->lock, flags);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_civoid snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
17662306a36Sopenharmony_ci					struct snd_firewire_motu_command_dsp_meter *meter)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct msg_parser *parser = motu->message_parser;
17962306a36Sopenharmony_ci	unsigned long flags;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	spin_lock_irqsave(&parser->lock, flags);
18262306a36Sopenharmony_ci	memcpy(meter, &parser->meter, sizeof(*meter));
18362306a36Sopenharmony_ci	spin_unlock_irqrestore(&parser->lock, flags);
18462306a36Sopenharmony_ci}
185