162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * motu.c - a part of driver for MOTU FireWire series
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "motu.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define OUI_MOTU	0x0001f2
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciMODULE_DESCRIPTION("MOTU FireWire driver");
1362306a36Sopenharmony_ciMODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
1462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciconst unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
1762306a36Sopenharmony_ci	/* mode 0 */
1862306a36Sopenharmony_ci	[0] =  44100,
1962306a36Sopenharmony_ci	[1] =  48000,
2062306a36Sopenharmony_ci	/* mode 1 */
2162306a36Sopenharmony_ci	[2] =  88200,
2262306a36Sopenharmony_ci	[3] =  96000,
2362306a36Sopenharmony_ci	/* mode 2 */
2462306a36Sopenharmony_ci	[4] = 176400,
2562306a36Sopenharmony_ci	[5] = 192000,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void name_card(struct snd_motu *motu)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct fw_device *fw_dev = fw_parent_device(motu->unit);
3162306a36Sopenharmony_ci	struct fw_csr_iterator it;
3262306a36Sopenharmony_ci	int key, val;
3362306a36Sopenharmony_ci	u32 version = 0;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	fw_csr_iterator_init(&it, motu->unit->directory);
3662306a36Sopenharmony_ci	while (fw_csr_iterator_next(&it, &key, &val)) {
3762306a36Sopenharmony_ci		switch (key) {
3862306a36Sopenharmony_ci		case CSR_MODEL:
3962306a36Sopenharmony_ci			version = val;
4062306a36Sopenharmony_ci			break;
4162306a36Sopenharmony_ci		}
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	strcpy(motu->card->driver, "FW-MOTU");
4562306a36Sopenharmony_ci	strcpy(motu->card->shortname, motu->spec->name);
4662306a36Sopenharmony_ci	strcpy(motu->card->mixername, motu->spec->name);
4762306a36Sopenharmony_ci	snprintf(motu->card->longname, sizeof(motu->card->longname),
4862306a36Sopenharmony_ci		 "MOTU %s (version:%06x), GUID %08x%08x at %s, S%d",
4962306a36Sopenharmony_ci		 motu->spec->name, version,
5062306a36Sopenharmony_ci		 fw_dev->config_rom[3], fw_dev->config_rom[4],
5162306a36Sopenharmony_ci		 dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void motu_card_free(struct snd_card *card)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct snd_motu *motu = card->private_data;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	snd_motu_transaction_unregister(motu);
5962306a36Sopenharmony_ci	snd_motu_stream_destroy_duplex(motu);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	mutex_destroy(&motu->mutex);
6262306a36Sopenharmony_ci	fw_unit_put(motu->unit);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct snd_card *card;
6862306a36Sopenharmony_ci	struct snd_motu *motu;
6962306a36Sopenharmony_ci	int err;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
7262306a36Sopenharmony_ci	if (err < 0)
7362306a36Sopenharmony_ci		return err;
7462306a36Sopenharmony_ci	card->private_free = motu_card_free;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	motu = card->private_data;
7762306a36Sopenharmony_ci	motu->unit = fw_unit_get(unit);
7862306a36Sopenharmony_ci	dev_set_drvdata(&unit->device, motu);
7962306a36Sopenharmony_ci	motu->card = card;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	motu->spec = (const struct snd_motu_spec *)entry->driver_data;
8262306a36Sopenharmony_ci	mutex_init(&motu->mutex);
8362306a36Sopenharmony_ci	spin_lock_init(&motu->lock);
8462306a36Sopenharmony_ci	init_waitqueue_head(&motu->hwdep_wait);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	name_card(motu);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	err = snd_motu_transaction_register(motu);
8962306a36Sopenharmony_ci	if (err < 0)
9062306a36Sopenharmony_ci		goto error;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	err = snd_motu_stream_init_duplex(motu);
9362306a36Sopenharmony_ci	if (err < 0)
9462306a36Sopenharmony_ci		goto error;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	snd_motu_proc_init(motu);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	err = snd_motu_create_pcm_devices(motu);
9962306a36Sopenharmony_ci	if (err < 0)
10062306a36Sopenharmony_ci		goto error;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
10362306a36Sopenharmony_ci	    (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) ||
10462306a36Sopenharmony_ci	    (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
10562306a36Sopenharmony_ci	    (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) {
10662306a36Sopenharmony_ci		err = snd_motu_create_midi_devices(motu);
10762306a36Sopenharmony_ci		if (err < 0)
10862306a36Sopenharmony_ci			goto error;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	err = snd_motu_create_hwdep_device(motu);
11262306a36Sopenharmony_ci	if (err < 0)
11362306a36Sopenharmony_ci		goto error;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
11662306a36Sopenharmony_ci		err = snd_motu_register_dsp_message_parser_new(motu);
11762306a36Sopenharmony_ci		if (err < 0)
11862306a36Sopenharmony_ci			goto error;
11962306a36Sopenharmony_ci	} else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
12062306a36Sopenharmony_ci		err = snd_motu_command_dsp_message_parser_new(motu);
12162306a36Sopenharmony_ci		if (err < 0)
12262306a36Sopenharmony_ci			goto error;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	err = snd_card_register(card);
12662306a36Sopenharmony_ci	if (err < 0)
12762306a36Sopenharmony_ci		goto error;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_cierror:
13162306a36Sopenharmony_ci	snd_card_free(card);
13262306a36Sopenharmony_ci	return err;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void motu_remove(struct fw_unit *unit)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct snd_motu *motu = dev_get_drvdata(&unit->device);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	// Block till all of ALSA character devices are released.
14062306a36Sopenharmony_ci	snd_card_free(motu->card);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void motu_bus_update(struct fw_unit *unit)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct snd_motu *motu = dev_get_drvdata(&unit->device);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* The handler address register becomes initialized. */
14862306a36Sopenharmony_ci	snd_motu_transaction_reregister(motu);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#define SND_MOTU_DEV_ENTRY(model, data)			\
15262306a36Sopenharmony_ci{							\
15362306a36Sopenharmony_ci	.match_flags	= IEEE1394_MATCH_VENDOR_ID |	\
15462306a36Sopenharmony_ci			  IEEE1394_MATCH_SPECIFIER_ID |	\
15562306a36Sopenharmony_ci			  IEEE1394_MATCH_VERSION,	\
15662306a36Sopenharmony_ci	.vendor_id	= OUI_MOTU,			\
15762306a36Sopenharmony_ci	.specifier_id	= OUI_MOTU,			\
15862306a36Sopenharmony_ci	.version	= model,			\
15962306a36Sopenharmony_ci	.driver_data	= (kernel_ulong_t)data,		\
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic const struct ieee1394_device_id motu_id_table[] = {
16362306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828),
16462306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896),
16562306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
16662306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000005, &snd_motu_spec_896hd),
16762306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
16862306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
16962306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
17062306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
17162306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
17262306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3),
17362306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
17462306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
17562306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
17662306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16),
17762306a36Sopenharmony_ci	SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
17862306a36Sopenharmony_ci	{ }
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, motu_id_table);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct fw_driver motu_driver = {
18362306a36Sopenharmony_ci	.driver   = {
18462306a36Sopenharmony_ci		.owner	= THIS_MODULE,
18562306a36Sopenharmony_ci		.name	= KBUILD_MODNAME,
18662306a36Sopenharmony_ci		.bus	= &fw_bus_type,
18762306a36Sopenharmony_ci	},
18862306a36Sopenharmony_ci	.probe    = motu_probe,
18962306a36Sopenharmony_ci	.update   = motu_bus_update,
19062306a36Sopenharmony_ci	.remove   = motu_remove,
19162306a36Sopenharmony_ci	.id_table = motu_id_table,
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int __init alsa_motu_init(void)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return driver_register(&motu_driver.driver);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void __exit alsa_motu_exit(void)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	driver_unregister(&motu_driver.driver);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cimodule_init(alsa_motu_init);
20562306a36Sopenharmony_cimodule_exit(alsa_motu_exit);
206