summaryrefslogtreecommitdiff
path: root/sound/usb/line6/variax.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 08:51:59 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 08:51:59 -0800
commita323ae93a74f669d890926187c68c711895e3454 (patch)
tree9a4ab8ed7bb98dc4321606332a883834ef7c8f6f /sound/usb/line6/variax.c
parent3e63430a5cc26bc90a6e33ab33f901196b7b63ac (diff)
parent0e806151e86be52caa1349fa490eab8f09a2b6f5 (diff)
Merge tag 'sound-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "In this batch, you can find lots of cleanups through the whole subsystem, as our good New Year's resolution. Lots of LOCs and commits are about LINE6 driver that was promoted finally from staging tree, and as usual, there've been widely spread ASoC changes. Here some highlights: ALSA core changes - Embedding struct device into ALSA core structures - sequencer core cleanups / fixes - PCM msbits constraints cleanups / fixes - New SNDRV_PCM_TRIGGER_DRAIN command - PCM kerneldoc fixes, header cleanups - PCM code cleanups using more standard codes - Control notification ID fixes Driver cleanups - Cleanups of PCI PM callbacks - Timer helper usages cleanups - Simplification (e.g. argument reduction) of many driver codes HD-audio - Hotkey and LED support on HP laptops with Realtek codecs - Dock station support on HP laptops - Toshiba Satellite S50D fixup - Enhanced wallclock timestamp handling for HD-audio - Componentization to simplify the linkage between i915 and hd-audio drivers for Intel HDMI/DP USB-audio - Akai MPC Element support - Enhanced timestamp handling ASoC - Lots of refactoringin ASoC core, moving drivers to more data driven initialization and rationalizing a lot of DAPM usage - Much improved handling of CDCLK clocks on Samsung I2S controllers - Lots of driver specific cleanups and feature improvements - CODEC support for TI PCM514x and TLV320AIC3104 devices - Board support for Tegra systems with Realtek RT5677 - New driver for Maxim max98357a - More enhancements / fixes for Intel SST driver Others - Promotion of LINE6 driver from staging along with lots of rewrites and cleanups - DT support for old non-ASoC atmel driver - oxygen cleanups, XIO2001 init, Studio Evolution SE6x support - Emu8000 DRAM size detection fix on ISA(!!) AWE64 boards - A few more ak411x fixes for ice1724 boards" * tag 'sound-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (542 commits) ALSA: line6: toneport: Use explicit type for firmware version ALSA: line6: Use explicit type for serial number ALSA: line6: Return EIO if read/write not successful ALSA: line6: Return error if device not responding ALSA: line6: Add delay before reading status ASoC: Intel: Clean data after SST fw fetch ALSA: hda - Add docking station support for another HP machine ALSA: control: fix failure to return new numerical ID in 'replace' event data ALSA: usb: update trigger timestamp on first non-zero URB submitted ALSA: hda: read trigger_timestamp immediately after starting DMA ALSA: pcm: allow for trigger_tstamp snapshot in .trigger ALSA: pcm: don't override timestamp unconditionally ALSA: off by one bug in snd_riptide_joystick_probe() ASoC: rt5670: Set use_single_rw flag for regmap ASoC: rt286: Add rt288 codec support ASoC: max98357a: Fix build in !CONFIG_OF case ASoC: Intel: fix platform_no_drv_owner.cocci warnings ARM: dts: Switch Odroid X2/U2 to simple-audio-card ARM: dts: Exynos4 and Odroid X2/U3 sound device nodes update ALSA: control: fix failure to return numerical ID in 'add' event ...
Diffstat (limited to 'sound/usb/line6/variax.c')
-rw-r--r--sound/usb/line6/variax.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
new file mode 100644
index 000000000000..ddc23ddf0750
--- /dev/null
+++ b/sound/usb/line6/variax.c
@@ -0,0 +1,306 @@
+/*
+ * Line 6 Linux USB driver
+ *
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <sound/core.h>
+
+#include "driver.h"
+
+#define VARIAX_STARTUP_DELAY1 1000
+#define VARIAX_STARTUP_DELAY3 100
+#define VARIAX_STARTUP_DELAY4 100
+
+/*
+ Stages of Variax startup procedure
+*/
+enum {
+ VARIAX_STARTUP_INIT = 1,
+ VARIAX_STARTUP_VERSIONREQ,
+ VARIAX_STARTUP_WAIT,
+ VARIAX_STARTUP_ACTIVATE,
+ VARIAX_STARTUP_WORKQUEUE,
+ VARIAX_STARTUP_SETUP,
+ VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1
+};
+
+enum {
+ LINE6_PODXTLIVE_VARIAX,
+ LINE6_VARIAX
+};
+
+struct usb_line6_variax {
+ /* Generic Line 6 USB data */
+ struct usb_line6 line6;
+
+ /* Buffer for activation code */
+ unsigned char *buffer_activate;
+
+ /* Handler for device initialization */
+ struct work_struct startup_work;
+
+ /* Timers for device initialization */
+ struct timer_list startup_timer1;
+ struct timer_list startup_timer2;
+
+ /* Current progress in startup procedure */
+ int startup_progress;
+};
+
+#define VARIAX_OFFSET_ACTIVATE 7
+
+/*
+ This message is sent by the device during initialization and identifies
+ the connected guitar version.
+*/
+static const char variax_init_version[] = {
+ 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
+ 0x07, 0x00, 0x00, 0x00
+};
+
+/*
+ This message is the last one sent by the device during initialization.
+*/
+static const char variax_init_done[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
+};
+
+static const char variax_activate[] = {
+ 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
+ 0xf7
+};
+
+/* forward declarations: */
+static void variax_startup2(unsigned long data);
+static void variax_startup4(unsigned long data);
+static void variax_startup5(unsigned long data);
+
+static void variax_activate_async(struct usb_line6_variax *variax, int a)
+{
+ variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
+ line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
+ sizeof(variax_activate));
+}
+
+/*
+ Variax startup procedure.
+ This is a sequence of functions with special requirements (e.g., must
+ not run immediately after initialization, must not run in interrupt
+ context). After the last one has finished, the device is ready to use.
+*/
+
+static void variax_startup1(struct usb_line6_variax *variax)
+{
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
+ variax_startup2, (unsigned long)variax);
+}
+
+static void variax_startup2(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+ struct usb_line6 *line6 = &variax->line6;
+
+ /* schedule another startup procedure until startup is complete: */
+ if (variax->startup_progress >= VARIAX_STARTUP_LAST)
+ return;
+
+ variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
+ line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
+ variax_startup2, (unsigned long)variax);
+
+ /* request firmware version: */
+ line6_version_request_async(line6);
+}
+
+static void variax_startup3(struct usb_line6_variax *variax)
+{
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
+ variax_startup4, (unsigned long)variax);
+}
+
+static void variax_startup4(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+
+ CHECK_STARTUP_PROGRESS(variax->startup_progress,
+ VARIAX_STARTUP_ACTIVATE);
+
+ /* activate device: */
+ variax_activate_async(variax, 1);
+ line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
+ variax_startup5, (unsigned long)variax);
+}
+
+static void variax_startup5(unsigned long data)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
+
+ CHECK_STARTUP_PROGRESS(variax->startup_progress,
+ VARIAX_STARTUP_WORKQUEUE);
+
+ /* schedule work for global work queue: */
+ schedule_work(&variax->startup_work);
+}
+
+static void variax_startup6(struct work_struct *work)
+{
+ struct usb_line6_variax *variax =
+ container_of(work, struct usb_line6_variax, startup_work);
+
+ CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
+
+ /* ALSA audio interface: */
+ snd_card_register(variax->line6.card);
+}
+
+/*
+ Process a completely received message.
+*/
+static void line6_variax_process_message(struct usb_line6 *line6)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
+ const unsigned char *buf = variax->line6.buffer_message;
+
+ switch (buf[0]) {
+ case LINE6_RESET:
+ dev_info(variax->line6.ifcdev, "VARIAX reset\n");
+ break;
+
+ case LINE6_SYSEX_BEGIN:
+ if (memcmp(buf + 1, variax_init_version + 1,
+ sizeof(variax_init_version) - 1) == 0) {
+ variax_startup3(variax);
+ } else if (memcmp(buf + 1, variax_init_done + 1,
+ sizeof(variax_init_done) - 1) == 0) {
+ /* notify of complete initialization: */
+ variax_startup4((unsigned long)variax);
+ }
+ break;
+ }
+}
+
+/*
+ Variax destructor.
+*/
+static void line6_variax_disconnect(struct usb_line6 *line6)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *)line6;
+
+ del_timer(&variax->startup_timer1);
+ del_timer(&variax->startup_timer2);
+ cancel_work_sync(&variax->startup_work);
+
+ kfree(variax->buffer_activate);
+}
+
+/*
+ Try to init workbench device.
+*/
+static int variax_init(struct usb_line6 *line6,
+ const struct usb_device_id *id)
+{
+ struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
+ int err;
+
+ line6->process_message = line6_variax_process_message;
+ line6->disconnect = line6_variax_disconnect;
+
+ init_timer(&variax->startup_timer1);
+ init_timer(&variax->startup_timer2);
+ INIT_WORK(&variax->startup_work, variax_startup6);
+
+ /* initialize USB buffers: */
+ variax->buffer_activate = kmemdup(variax_activate,
+ sizeof(variax_activate), GFP_KERNEL);
+
+ if (variax->buffer_activate == NULL)
+ return -ENOMEM;
+
+ /* initialize MIDI subsystem: */
+ err = line6_init_midi(&variax->line6);
+ if (err < 0)
+ return err;
+
+ /* initiate startup procedure: */
+ variax_startup1(variax);
+ return 0;
+}
+
+#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
+#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
+
+/* table of devices that work with this driver */
+static const struct usb_device_id variax_id_table[] = {
+ { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
+ { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, variax_id_table);
+
+static const struct line6_properties variax_properties_table[] = {
+ [LINE6_PODXTLIVE_VARIAX] = {
+ .id = "PODxtLive",
+ .name = "PODxt Live",
+ .capabilities = LINE6_CAP_CONTROL,
+ .altsetting = 1,
+ .ep_ctrl_r = 0x86,
+ .ep_ctrl_w = 0x05,
+ .ep_audio_r = 0x82,
+ .ep_audio_w = 0x01,
+ },
+ [LINE6_VARIAX] = {
+ .id = "Variax",
+ .name = "Variax Workbench",
+ .capabilities = LINE6_CAP_CONTROL,
+ .altsetting = 1,
+ .ep_ctrl_r = 0x82,
+ .ep_ctrl_w = 0x01,
+ /* no audio channel */
+ }
+};
+
+/*
+ Probe USB device.
+*/
+static int variax_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return line6_probe(interface, id, "Line6-Variax",
+ &variax_properties_table[id->driver_info],
+ variax_init, sizeof(struct usb_line6_variax));
+}
+
+static struct usb_driver variax_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = variax_probe,
+ .disconnect = line6_disconnect,
+#ifdef CONFIG_PM
+ .suspend = line6_suspend,
+ .resume = line6_resume,
+ .reset_resume = line6_resume,
+#endif
+ .id_table = variax_id_table,
+};
+
+module_usb_driver(variax_driver);
+
+MODULE_DESCRIPTION("Vairax Workbench USB driver");
+MODULE_LICENSE("GPL");