18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * digi00x.c - a part of driver for Digidesign Digi 002/003 family
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "digi00x.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define VENDOR_DIGIDESIGN	0x00a07e
158c2ecf20Sopenharmony_ci#define MODEL_CONSOLE		0x000001
168c2ecf20Sopenharmony_ci#define MODEL_RACK		0x000002
178c2ecf20Sopenharmony_ci#define SPEC_VERSION		0x000001
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int name_card(struct snd_dg00x *dg00x)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
228c2ecf20Sopenharmony_ci	char name[32] = {0};
238c2ecf20Sopenharmony_ci	char *model;
248c2ecf20Sopenharmony_ci	int err;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
278c2ecf20Sopenharmony_ci			    sizeof(name));
288c2ecf20Sopenharmony_ci	if (err < 0)
298c2ecf20Sopenharmony_ci		return err;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	model = skip_spaces(name);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	strcpy(dg00x->card->driver, "Digi00x");
348c2ecf20Sopenharmony_ci	strcpy(dg00x->card->shortname, model);
358c2ecf20Sopenharmony_ci	strcpy(dg00x->card->mixername, model);
368c2ecf20Sopenharmony_ci	snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
378c2ecf20Sopenharmony_ci		 "Digidesign %s, GUID %08x%08x at %s, S%d", model,
388c2ecf20Sopenharmony_ci		 fw_dev->config_rom[3], fw_dev->config_rom[4],
398c2ecf20Sopenharmony_ci		 dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void dg00x_card_free(struct snd_card *card)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct snd_dg00x *dg00x = card->private_data;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	snd_dg00x_stream_destroy_duplex(dg00x);
498c2ecf20Sopenharmony_ci	snd_dg00x_transaction_unregister(dg00x);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void do_registration(struct work_struct *work)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct snd_dg00x *dg00x =
558c2ecf20Sopenharmony_ci			container_of(work, struct snd_dg00x, dwork.work);
568c2ecf20Sopenharmony_ci	int err;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (dg00x->registered)
598c2ecf20Sopenharmony_ci		return;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
628c2ecf20Sopenharmony_ci			   &dg00x->card);
638c2ecf20Sopenharmony_ci	if (err < 0)
648c2ecf20Sopenharmony_ci		return;
658c2ecf20Sopenharmony_ci	dg00x->card->private_free = dg00x_card_free;
668c2ecf20Sopenharmony_ci	dg00x->card->private_data = dg00x;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	err = name_card(dg00x);
698c2ecf20Sopenharmony_ci	if (err < 0)
708c2ecf20Sopenharmony_ci		goto error;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	err = snd_dg00x_stream_init_duplex(dg00x);
738c2ecf20Sopenharmony_ci	if (err < 0)
748c2ecf20Sopenharmony_ci		goto error;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	snd_dg00x_proc_init(dg00x);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	err = snd_dg00x_create_pcm_devices(dg00x);
798c2ecf20Sopenharmony_ci	if (err < 0)
808c2ecf20Sopenharmony_ci		goto error;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	err = snd_dg00x_create_midi_devices(dg00x);
838c2ecf20Sopenharmony_ci	if (err < 0)
848c2ecf20Sopenharmony_ci		goto error;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	err = snd_dg00x_create_hwdep_device(dg00x);
878c2ecf20Sopenharmony_ci	if (err < 0)
888c2ecf20Sopenharmony_ci		goto error;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	err = snd_dg00x_transaction_register(dg00x);
918c2ecf20Sopenharmony_ci	if (err < 0)
928c2ecf20Sopenharmony_ci		goto error;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	err = snd_card_register(dg00x->card);
958c2ecf20Sopenharmony_ci	if (err < 0)
968c2ecf20Sopenharmony_ci		goto error;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	dg00x->registered = true;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return;
1018c2ecf20Sopenharmony_cierror:
1028c2ecf20Sopenharmony_ci	snd_card_free(dg00x->card);
1038c2ecf20Sopenharmony_ci	dev_info(&dg00x->unit->device,
1048c2ecf20Sopenharmony_ci		 "Sound card registration failed: %d\n", err);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int snd_dg00x_probe(struct fw_unit *unit,
1088c2ecf20Sopenharmony_ci			   const struct ieee1394_device_id *entry)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct snd_dg00x *dg00x;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Allocate this independent of sound card instance. */
1138c2ecf20Sopenharmony_ci	dg00x = devm_kzalloc(&unit->device, sizeof(struct snd_dg00x),
1148c2ecf20Sopenharmony_ci			     GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	if (!dg00x)
1168c2ecf20Sopenharmony_ci		return -ENOMEM;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	dg00x->unit = fw_unit_get(unit);
1198c2ecf20Sopenharmony_ci	dev_set_drvdata(&unit->device, dg00x);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	mutex_init(&dg00x->mutex);
1228c2ecf20Sopenharmony_ci	spin_lock_init(&dg00x->lock);
1238c2ecf20Sopenharmony_ci	init_waitqueue_head(&dg00x->hwdep_wait);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	dg00x->is_console = entry->model_id == MODEL_CONSOLE;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Allocate and register this sound card later. */
1288c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
1298c2ecf20Sopenharmony_ci	snd_fw_schedule_registration(unit, &dg00x->dwork);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void snd_dg00x_update(struct fw_unit *unit)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Postpone a workqueue for deferred registration. */
1398c2ecf20Sopenharmony_ci	if (!dg00x->registered)
1408c2ecf20Sopenharmony_ci		snd_fw_schedule_registration(unit, &dg00x->dwork);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	snd_dg00x_transaction_reregister(dg00x);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci	 * After registration, userspace can start packet streaming, then this
1468c2ecf20Sopenharmony_ci	 * code block works fine.
1478c2ecf20Sopenharmony_ci	 */
1488c2ecf20Sopenharmony_ci	if (dg00x->registered) {
1498c2ecf20Sopenharmony_ci		mutex_lock(&dg00x->mutex);
1508c2ecf20Sopenharmony_ci		snd_dg00x_stream_update_duplex(dg00x);
1518c2ecf20Sopenharmony_ci		mutex_unlock(&dg00x->mutex);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void snd_dg00x_remove(struct fw_unit *unit)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/*
1608c2ecf20Sopenharmony_ci	 * Confirm to stop the work for registration before the sound card is
1618c2ecf20Sopenharmony_ci	 * going to be released. The work is not scheduled again because bus
1628c2ecf20Sopenharmony_ci	 * reset handler is not called anymore.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&dg00x->dwork);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (dg00x->registered) {
1678c2ecf20Sopenharmony_ci		// Block till all of ALSA character devices are released.
1688c2ecf20Sopenharmony_ci		snd_card_free(dg00x->card);
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	mutex_destroy(&dg00x->mutex);
1728c2ecf20Sopenharmony_ci	fw_unit_put(dg00x->unit);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic const struct ieee1394_device_id snd_dg00x_id_table[] = {
1768c2ecf20Sopenharmony_ci	/* Both of 002/003 use the same ID. */
1778c2ecf20Sopenharmony_ci	{
1788c2ecf20Sopenharmony_ci		.match_flags = IEEE1394_MATCH_VENDOR_ID |
1798c2ecf20Sopenharmony_ci			       IEEE1394_MATCH_VERSION |
1808c2ecf20Sopenharmony_ci			       IEEE1394_MATCH_MODEL_ID,
1818c2ecf20Sopenharmony_ci		.vendor_id = VENDOR_DIGIDESIGN,
1828c2ecf20Sopenharmony_ci		.version = SPEC_VERSION,
1838c2ecf20Sopenharmony_ci		.model_id = MODEL_CONSOLE,
1848c2ecf20Sopenharmony_ci	},
1858c2ecf20Sopenharmony_ci	{
1868c2ecf20Sopenharmony_ci		.match_flags = IEEE1394_MATCH_VENDOR_ID |
1878c2ecf20Sopenharmony_ci			       IEEE1394_MATCH_VERSION |
1888c2ecf20Sopenharmony_ci			       IEEE1394_MATCH_MODEL_ID,
1898c2ecf20Sopenharmony_ci		.vendor_id = VENDOR_DIGIDESIGN,
1908c2ecf20Sopenharmony_ci		.version = SPEC_VERSION,
1918c2ecf20Sopenharmony_ci		.model_id = MODEL_RACK,
1928c2ecf20Sopenharmony_ci	},
1938c2ecf20Sopenharmony_ci	{}
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic struct fw_driver dg00x_driver = {
1988c2ecf20Sopenharmony_ci	.driver = {
1998c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
2008c2ecf20Sopenharmony_ci		.name = KBUILD_MODNAME,
2018c2ecf20Sopenharmony_ci		.bus = &fw_bus_type,
2028c2ecf20Sopenharmony_ci	},
2038c2ecf20Sopenharmony_ci	.probe    = snd_dg00x_probe,
2048c2ecf20Sopenharmony_ci	.update   = snd_dg00x_update,
2058c2ecf20Sopenharmony_ci	.remove   = snd_dg00x_remove,
2068c2ecf20Sopenharmony_ci	.id_table = snd_dg00x_id_table,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int __init snd_dg00x_init(void)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	return driver_register(&dg00x_driver.driver);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void __exit snd_dg00x_exit(void)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	driver_unregister(&dg00x_driver.driver);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cimodule_init(snd_dg00x_init);
2208c2ecf20Sopenharmony_cimodule_exit(snd_dg00x_exit);
221