This is for usb audio offloading. usb audio offloading processes usb audio stream data directly from the audio box even if ap usb enters suspend, there is no problem in stream data transmission. This obtains power saving effect while using usb audio device.
AP usb and audio usb f/w communicate via AUDIO IPC. By performing AUDIO IPC in the vendor operations, abox can access and control the xhci to transmit the data directly.
The types of commands and data delivered through AUDIO IPC include the following (AP USB <-> AUDIO USB f/w) : 1. usb audio connection/disconnection status 2. xhci memory information 3. full descriptor for usb audio device 4. UAC(usb audio class) control command
Signed-off-by: Oh Eomji eomji.oh@samsung.com --- sound/usb/Kconfig | 9 + sound/usb/Makefile | 2 + sound/usb/exynos_usb_audio.c | 560 +++++++++++++++++++++++++++++++++++++++++++ sound/usb/exynos_usb_audio.h | 150 ++++++++++++ 4 files changed, 721 insertions(+) create mode 100644 sound/usb/exynos_usb_audio.c create mode 100644 sound/usb/exynos_usb_audio.h
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 059242f..70252a3 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -27,6 +27,15 @@ config SND_USB_AUDIO config SND_USB_AUDIO_USE_MEDIA_CONTROLLER bool
+config SND_EXYNOS_USB_AUDIO + tristate "EXYNOS USB Audio offloading module" + depends on SND_USB_AUDIO + help + Say Y here to include support for Exynos USB Audio ABOX offloading. + + To compile this driver as a module, choose M here: the module + will be called exynos-usb-audio-offloading. + config SND_USB_UA101 tristate "Edirol UA-101/UA-1000 driver" select SND_PCM diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 9ccb21a..bf019c7 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -28,6 +28,8 @@ snd-usbmidi-lib-objs := midi.o
# Toplevel Module Dependency obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o +obj-$(CONFIG_SND_EXYNOS_USB_AUDIO) += exynos-usb-audio-offloading.o +exynos-usb-audio-offloading-y += exynos_usb_audio.o
obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o diff --git a/sound/usb/exynos_usb_audio.c b/sound/usb/exynos_usb_audio.c new file mode 100644 index 0000000..456cc78 --- /dev/null +++ b/sound/usb/exynos_usb_audio.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB Audio offloading Driver for Exynos + * + * Copyright (c) 2017 by Kyounghye Yun k-hye.yun@samsung.com + * + */ + + +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/usb.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v2.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/completion.h> + +#include <sound/pcm.h> +#include "../../../sound/usb/exynos_usb_audio.h" +#include "usbaudio.h" +#include "helper.h" +#include "card.h" +#include "quirks.h" + +static struct exynos_usb_audio *usb_audio; +static struct snd_usb_audio_vendor_ops exynos_usb_audio_ops; + +struct hcd_hw_info *g_hwinfo; +EXPORT_SYMBOL_GPL(g_hwinfo); +int otg_connection; +EXPORT_SYMBOL_GPL(otg_connection); +int usb_audio_connection; +EXPORT_SYMBOL_GPL(usb_audio_connection); + +static void exynos_usb_audio_set_device(struct usb_device *udev) +{ + usb_audio->udev = udev; + usb_audio->is_audio = 1; +} + +static int exynos_usb_audio_unmap_all(void) +{ + /* + * TODO: unmapping pcm buffer, usb descriptor, Device Context, + * Input Context, ERST, URAM. + */ + + return 0; +} + +static int exynos_usb_audio_pcmbuf(struct usb_device *udev, int iface) +{ + if (!usb_audio->is_audio || !otg_connection) + return -1; + + /* + * TODO: Transmit the DRAM address that contains the xhci device + * information,and the DMA address required for operation to the abox + * usb f/w. + */ + + return 0; +} + +static int exynos_usb_audio_setrate(int iface, int rate, int alt) +{ + if (!usb_audio->is_audio || !otg_connection) + return -1; + + /* + * TODO: Notify the abox usb f/w the audio sample rate supported by + * the interface of the connected audio device. + */ + + return 0; +} + +static int exynos_usb_audio_setintf(struct usb_device *udev, int iface, int alt, int direction) +{ + struct hcd_hw_info *hwinfo = g_hwinfo; + u64 in_offset, out_offset; + + if (!usb_audio->pcm_open_done) + return -EPERM; + + if (!usb_audio->is_audio || !otg_connection) + return -1; + + if (direction) { + /* IN EP */ + if (!usb_audio->indeq_map_done || + (hwinfo->in_deq != hwinfo->old_in_deq)) { + /* TODO: Transmit pcm interface number, alt setting + * number to abox usb f/w + */ + usb_audio->indeq_map_done = 1; + in_offset = hwinfo->in_deq % PAGE_SIZE; + } + + if (hwinfo->fb_out_deq) { + if (!usb_audio->fb_outdeq_map_done || + (hwinfo->fb_out_deq != hwinfo->fb_old_out_deq)) { + /* TODO: Transmit pcm interface number, + * alt setting number to abox usb f/w + */ + usb_audio->fb_outdeq_map_done = 1; + out_offset = hwinfo->fb_out_deq % PAGE_SIZE; + } + } + } else { + /* OUT EP */ + if (!usb_audio->outdeq_map_done || + (hwinfo->out_deq != hwinfo->old_out_deq)) { + /* TODO: Transmit pcm interface number, alt setting + * number to abox usb f/w + */ + usb_audio->outdeq_map_done = 1; + out_offset = hwinfo->out_deq % PAGE_SIZE; + } + + if (hwinfo->fb_in_deq) { + if (!usb_audio->fb_indeq_map_done || + (hwinfo->fb_in_deq != hwinfo->fb_old_in_deq)) { + /* TODO: Transmit pcm interface number, + * alt setting number to abox usb f/w + */ + usb_audio->fb_indeq_map_done = 1; + in_offset = hwinfo->fb_in_deq % PAGE_SIZE; + } + } + } + + /* one more check connection to prevent kernel panic */ + if (!usb_audio->is_audio || !otg_connection) + return -1; + + /* TODO: Notify abox usb f/w a dequeue pointer */ + + return 0; +} + +static int exynos_usb_audio_hcd(struct usb_device *udev) +{ + struct hcd_hw_info *hwinfo = g_hwinfo; + + /* back up each address for unmap */ + usb_audio->dcbaa_dma = hwinfo->dcbaa_dma; + usb_audio->save_dma = hwinfo->save_dma; + usb_audio->in_ctx = hwinfo->in_ctx; + usb_audio->out_ctx = hwinfo->out_ctx; + usb_audio->erst_addr = hwinfo->erst_addr; + usb_audio->speed = hwinfo->speed; + usb_audio->use_uram = hwinfo->use_uram; + + /* + * TODO: DCBAA, Device Context, Input Context, URAM, ERST mapping + * and notify abox usb f/w the address about xhci h/w resource to + * directly control the xhci in abox. + */ + + return 0; +} + +static int exynos_usb_audio_desc(struct usb_device *udev) +{ + int configuration, cfgno, i; + unsigned char *buffer; + u64 desc_addr; + u64 offset; + + configuration = usb_choose_configuration(udev); + + cfgno = -1; + for (i = 0; i < udev->descriptor.bNumConfigurations; i++) { + if (udev->config[i].desc.bConfigurationValue == + configuration) { + cfgno = i; + break; + } + } + + if (cfgno == -1) + cfgno = 0; + + /* need to memory mapping for usb descriptor */ + buffer = udev->rawdescriptors[cfgno]; + desc_addr = virt_to_phys(buffer); + offset = desc_addr % PAGE_SIZE; + + /* store address information */ + usb_audio->desc_addr = desc_addr; + usb_audio->offset = offset; + + desc_addr -= offset; + + /* + * TODO: Notify the abox usb f/w that all descriptors have been recived + * from the connected usb audio device, and that copy and memory mapping + * have beed completed so that it can be used in abox usb f/w + */ + + return 0; +} + +static int exynos_usb_audio_conn(struct usb_device *udev, int is_conn) +{ + + /* TODO: Notify abox usb f/w whether usb device is connected or not */ + if (!is_conn) { + if (usb_audio->is_audio) { + usb_audio->is_audio = 0; + usb_audio->usb_audio_state = USB_AUDIO_REMOVING; + } + } else { + cancel_work_sync(&usb_audio->usb_work); + usb_audio->indeq_map_done = 0; + usb_audio->outdeq_map_done = 0; + usb_audio->fb_indeq_map_done = 0; + usb_audio->fb_outdeq_map_done = 0; + usb_audio->pcm_open_done = 0; + reinit_completion(&usb_audio->discon_done); + usb_audio->usb_audio_state = USB_AUDIO_CONNECT; + usb_audio_connection = 1; + } + + return 0; +} + +static int exynos_usb_audio_pcm(bool is_open, bool direction) +{ + if (!usb_audio->is_audio || !otg_connection) + return -1; + + if (is_open) + usb_audio->pcm_open_done = 1; + + /* TODO: Notify the abox usb f/w the pcm open/close status */ + + return 0; +} + +static void exynos_usb_audio_work(struct work_struct *w) +{ + /* Don't unmap in USB_AUDIO_TIMEOUT_PROBE state */ + if (usb_audio->usb_audio_state != USB_AUDIO_REMOVING) + return; + + exynos_usb_audio_unmap_all(); + usb_audio->usb_audio_state = USB_AUDIO_DISCONNECT; + usb_audio_connection = 0; + complete(&usb_audio->discon_done); +} + +static int exynos_usb_scenario_ctl_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 = AUDIO_MODE_NORMAL; + uinfo->value.integer.max = AUDIO_MODE_CALL_SCREEN; + return 0; +} + +static int exynos_usb_scenario_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct exynos_usb_audio *usb = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = usb->user_scenario; + return 0; +} + +static int exynos_usb_scenario_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct exynos_usb_audio *usb = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (usb->user_scenario != + ucontrol->value.integer.value[0]) { + usb->user_scenario = ucontrol->value.integer.value[0]; + changed = 1; + } + + return changed; +} + +static int exynos_usb_add_ctls(struct snd_usb_audio *chip, + unsigned long private_value) +{ + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "USB Audio Mode", + .info = exynos_usb_scenario_ctl_info, + .get = exynos_usb_scenario_ctl_get, + .put = exynos_usb_scenario_ctl_put, + }; + + int err; + + if (!chip) + return -ENODEV; + + knew.private_value = private_value; + usb_audio->kctl = snd_ctl_new1(&knew, usb_audio); + if (!usb_audio->kctl) + return -ENOMEM; + + err = snd_ctl_add(chip->card, usb_audio->kctl); + if (err < 0) + return err; + + return 0; +} + +int exynos_usb_audio_init(struct device *dev, struct platform_device *pdev) +{ + struct device_node *np = dev->of_node; + struct device_node *np_abox; + struct platform_device *pdev_abox; + int ret = 0; + + if (!usb_audio) { + usb_audio = kmalloc(sizeof(struct exynos_usb_audio), GFP_KERNEL); + if (!usb_audio) + return -ENOMEM; + } + + np_abox = of_parse_phandle(np, "abox", 0); + if (!np_abox) + return -EPROBE_DEFER; + + pdev_abox = of_find_device_by_node(np_abox); + if (!pdev_abox) + return -EPROBE_DEFER; + + init_completion(&usb_audio->in_conn_stop); + init_completion(&usb_audio->out_conn_stop); + init_completion(&usb_audio->discon_done); + usb_audio->abox = pdev_abox; + usb_audio->hcd_pdev = pdev; + usb_audio->udev = NULL; + usb_audio->is_audio = 0; + usb_audio->is_first_probe = 1; + usb_audio->user_scenario = AUDIO_MODE_NORMAL; + usb_audio->usb_audio_state = USB_AUDIO_DISCONNECT; + usb_audio_connection = 0; + + INIT_WORK(&usb_audio->usb_work, exynos_usb_audio_work); + + /* interface function mapping */ + ret = snd_vendor_set_ops(&exynos_usb_audio_ops); + + return ret; +} +EXPORT_SYMBOL_GPL(exynos_usb_audio_init); + +/* card */ +static int exynos_usb_audio_connect(struct usb_interface *intf) +{ + struct usb_interface_descriptor *altsd; + struct usb_host_interface *alts; + struct usb_device *udev = interface_to_usbdev(intf); + int timeout = 0; + + alts = &intf->altsetting[0]; + altsd = get_iface_desc(alts); + + if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && + altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { + } else { + if (usb_audio->usb_audio_state == USB_AUDIO_REMOVING) { + timeout = wait_for_completion_timeout( + &usb_audio->discon_done, + msecs_to_jiffies(DISCONNECT_TIMEOUT)); + + if ((usb_audio->usb_audio_state == USB_AUDIO_REMOVING) + && !timeout) { + usb_audio->usb_audio_state = + USB_AUDIO_TIMEOUT_PROBE; + } + } + + if ((usb_audio->usb_audio_state == USB_AUDIO_DISCONNECT) + || (usb_audio->usb_audio_state == USB_AUDIO_TIMEOUT_PROBE)) { + exynos_usb_audio_set_device(udev); + exynos_usb_audio_hcd(udev); + exynos_usb_audio_conn(udev, 1); + exynos_usb_audio_desc(udev); + } else { + return -EPERM; + } + } + + return 0; +} + +static void exynos_usb_audio_disconn(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + exynos_usb_audio_conn(udev, 0); +} + +/* clock */ +static int exynos_usb_audio_set_interface(struct usb_device *udev, + struct usb_host_interface *alts, int iface, int alt) +{ + unsigned char ep; + unsigned char numEndpoints; + int direction; + int i; + int ret = 0; + + if (alts != NULL) { + numEndpoints = get_iface_desc(alts)->bNumEndpoints; + if (numEndpoints < 1) + return -22; + if (numEndpoints == 1) + ep = get_endpoint(alts, 0)->bEndpointAddress; + else { + for (i = 0; i < numEndpoints; i++) { + ep = get_endpoint(alts, i)->bmAttributes; + if (!(ep & 0x30)) { + ep = get_endpoint(alts, i)->bEndpointAddress; + break; + } + } + } + if (ep & USB_DIR_IN) + direction = SNDRV_PCM_STREAM_CAPTURE; + else + direction = SNDRV_PCM_STREAM_PLAYBACK; + + ret = exynos_usb_audio_setintf(udev, iface, alt, direction); + } + + return ret; +} + +/* pcm */ +static int exynos_usb_audio_set_rate(int iface, int rate, int alt) +{ + int ret; + + ret = exynos_usb_audio_setrate(iface, rate, alt); + + return ret; +} + +static int exynos_usb_audio_set_pcmbuf(struct usb_device *dev, int iface) +{ + int ret; + + ret = exynos_usb_audio_pcmbuf(dev, iface); + + return ret; +} + +static int exynos_usb_audio_set_pcm_intf(struct usb_interface *intf, + int iface, int alt, int direction) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int ret; + + ret = exynos_usb_audio_setintf(udev, iface, alt, direction); + + return ret; +} + +static int exynos_usb_audio_pcm_control(struct usb_device *udev, + enum snd_vendor_pcm_open_close onoff, int direction) +{ + int ret = 0; + + if (onoff == 1) { + ret = exynos_usb_audio_pcm(1, direction); + } else if (onoff == 0) { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + reinit_completion(&usb_audio->out_conn_stop); + else if (direction == SNDRV_PCM_STREAM_CAPTURE) + reinit_completion(&usb_audio->in_conn_stop); + + if (!usb_audio->pcm_open_done) + return 0; + + ret = exynos_usb_audio_pcm(0, direction); + } + + return ret; +} + +static int exynos_usb_audio_add_control(struct snd_usb_audio *chip) +{ + int ret; + + if (chip != NULL) + ret = exynos_usb_add_ctls(chip, 0); + else + ret = usb_audio->user_scenario; + + return ret; +} + +static int exynos_usb_audio_set_pcm_binterval(const struct audioformat *fp, + const struct audioformat *found, + int *cur_attr, int *attr) +{ + if (usb_audio->user_scenario >= AUDIO_MODE_IN_CALL) { + if (fp->datainterval < found->datainterval) { + found = fp; + cur_attr = attr; + } + } else { + if (fp->datainterval > found->datainterval) { + found = fp; + cur_attr = attr; + } + } + + return 0; +} + +/* Set interface function */ +static struct snd_usb_audio_vendor_ops exynos_usb_audio_ops = { + /* card */ + .connect = exynos_usb_audio_connect, + .disconnect = exynos_usb_audio_disconn, + /* clock */ + .set_interface = exynos_usb_audio_set_interface, + /* pcm */ + .set_rate = exynos_usb_audio_set_rate, + .set_pcm_buf = exynos_usb_audio_set_pcmbuf, + .set_pcm_intf = exynos_usb_audio_set_pcm_intf, + .set_pcm_connection = exynos_usb_audio_pcm_control, + .set_pcm_binterval = exynos_usb_audio_set_pcm_binterval, + .usb_add_ctls = exynos_usb_audio_add_control, +}; + +int exynos_usb_audio_exit(void) +{ + /* future use */ + return 0; +} +EXPORT_SYMBOL_GPL(exynos_usb_audio_exit); + +MODULE_AUTHOR("Jaehun Jung jh0801.jung@samsung.com"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Exynos USB Audio offloading driver"); + diff --git a/sound/usb/exynos_usb_audio.h b/sound/usb/exynos_usb_audio.h new file mode 100644 index 0000000..13707744 --- /dev/null +++ b/sound/usb/exynos_usb_audio.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* USB Audio Driver for Exynos + * + * Copyright (c) 2017 by Kyounghye Yun k-hye.yun@samsung.com + * + */ + +#ifndef __LINUX_USB_EXYNOS_AUDIO_H +#define __LINUX_USB_EXYNOS_AUDIO_H + +#include "../../../sound/usb/usbaudio.h" + +/* for KM */ + +#define USB_AUDIO_MEM_BASE 0xC0000000 + +#define USB_AUDIO_SAVE_RESTORE (USB_AUDIO_MEM_BASE) +#define USB_AUDIO_DEV_CTX (USB_AUDIO_SAVE_RESTORE+PAGE_SIZE) +#define USB_AUDIO_INPUT_CTX (USB_AUDIO_DEV_CTX+PAGE_SIZE) +#define USB_AUDIO_OUT_DEQ (USB_AUDIO_INPUT_CTX+PAGE_SIZE) +#define USB_AUDIO_IN_DEQ (USB_AUDIO_OUT_DEQ+PAGE_SIZE) +#define USB_AUDIO_FBOUT_DEQ (USB_AUDIO_IN_DEQ+PAGE_SIZE) +#define USB_AUDIO_FBIN_DEQ (USB_AUDIO_FBOUT_DEQ+PAGE_SIZE) +#define USB_AUDIO_ERST (USB_AUDIO_FBIN_DEQ+PAGE_SIZE) +#define USB_AUDIO_DESC (USB_AUDIO_ERST+PAGE_SIZE) +#define USB_AUDIO_PCM_OUTBUF (USB_AUDIO_MEM_BASE+0x100000) +#define USB_AUDIO_PCM_INBUF (USB_AUDIO_MEM_BASE+0x800000) + +#if defined(CONFIG_SOC_S5E9815) +#define USB_AUDIO_XHCI_BASE 0x12210000 +#define USB_URAM_BASE 0x122a0000 +#define USB_URAM_SIZE (SZ_1K * 24) +#elif defined(CONFIG_SOC_S5E9935) +#define USB_AUDIO_XHCI_BASE 0x10B00000 +#define USB_URAM_BASE 0x02a00000 +#define USB_URAM_SIZE (SZ_1K * 24) +#else +#error +#endif + +#define USB_AUDIO_CONNECT (1 << 0) +#define USB_AUDIO_REMOVING (1 << 1) +#define USB_AUDIO_DISCONNECT (1 << 2) +#define USB_AUDIO_TIMEOUT_PROBE (1 << 3) + +#define DISCONNECT_TIMEOUT (500) + +#define AUDIO_MODE_NORMAL 0 +#define AUDIO_MODE_RINGTONE 1 +#define AUDIO_MODE_IN_CALL 2 +#define AUDIO_MODE_IN_COMMUNICATION 3 +#define AUDIO_MODE_CALL_SCREEN 4 + +#define CALL_INTERVAL_THRESHOLD 3 + +#define USB_AUDIO_CONNECT (1 << 0) +#define USB_AUDIO_REMOVING (1 << 1) +#define USB_AUDIO_DISCONNECT (1 << 2) +#define USB_AUDIO_TIMEOUT_PROBE (1 << 3) + +#define DISCONNECT_TIMEOUT (500) + +struct host_data { + dma_addr_t out_data_dma; + dma_addr_t in_data_dma; + void *out_data_addr; + void *in_data_addr; +}; + +extern struct host_data xhci_data; + +struct exynos_usb_audio { + struct usb_device *udev; + struct platform_device *abox; + struct platform_device *hcd_pdev; + struct mutex lock; + struct work_struct usb_work; + struct completion in_conn_stop; + struct completion out_conn_stop; + struct completion discon_done; + + u64 out_buf_addr; + u64 in_buf_addr; + u64 pcm_offset; + u64 desc_addr; + u64 offset; + + /* for hw_info */ + u64 dcbaa_dma; + u64 in_ctx; + u64 out_ctx; + u64 erst_addr; + + int speed; + /* 1: in, 0: out */ + int set_ep; + int is_audio; + int is_first_probe; + u8 indeq_map_done; + u8 outdeq_map_done; + u8 fb_indeq_map_done; + u8 fb_outdeq_map_done; + u32 pcm_open_done; + u32 usb_audio_state; + + struct snd_kcontrol *kctl; + u32 user_scenario; + + void *pcm_buf; + u64 save_dma; + + bool use_uram; +}; + +struct hcd_hw_info { + /* for XHCI */ + int slot_id; + dma_addr_t erst_addr; + dma_addr_t dcbaa_dma; + dma_addr_t in_ctx; + dma_addr_t out_ctx; + dma_addr_t save_dma; + u64 cmd_ring; + /* Data Stream EP */ + u64 old_out_deq; + u64 old_in_deq; + u64 out_deq; + u64 in_deq; + int in_ep; + int out_ep; + /* feedback ep */ + int fb_in_ep; + int fb_out_ep; + u64 fb_old_out_deq; + u64 fb_old_in_deq; + u64 fb_out_deq; + u64 fb_in_deq; + /* Device Common Information */ + int speed; + void *out_buf; + u64 out_dma; + void *in_buf; + u64 in_dma; + int use_uram; + int rawdesc_length; +}; + +int exynos_usb_audio_init(struct device *dev, struct platform_device *pdev); +int exynos_usb_audio_exit(void); +#endif /* __LINUX_USB_EXYNOS_AUDIO_H */