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 15eb9fecb9SRuslan Bilovol #include <linux/module.h> 16eb9fecb9SRuslan Bilovol #include <sound/core.h> 17eb9fecb9SRuslan Bilovol #include <sound/pcm.h> 18eb9fecb9SRuslan Bilovol #include <sound/pcm_params.h> 19e89bb428SRuslan Bilovol #include <sound/control.h> 20eb9fecb9SRuslan Bilovol 21eb9fecb9SRuslan Bilovol #include "u_audio.h" 22eb9fecb9SRuslan Bilovol 23eb9fecb9SRuslan Bilovol #define BUFF_SIZE_MAX (PAGE_SIZE * 16) 24eb9fecb9SRuslan Bilovol #define PRD_SIZE_MAX PAGE_SIZE 25eb9fecb9SRuslan Bilovol #define MIN_PERIODS 4 26eb9fecb9SRuslan Bilovol 27eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */ 28eb9fecb9SRuslan Bilovol struct uac_rtd_params { 29eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; /* parent chip */ 30eb9fecb9SRuslan Bilovol bool ep_enabled; /* if the ep is enabled */ 31eb9fecb9SRuslan Bilovol 32eb9fecb9SRuslan Bilovol struct snd_pcm_substream *ss; 33eb9fecb9SRuslan Bilovol 34eb9fecb9SRuslan Bilovol /* Ring buffer */ 35eb9fecb9SRuslan Bilovol ssize_t hw_ptr; 36eb9fecb9SRuslan Bilovol 37eb9fecb9SRuslan Bilovol void *rbuf; 38eb9fecb9SRuslan Bilovol 39e89bb428SRuslan Bilovol unsigned int pitch; /* Stream pitch ratio to 1000000 */ 40f4408a98SJonas Stenvall unsigned int max_psize; /* MaxPacketSize of endpoint */ 41eb9fecb9SRuslan Bilovol 42d70f7598SJerome Brunet struct usb_request **reqs; 4324f779daSRuslan Bilovol 4424f779daSRuslan Bilovol struct usb_request *req_fback; /* Feedback endpoint request */ 4524f779daSRuslan Bilovol bool fb_ep_enabled; /* if the ep is enabled */ 46eb9fecb9SRuslan Bilovol }; 47eb9fecb9SRuslan Bilovol 48eb9fecb9SRuslan Bilovol struct snd_uac_chip { 49eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 50eb9fecb9SRuslan Bilovol 51eb9fecb9SRuslan Bilovol struct uac_rtd_params p_prm; 52eb9fecb9SRuslan Bilovol struct uac_rtd_params c_prm; 53eb9fecb9SRuslan Bilovol 54eb9fecb9SRuslan Bilovol struct snd_card *card; 55eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 56eb9fecb9SRuslan Bilovol 57eb9fecb9SRuslan Bilovol /* timekeeping for the playback endpoint */ 58eb9fecb9SRuslan Bilovol unsigned int p_interval; 59eb9fecb9SRuslan Bilovol unsigned int p_residue; 60eb9fecb9SRuslan Bilovol 61eb9fecb9SRuslan Bilovol /* pre-calculated values for playback iso completion */ 62eb9fecb9SRuslan Bilovol unsigned int p_pktsize; 63eb9fecb9SRuslan Bilovol unsigned int p_pktsize_residue; 64eb9fecb9SRuslan Bilovol unsigned int p_framesize; 65eb9fecb9SRuslan Bilovol }; 66eb9fecb9SRuslan Bilovol 672ab3c34cSBhumika Goyal static const struct snd_pcm_hardware uac_pcm_hardware = { 68eb9fecb9SRuslan Bilovol .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER 69eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID 70eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 71eb9fecb9SRuslan Bilovol .rates = SNDRV_PCM_RATE_CONTINUOUS, 72eb9fecb9SRuslan Bilovol .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, 73eb9fecb9SRuslan Bilovol .buffer_bytes_max = BUFF_SIZE_MAX, 74eb9fecb9SRuslan Bilovol .period_bytes_max = PRD_SIZE_MAX, 75eb9fecb9SRuslan Bilovol .periods_min = MIN_PERIODS, 76eb9fecb9SRuslan Bilovol }; 77eb9fecb9SRuslan Bilovol 7824f779daSRuslan Bilovol static void u_audio_set_fback_frequency(enum usb_device_speed speed, 79e89bb428SRuslan Bilovol unsigned long long freq, 80e89bb428SRuslan Bilovol unsigned int pitch, 81e89bb428SRuslan Bilovol void *buf) 8224f779daSRuslan Bilovol { 8324f779daSRuslan Bilovol u32 ff = 0; 8424f779daSRuslan Bilovol 85e89bb428SRuslan Bilovol /* 86e89bb428SRuslan Bilovol * Because the pitch base is 1000000, the final divider here 87e89bb428SRuslan Bilovol * will be 1000 * 1000000 = 1953125 << 9 88e89bb428SRuslan Bilovol * 89e89bb428SRuslan Bilovol * Instead of dealing with big numbers lets fold this 9 left shift 90e89bb428SRuslan Bilovol */ 91e89bb428SRuslan Bilovol 9224f779daSRuslan Bilovol if (speed == USB_SPEED_FULL) { 9324f779daSRuslan Bilovol /* 9424f779daSRuslan Bilovol * Full-speed feedback endpoints report frequency 95e89bb428SRuslan Bilovol * in samples/frame 9624f779daSRuslan Bilovol * Format is encoded in Q10.10 left-justified in the 24 bits, 9724f779daSRuslan Bilovol * so that it has a Q10.14 format. 98e89bb428SRuslan Bilovol * 99e89bb428SRuslan Bilovol * ff = (freq << 14) / 1000 10024f779daSRuslan Bilovol */ 101e89bb428SRuslan Bilovol freq <<= 5; 10224f779daSRuslan Bilovol } else { 10324f779daSRuslan Bilovol /* 10424f779daSRuslan Bilovol * High-speed feedback endpoints report frequency 10524f779daSRuslan Bilovol * in samples/microframe. 10624f779daSRuslan Bilovol * Format is encoded in Q12.13 fitted into four bytes so that 10724f779daSRuslan Bilovol * the binary point is located between the second and the third 10824f779daSRuslan Bilovol * byte fromat (that is Q16.16) 109e89bb428SRuslan Bilovol * 110e89bb428SRuslan Bilovol * ff = (freq << 16) / 8000 11124f779daSRuslan Bilovol */ 112e89bb428SRuslan Bilovol freq <<= 4; 11324f779daSRuslan Bilovol } 114e89bb428SRuslan Bilovol 115e89bb428SRuslan Bilovol ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 116e89bb428SRuslan Bilovol 11724f779daSRuslan Bilovol *(__le32 *)buf = cpu_to_le32(ff); 11824f779daSRuslan Bilovol } 11924f779daSRuslan Bilovol 120eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) 121eb9fecb9SRuslan Bilovol { 122f4408a98SJonas Stenvall unsigned int pending; 123eb9fecb9SRuslan Bilovol unsigned int hw_ptr; 124eb9fecb9SRuslan Bilovol int status = req->status; 125eb9fecb9SRuslan Bilovol struct snd_pcm_substream *substream; 12696afb54eSVladimir Zapolskiy struct snd_pcm_runtime *runtime; 12729865117SJerome Brunet struct uac_rtd_params *prm = req->context; 128eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 129eb9fecb9SRuslan Bilovol 130eb9fecb9SRuslan Bilovol /* i/f shutting down */ 1317de8681bSJack Pham if (!prm->ep_enabled) { 1327de8681bSJack Pham usb_ep_free_request(ep, req); 1337de8681bSJack Pham return; 1347de8681bSJack Pham } 1357de8681bSJack Pham 1367de8681bSJack Pham if (req->status == -ESHUTDOWN) 137eb9fecb9SRuslan Bilovol return; 138eb9fecb9SRuslan Bilovol 139eb9fecb9SRuslan Bilovol /* 140eb9fecb9SRuslan Bilovol * We can't really do much about bad xfers. 141eb9fecb9SRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 142eb9fecb9SRuslan Bilovol */ 143eb9fecb9SRuslan Bilovol if (status) 144eb9fecb9SRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 145eb9fecb9SRuslan Bilovol __func__, status, req->actual, req->length); 146eb9fecb9SRuslan Bilovol 147eb9fecb9SRuslan Bilovol substream = prm->ss; 148eb9fecb9SRuslan Bilovol 149eb9fecb9SRuslan Bilovol /* Do nothing if ALSA isn't active */ 150eb9fecb9SRuslan Bilovol if (!substream) 151eb9fecb9SRuslan Bilovol goto exit; 152eb9fecb9SRuslan Bilovol 153d70f7598SJerome Brunet snd_pcm_stream_lock(substream); 15456bc6158SVladimir Zapolskiy 15596afb54eSVladimir Zapolskiy runtime = substream->runtime; 15656bc6158SVladimir Zapolskiy if (!runtime || !snd_pcm_running(substream)) { 157d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 15856bc6158SVladimir Zapolskiy goto exit; 15956bc6158SVladimir Zapolskiy } 16056bc6158SVladimir Zapolskiy 161eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 162eb9fecb9SRuslan Bilovol /* 163eb9fecb9SRuslan Bilovol * For each IN packet, take the quotient of the current data 164eb9fecb9SRuslan Bilovol * rate and the endpoint's interval as the base packet size. 165eb9fecb9SRuslan Bilovol * If there is a residue from this division, add it to the 166eb9fecb9SRuslan Bilovol * residue accumulator. 167eb9fecb9SRuslan Bilovol */ 168eb9fecb9SRuslan Bilovol req->length = uac->p_pktsize; 169eb9fecb9SRuslan Bilovol uac->p_residue += uac->p_pktsize_residue; 170eb9fecb9SRuslan Bilovol 171eb9fecb9SRuslan Bilovol /* 172eb9fecb9SRuslan Bilovol * Whenever there are more bytes in the accumulator than we 173eb9fecb9SRuslan Bilovol * need to add one more sample frame, increase this packet's 174eb9fecb9SRuslan Bilovol * size and decrease the accumulator. 175eb9fecb9SRuslan Bilovol */ 176eb9fecb9SRuslan Bilovol if (uac->p_residue / uac->p_interval >= uac->p_framesize) { 177eb9fecb9SRuslan Bilovol req->length += uac->p_framesize; 178eb9fecb9SRuslan Bilovol uac->p_residue -= uac->p_framesize * 179eb9fecb9SRuslan Bilovol uac->p_interval; 180eb9fecb9SRuslan Bilovol } 181eb9fecb9SRuslan Bilovol 182eb9fecb9SRuslan Bilovol req->actual = req->length; 183eb9fecb9SRuslan Bilovol } 184eb9fecb9SRuslan Bilovol 185eb9fecb9SRuslan Bilovol hw_ptr = prm->hw_ptr; 186eb9fecb9SRuslan Bilovol 187eb9fecb9SRuslan Bilovol /* Pack USB load in ALSA ring buffer */ 18896afb54eSVladimir Zapolskiy pending = runtime->dma_bytes - hw_ptr; 189eb9fecb9SRuslan Bilovol 190eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 191eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 19296afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, pending); 19396afb54eSVladimir Zapolskiy memcpy(req->buf + pending, runtime->dma_area, 194eb9fecb9SRuslan Bilovol req->actual - pending); 195eb9fecb9SRuslan Bilovol } else { 19696afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, 19796afb54eSVladimir Zapolskiy req->actual); 198eb9fecb9SRuslan Bilovol } 199eb9fecb9SRuslan Bilovol } else { 200eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 20196afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, pending); 20296afb54eSVladimir Zapolskiy memcpy(runtime->dma_area, req->buf + pending, 203eb9fecb9SRuslan Bilovol req->actual - pending); 204eb9fecb9SRuslan Bilovol } else { 20596afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, 20696afb54eSVladimir Zapolskiy req->actual); 207eb9fecb9SRuslan Bilovol } 208eb9fecb9SRuslan Bilovol } 209eb9fecb9SRuslan Bilovol 2106b37bd78SJoshua Frkuska /* update hw_ptr after data is copied to memory */ 21196afb54eSVladimir Zapolskiy prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; 212773e53d5SVladimir Zapolskiy hw_ptr = prm->hw_ptr; 213d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 2146b37bd78SJoshua Frkuska 215773e53d5SVladimir Zapolskiy if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) 216773e53d5SVladimir Zapolskiy snd_pcm_period_elapsed(substream); 217773e53d5SVladimir Zapolskiy 218eb9fecb9SRuslan Bilovol exit: 219eb9fecb9SRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 220eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 221eb9fecb9SRuslan Bilovol } 222eb9fecb9SRuslan Bilovol 22324f779daSRuslan Bilovol static void u_audio_iso_fback_complete(struct usb_ep *ep, 22424f779daSRuslan Bilovol struct usb_request *req) 22524f779daSRuslan Bilovol { 22624f779daSRuslan Bilovol struct uac_rtd_params *prm = req->context; 22724f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 22824f779daSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 229e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 23024f779daSRuslan Bilovol int status = req->status; 23124f779daSRuslan Bilovol 23224f779daSRuslan Bilovol /* i/f shutting down */ 233*75432ba5SJerome Brunet if (!prm->fb_ep_enabled) { 234*75432ba5SJerome Brunet kfree(req->buf); 235*75432ba5SJerome Brunet usb_ep_free_request(ep, req); 236*75432ba5SJerome Brunet return; 237*75432ba5SJerome Brunet } 238*75432ba5SJerome Brunet 239*75432ba5SJerome Brunet if (req->status == -ESHUTDOWN) 24024f779daSRuslan Bilovol return; 24124f779daSRuslan Bilovol 24224f779daSRuslan Bilovol /* 24324f779daSRuslan Bilovol * We can't really do much about bad xfers. 24424f779daSRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 24524f779daSRuslan Bilovol */ 24624f779daSRuslan Bilovol if (status) 24724f779daSRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 24824f779daSRuslan Bilovol __func__, status, req->actual, req->length); 24924f779daSRuslan Bilovol 25024f779daSRuslan Bilovol u_audio_set_fback_frequency(audio_dev->gadget->speed, 251e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 252e89bb428SRuslan Bilovol req->buf); 25324f779daSRuslan Bilovol 25424f779daSRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 25524f779daSRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 25624f779daSRuslan Bilovol } 25724f779daSRuslan Bilovol 258eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 259eb9fecb9SRuslan Bilovol { 260eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 261eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 262eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 263eb9fecb9SRuslan Bilovol struct uac_params *params; 264eb9fecb9SRuslan Bilovol int err = 0; 265eb9fecb9SRuslan Bilovol 266eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 267eb9fecb9SRuslan Bilovol params = &audio_dev->params; 268eb9fecb9SRuslan Bilovol 269eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 270eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 271eb9fecb9SRuslan Bilovol else 272eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 273eb9fecb9SRuslan Bilovol 274eb9fecb9SRuslan Bilovol /* Reset */ 275eb9fecb9SRuslan Bilovol prm->hw_ptr = 0; 276eb9fecb9SRuslan Bilovol 277eb9fecb9SRuslan Bilovol switch (cmd) { 278eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_START: 279eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_RESUME: 280eb9fecb9SRuslan Bilovol prm->ss = substream; 281eb9fecb9SRuslan Bilovol break; 282eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_STOP: 283eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_SUSPEND: 284eb9fecb9SRuslan Bilovol prm->ss = NULL; 285eb9fecb9SRuslan Bilovol break; 286eb9fecb9SRuslan Bilovol default: 287eb9fecb9SRuslan Bilovol err = -EINVAL; 288eb9fecb9SRuslan Bilovol } 289eb9fecb9SRuslan Bilovol 290eb9fecb9SRuslan Bilovol /* Clear buffer after Play stops */ 291eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) 292eb9fecb9SRuslan Bilovol memset(prm->rbuf, 0, prm->max_psize * params->req_number); 293eb9fecb9SRuslan Bilovol 294eb9fecb9SRuslan Bilovol return err; 295eb9fecb9SRuslan Bilovol } 296eb9fecb9SRuslan Bilovol 297eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) 298eb9fecb9SRuslan Bilovol { 299eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 300eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 301eb9fecb9SRuslan Bilovol 302eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 303eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 304eb9fecb9SRuslan Bilovol else 305eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 306eb9fecb9SRuslan Bilovol 307eb9fecb9SRuslan Bilovol return bytes_to_frames(substream->runtime, prm->hw_ptr); 308eb9fecb9SRuslan Bilovol } 309eb9fecb9SRuslan Bilovol 31025dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize) 31125dbd75dSJerome Brunet { 31225dbd75dSJerome Brunet u64 ret; 31325dbd75dSJerome Brunet 31425dbd75dSJerome Brunet switch (ssize) { 31525dbd75dSJerome Brunet case 3: 31625dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S24_3LE; 31725dbd75dSJerome Brunet break; 31825dbd75dSJerome Brunet case 4: 31925dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S32_LE; 32025dbd75dSJerome Brunet break; 32125dbd75dSJerome Brunet default: 32225dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S16_LE; 32325dbd75dSJerome Brunet break; 32425dbd75dSJerome Brunet } 32525dbd75dSJerome Brunet 32625dbd75dSJerome Brunet return ret; 32725dbd75dSJerome Brunet } 32825dbd75dSJerome Brunet 329eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream) 330eb9fecb9SRuslan Bilovol { 331eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 332eb9fecb9SRuslan Bilovol struct snd_pcm_runtime *runtime = substream->runtime; 333eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 334eb9fecb9SRuslan Bilovol struct uac_params *params; 335eb9fecb9SRuslan Bilovol int p_ssize, c_ssize; 336eb9fecb9SRuslan Bilovol int p_srate, c_srate; 337eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 338eb9fecb9SRuslan Bilovol 339eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 340eb9fecb9SRuslan Bilovol params = &audio_dev->params; 341eb9fecb9SRuslan Bilovol p_ssize = params->p_ssize; 342eb9fecb9SRuslan Bilovol c_ssize = params->c_ssize; 343eb9fecb9SRuslan Bilovol p_srate = params->p_srate; 344eb9fecb9SRuslan Bilovol c_srate = params->c_srate; 345eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 346eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 347eb9fecb9SRuslan Bilovol uac->p_residue = 0; 348eb9fecb9SRuslan Bilovol 349eb9fecb9SRuslan Bilovol runtime->hw = uac_pcm_hardware; 350eb9fecb9SRuslan Bilovol 351eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 352eb9fecb9SRuslan Bilovol runtime->hw.rate_min = p_srate; 35325dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(p_ssize); 354eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(p_chmask); 355eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize 356eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 357eb9fecb9SRuslan Bilovol } else { 358eb9fecb9SRuslan Bilovol runtime->hw.rate_min = c_srate; 35925dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(c_ssize); 360eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(c_chmask); 361eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize 362eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 363eb9fecb9SRuslan Bilovol } 364eb9fecb9SRuslan Bilovol 365eb9fecb9SRuslan Bilovol runtime->hw.rate_max = runtime->hw.rate_min; 366eb9fecb9SRuslan Bilovol runtime->hw.channels_max = runtime->hw.channels_min; 367eb9fecb9SRuslan Bilovol 368eb9fecb9SRuslan Bilovol snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 369eb9fecb9SRuslan Bilovol 370eb9fecb9SRuslan Bilovol return 0; 371eb9fecb9SRuslan Bilovol } 372eb9fecb9SRuslan Bilovol 373eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */ 374eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream) 375eb9fecb9SRuslan Bilovol { 376eb9fecb9SRuslan Bilovol return 0; 377eb9fecb9SRuslan Bilovol } 378eb9fecb9SRuslan Bilovol 379640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = { 380eb9fecb9SRuslan Bilovol .open = uac_pcm_open, 381eb9fecb9SRuslan Bilovol .close = uac_pcm_null, 382eb9fecb9SRuslan Bilovol .trigger = uac_pcm_trigger, 383eb9fecb9SRuslan Bilovol .pointer = uac_pcm_pointer, 384eb9fecb9SRuslan Bilovol .prepare = uac_pcm_null, 385eb9fecb9SRuslan Bilovol }; 386eb9fecb9SRuslan Bilovol 387eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 388eb9fecb9SRuslan Bilovol { 389eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 390eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 391eb9fecb9SRuslan Bilovol struct uac_params *params; 392eb9fecb9SRuslan Bilovol int i; 393eb9fecb9SRuslan Bilovol 394eb9fecb9SRuslan Bilovol if (!prm->ep_enabled) 395eb9fecb9SRuslan Bilovol return; 396eb9fecb9SRuslan Bilovol 397eb9fecb9SRuslan Bilovol prm->ep_enabled = false; 398eb9fecb9SRuslan Bilovol 399eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 400eb9fecb9SRuslan Bilovol params = &audio_dev->params; 401eb9fecb9SRuslan Bilovol 402eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 40329865117SJerome Brunet if (prm->reqs[i]) { 40429865117SJerome Brunet if (usb_ep_dequeue(ep, prm->reqs[i])) 40529865117SJerome Brunet usb_ep_free_request(ep, prm->reqs[i]); 4067de8681bSJack Pham /* 4077de8681bSJack Pham * If usb_ep_dequeue() cannot successfully dequeue the 4087de8681bSJack Pham * request, the request will be freed by the completion 4097de8681bSJack Pham * callback. 4107de8681bSJack Pham */ 4117de8681bSJack Pham 41229865117SJerome Brunet prm->reqs[i] = NULL; 413eb9fecb9SRuslan Bilovol } 414eb9fecb9SRuslan Bilovol } 415eb9fecb9SRuslan Bilovol 416eb9fecb9SRuslan Bilovol if (usb_ep_disable(ep)) 417eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 418eb9fecb9SRuslan Bilovol } 419eb9fecb9SRuslan Bilovol 42024f779daSRuslan Bilovol static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) 42124f779daSRuslan Bilovol { 42224f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 42324f779daSRuslan Bilovol 42424f779daSRuslan Bilovol if (!prm->fb_ep_enabled) 42524f779daSRuslan Bilovol return; 42624f779daSRuslan Bilovol 42724f779daSRuslan Bilovol prm->fb_ep_enabled = false; 42824f779daSRuslan Bilovol 42924f779daSRuslan Bilovol if (prm->req_fback) { 430*75432ba5SJerome Brunet if (usb_ep_dequeue(ep, prm->req_fback)) { 43124f779daSRuslan Bilovol kfree(prm->req_fback->buf); 43224f779daSRuslan Bilovol usb_ep_free_request(ep, prm->req_fback); 433*75432ba5SJerome Brunet } 43424f779daSRuslan Bilovol prm->req_fback = NULL; 43524f779daSRuslan Bilovol } 43624f779daSRuslan Bilovol 43724f779daSRuslan Bilovol if (usb_ep_disable(ep)) 43824f779daSRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 43924f779daSRuslan Bilovol } 440eb9fecb9SRuslan Bilovol 441eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev) 442eb9fecb9SRuslan Bilovol { 443eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 444eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 445eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 44624f779daSRuslan Bilovol struct usb_request *req, *req_fback; 44724f779daSRuslan Bilovol struct usb_ep *ep, *ep_fback; 448eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 449eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 450eb9fecb9SRuslan Bilovol int req_len, i; 451eb9fecb9SRuslan Bilovol 452eb9fecb9SRuslan Bilovol ep = audio_dev->out_ep; 453eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 454eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 455904967c6SJohn Keeping req_len = ep->maxpacket; 456eb9fecb9SRuslan Bilovol 457eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 458eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 459eb9fecb9SRuslan Bilovol 460eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 46129865117SJerome Brunet if (!prm->reqs[i]) { 462eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 463eb9fecb9SRuslan Bilovol if (req == NULL) 464eb9fecb9SRuslan Bilovol return -ENOMEM; 465eb9fecb9SRuslan Bilovol 46629865117SJerome Brunet prm->reqs[i] = req; 467eb9fecb9SRuslan Bilovol 468eb9fecb9SRuslan Bilovol req->zero = 0; 46929865117SJerome Brunet req->context = prm; 470eb9fecb9SRuslan Bilovol req->length = req_len; 471eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 472904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 473eb9fecb9SRuslan Bilovol } 474eb9fecb9SRuslan Bilovol 47529865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 476eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 477eb9fecb9SRuslan Bilovol } 478eb9fecb9SRuslan Bilovol 47924f779daSRuslan Bilovol ep_fback = audio_dev->in_ep_fback; 48024f779daSRuslan Bilovol if (!ep_fback) 48124f779daSRuslan Bilovol return 0; 48224f779daSRuslan Bilovol 48324f779daSRuslan Bilovol /* Setup feedback endpoint */ 48424f779daSRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep_fback); 48524f779daSRuslan Bilovol prm->fb_ep_enabled = true; 48624f779daSRuslan Bilovol usb_ep_enable(ep_fback); 48724f779daSRuslan Bilovol req_len = ep_fback->maxpacket; 48824f779daSRuslan Bilovol 48924f779daSRuslan Bilovol req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); 49024f779daSRuslan Bilovol if (req_fback == NULL) 49124f779daSRuslan Bilovol return -ENOMEM; 49224f779daSRuslan Bilovol 49324f779daSRuslan Bilovol prm->req_fback = req_fback; 49424f779daSRuslan Bilovol req_fback->zero = 0; 49524f779daSRuslan Bilovol req_fback->context = prm; 49624f779daSRuslan Bilovol req_fback->length = req_len; 49724f779daSRuslan Bilovol req_fback->complete = u_audio_iso_fback_complete; 49824f779daSRuslan Bilovol 49924f779daSRuslan Bilovol req_fback->buf = kzalloc(req_len, GFP_ATOMIC); 50024f779daSRuslan Bilovol if (!req_fback->buf) 50124f779daSRuslan Bilovol return -ENOMEM; 50224f779daSRuslan Bilovol 50324f779daSRuslan Bilovol /* 50424f779daSRuslan Bilovol * Configure the feedback endpoint's reported frequency. 50524f779daSRuslan Bilovol * Always start with original frequency since its deviation can't 50624f779daSRuslan Bilovol * be meauserd at start of playback 50724f779daSRuslan Bilovol */ 508e89bb428SRuslan Bilovol prm->pitch = 1000000; 50924f779daSRuslan Bilovol u_audio_set_fback_frequency(audio_dev->gadget->speed, 510e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 511e89bb428SRuslan Bilovol req_fback->buf); 51224f779daSRuslan Bilovol 51324f779daSRuslan Bilovol if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) 51424f779daSRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 51524f779daSRuslan Bilovol 516eb9fecb9SRuslan Bilovol return 0; 517eb9fecb9SRuslan Bilovol } 518eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture); 519eb9fecb9SRuslan Bilovol 520eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev) 521eb9fecb9SRuslan Bilovol { 522eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 523eb9fecb9SRuslan Bilovol 52424f779daSRuslan Bilovol if (audio_dev->in_ep_fback) 52524f779daSRuslan Bilovol free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 526eb9fecb9SRuslan Bilovol free_ep(&uac->c_prm, audio_dev->out_ep); 527eb9fecb9SRuslan Bilovol } 528eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture); 529eb9fecb9SRuslan Bilovol 530eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev) 531eb9fecb9SRuslan Bilovol { 532eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 533eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 534eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 535eb9fecb9SRuslan Bilovol struct usb_request *req; 536eb9fecb9SRuslan Bilovol struct usb_ep *ep; 537eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 538eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 5396b02af34SJohn Keeping unsigned int factor; 540eb9fecb9SRuslan Bilovol const struct usb_endpoint_descriptor *ep_desc; 541eb9fecb9SRuslan Bilovol int req_len, i; 542eb9fecb9SRuslan Bilovol 543eb9fecb9SRuslan Bilovol ep = audio_dev->in_ep; 544eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 545eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 546eb9fecb9SRuslan Bilovol 547eb9fecb9SRuslan Bilovol ep_desc = ep->desc; 548eb9fecb9SRuslan Bilovol 549eb9fecb9SRuslan Bilovol /* pre-calculate the playback endpoint's interval */ 550eb9fecb9SRuslan Bilovol if (gadget->speed == USB_SPEED_FULL) 551eb9fecb9SRuslan Bilovol factor = 1000; 552eb9fecb9SRuslan Bilovol else 553eb9fecb9SRuslan Bilovol factor = 8000; 554eb9fecb9SRuslan Bilovol 555eb9fecb9SRuslan Bilovol /* pre-compute some values for iso_complete() */ 556eb9fecb9SRuslan Bilovol uac->p_framesize = params->p_ssize * 557eb9fecb9SRuslan Bilovol num_channels(params->p_chmask); 558eb9fecb9SRuslan Bilovol uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); 5596b02af34SJohn Keeping uac->p_pktsize = min_t(unsigned int, 5606b02af34SJohn Keeping uac->p_framesize * 5616b02af34SJohn Keeping (params->p_srate / uac->p_interval), 562904967c6SJohn Keeping ep->maxpacket); 563eb9fecb9SRuslan Bilovol 564904967c6SJohn Keeping if (uac->p_pktsize < ep->maxpacket) 5656b02af34SJohn Keeping uac->p_pktsize_residue = uac->p_framesize * 5666b02af34SJohn Keeping (params->p_srate % uac->p_interval); 567eb9fecb9SRuslan Bilovol else 568eb9fecb9SRuslan Bilovol uac->p_pktsize_residue = 0; 569eb9fecb9SRuslan Bilovol 570eb9fecb9SRuslan Bilovol req_len = uac->p_pktsize; 571eb9fecb9SRuslan Bilovol uac->p_residue = 0; 572eb9fecb9SRuslan Bilovol 573eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 574eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 575eb9fecb9SRuslan Bilovol 576eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 57729865117SJerome Brunet if (!prm->reqs[i]) { 578eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 579eb9fecb9SRuslan Bilovol if (req == NULL) 580eb9fecb9SRuslan Bilovol return -ENOMEM; 581eb9fecb9SRuslan Bilovol 58229865117SJerome Brunet prm->reqs[i] = req; 583eb9fecb9SRuslan Bilovol 584eb9fecb9SRuslan Bilovol req->zero = 0; 58529865117SJerome Brunet req->context = prm; 586eb9fecb9SRuslan Bilovol req->length = req_len; 587eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 588904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 589eb9fecb9SRuslan Bilovol } 590eb9fecb9SRuslan Bilovol 59129865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 592eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 593eb9fecb9SRuslan Bilovol } 594eb9fecb9SRuslan Bilovol 595eb9fecb9SRuslan Bilovol return 0; 596eb9fecb9SRuslan Bilovol } 597eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback); 598eb9fecb9SRuslan Bilovol 599eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev) 600eb9fecb9SRuslan Bilovol { 601eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 602eb9fecb9SRuslan Bilovol 603eb9fecb9SRuslan Bilovol free_ep(&uac->p_prm, audio_dev->in_ep); 604eb9fecb9SRuslan Bilovol } 605eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback); 606eb9fecb9SRuslan Bilovol 607e89bb428SRuslan Bilovol static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, 608e89bb428SRuslan Bilovol struct snd_ctl_elem_info *uinfo) 609e89bb428SRuslan Bilovol { 610e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 611e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 612e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 613e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 614e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 615e89bb428SRuslan Bilovol 616e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 617e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 618e89bb428SRuslan Bilovol 619e89bb428SRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 620e89bb428SRuslan Bilovol uinfo->count = 1; 621e89bb428SRuslan Bilovol uinfo->value.integer.min = pitch_min; 622e89bb428SRuslan Bilovol uinfo->value.integer.max = pitch_max; 623e89bb428SRuslan Bilovol uinfo->value.integer.step = 1; 624e89bb428SRuslan Bilovol return 0; 625e89bb428SRuslan Bilovol } 626e89bb428SRuslan Bilovol 627e89bb428SRuslan Bilovol static int u_audio_pitch_get(struct snd_kcontrol *kcontrol, 628e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 629e89bb428SRuslan Bilovol { 630e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 631e89bb428SRuslan Bilovol 632e89bb428SRuslan Bilovol ucontrol->value.integer.value[0] = prm->pitch; 633e89bb428SRuslan Bilovol 634e89bb428SRuslan Bilovol return 0; 635e89bb428SRuslan Bilovol } 636e89bb428SRuslan Bilovol 637e89bb428SRuslan Bilovol static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, 638e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 639e89bb428SRuslan Bilovol { 640e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 641e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 642e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 643e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 644e89bb428SRuslan Bilovol unsigned int val; 645e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 646e89bb428SRuslan Bilovol int change = 0; 647e89bb428SRuslan Bilovol 648e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 649e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 650e89bb428SRuslan Bilovol 651e89bb428SRuslan Bilovol val = ucontrol->value.integer.value[0]; 652e89bb428SRuslan Bilovol 653e89bb428SRuslan Bilovol if (val < pitch_min) 654e89bb428SRuslan Bilovol val = pitch_min; 655e89bb428SRuslan Bilovol if (val > pitch_max) 656e89bb428SRuslan Bilovol val = pitch_max; 657e89bb428SRuslan Bilovol 658e89bb428SRuslan Bilovol if (prm->pitch != val) { 659e89bb428SRuslan Bilovol prm->pitch = val; 660e89bb428SRuslan Bilovol change = 1; 661e89bb428SRuslan Bilovol } 662e89bb428SRuslan Bilovol 663e89bb428SRuslan Bilovol return change; 664e89bb428SRuslan Bilovol } 665e89bb428SRuslan Bilovol 666e89bb428SRuslan Bilovol static const struct snd_kcontrol_new u_audio_controls[] = { 667e89bb428SRuslan Bilovol { 668e89bb428SRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_PCM, 669e89bb428SRuslan Bilovol .name = "Capture Pitch 1000000", 670e89bb428SRuslan Bilovol .info = u_audio_pitch_info, 671e89bb428SRuslan Bilovol .get = u_audio_pitch_get, 672e89bb428SRuslan Bilovol .put = u_audio_pitch_put, 673e89bb428SRuslan Bilovol }, 674e89bb428SRuslan Bilovol }; 675e89bb428SRuslan Bilovol 676eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, 677eb9fecb9SRuslan Bilovol const char *card_name) 678eb9fecb9SRuslan Bilovol { 679eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 680eb9fecb9SRuslan Bilovol struct snd_card *card; 681eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 682e89bb428SRuslan Bilovol struct snd_kcontrol *kctl; 683eb9fecb9SRuslan Bilovol struct uac_params *params; 684eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 685eb9fecb9SRuslan Bilovol int err; 686eb9fecb9SRuslan Bilovol 687eb9fecb9SRuslan Bilovol if (!g_audio) 688eb9fecb9SRuslan Bilovol return -EINVAL; 689eb9fecb9SRuslan Bilovol 690eb9fecb9SRuslan Bilovol uac = kzalloc(sizeof(*uac), GFP_KERNEL); 691eb9fecb9SRuslan Bilovol if (!uac) 692eb9fecb9SRuslan Bilovol return -ENOMEM; 693eb9fecb9SRuslan Bilovol g_audio->uac = uac; 694eb9fecb9SRuslan Bilovol uac->audio_dev = g_audio; 695eb9fecb9SRuslan Bilovol 696eb9fecb9SRuslan Bilovol params = &g_audio->params; 697eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 698eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 699eb9fecb9SRuslan Bilovol 700eb9fecb9SRuslan Bilovol if (c_chmask) { 701eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->c_prm; 702eb9fecb9SRuslan Bilovol 703eb9fecb9SRuslan Bilovol uac->c_prm.uac = uac; 704eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->out_ep_maxpsize; 705eb9fecb9SRuslan Bilovol 70629865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 70729865117SJerome Brunet sizeof(struct usb_request *), 708eb9fecb9SRuslan Bilovol GFP_KERNEL); 70929865117SJerome Brunet if (!prm->reqs) { 710eb9fecb9SRuslan Bilovol err = -ENOMEM; 711eb9fecb9SRuslan Bilovol goto fail; 712eb9fecb9SRuslan Bilovol } 713eb9fecb9SRuslan Bilovol 714eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 715eb9fecb9SRuslan Bilovol GFP_KERNEL); 716eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 717eb9fecb9SRuslan Bilovol prm->max_psize = 0; 718eb9fecb9SRuslan Bilovol err = -ENOMEM; 719eb9fecb9SRuslan Bilovol goto fail; 720eb9fecb9SRuslan Bilovol } 721eb9fecb9SRuslan Bilovol } 722eb9fecb9SRuslan Bilovol 723eb9fecb9SRuslan Bilovol if (p_chmask) { 724eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->p_prm; 725eb9fecb9SRuslan Bilovol 726eb9fecb9SRuslan Bilovol uac->p_prm.uac = uac; 727eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->in_ep_maxpsize; 728eb9fecb9SRuslan Bilovol 72929865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 73029865117SJerome Brunet sizeof(struct usb_request *), 731eb9fecb9SRuslan Bilovol GFP_KERNEL); 73229865117SJerome Brunet if (!prm->reqs) { 733eb9fecb9SRuslan Bilovol err = -ENOMEM; 734eb9fecb9SRuslan Bilovol goto fail; 735eb9fecb9SRuslan Bilovol } 736eb9fecb9SRuslan Bilovol 737eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 738eb9fecb9SRuslan Bilovol GFP_KERNEL); 739eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 740eb9fecb9SRuslan Bilovol prm->max_psize = 0; 741eb9fecb9SRuslan Bilovol err = -ENOMEM; 742eb9fecb9SRuslan Bilovol goto fail; 743eb9fecb9SRuslan Bilovol } 744eb9fecb9SRuslan Bilovol } 745eb9fecb9SRuslan Bilovol 746eb9fecb9SRuslan Bilovol /* Choose any slot, with no id */ 747eb9fecb9SRuslan Bilovol err = snd_card_new(&g_audio->gadget->dev, 748eb9fecb9SRuslan Bilovol -1, NULL, THIS_MODULE, 0, &card); 749eb9fecb9SRuslan Bilovol if (err < 0) 750eb9fecb9SRuslan Bilovol goto fail; 751eb9fecb9SRuslan Bilovol 752eb9fecb9SRuslan Bilovol uac->card = card; 753eb9fecb9SRuslan Bilovol 754eb9fecb9SRuslan Bilovol /* 755eb9fecb9SRuslan Bilovol * Create first PCM device 756eb9fecb9SRuslan Bilovol * Create a substream only for non-zero channel streams 757eb9fecb9SRuslan Bilovol */ 758eb9fecb9SRuslan Bilovol err = snd_pcm_new(uac->card, pcm_name, 0, 759eb9fecb9SRuslan Bilovol p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); 760eb9fecb9SRuslan Bilovol if (err < 0) 761eb9fecb9SRuslan Bilovol goto snd_fail; 762eb9fecb9SRuslan Bilovol 763d23922fcSRuslan Bilovol strscpy(pcm->name, pcm_name, sizeof(pcm->name)); 764eb9fecb9SRuslan Bilovol pcm->private_data = uac; 765eb9fecb9SRuslan Bilovol uac->pcm = pcm; 766eb9fecb9SRuslan Bilovol 767eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); 768eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); 769eb9fecb9SRuslan Bilovol 770e89bb428SRuslan Bilovol if (c_chmask && g_audio->in_ep_fback) { 771e89bb428SRuslan Bilovol strscpy(card->mixername, card_name, sizeof(card->driver)); 772e89bb428SRuslan Bilovol 773e89bb428SRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm); 774e89bb428SRuslan Bilovol if (!kctl) { 775e89bb428SRuslan Bilovol err = -ENOMEM; 776e89bb428SRuslan Bilovol goto snd_fail; 777e89bb428SRuslan Bilovol } 778e89bb428SRuslan Bilovol 779e89bb428SRuslan Bilovol kctl->id.device = pcm->device; 780e89bb428SRuslan Bilovol kctl->id.subdevice = 0; 781e89bb428SRuslan Bilovol 782e89bb428SRuslan Bilovol err = snd_ctl_add(card, kctl); 783e89bb428SRuslan Bilovol if (err < 0) 784e89bb428SRuslan Bilovol goto snd_fail; 785e89bb428SRuslan Bilovol } 786e89bb428SRuslan Bilovol 787d23922fcSRuslan Bilovol strscpy(card->driver, card_name, sizeof(card->driver)); 788d23922fcSRuslan Bilovol strscpy(card->shortname, card_name, sizeof(card->shortname)); 789eb9fecb9SRuslan Bilovol sprintf(card->longname, "%s %i", card_name, card->dev->id); 790eb9fecb9SRuslan Bilovol 791d27ab1e6STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 79267b2945dSTakashi Iwai NULL, 0, BUFF_SIZE_MAX); 793eb9fecb9SRuslan Bilovol 794eb9fecb9SRuslan Bilovol err = snd_card_register(card); 795eb9fecb9SRuslan Bilovol 796eb9fecb9SRuslan Bilovol if (!err) 797eb9fecb9SRuslan Bilovol return 0; 798eb9fecb9SRuslan Bilovol 799eb9fecb9SRuslan Bilovol snd_fail: 800eb9fecb9SRuslan Bilovol snd_card_free(card); 801eb9fecb9SRuslan Bilovol fail: 80229865117SJerome Brunet kfree(uac->p_prm.reqs); 80329865117SJerome Brunet kfree(uac->c_prm.reqs); 804eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 805eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 806eb9fecb9SRuslan Bilovol kfree(uac); 807eb9fecb9SRuslan Bilovol 808eb9fecb9SRuslan Bilovol return err; 809eb9fecb9SRuslan Bilovol } 810eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup); 811eb9fecb9SRuslan Bilovol 812eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio) 813eb9fecb9SRuslan Bilovol { 814eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 815eb9fecb9SRuslan Bilovol struct snd_card *card; 816eb9fecb9SRuslan Bilovol 817eb9fecb9SRuslan Bilovol if (!g_audio || !g_audio->uac) 818eb9fecb9SRuslan Bilovol return; 819eb9fecb9SRuslan Bilovol 820eb9fecb9SRuslan Bilovol uac = g_audio->uac; 821eb9fecb9SRuslan Bilovol card = uac->card; 822eb9fecb9SRuslan Bilovol if (card) 823eb9fecb9SRuslan Bilovol snd_card_free(card); 824eb9fecb9SRuslan Bilovol 82529865117SJerome Brunet kfree(uac->p_prm.reqs); 82629865117SJerome Brunet kfree(uac->c_prm.reqs); 827eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 828eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 829eb9fecb9SRuslan Bilovol kfree(uac); 830eb9fecb9SRuslan Bilovol } 831eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup); 832eb9fecb9SRuslan Bilovol 833eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL"); 834eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); 835eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol"); 836