15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2eb9fecb9SRuslan Bilovol /* 3eb9fecb9SRuslan Bilovol * u_audio.c -- interface to USB gadget "ALSA sound card" utilities 4eb9fecb9SRuslan Bilovol * 5eb9fecb9SRuslan Bilovol * Copyright (C) 2016 6eb9fecb9SRuslan Bilovol * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com> 7eb9fecb9SRuslan Bilovol * 8eb9fecb9SRuslan Bilovol * Sound card implementation was cut-and-pasted with changes 9eb9fecb9SRuslan Bilovol * from f_uac2.c and has: 10eb9fecb9SRuslan Bilovol * Copyright (C) 2011 11eb9fecb9SRuslan Bilovol * Yadwinder Singh (yadi.brar01@gmail.com) 12eb9fecb9SRuslan Bilovol * Jaswinder Singh (jaswinder.singh@linaro.org) 13eb9fecb9SRuslan Bilovol */ 14eb9fecb9SRuslan Bilovol 1502de698cSRuslan Bilovol #include <linux/kernel.h> 16eb9fecb9SRuslan Bilovol #include <linux/module.h> 17eb9fecb9SRuslan Bilovol #include <sound/core.h> 18eb9fecb9SRuslan Bilovol #include <sound/pcm.h> 19eb9fecb9SRuslan Bilovol #include <sound/pcm_params.h> 20e89bb428SRuslan Bilovol #include <sound/control.h> 2102de698cSRuslan Bilovol #include <sound/tlv.h> 2202de698cSRuslan Bilovol #include <linux/usb/audio.h> 23eb9fecb9SRuslan Bilovol 24eb9fecb9SRuslan Bilovol #include "u_audio.h" 25eb9fecb9SRuslan Bilovol 26eb9fecb9SRuslan Bilovol #define BUFF_SIZE_MAX (PAGE_SIZE * 16) 27eb9fecb9SRuslan Bilovol #define PRD_SIZE_MAX PAGE_SIZE 28eb9fecb9SRuslan Bilovol #define MIN_PERIODS 4 29eb9fecb9SRuslan Bilovol 3002de698cSRuslan Bilovol enum { 3102de698cSRuslan Bilovol UAC_FBACK_CTRL, 326fec018aSPavel Hofman UAC_P_PITCH_CTRL, 3302de698cSRuslan Bilovol UAC_MUTE_CTRL, 3402de698cSRuslan Bilovol UAC_VOLUME_CTRL, 35c565ad07SJulian Scheel UAC_RATE_CTRL, 3602de698cSRuslan Bilovol }; 3702de698cSRuslan Bilovol 38eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */ 39eb9fecb9SRuslan Bilovol struct uac_rtd_params { 40eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; /* parent chip */ 41eb9fecb9SRuslan Bilovol bool ep_enabled; /* if the ep is enabled */ 42eb9fecb9SRuslan Bilovol 43eb9fecb9SRuslan Bilovol struct snd_pcm_substream *ss; 44eb9fecb9SRuslan Bilovol 45eb9fecb9SRuslan Bilovol /* Ring buffer */ 46eb9fecb9SRuslan Bilovol ssize_t hw_ptr; 47eb9fecb9SRuslan Bilovol 48eb9fecb9SRuslan Bilovol void *rbuf; 49eb9fecb9SRuslan Bilovol 50e89bb428SRuslan Bilovol unsigned int pitch; /* Stream pitch ratio to 1000000 */ 51f4408a98SJonas Stenvall unsigned int max_psize; /* MaxPacketSize of endpoint */ 52eb9fecb9SRuslan Bilovol 53d70f7598SJerome Brunet struct usb_request **reqs; 5424f779daSRuslan Bilovol 5524f779daSRuslan Bilovol struct usb_request *req_fback; /* Feedback endpoint request */ 5624f779daSRuslan Bilovol bool fb_ep_enabled; /* if the ep is enabled */ 5702de698cSRuslan Bilovol 5802de698cSRuslan Bilovol /* Volume/Mute controls and their state */ 5902de698cSRuslan Bilovol int fu_id; /* Feature Unit ID */ 60453d3fa9SChris Wulff struct snd_ctl_elem_id snd_kctl_volume_id; 61453d3fa9SChris Wulff struct snd_ctl_elem_id snd_kctl_mute_id; 6202de698cSRuslan Bilovol s16 volume_min, volume_max, volume_res; 6302de698cSRuslan Bilovol s16 volume; 6402de698cSRuslan Bilovol int mute; 6502de698cSRuslan Bilovol 66453d3fa9SChris Wulff struct snd_ctl_elem_id snd_kctl_rate_id; /* read-only current rate */ 678722a949SPavel Hofman int srate; /* selected samplerate */ 688fe9a03fSPavel Hofman int active; /* playback/capture running */ 69c565ad07SJulian Scheel 7002de698cSRuslan Bilovol spinlock_t lock; /* lock for control transfers */ 7102de698cSRuslan Bilovol 72eb9fecb9SRuslan Bilovol }; 73eb9fecb9SRuslan Bilovol 74eb9fecb9SRuslan Bilovol struct snd_uac_chip { 75eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 76eb9fecb9SRuslan Bilovol 77eb9fecb9SRuslan Bilovol struct uac_rtd_params p_prm; 78eb9fecb9SRuslan Bilovol struct uac_rtd_params c_prm; 79eb9fecb9SRuslan Bilovol 80eb9fecb9SRuslan Bilovol struct snd_card *card; 81eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 82eb9fecb9SRuslan Bilovol 83eb9fecb9SRuslan Bilovol /* pre-calculated values for playback iso completion */ 846fec018aSPavel Hofman unsigned long long p_residue_mil; 85f2f69bf6SJohn Keeping unsigned int p_interval; 86eb9fecb9SRuslan Bilovol unsigned int p_framesize; 87eb9fecb9SRuslan Bilovol }; 88eb9fecb9SRuslan Bilovol 892ab3c34cSBhumika Goyal static const struct snd_pcm_hardware uac_pcm_hardware = { 90eb9fecb9SRuslan Bilovol .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER 91eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID 92eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 93eb9fecb9SRuslan Bilovol .rates = SNDRV_PCM_RATE_CONTINUOUS, 94eb9fecb9SRuslan Bilovol .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, 95eb9fecb9SRuslan Bilovol .buffer_bytes_max = BUFF_SIZE_MAX, 96eb9fecb9SRuslan Bilovol .period_bytes_max = PRD_SIZE_MAX, 97eb9fecb9SRuslan Bilovol .periods_min = MIN_PERIODS, 98eb9fecb9SRuslan Bilovol }; 99eb9fecb9SRuslan Bilovol 10024f779daSRuslan Bilovol static void u_audio_set_fback_frequency(enum usb_device_speed speed, 101f5dfd98aSPavel Hofman struct usb_ep *out_ep, 102e89bb428SRuslan Bilovol unsigned long long freq, 103e89bb428SRuslan Bilovol unsigned int pitch, 104e89bb428SRuslan Bilovol void *buf) 10524f779daSRuslan Bilovol { 10624f779daSRuslan Bilovol u32 ff = 0; 107f5dfd98aSPavel Hofman const struct usb_endpoint_descriptor *ep_desc; 10824f779daSRuslan Bilovol 109e89bb428SRuslan Bilovol /* 110e89bb428SRuslan Bilovol * Because the pitch base is 1000000, the final divider here 111e89bb428SRuslan Bilovol * will be 1000 * 1000000 = 1953125 << 9 112e89bb428SRuslan Bilovol * 113e89bb428SRuslan Bilovol * Instead of dealing with big numbers lets fold this 9 left shift 114e89bb428SRuslan Bilovol */ 115e89bb428SRuslan Bilovol 11624f779daSRuslan Bilovol if (speed == USB_SPEED_FULL) { 11724f779daSRuslan Bilovol /* 11824f779daSRuslan Bilovol * Full-speed feedback endpoints report frequency 119e89bb428SRuslan Bilovol * in samples/frame 12024f779daSRuslan Bilovol * Format is encoded in Q10.10 left-justified in the 24 bits, 12124f779daSRuslan Bilovol * so that it has a Q10.14 format. 122e89bb428SRuslan Bilovol * 123e89bb428SRuslan Bilovol * ff = (freq << 14) / 1000 12424f779daSRuslan Bilovol */ 125e89bb428SRuslan Bilovol freq <<= 5; 12624f779daSRuslan Bilovol } else { 12724f779daSRuslan Bilovol /* 12824f779daSRuslan Bilovol * High-speed feedback endpoints report frequency 12924f779daSRuslan Bilovol * in samples/microframe. 13024f779daSRuslan Bilovol * Format is encoded in Q12.13 fitted into four bytes so that 13124f779daSRuslan Bilovol * the binary point is located between the second and the third 13224f779daSRuslan Bilovol * byte fromat (that is Q16.16) 133e89bb428SRuslan Bilovol * 134e89bb428SRuslan Bilovol * ff = (freq << 16) / 8000 135f5dfd98aSPavel Hofman * 136f5dfd98aSPavel Hofman * Win10 and OSX UAC2 drivers require number of samples per packet 137f5dfd98aSPavel Hofman * in order to honor the feedback value. 138f5dfd98aSPavel Hofman * Linux snd-usb-audio detects the applied bit-shift automatically. 13924f779daSRuslan Bilovol */ 140f5dfd98aSPavel Hofman ep_desc = out_ep->desc; 141f5dfd98aSPavel Hofman freq <<= 4 + (ep_desc->bInterval - 1); 14224f779daSRuslan Bilovol } 143e89bb428SRuslan Bilovol 144e89bb428SRuslan Bilovol ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 145e89bb428SRuslan Bilovol 14624f779daSRuslan Bilovol *(__le32 *)buf = cpu_to_le32(ff); 14724f779daSRuslan Bilovol } 14824f779daSRuslan Bilovol 149eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) 150eb9fecb9SRuslan Bilovol { 151f4408a98SJonas Stenvall unsigned int pending; 152eb9fecb9SRuslan Bilovol unsigned int hw_ptr; 153eb9fecb9SRuslan Bilovol int status = req->status; 154eb9fecb9SRuslan Bilovol struct snd_pcm_substream *substream; 15596afb54eSVladimir Zapolskiy struct snd_pcm_runtime *runtime; 15629865117SJerome Brunet struct uac_rtd_params *prm = req->context; 157eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 1586fec018aSPavel Hofman unsigned int frames, p_pktsize; 1596fec018aSPavel Hofman unsigned long long pitched_rate_mil, p_pktsize_residue_mil, 1606fec018aSPavel Hofman residue_frames_mil, div_result; 161eb9fecb9SRuslan Bilovol 162eb9fecb9SRuslan Bilovol /* i/f shutting down */ 1637de8681bSJack Pham if (!prm->ep_enabled) { 1647de8681bSJack Pham usb_ep_free_request(ep, req); 1657de8681bSJack Pham return; 1667de8681bSJack Pham } 1677de8681bSJack Pham 1687de8681bSJack Pham if (req->status == -ESHUTDOWN) 169eb9fecb9SRuslan Bilovol return; 170eb9fecb9SRuslan Bilovol 171eb9fecb9SRuslan Bilovol /* 172eb9fecb9SRuslan Bilovol * We can't really do much about bad xfers. 173eb9fecb9SRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 174eb9fecb9SRuslan Bilovol */ 175eb9fecb9SRuslan Bilovol if (status) 176eb9fecb9SRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 177eb9fecb9SRuslan Bilovol __func__, status, req->actual, req->length); 178eb9fecb9SRuslan Bilovol 179eb9fecb9SRuslan Bilovol substream = prm->ss; 180eb9fecb9SRuslan Bilovol 181eb9fecb9SRuslan Bilovol /* Do nothing if ALSA isn't active */ 182eb9fecb9SRuslan Bilovol if (!substream) 183eb9fecb9SRuslan Bilovol goto exit; 184eb9fecb9SRuslan Bilovol 185d70f7598SJerome Brunet snd_pcm_stream_lock(substream); 18656bc6158SVladimir Zapolskiy 18796afb54eSVladimir Zapolskiy runtime = substream->runtime; 18856bc6158SVladimir Zapolskiy if (!runtime || !snd_pcm_running(substream)) { 189d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 19056bc6158SVladimir Zapolskiy goto exit; 19156bc6158SVladimir Zapolskiy } 19256bc6158SVladimir Zapolskiy 193eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 194eb9fecb9SRuslan Bilovol /* 195eb9fecb9SRuslan Bilovol * For each IN packet, take the quotient of the current data 196eb9fecb9SRuslan Bilovol * rate and the endpoint's interval as the base packet size. 197eb9fecb9SRuslan Bilovol * If there is a residue from this division, add it to the 198eb9fecb9SRuslan Bilovol * residue accumulator. 199eb9fecb9SRuslan Bilovol */ 200f2f69bf6SJohn Keeping unsigned long long p_interval_mil = uac->p_interval * 1000000ULL; 201f2f69bf6SJohn Keeping 2028722a949SPavel Hofman pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch; 2036fec018aSPavel Hofman div_result = pitched_rate_mil; 204f2f69bf6SJohn Keeping do_div(div_result, uac->p_interval); 205f2f69bf6SJohn Keeping do_div(div_result, 1000000); 2066fec018aSPavel Hofman frames = (unsigned int) div_result; 2076fec018aSPavel Hofman 2086fec018aSPavel Hofman pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n", 2098722a949SPavel Hofman prm->srate, prm->pitch, p_interval_mil, frames); 2106fec018aSPavel Hofman 2116fec018aSPavel Hofman p_pktsize = min_t(unsigned int, 2126fec018aSPavel Hofman uac->p_framesize * frames, 2136fec018aSPavel Hofman ep->maxpacket); 2146fec018aSPavel Hofman 2156fec018aSPavel Hofman if (p_pktsize < ep->maxpacket) { 216f2f69bf6SJohn Keeping residue_frames_mil = pitched_rate_mil - frames * p_interval_mil; 2176fec018aSPavel Hofman p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil; 2186fec018aSPavel Hofman } else 2196fec018aSPavel Hofman p_pktsize_residue_mil = 0; 2206fec018aSPavel Hofman 2216fec018aSPavel Hofman req->length = p_pktsize; 2226fec018aSPavel Hofman uac->p_residue_mil += p_pktsize_residue_mil; 223eb9fecb9SRuslan Bilovol 224eb9fecb9SRuslan Bilovol /* 2256fec018aSPavel Hofman * Whenever there are more bytes in the accumulator p_residue_mil than we 226eb9fecb9SRuslan Bilovol * need to add one more sample frame, increase this packet's 227eb9fecb9SRuslan Bilovol * size and decrease the accumulator. 228eb9fecb9SRuslan Bilovol */ 2296fec018aSPavel Hofman div_result = uac->p_residue_mil; 230f2f69bf6SJohn Keeping do_div(div_result, uac->p_interval); 231f2f69bf6SJohn Keeping do_div(div_result, 1000000); 2326fec018aSPavel Hofman if ((unsigned int) div_result >= uac->p_framesize) { 233eb9fecb9SRuslan Bilovol req->length += uac->p_framesize; 234f2f69bf6SJohn Keeping uac->p_residue_mil -= uac->p_framesize * p_interval_mil; 2356fec018aSPavel Hofman pr_debug("increased req length to %d\n", req->length); 236eb9fecb9SRuslan Bilovol } 2376fec018aSPavel Hofman pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil); 238eb9fecb9SRuslan Bilovol 239eb9fecb9SRuslan Bilovol req->actual = req->length; 240eb9fecb9SRuslan Bilovol } 241eb9fecb9SRuslan Bilovol 242eb9fecb9SRuslan Bilovol hw_ptr = prm->hw_ptr; 243eb9fecb9SRuslan Bilovol 244eb9fecb9SRuslan Bilovol /* Pack USB load in ALSA ring buffer */ 24596afb54eSVladimir Zapolskiy pending = runtime->dma_bytes - hw_ptr; 246eb9fecb9SRuslan Bilovol 247eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 248eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 24996afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, pending); 25096afb54eSVladimir Zapolskiy memcpy(req->buf + pending, runtime->dma_area, 251eb9fecb9SRuslan Bilovol req->actual - pending); 252eb9fecb9SRuslan Bilovol } else { 25396afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, 25496afb54eSVladimir Zapolskiy req->actual); 255eb9fecb9SRuslan Bilovol } 256eb9fecb9SRuslan Bilovol } else { 257eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 25896afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, pending); 25996afb54eSVladimir Zapolskiy memcpy(runtime->dma_area, req->buf + pending, 260eb9fecb9SRuslan Bilovol req->actual - pending); 261eb9fecb9SRuslan Bilovol } else { 26296afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, 26396afb54eSVladimir Zapolskiy req->actual); 264eb9fecb9SRuslan Bilovol } 265eb9fecb9SRuslan Bilovol } 266eb9fecb9SRuslan Bilovol 2676b37bd78SJoshua Frkuska /* update hw_ptr after data is copied to memory */ 26896afb54eSVladimir Zapolskiy prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; 269773e53d5SVladimir Zapolskiy hw_ptr = prm->hw_ptr; 270d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 2716b37bd78SJoshua Frkuska 272773e53d5SVladimir Zapolskiy if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) 273773e53d5SVladimir Zapolskiy snd_pcm_period_elapsed(substream); 274773e53d5SVladimir Zapolskiy 275eb9fecb9SRuslan Bilovol exit: 276eb9fecb9SRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 277eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 278eb9fecb9SRuslan Bilovol } 279eb9fecb9SRuslan Bilovol 28024f779daSRuslan Bilovol static void u_audio_iso_fback_complete(struct usb_ep *ep, 28124f779daSRuslan Bilovol struct usb_request *req) 28224f779daSRuslan Bilovol { 28324f779daSRuslan Bilovol struct uac_rtd_params *prm = req->context; 28424f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 28524f779daSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 28624f779daSRuslan Bilovol int status = req->status; 28724f779daSRuslan Bilovol 28824f779daSRuslan Bilovol /* i/f shutting down */ 28975432ba5SJerome Brunet if (!prm->fb_ep_enabled) { 29075432ba5SJerome Brunet kfree(req->buf); 29175432ba5SJerome Brunet usb_ep_free_request(ep, req); 29275432ba5SJerome Brunet return; 29375432ba5SJerome Brunet } 29475432ba5SJerome Brunet 29575432ba5SJerome Brunet if (req->status == -ESHUTDOWN) 29624f779daSRuslan Bilovol return; 29724f779daSRuslan Bilovol 29824f779daSRuslan Bilovol /* 29924f779daSRuslan Bilovol * We can't really do much about bad xfers. 30024f779daSRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 30124f779daSRuslan Bilovol */ 30224f779daSRuslan Bilovol if (status) 30324f779daSRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 30424f779daSRuslan Bilovol __func__, status, req->actual, req->length); 30524f779daSRuslan Bilovol 306f5dfd98aSPavel Hofman u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep, 3078722a949SPavel Hofman prm->srate, prm->pitch, 308e89bb428SRuslan Bilovol req->buf); 30924f779daSRuslan Bilovol 31024f779daSRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 31124f779daSRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 31224f779daSRuslan Bilovol } 31324f779daSRuslan Bilovol 314eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 315eb9fecb9SRuslan Bilovol { 316eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 317eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 318eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 319eb9fecb9SRuslan Bilovol struct uac_params *params; 320eb9fecb9SRuslan Bilovol int err = 0; 321eb9fecb9SRuslan Bilovol 322eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 323eb9fecb9SRuslan Bilovol params = &audio_dev->params; 324eb9fecb9SRuslan Bilovol 325eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 326eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 327eb9fecb9SRuslan Bilovol else 328eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 329eb9fecb9SRuslan Bilovol 330eb9fecb9SRuslan Bilovol /* Reset */ 331eb9fecb9SRuslan Bilovol prm->hw_ptr = 0; 332eb9fecb9SRuslan Bilovol 333eb9fecb9SRuslan Bilovol switch (cmd) { 334eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_START: 335eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_RESUME: 336eb9fecb9SRuslan Bilovol prm->ss = substream; 337eb9fecb9SRuslan Bilovol break; 338eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_STOP: 339eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_SUSPEND: 340eb9fecb9SRuslan Bilovol prm->ss = NULL; 341eb9fecb9SRuslan Bilovol break; 342eb9fecb9SRuslan Bilovol default: 343eb9fecb9SRuslan Bilovol err = -EINVAL; 344eb9fecb9SRuslan Bilovol } 345eb9fecb9SRuslan Bilovol 346eb9fecb9SRuslan Bilovol /* Clear buffer after Play stops */ 347eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) 348eb9fecb9SRuslan Bilovol memset(prm->rbuf, 0, prm->max_psize * params->req_number); 349eb9fecb9SRuslan Bilovol 350eb9fecb9SRuslan Bilovol return err; 351eb9fecb9SRuslan Bilovol } 352eb9fecb9SRuslan Bilovol 353eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) 354eb9fecb9SRuslan Bilovol { 355eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 356eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 357eb9fecb9SRuslan Bilovol 358eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 359eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 360eb9fecb9SRuslan Bilovol else 361eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 362eb9fecb9SRuslan Bilovol 363eb9fecb9SRuslan Bilovol return bytes_to_frames(substream->runtime, prm->hw_ptr); 364eb9fecb9SRuslan Bilovol } 365eb9fecb9SRuslan Bilovol 36625dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize) 36725dbd75dSJerome Brunet { 36825dbd75dSJerome Brunet u64 ret; 36925dbd75dSJerome Brunet 37025dbd75dSJerome Brunet switch (ssize) { 37125dbd75dSJerome Brunet case 3: 37225dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S24_3LE; 37325dbd75dSJerome Brunet break; 37425dbd75dSJerome Brunet case 4: 37525dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S32_LE; 37625dbd75dSJerome Brunet break; 37725dbd75dSJerome Brunet default: 37825dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S16_LE; 37925dbd75dSJerome Brunet break; 38025dbd75dSJerome Brunet } 38125dbd75dSJerome Brunet 38225dbd75dSJerome Brunet return ret; 38325dbd75dSJerome Brunet } 38425dbd75dSJerome Brunet 385eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream) 386eb9fecb9SRuslan Bilovol { 387eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 388eb9fecb9SRuslan Bilovol struct snd_pcm_runtime *runtime = substream->runtime; 389eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 390eb9fecb9SRuslan Bilovol struct uac_params *params; 3918722a949SPavel Hofman struct uac_rtd_params *prm; 392eb9fecb9SRuslan Bilovol int p_ssize, c_ssize; 393eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 394eb9fecb9SRuslan Bilovol 395eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 396eb9fecb9SRuslan Bilovol params = &audio_dev->params; 397eb9fecb9SRuslan Bilovol p_ssize = params->p_ssize; 398eb9fecb9SRuslan Bilovol c_ssize = params->c_ssize; 399eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 400eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 4016fec018aSPavel Hofman uac->p_residue_mil = 0; 402eb9fecb9SRuslan Bilovol 403eb9fecb9SRuslan Bilovol runtime->hw = uac_pcm_hardware; 404eb9fecb9SRuslan Bilovol 405eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40625dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(p_ssize); 407eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(p_chmask); 4088722a949SPavel Hofman prm = &uac->p_prm; 409eb9fecb9SRuslan Bilovol } else { 41025dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(c_ssize); 411eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(c_chmask); 4128722a949SPavel Hofman prm = &uac->c_prm; 413eb9fecb9SRuslan Bilovol } 414eb9fecb9SRuslan Bilovol 4158722a949SPavel Hofman runtime->hw.period_bytes_min = 2 * prm->max_psize 4168722a949SPavel Hofman / runtime->hw.periods_min; 4178722a949SPavel Hofman runtime->hw.rate_min = prm->srate; 418eb9fecb9SRuslan Bilovol runtime->hw.rate_max = runtime->hw.rate_min; 419eb9fecb9SRuslan Bilovol runtime->hw.channels_max = runtime->hw.channels_min; 420eb9fecb9SRuslan Bilovol 421eb9fecb9SRuslan Bilovol snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 422eb9fecb9SRuslan Bilovol 423eb9fecb9SRuslan Bilovol return 0; 424eb9fecb9SRuslan Bilovol } 425eb9fecb9SRuslan Bilovol 426eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */ 427eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream) 428eb9fecb9SRuslan Bilovol { 429eb9fecb9SRuslan Bilovol return 0; 430eb9fecb9SRuslan Bilovol } 431eb9fecb9SRuslan Bilovol 432640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = { 433eb9fecb9SRuslan Bilovol .open = uac_pcm_open, 434eb9fecb9SRuslan Bilovol .close = uac_pcm_null, 435eb9fecb9SRuslan Bilovol .trigger = uac_pcm_trigger, 436eb9fecb9SRuslan Bilovol .pointer = uac_pcm_pointer, 437eb9fecb9SRuslan Bilovol .prepare = uac_pcm_null, 438eb9fecb9SRuslan Bilovol }; 439eb9fecb9SRuslan Bilovol 440eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 441eb9fecb9SRuslan Bilovol { 442eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 443eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 444eb9fecb9SRuslan Bilovol struct uac_params *params; 445eb9fecb9SRuslan Bilovol int i; 446eb9fecb9SRuslan Bilovol 447eb9fecb9SRuslan Bilovol if (!prm->ep_enabled) 448eb9fecb9SRuslan Bilovol return; 449eb9fecb9SRuslan Bilovol 450eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 451eb9fecb9SRuslan Bilovol params = &audio_dev->params; 452eb9fecb9SRuslan Bilovol 453eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 45429865117SJerome Brunet if (prm->reqs[i]) { 45529865117SJerome Brunet if (usb_ep_dequeue(ep, prm->reqs[i])) 45629865117SJerome Brunet usb_ep_free_request(ep, prm->reqs[i]); 4577de8681bSJack Pham /* 4587de8681bSJack Pham * If usb_ep_dequeue() cannot successfully dequeue the 4597de8681bSJack Pham * request, the request will be freed by the completion 4607de8681bSJack Pham * callback. 4617de8681bSJack Pham */ 4627de8681bSJack Pham 46329865117SJerome Brunet prm->reqs[i] = NULL; 464eb9fecb9SRuslan Bilovol } 465eb9fecb9SRuslan Bilovol } 466eb9fecb9SRuslan Bilovol 467068fdad2SJerome Brunet prm->ep_enabled = false; 468068fdad2SJerome Brunet 469eb9fecb9SRuslan Bilovol if (usb_ep_disable(ep)) 470eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 471eb9fecb9SRuslan Bilovol } 472eb9fecb9SRuslan Bilovol 47324f779daSRuslan Bilovol static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) 47424f779daSRuslan Bilovol { 47524f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 47624f779daSRuslan Bilovol 47724f779daSRuslan Bilovol if (!prm->fb_ep_enabled) 47824f779daSRuslan Bilovol return; 47924f779daSRuslan Bilovol 48024f779daSRuslan Bilovol if (prm->req_fback) { 48175432ba5SJerome Brunet if (usb_ep_dequeue(ep, prm->req_fback)) { 48224f779daSRuslan Bilovol kfree(prm->req_fback->buf); 48324f779daSRuslan Bilovol usb_ep_free_request(ep, prm->req_fback); 48475432ba5SJerome Brunet } 48524f779daSRuslan Bilovol prm->req_fback = NULL; 48624f779daSRuslan Bilovol } 48724f779daSRuslan Bilovol 488068fdad2SJerome Brunet prm->fb_ep_enabled = false; 489068fdad2SJerome Brunet 49024f779daSRuslan Bilovol if (usb_ep_disable(ep)) 49124f779daSRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 49224f779daSRuslan Bilovol } 493eb9fecb9SRuslan Bilovol 4948fe9a03fSPavel Hofman static void set_active(struct uac_rtd_params *prm, bool active) 4958fe9a03fSPavel Hofman { 4968fe9a03fSPavel Hofman // notifying through the Rate ctrl 4978fe9a03fSPavel Hofman unsigned long flags; 4988fe9a03fSPavel Hofman 4998fe9a03fSPavel Hofman spin_lock_irqsave(&prm->lock, flags); 5008fe9a03fSPavel Hofman if (prm->active != active) { 5018fe9a03fSPavel Hofman prm->active = active; 5028fe9a03fSPavel Hofman snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 503453d3fa9SChris Wulff &prm->snd_kctl_rate_id); 5048fe9a03fSPavel Hofman } 5058fe9a03fSPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 5068fe9a03fSPavel Hofman } 5078fe9a03fSPavel Hofman 508c565ad07SJulian Scheel int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) 509c565ad07SJulian Scheel { 510c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 5118722a949SPavel Hofman struct snd_uac_chip *uac = audio_dev->uac; 5128722a949SPavel Hofman struct uac_rtd_params *prm; 513c565ad07SJulian Scheel int i; 5148722a949SPavel Hofman unsigned long flags; 515c565ad07SJulian Scheel 516c565ad07SJulian Scheel dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 5178722a949SPavel Hofman prm = &uac->c_prm; 518c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 519c565ad07SJulian Scheel if (params->c_srates[i] == srate) { 5208722a949SPavel Hofman spin_lock_irqsave(&prm->lock, flags); 5218722a949SPavel Hofman prm->srate = srate; 5228722a949SPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 523c565ad07SJulian Scheel return 0; 524c565ad07SJulian Scheel } 525c565ad07SJulian Scheel if (params->c_srates[i] == 0) 526c565ad07SJulian Scheel break; 527c565ad07SJulian Scheel } 528c565ad07SJulian Scheel 529c565ad07SJulian Scheel return -EINVAL; 530c565ad07SJulian Scheel } 531c565ad07SJulian Scheel EXPORT_SYMBOL_GPL(u_audio_set_capture_srate); 532c565ad07SJulian Scheel 533eb3a1ce6SPavel Hofman int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val) 534eb3a1ce6SPavel Hofman { 535eb3a1ce6SPavel Hofman struct snd_uac_chip *uac = audio_dev->uac; 536eb3a1ce6SPavel Hofman struct uac_rtd_params *prm; 537eb3a1ce6SPavel Hofman unsigned long flags; 538eb3a1ce6SPavel Hofman 539eb3a1ce6SPavel Hofman prm = &uac->c_prm; 540eb3a1ce6SPavel Hofman spin_lock_irqsave(&prm->lock, flags); 541eb3a1ce6SPavel Hofman *val = prm->srate; 542eb3a1ce6SPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 543eb3a1ce6SPavel Hofman return 0; 544eb3a1ce6SPavel Hofman } 545eb3a1ce6SPavel Hofman EXPORT_SYMBOL_GPL(u_audio_get_capture_srate); 546eb3a1ce6SPavel Hofman 547c565ad07SJulian Scheel int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) 548c565ad07SJulian Scheel { 549c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 5508722a949SPavel Hofman struct snd_uac_chip *uac = audio_dev->uac; 5518722a949SPavel Hofman struct uac_rtd_params *prm; 552c565ad07SJulian Scheel int i; 5538722a949SPavel Hofman unsigned long flags; 554c565ad07SJulian Scheel 555c565ad07SJulian Scheel dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 5568722a949SPavel Hofman prm = &uac->p_prm; 557c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 558c565ad07SJulian Scheel if (params->p_srates[i] == srate) { 5598722a949SPavel Hofman spin_lock_irqsave(&prm->lock, flags); 5608722a949SPavel Hofman prm->srate = srate; 5618722a949SPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 562c565ad07SJulian Scheel return 0; 563c565ad07SJulian Scheel } 564c565ad07SJulian Scheel if (params->p_srates[i] == 0) 565c565ad07SJulian Scheel break; 566c565ad07SJulian Scheel } 567c565ad07SJulian Scheel 568c565ad07SJulian Scheel return -EINVAL; 569c565ad07SJulian Scheel } 570c565ad07SJulian Scheel EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); 571c565ad07SJulian Scheel 572eb3a1ce6SPavel Hofman int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val) 573eb3a1ce6SPavel Hofman { 574eb3a1ce6SPavel Hofman struct snd_uac_chip *uac = audio_dev->uac; 575eb3a1ce6SPavel Hofman struct uac_rtd_params *prm; 576eb3a1ce6SPavel Hofman unsigned long flags; 577eb3a1ce6SPavel Hofman 578eb3a1ce6SPavel Hofman prm = &uac->p_prm; 579eb3a1ce6SPavel Hofman spin_lock_irqsave(&prm->lock, flags); 580eb3a1ce6SPavel Hofman *val = prm->srate; 581eb3a1ce6SPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 582eb3a1ce6SPavel Hofman return 0; 583eb3a1ce6SPavel Hofman } 584eb3a1ce6SPavel Hofman EXPORT_SYMBOL_GPL(u_audio_get_playback_srate); 585eb3a1ce6SPavel Hofman 586eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev) 587eb9fecb9SRuslan Bilovol { 588eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 589eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 590eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 59124f779daSRuslan Bilovol struct usb_request *req, *req_fback; 59224f779daSRuslan Bilovol struct usb_ep *ep, *ep_fback; 593eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 594eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 595eb9fecb9SRuslan Bilovol int req_len, i; 596eb9fecb9SRuslan Bilovol 597eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 5988722a949SPavel Hofman dev_dbg(dev, "start capture with rate %d\n", prm->srate); 5998722a949SPavel Hofman ep = audio_dev->out_ep; 600eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 601904967c6SJohn Keeping req_len = ep->maxpacket; 602eb9fecb9SRuslan Bilovol 603eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 604eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 605eb9fecb9SRuslan Bilovol 606eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 60729865117SJerome Brunet if (!prm->reqs[i]) { 608eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 609eb9fecb9SRuslan Bilovol if (req == NULL) 610eb9fecb9SRuslan Bilovol return -ENOMEM; 611eb9fecb9SRuslan Bilovol 61229865117SJerome Brunet prm->reqs[i] = req; 613eb9fecb9SRuslan Bilovol 614eb9fecb9SRuslan Bilovol req->zero = 0; 61529865117SJerome Brunet req->context = prm; 616eb9fecb9SRuslan Bilovol req->length = req_len; 617eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 618904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 619eb9fecb9SRuslan Bilovol } 620eb9fecb9SRuslan Bilovol 62129865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 622eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 623eb9fecb9SRuslan Bilovol } 624eb9fecb9SRuslan Bilovol 6258fe9a03fSPavel Hofman set_active(&uac->c_prm, true); 6268fe9a03fSPavel Hofman 62724f779daSRuslan Bilovol ep_fback = audio_dev->in_ep_fback; 62824f779daSRuslan Bilovol if (!ep_fback) 62924f779daSRuslan Bilovol return 0; 63024f779daSRuslan Bilovol 63124f779daSRuslan Bilovol /* Setup feedback endpoint */ 63224f779daSRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep_fback); 63324f779daSRuslan Bilovol prm->fb_ep_enabled = true; 63424f779daSRuslan Bilovol usb_ep_enable(ep_fback); 63524f779daSRuslan Bilovol req_len = ep_fback->maxpacket; 63624f779daSRuslan Bilovol 63724f779daSRuslan Bilovol req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); 63824f779daSRuslan Bilovol if (req_fback == NULL) 63924f779daSRuslan Bilovol return -ENOMEM; 64024f779daSRuslan Bilovol 64124f779daSRuslan Bilovol prm->req_fback = req_fback; 64224f779daSRuslan Bilovol req_fback->zero = 0; 64324f779daSRuslan Bilovol req_fback->context = prm; 64424f779daSRuslan Bilovol req_fback->length = req_len; 64524f779daSRuslan Bilovol req_fback->complete = u_audio_iso_fback_complete; 64624f779daSRuslan Bilovol 64724f779daSRuslan Bilovol req_fback->buf = kzalloc(req_len, GFP_ATOMIC); 64824f779daSRuslan Bilovol if (!req_fback->buf) 64924f779daSRuslan Bilovol return -ENOMEM; 65024f779daSRuslan Bilovol 65124f779daSRuslan Bilovol /* 65224f779daSRuslan Bilovol * Configure the feedback endpoint's reported frequency. 65324f779daSRuslan Bilovol * Always start with original frequency since its deviation can't 65424f779daSRuslan Bilovol * be meauserd at start of playback 65524f779daSRuslan Bilovol */ 656e89bb428SRuslan Bilovol prm->pitch = 1000000; 657f5dfd98aSPavel Hofman u_audio_set_fback_frequency(audio_dev->gadget->speed, ep, 6588722a949SPavel Hofman prm->srate, prm->pitch, 659e89bb428SRuslan Bilovol req_fback->buf); 66024f779daSRuslan Bilovol 66124f779daSRuslan Bilovol if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) 66224f779daSRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 66324f779daSRuslan Bilovol 664eb9fecb9SRuslan Bilovol return 0; 665eb9fecb9SRuslan Bilovol } 666eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture); 667eb9fecb9SRuslan Bilovol 668eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev) 669eb9fecb9SRuslan Bilovol { 670eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 671eb9fecb9SRuslan Bilovol 6728fe9a03fSPavel Hofman set_active(&uac->c_prm, false); 67324f779daSRuslan Bilovol if (audio_dev->in_ep_fback) 67424f779daSRuslan Bilovol free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 675eb9fecb9SRuslan Bilovol free_ep(&uac->c_prm, audio_dev->out_ep); 676eb9fecb9SRuslan Bilovol } 677eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture); 678eb9fecb9SRuslan Bilovol 679eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev) 680eb9fecb9SRuslan Bilovol { 681eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 682eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 683eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 684eb9fecb9SRuslan Bilovol struct usb_request *req; 685eb9fecb9SRuslan Bilovol struct usb_ep *ep; 686eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 687eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 6886b02af34SJohn Keeping unsigned int factor; 689eb9fecb9SRuslan Bilovol const struct usb_endpoint_descriptor *ep_desc; 690eb9fecb9SRuslan Bilovol int req_len, i; 691f2f69bf6SJohn Keeping unsigned int p_pktsize; 692eb9fecb9SRuslan Bilovol 693eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 6948722a949SPavel Hofman dev_dbg(dev, "start playback with rate %d\n", prm->srate); 6958722a949SPavel Hofman ep = audio_dev->in_ep; 696eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 697eb9fecb9SRuslan Bilovol 698eb9fecb9SRuslan Bilovol ep_desc = ep->desc; 6996fec018aSPavel Hofman /* 7006fec018aSPavel Hofman * Always start with original frequency 7016fec018aSPavel Hofman */ 7026fec018aSPavel Hofman prm->pitch = 1000000; 703eb9fecb9SRuslan Bilovol 704eb9fecb9SRuslan Bilovol /* pre-calculate the playback endpoint's interval */ 705eb9fecb9SRuslan Bilovol if (gadget->speed == USB_SPEED_FULL) 706eb9fecb9SRuslan Bilovol factor = 1000; 707eb9fecb9SRuslan Bilovol else 708eb9fecb9SRuslan Bilovol factor = 8000; 709eb9fecb9SRuslan Bilovol 710eb9fecb9SRuslan Bilovol /* pre-compute some values for iso_complete() */ 711eb9fecb9SRuslan Bilovol uac->p_framesize = params->p_ssize * 712eb9fecb9SRuslan Bilovol num_channels(params->p_chmask); 713f2f69bf6SJohn Keeping uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); 7146fec018aSPavel Hofman p_pktsize = min_t(unsigned int, 7156b02af34SJohn Keeping uac->p_framesize * 7168722a949SPavel Hofman (prm->srate / uac->p_interval), 717904967c6SJohn Keeping ep->maxpacket); 718eb9fecb9SRuslan Bilovol 7196fec018aSPavel Hofman req_len = p_pktsize; 7206fec018aSPavel Hofman uac->p_residue_mil = 0; 721eb9fecb9SRuslan Bilovol 722eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 723eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 724eb9fecb9SRuslan Bilovol 725eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 72629865117SJerome Brunet if (!prm->reqs[i]) { 727eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 728eb9fecb9SRuslan Bilovol if (req == NULL) 729eb9fecb9SRuslan Bilovol return -ENOMEM; 730eb9fecb9SRuslan Bilovol 73129865117SJerome Brunet prm->reqs[i] = req; 732eb9fecb9SRuslan Bilovol 733eb9fecb9SRuslan Bilovol req->zero = 0; 73429865117SJerome Brunet req->context = prm; 735eb9fecb9SRuslan Bilovol req->length = req_len; 736eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 737904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 738eb9fecb9SRuslan Bilovol } 739eb9fecb9SRuslan Bilovol 74029865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 741eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 742eb9fecb9SRuslan Bilovol } 743eb9fecb9SRuslan Bilovol 7448fe9a03fSPavel Hofman set_active(&uac->p_prm, true); 7458fe9a03fSPavel Hofman 746eb9fecb9SRuslan Bilovol return 0; 747eb9fecb9SRuslan Bilovol } 748eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback); 749eb9fecb9SRuslan Bilovol 750eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev) 751eb9fecb9SRuslan Bilovol { 752eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 753eb9fecb9SRuslan Bilovol 7548fe9a03fSPavel Hofman set_active(&uac->p_prm, false); 755eb9fecb9SRuslan Bilovol free_ep(&uac->p_prm, audio_dev->in_ep); 756eb9fecb9SRuslan Bilovol } 757eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback); 758eb9fecb9SRuslan Bilovol 75962385cf1SPavel Hofman void u_audio_suspend(struct g_audio *audio_dev) 76062385cf1SPavel Hofman { 76162385cf1SPavel Hofman struct snd_uac_chip *uac = audio_dev->uac; 76262385cf1SPavel Hofman 76362385cf1SPavel Hofman set_active(&uac->p_prm, false); 76462385cf1SPavel Hofman set_active(&uac->c_prm, false); 76562385cf1SPavel Hofman } 76662385cf1SPavel Hofman EXPORT_SYMBOL_GPL(u_audio_suspend); 76762385cf1SPavel Hofman 76802de698cSRuslan Bilovol int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) 76902de698cSRuslan Bilovol { 77002de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 77102de698cSRuslan Bilovol struct uac_rtd_params *prm; 77202de698cSRuslan Bilovol unsigned long flags; 77302de698cSRuslan Bilovol 77402de698cSRuslan Bilovol if (playback) 77502de698cSRuslan Bilovol prm = &uac->p_prm; 77602de698cSRuslan Bilovol else 77702de698cSRuslan Bilovol prm = &uac->c_prm; 77802de698cSRuslan Bilovol 77902de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 78002de698cSRuslan Bilovol *val = prm->volume; 78102de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 78202de698cSRuslan Bilovol 78302de698cSRuslan Bilovol return 0; 78402de698cSRuslan Bilovol } 78502de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_volume); 78602de698cSRuslan Bilovol 78702de698cSRuslan Bilovol int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) 78802de698cSRuslan Bilovol { 78902de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 79002de698cSRuslan Bilovol struct uac_rtd_params *prm; 79102de698cSRuslan Bilovol unsigned long flags; 79202de698cSRuslan Bilovol int change = 0; 79302de698cSRuslan Bilovol 79402de698cSRuslan Bilovol if (playback) 79502de698cSRuslan Bilovol prm = &uac->p_prm; 79602de698cSRuslan Bilovol else 79702de698cSRuslan Bilovol prm = &uac->c_prm; 79802de698cSRuslan Bilovol 79902de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 80002de698cSRuslan Bilovol val = clamp(val, prm->volume_min, prm->volume_max); 80102de698cSRuslan Bilovol if (prm->volume != val) { 80202de698cSRuslan Bilovol prm->volume = val; 80302de698cSRuslan Bilovol change = 1; 80402de698cSRuslan Bilovol } 80502de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 80602de698cSRuslan Bilovol 80702de698cSRuslan Bilovol if (change) 80802de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 809453d3fa9SChris Wulff &prm->snd_kctl_volume_id); 81002de698cSRuslan Bilovol 81102de698cSRuslan Bilovol return 0; 81202de698cSRuslan Bilovol } 81302de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_volume); 81402de698cSRuslan Bilovol 81502de698cSRuslan Bilovol int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) 81602de698cSRuslan Bilovol { 81702de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 81802de698cSRuslan Bilovol struct uac_rtd_params *prm; 81902de698cSRuslan Bilovol unsigned long flags; 82002de698cSRuslan Bilovol 82102de698cSRuslan Bilovol if (playback) 82202de698cSRuslan Bilovol prm = &uac->p_prm; 82302de698cSRuslan Bilovol else 82402de698cSRuslan Bilovol prm = &uac->c_prm; 82502de698cSRuslan Bilovol 82602de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 82702de698cSRuslan Bilovol *val = prm->mute; 82802de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 82902de698cSRuslan Bilovol 83002de698cSRuslan Bilovol return 0; 83102de698cSRuslan Bilovol } 83202de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_mute); 83302de698cSRuslan Bilovol 83402de698cSRuslan Bilovol int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) 83502de698cSRuslan Bilovol { 83602de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 83702de698cSRuslan Bilovol struct uac_rtd_params *prm; 83802de698cSRuslan Bilovol unsigned long flags; 83902de698cSRuslan Bilovol int change = 0; 84002de698cSRuslan Bilovol int mute; 84102de698cSRuslan Bilovol 84202de698cSRuslan Bilovol if (playback) 84302de698cSRuslan Bilovol prm = &uac->p_prm; 84402de698cSRuslan Bilovol else 84502de698cSRuslan Bilovol prm = &uac->c_prm; 84602de698cSRuslan Bilovol 84702de698cSRuslan Bilovol mute = val ? 1 : 0; 84802de698cSRuslan Bilovol 84902de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 85002de698cSRuslan Bilovol if (prm->mute != mute) { 85102de698cSRuslan Bilovol prm->mute = mute; 85202de698cSRuslan Bilovol change = 1; 85302de698cSRuslan Bilovol } 85402de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 85502de698cSRuslan Bilovol 85602de698cSRuslan Bilovol if (change) 85702de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 858453d3fa9SChris Wulff &prm->snd_kctl_mute_id); 85902de698cSRuslan Bilovol 86002de698cSRuslan Bilovol return 0; 86102de698cSRuslan Bilovol } 86202de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_mute); 86302de698cSRuslan Bilovol 86402de698cSRuslan Bilovol 865e89bb428SRuslan Bilovol static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, 866e89bb428SRuslan Bilovol struct snd_ctl_elem_info *uinfo) 867e89bb428SRuslan Bilovol { 868e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 869e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 870e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 871e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 872e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 873e89bb428SRuslan Bilovol 874e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 875e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 876e89bb428SRuslan Bilovol 877e89bb428SRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 878e89bb428SRuslan Bilovol uinfo->count = 1; 879e89bb428SRuslan Bilovol uinfo->value.integer.min = pitch_min; 880e89bb428SRuslan Bilovol uinfo->value.integer.max = pitch_max; 881e89bb428SRuslan Bilovol uinfo->value.integer.step = 1; 882e89bb428SRuslan Bilovol return 0; 883e89bb428SRuslan Bilovol } 884e89bb428SRuslan Bilovol 885e89bb428SRuslan Bilovol static int u_audio_pitch_get(struct snd_kcontrol *kcontrol, 886e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 887e89bb428SRuslan Bilovol { 888e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 889e89bb428SRuslan Bilovol 890e89bb428SRuslan Bilovol ucontrol->value.integer.value[0] = prm->pitch; 891e89bb428SRuslan Bilovol 892e89bb428SRuslan Bilovol return 0; 893e89bb428SRuslan Bilovol } 894e89bb428SRuslan Bilovol 895e89bb428SRuslan Bilovol static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, 896e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 897e89bb428SRuslan Bilovol { 898e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 899e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 900e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 901e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 902e89bb428SRuslan Bilovol unsigned int val; 903e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 904e89bb428SRuslan Bilovol int change = 0; 905e89bb428SRuslan Bilovol 906e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 907e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 908e89bb428SRuslan Bilovol 909e89bb428SRuslan Bilovol val = ucontrol->value.integer.value[0]; 910e89bb428SRuslan Bilovol 911e89bb428SRuslan Bilovol if (val < pitch_min) 912e89bb428SRuslan Bilovol val = pitch_min; 913e89bb428SRuslan Bilovol if (val > pitch_max) 914e89bb428SRuslan Bilovol val = pitch_max; 915e89bb428SRuslan Bilovol 916e89bb428SRuslan Bilovol if (prm->pitch != val) { 917e89bb428SRuslan Bilovol prm->pitch = val; 918e89bb428SRuslan Bilovol change = 1; 919e89bb428SRuslan Bilovol } 920e89bb428SRuslan Bilovol 921e89bb428SRuslan Bilovol return change; 922e89bb428SRuslan Bilovol } 923e89bb428SRuslan Bilovol 92402de698cSRuslan Bilovol static int u_audio_mute_info(struct snd_kcontrol *kcontrol, 92502de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 926e89bb428SRuslan Bilovol { 92702de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 92802de698cSRuslan Bilovol uinfo->count = 1; 92902de698cSRuslan Bilovol uinfo->value.integer.min = 0; 93002de698cSRuslan Bilovol uinfo->value.integer.max = 1; 93102de698cSRuslan Bilovol uinfo->value.integer.step = 1; 93202de698cSRuslan Bilovol 93302de698cSRuslan Bilovol return 0; 93402de698cSRuslan Bilovol } 93502de698cSRuslan Bilovol 93602de698cSRuslan Bilovol static int u_audio_mute_get(struct snd_kcontrol *kcontrol, 93702de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 93802de698cSRuslan Bilovol { 93902de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 94002de698cSRuslan Bilovol unsigned long flags; 94102de698cSRuslan Bilovol 94202de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 94302de698cSRuslan Bilovol ucontrol->value.integer.value[0] = !prm->mute; 94402de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 94502de698cSRuslan Bilovol 94602de698cSRuslan Bilovol return 0; 94702de698cSRuslan Bilovol } 94802de698cSRuslan Bilovol 94902de698cSRuslan Bilovol static int u_audio_mute_put(struct snd_kcontrol *kcontrol, 95002de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 95102de698cSRuslan Bilovol { 95202de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 95302de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 95402de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 95502de698cSRuslan Bilovol unsigned int val; 95602de698cSRuslan Bilovol unsigned long flags; 95702de698cSRuslan Bilovol int change = 0; 95802de698cSRuslan Bilovol 95902de698cSRuslan Bilovol val = !ucontrol->value.integer.value[0]; 96002de698cSRuslan Bilovol 96102de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 96202de698cSRuslan Bilovol if (val != prm->mute) { 96302de698cSRuslan Bilovol prm->mute = val; 96402de698cSRuslan Bilovol change = 1; 96502de698cSRuslan Bilovol } 96602de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 96702de698cSRuslan Bilovol 96802de698cSRuslan Bilovol if (change && audio_dev->notify) 96902de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); 97002de698cSRuslan Bilovol 97102de698cSRuslan Bilovol return change; 97202de698cSRuslan Bilovol } 97302de698cSRuslan Bilovol 97402de698cSRuslan Bilovol /* 97502de698cSRuslan Bilovol * TLV callback for mixer volume controls 97602de698cSRuslan Bilovol */ 97702de698cSRuslan Bilovol static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, 97802de698cSRuslan Bilovol unsigned int size, unsigned int __user *_tlv) 97902de698cSRuslan Bilovol { 98002de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 98102de698cSRuslan Bilovol DECLARE_TLV_DB_MINMAX(scale, 0, 0); 98202de698cSRuslan Bilovol 98302de698cSRuslan Bilovol if (size < sizeof(scale)) 98402de698cSRuslan Bilovol return -ENOMEM; 98502de698cSRuslan Bilovol 98602de698cSRuslan Bilovol /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ 98702de698cSRuslan Bilovol scale[2] = (prm->volume_min * 100) / 256; 98802de698cSRuslan Bilovol scale[3] = (prm->volume_max * 100) / 256; 98902de698cSRuslan Bilovol if (copy_to_user(_tlv, scale, sizeof(scale))) 99002de698cSRuslan Bilovol return -EFAULT; 99102de698cSRuslan Bilovol 99202de698cSRuslan Bilovol return 0; 99302de698cSRuslan Bilovol } 99402de698cSRuslan Bilovol 99502de698cSRuslan Bilovol static int u_audio_volume_info(struct snd_kcontrol *kcontrol, 99602de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 99702de698cSRuslan Bilovol { 99802de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 99902de698cSRuslan Bilovol 100002de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 100102de698cSRuslan Bilovol uinfo->count = 1; 100202de698cSRuslan Bilovol uinfo->value.integer.min = 0; 100302de698cSRuslan Bilovol uinfo->value.integer.max = 100402de698cSRuslan Bilovol (prm->volume_max - prm->volume_min + prm->volume_res - 1) 100502de698cSRuslan Bilovol / prm->volume_res; 100602de698cSRuslan Bilovol uinfo->value.integer.step = 1; 100702de698cSRuslan Bilovol 100802de698cSRuslan Bilovol return 0; 100902de698cSRuslan Bilovol } 101002de698cSRuslan Bilovol 101102de698cSRuslan Bilovol static int u_audio_volume_get(struct snd_kcontrol *kcontrol, 101202de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 101302de698cSRuslan Bilovol { 101402de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 101502de698cSRuslan Bilovol unsigned long flags; 101602de698cSRuslan Bilovol 101702de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 101802de698cSRuslan Bilovol ucontrol->value.integer.value[0] = 101902de698cSRuslan Bilovol (prm->volume - prm->volume_min) / prm->volume_res; 102002de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 102102de698cSRuslan Bilovol 102202de698cSRuslan Bilovol return 0; 102302de698cSRuslan Bilovol } 102402de698cSRuslan Bilovol 102502de698cSRuslan Bilovol static int u_audio_volume_put(struct snd_kcontrol *kcontrol, 102602de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 102702de698cSRuslan Bilovol { 102802de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 102902de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 103002de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 103102de698cSRuslan Bilovol unsigned int val; 103202de698cSRuslan Bilovol s16 volume; 103302de698cSRuslan Bilovol unsigned long flags; 103402de698cSRuslan Bilovol int change = 0; 103502de698cSRuslan Bilovol 103602de698cSRuslan Bilovol val = ucontrol->value.integer.value[0]; 103702de698cSRuslan Bilovol 103802de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 103902de698cSRuslan Bilovol volume = (val * prm->volume_res) + prm->volume_min; 104002de698cSRuslan Bilovol volume = clamp(volume, prm->volume_min, prm->volume_max); 104102de698cSRuslan Bilovol if (volume != prm->volume) { 104202de698cSRuslan Bilovol prm->volume = volume; 104302de698cSRuslan Bilovol change = 1; 104402de698cSRuslan Bilovol } 104502de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 104602de698cSRuslan Bilovol 104702de698cSRuslan Bilovol if (change && audio_dev->notify) 104802de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); 104902de698cSRuslan Bilovol 105002de698cSRuslan Bilovol return change; 105102de698cSRuslan Bilovol } 105202de698cSRuslan Bilovol 1053c565ad07SJulian Scheel static int get_max_srate(const int *srates) 1054c565ad07SJulian Scheel { 1055c565ad07SJulian Scheel int i, max_srate = 0; 1056c565ad07SJulian Scheel 1057c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 1058c565ad07SJulian Scheel if (srates[i] == 0) 1059c565ad07SJulian Scheel break; 1060c565ad07SJulian Scheel if (srates[i] > max_srate) 1061c565ad07SJulian Scheel max_srate = srates[i]; 1062c565ad07SJulian Scheel } 1063c565ad07SJulian Scheel return max_srate; 1064c565ad07SJulian Scheel } 1065c565ad07SJulian Scheel 1066c565ad07SJulian Scheel static int get_min_srate(const int *srates) 1067c565ad07SJulian Scheel { 1068c565ad07SJulian Scheel int i, min_srate = INT_MAX; 1069c565ad07SJulian Scheel 1070c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 1071c565ad07SJulian Scheel if (srates[i] == 0) 1072c565ad07SJulian Scheel break; 1073c565ad07SJulian Scheel if (srates[i] < min_srate) 1074c565ad07SJulian Scheel min_srate = srates[i]; 1075c565ad07SJulian Scheel } 1076c565ad07SJulian Scheel return min_srate; 1077c565ad07SJulian Scheel } 1078c565ad07SJulian Scheel 1079c565ad07SJulian Scheel static int u_audio_rate_info(struct snd_kcontrol *kcontrol, 1080c565ad07SJulian Scheel struct snd_ctl_elem_info *uinfo) 1081c565ad07SJulian Scheel { 1082c565ad07SJulian Scheel const int *srates; 1083c565ad07SJulian Scheel struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 1084c565ad07SJulian Scheel struct snd_uac_chip *uac = prm->uac; 1085c565ad07SJulian Scheel struct g_audio *audio_dev = uac->audio_dev; 1086c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 1087c565ad07SJulian Scheel 1088c565ad07SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1089c565ad07SJulian Scheel uinfo->count = 1; 1090c565ad07SJulian Scheel 1091c565ad07SJulian Scheel if (prm == &uac->c_prm) 1092c565ad07SJulian Scheel srates = params->c_srates; 1093c565ad07SJulian Scheel else 1094c565ad07SJulian Scheel srates = params->p_srates; 1095c565ad07SJulian Scheel uinfo->value.integer.min = get_min_srate(srates); 1096c565ad07SJulian Scheel uinfo->value.integer.max = get_max_srate(srates); 1097c565ad07SJulian Scheel return 0; 1098c565ad07SJulian Scheel } 1099c565ad07SJulian Scheel 1100c565ad07SJulian Scheel static int u_audio_rate_get(struct snd_kcontrol *kcontrol, 1101c565ad07SJulian Scheel struct snd_ctl_elem_value *ucontrol) 1102c565ad07SJulian Scheel { 1103c565ad07SJulian Scheel struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 11048722a949SPavel Hofman unsigned long flags; 1105c565ad07SJulian Scheel 11068722a949SPavel Hofman spin_lock_irqsave(&prm->lock, flags); 11078fe9a03fSPavel Hofman if (prm->active) 11088722a949SPavel Hofman ucontrol->value.integer.value[0] = prm->srate; 11098fe9a03fSPavel Hofman else 11108fe9a03fSPavel Hofman /* not active: reporting zero rate */ 11118fe9a03fSPavel Hofman ucontrol->value.integer.value[0] = 0; 11128722a949SPavel Hofman spin_unlock_irqrestore(&prm->lock, flags); 1113c565ad07SJulian Scheel return 0; 1114c565ad07SJulian Scheel } 111502de698cSRuslan Bilovol 111602de698cSRuslan Bilovol static struct snd_kcontrol_new u_audio_controls[] = { 111702de698cSRuslan Bilovol [UAC_FBACK_CTRL] { 1118e89bb428SRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_PCM, 1119e89bb428SRuslan Bilovol .name = "Capture Pitch 1000000", 1120e89bb428SRuslan Bilovol .info = u_audio_pitch_info, 1121e89bb428SRuslan Bilovol .get = u_audio_pitch_get, 1122e89bb428SRuslan Bilovol .put = u_audio_pitch_put, 1123e89bb428SRuslan Bilovol }, 11246fec018aSPavel Hofman [UAC_P_PITCH_CTRL] { 11256fec018aSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_PCM, 11266fec018aSPavel Hofman .name = "Playback Pitch 1000000", 11276fec018aSPavel Hofman .info = u_audio_pitch_info, 11286fec018aSPavel Hofman .get = u_audio_pitch_get, 11296fec018aSPavel Hofman .put = u_audio_pitch_put, 11306fec018aSPavel Hofman }, 113102de698cSRuslan Bilovol [UAC_MUTE_CTRL] { 113202de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 113302de698cSRuslan Bilovol .name = "", /* will be filled later */ 113402de698cSRuslan Bilovol .info = u_audio_mute_info, 113502de698cSRuslan Bilovol .get = u_audio_mute_get, 113602de698cSRuslan Bilovol .put = u_audio_mute_put, 113702de698cSRuslan Bilovol }, 113802de698cSRuslan Bilovol [UAC_VOLUME_CTRL] { 113902de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 114002de698cSRuslan Bilovol .name = "", /* will be filled later */ 114102de698cSRuslan Bilovol .info = u_audio_volume_info, 114202de698cSRuslan Bilovol .get = u_audio_volume_get, 114302de698cSRuslan Bilovol .put = u_audio_volume_put, 114402de698cSRuslan Bilovol }, 1145c565ad07SJulian Scheel [UAC_RATE_CTRL] { 1146c565ad07SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_PCM, 1147c565ad07SJulian Scheel .name = "", /* will be filled later */ 1148c565ad07SJulian Scheel .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 1149c565ad07SJulian Scheel .info = u_audio_rate_info, 1150c565ad07SJulian Scheel .get = u_audio_rate_get, 1151c565ad07SJulian Scheel }, 1152e89bb428SRuslan Bilovol }; 1153e89bb428SRuslan Bilovol 1154eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, 1155eb9fecb9SRuslan Bilovol const char *card_name) 1156eb9fecb9SRuslan Bilovol { 1157eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 1158eb9fecb9SRuslan Bilovol struct snd_card *card; 1159eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 1160e89bb428SRuslan Bilovol struct snd_kcontrol *kctl; 1161eb9fecb9SRuslan Bilovol struct uac_params *params; 1162eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 116302de698cSRuslan Bilovol int i, err; 1164eb9fecb9SRuslan Bilovol 1165eb9fecb9SRuslan Bilovol if (!g_audio) 1166eb9fecb9SRuslan Bilovol return -EINVAL; 1167eb9fecb9SRuslan Bilovol 1168eb9fecb9SRuslan Bilovol uac = kzalloc(sizeof(*uac), GFP_KERNEL); 1169eb9fecb9SRuslan Bilovol if (!uac) 1170eb9fecb9SRuslan Bilovol return -ENOMEM; 1171eb9fecb9SRuslan Bilovol g_audio->uac = uac; 1172eb9fecb9SRuslan Bilovol uac->audio_dev = g_audio; 1173eb9fecb9SRuslan Bilovol 1174eb9fecb9SRuslan Bilovol params = &g_audio->params; 1175eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 1176eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 1177eb9fecb9SRuslan Bilovol 1178eb9fecb9SRuslan Bilovol if (c_chmask) { 1179eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->c_prm; 1180eb9fecb9SRuslan Bilovol 118102de698cSRuslan Bilovol spin_lock_init(&prm->lock); 1182eb9fecb9SRuslan Bilovol uac->c_prm.uac = uac; 1183eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->out_ep_maxpsize; 11848722a949SPavel Hofman prm->srate = params->c_srates[0]; 1185eb9fecb9SRuslan Bilovol 118629865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 118729865117SJerome Brunet sizeof(struct usb_request *), 1188eb9fecb9SRuslan Bilovol GFP_KERNEL); 118929865117SJerome Brunet if (!prm->reqs) { 1190eb9fecb9SRuslan Bilovol err = -ENOMEM; 1191eb9fecb9SRuslan Bilovol goto fail; 1192eb9fecb9SRuslan Bilovol } 1193eb9fecb9SRuslan Bilovol 1194eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 1195eb9fecb9SRuslan Bilovol GFP_KERNEL); 1196eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 1197eb9fecb9SRuslan Bilovol prm->max_psize = 0; 1198eb9fecb9SRuslan Bilovol err = -ENOMEM; 1199eb9fecb9SRuslan Bilovol goto fail; 1200eb9fecb9SRuslan Bilovol } 1201eb9fecb9SRuslan Bilovol } 1202eb9fecb9SRuslan Bilovol 1203eb9fecb9SRuslan Bilovol if (p_chmask) { 1204eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->p_prm; 1205eb9fecb9SRuslan Bilovol 120602de698cSRuslan Bilovol spin_lock_init(&prm->lock); 1207eb9fecb9SRuslan Bilovol uac->p_prm.uac = uac; 1208eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->in_ep_maxpsize; 12098722a949SPavel Hofman prm->srate = params->p_srates[0]; 1210eb9fecb9SRuslan Bilovol 121129865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 121229865117SJerome Brunet sizeof(struct usb_request *), 1213eb9fecb9SRuslan Bilovol GFP_KERNEL); 121429865117SJerome Brunet if (!prm->reqs) { 1215eb9fecb9SRuslan Bilovol err = -ENOMEM; 1216eb9fecb9SRuslan Bilovol goto fail; 1217eb9fecb9SRuslan Bilovol } 1218eb9fecb9SRuslan Bilovol 1219eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 1220eb9fecb9SRuslan Bilovol GFP_KERNEL); 1221eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 1222eb9fecb9SRuslan Bilovol prm->max_psize = 0; 1223eb9fecb9SRuslan Bilovol err = -ENOMEM; 1224eb9fecb9SRuslan Bilovol goto fail; 1225eb9fecb9SRuslan Bilovol } 1226eb9fecb9SRuslan Bilovol } 1227eb9fecb9SRuslan Bilovol 1228eb9fecb9SRuslan Bilovol /* Choose any slot, with no id */ 1229eb9fecb9SRuslan Bilovol err = snd_card_new(&g_audio->gadget->dev, 1230eb9fecb9SRuslan Bilovol -1, NULL, THIS_MODULE, 0, &card); 1231eb9fecb9SRuslan Bilovol if (err < 0) 1232eb9fecb9SRuslan Bilovol goto fail; 1233eb9fecb9SRuslan Bilovol 1234eb9fecb9SRuslan Bilovol uac->card = card; 1235eb9fecb9SRuslan Bilovol 1236eb9fecb9SRuslan Bilovol /* 1237eb9fecb9SRuslan Bilovol * Create first PCM device 1238eb9fecb9SRuslan Bilovol * Create a substream only for non-zero channel streams 1239eb9fecb9SRuslan Bilovol */ 1240eb9fecb9SRuslan Bilovol err = snd_pcm_new(uac->card, pcm_name, 0, 1241eb9fecb9SRuslan Bilovol p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); 1242eb9fecb9SRuslan Bilovol if (err < 0) 1243eb9fecb9SRuslan Bilovol goto snd_fail; 1244eb9fecb9SRuslan Bilovol 1245d23922fcSRuslan Bilovol strscpy(pcm->name, pcm_name, sizeof(pcm->name)); 1246eb9fecb9SRuslan Bilovol pcm->private_data = uac; 1247eb9fecb9SRuslan Bilovol uac->pcm = pcm; 1248eb9fecb9SRuslan Bilovol 1249eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); 1250eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); 1251eb9fecb9SRuslan Bilovol 125202de698cSRuslan Bilovol /* 125302de698cSRuslan Bilovol * Create mixer and controls 125402de698cSRuslan Bilovol * Create only if it's required on USB side 125502de698cSRuslan Bilovol */ 125602de698cSRuslan Bilovol if ((c_chmask && g_audio->in_ep_fback) 125702de698cSRuslan Bilovol || (p_chmask && params->p_fu.id) 125802de698cSRuslan Bilovol || (c_chmask && params->c_fu.id)) 1259e89bb428SRuslan Bilovol strscpy(card->mixername, card_name, sizeof(card->driver)); 1260e89bb428SRuslan Bilovol 126102de698cSRuslan Bilovol if (c_chmask && g_audio->in_ep_fback) { 126202de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], 126302de698cSRuslan Bilovol &uac->c_prm); 1264e89bb428SRuslan Bilovol if (!kctl) { 1265e89bb428SRuslan Bilovol err = -ENOMEM; 1266e89bb428SRuslan Bilovol goto snd_fail; 1267e89bb428SRuslan Bilovol } 1268e89bb428SRuslan Bilovol 1269e89bb428SRuslan Bilovol kctl->id.device = pcm->device; 1270e89bb428SRuslan Bilovol kctl->id.subdevice = 0; 1271e89bb428SRuslan Bilovol 1272e89bb428SRuslan Bilovol err = snd_ctl_add(card, kctl); 1273e89bb428SRuslan Bilovol if (err < 0) 1274e89bb428SRuslan Bilovol goto snd_fail; 1275e89bb428SRuslan Bilovol } 1276e89bb428SRuslan Bilovol 12776fec018aSPavel Hofman if (p_chmask) { 12786fec018aSPavel Hofman kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL], 12796fec018aSPavel Hofman &uac->p_prm); 12806fec018aSPavel Hofman if (!kctl) { 12816fec018aSPavel Hofman err = -ENOMEM; 12826fec018aSPavel Hofman goto snd_fail; 12836fec018aSPavel Hofman } 12846fec018aSPavel Hofman 12856fec018aSPavel Hofman kctl->id.device = pcm->device; 12866fec018aSPavel Hofman kctl->id.subdevice = 0; 12876fec018aSPavel Hofman 12886fec018aSPavel Hofman err = snd_ctl_add(card, kctl); 12896fec018aSPavel Hofman if (err < 0) 12906fec018aSPavel Hofman goto snd_fail; 12916fec018aSPavel Hofman } 12926fec018aSPavel Hofman 129302de698cSRuslan Bilovol for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { 129402de698cSRuslan Bilovol struct uac_rtd_params *prm; 129502de698cSRuslan Bilovol struct uac_fu_params *fu; 129602de698cSRuslan Bilovol char ctrl_name[24]; 129702de698cSRuslan Bilovol char *direction; 129802de698cSRuslan Bilovol 129902de698cSRuslan Bilovol if (!pcm->streams[i].substream_count) 130002de698cSRuslan Bilovol continue; 130102de698cSRuslan Bilovol 130202de698cSRuslan Bilovol if (i == SNDRV_PCM_STREAM_PLAYBACK) { 130302de698cSRuslan Bilovol prm = &uac->p_prm; 130402de698cSRuslan Bilovol fu = ¶ms->p_fu; 130502de698cSRuslan Bilovol direction = "Playback"; 130602de698cSRuslan Bilovol } else { 130702de698cSRuslan Bilovol prm = &uac->c_prm; 130802de698cSRuslan Bilovol fu = ¶ms->c_fu; 130902de698cSRuslan Bilovol direction = "Capture"; 131002de698cSRuslan Bilovol } 131102de698cSRuslan Bilovol 131202de698cSRuslan Bilovol prm->fu_id = fu->id; 131302de698cSRuslan Bilovol 131402de698cSRuslan Bilovol if (fu->mute_present) { 131502de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 131602de698cSRuslan Bilovol "PCM %s Switch", direction); 131702de698cSRuslan Bilovol 131802de698cSRuslan Bilovol u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; 131902de698cSRuslan Bilovol 132002de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], 132102de698cSRuslan Bilovol prm); 132202de698cSRuslan Bilovol if (!kctl) { 132302de698cSRuslan Bilovol err = -ENOMEM; 132402de698cSRuslan Bilovol goto snd_fail; 132502de698cSRuslan Bilovol } 132602de698cSRuslan Bilovol 132702de698cSRuslan Bilovol kctl->id.device = pcm->device; 1328601a5bc1SPavel Hofman kctl->id.subdevice = 0; 132902de698cSRuslan Bilovol 133002de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 133102de698cSRuslan Bilovol if (err < 0) 133202de698cSRuslan Bilovol goto snd_fail; 1333453d3fa9SChris Wulff prm->snd_kctl_mute_id = kctl->id; 133402de698cSRuslan Bilovol prm->mute = 0; 133502de698cSRuslan Bilovol } 133602de698cSRuslan Bilovol 133702de698cSRuslan Bilovol if (fu->volume_present) { 133802de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 133902de698cSRuslan Bilovol "PCM %s Volume", direction); 134002de698cSRuslan Bilovol 134102de698cSRuslan Bilovol u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; 134202de698cSRuslan Bilovol 134302de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], 134402de698cSRuslan Bilovol prm); 134502de698cSRuslan Bilovol if (!kctl) { 134602de698cSRuslan Bilovol err = -ENOMEM; 134702de698cSRuslan Bilovol goto snd_fail; 134802de698cSRuslan Bilovol } 134902de698cSRuslan Bilovol 135002de698cSRuslan Bilovol kctl->id.device = pcm->device; 1351601a5bc1SPavel Hofman kctl->id.subdevice = 0; 135202de698cSRuslan Bilovol 135302de698cSRuslan Bilovol 135402de698cSRuslan Bilovol kctl->tlv.c = u_audio_volume_tlv; 135502de698cSRuslan Bilovol kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | 135602de698cSRuslan Bilovol SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 135702de698cSRuslan Bilovol 135802de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 135902de698cSRuslan Bilovol if (err < 0) 136002de698cSRuslan Bilovol goto snd_fail; 1361453d3fa9SChris Wulff prm->snd_kctl_volume_id = kctl->id; 136202de698cSRuslan Bilovol prm->volume = fu->volume_max; 136302de698cSRuslan Bilovol prm->volume_max = fu->volume_max; 136402de698cSRuslan Bilovol prm->volume_min = fu->volume_min; 136502de698cSRuslan Bilovol prm->volume_res = fu->volume_res; 136602de698cSRuslan Bilovol } 1367c565ad07SJulian Scheel 1368c565ad07SJulian Scheel /* Add rate control */ 1369c565ad07SJulian Scheel snprintf(ctrl_name, sizeof(ctrl_name), 1370c565ad07SJulian Scheel "%s Rate", direction); 1371c565ad07SJulian Scheel u_audio_controls[UAC_RATE_CTRL].name = ctrl_name; 1372c565ad07SJulian Scheel 1373c565ad07SJulian Scheel kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm); 1374c565ad07SJulian Scheel if (!kctl) { 1375c565ad07SJulian Scheel err = -ENOMEM; 1376c565ad07SJulian Scheel goto snd_fail; 1377c565ad07SJulian Scheel } 1378c565ad07SJulian Scheel 1379c565ad07SJulian Scheel kctl->id.device = pcm->device; 1380c565ad07SJulian Scheel kctl->id.subdevice = 0; 1381c565ad07SJulian Scheel 1382c565ad07SJulian Scheel err = snd_ctl_add(card, kctl); 1383c565ad07SJulian Scheel if (err < 0) 1384c565ad07SJulian Scheel goto snd_fail; 1385453d3fa9SChris Wulff prm->snd_kctl_rate_id = kctl->id; 138602de698cSRuslan Bilovol } 138702de698cSRuslan Bilovol 1388d23922fcSRuslan Bilovol strscpy(card->driver, card_name, sizeof(card->driver)); 1389d23922fcSRuslan Bilovol strscpy(card->shortname, card_name, sizeof(card->shortname)); 1390eb9fecb9SRuslan Bilovol sprintf(card->longname, "%s %i", card_name, card->dev->id); 1391eb9fecb9SRuslan Bilovol 1392d27ab1e6STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 139367b2945dSTakashi Iwai NULL, 0, BUFF_SIZE_MAX); 1394eb9fecb9SRuslan Bilovol 1395eb9fecb9SRuslan Bilovol err = snd_card_register(card); 1396eb9fecb9SRuslan Bilovol 1397eb9fecb9SRuslan Bilovol if (!err) 1398eb9fecb9SRuslan Bilovol return 0; 1399eb9fecb9SRuslan Bilovol 1400eb9fecb9SRuslan Bilovol snd_fail: 1401eb9fecb9SRuslan Bilovol snd_card_free(card); 1402eb9fecb9SRuslan Bilovol fail: 140329865117SJerome Brunet kfree(uac->p_prm.reqs); 140429865117SJerome Brunet kfree(uac->c_prm.reqs); 1405eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1406eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1407eb9fecb9SRuslan Bilovol kfree(uac); 1408eb9fecb9SRuslan Bilovol 1409eb9fecb9SRuslan Bilovol return err; 1410eb9fecb9SRuslan Bilovol } 1411eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup); 1412eb9fecb9SRuslan Bilovol 1413eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio) 1414eb9fecb9SRuslan Bilovol { 1415eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 1416eb9fecb9SRuslan Bilovol struct snd_card *card; 1417eb9fecb9SRuslan Bilovol 1418eb9fecb9SRuslan Bilovol if (!g_audio || !g_audio->uac) 1419eb9fecb9SRuslan Bilovol return; 1420eb9fecb9SRuslan Bilovol 1421eb9fecb9SRuslan Bilovol uac = g_audio->uac; 1422eb9fecb9SRuslan Bilovol card = uac->card; 1423eb9fecb9SRuslan Bilovol if (card) 14246c67ed9aSAlvin Šipraga snd_card_free_when_closed(card); 1425eb9fecb9SRuslan Bilovol 142629865117SJerome Brunet kfree(uac->p_prm.reqs); 142729865117SJerome Brunet kfree(uac->c_prm.reqs); 1428eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1429eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1430eb9fecb9SRuslan Bilovol kfree(uac); 1431eb9fecb9SRuslan Bilovol } 1432eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup); 1433eb9fecb9SRuslan Bilovol 1434eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL"); 1435eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); 1436eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol"); 1437