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 15*02de698cSRuslan 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> 21*02de698cSRuslan Bilovol #include <sound/tlv.h> 22*02de698cSRuslan 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 30*02de698cSRuslan Bilovol enum { 31*02de698cSRuslan Bilovol UAC_FBACK_CTRL, 32*02de698cSRuslan Bilovol UAC_MUTE_CTRL, 33*02de698cSRuslan Bilovol UAC_VOLUME_CTRL, 34*02de698cSRuslan Bilovol }; 35*02de698cSRuslan Bilovol 36eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */ 37eb9fecb9SRuslan Bilovol struct uac_rtd_params { 38eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; /* parent chip */ 39eb9fecb9SRuslan Bilovol bool ep_enabled; /* if the ep is enabled */ 40eb9fecb9SRuslan Bilovol 41eb9fecb9SRuslan Bilovol struct snd_pcm_substream *ss; 42eb9fecb9SRuslan Bilovol 43eb9fecb9SRuslan Bilovol /* Ring buffer */ 44eb9fecb9SRuslan Bilovol ssize_t hw_ptr; 45eb9fecb9SRuslan Bilovol 46eb9fecb9SRuslan Bilovol void *rbuf; 47eb9fecb9SRuslan Bilovol 48e89bb428SRuslan Bilovol unsigned int pitch; /* Stream pitch ratio to 1000000 */ 49f4408a98SJonas Stenvall unsigned int max_psize; /* MaxPacketSize of endpoint */ 50eb9fecb9SRuslan Bilovol 51d70f7598SJerome Brunet struct usb_request **reqs; 5224f779daSRuslan Bilovol 5324f779daSRuslan Bilovol struct usb_request *req_fback; /* Feedback endpoint request */ 5424f779daSRuslan Bilovol bool fb_ep_enabled; /* if the ep is enabled */ 55*02de698cSRuslan Bilovol 56*02de698cSRuslan Bilovol /* Volume/Mute controls and their state */ 57*02de698cSRuslan Bilovol int fu_id; /* Feature Unit ID */ 58*02de698cSRuslan Bilovol struct snd_kcontrol *snd_kctl_volume; 59*02de698cSRuslan Bilovol struct snd_kcontrol *snd_kctl_mute; 60*02de698cSRuslan Bilovol s16 volume_min, volume_max, volume_res; 61*02de698cSRuslan Bilovol s16 volume; 62*02de698cSRuslan Bilovol int mute; 63*02de698cSRuslan Bilovol 64*02de698cSRuslan Bilovol spinlock_t lock; /* lock for control transfers */ 65*02de698cSRuslan Bilovol 66eb9fecb9SRuslan Bilovol }; 67eb9fecb9SRuslan Bilovol 68eb9fecb9SRuslan Bilovol struct snd_uac_chip { 69eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 70eb9fecb9SRuslan Bilovol 71eb9fecb9SRuslan Bilovol struct uac_rtd_params p_prm; 72eb9fecb9SRuslan Bilovol struct uac_rtd_params c_prm; 73eb9fecb9SRuslan Bilovol 74eb9fecb9SRuslan Bilovol struct snd_card *card; 75eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 76eb9fecb9SRuslan Bilovol 77eb9fecb9SRuslan Bilovol /* timekeeping for the playback endpoint */ 78eb9fecb9SRuslan Bilovol unsigned int p_interval; 79eb9fecb9SRuslan Bilovol unsigned int p_residue; 80eb9fecb9SRuslan Bilovol 81eb9fecb9SRuslan Bilovol /* pre-calculated values for playback iso completion */ 82eb9fecb9SRuslan Bilovol unsigned int p_pktsize; 83eb9fecb9SRuslan Bilovol unsigned int p_pktsize_residue; 84eb9fecb9SRuslan Bilovol unsigned int p_framesize; 85eb9fecb9SRuslan Bilovol }; 86eb9fecb9SRuslan Bilovol 872ab3c34cSBhumika Goyal static const struct snd_pcm_hardware uac_pcm_hardware = { 88eb9fecb9SRuslan Bilovol .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER 89eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID 90eb9fecb9SRuslan Bilovol | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 91eb9fecb9SRuslan Bilovol .rates = SNDRV_PCM_RATE_CONTINUOUS, 92eb9fecb9SRuslan Bilovol .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, 93eb9fecb9SRuslan Bilovol .buffer_bytes_max = BUFF_SIZE_MAX, 94eb9fecb9SRuslan Bilovol .period_bytes_max = PRD_SIZE_MAX, 95eb9fecb9SRuslan Bilovol .periods_min = MIN_PERIODS, 96eb9fecb9SRuslan Bilovol }; 97eb9fecb9SRuslan Bilovol 9824f779daSRuslan Bilovol static void u_audio_set_fback_frequency(enum usb_device_speed speed, 99e89bb428SRuslan Bilovol unsigned long long freq, 100e89bb428SRuslan Bilovol unsigned int pitch, 101e89bb428SRuslan Bilovol void *buf) 10224f779daSRuslan Bilovol { 10324f779daSRuslan Bilovol u32 ff = 0; 10424f779daSRuslan Bilovol 105e89bb428SRuslan Bilovol /* 106e89bb428SRuslan Bilovol * Because the pitch base is 1000000, the final divider here 107e89bb428SRuslan Bilovol * will be 1000 * 1000000 = 1953125 << 9 108e89bb428SRuslan Bilovol * 109e89bb428SRuslan Bilovol * Instead of dealing with big numbers lets fold this 9 left shift 110e89bb428SRuslan Bilovol */ 111e89bb428SRuslan Bilovol 11224f779daSRuslan Bilovol if (speed == USB_SPEED_FULL) { 11324f779daSRuslan Bilovol /* 11424f779daSRuslan Bilovol * Full-speed feedback endpoints report frequency 115e89bb428SRuslan Bilovol * in samples/frame 11624f779daSRuslan Bilovol * Format is encoded in Q10.10 left-justified in the 24 bits, 11724f779daSRuslan Bilovol * so that it has a Q10.14 format. 118e89bb428SRuslan Bilovol * 119e89bb428SRuslan Bilovol * ff = (freq << 14) / 1000 12024f779daSRuslan Bilovol */ 121e89bb428SRuslan Bilovol freq <<= 5; 12224f779daSRuslan Bilovol } else { 12324f779daSRuslan Bilovol /* 12424f779daSRuslan Bilovol * High-speed feedback endpoints report frequency 12524f779daSRuslan Bilovol * in samples/microframe. 12624f779daSRuslan Bilovol * Format is encoded in Q12.13 fitted into four bytes so that 12724f779daSRuslan Bilovol * the binary point is located between the second and the third 12824f779daSRuslan Bilovol * byte fromat (that is Q16.16) 129e89bb428SRuslan Bilovol * 130e89bb428SRuslan Bilovol * ff = (freq << 16) / 8000 13124f779daSRuslan Bilovol */ 132e89bb428SRuslan Bilovol freq <<= 4; 13324f779daSRuslan Bilovol } 134e89bb428SRuslan Bilovol 135e89bb428SRuslan Bilovol ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 136e89bb428SRuslan Bilovol 13724f779daSRuslan Bilovol *(__le32 *)buf = cpu_to_le32(ff); 13824f779daSRuslan Bilovol } 13924f779daSRuslan Bilovol 140eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) 141eb9fecb9SRuslan Bilovol { 142f4408a98SJonas Stenvall unsigned int pending; 143eb9fecb9SRuslan Bilovol unsigned int hw_ptr; 144eb9fecb9SRuslan Bilovol int status = req->status; 145eb9fecb9SRuslan Bilovol struct snd_pcm_substream *substream; 14696afb54eSVladimir Zapolskiy struct snd_pcm_runtime *runtime; 14729865117SJerome Brunet struct uac_rtd_params *prm = req->context; 148eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 149eb9fecb9SRuslan Bilovol 150eb9fecb9SRuslan Bilovol /* i/f shutting down */ 1517de8681bSJack Pham if (!prm->ep_enabled) { 1527de8681bSJack Pham usb_ep_free_request(ep, req); 1537de8681bSJack Pham return; 1547de8681bSJack Pham } 1557de8681bSJack Pham 1567de8681bSJack Pham if (req->status == -ESHUTDOWN) 157eb9fecb9SRuslan Bilovol return; 158eb9fecb9SRuslan Bilovol 159eb9fecb9SRuslan Bilovol /* 160eb9fecb9SRuslan Bilovol * We can't really do much about bad xfers. 161eb9fecb9SRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 162eb9fecb9SRuslan Bilovol */ 163eb9fecb9SRuslan Bilovol if (status) 164eb9fecb9SRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 165eb9fecb9SRuslan Bilovol __func__, status, req->actual, req->length); 166eb9fecb9SRuslan Bilovol 167eb9fecb9SRuslan Bilovol substream = prm->ss; 168eb9fecb9SRuslan Bilovol 169eb9fecb9SRuslan Bilovol /* Do nothing if ALSA isn't active */ 170eb9fecb9SRuslan Bilovol if (!substream) 171eb9fecb9SRuslan Bilovol goto exit; 172eb9fecb9SRuslan Bilovol 173d70f7598SJerome Brunet snd_pcm_stream_lock(substream); 17456bc6158SVladimir Zapolskiy 17596afb54eSVladimir Zapolskiy runtime = substream->runtime; 17656bc6158SVladimir Zapolskiy if (!runtime || !snd_pcm_running(substream)) { 177d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 17856bc6158SVladimir Zapolskiy goto exit; 17956bc6158SVladimir Zapolskiy } 18056bc6158SVladimir Zapolskiy 181eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 182eb9fecb9SRuslan Bilovol /* 183eb9fecb9SRuslan Bilovol * For each IN packet, take the quotient of the current data 184eb9fecb9SRuslan Bilovol * rate and the endpoint's interval as the base packet size. 185eb9fecb9SRuslan Bilovol * If there is a residue from this division, add it to the 186eb9fecb9SRuslan Bilovol * residue accumulator. 187eb9fecb9SRuslan Bilovol */ 188eb9fecb9SRuslan Bilovol req->length = uac->p_pktsize; 189eb9fecb9SRuslan Bilovol uac->p_residue += uac->p_pktsize_residue; 190eb9fecb9SRuslan Bilovol 191eb9fecb9SRuslan Bilovol /* 192eb9fecb9SRuslan Bilovol * Whenever there are more bytes in the accumulator than we 193eb9fecb9SRuslan Bilovol * need to add one more sample frame, increase this packet's 194eb9fecb9SRuslan Bilovol * size and decrease the accumulator. 195eb9fecb9SRuslan Bilovol */ 196eb9fecb9SRuslan Bilovol if (uac->p_residue / uac->p_interval >= uac->p_framesize) { 197eb9fecb9SRuslan Bilovol req->length += uac->p_framesize; 198eb9fecb9SRuslan Bilovol uac->p_residue -= uac->p_framesize * 199eb9fecb9SRuslan Bilovol uac->p_interval; 200eb9fecb9SRuslan Bilovol } 201eb9fecb9SRuslan Bilovol 202eb9fecb9SRuslan Bilovol req->actual = req->length; 203eb9fecb9SRuslan Bilovol } 204eb9fecb9SRuslan Bilovol 205eb9fecb9SRuslan Bilovol hw_ptr = prm->hw_ptr; 206eb9fecb9SRuslan Bilovol 207eb9fecb9SRuslan Bilovol /* Pack USB load in ALSA ring buffer */ 20896afb54eSVladimir Zapolskiy pending = runtime->dma_bytes - hw_ptr; 209eb9fecb9SRuslan Bilovol 210eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 211eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 21296afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, pending); 21396afb54eSVladimir Zapolskiy memcpy(req->buf + pending, runtime->dma_area, 214eb9fecb9SRuslan Bilovol req->actual - pending); 215eb9fecb9SRuslan Bilovol } else { 21696afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, 21796afb54eSVladimir Zapolskiy req->actual); 218eb9fecb9SRuslan Bilovol } 219eb9fecb9SRuslan Bilovol } else { 220eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 22196afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, pending); 22296afb54eSVladimir Zapolskiy memcpy(runtime->dma_area, req->buf + pending, 223eb9fecb9SRuslan Bilovol req->actual - pending); 224eb9fecb9SRuslan Bilovol } else { 22596afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, 22696afb54eSVladimir Zapolskiy req->actual); 227eb9fecb9SRuslan Bilovol } 228eb9fecb9SRuslan Bilovol } 229eb9fecb9SRuslan Bilovol 2306b37bd78SJoshua Frkuska /* update hw_ptr after data is copied to memory */ 23196afb54eSVladimir Zapolskiy prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; 232773e53d5SVladimir Zapolskiy hw_ptr = prm->hw_ptr; 233d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 2346b37bd78SJoshua Frkuska 235773e53d5SVladimir Zapolskiy if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) 236773e53d5SVladimir Zapolskiy snd_pcm_period_elapsed(substream); 237773e53d5SVladimir Zapolskiy 238eb9fecb9SRuslan Bilovol exit: 239eb9fecb9SRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 240eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 241eb9fecb9SRuslan Bilovol } 242eb9fecb9SRuslan Bilovol 24324f779daSRuslan Bilovol static void u_audio_iso_fback_complete(struct usb_ep *ep, 24424f779daSRuslan Bilovol struct usb_request *req) 24524f779daSRuslan Bilovol { 24624f779daSRuslan Bilovol struct uac_rtd_params *prm = req->context; 24724f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 24824f779daSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 249e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 25024f779daSRuslan Bilovol int status = req->status; 25124f779daSRuslan Bilovol 25224f779daSRuslan Bilovol /* i/f shutting down */ 25324f779daSRuslan Bilovol if (!prm->fb_ep_enabled || req->status == -ESHUTDOWN) 25424f779daSRuslan Bilovol return; 25524f779daSRuslan Bilovol 25624f779daSRuslan Bilovol /* 25724f779daSRuslan Bilovol * We can't really do much about bad xfers. 25824f779daSRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 25924f779daSRuslan Bilovol */ 26024f779daSRuslan Bilovol if (status) 26124f779daSRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 26224f779daSRuslan Bilovol __func__, status, req->actual, req->length); 26324f779daSRuslan Bilovol 26424f779daSRuslan Bilovol u_audio_set_fback_frequency(audio_dev->gadget->speed, 265e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 266e89bb428SRuslan Bilovol req->buf); 26724f779daSRuslan Bilovol 26824f779daSRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 26924f779daSRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 27024f779daSRuslan Bilovol } 27124f779daSRuslan Bilovol 272eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 273eb9fecb9SRuslan Bilovol { 274eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 275eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 276eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 277eb9fecb9SRuslan Bilovol struct uac_params *params; 278eb9fecb9SRuslan Bilovol int err = 0; 279eb9fecb9SRuslan Bilovol 280eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 281eb9fecb9SRuslan Bilovol params = &audio_dev->params; 282eb9fecb9SRuslan Bilovol 283eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 284eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 285eb9fecb9SRuslan Bilovol else 286eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 287eb9fecb9SRuslan Bilovol 288eb9fecb9SRuslan Bilovol /* Reset */ 289eb9fecb9SRuslan Bilovol prm->hw_ptr = 0; 290eb9fecb9SRuslan Bilovol 291eb9fecb9SRuslan Bilovol switch (cmd) { 292eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_START: 293eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_RESUME: 294eb9fecb9SRuslan Bilovol prm->ss = substream; 295eb9fecb9SRuslan Bilovol break; 296eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_STOP: 297eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_SUSPEND: 298eb9fecb9SRuslan Bilovol prm->ss = NULL; 299eb9fecb9SRuslan Bilovol break; 300eb9fecb9SRuslan Bilovol default: 301eb9fecb9SRuslan Bilovol err = -EINVAL; 302eb9fecb9SRuslan Bilovol } 303eb9fecb9SRuslan Bilovol 304eb9fecb9SRuslan Bilovol /* Clear buffer after Play stops */ 305eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) 306eb9fecb9SRuslan Bilovol memset(prm->rbuf, 0, prm->max_psize * params->req_number); 307eb9fecb9SRuslan Bilovol 308eb9fecb9SRuslan Bilovol return err; 309eb9fecb9SRuslan Bilovol } 310eb9fecb9SRuslan Bilovol 311eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) 312eb9fecb9SRuslan Bilovol { 313eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 314eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 315eb9fecb9SRuslan Bilovol 316eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 317eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 318eb9fecb9SRuslan Bilovol else 319eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 320eb9fecb9SRuslan Bilovol 321eb9fecb9SRuslan Bilovol return bytes_to_frames(substream->runtime, prm->hw_ptr); 322eb9fecb9SRuslan Bilovol } 323eb9fecb9SRuslan Bilovol 32425dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize) 32525dbd75dSJerome Brunet { 32625dbd75dSJerome Brunet u64 ret; 32725dbd75dSJerome Brunet 32825dbd75dSJerome Brunet switch (ssize) { 32925dbd75dSJerome Brunet case 3: 33025dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S24_3LE; 33125dbd75dSJerome Brunet break; 33225dbd75dSJerome Brunet case 4: 33325dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S32_LE; 33425dbd75dSJerome Brunet break; 33525dbd75dSJerome Brunet default: 33625dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S16_LE; 33725dbd75dSJerome Brunet break; 33825dbd75dSJerome Brunet } 33925dbd75dSJerome Brunet 34025dbd75dSJerome Brunet return ret; 34125dbd75dSJerome Brunet } 34225dbd75dSJerome Brunet 343eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream) 344eb9fecb9SRuslan Bilovol { 345eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 346eb9fecb9SRuslan Bilovol struct snd_pcm_runtime *runtime = substream->runtime; 347eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 348eb9fecb9SRuslan Bilovol struct uac_params *params; 349eb9fecb9SRuslan Bilovol int p_ssize, c_ssize; 350eb9fecb9SRuslan Bilovol int p_srate, c_srate; 351eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 352eb9fecb9SRuslan Bilovol 353eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 354eb9fecb9SRuslan Bilovol params = &audio_dev->params; 355eb9fecb9SRuslan Bilovol p_ssize = params->p_ssize; 356eb9fecb9SRuslan Bilovol c_ssize = params->c_ssize; 357eb9fecb9SRuslan Bilovol p_srate = params->p_srate; 358eb9fecb9SRuslan Bilovol c_srate = params->c_srate; 359eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 360eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 361eb9fecb9SRuslan Bilovol uac->p_residue = 0; 362eb9fecb9SRuslan Bilovol 363eb9fecb9SRuslan Bilovol runtime->hw = uac_pcm_hardware; 364eb9fecb9SRuslan Bilovol 365eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 366eb9fecb9SRuslan Bilovol runtime->hw.rate_min = p_srate; 36725dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(p_ssize); 368eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(p_chmask); 369eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize 370eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 371eb9fecb9SRuslan Bilovol } else { 372eb9fecb9SRuslan Bilovol runtime->hw.rate_min = c_srate; 37325dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(c_ssize); 374eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(c_chmask); 375eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize 376eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 377eb9fecb9SRuslan Bilovol } 378eb9fecb9SRuslan Bilovol 379eb9fecb9SRuslan Bilovol runtime->hw.rate_max = runtime->hw.rate_min; 380eb9fecb9SRuslan Bilovol runtime->hw.channels_max = runtime->hw.channels_min; 381eb9fecb9SRuslan Bilovol 382eb9fecb9SRuslan Bilovol snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 383eb9fecb9SRuslan Bilovol 384eb9fecb9SRuslan Bilovol return 0; 385eb9fecb9SRuslan Bilovol } 386eb9fecb9SRuslan Bilovol 387eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */ 388eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream) 389eb9fecb9SRuslan Bilovol { 390eb9fecb9SRuslan Bilovol return 0; 391eb9fecb9SRuslan Bilovol } 392eb9fecb9SRuslan Bilovol 393640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = { 394eb9fecb9SRuslan Bilovol .open = uac_pcm_open, 395eb9fecb9SRuslan Bilovol .close = uac_pcm_null, 396eb9fecb9SRuslan Bilovol .trigger = uac_pcm_trigger, 397eb9fecb9SRuslan Bilovol .pointer = uac_pcm_pointer, 398eb9fecb9SRuslan Bilovol .prepare = uac_pcm_null, 399eb9fecb9SRuslan Bilovol }; 400eb9fecb9SRuslan Bilovol 401eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 402eb9fecb9SRuslan Bilovol { 403eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 404eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 405eb9fecb9SRuslan Bilovol struct uac_params *params; 406eb9fecb9SRuslan Bilovol int i; 407eb9fecb9SRuslan Bilovol 408eb9fecb9SRuslan Bilovol if (!prm->ep_enabled) 409eb9fecb9SRuslan Bilovol return; 410eb9fecb9SRuslan Bilovol 411eb9fecb9SRuslan Bilovol prm->ep_enabled = false; 412eb9fecb9SRuslan Bilovol 413eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 414eb9fecb9SRuslan Bilovol params = &audio_dev->params; 415eb9fecb9SRuslan Bilovol 416eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 41729865117SJerome Brunet if (prm->reqs[i]) { 41829865117SJerome Brunet if (usb_ep_dequeue(ep, prm->reqs[i])) 41929865117SJerome Brunet usb_ep_free_request(ep, prm->reqs[i]); 4207de8681bSJack Pham /* 4217de8681bSJack Pham * If usb_ep_dequeue() cannot successfully dequeue the 4227de8681bSJack Pham * request, the request will be freed by the completion 4237de8681bSJack Pham * callback. 4247de8681bSJack Pham */ 4257de8681bSJack Pham 42629865117SJerome Brunet prm->reqs[i] = NULL; 427eb9fecb9SRuslan Bilovol } 428eb9fecb9SRuslan Bilovol } 429eb9fecb9SRuslan Bilovol 430eb9fecb9SRuslan Bilovol if (usb_ep_disable(ep)) 431eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 432eb9fecb9SRuslan Bilovol } 433eb9fecb9SRuslan Bilovol 43424f779daSRuslan Bilovol static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) 43524f779daSRuslan Bilovol { 43624f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 43724f779daSRuslan Bilovol 43824f779daSRuslan Bilovol if (!prm->fb_ep_enabled) 43924f779daSRuslan Bilovol return; 44024f779daSRuslan Bilovol 44124f779daSRuslan Bilovol prm->fb_ep_enabled = false; 44224f779daSRuslan Bilovol 44324f779daSRuslan Bilovol if (prm->req_fback) { 44424f779daSRuslan Bilovol usb_ep_dequeue(ep, prm->req_fback); 44524f779daSRuslan Bilovol kfree(prm->req_fback->buf); 44624f779daSRuslan Bilovol usb_ep_free_request(ep, prm->req_fback); 44724f779daSRuslan Bilovol prm->req_fback = NULL; 44824f779daSRuslan Bilovol } 44924f779daSRuslan Bilovol 45024f779daSRuslan Bilovol if (usb_ep_disable(ep)) 45124f779daSRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 45224f779daSRuslan Bilovol } 453eb9fecb9SRuslan Bilovol 454eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev) 455eb9fecb9SRuslan Bilovol { 456eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 457eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 458eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 45924f779daSRuslan Bilovol struct usb_request *req, *req_fback; 46024f779daSRuslan Bilovol struct usb_ep *ep, *ep_fback; 461eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 462eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 463eb9fecb9SRuslan Bilovol int req_len, i; 464eb9fecb9SRuslan Bilovol 465eb9fecb9SRuslan Bilovol ep = audio_dev->out_ep; 466eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 467eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 468904967c6SJohn Keeping req_len = ep->maxpacket; 469eb9fecb9SRuslan Bilovol 470eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 471eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 472eb9fecb9SRuslan Bilovol 473eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 47429865117SJerome Brunet if (!prm->reqs[i]) { 475eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 476eb9fecb9SRuslan Bilovol if (req == NULL) 477eb9fecb9SRuslan Bilovol return -ENOMEM; 478eb9fecb9SRuslan Bilovol 47929865117SJerome Brunet prm->reqs[i] = req; 480eb9fecb9SRuslan Bilovol 481eb9fecb9SRuslan Bilovol req->zero = 0; 48229865117SJerome Brunet req->context = prm; 483eb9fecb9SRuslan Bilovol req->length = req_len; 484eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 485904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 486eb9fecb9SRuslan Bilovol } 487eb9fecb9SRuslan Bilovol 48829865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 489eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 490eb9fecb9SRuslan Bilovol } 491eb9fecb9SRuslan Bilovol 49224f779daSRuslan Bilovol ep_fback = audio_dev->in_ep_fback; 49324f779daSRuslan Bilovol if (!ep_fback) 49424f779daSRuslan Bilovol return 0; 49524f779daSRuslan Bilovol 49624f779daSRuslan Bilovol /* Setup feedback endpoint */ 49724f779daSRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep_fback); 49824f779daSRuslan Bilovol prm->fb_ep_enabled = true; 49924f779daSRuslan Bilovol usb_ep_enable(ep_fback); 50024f779daSRuslan Bilovol req_len = ep_fback->maxpacket; 50124f779daSRuslan Bilovol 50224f779daSRuslan Bilovol req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); 50324f779daSRuslan Bilovol if (req_fback == NULL) 50424f779daSRuslan Bilovol return -ENOMEM; 50524f779daSRuslan Bilovol 50624f779daSRuslan Bilovol prm->req_fback = req_fback; 50724f779daSRuslan Bilovol req_fback->zero = 0; 50824f779daSRuslan Bilovol req_fback->context = prm; 50924f779daSRuslan Bilovol req_fback->length = req_len; 51024f779daSRuslan Bilovol req_fback->complete = u_audio_iso_fback_complete; 51124f779daSRuslan Bilovol 51224f779daSRuslan Bilovol req_fback->buf = kzalloc(req_len, GFP_ATOMIC); 51324f779daSRuslan Bilovol if (!req_fback->buf) 51424f779daSRuslan Bilovol return -ENOMEM; 51524f779daSRuslan Bilovol 51624f779daSRuslan Bilovol /* 51724f779daSRuslan Bilovol * Configure the feedback endpoint's reported frequency. 51824f779daSRuslan Bilovol * Always start with original frequency since its deviation can't 51924f779daSRuslan Bilovol * be meauserd at start of playback 52024f779daSRuslan Bilovol */ 521e89bb428SRuslan Bilovol prm->pitch = 1000000; 52224f779daSRuslan Bilovol u_audio_set_fback_frequency(audio_dev->gadget->speed, 523e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 524e89bb428SRuslan Bilovol req_fback->buf); 52524f779daSRuslan Bilovol 52624f779daSRuslan Bilovol if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) 52724f779daSRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 52824f779daSRuslan Bilovol 529eb9fecb9SRuslan Bilovol return 0; 530eb9fecb9SRuslan Bilovol } 531eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture); 532eb9fecb9SRuslan Bilovol 533eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev) 534eb9fecb9SRuslan Bilovol { 535eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 536eb9fecb9SRuslan Bilovol 53724f779daSRuslan Bilovol if (audio_dev->in_ep_fback) 53824f779daSRuslan Bilovol free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 539eb9fecb9SRuslan Bilovol free_ep(&uac->c_prm, audio_dev->out_ep); 540eb9fecb9SRuslan Bilovol } 541eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture); 542eb9fecb9SRuslan Bilovol 543eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev) 544eb9fecb9SRuslan Bilovol { 545eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 546eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 547eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 548eb9fecb9SRuslan Bilovol struct usb_request *req; 549eb9fecb9SRuslan Bilovol struct usb_ep *ep; 550eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 551eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 5526b02af34SJohn Keeping unsigned int factor; 553eb9fecb9SRuslan Bilovol const struct usb_endpoint_descriptor *ep_desc; 554eb9fecb9SRuslan Bilovol int req_len, i; 555eb9fecb9SRuslan Bilovol 556eb9fecb9SRuslan Bilovol ep = audio_dev->in_ep; 557eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 558eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 559eb9fecb9SRuslan Bilovol 560eb9fecb9SRuslan Bilovol ep_desc = ep->desc; 561eb9fecb9SRuslan Bilovol 562eb9fecb9SRuslan Bilovol /* pre-calculate the playback endpoint's interval */ 563eb9fecb9SRuslan Bilovol if (gadget->speed == USB_SPEED_FULL) 564eb9fecb9SRuslan Bilovol factor = 1000; 565eb9fecb9SRuslan Bilovol else 566eb9fecb9SRuslan Bilovol factor = 8000; 567eb9fecb9SRuslan Bilovol 568eb9fecb9SRuslan Bilovol /* pre-compute some values for iso_complete() */ 569eb9fecb9SRuslan Bilovol uac->p_framesize = params->p_ssize * 570eb9fecb9SRuslan Bilovol num_channels(params->p_chmask); 571eb9fecb9SRuslan Bilovol uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); 5726b02af34SJohn Keeping uac->p_pktsize = min_t(unsigned int, 5736b02af34SJohn Keeping uac->p_framesize * 5746b02af34SJohn Keeping (params->p_srate / uac->p_interval), 575904967c6SJohn Keeping ep->maxpacket); 576eb9fecb9SRuslan Bilovol 577904967c6SJohn Keeping if (uac->p_pktsize < ep->maxpacket) 5786b02af34SJohn Keeping uac->p_pktsize_residue = uac->p_framesize * 5796b02af34SJohn Keeping (params->p_srate % uac->p_interval); 580eb9fecb9SRuslan Bilovol else 581eb9fecb9SRuslan Bilovol uac->p_pktsize_residue = 0; 582eb9fecb9SRuslan Bilovol 583eb9fecb9SRuslan Bilovol req_len = uac->p_pktsize; 584eb9fecb9SRuslan Bilovol uac->p_residue = 0; 585eb9fecb9SRuslan Bilovol 586eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 587eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 588eb9fecb9SRuslan Bilovol 589eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 59029865117SJerome Brunet if (!prm->reqs[i]) { 591eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 592eb9fecb9SRuslan Bilovol if (req == NULL) 593eb9fecb9SRuslan Bilovol return -ENOMEM; 594eb9fecb9SRuslan Bilovol 59529865117SJerome Brunet prm->reqs[i] = req; 596eb9fecb9SRuslan Bilovol 597eb9fecb9SRuslan Bilovol req->zero = 0; 59829865117SJerome Brunet req->context = prm; 599eb9fecb9SRuslan Bilovol req->length = req_len; 600eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 601904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 602eb9fecb9SRuslan Bilovol } 603eb9fecb9SRuslan Bilovol 60429865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 605eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 606eb9fecb9SRuslan Bilovol } 607eb9fecb9SRuslan Bilovol 608eb9fecb9SRuslan Bilovol return 0; 609eb9fecb9SRuslan Bilovol } 610eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback); 611eb9fecb9SRuslan Bilovol 612eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev) 613eb9fecb9SRuslan Bilovol { 614eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 615eb9fecb9SRuslan Bilovol 616eb9fecb9SRuslan Bilovol free_ep(&uac->p_prm, audio_dev->in_ep); 617eb9fecb9SRuslan Bilovol } 618eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback); 619eb9fecb9SRuslan Bilovol 620*02de698cSRuslan Bilovol int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) 621*02de698cSRuslan Bilovol { 622*02de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 623*02de698cSRuslan Bilovol struct uac_rtd_params *prm; 624*02de698cSRuslan Bilovol unsigned long flags; 625*02de698cSRuslan Bilovol 626*02de698cSRuslan Bilovol if (playback) 627*02de698cSRuslan Bilovol prm = &uac->p_prm; 628*02de698cSRuslan Bilovol else 629*02de698cSRuslan Bilovol prm = &uac->c_prm; 630*02de698cSRuslan Bilovol 631*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 632*02de698cSRuslan Bilovol *val = prm->volume; 633*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 634*02de698cSRuslan Bilovol 635*02de698cSRuslan Bilovol return 0; 636*02de698cSRuslan Bilovol } 637*02de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_volume); 638*02de698cSRuslan Bilovol 639*02de698cSRuslan Bilovol int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) 640*02de698cSRuslan Bilovol { 641*02de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 642*02de698cSRuslan Bilovol struct uac_rtd_params *prm; 643*02de698cSRuslan Bilovol unsigned long flags; 644*02de698cSRuslan Bilovol int change = 0; 645*02de698cSRuslan Bilovol 646*02de698cSRuslan Bilovol if (playback) 647*02de698cSRuslan Bilovol prm = &uac->p_prm; 648*02de698cSRuslan Bilovol else 649*02de698cSRuslan Bilovol prm = &uac->c_prm; 650*02de698cSRuslan Bilovol 651*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 652*02de698cSRuslan Bilovol val = clamp(val, prm->volume_min, prm->volume_max); 653*02de698cSRuslan Bilovol if (prm->volume != val) { 654*02de698cSRuslan Bilovol prm->volume = val; 655*02de698cSRuslan Bilovol change = 1; 656*02de698cSRuslan Bilovol } 657*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 658*02de698cSRuslan Bilovol 659*02de698cSRuslan Bilovol if (change) 660*02de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 661*02de698cSRuslan Bilovol &prm->snd_kctl_volume->id); 662*02de698cSRuslan Bilovol 663*02de698cSRuslan Bilovol return 0; 664*02de698cSRuslan Bilovol } 665*02de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_volume); 666*02de698cSRuslan Bilovol 667*02de698cSRuslan Bilovol int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) 668*02de698cSRuslan Bilovol { 669*02de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 670*02de698cSRuslan Bilovol struct uac_rtd_params *prm; 671*02de698cSRuslan Bilovol unsigned long flags; 672*02de698cSRuslan Bilovol 673*02de698cSRuslan Bilovol if (playback) 674*02de698cSRuslan Bilovol prm = &uac->p_prm; 675*02de698cSRuslan Bilovol else 676*02de698cSRuslan Bilovol prm = &uac->c_prm; 677*02de698cSRuslan Bilovol 678*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 679*02de698cSRuslan Bilovol *val = prm->mute; 680*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 681*02de698cSRuslan Bilovol 682*02de698cSRuslan Bilovol return 0; 683*02de698cSRuslan Bilovol } 684*02de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_mute); 685*02de698cSRuslan Bilovol 686*02de698cSRuslan Bilovol int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) 687*02de698cSRuslan Bilovol { 688*02de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 689*02de698cSRuslan Bilovol struct uac_rtd_params *prm; 690*02de698cSRuslan Bilovol unsigned long flags; 691*02de698cSRuslan Bilovol int change = 0; 692*02de698cSRuslan Bilovol int mute; 693*02de698cSRuslan Bilovol 694*02de698cSRuslan Bilovol if (playback) 695*02de698cSRuslan Bilovol prm = &uac->p_prm; 696*02de698cSRuslan Bilovol else 697*02de698cSRuslan Bilovol prm = &uac->c_prm; 698*02de698cSRuslan Bilovol 699*02de698cSRuslan Bilovol mute = val ? 1 : 0; 700*02de698cSRuslan Bilovol 701*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 702*02de698cSRuslan Bilovol if (prm->mute != mute) { 703*02de698cSRuslan Bilovol prm->mute = mute; 704*02de698cSRuslan Bilovol change = 1; 705*02de698cSRuslan Bilovol } 706*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 707*02de698cSRuslan Bilovol 708*02de698cSRuslan Bilovol if (change) 709*02de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 710*02de698cSRuslan Bilovol &prm->snd_kctl_mute->id); 711*02de698cSRuslan Bilovol 712*02de698cSRuslan Bilovol return 0; 713*02de698cSRuslan Bilovol } 714*02de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_mute); 715*02de698cSRuslan Bilovol 716*02de698cSRuslan Bilovol 717e89bb428SRuslan Bilovol static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, 718e89bb428SRuslan Bilovol struct snd_ctl_elem_info *uinfo) 719e89bb428SRuslan Bilovol { 720e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 721e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 722e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 723e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 724e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 725e89bb428SRuslan Bilovol 726e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 727e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 728e89bb428SRuslan Bilovol 729e89bb428SRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 730e89bb428SRuslan Bilovol uinfo->count = 1; 731e89bb428SRuslan Bilovol uinfo->value.integer.min = pitch_min; 732e89bb428SRuslan Bilovol uinfo->value.integer.max = pitch_max; 733e89bb428SRuslan Bilovol uinfo->value.integer.step = 1; 734e89bb428SRuslan Bilovol return 0; 735e89bb428SRuslan Bilovol } 736e89bb428SRuslan Bilovol 737e89bb428SRuslan Bilovol static int u_audio_pitch_get(struct snd_kcontrol *kcontrol, 738e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 739e89bb428SRuslan Bilovol { 740e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 741e89bb428SRuslan Bilovol 742e89bb428SRuslan Bilovol ucontrol->value.integer.value[0] = prm->pitch; 743e89bb428SRuslan Bilovol 744e89bb428SRuslan Bilovol return 0; 745e89bb428SRuslan Bilovol } 746e89bb428SRuslan Bilovol 747e89bb428SRuslan Bilovol static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, 748e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 749e89bb428SRuslan Bilovol { 750e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 751e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 752e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 753e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 754e89bb428SRuslan Bilovol unsigned int val; 755e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 756e89bb428SRuslan Bilovol int change = 0; 757e89bb428SRuslan Bilovol 758e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 759e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 760e89bb428SRuslan Bilovol 761e89bb428SRuslan Bilovol val = ucontrol->value.integer.value[0]; 762e89bb428SRuslan Bilovol 763e89bb428SRuslan Bilovol if (val < pitch_min) 764e89bb428SRuslan Bilovol val = pitch_min; 765e89bb428SRuslan Bilovol if (val > pitch_max) 766e89bb428SRuslan Bilovol val = pitch_max; 767e89bb428SRuslan Bilovol 768e89bb428SRuslan Bilovol if (prm->pitch != val) { 769e89bb428SRuslan Bilovol prm->pitch = val; 770e89bb428SRuslan Bilovol change = 1; 771e89bb428SRuslan Bilovol } 772e89bb428SRuslan Bilovol 773e89bb428SRuslan Bilovol return change; 774e89bb428SRuslan Bilovol } 775e89bb428SRuslan Bilovol 776*02de698cSRuslan Bilovol static int u_audio_mute_info(struct snd_kcontrol *kcontrol, 777*02de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 778e89bb428SRuslan Bilovol { 779*02de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 780*02de698cSRuslan Bilovol uinfo->count = 1; 781*02de698cSRuslan Bilovol uinfo->value.integer.min = 0; 782*02de698cSRuslan Bilovol uinfo->value.integer.max = 1; 783*02de698cSRuslan Bilovol uinfo->value.integer.step = 1; 784*02de698cSRuslan Bilovol 785*02de698cSRuslan Bilovol return 0; 786*02de698cSRuslan Bilovol } 787*02de698cSRuslan Bilovol 788*02de698cSRuslan Bilovol static int u_audio_mute_get(struct snd_kcontrol *kcontrol, 789*02de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 790*02de698cSRuslan Bilovol { 791*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 792*02de698cSRuslan Bilovol unsigned long flags; 793*02de698cSRuslan Bilovol 794*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 795*02de698cSRuslan Bilovol ucontrol->value.integer.value[0] = !prm->mute; 796*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 797*02de698cSRuslan Bilovol 798*02de698cSRuslan Bilovol return 0; 799*02de698cSRuslan Bilovol } 800*02de698cSRuslan Bilovol 801*02de698cSRuslan Bilovol static int u_audio_mute_put(struct snd_kcontrol *kcontrol, 802*02de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 803*02de698cSRuslan Bilovol { 804*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 805*02de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 806*02de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 807*02de698cSRuslan Bilovol unsigned int val; 808*02de698cSRuslan Bilovol unsigned long flags; 809*02de698cSRuslan Bilovol int change = 0; 810*02de698cSRuslan Bilovol 811*02de698cSRuslan Bilovol val = !ucontrol->value.integer.value[0]; 812*02de698cSRuslan Bilovol 813*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 814*02de698cSRuslan Bilovol if (val != prm->mute) { 815*02de698cSRuslan Bilovol prm->mute = val; 816*02de698cSRuslan Bilovol change = 1; 817*02de698cSRuslan Bilovol } 818*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 819*02de698cSRuslan Bilovol 820*02de698cSRuslan Bilovol if (change && audio_dev->notify) 821*02de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); 822*02de698cSRuslan Bilovol 823*02de698cSRuslan Bilovol return change; 824*02de698cSRuslan Bilovol } 825*02de698cSRuslan Bilovol 826*02de698cSRuslan Bilovol /* 827*02de698cSRuslan Bilovol * TLV callback for mixer volume controls 828*02de698cSRuslan Bilovol */ 829*02de698cSRuslan Bilovol static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, 830*02de698cSRuslan Bilovol unsigned int size, unsigned int __user *_tlv) 831*02de698cSRuslan Bilovol { 832*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 833*02de698cSRuslan Bilovol DECLARE_TLV_DB_MINMAX(scale, 0, 0); 834*02de698cSRuslan Bilovol 835*02de698cSRuslan Bilovol if (size < sizeof(scale)) 836*02de698cSRuslan Bilovol return -ENOMEM; 837*02de698cSRuslan Bilovol 838*02de698cSRuslan Bilovol /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ 839*02de698cSRuslan Bilovol scale[2] = (prm->volume_min * 100) / 256; 840*02de698cSRuslan Bilovol scale[3] = (prm->volume_max * 100) / 256; 841*02de698cSRuslan Bilovol if (copy_to_user(_tlv, scale, sizeof(scale))) 842*02de698cSRuslan Bilovol return -EFAULT; 843*02de698cSRuslan Bilovol 844*02de698cSRuslan Bilovol return 0; 845*02de698cSRuslan Bilovol } 846*02de698cSRuslan Bilovol 847*02de698cSRuslan Bilovol static int u_audio_volume_info(struct snd_kcontrol *kcontrol, 848*02de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 849*02de698cSRuslan Bilovol { 850*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 851*02de698cSRuslan Bilovol 852*02de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 853*02de698cSRuslan Bilovol uinfo->count = 1; 854*02de698cSRuslan Bilovol uinfo->value.integer.min = 0; 855*02de698cSRuslan Bilovol uinfo->value.integer.max = 856*02de698cSRuslan Bilovol (prm->volume_max - prm->volume_min + prm->volume_res - 1) 857*02de698cSRuslan Bilovol / prm->volume_res; 858*02de698cSRuslan Bilovol uinfo->value.integer.step = 1; 859*02de698cSRuslan Bilovol 860*02de698cSRuslan Bilovol return 0; 861*02de698cSRuslan Bilovol } 862*02de698cSRuslan Bilovol 863*02de698cSRuslan Bilovol static int u_audio_volume_get(struct snd_kcontrol *kcontrol, 864*02de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 865*02de698cSRuslan Bilovol { 866*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 867*02de698cSRuslan Bilovol unsigned long flags; 868*02de698cSRuslan Bilovol 869*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 870*02de698cSRuslan Bilovol ucontrol->value.integer.value[0] = 871*02de698cSRuslan Bilovol (prm->volume - prm->volume_min) / prm->volume_res; 872*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 873*02de698cSRuslan Bilovol 874*02de698cSRuslan Bilovol return 0; 875*02de698cSRuslan Bilovol } 876*02de698cSRuslan Bilovol 877*02de698cSRuslan Bilovol static int u_audio_volume_put(struct snd_kcontrol *kcontrol, 878*02de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 879*02de698cSRuslan Bilovol { 880*02de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 881*02de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 882*02de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 883*02de698cSRuslan Bilovol unsigned int val; 884*02de698cSRuslan Bilovol s16 volume; 885*02de698cSRuslan Bilovol unsigned long flags; 886*02de698cSRuslan Bilovol int change = 0; 887*02de698cSRuslan Bilovol 888*02de698cSRuslan Bilovol val = ucontrol->value.integer.value[0]; 889*02de698cSRuslan Bilovol 890*02de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 891*02de698cSRuslan Bilovol volume = (val * prm->volume_res) + prm->volume_min; 892*02de698cSRuslan Bilovol volume = clamp(volume, prm->volume_min, prm->volume_max); 893*02de698cSRuslan Bilovol if (volume != prm->volume) { 894*02de698cSRuslan Bilovol prm->volume = volume; 895*02de698cSRuslan Bilovol change = 1; 896*02de698cSRuslan Bilovol } 897*02de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 898*02de698cSRuslan Bilovol 899*02de698cSRuslan Bilovol if (change && audio_dev->notify) 900*02de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); 901*02de698cSRuslan Bilovol 902*02de698cSRuslan Bilovol return change; 903*02de698cSRuslan Bilovol } 904*02de698cSRuslan Bilovol 905*02de698cSRuslan Bilovol 906*02de698cSRuslan Bilovol static struct snd_kcontrol_new u_audio_controls[] = { 907*02de698cSRuslan Bilovol [UAC_FBACK_CTRL] { 908e89bb428SRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_PCM, 909e89bb428SRuslan Bilovol .name = "Capture Pitch 1000000", 910e89bb428SRuslan Bilovol .info = u_audio_pitch_info, 911e89bb428SRuslan Bilovol .get = u_audio_pitch_get, 912e89bb428SRuslan Bilovol .put = u_audio_pitch_put, 913e89bb428SRuslan Bilovol }, 914*02de698cSRuslan Bilovol [UAC_MUTE_CTRL] { 915*02de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 916*02de698cSRuslan Bilovol .name = "", /* will be filled later */ 917*02de698cSRuslan Bilovol .info = u_audio_mute_info, 918*02de698cSRuslan Bilovol .get = u_audio_mute_get, 919*02de698cSRuslan Bilovol .put = u_audio_mute_put, 920*02de698cSRuslan Bilovol }, 921*02de698cSRuslan Bilovol [UAC_VOLUME_CTRL] { 922*02de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 923*02de698cSRuslan Bilovol .name = "", /* will be filled later */ 924*02de698cSRuslan Bilovol .info = u_audio_volume_info, 925*02de698cSRuslan Bilovol .get = u_audio_volume_get, 926*02de698cSRuslan Bilovol .put = u_audio_volume_put, 927*02de698cSRuslan Bilovol }, 928e89bb428SRuslan Bilovol }; 929e89bb428SRuslan Bilovol 930eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, 931eb9fecb9SRuslan Bilovol const char *card_name) 932eb9fecb9SRuslan Bilovol { 933eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 934eb9fecb9SRuslan Bilovol struct snd_card *card; 935eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 936e89bb428SRuslan Bilovol struct snd_kcontrol *kctl; 937eb9fecb9SRuslan Bilovol struct uac_params *params; 938eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 939*02de698cSRuslan Bilovol int i, err; 940eb9fecb9SRuslan Bilovol 941eb9fecb9SRuslan Bilovol if (!g_audio) 942eb9fecb9SRuslan Bilovol return -EINVAL; 943eb9fecb9SRuslan Bilovol 944eb9fecb9SRuslan Bilovol uac = kzalloc(sizeof(*uac), GFP_KERNEL); 945eb9fecb9SRuslan Bilovol if (!uac) 946eb9fecb9SRuslan Bilovol return -ENOMEM; 947eb9fecb9SRuslan Bilovol g_audio->uac = uac; 948eb9fecb9SRuslan Bilovol uac->audio_dev = g_audio; 949eb9fecb9SRuslan Bilovol 950eb9fecb9SRuslan Bilovol params = &g_audio->params; 951eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 952eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 953eb9fecb9SRuslan Bilovol 954eb9fecb9SRuslan Bilovol if (c_chmask) { 955eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->c_prm; 956eb9fecb9SRuslan Bilovol 957*02de698cSRuslan Bilovol spin_lock_init(&prm->lock); 958eb9fecb9SRuslan Bilovol uac->c_prm.uac = uac; 959eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->out_ep_maxpsize; 960eb9fecb9SRuslan Bilovol 96129865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 96229865117SJerome Brunet sizeof(struct usb_request *), 963eb9fecb9SRuslan Bilovol GFP_KERNEL); 96429865117SJerome Brunet if (!prm->reqs) { 965eb9fecb9SRuslan Bilovol err = -ENOMEM; 966eb9fecb9SRuslan Bilovol goto fail; 967eb9fecb9SRuslan Bilovol } 968eb9fecb9SRuslan Bilovol 969eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 970eb9fecb9SRuslan Bilovol GFP_KERNEL); 971eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 972eb9fecb9SRuslan Bilovol prm->max_psize = 0; 973eb9fecb9SRuslan Bilovol err = -ENOMEM; 974eb9fecb9SRuslan Bilovol goto fail; 975eb9fecb9SRuslan Bilovol } 976eb9fecb9SRuslan Bilovol } 977eb9fecb9SRuslan Bilovol 978eb9fecb9SRuslan Bilovol if (p_chmask) { 979eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->p_prm; 980eb9fecb9SRuslan Bilovol 981*02de698cSRuslan Bilovol spin_lock_init(&prm->lock); 982eb9fecb9SRuslan Bilovol uac->p_prm.uac = uac; 983eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->in_ep_maxpsize; 984eb9fecb9SRuslan Bilovol 98529865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 98629865117SJerome Brunet sizeof(struct usb_request *), 987eb9fecb9SRuslan Bilovol GFP_KERNEL); 98829865117SJerome Brunet if (!prm->reqs) { 989eb9fecb9SRuslan Bilovol err = -ENOMEM; 990eb9fecb9SRuslan Bilovol goto fail; 991eb9fecb9SRuslan Bilovol } 992eb9fecb9SRuslan Bilovol 993eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 994eb9fecb9SRuslan Bilovol GFP_KERNEL); 995eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 996eb9fecb9SRuslan Bilovol prm->max_psize = 0; 997eb9fecb9SRuslan Bilovol err = -ENOMEM; 998eb9fecb9SRuslan Bilovol goto fail; 999eb9fecb9SRuslan Bilovol } 1000eb9fecb9SRuslan Bilovol } 1001eb9fecb9SRuslan Bilovol 1002eb9fecb9SRuslan Bilovol /* Choose any slot, with no id */ 1003eb9fecb9SRuslan Bilovol err = snd_card_new(&g_audio->gadget->dev, 1004eb9fecb9SRuslan Bilovol -1, NULL, THIS_MODULE, 0, &card); 1005eb9fecb9SRuslan Bilovol if (err < 0) 1006eb9fecb9SRuslan Bilovol goto fail; 1007eb9fecb9SRuslan Bilovol 1008eb9fecb9SRuslan Bilovol uac->card = card; 1009eb9fecb9SRuslan Bilovol 1010eb9fecb9SRuslan Bilovol /* 1011eb9fecb9SRuslan Bilovol * Create first PCM device 1012eb9fecb9SRuslan Bilovol * Create a substream only for non-zero channel streams 1013eb9fecb9SRuslan Bilovol */ 1014eb9fecb9SRuslan Bilovol err = snd_pcm_new(uac->card, pcm_name, 0, 1015eb9fecb9SRuslan Bilovol p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); 1016eb9fecb9SRuslan Bilovol if (err < 0) 1017eb9fecb9SRuslan Bilovol goto snd_fail; 1018eb9fecb9SRuslan Bilovol 1019d23922fcSRuslan Bilovol strscpy(pcm->name, pcm_name, sizeof(pcm->name)); 1020eb9fecb9SRuslan Bilovol pcm->private_data = uac; 1021eb9fecb9SRuslan Bilovol uac->pcm = pcm; 1022eb9fecb9SRuslan Bilovol 1023eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); 1024eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); 1025eb9fecb9SRuslan Bilovol 1026*02de698cSRuslan Bilovol /* 1027*02de698cSRuslan Bilovol * Create mixer and controls 1028*02de698cSRuslan Bilovol * Create only if it's required on USB side 1029*02de698cSRuslan Bilovol */ 1030*02de698cSRuslan Bilovol if ((c_chmask && g_audio->in_ep_fback) 1031*02de698cSRuslan Bilovol || (p_chmask && params->p_fu.id) 1032*02de698cSRuslan Bilovol || (c_chmask && params->c_fu.id)) 1033e89bb428SRuslan Bilovol strscpy(card->mixername, card_name, sizeof(card->driver)); 1034e89bb428SRuslan Bilovol 1035*02de698cSRuslan Bilovol if (c_chmask && g_audio->in_ep_fback) { 1036*02de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], 1037*02de698cSRuslan Bilovol &uac->c_prm); 1038e89bb428SRuslan Bilovol if (!kctl) { 1039e89bb428SRuslan Bilovol err = -ENOMEM; 1040e89bb428SRuslan Bilovol goto snd_fail; 1041e89bb428SRuslan Bilovol } 1042e89bb428SRuslan Bilovol 1043e89bb428SRuslan Bilovol kctl->id.device = pcm->device; 1044e89bb428SRuslan Bilovol kctl->id.subdevice = 0; 1045e89bb428SRuslan Bilovol 1046e89bb428SRuslan Bilovol err = snd_ctl_add(card, kctl); 1047e89bb428SRuslan Bilovol if (err < 0) 1048e89bb428SRuslan Bilovol goto snd_fail; 1049e89bb428SRuslan Bilovol } 1050e89bb428SRuslan Bilovol 1051*02de698cSRuslan Bilovol for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { 1052*02de698cSRuslan Bilovol struct uac_rtd_params *prm; 1053*02de698cSRuslan Bilovol struct uac_fu_params *fu; 1054*02de698cSRuslan Bilovol char ctrl_name[24]; 1055*02de698cSRuslan Bilovol char *direction; 1056*02de698cSRuslan Bilovol 1057*02de698cSRuslan Bilovol if (!pcm->streams[i].substream_count) 1058*02de698cSRuslan Bilovol continue; 1059*02de698cSRuslan Bilovol 1060*02de698cSRuslan Bilovol if (i == SNDRV_PCM_STREAM_PLAYBACK) { 1061*02de698cSRuslan Bilovol prm = &uac->p_prm; 1062*02de698cSRuslan Bilovol fu = ¶ms->p_fu; 1063*02de698cSRuslan Bilovol direction = "Playback"; 1064*02de698cSRuslan Bilovol } else { 1065*02de698cSRuslan Bilovol prm = &uac->c_prm; 1066*02de698cSRuslan Bilovol fu = ¶ms->c_fu; 1067*02de698cSRuslan Bilovol direction = "Capture"; 1068*02de698cSRuslan Bilovol } 1069*02de698cSRuslan Bilovol 1070*02de698cSRuslan Bilovol prm->fu_id = fu->id; 1071*02de698cSRuslan Bilovol 1072*02de698cSRuslan Bilovol if (fu->mute_present) { 1073*02de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 1074*02de698cSRuslan Bilovol "PCM %s Switch", direction); 1075*02de698cSRuslan Bilovol 1076*02de698cSRuslan Bilovol u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; 1077*02de698cSRuslan Bilovol 1078*02de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], 1079*02de698cSRuslan Bilovol prm); 1080*02de698cSRuslan Bilovol if (!kctl) { 1081*02de698cSRuslan Bilovol err = -ENOMEM; 1082*02de698cSRuslan Bilovol goto snd_fail; 1083*02de698cSRuslan Bilovol } 1084*02de698cSRuslan Bilovol 1085*02de698cSRuslan Bilovol kctl->id.device = pcm->device; 1086*02de698cSRuslan Bilovol kctl->id.subdevice = i; 1087*02de698cSRuslan Bilovol 1088*02de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 1089*02de698cSRuslan Bilovol if (err < 0) 1090*02de698cSRuslan Bilovol goto snd_fail; 1091*02de698cSRuslan Bilovol prm->snd_kctl_mute = kctl; 1092*02de698cSRuslan Bilovol prm->mute = 0; 1093*02de698cSRuslan Bilovol } 1094*02de698cSRuslan Bilovol 1095*02de698cSRuslan Bilovol if (fu->volume_present) { 1096*02de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 1097*02de698cSRuslan Bilovol "PCM %s Volume", direction); 1098*02de698cSRuslan Bilovol 1099*02de698cSRuslan Bilovol u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; 1100*02de698cSRuslan Bilovol 1101*02de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], 1102*02de698cSRuslan Bilovol prm); 1103*02de698cSRuslan Bilovol if (!kctl) { 1104*02de698cSRuslan Bilovol err = -ENOMEM; 1105*02de698cSRuslan Bilovol goto snd_fail; 1106*02de698cSRuslan Bilovol } 1107*02de698cSRuslan Bilovol 1108*02de698cSRuslan Bilovol kctl->id.device = pcm->device; 1109*02de698cSRuslan Bilovol kctl->id.subdevice = i; 1110*02de698cSRuslan Bilovol 1111*02de698cSRuslan Bilovol 1112*02de698cSRuslan Bilovol kctl->tlv.c = u_audio_volume_tlv; 1113*02de698cSRuslan Bilovol kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | 1114*02de698cSRuslan Bilovol SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 1115*02de698cSRuslan Bilovol 1116*02de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 1117*02de698cSRuslan Bilovol if (err < 0) 1118*02de698cSRuslan Bilovol goto snd_fail; 1119*02de698cSRuslan Bilovol prm->snd_kctl_volume = kctl; 1120*02de698cSRuslan Bilovol prm->volume = fu->volume_max; 1121*02de698cSRuslan Bilovol prm->volume_max = fu->volume_max; 1122*02de698cSRuslan Bilovol prm->volume_min = fu->volume_min; 1123*02de698cSRuslan Bilovol prm->volume_res = fu->volume_res; 1124*02de698cSRuslan Bilovol } 1125*02de698cSRuslan Bilovol } 1126*02de698cSRuslan Bilovol 1127d23922fcSRuslan Bilovol strscpy(card->driver, card_name, sizeof(card->driver)); 1128d23922fcSRuslan Bilovol strscpy(card->shortname, card_name, sizeof(card->shortname)); 1129eb9fecb9SRuslan Bilovol sprintf(card->longname, "%s %i", card_name, card->dev->id); 1130eb9fecb9SRuslan Bilovol 1131d27ab1e6STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 113267b2945dSTakashi Iwai NULL, 0, BUFF_SIZE_MAX); 1133eb9fecb9SRuslan Bilovol 1134eb9fecb9SRuslan Bilovol err = snd_card_register(card); 1135eb9fecb9SRuslan Bilovol 1136eb9fecb9SRuslan Bilovol if (!err) 1137eb9fecb9SRuslan Bilovol return 0; 1138eb9fecb9SRuslan Bilovol 1139eb9fecb9SRuslan Bilovol snd_fail: 1140eb9fecb9SRuslan Bilovol snd_card_free(card); 1141eb9fecb9SRuslan Bilovol fail: 114229865117SJerome Brunet kfree(uac->p_prm.reqs); 114329865117SJerome Brunet kfree(uac->c_prm.reqs); 1144eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1145eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1146eb9fecb9SRuslan Bilovol kfree(uac); 1147eb9fecb9SRuslan Bilovol 1148eb9fecb9SRuslan Bilovol return err; 1149eb9fecb9SRuslan Bilovol } 1150eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup); 1151eb9fecb9SRuslan Bilovol 1152eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio) 1153eb9fecb9SRuslan Bilovol { 1154eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 1155eb9fecb9SRuslan Bilovol struct snd_card *card; 1156eb9fecb9SRuslan Bilovol 1157eb9fecb9SRuslan Bilovol if (!g_audio || !g_audio->uac) 1158eb9fecb9SRuslan Bilovol return; 1159eb9fecb9SRuslan Bilovol 1160eb9fecb9SRuslan Bilovol uac = g_audio->uac; 1161eb9fecb9SRuslan Bilovol card = uac->card; 1162eb9fecb9SRuslan Bilovol if (card) 1163eb9fecb9SRuslan Bilovol snd_card_free(card); 1164eb9fecb9SRuslan Bilovol 116529865117SJerome Brunet kfree(uac->p_prm.reqs); 116629865117SJerome Brunet kfree(uac->c_prm.reqs); 1167eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1168eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1169eb9fecb9SRuslan Bilovol kfree(uac); 1170eb9fecb9SRuslan Bilovol } 1171eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup); 1172eb9fecb9SRuslan Bilovol 1173eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL"); 1174eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); 1175eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol"); 1176