15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2eb9fecb9SRuslan Bilovol /* 3eb9fecb9SRuslan Bilovol * u_audio.c -- interface to USB gadget "ALSA sound card" utilities 4eb9fecb9SRuslan Bilovol * 5eb9fecb9SRuslan Bilovol * Copyright (C) 2016 6eb9fecb9SRuslan Bilovol * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com> 7eb9fecb9SRuslan Bilovol * 8eb9fecb9SRuslan Bilovol * Sound card implementation was cut-and-pasted with changes 9eb9fecb9SRuslan Bilovol * from f_uac2.c and has: 10eb9fecb9SRuslan Bilovol * Copyright (C) 2011 11eb9fecb9SRuslan Bilovol * Yadwinder Singh (yadi.brar01@gmail.com) 12eb9fecb9SRuslan Bilovol * Jaswinder Singh (jaswinder.singh@linaro.org) 13eb9fecb9SRuslan Bilovol */ 14eb9fecb9SRuslan Bilovol 1502de698cSRuslan Bilovol #include <linux/kernel.h> 16eb9fecb9SRuslan Bilovol #include <linux/module.h> 17eb9fecb9SRuslan Bilovol #include <sound/core.h> 18eb9fecb9SRuslan Bilovol #include <sound/pcm.h> 19eb9fecb9SRuslan Bilovol #include <sound/pcm_params.h> 20e89bb428SRuslan Bilovol #include <sound/control.h> 2102de698cSRuslan Bilovol #include <sound/tlv.h> 2202de698cSRuslan Bilovol #include <linux/usb/audio.h> 23eb9fecb9SRuslan Bilovol 24eb9fecb9SRuslan Bilovol #include "u_audio.h" 25eb9fecb9SRuslan Bilovol 26eb9fecb9SRuslan Bilovol #define BUFF_SIZE_MAX (PAGE_SIZE * 16) 27eb9fecb9SRuslan Bilovol #define PRD_SIZE_MAX PAGE_SIZE 28eb9fecb9SRuslan Bilovol #define MIN_PERIODS 4 29eb9fecb9SRuslan Bilovol 3002de698cSRuslan Bilovol enum { 3102de698cSRuslan Bilovol UAC_FBACK_CTRL, 326fec018aSPavel Hofman UAC_P_PITCH_CTRL, 3302de698cSRuslan Bilovol UAC_MUTE_CTRL, 3402de698cSRuslan Bilovol UAC_VOLUME_CTRL, 35*c565ad07SJulian Scheel UAC_RATE_CTRL, 3602de698cSRuslan Bilovol }; 3702de698cSRuslan Bilovol 38eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */ 39eb9fecb9SRuslan Bilovol struct uac_rtd_params { 40eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; /* parent chip */ 41eb9fecb9SRuslan Bilovol bool ep_enabled; /* if the ep is enabled */ 42eb9fecb9SRuslan Bilovol 43eb9fecb9SRuslan Bilovol struct snd_pcm_substream *ss; 44eb9fecb9SRuslan Bilovol 45eb9fecb9SRuslan Bilovol /* Ring buffer */ 46eb9fecb9SRuslan Bilovol ssize_t hw_ptr; 47eb9fecb9SRuslan Bilovol 48eb9fecb9SRuslan Bilovol void *rbuf; 49eb9fecb9SRuslan Bilovol 50e89bb428SRuslan Bilovol unsigned int pitch; /* Stream pitch ratio to 1000000 */ 51f4408a98SJonas Stenvall unsigned int max_psize; /* MaxPacketSize of endpoint */ 52eb9fecb9SRuslan Bilovol 53d70f7598SJerome Brunet struct usb_request **reqs; 5424f779daSRuslan Bilovol 5524f779daSRuslan Bilovol struct usb_request *req_fback; /* Feedback endpoint request */ 5624f779daSRuslan Bilovol bool fb_ep_enabled; /* if the ep is enabled */ 5702de698cSRuslan Bilovol 5802de698cSRuslan Bilovol /* Volume/Mute controls and their state */ 5902de698cSRuslan Bilovol int fu_id; /* Feature Unit ID */ 6002de698cSRuslan Bilovol struct snd_kcontrol *snd_kctl_volume; 6102de698cSRuslan Bilovol struct snd_kcontrol *snd_kctl_mute; 6202de698cSRuslan Bilovol s16 volume_min, volume_max, volume_res; 6302de698cSRuslan Bilovol s16 volume; 6402de698cSRuslan Bilovol int mute; 6502de698cSRuslan Bilovol 66*c565ad07SJulian Scheel struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ 67*c565ad07SJulian Scheel 6802de698cSRuslan Bilovol spinlock_t lock; /* lock for control transfers */ 6902de698cSRuslan Bilovol 70eb9fecb9SRuslan Bilovol }; 71eb9fecb9SRuslan Bilovol 72eb9fecb9SRuslan Bilovol struct snd_uac_chip { 73eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 74eb9fecb9SRuslan Bilovol 75eb9fecb9SRuslan Bilovol struct uac_rtd_params p_prm; 76eb9fecb9SRuslan Bilovol struct uac_rtd_params c_prm; 77eb9fecb9SRuslan Bilovol 78eb9fecb9SRuslan Bilovol struct snd_card *card; 79eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 80eb9fecb9SRuslan Bilovol 81eb9fecb9SRuslan Bilovol /* pre-calculated values for playback iso completion */ 826fec018aSPavel Hofman unsigned long long p_residue_mil; 83f2f69bf6SJohn Keeping unsigned int p_interval; 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, 99f5dfd98aSPavel Hofman struct usb_ep *out_ep, 100e89bb428SRuslan Bilovol unsigned long long freq, 101e89bb428SRuslan Bilovol unsigned int pitch, 102e89bb428SRuslan Bilovol void *buf) 10324f779daSRuslan Bilovol { 10424f779daSRuslan Bilovol u32 ff = 0; 105f5dfd98aSPavel Hofman const struct usb_endpoint_descriptor *ep_desc; 10624f779daSRuslan Bilovol 107e89bb428SRuslan Bilovol /* 108e89bb428SRuslan Bilovol * Because the pitch base is 1000000, the final divider here 109e89bb428SRuslan Bilovol * will be 1000 * 1000000 = 1953125 << 9 110e89bb428SRuslan Bilovol * 111e89bb428SRuslan Bilovol * Instead of dealing with big numbers lets fold this 9 left shift 112e89bb428SRuslan Bilovol */ 113e89bb428SRuslan Bilovol 11424f779daSRuslan Bilovol if (speed == USB_SPEED_FULL) { 11524f779daSRuslan Bilovol /* 11624f779daSRuslan Bilovol * Full-speed feedback endpoints report frequency 117e89bb428SRuslan Bilovol * in samples/frame 11824f779daSRuslan Bilovol * Format is encoded in Q10.10 left-justified in the 24 bits, 11924f779daSRuslan Bilovol * so that it has a Q10.14 format. 120e89bb428SRuslan Bilovol * 121e89bb428SRuslan Bilovol * ff = (freq << 14) / 1000 12224f779daSRuslan Bilovol */ 123e89bb428SRuslan Bilovol freq <<= 5; 12424f779daSRuslan Bilovol } else { 12524f779daSRuslan Bilovol /* 12624f779daSRuslan Bilovol * High-speed feedback endpoints report frequency 12724f779daSRuslan Bilovol * in samples/microframe. 12824f779daSRuslan Bilovol * Format is encoded in Q12.13 fitted into four bytes so that 12924f779daSRuslan Bilovol * the binary point is located between the second and the third 13024f779daSRuslan Bilovol * byte fromat (that is Q16.16) 131e89bb428SRuslan Bilovol * 132e89bb428SRuslan Bilovol * ff = (freq << 16) / 8000 133f5dfd98aSPavel Hofman * 134f5dfd98aSPavel Hofman * Win10 and OSX UAC2 drivers require number of samples per packet 135f5dfd98aSPavel Hofman * in order to honor the feedback value. 136f5dfd98aSPavel Hofman * Linux snd-usb-audio detects the applied bit-shift automatically. 13724f779daSRuslan Bilovol */ 138f5dfd98aSPavel Hofman ep_desc = out_ep->desc; 139f5dfd98aSPavel Hofman freq <<= 4 + (ep_desc->bInterval - 1); 14024f779daSRuslan Bilovol } 141e89bb428SRuslan Bilovol 142e89bb428SRuslan Bilovol ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 143e89bb428SRuslan Bilovol 14424f779daSRuslan Bilovol *(__le32 *)buf = cpu_to_le32(ff); 14524f779daSRuslan Bilovol } 14624f779daSRuslan Bilovol 147eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) 148eb9fecb9SRuslan Bilovol { 149f4408a98SJonas Stenvall unsigned int pending; 150eb9fecb9SRuslan Bilovol unsigned int hw_ptr; 151eb9fecb9SRuslan Bilovol int status = req->status; 152eb9fecb9SRuslan Bilovol struct snd_pcm_substream *substream; 15396afb54eSVladimir Zapolskiy struct snd_pcm_runtime *runtime; 15429865117SJerome Brunet struct uac_rtd_params *prm = req->context; 155eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 1566fec018aSPavel Hofman struct g_audio *audio_dev = uac->audio_dev; 1576fec018aSPavel Hofman struct uac_params *params = &audio_dev->params; 1586fec018aSPavel Hofman unsigned int frames, p_pktsize; 1596fec018aSPavel Hofman unsigned long long pitched_rate_mil, p_pktsize_residue_mil, 1606fec018aSPavel Hofman residue_frames_mil, div_result; 161eb9fecb9SRuslan Bilovol 162eb9fecb9SRuslan Bilovol /* i/f shutting down */ 1637de8681bSJack Pham if (!prm->ep_enabled) { 1647de8681bSJack Pham usb_ep_free_request(ep, req); 1657de8681bSJack Pham return; 1667de8681bSJack Pham } 1677de8681bSJack Pham 1687de8681bSJack Pham if (req->status == -ESHUTDOWN) 169eb9fecb9SRuslan Bilovol return; 170eb9fecb9SRuslan Bilovol 171eb9fecb9SRuslan Bilovol /* 172eb9fecb9SRuslan Bilovol * We can't really do much about bad xfers. 173eb9fecb9SRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 174eb9fecb9SRuslan Bilovol */ 175eb9fecb9SRuslan Bilovol if (status) 176eb9fecb9SRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 177eb9fecb9SRuslan Bilovol __func__, status, req->actual, req->length); 178eb9fecb9SRuslan Bilovol 179eb9fecb9SRuslan Bilovol substream = prm->ss; 180eb9fecb9SRuslan Bilovol 181eb9fecb9SRuslan Bilovol /* Do nothing if ALSA isn't active */ 182eb9fecb9SRuslan Bilovol if (!substream) 183eb9fecb9SRuslan Bilovol goto exit; 184eb9fecb9SRuslan Bilovol 185d70f7598SJerome Brunet snd_pcm_stream_lock(substream); 18656bc6158SVladimir Zapolskiy 18796afb54eSVladimir Zapolskiy runtime = substream->runtime; 18856bc6158SVladimir Zapolskiy if (!runtime || !snd_pcm_running(substream)) { 189d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 19056bc6158SVladimir Zapolskiy goto exit; 19156bc6158SVladimir Zapolskiy } 19256bc6158SVladimir Zapolskiy 193eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 194eb9fecb9SRuslan Bilovol /* 195eb9fecb9SRuslan Bilovol * For each IN packet, take the quotient of the current data 196eb9fecb9SRuslan Bilovol * rate and the endpoint's interval as the base packet size. 197eb9fecb9SRuslan Bilovol * If there is a residue from this division, add it to the 198eb9fecb9SRuslan Bilovol * residue accumulator. 199eb9fecb9SRuslan Bilovol */ 200f2f69bf6SJohn Keeping unsigned long long p_interval_mil = uac->p_interval * 1000000ULL; 201f2f69bf6SJohn Keeping 2026fec018aSPavel Hofman pitched_rate_mil = (unsigned long long) 2036fec018aSPavel Hofman params->p_srate * prm->pitch; 2046fec018aSPavel Hofman div_result = pitched_rate_mil; 205f2f69bf6SJohn Keeping do_div(div_result, uac->p_interval); 206f2f69bf6SJohn Keeping do_div(div_result, 1000000); 2076fec018aSPavel Hofman frames = (unsigned int) div_result; 2086fec018aSPavel Hofman 2096fec018aSPavel Hofman pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n", 210f2f69bf6SJohn Keeping params->p_srate, prm->pitch, p_interval_mil, frames); 2116fec018aSPavel Hofman 2126fec018aSPavel Hofman p_pktsize = min_t(unsigned int, 2136fec018aSPavel Hofman uac->p_framesize * frames, 2146fec018aSPavel Hofman ep->maxpacket); 2156fec018aSPavel Hofman 2166fec018aSPavel Hofman if (p_pktsize < ep->maxpacket) { 217f2f69bf6SJohn Keeping residue_frames_mil = pitched_rate_mil - frames * p_interval_mil; 2186fec018aSPavel Hofman p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil; 2196fec018aSPavel Hofman } else 2206fec018aSPavel Hofman p_pktsize_residue_mil = 0; 2216fec018aSPavel Hofman 2226fec018aSPavel Hofman req->length = p_pktsize; 2236fec018aSPavel Hofman uac->p_residue_mil += p_pktsize_residue_mil; 224eb9fecb9SRuslan Bilovol 225eb9fecb9SRuslan Bilovol /* 2266fec018aSPavel Hofman * Whenever there are more bytes in the accumulator p_residue_mil than we 227eb9fecb9SRuslan Bilovol * need to add one more sample frame, increase this packet's 228eb9fecb9SRuslan Bilovol * size and decrease the accumulator. 229eb9fecb9SRuslan Bilovol */ 2306fec018aSPavel Hofman div_result = uac->p_residue_mil; 231f2f69bf6SJohn Keeping do_div(div_result, uac->p_interval); 232f2f69bf6SJohn Keeping do_div(div_result, 1000000); 2336fec018aSPavel Hofman if ((unsigned int) div_result >= uac->p_framesize) { 234eb9fecb9SRuslan Bilovol req->length += uac->p_framesize; 235f2f69bf6SJohn Keeping uac->p_residue_mil -= uac->p_framesize * p_interval_mil; 2366fec018aSPavel Hofman pr_debug("increased req length to %d\n", req->length); 237eb9fecb9SRuslan Bilovol } 2386fec018aSPavel Hofman pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil); 239eb9fecb9SRuslan Bilovol 240eb9fecb9SRuslan Bilovol req->actual = req->length; 241eb9fecb9SRuslan Bilovol } 242eb9fecb9SRuslan Bilovol 243eb9fecb9SRuslan Bilovol hw_ptr = prm->hw_ptr; 244eb9fecb9SRuslan Bilovol 245eb9fecb9SRuslan Bilovol /* Pack USB load in ALSA ring buffer */ 24696afb54eSVladimir Zapolskiy pending = runtime->dma_bytes - hw_ptr; 247eb9fecb9SRuslan Bilovol 248eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 249eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 25096afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, pending); 25196afb54eSVladimir Zapolskiy memcpy(req->buf + pending, runtime->dma_area, 252eb9fecb9SRuslan Bilovol req->actual - pending); 253eb9fecb9SRuslan Bilovol } else { 25496afb54eSVladimir Zapolskiy memcpy(req->buf, runtime->dma_area + hw_ptr, 25596afb54eSVladimir Zapolskiy req->actual); 256eb9fecb9SRuslan Bilovol } 257eb9fecb9SRuslan Bilovol } else { 258eb9fecb9SRuslan Bilovol if (unlikely(pending < req->actual)) { 25996afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, pending); 26096afb54eSVladimir Zapolskiy memcpy(runtime->dma_area, req->buf + pending, 261eb9fecb9SRuslan Bilovol req->actual - pending); 262eb9fecb9SRuslan Bilovol } else { 26396afb54eSVladimir Zapolskiy memcpy(runtime->dma_area + hw_ptr, req->buf, 26496afb54eSVladimir Zapolskiy req->actual); 265eb9fecb9SRuslan Bilovol } 266eb9fecb9SRuslan Bilovol } 267eb9fecb9SRuslan Bilovol 2686b37bd78SJoshua Frkuska /* update hw_ptr after data is copied to memory */ 26996afb54eSVladimir Zapolskiy prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; 270773e53d5SVladimir Zapolskiy hw_ptr = prm->hw_ptr; 271d70f7598SJerome Brunet snd_pcm_stream_unlock(substream); 2726b37bd78SJoshua Frkuska 273773e53d5SVladimir Zapolskiy if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) 274773e53d5SVladimir Zapolskiy snd_pcm_period_elapsed(substream); 275773e53d5SVladimir Zapolskiy 276eb9fecb9SRuslan Bilovol exit: 277eb9fecb9SRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 278eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 279eb9fecb9SRuslan Bilovol } 280eb9fecb9SRuslan Bilovol 28124f779daSRuslan Bilovol static void u_audio_iso_fback_complete(struct usb_ep *ep, 28224f779daSRuslan Bilovol struct usb_request *req) 28324f779daSRuslan Bilovol { 28424f779daSRuslan Bilovol struct uac_rtd_params *prm = req->context; 28524f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 28624f779daSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 287e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 28824f779daSRuslan Bilovol int status = req->status; 28924f779daSRuslan Bilovol 29024f779daSRuslan Bilovol /* i/f shutting down */ 29175432ba5SJerome Brunet if (!prm->fb_ep_enabled) { 29275432ba5SJerome Brunet kfree(req->buf); 29375432ba5SJerome Brunet usb_ep_free_request(ep, req); 29475432ba5SJerome Brunet return; 29575432ba5SJerome Brunet } 29675432ba5SJerome Brunet 29775432ba5SJerome Brunet if (req->status == -ESHUTDOWN) 29824f779daSRuslan Bilovol return; 29924f779daSRuslan Bilovol 30024f779daSRuslan Bilovol /* 30124f779daSRuslan Bilovol * We can't really do much about bad xfers. 30224f779daSRuslan Bilovol * Afterall, the ISOCH xfers could fail legitimately. 30324f779daSRuslan Bilovol */ 30424f779daSRuslan Bilovol if (status) 30524f779daSRuslan Bilovol pr_debug("%s: iso_complete status(%d) %d/%d\n", 30624f779daSRuslan Bilovol __func__, status, req->actual, req->length); 30724f779daSRuslan Bilovol 308f5dfd98aSPavel Hofman u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep, 309e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 310e89bb428SRuslan Bilovol req->buf); 31124f779daSRuslan Bilovol 31224f779daSRuslan Bilovol if (usb_ep_queue(ep, req, GFP_ATOMIC)) 31324f779daSRuslan Bilovol dev_err(uac->card->dev, "%d Error!\n", __LINE__); 31424f779daSRuslan Bilovol } 31524f779daSRuslan Bilovol 316eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 317eb9fecb9SRuslan Bilovol { 318eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 319eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 320eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 321eb9fecb9SRuslan Bilovol struct uac_params *params; 322eb9fecb9SRuslan Bilovol int err = 0; 323eb9fecb9SRuslan Bilovol 324eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 325eb9fecb9SRuslan Bilovol params = &audio_dev->params; 326eb9fecb9SRuslan Bilovol 327eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 328eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 329eb9fecb9SRuslan Bilovol else 330eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 331eb9fecb9SRuslan Bilovol 332eb9fecb9SRuslan Bilovol /* Reset */ 333eb9fecb9SRuslan Bilovol prm->hw_ptr = 0; 334eb9fecb9SRuslan Bilovol 335eb9fecb9SRuslan Bilovol switch (cmd) { 336eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_START: 337eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_RESUME: 338eb9fecb9SRuslan Bilovol prm->ss = substream; 339eb9fecb9SRuslan Bilovol break; 340eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_STOP: 341eb9fecb9SRuslan Bilovol case SNDRV_PCM_TRIGGER_SUSPEND: 342eb9fecb9SRuslan Bilovol prm->ss = NULL; 343eb9fecb9SRuslan Bilovol break; 344eb9fecb9SRuslan Bilovol default: 345eb9fecb9SRuslan Bilovol err = -EINVAL; 346eb9fecb9SRuslan Bilovol } 347eb9fecb9SRuslan Bilovol 348eb9fecb9SRuslan Bilovol /* Clear buffer after Play stops */ 349eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) 350eb9fecb9SRuslan Bilovol memset(prm->rbuf, 0, prm->max_psize * params->req_number); 351eb9fecb9SRuslan Bilovol 352eb9fecb9SRuslan Bilovol return err; 353eb9fecb9SRuslan Bilovol } 354eb9fecb9SRuslan Bilovol 355eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) 356eb9fecb9SRuslan Bilovol { 357eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 358eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 359eb9fecb9SRuslan Bilovol 360eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 361eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 362eb9fecb9SRuslan Bilovol else 363eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 364eb9fecb9SRuslan Bilovol 365eb9fecb9SRuslan Bilovol return bytes_to_frames(substream->runtime, prm->hw_ptr); 366eb9fecb9SRuslan Bilovol } 367eb9fecb9SRuslan Bilovol 36825dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize) 36925dbd75dSJerome Brunet { 37025dbd75dSJerome Brunet u64 ret; 37125dbd75dSJerome Brunet 37225dbd75dSJerome Brunet switch (ssize) { 37325dbd75dSJerome Brunet case 3: 37425dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S24_3LE; 37525dbd75dSJerome Brunet break; 37625dbd75dSJerome Brunet case 4: 37725dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S32_LE; 37825dbd75dSJerome Brunet break; 37925dbd75dSJerome Brunet default: 38025dbd75dSJerome Brunet ret = SNDRV_PCM_FMTBIT_S16_LE; 38125dbd75dSJerome Brunet break; 38225dbd75dSJerome Brunet } 38325dbd75dSJerome Brunet 38425dbd75dSJerome Brunet return ret; 38525dbd75dSJerome Brunet } 38625dbd75dSJerome Brunet 387eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream) 388eb9fecb9SRuslan Bilovol { 389eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 390eb9fecb9SRuslan Bilovol struct snd_pcm_runtime *runtime = substream->runtime; 391eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 392eb9fecb9SRuslan Bilovol struct uac_params *params; 393eb9fecb9SRuslan Bilovol int p_ssize, c_ssize; 394eb9fecb9SRuslan Bilovol int p_srate, c_srate; 395eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 396eb9fecb9SRuslan Bilovol 397eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 398eb9fecb9SRuslan Bilovol params = &audio_dev->params; 399eb9fecb9SRuslan Bilovol p_ssize = params->p_ssize; 400eb9fecb9SRuslan Bilovol c_ssize = params->c_ssize; 401eb9fecb9SRuslan Bilovol p_srate = params->p_srate; 402eb9fecb9SRuslan Bilovol c_srate = params->c_srate; 403eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 404eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 4056fec018aSPavel Hofman uac->p_residue_mil = 0; 406eb9fecb9SRuslan Bilovol 407eb9fecb9SRuslan Bilovol runtime->hw = uac_pcm_hardware; 408eb9fecb9SRuslan Bilovol 409eb9fecb9SRuslan Bilovol if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 410eb9fecb9SRuslan Bilovol runtime->hw.rate_min = p_srate; 41125dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(p_ssize); 412eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(p_chmask); 413eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize 414eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 415eb9fecb9SRuslan Bilovol } else { 416eb9fecb9SRuslan Bilovol runtime->hw.rate_min = c_srate; 41725dbd75dSJerome Brunet runtime->hw.formats = uac_ssize_to_fmt(c_ssize); 418eb9fecb9SRuslan Bilovol runtime->hw.channels_min = num_channels(c_chmask); 419eb9fecb9SRuslan Bilovol runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize 420eb9fecb9SRuslan Bilovol / runtime->hw.periods_min; 421eb9fecb9SRuslan Bilovol } 422eb9fecb9SRuslan Bilovol 423eb9fecb9SRuslan Bilovol runtime->hw.rate_max = runtime->hw.rate_min; 424eb9fecb9SRuslan Bilovol runtime->hw.channels_max = runtime->hw.channels_min; 425eb9fecb9SRuslan Bilovol 426eb9fecb9SRuslan Bilovol snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 427eb9fecb9SRuslan Bilovol 428eb9fecb9SRuslan Bilovol return 0; 429eb9fecb9SRuslan Bilovol } 430eb9fecb9SRuslan Bilovol 431eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */ 432eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream) 433eb9fecb9SRuslan Bilovol { 434eb9fecb9SRuslan Bilovol return 0; 435eb9fecb9SRuslan Bilovol } 436eb9fecb9SRuslan Bilovol 437640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = { 438eb9fecb9SRuslan Bilovol .open = uac_pcm_open, 439eb9fecb9SRuslan Bilovol .close = uac_pcm_null, 440eb9fecb9SRuslan Bilovol .trigger = uac_pcm_trigger, 441eb9fecb9SRuslan Bilovol .pointer = uac_pcm_pointer, 442eb9fecb9SRuslan Bilovol .prepare = uac_pcm_null, 443eb9fecb9SRuslan Bilovol }; 444eb9fecb9SRuslan Bilovol 445eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 446eb9fecb9SRuslan Bilovol { 447eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 448eb9fecb9SRuslan Bilovol struct g_audio *audio_dev; 449eb9fecb9SRuslan Bilovol struct uac_params *params; 450eb9fecb9SRuslan Bilovol int i; 451eb9fecb9SRuslan Bilovol 452eb9fecb9SRuslan Bilovol if (!prm->ep_enabled) 453eb9fecb9SRuslan Bilovol return; 454eb9fecb9SRuslan Bilovol 455eb9fecb9SRuslan Bilovol audio_dev = uac->audio_dev; 456eb9fecb9SRuslan Bilovol params = &audio_dev->params; 457eb9fecb9SRuslan Bilovol 458eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 45929865117SJerome Brunet if (prm->reqs[i]) { 46029865117SJerome Brunet if (usb_ep_dequeue(ep, prm->reqs[i])) 46129865117SJerome Brunet usb_ep_free_request(ep, prm->reqs[i]); 4627de8681bSJack Pham /* 4637de8681bSJack Pham * If usb_ep_dequeue() cannot successfully dequeue the 4647de8681bSJack Pham * request, the request will be freed by the completion 4657de8681bSJack Pham * callback. 4667de8681bSJack Pham */ 4677de8681bSJack Pham 46829865117SJerome Brunet prm->reqs[i] = NULL; 469eb9fecb9SRuslan Bilovol } 470eb9fecb9SRuslan Bilovol } 471eb9fecb9SRuslan Bilovol 472068fdad2SJerome Brunet prm->ep_enabled = false; 473068fdad2SJerome Brunet 474eb9fecb9SRuslan Bilovol if (usb_ep_disable(ep)) 475eb9fecb9SRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 476eb9fecb9SRuslan Bilovol } 477eb9fecb9SRuslan Bilovol 47824f779daSRuslan Bilovol static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) 47924f779daSRuslan Bilovol { 48024f779daSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 48124f779daSRuslan Bilovol 48224f779daSRuslan Bilovol if (!prm->fb_ep_enabled) 48324f779daSRuslan Bilovol return; 48424f779daSRuslan Bilovol 48524f779daSRuslan Bilovol if (prm->req_fback) { 48675432ba5SJerome Brunet if (usb_ep_dequeue(ep, prm->req_fback)) { 48724f779daSRuslan Bilovol kfree(prm->req_fback->buf); 48824f779daSRuslan Bilovol usb_ep_free_request(ep, prm->req_fback); 48975432ba5SJerome Brunet } 49024f779daSRuslan Bilovol prm->req_fback = NULL; 49124f779daSRuslan Bilovol } 49224f779daSRuslan Bilovol 493068fdad2SJerome Brunet prm->fb_ep_enabled = false; 494068fdad2SJerome Brunet 49524f779daSRuslan Bilovol if (usb_ep_disable(ep)) 49624f779daSRuslan Bilovol dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 49724f779daSRuslan Bilovol } 498eb9fecb9SRuslan Bilovol 499*c565ad07SJulian Scheel int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) 500*c565ad07SJulian Scheel { 501*c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 502*c565ad07SJulian Scheel int i; 503*c565ad07SJulian Scheel 504*c565ad07SJulian Scheel dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 505*c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 506*c565ad07SJulian Scheel if (params->c_srates[i] == srate) { 507*c565ad07SJulian Scheel params->c_srate = srate; 508*c565ad07SJulian Scheel return 0; 509*c565ad07SJulian Scheel } 510*c565ad07SJulian Scheel if (params->c_srates[i] == 0) 511*c565ad07SJulian Scheel break; 512*c565ad07SJulian Scheel } 513*c565ad07SJulian Scheel 514*c565ad07SJulian Scheel return -EINVAL; 515*c565ad07SJulian Scheel } 516*c565ad07SJulian Scheel EXPORT_SYMBOL_GPL(u_audio_set_capture_srate); 517*c565ad07SJulian Scheel 518*c565ad07SJulian Scheel int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) 519*c565ad07SJulian Scheel { 520*c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 521*c565ad07SJulian Scheel int i; 522*c565ad07SJulian Scheel 523*c565ad07SJulian Scheel dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 524*c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 525*c565ad07SJulian Scheel if (params->p_srates[i] == srate) { 526*c565ad07SJulian Scheel params->p_srate = srate; 527*c565ad07SJulian Scheel return 0; 528*c565ad07SJulian Scheel } 529*c565ad07SJulian Scheel if (params->p_srates[i] == 0) 530*c565ad07SJulian Scheel break; 531*c565ad07SJulian Scheel } 532*c565ad07SJulian Scheel 533*c565ad07SJulian Scheel return -EINVAL; 534*c565ad07SJulian Scheel } 535*c565ad07SJulian Scheel EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); 536*c565ad07SJulian Scheel 537eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev) 538eb9fecb9SRuslan Bilovol { 539eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 540eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 541eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 54224f779daSRuslan Bilovol struct usb_request *req, *req_fback; 54324f779daSRuslan Bilovol struct usb_ep *ep, *ep_fback; 544eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 545eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 546eb9fecb9SRuslan Bilovol int req_len, i; 547eb9fecb9SRuslan Bilovol 548*c565ad07SJulian Scheel dev_dbg(dev, "start capture with rate %d\n", params->c_srate); 549eb9fecb9SRuslan Bilovol ep = audio_dev->out_ep; 550eb9fecb9SRuslan Bilovol prm = &uac->c_prm; 551eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 552904967c6SJohn Keeping req_len = ep->maxpacket; 553eb9fecb9SRuslan Bilovol 554eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 555eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 556eb9fecb9SRuslan Bilovol 557eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 55829865117SJerome Brunet if (!prm->reqs[i]) { 559eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 560eb9fecb9SRuslan Bilovol if (req == NULL) 561eb9fecb9SRuslan Bilovol return -ENOMEM; 562eb9fecb9SRuslan Bilovol 56329865117SJerome Brunet prm->reqs[i] = req; 564eb9fecb9SRuslan Bilovol 565eb9fecb9SRuslan Bilovol req->zero = 0; 56629865117SJerome Brunet req->context = prm; 567eb9fecb9SRuslan Bilovol req->length = req_len; 568eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 569904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 570eb9fecb9SRuslan Bilovol } 571eb9fecb9SRuslan Bilovol 57229865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 573eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 574eb9fecb9SRuslan Bilovol } 575eb9fecb9SRuslan Bilovol 57624f779daSRuslan Bilovol ep_fback = audio_dev->in_ep_fback; 57724f779daSRuslan Bilovol if (!ep_fback) 57824f779daSRuslan Bilovol return 0; 57924f779daSRuslan Bilovol 58024f779daSRuslan Bilovol /* Setup feedback endpoint */ 58124f779daSRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep_fback); 58224f779daSRuslan Bilovol prm->fb_ep_enabled = true; 58324f779daSRuslan Bilovol usb_ep_enable(ep_fback); 58424f779daSRuslan Bilovol req_len = ep_fback->maxpacket; 58524f779daSRuslan Bilovol 58624f779daSRuslan Bilovol req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); 58724f779daSRuslan Bilovol if (req_fback == NULL) 58824f779daSRuslan Bilovol return -ENOMEM; 58924f779daSRuslan Bilovol 59024f779daSRuslan Bilovol prm->req_fback = req_fback; 59124f779daSRuslan Bilovol req_fback->zero = 0; 59224f779daSRuslan Bilovol req_fback->context = prm; 59324f779daSRuslan Bilovol req_fback->length = req_len; 59424f779daSRuslan Bilovol req_fback->complete = u_audio_iso_fback_complete; 59524f779daSRuslan Bilovol 59624f779daSRuslan Bilovol req_fback->buf = kzalloc(req_len, GFP_ATOMIC); 59724f779daSRuslan Bilovol if (!req_fback->buf) 59824f779daSRuslan Bilovol return -ENOMEM; 59924f779daSRuslan Bilovol 60024f779daSRuslan Bilovol /* 60124f779daSRuslan Bilovol * Configure the feedback endpoint's reported frequency. 60224f779daSRuslan Bilovol * Always start with original frequency since its deviation can't 60324f779daSRuslan Bilovol * be meauserd at start of playback 60424f779daSRuslan Bilovol */ 605e89bb428SRuslan Bilovol prm->pitch = 1000000; 606f5dfd98aSPavel Hofman u_audio_set_fback_frequency(audio_dev->gadget->speed, ep, 607e89bb428SRuslan Bilovol params->c_srate, prm->pitch, 608e89bb428SRuslan Bilovol req_fback->buf); 60924f779daSRuslan Bilovol 61024f779daSRuslan Bilovol if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) 61124f779daSRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 61224f779daSRuslan Bilovol 613eb9fecb9SRuslan Bilovol return 0; 614eb9fecb9SRuslan Bilovol } 615eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture); 616eb9fecb9SRuslan Bilovol 617eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev) 618eb9fecb9SRuslan Bilovol { 619eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 620eb9fecb9SRuslan Bilovol 62124f779daSRuslan Bilovol if (audio_dev->in_ep_fback) 62224f779daSRuslan Bilovol free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 623eb9fecb9SRuslan Bilovol free_ep(&uac->c_prm, audio_dev->out_ep); 624eb9fecb9SRuslan Bilovol } 625eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture); 626eb9fecb9SRuslan Bilovol 627eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev) 628eb9fecb9SRuslan Bilovol { 629eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 630eb9fecb9SRuslan Bilovol struct usb_gadget *gadget = audio_dev->gadget; 631eb9fecb9SRuslan Bilovol struct device *dev = &gadget->dev; 632eb9fecb9SRuslan Bilovol struct usb_request *req; 633eb9fecb9SRuslan Bilovol struct usb_ep *ep; 634eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm; 635eb9fecb9SRuslan Bilovol struct uac_params *params = &audio_dev->params; 6366b02af34SJohn Keeping unsigned int factor; 637eb9fecb9SRuslan Bilovol const struct usb_endpoint_descriptor *ep_desc; 638eb9fecb9SRuslan Bilovol int req_len, i; 639f2f69bf6SJohn Keeping unsigned int p_pktsize; 640eb9fecb9SRuslan Bilovol 641*c565ad07SJulian Scheel dev_dbg(dev, "start playback with rate %d\n", params->p_srate); 642eb9fecb9SRuslan Bilovol ep = audio_dev->in_ep; 643eb9fecb9SRuslan Bilovol prm = &uac->p_prm; 644eb9fecb9SRuslan Bilovol config_ep_by_speed(gadget, &audio_dev->func, ep); 645eb9fecb9SRuslan Bilovol 646eb9fecb9SRuslan Bilovol ep_desc = ep->desc; 6476fec018aSPavel Hofman /* 6486fec018aSPavel Hofman * Always start with original frequency 6496fec018aSPavel Hofman */ 6506fec018aSPavel Hofman prm->pitch = 1000000; 651eb9fecb9SRuslan Bilovol 652eb9fecb9SRuslan Bilovol /* pre-calculate the playback endpoint's interval */ 653eb9fecb9SRuslan Bilovol if (gadget->speed == USB_SPEED_FULL) 654eb9fecb9SRuslan Bilovol factor = 1000; 655eb9fecb9SRuslan Bilovol else 656eb9fecb9SRuslan Bilovol factor = 8000; 657eb9fecb9SRuslan Bilovol 658eb9fecb9SRuslan Bilovol /* pre-compute some values for iso_complete() */ 659eb9fecb9SRuslan Bilovol uac->p_framesize = params->p_ssize * 660eb9fecb9SRuslan Bilovol num_channels(params->p_chmask); 661f2f69bf6SJohn Keeping uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); 6626fec018aSPavel Hofman p_pktsize = min_t(unsigned int, 6636b02af34SJohn Keeping uac->p_framesize * 664f2f69bf6SJohn Keeping (params->p_srate / uac->p_interval), 665904967c6SJohn Keeping ep->maxpacket); 666eb9fecb9SRuslan Bilovol 6676fec018aSPavel Hofman req_len = p_pktsize; 6686fec018aSPavel Hofman uac->p_residue_mil = 0; 669eb9fecb9SRuslan Bilovol 670eb9fecb9SRuslan Bilovol prm->ep_enabled = true; 671eb9fecb9SRuslan Bilovol usb_ep_enable(ep); 672eb9fecb9SRuslan Bilovol 673eb9fecb9SRuslan Bilovol for (i = 0; i < params->req_number; i++) { 67429865117SJerome Brunet if (!prm->reqs[i]) { 675eb9fecb9SRuslan Bilovol req = usb_ep_alloc_request(ep, GFP_ATOMIC); 676eb9fecb9SRuslan Bilovol if (req == NULL) 677eb9fecb9SRuslan Bilovol return -ENOMEM; 678eb9fecb9SRuslan Bilovol 67929865117SJerome Brunet prm->reqs[i] = req; 680eb9fecb9SRuslan Bilovol 681eb9fecb9SRuslan Bilovol req->zero = 0; 68229865117SJerome Brunet req->context = prm; 683eb9fecb9SRuslan Bilovol req->length = req_len; 684eb9fecb9SRuslan Bilovol req->complete = u_audio_iso_complete; 685904967c6SJohn Keeping req->buf = prm->rbuf + i * ep->maxpacket; 686eb9fecb9SRuslan Bilovol } 687eb9fecb9SRuslan Bilovol 68829865117SJerome Brunet if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 689eb9fecb9SRuslan Bilovol dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 690eb9fecb9SRuslan Bilovol } 691eb9fecb9SRuslan Bilovol 692eb9fecb9SRuslan Bilovol return 0; 693eb9fecb9SRuslan Bilovol } 694eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback); 695eb9fecb9SRuslan Bilovol 696eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev) 697eb9fecb9SRuslan Bilovol { 698eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 699eb9fecb9SRuslan Bilovol 700eb9fecb9SRuslan Bilovol free_ep(&uac->p_prm, audio_dev->in_ep); 701eb9fecb9SRuslan Bilovol } 702eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback); 703eb9fecb9SRuslan Bilovol 70402de698cSRuslan Bilovol int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) 70502de698cSRuslan Bilovol { 70602de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 70702de698cSRuslan Bilovol struct uac_rtd_params *prm; 70802de698cSRuslan Bilovol unsigned long flags; 70902de698cSRuslan Bilovol 71002de698cSRuslan Bilovol if (playback) 71102de698cSRuslan Bilovol prm = &uac->p_prm; 71202de698cSRuslan Bilovol else 71302de698cSRuslan Bilovol prm = &uac->c_prm; 71402de698cSRuslan Bilovol 71502de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 71602de698cSRuslan Bilovol *val = prm->volume; 71702de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 71802de698cSRuslan Bilovol 71902de698cSRuslan Bilovol return 0; 72002de698cSRuslan Bilovol } 72102de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_volume); 72202de698cSRuslan Bilovol 72302de698cSRuslan Bilovol int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) 72402de698cSRuslan Bilovol { 72502de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 72602de698cSRuslan Bilovol struct uac_rtd_params *prm; 72702de698cSRuslan Bilovol unsigned long flags; 72802de698cSRuslan Bilovol int change = 0; 72902de698cSRuslan Bilovol 73002de698cSRuslan Bilovol if (playback) 73102de698cSRuslan Bilovol prm = &uac->p_prm; 73202de698cSRuslan Bilovol else 73302de698cSRuslan Bilovol prm = &uac->c_prm; 73402de698cSRuslan Bilovol 73502de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 73602de698cSRuslan Bilovol val = clamp(val, prm->volume_min, prm->volume_max); 73702de698cSRuslan Bilovol if (prm->volume != val) { 73802de698cSRuslan Bilovol prm->volume = val; 73902de698cSRuslan Bilovol change = 1; 74002de698cSRuslan Bilovol } 74102de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 74202de698cSRuslan Bilovol 74302de698cSRuslan Bilovol if (change) 74402de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 74502de698cSRuslan Bilovol &prm->snd_kctl_volume->id); 74602de698cSRuslan Bilovol 74702de698cSRuslan Bilovol return 0; 74802de698cSRuslan Bilovol } 74902de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_volume); 75002de698cSRuslan Bilovol 75102de698cSRuslan Bilovol int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) 75202de698cSRuslan Bilovol { 75302de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 75402de698cSRuslan Bilovol struct uac_rtd_params *prm; 75502de698cSRuslan Bilovol unsigned long flags; 75602de698cSRuslan Bilovol 75702de698cSRuslan Bilovol if (playback) 75802de698cSRuslan Bilovol prm = &uac->p_prm; 75902de698cSRuslan Bilovol else 76002de698cSRuslan Bilovol prm = &uac->c_prm; 76102de698cSRuslan Bilovol 76202de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 76302de698cSRuslan Bilovol *val = prm->mute; 76402de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 76502de698cSRuslan Bilovol 76602de698cSRuslan Bilovol return 0; 76702de698cSRuslan Bilovol } 76802de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_get_mute); 76902de698cSRuslan Bilovol 77002de698cSRuslan Bilovol int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) 77102de698cSRuslan Bilovol { 77202de698cSRuslan Bilovol struct snd_uac_chip *uac = audio_dev->uac; 77302de698cSRuslan Bilovol struct uac_rtd_params *prm; 77402de698cSRuslan Bilovol unsigned long flags; 77502de698cSRuslan Bilovol int change = 0; 77602de698cSRuslan Bilovol int mute; 77702de698cSRuslan Bilovol 77802de698cSRuslan Bilovol if (playback) 77902de698cSRuslan Bilovol prm = &uac->p_prm; 78002de698cSRuslan Bilovol else 78102de698cSRuslan Bilovol prm = &uac->c_prm; 78202de698cSRuslan Bilovol 78302de698cSRuslan Bilovol mute = val ? 1 : 0; 78402de698cSRuslan Bilovol 78502de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 78602de698cSRuslan Bilovol if (prm->mute != mute) { 78702de698cSRuslan Bilovol prm->mute = mute; 78802de698cSRuslan Bilovol change = 1; 78902de698cSRuslan Bilovol } 79002de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 79102de698cSRuslan Bilovol 79202de698cSRuslan Bilovol if (change) 79302de698cSRuslan Bilovol snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 79402de698cSRuslan Bilovol &prm->snd_kctl_mute->id); 79502de698cSRuslan Bilovol 79602de698cSRuslan Bilovol return 0; 79702de698cSRuslan Bilovol } 79802de698cSRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_set_mute); 79902de698cSRuslan Bilovol 80002de698cSRuslan Bilovol 801e89bb428SRuslan Bilovol static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, 802e89bb428SRuslan Bilovol struct snd_ctl_elem_info *uinfo) 803e89bb428SRuslan Bilovol { 804e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 805e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 806e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 807e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 808e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 809e89bb428SRuslan Bilovol 810e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 811e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 812e89bb428SRuslan Bilovol 813e89bb428SRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 814e89bb428SRuslan Bilovol uinfo->count = 1; 815e89bb428SRuslan Bilovol uinfo->value.integer.min = pitch_min; 816e89bb428SRuslan Bilovol uinfo->value.integer.max = pitch_max; 817e89bb428SRuslan Bilovol uinfo->value.integer.step = 1; 818e89bb428SRuslan Bilovol return 0; 819e89bb428SRuslan Bilovol } 820e89bb428SRuslan Bilovol 821e89bb428SRuslan Bilovol static int u_audio_pitch_get(struct snd_kcontrol *kcontrol, 822e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 823e89bb428SRuslan Bilovol { 824e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 825e89bb428SRuslan Bilovol 826e89bb428SRuslan Bilovol ucontrol->value.integer.value[0] = prm->pitch; 827e89bb428SRuslan Bilovol 828e89bb428SRuslan Bilovol return 0; 829e89bb428SRuslan Bilovol } 830e89bb428SRuslan Bilovol 831e89bb428SRuslan Bilovol static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, 832e89bb428SRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 833e89bb428SRuslan Bilovol { 834e89bb428SRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 835e89bb428SRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 836e89bb428SRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 837e89bb428SRuslan Bilovol struct uac_params *params = &audio_dev->params; 838e89bb428SRuslan Bilovol unsigned int val; 839e89bb428SRuslan Bilovol unsigned int pitch_min, pitch_max; 840e89bb428SRuslan Bilovol int change = 0; 841e89bb428SRuslan Bilovol 842e89bb428SRuslan Bilovol pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 843e89bb428SRuslan Bilovol pitch_max = (1000 + params->fb_max) * 1000; 844e89bb428SRuslan Bilovol 845e89bb428SRuslan Bilovol val = ucontrol->value.integer.value[0]; 846e89bb428SRuslan Bilovol 847e89bb428SRuslan Bilovol if (val < pitch_min) 848e89bb428SRuslan Bilovol val = pitch_min; 849e89bb428SRuslan Bilovol if (val > pitch_max) 850e89bb428SRuslan Bilovol val = pitch_max; 851e89bb428SRuslan Bilovol 852e89bb428SRuslan Bilovol if (prm->pitch != val) { 853e89bb428SRuslan Bilovol prm->pitch = val; 854e89bb428SRuslan Bilovol change = 1; 855e89bb428SRuslan Bilovol } 856e89bb428SRuslan Bilovol 857e89bb428SRuslan Bilovol return change; 858e89bb428SRuslan Bilovol } 859e89bb428SRuslan Bilovol 86002de698cSRuslan Bilovol static int u_audio_mute_info(struct snd_kcontrol *kcontrol, 86102de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 862e89bb428SRuslan Bilovol { 86302de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 86402de698cSRuslan Bilovol uinfo->count = 1; 86502de698cSRuslan Bilovol uinfo->value.integer.min = 0; 86602de698cSRuslan Bilovol uinfo->value.integer.max = 1; 86702de698cSRuslan Bilovol uinfo->value.integer.step = 1; 86802de698cSRuslan Bilovol 86902de698cSRuslan Bilovol return 0; 87002de698cSRuslan Bilovol } 87102de698cSRuslan Bilovol 87202de698cSRuslan Bilovol static int u_audio_mute_get(struct snd_kcontrol *kcontrol, 87302de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 87402de698cSRuslan Bilovol { 87502de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 87602de698cSRuslan Bilovol unsigned long flags; 87702de698cSRuslan Bilovol 87802de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 87902de698cSRuslan Bilovol ucontrol->value.integer.value[0] = !prm->mute; 88002de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 88102de698cSRuslan Bilovol 88202de698cSRuslan Bilovol return 0; 88302de698cSRuslan Bilovol } 88402de698cSRuslan Bilovol 88502de698cSRuslan Bilovol static int u_audio_mute_put(struct snd_kcontrol *kcontrol, 88602de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 88702de698cSRuslan Bilovol { 88802de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 88902de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 89002de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 89102de698cSRuslan Bilovol unsigned int val; 89202de698cSRuslan Bilovol unsigned long flags; 89302de698cSRuslan Bilovol int change = 0; 89402de698cSRuslan Bilovol 89502de698cSRuslan Bilovol val = !ucontrol->value.integer.value[0]; 89602de698cSRuslan Bilovol 89702de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 89802de698cSRuslan Bilovol if (val != prm->mute) { 89902de698cSRuslan Bilovol prm->mute = val; 90002de698cSRuslan Bilovol change = 1; 90102de698cSRuslan Bilovol } 90202de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 90302de698cSRuslan Bilovol 90402de698cSRuslan Bilovol if (change && audio_dev->notify) 90502de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); 90602de698cSRuslan Bilovol 90702de698cSRuslan Bilovol return change; 90802de698cSRuslan Bilovol } 90902de698cSRuslan Bilovol 91002de698cSRuslan Bilovol /* 91102de698cSRuslan Bilovol * TLV callback for mixer volume controls 91202de698cSRuslan Bilovol */ 91302de698cSRuslan Bilovol static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, 91402de698cSRuslan Bilovol unsigned int size, unsigned int __user *_tlv) 91502de698cSRuslan Bilovol { 91602de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 91702de698cSRuslan Bilovol DECLARE_TLV_DB_MINMAX(scale, 0, 0); 91802de698cSRuslan Bilovol 91902de698cSRuslan Bilovol if (size < sizeof(scale)) 92002de698cSRuslan Bilovol return -ENOMEM; 92102de698cSRuslan Bilovol 92202de698cSRuslan Bilovol /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ 92302de698cSRuslan Bilovol scale[2] = (prm->volume_min * 100) / 256; 92402de698cSRuslan Bilovol scale[3] = (prm->volume_max * 100) / 256; 92502de698cSRuslan Bilovol if (copy_to_user(_tlv, scale, sizeof(scale))) 92602de698cSRuslan Bilovol return -EFAULT; 92702de698cSRuslan Bilovol 92802de698cSRuslan Bilovol return 0; 92902de698cSRuslan Bilovol } 93002de698cSRuslan Bilovol 93102de698cSRuslan Bilovol static int u_audio_volume_info(struct snd_kcontrol *kcontrol, 93202de698cSRuslan Bilovol struct snd_ctl_elem_info *uinfo) 93302de698cSRuslan Bilovol { 93402de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 93502de698cSRuslan Bilovol 93602de698cSRuslan Bilovol uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 93702de698cSRuslan Bilovol uinfo->count = 1; 93802de698cSRuslan Bilovol uinfo->value.integer.min = 0; 93902de698cSRuslan Bilovol uinfo->value.integer.max = 94002de698cSRuslan Bilovol (prm->volume_max - prm->volume_min + prm->volume_res - 1) 94102de698cSRuslan Bilovol / prm->volume_res; 94202de698cSRuslan Bilovol uinfo->value.integer.step = 1; 94302de698cSRuslan Bilovol 94402de698cSRuslan Bilovol return 0; 94502de698cSRuslan Bilovol } 94602de698cSRuslan Bilovol 94702de698cSRuslan Bilovol static int u_audio_volume_get(struct snd_kcontrol *kcontrol, 94802de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 94902de698cSRuslan Bilovol { 95002de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 95102de698cSRuslan Bilovol unsigned long flags; 95202de698cSRuslan Bilovol 95302de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 95402de698cSRuslan Bilovol ucontrol->value.integer.value[0] = 95502de698cSRuslan Bilovol (prm->volume - prm->volume_min) / prm->volume_res; 95602de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 95702de698cSRuslan Bilovol 95802de698cSRuslan Bilovol return 0; 95902de698cSRuslan Bilovol } 96002de698cSRuslan Bilovol 96102de698cSRuslan Bilovol static int u_audio_volume_put(struct snd_kcontrol *kcontrol, 96202de698cSRuslan Bilovol struct snd_ctl_elem_value *ucontrol) 96302de698cSRuslan Bilovol { 96402de698cSRuslan Bilovol struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 96502de698cSRuslan Bilovol struct snd_uac_chip *uac = prm->uac; 96602de698cSRuslan Bilovol struct g_audio *audio_dev = uac->audio_dev; 96702de698cSRuslan Bilovol unsigned int val; 96802de698cSRuslan Bilovol s16 volume; 96902de698cSRuslan Bilovol unsigned long flags; 97002de698cSRuslan Bilovol int change = 0; 97102de698cSRuslan Bilovol 97202de698cSRuslan Bilovol val = ucontrol->value.integer.value[0]; 97302de698cSRuslan Bilovol 97402de698cSRuslan Bilovol spin_lock_irqsave(&prm->lock, flags); 97502de698cSRuslan Bilovol volume = (val * prm->volume_res) + prm->volume_min; 97602de698cSRuslan Bilovol volume = clamp(volume, prm->volume_min, prm->volume_max); 97702de698cSRuslan Bilovol if (volume != prm->volume) { 97802de698cSRuslan Bilovol prm->volume = volume; 97902de698cSRuslan Bilovol change = 1; 98002de698cSRuslan Bilovol } 98102de698cSRuslan Bilovol spin_unlock_irqrestore(&prm->lock, flags); 98202de698cSRuslan Bilovol 98302de698cSRuslan Bilovol if (change && audio_dev->notify) 98402de698cSRuslan Bilovol audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); 98502de698cSRuslan Bilovol 98602de698cSRuslan Bilovol return change; 98702de698cSRuslan Bilovol } 98802de698cSRuslan Bilovol 989*c565ad07SJulian Scheel static int get_max_srate(const int *srates) 990*c565ad07SJulian Scheel { 991*c565ad07SJulian Scheel int i, max_srate = 0; 992*c565ad07SJulian Scheel 993*c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 994*c565ad07SJulian Scheel if (srates[i] == 0) 995*c565ad07SJulian Scheel break; 996*c565ad07SJulian Scheel if (srates[i] > max_srate) 997*c565ad07SJulian Scheel max_srate = srates[i]; 998*c565ad07SJulian Scheel } 999*c565ad07SJulian Scheel return max_srate; 1000*c565ad07SJulian Scheel } 1001*c565ad07SJulian Scheel 1002*c565ad07SJulian Scheel static int get_min_srate(const int *srates) 1003*c565ad07SJulian Scheel { 1004*c565ad07SJulian Scheel int i, min_srate = INT_MAX; 1005*c565ad07SJulian Scheel 1006*c565ad07SJulian Scheel for (i = 0; i < UAC_MAX_RATES; i++) { 1007*c565ad07SJulian Scheel if (srates[i] == 0) 1008*c565ad07SJulian Scheel break; 1009*c565ad07SJulian Scheel if (srates[i] < min_srate) 1010*c565ad07SJulian Scheel min_srate = srates[i]; 1011*c565ad07SJulian Scheel } 1012*c565ad07SJulian Scheel return min_srate; 1013*c565ad07SJulian Scheel } 1014*c565ad07SJulian Scheel 1015*c565ad07SJulian Scheel static int u_audio_rate_info(struct snd_kcontrol *kcontrol, 1016*c565ad07SJulian Scheel struct snd_ctl_elem_info *uinfo) 1017*c565ad07SJulian Scheel { 1018*c565ad07SJulian Scheel const int *srates; 1019*c565ad07SJulian Scheel struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 1020*c565ad07SJulian Scheel struct snd_uac_chip *uac = prm->uac; 1021*c565ad07SJulian Scheel struct g_audio *audio_dev = uac->audio_dev; 1022*c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 1023*c565ad07SJulian Scheel 1024*c565ad07SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1025*c565ad07SJulian Scheel uinfo->count = 1; 1026*c565ad07SJulian Scheel 1027*c565ad07SJulian Scheel if (prm == &uac->c_prm) 1028*c565ad07SJulian Scheel srates = params->c_srates; 1029*c565ad07SJulian Scheel else 1030*c565ad07SJulian Scheel srates = params->p_srates; 1031*c565ad07SJulian Scheel uinfo->value.integer.min = get_min_srate(srates); 1032*c565ad07SJulian Scheel uinfo->value.integer.max = get_max_srate(srates); 1033*c565ad07SJulian Scheel return 0; 1034*c565ad07SJulian Scheel } 1035*c565ad07SJulian Scheel 1036*c565ad07SJulian Scheel static int u_audio_rate_get(struct snd_kcontrol *kcontrol, 1037*c565ad07SJulian Scheel struct snd_ctl_elem_value *ucontrol) 1038*c565ad07SJulian Scheel { 1039*c565ad07SJulian Scheel struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 1040*c565ad07SJulian Scheel struct snd_uac_chip *uac = prm->uac; 1041*c565ad07SJulian Scheel struct g_audio *audio_dev = uac->audio_dev; 1042*c565ad07SJulian Scheel struct uac_params *params = &audio_dev->params; 1043*c565ad07SJulian Scheel 1044*c565ad07SJulian Scheel if (prm == &uac->c_prm) 1045*c565ad07SJulian Scheel ucontrol->value.integer.value[0] = params->c_srate; 1046*c565ad07SJulian Scheel else 1047*c565ad07SJulian Scheel ucontrol->value.integer.value[0] = params->p_srate; 1048*c565ad07SJulian Scheel 1049*c565ad07SJulian Scheel return 0; 1050*c565ad07SJulian Scheel } 105102de698cSRuslan Bilovol 105202de698cSRuslan Bilovol static struct snd_kcontrol_new u_audio_controls[] = { 105302de698cSRuslan Bilovol [UAC_FBACK_CTRL] { 1054e89bb428SRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_PCM, 1055e89bb428SRuslan Bilovol .name = "Capture Pitch 1000000", 1056e89bb428SRuslan Bilovol .info = u_audio_pitch_info, 1057e89bb428SRuslan Bilovol .get = u_audio_pitch_get, 1058e89bb428SRuslan Bilovol .put = u_audio_pitch_put, 1059e89bb428SRuslan Bilovol }, 10606fec018aSPavel Hofman [UAC_P_PITCH_CTRL] { 10616fec018aSPavel Hofman .iface = SNDRV_CTL_ELEM_IFACE_PCM, 10626fec018aSPavel Hofman .name = "Playback Pitch 1000000", 10636fec018aSPavel Hofman .info = u_audio_pitch_info, 10646fec018aSPavel Hofman .get = u_audio_pitch_get, 10656fec018aSPavel Hofman .put = u_audio_pitch_put, 10666fec018aSPavel Hofman }, 106702de698cSRuslan Bilovol [UAC_MUTE_CTRL] { 106802de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 106902de698cSRuslan Bilovol .name = "", /* will be filled later */ 107002de698cSRuslan Bilovol .info = u_audio_mute_info, 107102de698cSRuslan Bilovol .get = u_audio_mute_get, 107202de698cSRuslan Bilovol .put = u_audio_mute_put, 107302de698cSRuslan Bilovol }, 107402de698cSRuslan Bilovol [UAC_VOLUME_CTRL] { 107502de698cSRuslan Bilovol .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 107602de698cSRuslan Bilovol .name = "", /* will be filled later */ 107702de698cSRuslan Bilovol .info = u_audio_volume_info, 107802de698cSRuslan Bilovol .get = u_audio_volume_get, 107902de698cSRuslan Bilovol .put = u_audio_volume_put, 108002de698cSRuslan Bilovol }, 1081*c565ad07SJulian Scheel [UAC_RATE_CTRL] { 1082*c565ad07SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_PCM, 1083*c565ad07SJulian Scheel .name = "", /* will be filled later */ 1084*c565ad07SJulian Scheel .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 1085*c565ad07SJulian Scheel .info = u_audio_rate_info, 1086*c565ad07SJulian Scheel .get = u_audio_rate_get, 1087*c565ad07SJulian Scheel }, 1088e89bb428SRuslan Bilovol }; 1089e89bb428SRuslan Bilovol 1090eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, 1091eb9fecb9SRuslan Bilovol const char *card_name) 1092eb9fecb9SRuslan Bilovol { 1093eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 1094eb9fecb9SRuslan Bilovol struct snd_card *card; 1095eb9fecb9SRuslan Bilovol struct snd_pcm *pcm; 1096e89bb428SRuslan Bilovol struct snd_kcontrol *kctl; 1097eb9fecb9SRuslan Bilovol struct uac_params *params; 1098eb9fecb9SRuslan Bilovol int p_chmask, c_chmask; 109902de698cSRuslan Bilovol int i, err; 1100eb9fecb9SRuslan Bilovol 1101eb9fecb9SRuslan Bilovol if (!g_audio) 1102eb9fecb9SRuslan Bilovol return -EINVAL; 1103eb9fecb9SRuslan Bilovol 1104eb9fecb9SRuslan Bilovol uac = kzalloc(sizeof(*uac), GFP_KERNEL); 1105eb9fecb9SRuslan Bilovol if (!uac) 1106eb9fecb9SRuslan Bilovol return -ENOMEM; 1107eb9fecb9SRuslan Bilovol g_audio->uac = uac; 1108eb9fecb9SRuslan Bilovol uac->audio_dev = g_audio; 1109eb9fecb9SRuslan Bilovol 1110eb9fecb9SRuslan Bilovol params = &g_audio->params; 1111eb9fecb9SRuslan Bilovol p_chmask = params->p_chmask; 1112eb9fecb9SRuslan Bilovol c_chmask = params->c_chmask; 1113eb9fecb9SRuslan Bilovol 1114eb9fecb9SRuslan Bilovol if (c_chmask) { 1115eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->c_prm; 1116eb9fecb9SRuslan Bilovol 111702de698cSRuslan Bilovol spin_lock_init(&prm->lock); 1118eb9fecb9SRuslan Bilovol uac->c_prm.uac = uac; 1119eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->out_ep_maxpsize; 1120eb9fecb9SRuslan Bilovol 112129865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 112229865117SJerome Brunet sizeof(struct usb_request *), 1123eb9fecb9SRuslan Bilovol GFP_KERNEL); 112429865117SJerome Brunet if (!prm->reqs) { 1125eb9fecb9SRuslan Bilovol err = -ENOMEM; 1126eb9fecb9SRuslan Bilovol goto fail; 1127eb9fecb9SRuslan Bilovol } 1128eb9fecb9SRuslan Bilovol 1129eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 1130eb9fecb9SRuslan Bilovol GFP_KERNEL); 1131eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 1132eb9fecb9SRuslan Bilovol prm->max_psize = 0; 1133eb9fecb9SRuslan Bilovol err = -ENOMEM; 1134eb9fecb9SRuslan Bilovol goto fail; 1135eb9fecb9SRuslan Bilovol } 1136eb9fecb9SRuslan Bilovol } 1137eb9fecb9SRuslan Bilovol 1138eb9fecb9SRuslan Bilovol if (p_chmask) { 1139eb9fecb9SRuslan Bilovol struct uac_rtd_params *prm = &uac->p_prm; 1140eb9fecb9SRuslan Bilovol 114102de698cSRuslan Bilovol spin_lock_init(&prm->lock); 1142eb9fecb9SRuslan Bilovol uac->p_prm.uac = uac; 1143eb9fecb9SRuslan Bilovol prm->max_psize = g_audio->in_ep_maxpsize; 1144eb9fecb9SRuslan Bilovol 114529865117SJerome Brunet prm->reqs = kcalloc(params->req_number, 114629865117SJerome Brunet sizeof(struct usb_request *), 1147eb9fecb9SRuslan Bilovol GFP_KERNEL); 114829865117SJerome Brunet if (!prm->reqs) { 1149eb9fecb9SRuslan Bilovol err = -ENOMEM; 1150eb9fecb9SRuslan Bilovol goto fail; 1151eb9fecb9SRuslan Bilovol } 1152eb9fecb9SRuslan Bilovol 1153eb9fecb9SRuslan Bilovol prm->rbuf = kcalloc(params->req_number, prm->max_psize, 1154eb9fecb9SRuslan Bilovol GFP_KERNEL); 1155eb9fecb9SRuslan Bilovol if (!prm->rbuf) { 1156eb9fecb9SRuslan Bilovol prm->max_psize = 0; 1157eb9fecb9SRuslan Bilovol err = -ENOMEM; 1158eb9fecb9SRuslan Bilovol goto fail; 1159eb9fecb9SRuslan Bilovol } 1160eb9fecb9SRuslan Bilovol } 1161eb9fecb9SRuslan Bilovol 1162eb9fecb9SRuslan Bilovol /* Choose any slot, with no id */ 1163eb9fecb9SRuslan Bilovol err = snd_card_new(&g_audio->gadget->dev, 1164eb9fecb9SRuslan Bilovol -1, NULL, THIS_MODULE, 0, &card); 1165eb9fecb9SRuslan Bilovol if (err < 0) 1166eb9fecb9SRuslan Bilovol goto fail; 1167eb9fecb9SRuslan Bilovol 1168eb9fecb9SRuslan Bilovol uac->card = card; 1169eb9fecb9SRuslan Bilovol 1170eb9fecb9SRuslan Bilovol /* 1171eb9fecb9SRuslan Bilovol * Create first PCM device 1172eb9fecb9SRuslan Bilovol * Create a substream only for non-zero channel streams 1173eb9fecb9SRuslan Bilovol */ 1174eb9fecb9SRuslan Bilovol err = snd_pcm_new(uac->card, pcm_name, 0, 1175eb9fecb9SRuslan Bilovol p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); 1176eb9fecb9SRuslan Bilovol if (err < 0) 1177eb9fecb9SRuslan Bilovol goto snd_fail; 1178eb9fecb9SRuslan Bilovol 1179d23922fcSRuslan Bilovol strscpy(pcm->name, pcm_name, sizeof(pcm->name)); 1180eb9fecb9SRuslan Bilovol pcm->private_data = uac; 1181eb9fecb9SRuslan Bilovol uac->pcm = pcm; 1182eb9fecb9SRuslan Bilovol 1183eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); 1184eb9fecb9SRuslan Bilovol snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); 1185eb9fecb9SRuslan Bilovol 118602de698cSRuslan Bilovol /* 118702de698cSRuslan Bilovol * Create mixer and controls 118802de698cSRuslan Bilovol * Create only if it's required on USB side 118902de698cSRuslan Bilovol */ 119002de698cSRuslan Bilovol if ((c_chmask && g_audio->in_ep_fback) 119102de698cSRuslan Bilovol || (p_chmask && params->p_fu.id) 119202de698cSRuslan Bilovol || (c_chmask && params->c_fu.id)) 1193e89bb428SRuslan Bilovol strscpy(card->mixername, card_name, sizeof(card->driver)); 1194e89bb428SRuslan Bilovol 119502de698cSRuslan Bilovol if (c_chmask && g_audio->in_ep_fback) { 119602de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], 119702de698cSRuslan Bilovol &uac->c_prm); 1198e89bb428SRuslan Bilovol if (!kctl) { 1199e89bb428SRuslan Bilovol err = -ENOMEM; 1200e89bb428SRuslan Bilovol goto snd_fail; 1201e89bb428SRuslan Bilovol } 1202e89bb428SRuslan Bilovol 1203e89bb428SRuslan Bilovol kctl->id.device = pcm->device; 1204e89bb428SRuslan Bilovol kctl->id.subdevice = 0; 1205e89bb428SRuslan Bilovol 1206e89bb428SRuslan Bilovol err = snd_ctl_add(card, kctl); 1207e89bb428SRuslan Bilovol if (err < 0) 1208e89bb428SRuslan Bilovol goto snd_fail; 1209e89bb428SRuslan Bilovol } 1210e89bb428SRuslan Bilovol 12116fec018aSPavel Hofman if (p_chmask) { 12126fec018aSPavel Hofman kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL], 12136fec018aSPavel Hofman &uac->p_prm); 12146fec018aSPavel Hofman if (!kctl) { 12156fec018aSPavel Hofman err = -ENOMEM; 12166fec018aSPavel Hofman goto snd_fail; 12176fec018aSPavel Hofman } 12186fec018aSPavel Hofman 12196fec018aSPavel Hofman kctl->id.device = pcm->device; 12206fec018aSPavel Hofman kctl->id.subdevice = 0; 12216fec018aSPavel Hofman 12226fec018aSPavel Hofman err = snd_ctl_add(card, kctl); 12236fec018aSPavel Hofman if (err < 0) 12246fec018aSPavel Hofman goto snd_fail; 12256fec018aSPavel Hofman } 12266fec018aSPavel Hofman 122702de698cSRuslan Bilovol for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { 122802de698cSRuslan Bilovol struct uac_rtd_params *prm; 122902de698cSRuslan Bilovol struct uac_fu_params *fu; 123002de698cSRuslan Bilovol char ctrl_name[24]; 123102de698cSRuslan Bilovol char *direction; 123202de698cSRuslan Bilovol 123302de698cSRuslan Bilovol if (!pcm->streams[i].substream_count) 123402de698cSRuslan Bilovol continue; 123502de698cSRuslan Bilovol 123602de698cSRuslan Bilovol if (i == SNDRV_PCM_STREAM_PLAYBACK) { 123702de698cSRuslan Bilovol prm = &uac->p_prm; 123802de698cSRuslan Bilovol fu = ¶ms->p_fu; 123902de698cSRuslan Bilovol direction = "Playback"; 124002de698cSRuslan Bilovol } else { 124102de698cSRuslan Bilovol prm = &uac->c_prm; 124202de698cSRuslan Bilovol fu = ¶ms->c_fu; 124302de698cSRuslan Bilovol direction = "Capture"; 124402de698cSRuslan Bilovol } 124502de698cSRuslan Bilovol 124602de698cSRuslan Bilovol prm->fu_id = fu->id; 124702de698cSRuslan Bilovol 124802de698cSRuslan Bilovol if (fu->mute_present) { 124902de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 125002de698cSRuslan Bilovol "PCM %s Switch", direction); 125102de698cSRuslan Bilovol 125202de698cSRuslan Bilovol u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; 125302de698cSRuslan Bilovol 125402de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], 125502de698cSRuslan Bilovol prm); 125602de698cSRuslan Bilovol if (!kctl) { 125702de698cSRuslan Bilovol err = -ENOMEM; 125802de698cSRuslan Bilovol goto snd_fail; 125902de698cSRuslan Bilovol } 126002de698cSRuslan Bilovol 126102de698cSRuslan Bilovol kctl->id.device = pcm->device; 1262601a5bc1SPavel Hofman kctl->id.subdevice = 0; 126302de698cSRuslan Bilovol 126402de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 126502de698cSRuslan Bilovol if (err < 0) 126602de698cSRuslan Bilovol goto snd_fail; 126702de698cSRuslan Bilovol prm->snd_kctl_mute = kctl; 126802de698cSRuslan Bilovol prm->mute = 0; 126902de698cSRuslan Bilovol } 127002de698cSRuslan Bilovol 127102de698cSRuslan Bilovol if (fu->volume_present) { 127202de698cSRuslan Bilovol snprintf(ctrl_name, sizeof(ctrl_name), 127302de698cSRuslan Bilovol "PCM %s Volume", direction); 127402de698cSRuslan Bilovol 127502de698cSRuslan Bilovol u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; 127602de698cSRuslan Bilovol 127702de698cSRuslan Bilovol kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], 127802de698cSRuslan Bilovol prm); 127902de698cSRuslan Bilovol if (!kctl) { 128002de698cSRuslan Bilovol err = -ENOMEM; 128102de698cSRuslan Bilovol goto snd_fail; 128202de698cSRuslan Bilovol } 128302de698cSRuslan Bilovol 128402de698cSRuslan Bilovol kctl->id.device = pcm->device; 1285601a5bc1SPavel Hofman kctl->id.subdevice = 0; 128602de698cSRuslan Bilovol 128702de698cSRuslan Bilovol 128802de698cSRuslan Bilovol kctl->tlv.c = u_audio_volume_tlv; 128902de698cSRuslan Bilovol kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | 129002de698cSRuslan Bilovol SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 129102de698cSRuslan Bilovol 129202de698cSRuslan Bilovol err = snd_ctl_add(card, kctl); 129302de698cSRuslan Bilovol if (err < 0) 129402de698cSRuslan Bilovol goto snd_fail; 129502de698cSRuslan Bilovol prm->snd_kctl_volume = kctl; 129602de698cSRuslan Bilovol prm->volume = fu->volume_max; 129702de698cSRuslan Bilovol prm->volume_max = fu->volume_max; 129802de698cSRuslan Bilovol prm->volume_min = fu->volume_min; 129902de698cSRuslan Bilovol prm->volume_res = fu->volume_res; 130002de698cSRuslan Bilovol } 1301*c565ad07SJulian Scheel 1302*c565ad07SJulian Scheel /* Add rate control */ 1303*c565ad07SJulian Scheel snprintf(ctrl_name, sizeof(ctrl_name), 1304*c565ad07SJulian Scheel "%s Rate", direction); 1305*c565ad07SJulian Scheel u_audio_controls[UAC_RATE_CTRL].name = ctrl_name; 1306*c565ad07SJulian Scheel 1307*c565ad07SJulian Scheel kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm); 1308*c565ad07SJulian Scheel if (!kctl) { 1309*c565ad07SJulian Scheel err = -ENOMEM; 1310*c565ad07SJulian Scheel goto snd_fail; 1311*c565ad07SJulian Scheel } 1312*c565ad07SJulian Scheel 1313*c565ad07SJulian Scheel kctl->id.device = pcm->device; 1314*c565ad07SJulian Scheel kctl->id.subdevice = 0; 1315*c565ad07SJulian Scheel 1316*c565ad07SJulian Scheel err = snd_ctl_add(card, kctl); 1317*c565ad07SJulian Scheel if (err < 0) 1318*c565ad07SJulian Scheel goto snd_fail; 1319*c565ad07SJulian Scheel prm->snd_kctl_rate = kctl; 132002de698cSRuslan Bilovol } 132102de698cSRuslan Bilovol 1322d23922fcSRuslan Bilovol strscpy(card->driver, card_name, sizeof(card->driver)); 1323d23922fcSRuslan Bilovol strscpy(card->shortname, card_name, sizeof(card->shortname)); 1324eb9fecb9SRuslan Bilovol sprintf(card->longname, "%s %i", card_name, card->dev->id); 1325eb9fecb9SRuslan Bilovol 1326d27ab1e6STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 132767b2945dSTakashi Iwai NULL, 0, BUFF_SIZE_MAX); 1328eb9fecb9SRuslan Bilovol 1329eb9fecb9SRuslan Bilovol err = snd_card_register(card); 1330eb9fecb9SRuslan Bilovol 1331eb9fecb9SRuslan Bilovol if (!err) 1332eb9fecb9SRuslan Bilovol return 0; 1333eb9fecb9SRuslan Bilovol 1334eb9fecb9SRuslan Bilovol snd_fail: 1335eb9fecb9SRuslan Bilovol snd_card_free(card); 1336eb9fecb9SRuslan Bilovol fail: 133729865117SJerome Brunet kfree(uac->p_prm.reqs); 133829865117SJerome Brunet kfree(uac->c_prm.reqs); 1339eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1340eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1341eb9fecb9SRuslan Bilovol kfree(uac); 1342eb9fecb9SRuslan Bilovol 1343eb9fecb9SRuslan Bilovol return err; 1344eb9fecb9SRuslan Bilovol } 1345eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup); 1346eb9fecb9SRuslan Bilovol 1347eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio) 1348eb9fecb9SRuslan Bilovol { 1349eb9fecb9SRuslan Bilovol struct snd_uac_chip *uac; 1350eb9fecb9SRuslan Bilovol struct snd_card *card; 1351eb9fecb9SRuslan Bilovol 1352eb9fecb9SRuslan Bilovol if (!g_audio || !g_audio->uac) 1353eb9fecb9SRuslan Bilovol return; 1354eb9fecb9SRuslan Bilovol 1355eb9fecb9SRuslan Bilovol uac = g_audio->uac; 1356eb9fecb9SRuslan Bilovol card = uac->card; 1357eb9fecb9SRuslan Bilovol if (card) 1358eb9fecb9SRuslan Bilovol snd_card_free(card); 1359eb9fecb9SRuslan Bilovol 136029865117SJerome Brunet kfree(uac->p_prm.reqs); 136129865117SJerome Brunet kfree(uac->c_prm.reqs); 1362eb9fecb9SRuslan Bilovol kfree(uac->p_prm.rbuf); 1363eb9fecb9SRuslan Bilovol kfree(uac->c_prm.rbuf); 1364eb9fecb9SRuslan Bilovol kfree(uac); 1365eb9fecb9SRuslan Bilovol } 1366eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup); 1367eb9fecb9SRuslan Bilovol 1368eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL"); 1369eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); 1370eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol"); 1371