diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/6fire/control.c | 105 | ||||
-rw-r--r-- | sound/usb/6fire/control.h | 17 | ||||
-rw-r--r-- | sound/usb/6fire/firmware.c | 73 | ||||
-rw-r--r-- | sound/usb/6fire/pcm.c | 97 | ||||
-rw-r--r-- | sound/usb/Kconfig | 10 | ||||
-rw-r--r-- | sound/usb/card.c | 17 | ||||
-rw-r--r-- | sound/usb/clock.c | 11 | ||||
-rw-r--r-- | sound/usb/debug.h | 2 | ||||
-rw-r--r-- | sound/usb/format.c | 1 | ||||
-rw-r--r-- | sound/usb/mixer.c | 42 | ||||
-rw-r--r-- | sound/usb/mixer.h | 14 | ||||
-rw-r--r-- | sound/usb/mixer_quirks.c | 82 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 51 | ||||
-rw-r--r-- | sound/usb/quirks.c | 19 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 1 |
15 files changed, 398 insertions, 144 deletions
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 24846351118..ac828eff1a6 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -65,6 +65,15 @@ init_data[] = { { 0 } /* TERMINATING ENTRY */ }; +static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; +/* values to write to soundcard register for all samplerates */ +static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; +static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; + +enum { + DIGITAL_THRU_ONLY_SAMPLERATE = 3 +}; + static void usb6fire_control_master_vol_update(struct control_runtime *rt) { struct comm_runtime *comm_rt = rt->chip->comm; @@ -95,6 +104,67 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt) } } +static int usb6fire_control_set_rate(struct control_runtime *rt, int rate) +{ + int ret; + struct usb_device *device = rt->chip->dev; + struct comm_runtime *comm_rt = rt->chip->comm; + + if (rate < 0 || rate >= CONTROL_N_RATES) + return -EINVAL; + + ret = usb_set_interface(device, 1, rates_altsetting[rate]); + if (ret < 0) + return ret; + + /* set soundcard clock */ + ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], + rates_6fire_vh[rate]); + if (ret < 0) + return ret; + + return 0; +} + +static int usb6fire_control_set_channels( + struct control_runtime *rt, int n_analog_out, + int n_analog_in, bool spdif_out, bool spdif_in) +{ + int ret; + struct comm_runtime *comm_rt = rt->chip->comm; + + /* enable analog inputs and outputs + * (one bit per stereo-channel) */ + ret = comm_rt->write16(comm_rt, 0x02, 0x02, + (1 << (n_analog_out / 2)) - 1, + (1 << (n_analog_in / 2)) - 1); + if (ret < 0) + return ret; + + /* disable digital inputs and outputs */ + /* TODO: use spdif_x to enable/disable digital channels */ + ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); + if (ret < 0) + return ret; + + return 0; +} + +static int usb6fire_control_streaming_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + + if (comm_rt) { + if (!rt->usb_streaming && rt->digital_thru_switch) + usb6fire_control_set_rate(rt, + DIGITAL_THRU_ONLY_SAMPLERATE); + return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, + (rt->usb_streaming ? 0x01 : 0x00) | + (rt->digital_thru_switch ? 0x08 : 0x00)); + } + return -EINVAL; +} + static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, return 0; } +static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { + rt->digital_thru_switch = ucontrol->value.integer.value[0]; + usb6fire_control_streaming_update(rt); + changed = 1; + } + return changed; +} + +static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = rt->digital_thru_switch; + return 0; +} + static struct __devinitdata snd_kcontrol_new elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = { .get = usb6fire_control_opt_coax_get, .put = usb6fire_control_opt_coax_put }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Thru Playback Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = usb6fire_control_digital_thru_get, + .put = usb6fire_control_digital_thru_put + }, {} }; @@ -238,6 +339,9 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) return -ENOMEM; rt->chip = chip; + rt->update_streaming = usb6fire_control_streaming_update; + rt->set_rate = usb6fire_control_set_rate; + rt->set_channels = usb6fire_control_set_channels; i = 0; while (init_data[i].type) { @@ -249,6 +353,7 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) usb6fire_control_opt_coax_update(rt); usb6fire_control_line_phono_update(rt); usb6fire_control_master_vol_update(rt); + usb6fire_control_streaming_update(rt); i = 0; while (elements[i].name) { diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index b534c777ab0..8f5aeead2e3 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h @@ -21,12 +21,29 @@ enum { CONTROL_MAX_ELEMENTS = 32 }; +enum { + CONTROL_RATE_44KHZ, + CONTROL_RATE_48KHZ, + CONTROL_RATE_88KHZ, + CONTROL_RATE_96KHZ, + CONTROL_RATE_176KHZ, + CONTROL_RATE_192KHZ, + CONTROL_N_RATES +}; + struct control_runtime { + int (*update_streaming)(struct control_runtime *rt); + int (*set_rate)(struct control_runtime *rt, int rate); + int (*set_channels)(struct control_runtime *rt, int n_analog_out, + int n_analog_in, bool spdif_out, bool spdif_in); + struct sfire_chip *chip; struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS]; bool opt_coax_switch; bool line_phono_switch; + bool digital_thru_switch; + bool usb_streaming; u8 master_vol; }; diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 86c1a310376..d47beffedb0 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -3,12 +3,6 @@ * * Firmware loader * - * Currently not working for all devices. To be able to use the device - * in linux, it is also possible to let the windows driver upload the firmware. - * For that, start the computer in windows and reboot. - * As long as the device is connected to the power supply, no firmware reload - * needs to be performed. - * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 * Version: 0.3.0 @@ -21,6 +15,7 @@ */ #include <linux/firmware.h> +#include <linux/bitrev.h> #include "firmware.h" #include "chip.h" @@ -33,32 +28,6 @@ enum { FPGA_BUFSIZE = 512, FPGA_EP = 2 }; -static const u8 BIT_REVERSE_TABLE[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, - 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, - 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, - 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, - 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, - 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, - 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, - 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, - 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, - 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, - 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, - 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, - 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, - 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, - 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, - 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, - 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, - 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, - 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, - 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, - 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, - 0xbf, 0x7f, 0xff }; - /* * wMaxPacketSize of pcm endpoints. * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c @@ -72,6 +41,10 @@ static const u8 ep_w_max_packet_size[] = { 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */ }; +static const u8 known_fw_versions[][4] = { + { 0x03, 0x01, 0x0b, 0x00 } +}; + struct ihex_record { u16 address; u8 len; @@ -340,7 +313,7 @@ static int usb6fire_fw_fpga_upload( while (c != end) { for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) - buffer[i] = BIT_REVERSE_TABLE[(u8) *c]; + buffer[i] = byte_rev_table[(u8) *c]; ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { @@ -363,6 +336,25 @@ static int usb6fire_fw_fpga_upload( return 0; } +/* check, if the firmware version the devices has currently loaded + * is known by this driver. 'version' needs to have 4 bytes version + * info data. */ +static int usb6fire_fw_check(u8 *version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++) + if (!memcmp(version, known_fw_versions + i, 4)) + return 0; + + snd_printk(KERN_ERR PREFIX "invalid fimware version in device: " + "%02x %02x %02x %02x. " + "please reconnect to power. if this failure " + "still happens, check your firmware installation.", + version[0], version[1], version[2], version[3]); + return -EINVAL; +} + int usb6fire_fw_init(struct usb_interface *intf) { int i; @@ -378,9 +370,7 @@ int usb6fire_fw_init(struct usb_interface *intf) "firmware state.\n"); return ret; } - if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55 - || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7] - != 0x00) { + if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) { snd_printk(KERN_ERR PREFIX "unknown device firmware state " "received from device: "); for (i = 0; i < 8; i++) @@ -389,7 +379,7 @@ int usb6fire_fw_init(struct usb_interface *intf) return -EIO; } /* do we need fpga loader ezusb firmware? */ - if (buffer[3] == 0x01 && buffer[6] == 0x19) { + if (buffer[3] == 0x01) { ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6firel2.ihx", 0, NULL, 0); if (ret < 0) @@ -397,7 +387,10 @@ int usb6fire_fw_init(struct usb_interface *intf) return FW_NOT_READY; } /* do we need fpga firmware and application ezusb firmware? */ - else if (buffer[3] == 0x02 && buffer[6] == 0x0b) { + else if (buffer[3] == 0x02) { + ret = usb6fire_fw_check(buffer + 4); + if (ret < 0) + return ret; ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin"); if (ret < 0) return ret; @@ -410,8 +403,8 @@ int usb6fire_fw_init(struct usb_interface *intf) return FW_NOT_READY; } /* all fw loaded? */ - else if (buffer[3] == 0x03 && buffer[6] == 0x0b) - return 0; + else if (buffer[3] == 0x03) + return usb6fire_fw_check(buffer + 4); /* unknown data? */ else { snd_printk(KERN_ERR PREFIX "unknown device firmware state " diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba62c7468ba..b137b25865c 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -17,26 +17,23 @@ #include "pcm.h" #include "chip.h" #include "comm.h" +#include "control.h" enum { OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4 }; /* keep next two synced with - * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */ + * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE + * and CONTROL_RATE_XXX in control.h */ static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 }; static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 }; static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; -static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; static const int rates_alsaid[] = { SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000, SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000, SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 }; -/* values to write to soundcard register for all samplerates */ -static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; -static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; - enum { /* settings for pcm */ OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024 }; @@ -48,15 +45,6 @@ enum { /* pcm streaming states */ STREAM_STOPPING }; -enum { /* pcm sample rates (also index into RATES_XXX[]) */ - RATE_44KHZ, - RATE_48KHZ, - RATE_88KHZ, - RATE_96KHZ, - RATE_176KHZ, - RATE_192KHZ -}; - static const struct snd_pcm_hardware pcm_hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | @@ -64,7 +52,7 @@ static const struct snd_pcm_hardware pcm_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH, - .formats = SNDRV_PCM_FMTBIT_S24_LE, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -87,57 +75,34 @@ static const struct snd_pcm_hardware pcm_hw = { static int usb6fire_pcm_set_rate(struct pcm_runtime *rt) { int ret; - struct usb_device *device = rt->chip->dev; - struct comm_runtime *comm_rt = rt->chip->comm; + struct control_runtime *ctrl_rt = rt->chip->control; - if (rt->rate >= ARRAY_SIZE(rates)) - return -EINVAL; - /* disable streaming */ - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); + ctrl_rt->usb_streaming = false; + ret = ctrl_rt->update_streaming(ctrl_rt); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error stopping streaming while " "setting samplerate %d.\n", rates[rt->rate]); return ret; } - ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error setting interface " - "altsetting %d for samplerate %d.\n", - rates_altsetting[rt->rate], rates[rt->rate]); - return ret; - } - - /* set soundcard clock */ - ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate], - rates_6fire_vh[rt->rate]); + ret = ctrl_rt->set_rate(ctrl_rt, rt->rate); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n", rates[rt->rate]); return ret; } - /* enable analog inputs and outputs - * (one bit per stereo-channel) */ - ret = comm_rt->write16(comm_rt, 0x02, 0x02, - (1 << (OUT_N_CHANNELS / 2)) - 1, - (1 << (IN_N_CHANNELS / 2)) - 1); + ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS, + false, false); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error initializing analog channels " + snd_printk(KERN_ERR PREFIX "error initializing channels " "while setting samplerate %d.\n", rates[rt->rate]); return ret; } - /* disable digital inputs and outputs */ - ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error initializing digital " - "channels while setting samplerate %d.\n", - rates[rt->rate]); - return ret; - } - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01); + ctrl_rt->usb_streaming = true; + ret = ctrl_rt->update_streaming(ctrl_rt); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error starting streaming while " "setting samplerate %d.\n", rates[rt->rate]); @@ -168,12 +133,15 @@ static struct pcm_substream *usb6fire_pcm_get_substream( static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) { int i; + struct control_runtime *ctrl_rt = rt->chip->control; if (rt->stream_state != STREAM_DISABLED) { for (i = 0; i < PCM_N_URBS; i++) { usb_kill_urb(&rt->in_urbs[i].instance); usb_kill_urb(&rt->out_urbs[i].instance); } + ctrl_rt->usb_streaming = false; + ctrl_rt->update_streaming(ctrl_rt); rt->stream_state = STREAM_DISABLED; } } @@ -228,7 +196,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) unsigned int total_length = 0; struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; - u32 *src = (u32 *) urb->buffer; + u32 *src = NULL; u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off * (alsa_rt->frame_bits >> 3)); u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size @@ -244,7 +212,12 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) else frame_count = 0; - src = (u32 *) (urb->buffer + total_length); + if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE) + src = (u32 *) (urb->buffer + total_length); + else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE) + src = (u32 *) (urb->buffer - 1 + total_length); + else + return; src++; /* skip leading 4 bytes of every packet */ total_length += urb->packets[i].length; for (frame = 0; frame < frame_count; frame++) { @@ -274,9 +247,18 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub, * (alsa_rt->frame_bits >> 3)); u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size * (alsa_rt->frame_bits >> 3)); - u32 *dest = (u32 *) urb->buffer; + u32 *dest; int bytes_per_frame = alsa_rt->channels << 2; + if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE) + dest = (u32 *) (urb->buffer - 1); + else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE) + dest = (u32 *) (urb->buffer); + else { + snd_printk(KERN_ERR PREFIX "Unknown sample format."); + return; + } + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { /* at least 4 header bytes for valid packet. * after that: 32 bits per sample for analog channels */ @@ -456,7 +438,7 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) /* all substreams closed? if so, stop streaming */ if (!rt->playback.instance && !rt->capture.instance) { usb6fire_pcm_stream_stop(rt); - rt->rate = -1; + rt->rate = ARRAY_SIZE(rates); } } mutex_unlock(&rt->stream_mutex); @@ -480,7 +462,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; - int i; int ret; if (rt->panic) @@ -493,12 +474,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) sub->period_off = 0; if (rt->stream_state == STREAM_DISABLED) { - for (i = 0; i < ARRAY_SIZE(rates); i++) - if (alsa_rt->rate == rates[i]) { - rt->rate = i; + for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++) + if (alsa_rt->rate == rates[rt->rate]) break; - } - if (i == ARRAY_SIZE(rates)) { + if (rt->rate == ARRAY_SIZE(rates)) { mutex_unlock(&rt->stream_mutex); snd_printk("invalid rate %d in prepare.\n", alsa_rt->rate); @@ -613,7 +592,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) rt->chip = chip; rt->stream_state = STREAM_DISABLED; - rt->rate = -1; + rt->rate = ARRAY_SIZE(rates); init_waitqueue_head(&rt->stream_wait_queue); mutex_init(&rt->stream_mutex); diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 97724d8fa9f..8beb77563da 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -100,19 +100,17 @@ config SND_USB_US122L config SND_USB_6FIRE tristate "TerraTec DMX 6Fire USB" - depends on EXPERIMENTAL select FW_LOADER + select BITREVERSE select SND_RAWMIDI select SND_PCM help Say Y here to include support for TerraTec 6fire DMX USB interface. You will need firmware files in order to be able to use the device - after it has been coldstarted. This driver currently does not support - firmware loading for all devices. If you own such a device, - you could start windows and let the windows driver upload - the firmware. As long as you do not unplug your device from power, - it should be usable. + after it has been coldstarted. An install script for the firmware + and further help can be found at + http://sixfireusb.sourceforge.net endif # SND_USB diff --git a/sound/usb/card.c b/sound/usb/card.c index a90662af2d6..220c6167dd8 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -48,6 +48,7 @@ #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <sound/control.h> #include <sound/core.h> #include <sound/info.h> #include <sound/pcm.h> @@ -492,14 +493,6 @@ static void *snd_usb_audio_probe(struct usb_device *dev, } } - chip->txfr_quirk = 0; - err = 1; /* continue */ - if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { - /* need some special handlings */ - if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) - goto __error; - } - /* * For devices with more than one control interface, we assume the * first contains the audio controls. We might need a more specific @@ -508,6 +501,14 @@ static void *snd_usb_audio_probe(struct usb_device *dev, if (!chip->ctrl_intf) chip->ctrl_intf = alts; + chip->txfr_quirk = 0; + err = 1; /* continue */ + if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { + /* need some special handlings */ + if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) + goto __error; + } + if (err > 0) { /* create normal USB audio interfaces */ if (snd_usb_create_streams(chip, ifnum) < 0 || diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 7754a103454..075195e8661 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) int err; unsigned char data; struct usb_device *dev = chip->dev; + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) + return 0; + + /* If a clock source can't tell us whether it's valid, we assume it is */ + if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) + return 1; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, @@ -114,7 +123,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) if (err < 0) { snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n", __func__, source_id); - return err; + return 0; } return !!data; diff --git a/sound/usb/debug.h b/sound/usb/debug.h index 343ec2d9ee6..58030176f00 100644 --- a/sound/usb/debug.h +++ b/sound/usb/debug.h @@ -8,7 +8,7 @@ #ifdef HW_CONST_DEBUG #define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) #else -#define hwc_debug(fmt, args...) /**/ +#define hwc_debug(fmt, args...) do { } while(0) #endif #endif /* __USBAUDIO_DEBUG_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c index f079b5e2ab2..8d042dce0d1 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -30,6 +30,7 @@ #include "helper.h" #include "debug.h" #include "clock.h" +#include "format.h" /* * parse the audio format type I descriptor diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 6ec33b62e6c..c22fa76e363 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -86,16 +86,6 @@ struct mixer_build { const struct usbmix_selector_map *selector_map; }; -enum { - USB_MIXER_BOOLEAN, - USB_MIXER_INV_BOOLEAN, - USB_MIXER_S8, - USB_MIXER_U8, - USB_MIXER_S16, - USB_MIXER_U16, -}; - - /*E-mu 0202/0404/0204 eXtension Unit(XU) control*/ enum { USB_XU_CLOCK_RATE = 0xe301, @@ -535,20 +525,21 @@ static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_ou * if failed, give up and free the control instance. */ -static int add_control_to_empty(struct mixer_build *state, struct snd_kcontrol *kctl) +int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { struct usb_mixer_elem_info *cval = kctl->private_data; int err; - while (snd_ctl_find_id(state->chip->card, &kctl->id)) + while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) kctl->id.index++; - if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { + if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) { snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); return err; } cval->elem_id = &kctl->id; - cval->next_id_elem = state->mixer->id_elems[cval->id]; - state->mixer->id_elems[cval->id] = cval; + cval->next_id_elem = mixer->id_elems[cval->id]; + mixer->id_elems[cval->id] = cval; return 0; } @@ -984,6 +975,9 @@ static struct snd_kcontrol_new usb_feature_unit_ctl_ro = { .put = NULL, }; +/* This symbol is exported in order to allow the mixer quirks to + * hook up to the standard feature unit control mechanism */ +struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl; /* * build a feature control @@ -1097,11 +1091,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); if (control == UAC_FU_VOLUME) { - kctl->tlv.c = mixer_vol_tlv; - kctl->vd[0].access |= - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; check_mapped_dB(map, cval); + if (cval->dBmin < cval->dBmax) { + kctl->tlv.c = mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } } break; @@ -1174,7 +1170,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); - add_control_to_empty(state, kctl); + snd_usb_mixer_add_control(state->mixer, kctl); } @@ -1338,7 +1334,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - add_control_to_empty(state, kctl); + snd_usb_mixer_add_control(state->mixer, kctl); } @@ -1639,7 +1635,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - if ((err = add_control_to_empty(state, kctl)) < 0) + if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0) return err; } return 0; @@ -1856,7 +1852,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", cval->id, kctl->id.name, desc->bNrInPins); - if ((err = add_control_to_empty(state, kctl)) < 0) + if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0) return err; return 0; diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index b4a2c8165e4..ae1a14dcfe8 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -24,7 +24,16 @@ struct usb_mixer_interface { u8 xonar_u1_status; }; -#define MAX_CHANNELS 10 /* max logical channels */ +#define MAX_CHANNELS 16 /* max logical channels */ + +enum { + USB_MIXER_BOOLEAN, + USB_MIXER_INV_BOOLEAN, + USB_MIXER_S8, + USB_MIXER_U8, + USB_MIXER_S16, + USB_MIXER_U16, +}; struct usb_mixer_elem_info { struct usb_mixer_interface *mixer; @@ -55,4 +64,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer); int snd_usb_mixer_activate(struct usb_mixer_interface *mixer); +int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl); + #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 73dcc8256bc..3d0f4873112 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -40,6 +40,8 @@ #include "mixer_quirks.h" #include "helper.h" +extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; + /* * Sound Blaster remote control configuration * @@ -61,6 +63,7 @@ static const struct rc_config { { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */ + { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */ { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ }; @@ -188,6 +191,12 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0, 100); + /* USB X-Fi S51 Pro */ + if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0, 100); else err = snd_usb_ctl_msg(mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, @@ -234,9 +243,13 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) /* USB X-Fi S51 doesn't have a CMSS LED */ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) continue; + /* USB X-Fi S51 Pro doesn't have one either */ + if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0) + continue; if (i > 1 && /* Live24ext has 2 LEDs only */ (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || + mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) break; err = snd_ctl_add(mixer->chip->card, @@ -481,6 +494,69 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, return err; } +/* M-Audio FastTrack Ultra quirks */ + +/* private_free callback */ +static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +{ + kfree(kctl->private_data); + kctl->private_data = NULL; +} + +static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer, + int in, int out, const char *name) +{ + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return -ENOMEM; + + cval->id = 5; + cval->mixer = mixer; + cval->val_type = USB_MIXER_S16; + cval->channels = 1; + cval->control = out + 1; + cval->cmask = 1 << in; + + kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); + if (!kctl) { + kfree(cval); + return -ENOMEM; + } + + snprintf(kctl->id.name, sizeof(kctl->id.name), name); + kctl->private_free = usb_mixer_elem_free; + return snd_usb_mixer_add_control(mixer, kctl); +} + +static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) +{ + char name[64]; + int in, out, err; + + for (out = 0; out < 8; out++) { + for (in = 0; in < 8; in++) { + snprintf(name, sizeof(name), + "AIn%d - Out%d Capture Volume", in + 1, out + 1); + err = snd_maudio_ftu_create_ctl(mixer, in, out, name); + if (err < 0) + return err; + } + + for (in = 8; in < 16; in++) { + snprintf(name, sizeof(name), + "DIn%d - Out%d Playback Volume", in - 7, out + 1); + err = snd_maudio_ftu_create_ctl(mixer, in, out, name); + if (err < 0) + return err; + } + } + + return 0; +} + void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, unsigned char samplerate_id) { @@ -512,6 +588,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x041e, 0x3020): case USB_ID(0x041e, 0x3040): case USB_ID(0x041e, 0x3042): + case USB_ID(0x041e, 0x30df): case USB_ID(0x041e, 0x3048): err = snd_audigy2nx_controls_create(mixer); if (err < 0) @@ -521,6 +598,11 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) snd_audigy2nx_proc_read); break; + case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ + err = snd_maudio_ftu_create_mixer(mixer); + break; + case USB_ID(0x0b05, 0x1739): case USB_ID(0x0b05, 0x1743): err = snd_xonar_u1_controls_create(mixer); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c66d3f64dcf..0b2ae8e1c02 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1651,6 +1651,32 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE(0x0582, 0x0127), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Roland", */ + /* .product_name = "GR-55", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, /* Guillemot devices */ { @@ -1953,7 +1979,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - USB_DEVICE(0x0763, 0x2080), + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ /* .product_name = "Fast Track Ultra", */ @@ -1962,7 +1988,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = & (const struct snd_usb_audio_quirk[]) { { .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE + .type = QUIRK_AUDIO_STANDARD_MIXER, }, { .ifnum = 1, @@ -2020,7 +2046,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - USB_DEVICE(0x0763, 0x2081), + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ /* .product_name = "Fast Track Ultra 8R", */ @@ -2029,7 +2055,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = & (const struct snd_usb_audio_quirk[]) { { .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE + .type = QUIRK_AUDIO_STANDARD_MIXER, }, { .ifnum = 1, @@ -2179,6 +2205,17 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +/* KORG devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "KORG, Inc.", + /* .product_name = "PANDORA PX5D", */ + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE, + } +}, + /* AKAI devices */ { USB_DEVICE(0x09e8, 0x0062), @@ -2332,6 +2369,12 @@ YAMAHA_DEVICE(0x7010, "UB99"), /* Native Instruments MK2 series */ { + /* Komplete Audio 6 */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x17cc, + .idProduct = 0x1000, +}, +{ /* Traktor Audio 6 */ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x17cc, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 1b94ec3a336..2e969cbb393 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> +#include <sound/control.h> #include <sound/core.h> #include <sound/info.h> #include <sound/pcm.h> @@ -263,6 +264,20 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, } /* + * Create a standard mixer for the specified interface. + */ +static int create_standard_mixer_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + if (quirk->ifnum < 0) + return 0; + + return snd_usb_create_mixer(chip, quirk->ifnum, 0); +} + +/* * audio-interface quirks * * returns zero if no standard audio/MIDI parsing is needed. @@ -294,7 +309,8 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, - [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk + [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk, + [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, }; if (quirk->type < QUIRK_TYPE_COUNT) { @@ -540,6 +556,7 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, /* Access Music VirusTI Desktop */ return snd_usb_accessmusic_boot_quirk(dev); + case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */ case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */ case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */ return snd_usb_nativeinstruments_boot_quirk(dev); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 32f2a97f2f1..1e79986b577 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -84,6 +84,7 @@ enum quirk_type { QUIRK_AUDIO_FIXED_ENDPOINT, QUIRK_AUDIO_EDIROL_UAXX, QUIRK_AUDIO_ALIGN_TRANSFER, + QUIRK_AUDIO_STANDARD_MIXER, QUIRK_TYPE_COUNT }; |