424 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series4//5// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>6 7// Below models allow software to configure their DSP functions by asynchronous transaction8// to access their internal registers.9// * 828 mk210// * 896hd11// * Traveler12// * 8 pre13// * Ultralite14// * 4 pre15// * Audio Express16//17// Additionally, isochronous packets from the above models include messages to notify state of18// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user19// operates hardware components such as dial and switch, corresponding messages are transferred.20// The messages include Hardware metering and MIDI messages as well.21 22#include "motu.h"23 24#define MSG_FLAG_POS 425#define MSG_FLAG_TYPE_MASK 0xf826#define MSG_FLAG_MIDI_MASK 0x0127#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x0628#define MSG_FLAG_8PRE 0x0029#define MSG_FLAG_ULTRALITE 0x0430#define MSG_FLAG_TRAVELER 0x0431#define MSG_FLAG_828MK2 0x0432#define MSG_FLAG_896HD 0x0433#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte.34#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte.35#define MSG_FLAG_TYPE_SHIFT 336#define MSG_VALUE_POS 537#define MSG_MIDI_BYTE_POS 638#define MSG_METER_IDX_POS 739 40// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte41// is in 7th byte.42#define MSG_METER_IDX_POS_4PRE_AE 643#define MSG_MIDI_BYTE_POS_4PRE_AE 744#define MSG_FLAG_MIDI_POS_4PRE_AE 845 46enum register_dsp_msg_type {47 // Used for messages with no information.48 INVALID = 0x00,49 MIXER_SELECT = 0x01,50 MIXER_SRC_GAIN = 0x02,51 MIXER_SRC_PAN = 0x03,52 MIXER_SRC_FLAG = 0x04,53 MIXER_OUTPUT_PAIRED_VOLUME = 0x05,54 MIXER_OUTPUT_PAIRED_FLAG = 0x06,55 MAIN_OUTPUT_PAIRED_VOLUME = 0x07,56 HP_OUTPUT_PAIRED_VOLUME = 0x08,57 HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,58 // Transferred by all models but the purpose is still unknown.59 UNKNOWN_0 = 0x0a,60 // Specific to 828mk2, 896hd, Traveler.61 UNKNOWN_2 = 0x0c,62 // Specific to 828mk2, Traveler, and 896hd (not functional).63 LINE_INPUT_BOOST = 0x0d,64 // Specific to 828mk2, Traveler, and 896hd (not functional).65 LINE_INPUT_NOMINAL_LEVEL = 0x0e,66 // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).67 INPUT_GAIN_AND_INVERT = 0x15,68 // Specific to 4 pre, and Audio express.69 INPUT_FLAG = 0x16,70 // Specific to 4 pre, and Audio express.71 MIXER_SRC_PAIRED_BALANCE = 0x17,72 // Specific to 4 pre, and Audio express.73 MIXER_SRC_PAIRED_WIDTH = 0x18,74 // Transferred by all models. This type of message interposes the series of the other75 // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and76 // Traveler, one of physical outputs is selected for the message. The selection is done77 // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.78 METER = 0x1f,79};80 81#define EVENT_QUEUE_SIZE 1682 83struct msg_parser {84 spinlock_t lock;85 struct snd_firewire_motu_register_dsp_meter meter;86 bool meter_pos_quirk;87 88 struct snd_firewire_motu_register_dsp_parameter param;89 u8 prev_mixer_src_type;90 u8 mixer_ch;91 u8 mixer_src_ch;92 93 u8 input_ch;94 u8 prev_msg_type;95 96 u32 event_queue[EVENT_QUEUE_SIZE];97 unsigned int push_pos;98 unsigned int pull_pos;99};100 101int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)102{103 struct msg_parser *parser;104 parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);105 if (!parser)106 return -ENOMEM;107 spin_lock_init(&parser->lock);108 if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)109 parser->meter_pos_quirk = true;110 motu->message_parser = parser;111 return 0;112}113 114int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)115{116 struct msg_parser *parser = motu->message_parser;117 118 parser->prev_mixer_src_type = INVALID;119 parser->mixer_ch = 0xff;120 parser->mixer_src_ch = 0xff;121 parser->prev_msg_type = INVALID;122 123 return 0;124}125 126// Rough implementaion of queue without overrun check.127static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)128{129 struct msg_parser *parser = motu->message_parser;130 unsigned int pos = parser->push_pos;131 u32 entry;132 133 if (!motu->hwdep || motu->hwdep->used == 0)134 return;135 136 entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;137 parser->event_queue[pos] = entry;138 139 ++pos;140 if (pos >= EVENT_QUEUE_SIZE)141 pos = 0;142 parser->push_pos = pos;143}144 145void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,146 const struct pkt_desc *desc, unsigned int count)147{148 struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);149 unsigned int data_block_quadlets = s->data_block_quadlets;150 struct msg_parser *parser = motu->message_parser;151 bool meter_pos_quirk = parser->meter_pos_quirk;152 unsigned int pos = parser->push_pos;153 unsigned long flags;154 int i;155 156 spin_lock_irqsave(&parser->lock, flags);157 158 for (i = 0; i < count; ++i) {159 __be32 *buffer = desc->ctx_payload;160 unsigned int data_blocks = desc->data_blocks;161 int j;162 163 desc = amdtp_stream_next_packet_desc(s, desc);164 165 for (j = 0; j < data_blocks; ++j) {166 u8 *b = (u8 *)buffer;167 u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;168 u8 val = b[MSG_VALUE_POS];169 170 buffer += data_block_quadlets;171 172 switch (msg_type) {173 case MIXER_SELECT:174 {175 u8 mixer_ch = val / 0x20;176 if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {177 parser->mixer_src_ch = 0;178 parser->mixer_ch = mixer_ch;179 }180 break;181 }182 case MIXER_SRC_GAIN:183 case MIXER_SRC_PAN:184 case MIXER_SRC_FLAG:185 case MIXER_SRC_PAIRED_BALANCE:186 case MIXER_SRC_PAIRED_WIDTH:187 {188 struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;189 u8 mixer_ch = parser->mixer_ch;190 u8 mixer_src_ch = parser->mixer_src_ch;191 192 if (msg_type != parser->prev_mixer_src_type)193 mixer_src_ch = 0;194 else195 ++mixer_src_ch;196 parser->prev_mixer_src_type = msg_type;197 198 if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&199 mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {200 u8 mixer_ch = parser->mixer_ch;201 202 switch (msg_type) {203 case MIXER_SRC_GAIN:204 if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {205 queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);206 param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;207 }208 break;209 case MIXER_SRC_PAN:210 if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {211 queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);212 param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;213 }214 break;215 case MIXER_SRC_FLAG:216 if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {217 queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);218 param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;219 }220 break;221 case MIXER_SRC_PAIRED_BALANCE:222 if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {223 queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);224 param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;225 }226 break;227 case MIXER_SRC_PAIRED_WIDTH:228 if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {229 queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);230 param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;231 }232 break;233 default:234 break;235 }236 237 parser->mixer_src_ch = mixer_src_ch;238 }239 break;240 }241 case MIXER_OUTPUT_PAIRED_VOLUME:242 case MIXER_OUTPUT_PAIRED_FLAG:243 {244 struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;245 u8 mixer_ch = parser->mixer_ch;246 247 if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {248 switch (msg_type) {249 case MIXER_OUTPUT_PAIRED_VOLUME:250 if (param->mixer.output.paired_volume[mixer_ch] != val) {251 queue_event(motu, msg_type, mixer_ch, 0, val);252 param->mixer.output.paired_volume[mixer_ch] = val;253 }254 break;255 case MIXER_OUTPUT_PAIRED_FLAG:256 if (param->mixer.output.paired_flag[mixer_ch] != val) {257 queue_event(motu, msg_type, mixer_ch, 0, val);258 param->mixer.output.paired_flag[mixer_ch] = val;259 }260 break;261 default:262 break;263 }264 }265 break;266 }267 case MAIN_OUTPUT_PAIRED_VOLUME:268 if (parser->param.output.main_paired_volume != val) {269 queue_event(motu, msg_type, 0, 0, val);270 parser->param.output.main_paired_volume = val;271 }272 break;273 case HP_OUTPUT_PAIRED_VOLUME:274 if (parser->param.output.hp_paired_volume != val) {275 queue_event(motu, msg_type, 0, 0, val);276 parser->param.output.hp_paired_volume = val;277 }278 break;279 case HP_OUTPUT_PAIRED_ASSIGNMENT:280 if (parser->param.output.hp_paired_assignment != val) {281 queue_event(motu, msg_type, 0, 0, val);282 parser->param.output.hp_paired_assignment = val;283 }284 break;285 case LINE_INPUT_BOOST:286 if (parser->param.line_input.boost_flag != val) {287 queue_event(motu, msg_type, 0, 0, val);288 parser->param.line_input.boost_flag = val;289 }290 break;291 case LINE_INPUT_NOMINAL_LEVEL:292 if (parser->param.line_input.nominal_level_flag != val) {293 queue_event(motu, msg_type, 0, 0, val);294 parser->param.line_input.nominal_level_flag = val;295 }296 break;297 case INPUT_GAIN_AND_INVERT:298 case INPUT_FLAG:299 {300 struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;301 u8 input_ch = parser->input_ch;302 303 if (parser->prev_msg_type != msg_type)304 input_ch = 0;305 else306 ++input_ch;307 308 if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {309 switch (msg_type) {310 case INPUT_GAIN_AND_INVERT:311 if (param->input.gain_and_invert[input_ch] != val) {312 queue_event(motu, msg_type, input_ch, 0, val);313 param->input.gain_and_invert[input_ch] = val;314 }315 break;316 case INPUT_FLAG:317 if (param->input.flag[input_ch] != val) {318 queue_event(motu, msg_type, input_ch, 0, val);319 param->input.flag[input_ch] = val;320 }321 break;322 default:323 break;324 }325 parser->input_ch = input_ch;326 }327 break;328 }329 case UNKNOWN_0:330 case UNKNOWN_2:331 break;332 case METER:333 {334 u8 pos;335 336 if (!meter_pos_quirk)337 pos = b[MSG_METER_IDX_POS];338 else339 pos = b[MSG_METER_IDX_POS_4PRE_AE];340 341 if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {342 parser->meter.data[pos] = val;343 } else if (pos >= 0x80) {344 pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);345 346 if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)347 parser->meter.data[pos] = val;348 }349 350 // The message for meter is interruptible to the series of other351 // types of messages. Don't cache it.352 fallthrough;353 }354 case INVALID:355 default:356 // Don't cache it.357 continue;358 }359 360 parser->prev_msg_type = msg_type;361 }362 }363 364 if (pos != parser->push_pos)365 wake_up(&motu->hwdep_wait);366 367 spin_unlock_irqrestore(&parser->lock, flags);368}369 370void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,371 struct snd_firewire_motu_register_dsp_meter *meter)372{373 struct msg_parser *parser = motu->message_parser;374 unsigned long flags;375 376 spin_lock_irqsave(&parser->lock, flags);377 memcpy(meter, &parser->meter, sizeof(*meter));378 spin_unlock_irqrestore(&parser->lock, flags);379}380 381void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,382 struct snd_firewire_motu_register_dsp_parameter *param)383{384 struct msg_parser *parser = motu->message_parser;385 unsigned long flags;386 387 spin_lock_irqsave(&parser->lock, flags);388 memcpy(param, &parser->param, sizeof(*param));389 spin_unlock_irqrestore(&parser->lock, flags);390}391 392unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)393{394 struct msg_parser *parser = motu->message_parser;395 396 if (parser->pull_pos > parser->push_pos)397 return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;398 else399 return parser->push_pos - parser->pull_pos;400}401 402bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)403{404 struct msg_parser *parser = motu->message_parser;405 unsigned int pos = parser->pull_pos;406 unsigned long flags;407 408 if (pos == parser->push_pos)409 return false;410 411 spin_lock_irqsave(&parser->lock, flags);412 413 *event = parser->event_queue[pos];414 415 ++pos;416 if (pos >= EVENT_QUEUE_SIZE)417 pos = 0;418 parser->pull_pos = pos;419 420 spin_unlock_irqrestore(&parser->lock, flags);421 422 return true;423}424