summaryrefslogtreecommitdiff
path: root/sound/firewire/fireface/ff.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 11:58:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 11:58:59 -0700
commit221656e7c4ce342b99c31eca96c1cbb6d1dce45f (patch)
tree1af3655475361f3ec75d8e26bc8d5f4bad8a87b0 /sound/firewire/fireface/ff.c
parent2f34c1231bfc9f2550f934acb268ac7315fb3837 (diff)
parenta5c3b32a1146e44f6b38fdfdfffc27842953420c (diff)
Merge tag 'sound-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "It was a relatively calm development cycle, and no scaring changes are seen in both core and driver sides. Here are some highlights: ASoC: - A new API for hooking up jacks more generically and easily - Card longname is set based on DMI for a unique UCM profile - Lots of Intel driver fixes: Atom, Broxton, Skylake and newer chips - New drivers for Cirrus CS35L35, DIO DIO2125, Everest ES7132, HiSilicon hi6210, Maxim MAX98927, MT2701 systems with WM8960, Nuvoton NAU8824, Odroid systems, ST STM32 SAI controllers and x86 systems with DA7213 HD-audio: - Many new quirks to support headset for various devices (mostly ASUS ones) as usual - Support for dual codecs on some Gigabyte mobos and Lenovo laptop - Improvement on PCM position reporting for Skylake and newer FireWire: - New drivers for MOTU and RME Fireface series - Updates for Digidesign Digi00x and TASCAM series - Support for tracepoints Others: - USB-audio: improved support for quirk_alias option - Cleanups, constification allover the places" * tag 'sound-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (299 commits) ASoC: codec: wm8960: Relax bit clock computation when using PLL ASoC: codec: wm9860: avoid maybe-uninitialized warning ASoC: nau8824: leave Class D gain at chip default ASoC: nau8824: rename controls to match DAPM controls ASoC: Intel: Skylake: Return negative error code ASoC: Intel: Skylake: Fix unused variable warning ASoC: Intel: Skylake: fix uninitialized pointer use ASoC: sti: Fix error handling if of_clk_get() fails ASoC: cs4271: configure reset GPIO as output ASoC: dwc: Disallow building designware_pcm as a module ALSA: ali5451: fix spelling mistake in "ali_capture_preapre" ASoC: stm32: add SAI driver ASoC: stm32: add bindings for SAI ASoC: Intel: Skylake: Add loadable module support on KBL platform ASoC: Intel: Skylake: Modify load_lib_ipc arguments for a nowait version ASoC: Intel: Skylake: Register dsp_fw_ops for kabylake ASoC: Intel: Skylake: Modify arguments to reuse module transfer function ASoC: Intel: Skylake: Commonize library load ASoC: Intel: Skylake: Move sst common initialization to a helper function ASoC: nau8824: new driver ...
Diffstat (limited to 'sound/firewire/fireface/ff.c')
-rw-r--r--sound/firewire/fireface/ff.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
new file mode 100644
index 000000000000..eee7c8eac7a6
--- /dev/null
+++ b/sound/firewire/fireface/ff.c
@@ -0,0 +1,209 @@
+/*
+ * ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+#define OUI_RME 0x000a35
+
+MODULE_DESCRIPTION("RME Fireface series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static void name_card(struct snd_ff *ff)
+{
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+
+ strcpy(ff->card->driver, "Fireface");
+ strcpy(ff->card->shortname, ff->spec->name);
+ strcpy(ff->card->mixername, ff->spec->name);
+ snprintf(ff->card->longname, sizeof(ff->card->longname),
+ "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void ff_free(struct snd_ff *ff)
+{
+ snd_ff_stream_destroy_duplex(ff);
+ snd_ff_transaction_unregister(ff);
+
+ fw_unit_put(ff->unit);
+
+ mutex_destroy(&ff->mutex);
+ kfree(ff);
+}
+
+static void ff_card_free(struct snd_card *card)
+{
+ ff_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
+ int err;
+
+ if (ff->registered)
+ return;
+
+ err = snd_card_new(&ff->unit->device, -1, NULL, THIS_MODULE, 0,
+ &ff->card);
+ if (err < 0)
+ return;
+
+ err = snd_ff_transaction_register(ff);
+ if (err < 0)
+ goto error;
+
+ name_card(ff);
+
+ err = snd_ff_stream_init_duplex(ff);
+ if (err < 0)
+ goto error;
+
+ snd_ff_proc_init(ff);
+
+ err = snd_ff_create_midi_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_pcm_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_hwdep_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(ff->card);
+ if (err < 0)
+ goto error;
+
+ ff->card->private_free = ff_card_free;
+ ff->card->private_data = ff;
+ ff->registered = true;
+
+ return;
+error:
+ snd_ff_transaction_unregister(ff);
+ snd_ff_stream_destroy_duplex(ff);
+ snd_card_free(ff->card);
+ dev_info(&ff->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_ff_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_ff *ff;
+
+ ff = kzalloc(sizeof(struct snd_ff), GFP_KERNEL);
+ if (ff == NULL)
+ return -ENOMEM;
+
+ /* initialize myself */
+ ff->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, ff);
+
+ mutex_init(&ff->mutex);
+ spin_lock_init(&ff->lock);
+ init_waitqueue_head(&ff->hwdep_wait);
+
+ ff->spec = (const struct snd_ff_spec *)entry->driver_data;
+
+ /* Register this sound card later. */
+ INIT_DEFERRABLE_WORK(&ff->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &ff->dwork);
+
+ return 0;
+}
+
+static void snd_ff_update(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ /* Postpone a workqueue for deferred registration. */
+ if (!ff->registered)
+ snd_fw_schedule_registration(unit, &ff->dwork);
+
+ snd_ff_transaction_reregister(ff);
+
+ if (ff->registered)
+ snd_ff_stream_update_duplex(ff);
+}
+
+static void snd_ff_remove(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_work_sync(&ff->dwork.work);
+
+ if (ff->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(ff->card);
+ } else {
+ /* Don't forget this case. */
+ ff_free(ff);
+ }
+}
+
+static struct snd_ff_spec spec_ff400 = {
+ .name = "Fireface400",
+ .pcm_capture_channels = {18, 14, 10},
+ .pcm_playback_channels = {18, 14, 10},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_ff400,
+};
+
+static const struct ieee1394_device_id snd_ff_id_table[] = {
+ /* Fireface 400 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = 0x000a35,
+ .version = 0x000002,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff400,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
+
+static struct fw_driver ff_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-fireface",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_ff_probe,
+ .update = snd_ff_update,
+ .remove = snd_ff_remove,
+ .id_table = snd_ff_id_table,
+};
+
+static int __init snd_ff_init(void)
+{
+ return driver_register(&ff_driver.driver);
+}
+
+static void __exit snd_ff_exit(void)
+{
+ driver_unregister(&ff_driver.driver);
+}
+
+module_init(snd_ff_init);
+module_exit(snd_ff_exit);