]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/line6/toneport.c
Merge branch 'for-2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc...
[mv-sheeva.git] / drivers / staging / line6 / toneport.c
index e6770ea179364e8579d7646d72fec4c0379c64bf..6a10b0f9749a4cee25e54a717072b734a67e5baf 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Line6 Linux USB driver - 0.8.0
+ * Line6 Linux USB driver - 0.9.1beta
  *
- * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
  *                         Emil Myhrman (emil.myhrman@gmail.com)
  *
  *     This program is free software; you can redistribute it and/or
  *
  */
 
-#include "driver.h"
+#include <linux/wait.h>
+#include <sound/control.h>
 
 #include "audio.h"
 #include "capture.h"
+#include "driver.h"
 #include "playback.h"
 #include "toneport.h"
 
 static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
 
+#define TONEPORT_PCM_DELAY 1
+
 static struct snd_ratden toneport_ratden = {
        .num_min = 44100,
        .num_max = 44100,
@@ -33,6 +37,9 @@ static struct line6_pcm_properties toneport_pcm_properties = {
                                           SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                           SNDRV_PCM_INFO_MMAP_VALID |
                                           SNDRV_PCM_INFO_PAUSE |
+#ifdef CONFIG_PM
+                                          SNDRV_PCM_INFO_RESUME |
+#endif
                                           SNDRV_PCM_INFO_SYNC_START),
                                  .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                  .rates = SNDRV_PCM_RATE_KNOT,
@@ -41,7 +48,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
                                  .channels_min = 2,
                                  .channels_max = 2,
                                  .buffer_bytes_max = 60000,
-                                 .period_bytes_min = 180 * 4,
+                                 .period_bytes_min = 64,
                                  .period_bytes_max = 8192,
                                  .periods_min = 1,
                                  .periods_max = 1024},
@@ -50,6 +57,9 @@ static struct line6_pcm_properties toneport_pcm_properties = {
                                          SNDRV_PCM_INFO_INTERLEAVED |
                                          SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                          SNDRV_PCM_INFO_MMAP_VALID |
+#ifdef CONFIG_PM
+                                         SNDRV_PCM_INFO_RESUME |
+#endif
                                          SNDRV_PCM_INFO_SYNC_START),
                                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                 .rates = SNDRV_PCM_RATE_KNOT,
@@ -58,7 +68,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
                                 .channels_min = 2,
                                 .channels_max = 2,
                                 .buffer_bytes_max = 60000,
-                                .period_bytes_min = 188 * 4,
+                                .period_bytes_min = 64,
                                 .period_bytes_max = 8192,
                                 .periods_min = 1,
                                 .periods_max = 1024},
@@ -77,6 +87,26 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 static int led_red = 0x00;
 static int led_green = 0x26;
 
+struct ToneportSourceInfo {
+       const char *name;
+       int code;
+};
+
+static const struct ToneportSourceInfo toneport_source_info[] = {
+       {"Microphone", 0x0a01},
+       {"Line", 0x0801},
+       {"Instrument", 0x0b01},
+       {"Inst & Mic", 0x0901}
+};
+
+static bool toneport_has_led(short product)
+{
+       return
+           (product == LINE6_DEVID_GUITARPORT) ||
+           (product == LINE6_DEVID_TONEPORT_GX);
+       /* add your device here if you are missing support for the LEDs */
+}
+
 static void toneport_update_led(struct device *dev)
 {
        struct usb_interface *interface = to_usb_interface(dev);
@@ -145,6 +175,120 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
        return 0;
 }
 
+/* monitor info callback */
+static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 256;
+       return 0;
+}
+
+/* monitor get callback */
+static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = line6pcm->volume_monitor;
+       return 0;
+}
+
+/* monitor put callback */
+static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+
+       if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor)
+               return 0;
+
+       line6pcm->volume_monitor = ucontrol->value.integer.value[0];
+
+       if (line6pcm->volume_monitor > 0)
+               line6_pcm_start(line6pcm, MASK_PCM_MONITOR);
+       else
+               line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
+
+       return 1;
+}
+
+/* source info callback */
+static int snd_toneport_source_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       const int size = ARRAY_SIZE(toneport_source_info);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = size;
+
+       if (uinfo->value.enumerated.item >= size)
+               uinfo->value.enumerated.item = size - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+              toneport_source_info[uinfo->value.enumerated.item].name);
+
+       return 0;
+}
+
+/* source get callback */
+static int snd_toneport_source_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+       struct usb_line6_toneport *toneport =
+           (struct usb_line6_toneport *)line6pcm->line6;
+       ucontrol->value.enumerated.item[0] = toneport->source;
+       return 0;
+}
+
+/* source put callback */
+static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
+       struct usb_line6_toneport *toneport =
+           (struct usb_line6_toneport *)line6pcm->line6;
+
+       if (ucontrol->value.enumerated.item[0] == toneport->source)
+               return 0;
+
+       toneport->source = ucontrol->value.enumerated.item[0];
+       toneport_send_cmd(toneport->line6.usbdev,
+                         toneport_source_info[toneport->source].code, 0x0000);
+       return 1;
+}
+
+static void toneport_start_pcm(unsigned long arg)
+{
+       struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
+       struct usb_line6 *line6 = &toneport->line6;
+       line6_pcm_start(line6->line6pcm, MASK_PCM_MONITOR);
+}
+
+/* control definition */
+static struct snd_kcontrol_new toneport_control_monitor = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Monitor Playback Volume",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = snd_toneport_monitor_info,
+       .get = snd_toneport_monitor_get,
+       .put = snd_toneport_monitor_put
+};
+
+/* source selector definition */
+static struct snd_kcontrol_new toneport_control_source = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "PCM Capture Source",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = snd_toneport_source_info,
+       .get = snd_toneport_source_get,
+       .put = snd_toneport_source_put
+};
+
 /*
        Toneport destructor.
 */
@@ -162,79 +306,138 @@ static void toneport_destruct(struct usb_interface *interface)
 }
 
 /*
-        Init Toneport device.
+       Setup Toneport device.
+*/
+static void toneport_setup(struct usb_line6_toneport *toneport)
+{
+       int ticks;
+       struct usb_line6 *line6 = &toneport->line6;
+       struct usb_device *usbdev = line6->usbdev;
+
+       /* sync time on device with host: */
+       ticks = (int)get_seconds();
+       line6_write_data(line6, 0x80c6, &ticks, 4);
+
+       /* enable device: */
+       toneport_send_cmd(usbdev, 0x0301, 0x0000);
+
+       /* initialize source select: */
+       switch (usbdev->descriptor.idProduct) {
+       case LINE6_DEVID_TONEPORT_UX1:
+       case LINE6_DEVID_PODSTUDIO_UX1:
+               toneport_send_cmd(usbdev,
+                                 toneport_source_info[toneport->source].code,
+                                 0x0000);
+       }
+
+       if (toneport_has_led(usbdev->descriptor.idProduct))
+               toneport_update_led(&usbdev->dev);
+}
+
+/*
+        Try to init Toneport device.
 */
-int toneport_init(struct usb_interface *interface,
-                 struct usb_line6_toneport *toneport)
+static int toneport_try_init(struct usb_interface *interface,
+                            struct usb_line6_toneport *toneport)
 {
-       int err, ticks;
+       int err;
        struct usb_line6 *line6 = &toneport->line6;
-       struct usb_device *usbdev;
+       struct usb_device *usbdev = line6->usbdev;
 
        if ((interface == NULL) || (toneport == NULL))
                return -ENODEV;
 
        /* initialize audio system: */
        err = line6_init_audio(line6);
-       if (err < 0) {
-               toneport_destruct(interface);
+       if (err < 0)
                return err;
-       }
 
        /* initialize PCM subsystem: */
        err = line6_init_pcm(line6, &toneport_pcm_properties);
-       if (err < 0) {
-               toneport_destruct(interface);
+       if (err < 0)
+               return err;
+
+       /* register monitor control: */
+       err = snd_ctl_add(line6->card,
+                         snd_ctl_new1(&toneport_control_monitor,
+                                      line6->line6pcm));
+       if (err < 0)
                return err;
+
+       /* register source select control: */
+       switch (usbdev->descriptor.idProduct) {
+       case LINE6_DEVID_TONEPORT_UX1:
+       case LINE6_DEVID_PODSTUDIO_UX1:
+               err =
+                   snd_ctl_add(line6->card,
+                               snd_ctl_new1(&toneport_control_source,
+                                            line6->line6pcm));
+               if (err < 0)
+                       return err;
        }
 
        /* register audio system: */
        err = line6_register_audio(line6);
-       if (err < 0) {
-               toneport_destruct(interface);
+       if (err < 0)
                return err;
-       }
 
-       usbdev = line6->usbdev;
        line6_read_serial_number(line6, &toneport->serial_number);
        line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
 
-       /* sync time on device with host: */
-       ticks = (int)get_seconds();
-       line6_write_data(line6, 0x80c6, &ticks, 4);
-
-       /*
-          seems to work without the first two...
-        */
-       /* toneport_send_cmd(usbdev, 0x0201, 0x0002); */
-       /* toneport_send_cmd(usbdev, 0x0801, 0x0000); */
-       /* only one that works for me; on GP, TP might be different? */
-       toneport_send_cmd(usbdev, 0x0301, 0x0000);
-
-       if (usbdev->descriptor.idProduct != LINE6_DEVID_GUITARPORT) {
+       if (toneport_has_led(usbdev->descriptor.idProduct)) {
                CHECK_RETURN(device_create_file
                             (&interface->dev, &dev_attr_led_red));
                CHECK_RETURN(device_create_file
                             (&interface->dev, &dev_attr_led_green));
-               toneport_update_led(&usbdev->dev);
        }
 
+       toneport_setup(toneport);
+
+       init_timer(&toneport->timer);
+       toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ;
+       toneport->timer.function = toneport_start_pcm;
+       toneport->timer.data = (unsigned long)toneport;
+       add_timer(&toneport->timer);
+
        return 0;
 }
 
+/*
+        Init Toneport device (and clean up in case of failure).
+*/
+int line6_toneport_init(struct usb_interface *interface,
+                       struct usb_line6_toneport *toneport)
+{
+       int err = toneport_try_init(interface, toneport);
+
+       if (err < 0)
+               toneport_destruct(interface);
+
+       return err;
+}
+
+/*
+       Resume Toneport device after reset.
+*/
+void line6_toneport_reset_resume(struct usb_line6_toneport *toneport)
+{
+       toneport_setup(toneport);
+}
+
 /*
        Toneport device disconnected.
 */
-void toneport_disconnect(struct usb_interface *interface)
+void line6_toneport_disconnect(struct usb_interface *interface)
 {
        struct usb_line6_toneport *toneport;
 
        if (interface == NULL)
                return;
+
        toneport = usb_get_intfdata(interface);
+       del_timer_sync(&toneport->timer);
 
-       if (toneport->line6.usbdev->descriptor.idProduct !=
-           LINE6_DEVID_GUITARPORT) {
+       if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) {
                device_remove_file(&interface->dev, &dev_attr_led_red);
                device_remove_file(&interface->dev, &dev_attr_led_green);
        }
@@ -243,8 +446,8 @@ void toneport_disconnect(struct usb_interface *interface)
                struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm;
 
                if (line6pcm != NULL) {
-                       unlink_wait_clear_audio_out_urbs(line6pcm);
-                       unlink_wait_clear_audio_in_urbs(line6pcm);
+                       line6_pcm_stop(line6pcm, MASK_PCM_MONITOR);
+                       line6_pcm_disconnect(line6pcm);
                }
        }