diff options
Diffstat (limited to 'sound/usb/6fire')
| -rw-r--r-- | sound/usb/6fire/chip.c | 41 | ||||
| -rw-r--r-- | sound/usb/6fire/chip.h | 1 | ||||
| -rw-r--r-- | sound/usb/6fire/comm.c | 48 | ||||
| -rw-r--r-- | sound/usb/6fire/comm.h | 5 | ||||
| -rw-r--r-- | sound/usb/6fire/common.h | 1 | ||||
| -rw-r--r-- | sound/usb/6fire/control.c | 454 | ||||
| -rw-r--r-- | sound/usb/6fire/control.h | 26 | ||||
| -rw-r--r-- | sound/usb/6fire/firmware.c | 169 | ||||
| -rw-r--r-- | sound/usb/6fire/firmware.h | 2 | ||||
| -rw-r--r-- | sound/usb/6fire/midi.c | 31 | ||||
| -rw-r--r-- | sound/usb/6fire/midi.h | 9 | ||||
| -rw-r--r-- | sound/usb/6fire/pcm.c | 223 | ||||
| -rw-r--r-- | sound/usb/6fire/pcm.h | 5 |
13 files changed, 720 insertions, 295 deletions
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index c7dca7b0b9f..dcddfc354ba 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -29,13 +28,13 @@ #include <sound/initval.h> MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>"); -MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0"); +MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver"); MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}"); +MODULE_SUPPORTED_DEVICE("{{TerraTec,DMX 6Fire USB}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */ +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */ static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; @@ -83,8 +82,8 @@ static void usb6fire_chip_destroy(struct sfire_chip *chip) } } -static int __devinit usb6fire_chip_probe(struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb6fire_chip_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) { int ret; int i; @@ -102,12 +101,12 @@ static int __devinit usb6fire_chip_probe(struct usb_interface *intf, usb_set_intfdata(intf, chips[i]); mutex_unlock(®ister_mutex); return 0; - } else if (regidx < 0) + } else if (!devices[i] && regidx < 0) regidx = i; } if (regidx < 0) { mutex_unlock(®ister_mutex); - snd_printk(KERN_ERR PREFIX "too many cards registered.\n"); + dev_err(&intf->dev, "too many cards registered.\n"); return -ENODEV; } devices[regidx] = device; @@ -122,20 +121,19 @@ static int __devinit usb6fire_chip_probe(struct usb_interface *intf, /* if we are here, card can be registered in alsa. */ if (usb_set_interface(device, 0, 0) != 0) { - snd_printk(KERN_ERR PREFIX "can't set first interface.\n"); + dev_err(&intf->dev, "can't set first interface.\n"); return -EIO; } - ret = snd_card_create(index[regidx], id[regidx], THIS_MODULE, - sizeof(struct sfire_chip), &card); + ret = snd_card_new(&intf->dev, index[regidx], id[regidx], + THIS_MODULE, sizeof(struct sfire_chip), &card); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "cannot create alsa card.\n"); + dev_err(&intf->dev, "cannot create alsa card.\n"); return ret; } strcpy(card->driver, "6FireUSB"); strcpy(card->shortname, "TerraTec DMX6FireUSB"); sprintf(card->longname, "%s at %d:%d", card->shortname, device->bus->busnum, device->devnum); - snd_card_set_dev(card, &intf->dev); chip = card->private_data; chips[regidx] = chip; @@ -170,7 +168,7 @@ static int __devinit usb6fire_chip_probe(struct usb_interface *intf, ret = snd_card_register(card); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "cannot register card."); + dev_err(&intf->dev, "cannot register card."); usb6fire_chip_destroy(chip); return ret; } @@ -211,22 +209,11 @@ static struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); -static struct usb_driver driver = { +static struct usb_driver usb_driver = { .name = "snd-usb-6fire", .probe = usb6fire_chip_probe, .disconnect = usb6fire_chip_disconnect, .id_table = device_table, }; -static int __init usb6fire_chip_init(void) -{ - return usb_register(&driver); -} - -static void __exit usb6fire_chip_cleanup(void) -{ - usb_deregister(&driver); -} - -module_init(usb6fire_chip_init); -module_exit(usb6fire_chip_cleanup); +module_usb_driver(usb_driver); diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h index d11e5cb520f..bde02d105a5 100644 --- a/sound/usb/6fire/chip.h +++ b/sound/usb/6fire/chip.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index c994daa57af..161215d78d9 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -52,7 +51,7 @@ static void usb6fire_comm_receiver_handler(struct urb *urb) urb->status = 0; urb->actual_length = 0; if (usb_submit_urb(urb, GFP_ATOMIC) < 0) - snd_printk(KERN_WARNING PREFIX + dev_warn(&urb->dev->dev, "comm data receiver aborted.\n"); } } @@ -111,31 +110,56 @@ static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev) static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request, u8 reg, u8 value) { - u8 buffer[13]; /* 13: maximum length of message */ + u8 *buffer; + int ret; + + /* 13: maximum length of message */ + buffer = kmalloc(13, GFP_KERNEL); + if (!buffer) + return -ENOMEM; usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00); - return usb6fire_comm_send_buffer(buffer, rt->chip->dev); + ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev); + + kfree(buffer); + return ret; } static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, u8 reg, u8 vl, u8 vh) { - u8 buffer[13]; /* 13: maximum length of message */ + u8 *buffer; + int ret; + + /* 13: maximum length of message */ + buffer = kmalloc(13, GFP_KERNEL); + if (!buffer) + return -ENOMEM; usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh); - return usb6fire_comm_send_buffer(buffer, rt->chip->dev); + ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev); + + kfree(buffer); + return ret; } -int __devinit usb6fire_comm_init(struct sfire_chip *chip) +int usb6fire_comm_init(struct sfire_chip *chip) { struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime), GFP_KERNEL); - struct urb *urb = &rt->receiver; + struct urb *urb; int ret; if (!rt) return -ENOMEM; + rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL); + if (!rt->receiver_buffer) { + kfree(rt); + return -ENOMEM; + } + + urb = &rt->receiver; rt->serial = 1; rt->chip = chip; usb_init_urb(urb); @@ -153,8 +177,9 @@ int __devinit usb6fire_comm_init(struct sfire_chip *chip) urb->interval = 1; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { + kfree(rt->receiver_buffer); kfree(rt); - snd_printk(KERN_ERR PREFIX "cannot create comm data receiver."); + dev_err(&chip->dev->dev, "cannot create comm data receiver."); return ret; } chip->comm = rt; @@ -171,6 +196,9 @@ void usb6fire_comm_abort(struct sfire_chip *chip) void usb6fire_comm_destroy(struct sfire_chip *chip) { - kfree(chip->comm); + struct comm_runtime *rt = chip->comm; + + kfree(rt->receiver_buffer); + kfree(rt); chip->comm = NULL; } diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h index edc5dc84b88..780d5ed8e5d 100644 --- a/sound/usb/6fire/comm.h +++ b/sound/usb/6fire/comm.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -25,7 +24,7 @@ struct comm_runtime { struct sfire_chip *chip; struct urb receiver; - u8 receiver_buffer[COMM_RECEIVER_BUFSIZE]; + u8 *receiver_buffer; u8 serial; /* urb serial */ @@ -37,7 +36,7 @@ struct comm_runtime { u8 vh, u8 vl); }; -int __devinit usb6fire_comm_init(struct sfire_chip *chip); +int usb6fire_comm_init(struct sfire_chip *chip); void usb6fire_comm_abort(struct sfire_chip *chip); void usb6fire_comm_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_COMM_H */ diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h index 7dbeb4a3783..b6eb03ed1c2 100644 --- a/sound/usb/6fire/common.h +++ b/sound/usb/6fire/common.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 24846351118..184e3987ac2 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -5,9 +5,12 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * + * Thanks to: + * - Holger Ruckdeschel: he found out how to control individual channel + * volumes and introduced mute switch + * * 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; either version 2 of the License, or @@ -16,6 +19,7 @@ #include <linux/interrupt.h> #include <sound/control.h> +#include <sound/tlv.h> #include "control.h" #include "comm.h" @@ -25,26 +29,6 @@ static char *opt_coax_texts[2] = { "Optical", "Coax" }; static char *line_phono_texts[2] = { "Line", "Phono" }; /* - * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$ - * this is done because the linear values cause rapid degredation - * of volume in the uppermost region. - */ -static const u8 log_volume_table[128] = { - 0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34, - 0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43, - 0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, - 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, - 0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, - 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, - 0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, - 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, - 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, - 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, - 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, - 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f }; - -/* * data that needs to be sent to device. sets up card internal stuff. * values dumped from windows driver and filtered by trial'n'error. */ @@ -59,22 +43,58 @@ init_data[] = { { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, - { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, + { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, { 0 } /* TERMINATING ENTRY */ }; -static void usb6fire_control_master_vol_update(struct control_runtime *rt) +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}; + +static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0); +static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500); + +enum { + DIGITAL_THRU_ONLY_SAMPLERATE = 3 +}; + +static void usb6fire_control_output_vol_update(struct control_runtime *rt) { struct comm_runtime *comm_rt = rt->chip->comm; - if (comm_rt) { - /* set volume */ - comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f - - log_volume_table[rt->master_vol]); - /* unmute */ - comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00); - } + int i; + + if (comm_rt) + for (i = 0; i < 6; i++) + if (!(rt->ovol_updated & (1 << i))) { + comm_rt->write8(comm_rt, 0x12, 0x0f + i, + 180 - rt->output_vol[i]); + rt->ovol_updated |= 1 << i; + } +} + +static void usb6fire_control_output_mute_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + + if (comm_rt) + comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute); +} + +static void usb6fire_control_input_vol_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + int i; + + if (comm_rt) + for (i = 0; i < 2; i++) + if (!(rt->ivol_updated & (1 << i))) { + comm_rt->write8(comm_rt, 0x12, 0x1c + i, + rt->input_vol[i] & 0x3f); + rt->ivol_updated |= 1 << i; + } } static void usb6fire_control_line_phono_update(struct control_runtime *rt) @@ -95,34 +115,212 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt) } } -static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, +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_output_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; + uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 127; + uinfo->value.integer.max = 180; return 0; } -static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol, +static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; int changed = 0; - if (rt->master_vol != ucontrol->value.integer.value[0]) { - rt->master_vol = ucontrol->value.integer.value[0]; - usb6fire_control_master_vol_update(rt); + + if (ch > 4) { + dev_err(&rt->chip->dev->dev, + "Invalid channel in volume control."); + return -EINVAL; + } + + if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { + rt->output_vol[ch] = ucontrol->value.integer.value[0]; + rt->ovol_updated &= ~(1 << ch); + changed = 1; + } + if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { + rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; + rt->ovol_updated &= ~(2 << ch); changed = 1; } + + if (changed) + usb6fire_control_output_vol_update(rt); + return changed; } -static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol, +static int usb6fire_control_output_vol_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->master_vol; + unsigned int ch = kcontrol->private_value; + + if (ch > 4) { + dev_err(&rt->chip->dev->dev, + "Invalid channel in volume control."); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = rt->output_vol[ch]; + ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; + return 0; +} + +static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; + u8 old = rt->output_mute; + u8 value = 0; + + if (ch > 4) { + dev_err(&rt->chip->dev->dev, + "Invalid channel in volume control."); + return -EINVAL; + } + + rt->output_mute &= ~(3 << ch); + if (ucontrol->value.integer.value[0]) + value |= 1; + if (ucontrol->value.integer.value[1]) + value |= 2; + rt->output_mute |= value << ch; + + if (rt->output_mute != old) + usb6fire_control_output_mute_update(rt); + + return rt->output_mute != old; +} + +static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + unsigned int ch = kcontrol->private_value; + u8 value = rt->output_mute >> ch; + + if (ch > 4) { + dev_err(&rt->chip->dev->dev, + "Invalid channel in volume control."); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = 1 & value; + value >>= 1; + ucontrol->value.integer.value[1] = 1 & value; + + return 0; +} + +static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 30; + return 0; +} + +static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { + rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; + rt->ivol_updated &= ~(1 << 0); + changed = 1; + } + if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { + rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; + rt->ivol_updated &= ~(1 << 1); + changed = 1; + } + + if (changed) + usb6fire_control_input_vol_update(rt); + + return changed; +} + +static int usb6fire_control_input_vol_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->input_vol[0] + 15; + ucontrol->value.integer.value[1] = rt->input_vol[1] + 15; + return 0; } @@ -195,16 +393,103 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, return 0; } -static struct __devinitdata snd_kcontrol_new elements[] = { +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 snd_kcontrol_new vol_elements[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Volume", + .index = 0, + .private_value = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Volume", + .index = 1, + .private_value = 2, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Volume", + .index = 2, + .private_value = 4, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_output_vol_info, + .get = usb6fire_control_output_vol_get, + .put = usb6fire_control_output_vol_put, + .tlv = { .p = tlv_output } + }, + {} +}; + +static struct snd_kcontrol_new mute_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", + .name = "Analog Playback Switch", .index = 0, + .private_value = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Switch", + .index = 1, + .private_value = 2, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Playback Switch", + .index = 2, + .private_value = 4, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = usb6fire_control_master_vol_info, - .get = usb6fire_control_master_vol_get, - .put = usb6fire_control_master_vol_put + .info = snd_ctl_boolean_stereo_info, + .get = usb6fire_control_output_mute_get, + .put = usb6fire_control_output_mute_put, }, + {} +}; + +static struct snd_kcontrol_new elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line/Phono Capture Route", @@ -223,10 +508,64 @@ 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 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Capture Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = usb6fire_control_input_vol_info, + .get = usb6fire_control_input_vol_get, + .put = usb6fire_control_input_vol_put, + .tlv = { .p = tlv_input } + }, {} }; -int __devinit usb6fire_control_init(struct sfire_chip *chip) +static int usb6fire_control_add_virtual( + struct control_runtime *rt, + struct snd_card *card, + char *name, + struct snd_kcontrol_new *elems) +{ + int ret; + int i; + struct snd_kcontrol *vmaster = + snd_ctl_make_virtual_master(name, tlv_output); + struct snd_kcontrol *control; + + if (!vmaster) + return -ENOMEM; + ret = snd_ctl_add(card, vmaster); + if (ret < 0) + return ret; + + i = 0; + while (elems[i].name) { + control = snd_ctl_new1(&elems[i], rt); + if (!control) + return -ENOMEM; + ret = snd_ctl_add(card, control); + if (ret < 0) + return ret; + ret = snd_ctl_add_slave(vmaster, control); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +int usb6fire_control_init(struct sfire_chip *chip) { int i; int ret; @@ -238,6 +577,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) { @@ -248,14 +590,32 @@ 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_output_vol_update(rt); + usb6fire_control_output_mute_update(rt); + usb6fire_control_input_vol_update(rt); + usb6fire_control_streaming_update(rt); + + ret = usb6fire_control_add_virtual(rt, chip->card, + "Master Playback Volume", vol_elements); + if (ret) { + dev_err(&chip->dev->dev, "cannot add control.\n"); + kfree(rt); + return ret; + } + ret = usb6fire_control_add_virtual(rt, chip->card, + "Master Playback Switch", mute_elements); + if (ret) { + dev_err(&chip->dev->dev, "cannot add control.\n"); + kfree(rt); + return ret; + } i = 0; while (elements[i].name) { ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); if (ret < 0) { kfree(rt); - snd_printk(KERN_ERR PREFIX "cannot add control.\n"); + dev_err(&chip->dev->dev, "cannot add control.\n"); return ret; } i++; diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index b534c777ab0..5a40ba14348 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -21,16 +20,37 @@ 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; - u8 master_vol; + bool digital_thru_switch; + bool usb_streaming; + u8 output_vol[6]; + u8 ovol_updated; + u8 output_mute; + s8 input_vol[2]; + u8 ivol_updated; }; -int __devinit usb6fire_control_init(struct sfire_chip *chip); +int usb6fire_control_init(struct sfire_chip *chip); void usb6fire_control_abort(struct sfire_chip *chip); void usb6fire_control_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_CONTROL_H */ diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 9081a54a9c6..3b02e54b8f6 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -3,15 +3,8 @@ * * 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 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -21,6 +14,9 @@ */ #include <linux/firmware.h> +#include <linux/module.h> +#include <linux/bitrev.h> +#include <linux/kernel.h> #include "firmware.h" #include "chip.h" @@ -33,32 +29,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,11 +42,15 @@ 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[][2] = { + { 0x03, 0x01 } +}; + struct ihex_record { u16 address; u8 len; u8 data[256]; - char error; /* true if an error occured parsing this record */ + char error; /* true if an error occurred parsing this record */ u8 max_len; /* maximum record length in whole ihex */ @@ -86,28 +60,26 @@ struct ihex_record { unsigned int txt_offset; /* current position in txt_data */ }; -static u8 usb6fire_fw_ihex_nibble(const u8 n) -{ - if (n >= '0' && n <= '9') - return n - '0'; - else if (n >= 'A' && n <= 'F') - return n - ('A' - 10); - else if (n >= 'a' && n <= 'f') - return n - ('a' - 10); - return 0; -} - static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc) { - u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) | - usb6fire_fw_ihex_nibble(data[1]); + u8 val = 0; + int hval; + + hval = hex_to_bin(data[0]); + if (hval >= 0) + val |= (hval << 4); + + hval = hex_to_bin(data[1]); + if (hval >= 0) + val |= hval; + *crc += val; return val; } /* * returns true if record is available, false otherwise. - * iff an error occured, false will be returned and record->error will be true. + * iff an error occurred, false will be returned and record->error will be true. */ static bool usb6fire_fw_ihex_next_record(struct ihex_record *record) { @@ -237,7 +209,7 @@ static int usb6fire_fw_ezusb_upload( int ret; u8 data; struct usb_device *device = interface_to_usbdev(intf); - const struct firmware *fw = 0; + const struct firmware *fw = NULL; struct ihex_record *rec = kmalloc(sizeof(struct ihex_record), GFP_KERNEL); @@ -247,15 +219,16 @@ static int usb6fire_fw_ezusb_upload( ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { kfree(rec); - snd_printk(KERN_ERR PREFIX "error requesting ezusb " - "firmware %s.\n", fwname); + dev_err(&intf->dev, + "error requesting ezusb firmware %s.\n", fwname); return ret; } ret = usb6fire_fw_ihex_init(fw, rec); if (ret < 0) { kfree(rec); - snd_printk(KERN_ERR PREFIX "error validating ezusb " - "firmware %s.\n", fwname); + release_firmware(fw); + dev_err(&intf->dev, + "error validating ezusb firmware %s.\n", fwname); return ret; } /* upload firmware image */ @@ -264,8 +237,9 @@ static int usb6fire_fw_ezusb_upload( if (ret < 0) { kfree(rec); release_firmware(fw); - snd_printk(KERN_ERR PREFIX "unable to upload ezusb " - "firmware %s: begin message.\n", fwname); + dev_err(&intf->dev, + "unable to upload ezusb firmware %s: begin message.\n", + fwname); return ret; } @@ -275,8 +249,9 @@ static int usb6fire_fw_ezusb_upload( if (ret < 0) { kfree(rec); release_firmware(fw); - snd_printk(KERN_ERR PREFIX "unable to upload ezusb " - "firmware %s: data urb.\n", fwname); + dev_err(&intf->dev, + "unable to upload ezusb firmware %s: data urb.\n", + fwname); return ret; } } @@ -287,8 +262,9 @@ static int usb6fire_fw_ezusb_upload( ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr, postdata, postlen); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "unable to upload ezusb " - "firmware %s: post urb.\n", fwname); + dev_err(&intf->dev, + "unable to upload ezusb firmware %s: post urb.\n", + fwname); return ret; } } @@ -296,9 +272,9 @@ static int usb6fire_fw_ezusb_upload( data = 0x00; /* resume ezusb cpu */ ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); if (ret < 0) { - release_firmware(fw); - snd_printk(KERN_ERR PREFIX "unable to upload ezusb " - "firmware %s: end message.\n", fwname); + dev_err(&intf->dev, + "unable to upload ezusb firmware %s: end message.\n", + fwname); return ret; } return 0; @@ -320,7 +296,7 @@ static int usb6fire_fw_fpga_upload( ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "unable to get fpga firmware %s.\n", + dev_err(&intf->dev, "unable to get fpga firmware %s.\n", fwname); kfree(buffer); return -EIO; @@ -333,21 +309,21 @@ static int usb6fire_fw_fpga_upload( if (ret < 0) { kfree(buffer); release_firmware(fw); - snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: " - "begin urb.\n"); + dev_err(&intf->dev, + "unable to upload fpga firmware: begin urb.\n"); return ret; } 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) { release_firmware(fw); kfree(buffer); - snd_printk(KERN_ERR PREFIX "unable to upload fpga " - "firmware: fw urb.\n"); + dev_err(&intf->dev, + "unable to upload fpga firmware: fw urb.\n"); return ret; } } @@ -356,13 +332,31 @@ static int usb6fire_fw_fpga_upload( ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: " - "end urb.\n"); + dev_err(&intf->dev, + "unable to upload fpga firmware: end urb.\n"); return ret; } 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(struct usb_interface *intf, const u8 *version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++) + if (!memcmp(version, known_fw_versions + i, 2)) + return 0; + + dev_err(&intf->dev, "invalid fimware version in device: %4ph. " + "please reconnect to power. if this failure " + "still happens, check your firmware installation.", + version); + return -EINVAL; +} + int usb6fire_fw_init(struct usb_interface *intf) { int i; @@ -374,22 +368,20 @@ int usb6fire_fw_init(struct usb_interface *intf) ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "unable to receive device " - "firmware state.\n"); + dev_err(&intf->dev, + "unable to receive device firmware state.\n"); return ret; } - if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55 - || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7] - != 0x00) { - snd_printk(KERN_ERR PREFIX "unknown device firmware state " - "received from device: "); + if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) { + dev_err(&intf->dev, + "unknown device firmware state received from device:"); for (i = 0; i < 8; i++) - snd_printk("%02x ", buffer[i]); - snd_printk("\n"); + printk(KERN_CONT "%02x ", buffer[i]); + printk(KERN_CONT "\n"); 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 +389,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(intf, buffer + 4); + if (ret < 0) + return ret; ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin"); if (ret < 0) return ret; @@ -410,15 +405,15 @@ 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(intf, buffer + 4); /* unknown data? */ else { - snd_printk(KERN_ERR PREFIX "unknown device firmware state " - "received from device: "); + dev_err(&intf->dev, + "unknown device firmware state received from device: "); for (i = 0; i < 8; i++) - snd_printk("%02x ", buffer[i]); - snd_printk("\n"); + printk(KERN_CONT "%02x ", buffer[i]); + printk(KERN_CONT "\n"); return -EIO; } return 0; diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h index 00856989538..c109c4f75ab 100644 --- a/sound/usb/6fire/firmware.h +++ b/sound/usb/6fire/firmware.h @@ -22,6 +22,6 @@ enum /* firmware state of device */ FW_NOT_READY = 1 }; -int __devinit usb6fire_fw_init(struct usb_interface *intf); +int usb6fire_fw_init(struct usb_interface *intf); #endif /* USB6FIRE_FIRMWARE_H */ diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 13f4509dce2..3d410969553 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -20,6 +19,10 @@ #include "chip.h" #include "comm.h" +enum { + MIDI_BUFSIZE = 64 +}; + static void usb6fire_midi_out_handler(struct urb *urb) { struct midi_runtime *rt = urb->context; @@ -38,8 +41,9 @@ static void usb6fire_midi_out_handler(struct urb *urb) ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) - snd_printk(KERN_ERR PREFIX "midi out urb " - "submit failed: %d\n", ret); + dev_err(&urb->dev->dev, + "midi out urb submit failed: %d\n", + ret); } else /* no more data to transmit */ rt->out = NULL; } @@ -91,8 +95,9 @@ static void usb6fire_midi_out_trigger( ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) - snd_printk(KERN_ERR PREFIX "midi out urb " - "submit failed: %d\n", ret); + dev_err(&urb->dev->dev, + "midi out urb submit failed: %d\n", + ret); else rt->out = alsa_sub; } @@ -147,7 +152,7 @@ static struct snd_rawmidi_ops in_ops = { .trigger = usb6fire_midi_in_trigger }; -int __devinit usb6fire_midi_init(struct sfire_chip *chip) +int usb6fire_midi_init(struct sfire_chip *chip) { int ret; struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), @@ -157,6 +162,12 @@ int __devinit usb6fire_midi_init(struct sfire_chip *chip) if (!rt) return -ENOMEM; + rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); + if (!rt->out_buffer) { + kfree(rt); + return -ENOMEM; + } + rt->chip = chip; rt->in_received = usb6fire_midi_in_received; rt->out_buffer[0] = 0x80; /* 'send midi' command */ @@ -170,8 +181,9 @@ int __devinit usb6fire_midi_init(struct sfire_chip *chip) ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); if (ret < 0) { + kfree(rt->out_buffer); kfree(rt); - snd_printk(KERN_ERR PREFIX "unable to create midi.\n"); + dev_err(&chip->dev->dev, "unable to create midi.\n"); return ret; } rt->instance->private_data = rt; @@ -198,6 +210,9 @@ void usb6fire_midi_abort(struct sfire_chip *chip) void usb6fire_midi_destroy(struct sfire_chip *chip) { - kfree(chip->midi); + struct midi_runtime *rt = chip->midi; + + kfree(rt->out_buffer); + kfree(rt); chip->midi = NULL; } diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h index 97a7bf66913..84851b9f555 100644 --- a/sound/usb/6fire/midi.h +++ b/sound/usb/6fire/midi.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -17,10 +16,6 @@ #include "common.h" -enum { - MIDI_BUFSIZE = 64 -}; - struct midi_runtime { struct sfire_chip *chip; struct snd_rawmidi *instance; @@ -33,13 +28,13 @@ struct midi_runtime { struct snd_rawmidi_substream *out; struct urb out_urb; u8 out_serial; /* serial number of out packet */ - u8 out_buffer[MIDI_BUFSIZE]; + u8 *out_buffer; int buffer_offset; void (*in_received)(struct midi_runtime *rt, u8 *data, int length); }; -int __devinit usb6fire_midi_init(struct sfire_chip *chip); +int usb6fire_midi_init(struct sfire_chip *chip); void usb6fire_midi_abort(struct sfire_chip *chip); void usb6fire_midi_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_MIDI_H */ diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba62c7468ba..ba40489b2de 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -5,7 +5,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -17,26 +16,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 +44,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 +51,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,60 +74,40 @@ 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; - - if (rt->rate >= ARRAY_SIZE(rates)) - return -EINVAL; - /* disable streaming */ - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error stopping streaming while " - "setting samplerate %d.\n", rates[rt->rate]); - return ret; - } + struct control_runtime *ctrl_rt = rt->chip->control; - ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]); + ctrl_rt->usb_streaming = false; + ret = ctrl_rt->update_streaming(ctrl_rt); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error setting interface " - "altsetting %d for samplerate %d.\n", - rates_altsetting[rt->rate], rates[rt->rate]); + dev_err(&rt->chip->dev->dev, + "error stopping streaming while setting samplerate %d.\n", + 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]); + dev_err(&rt->chip->dev->dev, + "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); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error initializing analog 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); + 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 digital " - "channels while setting samplerate %d.\n", - rates[rt->rate]); + dev_err(&rt->chip->dev->dev, + "error initializing 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]); + dev_err(&rt->chip->dev->dev, + "error starting streaming while setting samplerate %d.\n", + rates[rt->rate]); return ret; } @@ -160,7 +127,7 @@ static struct pcm_substream *usb6fire_pcm_get_substream( return &rt->playback; else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) return &rt->capture; - snd_printk(KERN_ERR PREFIX "error getting pcm substream slot.\n"); + dev_err(&rt->chip->dev->dev, "error getting pcm substream slot.\n"); return NULL; } @@ -168,12 +135,18 @@ 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) { + + rt->stream_state = STREAM_STOPPING; + 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 +201,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 +217,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 +252,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 { + dev_err(&rt->chip->dev->dev, "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 */ @@ -323,8 +310,8 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) } if (rt->stream_state == STREAM_DISABLED) { - snd_printk(KERN_ERR PREFIX "internal error: " - "stream disabled in in-urb handler.\n"); + dev_err(&rt->chip->dev->dev, + "internal error: stream disabled in in-urb handler.\n"); return; } @@ -413,12 +400,12 @@ static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub) alsa_rt->hw = pcm_hw; if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (rt->rate >= 0) + if (rt->rate < ARRAY_SIZE(rates)) alsa_rt->hw.rates = rates_alsaid[rt->rate]; alsa_rt->hw.channels_max = OUT_N_CHANNELS; sub = &rt->playback; } else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (rt->rate >= 0) + if (rt->rate < ARRAY_SIZE(rates)) alsa_rt->hw.rates = rates_alsaid[rt->rate]; alsa_rt->hw.channels_max = IN_N_CHANNELS; sub = &rt->capture; @@ -426,7 +413,7 @@ static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub) if (!sub) { mutex_unlock(&rt->stream_mutex); - snd_printk(KERN_ERR PREFIX "invalid stream type.\n"); + dev_err(&rt->chip->dev->dev, "invalid stream type.\n"); return -EINVAL; } @@ -456,7 +443,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); @@ -466,13 +453,13 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_malloc_pages(alsa_sub, - params_buffer_bytes(hw_params)); + return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub, + params_buffer_bytes(hw_params)); } static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub) { - return snd_pcm_lib_free_pages(alsa_sub); + return snd_pcm_lib_free_vmalloc_buffer(alsa_sub); } static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) @@ -480,7 +467,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,15 +479,14 @@ 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); + dev_err(&rt->chip->dev->dev, + "invalid rate %d in prepare.\n", + alsa_rt->rate); return -EINVAL; } @@ -513,8 +498,8 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) ret = usb6fire_pcm_stream_start(rt); if (ret) { mutex_unlock(&rt->stream_mutex); - snd_printk(KERN_ERR PREFIX - "could not start pcm stream.\n"); + dev_err(&rt->chip->dev->dev, + "could not start pcm stream.\n"); return ret; } } @@ -562,7 +547,7 @@ static snd_pcm_uframes_t usb6fire_pcm_pointer( snd_pcm_uframes_t ret; if (rt->panic || !sub) - return SNDRV_PCM_STATE_XRUN; + return SNDRV_PCM_POS_XRUN; spin_lock_irqsave(&sub->lock, flags); ret = sub->dma_off; @@ -579,11 +564,13 @@ static struct snd_pcm_ops pcm_ops = { .prepare = usb6fire_pcm_prepare, .trigger = usb6fire_pcm_trigger, .pointer = usb6fire_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, }; -static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb, - struct sfire_chip *chip, bool in, int ep, - void (*handler)(struct urb *)) +static void usb6fire_pcm_init_urb(struct pcm_urb *urb, + struct sfire_chip *chip, bool in, int ep, + void (*handler)(struct urb *)) { urb->chip = chip; usb_init_urb(&urb->instance); @@ -594,13 +581,39 @@ static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb, urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep) : usb_sndisocpipe(chip->dev, ep); urb->instance.interval = 1; - urb->instance.transfer_flags = URB_ISO_ASAP; urb->instance.complete = handler; urb->instance.context = urb; urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB; } -int __devinit usb6fire_pcm_init(struct sfire_chip *chip) +static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt) +{ + int i; + + for (i = 0; i < PCM_N_URBS; i++) { + rt->out_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB + * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + if (!rt->out_urbs[i].buffer) + return -ENOMEM; + rt->in_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB + * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + if (!rt->in_urbs[i].buffer) + return -ENOMEM; + } + return 0; +} + +static void usb6fire_pcm_buffers_destroy(struct pcm_runtime *rt) +{ + int i; + + for (i = 0; i < PCM_N_URBS; i++) { + kfree(rt->out_urbs[i].buffer); + kfree(rt->in_urbs[i].buffer); + } +} + +int usb6fire_pcm_init(struct sfire_chip *chip) { int i; int ret; @@ -611,9 +624,16 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) if (!rt) return -ENOMEM; + ret = usb6fire_pcm_buffers_init(rt); + if (ret) { + usb6fire_pcm_buffers_destroy(rt); + kfree(rt); + return ret; + } + 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); @@ -632,8 +652,9 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm); if (ret < 0) { + usb6fire_pcm_buffers_destroy(rt); kfree(rt); - snd_printk(KERN_ERR PREFIX "cannot create pcm instance.\n"); + dev_err(&chip->dev->dev, "cannot create pcm instance.\n"); return ret; } @@ -642,14 +663,11 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFSIZE, MAX_BUFSIZE); if (ret) { + usb6fire_pcm_buffers_destroy(rt); kfree(rt); - snd_printk(KERN_ERR PREFIX - "error preallocating pcm buffers.\n"); + dev_err(&chip->dev->dev, + "error preallocating pcm buffers.\n"); return ret; } rt->instance = pcm; @@ -661,17 +679,25 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) void usb6fire_pcm_abort(struct sfire_chip *chip) { struct pcm_runtime *rt = chip->pcm; + unsigned long flags; int i; if (rt) { rt->panic = true; - if (rt->playback.instance) + if (rt->playback.instance) { + snd_pcm_stream_lock_irqsave(rt->playback.instance, flags); snd_pcm_stop(rt->playback.instance, SNDRV_PCM_STATE_XRUN); - if (rt->capture.instance) + snd_pcm_stream_unlock_irqrestore(rt->playback.instance, flags); + } + + if (rt->capture.instance) { + snd_pcm_stream_lock_irqsave(rt->capture.instance, flags); snd_pcm_stop(rt->capture.instance, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(rt->capture.instance, flags); + } for (i = 0; i < PCM_N_URBS; i++) { usb_poison_urb(&rt->in_urbs[i].instance); @@ -683,6 +709,9 @@ void usb6fire_pcm_abort(struct sfire_chip *chip) void usb6fire_pcm_destroy(struct sfire_chip *chip) { - kfree(chip->pcm); + struct pcm_runtime *rt = chip->pcm; + + usb6fire_pcm_buffers_destroy(rt); + kfree(rt); chip->pcm = NULL; } diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h index 2bee8137400..f5779d6182c 100644 --- a/sound/usb/6fire/pcm.h +++ b/sound/usb/6fire/pcm.h @@ -3,7 +3,6 @@ * * Author: Torsten Schenk <torsten.schenk@zoho.com> * Created: Jan 01, 2011 - * Version: 0.3.0 * Copyright: (C) Torsten Schenk * * This program is free software; you can redistribute it and/or modify @@ -33,7 +32,7 @@ struct pcm_urb { struct urb instance; struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB]; /* END DO NOT SEPARATE */ - u8 buffer[PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE]; + u8 *buffer; struct pcm_urb *peer; }; @@ -70,7 +69,7 @@ struct pcm_runtime { bool stream_wait_cond; }; -int __devinit usb6fire_pcm_init(struct sfire_chip *chip); +int usb6fire_pcm_init(struct sfire_chip *chip); void usb6fire_pcm_abort(struct sfire_chip *chip); void usb6fire_pcm_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_PCM_H */ |
