162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * u_audio.c -- interface to USB gadget "ALSA sound card" utilities 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 662306a36Sopenharmony_ci * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Sound card implementation was cut-and-pasted with changes 962306a36Sopenharmony_ci * from f_uac2.c and has: 1062306a36Sopenharmony_ci * Copyright (C) 2011 1162306a36Sopenharmony_ci * Yadwinder Singh (yadi.brar01@gmail.com) 1262306a36Sopenharmony_ci * Jaswinder Singh (jaswinder.singh@linaro.org) 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/pcm.h> 1962306a36Sopenharmony_ci#include <sound/pcm_params.h> 2062306a36Sopenharmony_ci#include <sound/control.h> 2162306a36Sopenharmony_ci#include <sound/tlv.h> 2262306a36Sopenharmony_ci#include <linux/usb/audio.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "u_audio.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define BUFF_SIZE_MAX (PAGE_SIZE * 16) 2762306a36Sopenharmony_ci#define PRD_SIZE_MAX PAGE_SIZE 2862306a36Sopenharmony_ci#define MIN_PERIODS 4 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cienum { 3162306a36Sopenharmony_ci UAC_FBACK_CTRL, 3262306a36Sopenharmony_ci UAC_P_PITCH_CTRL, 3362306a36Sopenharmony_ci UAC_MUTE_CTRL, 3462306a36Sopenharmony_ci UAC_VOLUME_CTRL, 3562306a36Sopenharmony_ci UAC_RATE_CTRL, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Runtime data params for one stream */ 3962306a36Sopenharmony_cistruct uac_rtd_params { 4062306a36Sopenharmony_ci struct snd_uac_chip *uac; /* parent chip */ 4162306a36Sopenharmony_ci bool ep_enabled; /* if the ep is enabled */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci struct snd_pcm_substream *ss; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* Ring buffer */ 4662306a36Sopenharmony_ci ssize_t hw_ptr; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci void *rbuf; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci unsigned int pitch; /* Stream pitch ratio to 1000000 */ 5162306a36Sopenharmony_ci unsigned int max_psize; /* MaxPacketSize of endpoint */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct usb_request **reqs; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct usb_request *req_fback; /* Feedback endpoint request */ 5662306a36Sopenharmony_ci bool fb_ep_enabled; /* if the ep is enabled */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Volume/Mute controls and their state */ 5962306a36Sopenharmony_ci int fu_id; /* Feature Unit ID */ 6062306a36Sopenharmony_ci struct snd_kcontrol *snd_kctl_volume; 6162306a36Sopenharmony_ci struct snd_kcontrol *snd_kctl_mute; 6262306a36Sopenharmony_ci s16 volume_min, volume_max, volume_res; 6362306a36Sopenharmony_ci s16 volume; 6462306a36Sopenharmony_ci int mute; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ 6762306a36Sopenharmony_ci int srate; /* selected samplerate */ 6862306a36Sopenharmony_ci int active; /* playback/capture running */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spinlock_t lock; /* lock for control transfers */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct snd_uac_chip { 7562306a36Sopenharmony_ci struct g_audio *audio_dev; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci struct uac_rtd_params p_prm; 7862306a36Sopenharmony_ci struct uac_rtd_params c_prm; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci struct snd_card *card; 8162306a36Sopenharmony_ci struct snd_pcm *pcm; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* pre-calculated values for playback iso completion */ 8462306a36Sopenharmony_ci unsigned long long p_residue_mil; 8562306a36Sopenharmony_ci unsigned int p_interval; 8662306a36Sopenharmony_ci unsigned int p_framesize; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct snd_pcm_hardware uac_pcm_hardware = { 9062306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER 9162306a36Sopenharmony_ci | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID 9262306a36Sopenharmony_ci | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 9362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 9462306a36Sopenharmony_ci .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, 9562306a36Sopenharmony_ci .buffer_bytes_max = BUFF_SIZE_MAX, 9662306a36Sopenharmony_ci .period_bytes_max = PRD_SIZE_MAX, 9762306a36Sopenharmony_ci .periods_min = MIN_PERIODS, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void u_audio_set_fback_frequency(enum usb_device_speed speed, 10162306a36Sopenharmony_ci struct usb_ep *out_ep, 10262306a36Sopenharmony_ci unsigned long long freq, 10362306a36Sopenharmony_ci unsigned int pitch, 10462306a36Sopenharmony_ci void *buf) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 ff = 0; 10762306a36Sopenharmony_ci const struct usb_endpoint_descriptor *ep_desc; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Because the pitch base is 1000000, the final divider here 11162306a36Sopenharmony_ci * will be 1000 * 1000000 = 1953125 << 9 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Instead of dealing with big numbers lets fold this 9 left shift 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (speed == USB_SPEED_FULL) { 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Full-speed feedback endpoints report frequency 11962306a36Sopenharmony_ci * in samples/frame 12062306a36Sopenharmony_ci * Format is encoded in Q10.10 left-justified in the 24 bits, 12162306a36Sopenharmony_ci * so that it has a Q10.14 format. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * ff = (freq << 14) / 1000 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci freq <<= 5; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * High-speed feedback endpoints report frequency 12962306a36Sopenharmony_ci * in samples/microframe. 13062306a36Sopenharmony_ci * Format is encoded in Q12.13 fitted into four bytes so that 13162306a36Sopenharmony_ci * the binary point is located between the second and the third 13262306a36Sopenharmony_ci * byte fromat (that is Q16.16) 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * ff = (freq << 16) / 8000 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * Win10 and OSX UAC2 drivers require number of samples per packet 13762306a36Sopenharmony_ci * in order to honor the feedback value. 13862306a36Sopenharmony_ci * Linux snd-usb-audio detects the applied bit-shift automatically. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci ep_desc = out_ep->desc; 14162306a36Sopenharmony_ci freq <<= 4 + (ep_desc->bInterval - 1); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci *(__le32 *)buf = cpu_to_le32(ff); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned int pending; 15262306a36Sopenharmony_ci unsigned int hw_ptr; 15362306a36Sopenharmony_ci int status = req->status; 15462306a36Sopenharmony_ci struct snd_pcm_substream *substream; 15562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 15662306a36Sopenharmony_ci struct uac_rtd_params *prm = req->context; 15762306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 15862306a36Sopenharmony_ci unsigned int frames, p_pktsize; 15962306a36Sopenharmony_ci unsigned long long pitched_rate_mil, p_pktsize_residue_mil, 16062306a36Sopenharmony_ci residue_frames_mil, div_result; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* i/f shutting down */ 16362306a36Sopenharmony_ci if (!prm->ep_enabled) { 16462306a36Sopenharmony_ci usb_ep_free_request(ep, req); 16562306a36Sopenharmony_ci return; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (req->status == -ESHUTDOWN) 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * We can't really do much about bad xfers. 17362306a36Sopenharmony_ci * Afterall, the ISOCH xfers could fail legitimately. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci if (status) 17662306a36Sopenharmony_ci pr_debug("%s: iso_complete status(%d) %d/%d\n", 17762306a36Sopenharmony_ci __func__, status, req->actual, req->length); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci substream = prm->ss; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Do nothing if ALSA isn't active */ 18262306a36Sopenharmony_ci if (!substream) 18362306a36Sopenharmony_ci goto exit; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci snd_pcm_stream_lock(substream); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci runtime = substream->runtime; 18862306a36Sopenharmony_ci if (!runtime || !snd_pcm_running(substream)) { 18962306a36Sopenharmony_ci snd_pcm_stream_unlock(substream); 19062306a36Sopenharmony_ci goto exit; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * For each IN packet, take the quotient of the current data 19662306a36Sopenharmony_ci * rate and the endpoint's interval as the base packet size. 19762306a36Sopenharmony_ci * If there is a residue from this division, add it to the 19862306a36Sopenharmony_ci * residue accumulator. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci unsigned long long p_interval_mil = uac->p_interval * 1000000ULL; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch; 20362306a36Sopenharmony_ci div_result = pitched_rate_mil; 20462306a36Sopenharmony_ci do_div(div_result, uac->p_interval); 20562306a36Sopenharmony_ci do_div(div_result, 1000000); 20662306a36Sopenharmony_ci frames = (unsigned int) div_result; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n", 20962306a36Sopenharmony_ci prm->srate, prm->pitch, p_interval_mil, frames); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci p_pktsize = min_t(unsigned int, 21262306a36Sopenharmony_ci uac->p_framesize * frames, 21362306a36Sopenharmony_ci ep->maxpacket); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (p_pktsize < ep->maxpacket) { 21662306a36Sopenharmony_ci residue_frames_mil = pitched_rate_mil - frames * p_interval_mil; 21762306a36Sopenharmony_ci p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil; 21862306a36Sopenharmony_ci } else 21962306a36Sopenharmony_ci p_pktsize_residue_mil = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci req->length = p_pktsize; 22262306a36Sopenharmony_ci uac->p_residue_mil += p_pktsize_residue_mil; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Whenever there are more bytes in the accumulator p_residue_mil than we 22662306a36Sopenharmony_ci * need to add one more sample frame, increase this packet's 22762306a36Sopenharmony_ci * size and decrease the accumulator. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci div_result = uac->p_residue_mil; 23062306a36Sopenharmony_ci do_div(div_result, uac->p_interval); 23162306a36Sopenharmony_ci do_div(div_result, 1000000); 23262306a36Sopenharmony_ci if ((unsigned int) div_result >= uac->p_framesize) { 23362306a36Sopenharmony_ci req->length += uac->p_framesize; 23462306a36Sopenharmony_ci uac->p_residue_mil -= uac->p_framesize * p_interval_mil; 23562306a36Sopenharmony_ci pr_debug("increased req length to %d\n", req->length); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci req->actual = req->length; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci hw_ptr = prm->hw_ptr; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Pack USB load in ALSA ring buffer */ 24562306a36Sopenharmony_ci pending = runtime->dma_bytes - hw_ptr; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 24862306a36Sopenharmony_ci if (unlikely(pending < req->actual)) { 24962306a36Sopenharmony_ci memcpy(req->buf, runtime->dma_area + hw_ptr, pending); 25062306a36Sopenharmony_ci memcpy(req->buf + pending, runtime->dma_area, 25162306a36Sopenharmony_ci req->actual - pending); 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci memcpy(req->buf, runtime->dma_area + hw_ptr, 25462306a36Sopenharmony_ci req->actual); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci if (unlikely(pending < req->actual)) { 25862306a36Sopenharmony_ci memcpy(runtime->dma_area + hw_ptr, req->buf, pending); 25962306a36Sopenharmony_ci memcpy(runtime->dma_area, req->buf + pending, 26062306a36Sopenharmony_ci req->actual - pending); 26162306a36Sopenharmony_ci } else { 26262306a36Sopenharmony_ci memcpy(runtime->dma_area + hw_ptr, req->buf, 26362306a36Sopenharmony_ci req->actual); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* update hw_ptr after data is copied to memory */ 26862306a36Sopenharmony_ci prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; 26962306a36Sopenharmony_ci hw_ptr = prm->hw_ptr; 27062306a36Sopenharmony_ci snd_pcm_stream_unlock(substream); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) 27362306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciexit: 27662306a36Sopenharmony_ci if (usb_ep_queue(ep, req, GFP_ATOMIC)) 27762306a36Sopenharmony_ci dev_err(uac->card->dev, "%d Error!\n", __LINE__); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void u_audio_iso_fback_complete(struct usb_ep *ep, 28162306a36Sopenharmony_ci struct usb_request *req) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct uac_rtd_params *prm = req->context; 28462306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 28562306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 28662306a36Sopenharmony_ci int status = req->status; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* i/f shutting down */ 28962306a36Sopenharmony_ci if (!prm->fb_ep_enabled) { 29062306a36Sopenharmony_ci kfree(req->buf); 29162306a36Sopenharmony_ci usb_ep_free_request(ep, req); 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (req->status == -ESHUTDOWN) 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * We can't really do much about bad xfers. 30062306a36Sopenharmony_ci * Afterall, the ISOCH xfers could fail legitimately. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (status) 30362306a36Sopenharmony_ci pr_debug("%s: iso_complete status(%d) %d/%d\n", 30462306a36Sopenharmony_ci __func__, status, req->actual, req->length); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep, 30762306a36Sopenharmony_ci prm->srate, prm->pitch, 30862306a36Sopenharmony_ci req->buf); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (usb_ep_queue(ep, req, GFP_ATOMIC)) 31162306a36Sopenharmony_ci dev_err(uac->card->dev, "%d Error!\n", __LINE__); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 31762306a36Sopenharmony_ci struct uac_rtd_params *prm; 31862306a36Sopenharmony_ci struct g_audio *audio_dev; 31962306a36Sopenharmony_ci struct uac_params *params; 32062306a36Sopenharmony_ci int err = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci audio_dev = uac->audio_dev; 32362306a36Sopenharmony_ci params = &audio_dev->params; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 32662306a36Sopenharmony_ci prm = &uac->p_prm; 32762306a36Sopenharmony_ci else 32862306a36Sopenharmony_ci prm = &uac->c_prm; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Reset */ 33162306a36Sopenharmony_ci prm->hw_ptr = 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci switch (cmd) { 33462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 33562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 33662306a36Sopenharmony_ci prm->ss = substream; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 33962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 34062306a36Sopenharmony_ci prm->ss = NULL; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci default: 34362306a36Sopenharmony_ci err = -EINVAL; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Clear buffer after Play stops */ 34762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) 34862306a36Sopenharmony_ci memset(prm->rbuf, 0, prm->max_psize * params->req_number); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return err; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 35662306a36Sopenharmony_ci struct uac_rtd_params *prm; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 35962306a36Sopenharmony_ci prm = &uac->p_prm; 36062306a36Sopenharmony_ci else 36162306a36Sopenharmony_ci prm = &uac->c_prm; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, prm->hw_ptr); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic u64 uac_ssize_to_fmt(int ssize) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci u64 ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci switch (ssize) { 37162306a36Sopenharmony_ci case 3: 37262306a36Sopenharmony_ci ret = SNDRV_PCM_FMTBIT_S24_3LE; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case 4: 37562306a36Sopenharmony_ci ret = SNDRV_PCM_FMTBIT_S32_LE; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci default: 37862306a36Sopenharmony_ci ret = SNDRV_PCM_FMTBIT_S16_LE; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int uac_pcm_open(struct snd_pcm_substream *substream) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); 38862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 38962306a36Sopenharmony_ci struct g_audio *audio_dev; 39062306a36Sopenharmony_ci struct uac_params *params; 39162306a36Sopenharmony_ci struct uac_rtd_params *prm; 39262306a36Sopenharmony_ci int p_ssize, c_ssize; 39362306a36Sopenharmony_ci int p_chmask, c_chmask; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci audio_dev = uac->audio_dev; 39662306a36Sopenharmony_ci params = &audio_dev->params; 39762306a36Sopenharmony_ci p_ssize = params->p_ssize; 39862306a36Sopenharmony_ci c_ssize = params->c_ssize; 39962306a36Sopenharmony_ci p_chmask = params->p_chmask; 40062306a36Sopenharmony_ci c_chmask = params->c_chmask; 40162306a36Sopenharmony_ci uac->p_residue_mil = 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci runtime->hw = uac_pcm_hardware; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40662306a36Sopenharmony_ci runtime->hw.formats = uac_ssize_to_fmt(p_ssize); 40762306a36Sopenharmony_ci runtime->hw.channels_min = num_channels(p_chmask); 40862306a36Sopenharmony_ci prm = &uac->p_prm; 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci runtime->hw.formats = uac_ssize_to_fmt(c_ssize); 41162306a36Sopenharmony_ci runtime->hw.channels_min = num_channels(c_chmask); 41262306a36Sopenharmony_ci prm = &uac->c_prm; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci runtime->hw.period_bytes_min = 2 * prm->max_psize 41662306a36Sopenharmony_ci / runtime->hw.periods_min; 41762306a36Sopenharmony_ci runtime->hw.rate_min = prm->srate; 41862306a36Sopenharmony_ci runtime->hw.rate_max = runtime->hw.rate_min; 41962306a36Sopenharmony_ci runtime->hw.channels_max = runtime->hw.channels_min; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* ALSA cries without these function pointers */ 42762306a36Sopenharmony_cistatic int uac_pcm_null(struct snd_pcm_substream *substream) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct snd_pcm_ops uac_pcm_ops = { 43362306a36Sopenharmony_ci .open = uac_pcm_open, 43462306a36Sopenharmony_ci .close = uac_pcm_null, 43562306a36Sopenharmony_ci .trigger = uac_pcm_trigger, 43662306a36Sopenharmony_ci .pointer = uac_pcm_pointer, 43762306a36Sopenharmony_ci .prepare = uac_pcm_null, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 44362306a36Sopenharmony_ci struct g_audio *audio_dev; 44462306a36Sopenharmony_ci struct uac_params *params; 44562306a36Sopenharmony_ci int i; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!prm->ep_enabled) 44862306a36Sopenharmony_ci return; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci audio_dev = uac->audio_dev; 45162306a36Sopenharmony_ci params = &audio_dev->params; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci for (i = 0; i < params->req_number; i++) { 45462306a36Sopenharmony_ci if (prm->reqs[i]) { 45562306a36Sopenharmony_ci if (usb_ep_dequeue(ep, prm->reqs[i])) 45662306a36Sopenharmony_ci usb_ep_free_request(ep, prm->reqs[i]); 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * If usb_ep_dequeue() cannot successfully dequeue the 45962306a36Sopenharmony_ci * request, the request will be freed by the completion 46062306a36Sopenharmony_ci * callback. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci prm->reqs[i] = NULL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci prm->ep_enabled = false; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (usb_ep_disable(ep)) 47062306a36Sopenharmony_ci dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!prm->fb_ep_enabled) 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (prm->req_fback) { 48162306a36Sopenharmony_ci if (usb_ep_dequeue(ep, prm->req_fback)) { 48262306a36Sopenharmony_ci kfree(prm->req_fback->buf); 48362306a36Sopenharmony_ci usb_ep_free_request(ep, prm->req_fback); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci prm->req_fback = NULL; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci prm->fb_ep_enabled = false; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (usb_ep_disable(ep)) 49162306a36Sopenharmony_ci dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void set_active(struct uac_rtd_params *prm, bool active) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci // notifying through the Rate ctrl 49762306a36Sopenharmony_ci struct snd_kcontrol *kctl = prm->snd_kctl_rate; 49862306a36Sopenharmony_ci unsigned long flags; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 50162306a36Sopenharmony_ci if (prm->active != active) { 50262306a36Sopenharmony_ci prm->active = active; 50362306a36Sopenharmony_ci snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 50462306a36Sopenharmony_ci &kctl->id); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 51262306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 51362306a36Sopenharmony_ci struct uac_rtd_params *prm; 51462306a36Sopenharmony_ci int i; 51562306a36Sopenharmony_ci unsigned long flags; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 51862306a36Sopenharmony_ci prm = &uac->c_prm; 51962306a36Sopenharmony_ci for (i = 0; i < UAC_MAX_RATES; i++) { 52062306a36Sopenharmony_ci if (params->c_srates[i] == srate) { 52162306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 52262306a36Sopenharmony_ci prm->srate = srate; 52362306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci if (params->c_srates[i] == 0) 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_set_capture_srate); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciint u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 53762306a36Sopenharmony_ci struct uac_rtd_params *prm; 53862306a36Sopenharmony_ci unsigned long flags; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci prm = &uac->c_prm; 54162306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 54262306a36Sopenharmony_ci *val = prm->srate; 54362306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_get_capture_srate); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciint u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 55162306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 55262306a36Sopenharmony_ci struct uac_rtd_params *prm; 55362306a36Sopenharmony_ci int i; 55462306a36Sopenharmony_ci unsigned long flags; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); 55762306a36Sopenharmony_ci prm = &uac->p_prm; 55862306a36Sopenharmony_ci for (i = 0; i < UAC_MAX_RATES; i++) { 55962306a36Sopenharmony_ci if (params->p_srates[i] == srate) { 56062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 56162306a36Sopenharmony_ci prm->srate = srate; 56262306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci if (params->p_srates[i] == 0) 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_set_playback_srate); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ciint u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 57662306a36Sopenharmony_ci struct uac_rtd_params *prm; 57762306a36Sopenharmony_ci unsigned long flags; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci prm = &uac->p_prm; 58062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 58162306a36Sopenharmony_ci *val = prm->srate; 58262306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_get_playback_srate); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ciint u_audio_start_capture(struct g_audio *audio_dev) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 59062306a36Sopenharmony_ci struct usb_gadget *gadget = audio_dev->gadget; 59162306a36Sopenharmony_ci struct device *dev = &gadget->dev; 59262306a36Sopenharmony_ci struct usb_request *req, *req_fback; 59362306a36Sopenharmony_ci struct usb_ep *ep, *ep_fback; 59462306a36Sopenharmony_ci struct uac_rtd_params *prm; 59562306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 59662306a36Sopenharmony_ci int req_len, i; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci prm = &uac->c_prm; 59962306a36Sopenharmony_ci dev_dbg(dev, "start capture with rate %d\n", prm->srate); 60062306a36Sopenharmony_ci ep = audio_dev->out_ep; 60162306a36Sopenharmony_ci config_ep_by_speed(gadget, &audio_dev->func, ep); 60262306a36Sopenharmony_ci req_len = ep->maxpacket; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci prm->ep_enabled = true; 60562306a36Sopenharmony_ci usb_ep_enable(ep); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci for (i = 0; i < params->req_number; i++) { 60862306a36Sopenharmony_ci if (!prm->reqs[i]) { 60962306a36Sopenharmony_ci req = usb_ep_alloc_request(ep, GFP_ATOMIC); 61062306a36Sopenharmony_ci if (req == NULL) 61162306a36Sopenharmony_ci return -ENOMEM; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci prm->reqs[i] = req; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci req->zero = 0; 61662306a36Sopenharmony_ci req->context = prm; 61762306a36Sopenharmony_ci req->length = req_len; 61862306a36Sopenharmony_ci req->complete = u_audio_iso_complete; 61962306a36Sopenharmony_ci req->buf = prm->rbuf + i * ep->maxpacket; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 62362306a36Sopenharmony_ci dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci set_active(&uac->c_prm, true); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ep_fback = audio_dev->in_ep_fback; 62962306a36Sopenharmony_ci if (!ep_fback) 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Setup feedback endpoint */ 63362306a36Sopenharmony_ci config_ep_by_speed(gadget, &audio_dev->func, ep_fback); 63462306a36Sopenharmony_ci prm->fb_ep_enabled = true; 63562306a36Sopenharmony_ci usb_ep_enable(ep_fback); 63662306a36Sopenharmony_ci req_len = ep_fback->maxpacket; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); 63962306a36Sopenharmony_ci if (req_fback == NULL) 64062306a36Sopenharmony_ci return -ENOMEM; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci prm->req_fback = req_fback; 64362306a36Sopenharmony_ci req_fback->zero = 0; 64462306a36Sopenharmony_ci req_fback->context = prm; 64562306a36Sopenharmony_ci req_fback->length = req_len; 64662306a36Sopenharmony_ci req_fback->complete = u_audio_iso_fback_complete; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci req_fback->buf = kzalloc(req_len, GFP_ATOMIC); 64962306a36Sopenharmony_ci if (!req_fback->buf) 65062306a36Sopenharmony_ci return -ENOMEM; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * Configure the feedback endpoint's reported frequency. 65462306a36Sopenharmony_ci * Always start with original frequency since its deviation can't 65562306a36Sopenharmony_ci * be meauserd at start of playback 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci prm->pitch = 1000000; 65862306a36Sopenharmony_ci u_audio_set_fback_frequency(audio_dev->gadget->speed, ep, 65962306a36Sopenharmony_ci prm->srate, prm->pitch, 66062306a36Sopenharmony_ci req_fback->buf); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) 66362306a36Sopenharmony_ci dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_start_capture); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_civoid u_audio_stop_capture(struct g_audio *audio_dev) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci set_active(&uac->c_prm, false); 67462306a36Sopenharmony_ci if (audio_dev->in_ep_fback) 67562306a36Sopenharmony_ci free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 67662306a36Sopenharmony_ci free_ep(&uac->c_prm, audio_dev->out_ep); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_stop_capture); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciint u_audio_start_playback(struct g_audio *audio_dev) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 68362306a36Sopenharmony_ci struct usb_gadget *gadget = audio_dev->gadget; 68462306a36Sopenharmony_ci struct device *dev = &gadget->dev; 68562306a36Sopenharmony_ci struct usb_request *req; 68662306a36Sopenharmony_ci struct usb_ep *ep; 68762306a36Sopenharmony_ci struct uac_rtd_params *prm; 68862306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 68962306a36Sopenharmony_ci unsigned int factor; 69062306a36Sopenharmony_ci const struct usb_endpoint_descriptor *ep_desc; 69162306a36Sopenharmony_ci int req_len, i; 69262306a36Sopenharmony_ci unsigned int p_pktsize; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci prm = &uac->p_prm; 69562306a36Sopenharmony_ci dev_dbg(dev, "start playback with rate %d\n", prm->srate); 69662306a36Sopenharmony_ci ep = audio_dev->in_ep; 69762306a36Sopenharmony_ci config_ep_by_speed(gadget, &audio_dev->func, ep); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ep_desc = ep->desc; 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Always start with original frequency 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci prm->pitch = 1000000; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* pre-calculate the playback endpoint's interval */ 70662306a36Sopenharmony_ci if (gadget->speed == USB_SPEED_FULL) 70762306a36Sopenharmony_ci factor = 1000; 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci factor = 8000; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* pre-compute some values for iso_complete() */ 71262306a36Sopenharmony_ci uac->p_framesize = params->p_ssize * 71362306a36Sopenharmony_ci num_channels(params->p_chmask); 71462306a36Sopenharmony_ci uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); 71562306a36Sopenharmony_ci p_pktsize = min_t(unsigned int, 71662306a36Sopenharmony_ci uac->p_framesize * 71762306a36Sopenharmony_ci (prm->srate / uac->p_interval), 71862306a36Sopenharmony_ci ep->maxpacket); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci req_len = p_pktsize; 72162306a36Sopenharmony_ci uac->p_residue_mil = 0; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci prm->ep_enabled = true; 72462306a36Sopenharmony_ci usb_ep_enable(ep); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci for (i = 0; i < params->req_number; i++) { 72762306a36Sopenharmony_ci if (!prm->reqs[i]) { 72862306a36Sopenharmony_ci req = usb_ep_alloc_request(ep, GFP_ATOMIC); 72962306a36Sopenharmony_ci if (req == NULL) 73062306a36Sopenharmony_ci return -ENOMEM; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci prm->reqs[i] = req; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci req->zero = 0; 73562306a36Sopenharmony_ci req->context = prm; 73662306a36Sopenharmony_ci req->length = req_len; 73762306a36Sopenharmony_ci req->complete = u_audio_iso_complete; 73862306a36Sopenharmony_ci req->buf = prm->rbuf + i * ep->maxpacket; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) 74262306a36Sopenharmony_ci dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci set_active(&uac->p_prm, true); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_start_playback); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_civoid u_audio_stop_playback(struct g_audio *audio_dev) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci set_active(&uac->p_prm, false); 75662306a36Sopenharmony_ci free_ep(&uac->p_prm, audio_dev->in_ep); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_stop_playback); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_civoid u_audio_suspend(struct g_audio *audio_dev) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci set_active(&uac->p_prm, false); 76562306a36Sopenharmony_ci set_active(&uac->c_prm, false); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_suspend); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ciint u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 77262306a36Sopenharmony_ci struct uac_rtd_params *prm; 77362306a36Sopenharmony_ci unsigned long flags; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (playback) 77662306a36Sopenharmony_ci prm = &uac->p_prm; 77762306a36Sopenharmony_ci else 77862306a36Sopenharmony_ci prm = &uac->c_prm; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 78162306a36Sopenharmony_ci *val = prm->volume; 78262306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_get_volume); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ciint u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 79162306a36Sopenharmony_ci struct uac_rtd_params *prm; 79262306a36Sopenharmony_ci unsigned long flags; 79362306a36Sopenharmony_ci int change = 0; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (playback) 79662306a36Sopenharmony_ci prm = &uac->p_prm; 79762306a36Sopenharmony_ci else 79862306a36Sopenharmony_ci prm = &uac->c_prm; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 80162306a36Sopenharmony_ci val = clamp(val, prm->volume_min, prm->volume_max); 80262306a36Sopenharmony_ci if (prm->volume != val) { 80362306a36Sopenharmony_ci prm->volume = val; 80462306a36Sopenharmony_ci change = 1; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (change) 80962306a36Sopenharmony_ci snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 81062306a36Sopenharmony_ci &prm->snd_kctl_volume->id); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_set_volume); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ciint u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 81962306a36Sopenharmony_ci struct uac_rtd_params *prm; 82062306a36Sopenharmony_ci unsigned long flags; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (playback) 82362306a36Sopenharmony_ci prm = &uac->p_prm; 82462306a36Sopenharmony_ci else 82562306a36Sopenharmony_ci prm = &uac->c_prm; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 82862306a36Sopenharmony_ci *val = prm->mute; 82962306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_get_mute); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ciint u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct snd_uac_chip *uac = audio_dev->uac; 83862306a36Sopenharmony_ci struct uac_rtd_params *prm; 83962306a36Sopenharmony_ci unsigned long flags; 84062306a36Sopenharmony_ci int change = 0; 84162306a36Sopenharmony_ci int mute; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (playback) 84462306a36Sopenharmony_ci prm = &uac->p_prm; 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci prm = &uac->c_prm; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci mute = val ? 1 : 0; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 85162306a36Sopenharmony_ci if (prm->mute != mute) { 85262306a36Sopenharmony_ci prm->mute = mute; 85362306a36Sopenharmony_ci change = 1; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (change) 85862306a36Sopenharmony_ci snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 85962306a36Sopenharmony_ci &prm->snd_kctl_mute->id); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(u_audio_set_mute); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int u_audio_pitch_info(struct snd_kcontrol *kcontrol, 86762306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 87062306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 87162306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 87262306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 87362306a36Sopenharmony_ci unsigned int pitch_min, pitch_max; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 87662306a36Sopenharmony_ci pitch_max = (1000 + params->fb_max) * 1000; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 87962306a36Sopenharmony_ci uinfo->count = 1; 88062306a36Sopenharmony_ci uinfo->value.integer.min = pitch_min; 88162306a36Sopenharmony_ci uinfo->value.integer.max = pitch_max; 88262306a36Sopenharmony_ci uinfo->value.integer.step = 1; 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic int u_audio_pitch_get(struct snd_kcontrol *kcontrol, 88762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = prm->pitch; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int u_audio_pitch_put(struct snd_kcontrol *kcontrol, 89762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 90062306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 90162306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 90262306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 90362306a36Sopenharmony_ci unsigned int val; 90462306a36Sopenharmony_ci unsigned int pitch_min, pitch_max; 90562306a36Sopenharmony_ci int change = 0; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; 90862306a36Sopenharmony_ci pitch_max = (1000 + params->fb_max) * 1000; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci val = ucontrol->value.integer.value[0]; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (val < pitch_min) 91362306a36Sopenharmony_ci val = pitch_min; 91462306a36Sopenharmony_ci if (val > pitch_max) 91562306a36Sopenharmony_ci val = pitch_max; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (prm->pitch != val) { 91862306a36Sopenharmony_ci prm->pitch = val; 91962306a36Sopenharmony_ci change = 1; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return change; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int u_audio_mute_info(struct snd_kcontrol *kcontrol, 92662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 92962306a36Sopenharmony_ci uinfo->count = 1; 93062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 93162306a36Sopenharmony_ci uinfo->value.integer.max = 1; 93262306a36Sopenharmony_ci uinfo->value.integer.step = 1; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic int u_audio_mute_get(struct snd_kcontrol *kcontrol, 93862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 94162306a36Sopenharmony_ci unsigned long flags; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 94462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !prm->mute; 94562306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci return 0; 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int u_audio_mute_put(struct snd_kcontrol *kcontrol, 95162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 95462306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 95562306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 95662306a36Sopenharmony_ci unsigned int val; 95762306a36Sopenharmony_ci unsigned long flags; 95862306a36Sopenharmony_ci int change = 0; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci val = !ucontrol->value.integer.value[0]; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 96362306a36Sopenharmony_ci if (val != prm->mute) { 96462306a36Sopenharmony_ci prm->mute = val; 96562306a36Sopenharmony_ci change = 1; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (change && audio_dev->notify) 97062306a36Sopenharmony_ci audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return change; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/* 97662306a36Sopenharmony_ci * TLV callback for mixer volume controls 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_cistatic int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, 97962306a36Sopenharmony_ci unsigned int size, unsigned int __user *_tlv) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 98262306a36Sopenharmony_ci DECLARE_TLV_DB_MINMAX(scale, 0, 0); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (size < sizeof(scale)) 98562306a36Sopenharmony_ci return -ENOMEM; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ 98862306a36Sopenharmony_ci scale[2] = (prm->volume_min * 100) / 256; 98962306a36Sopenharmony_ci scale[3] = (prm->volume_max * 100) / 256; 99062306a36Sopenharmony_ci if (copy_to_user(_tlv, scale, sizeof(scale))) 99162306a36Sopenharmony_ci return -EFAULT; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic int u_audio_volume_info(struct snd_kcontrol *kcontrol, 99762306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 100262306a36Sopenharmony_ci uinfo->count = 1; 100362306a36Sopenharmony_ci uinfo->value.integer.min = 0; 100462306a36Sopenharmony_ci uinfo->value.integer.max = 100562306a36Sopenharmony_ci (prm->volume_max - prm->volume_min + prm->volume_res - 1) 100662306a36Sopenharmony_ci / prm->volume_res; 100762306a36Sopenharmony_ci uinfo->value.integer.step = 1; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int u_audio_volume_get(struct snd_kcontrol *kcontrol, 101362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 101662306a36Sopenharmony_ci unsigned long flags; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 101962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 102062306a36Sopenharmony_ci (prm->volume - prm->volume_min) / prm->volume_res; 102162306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return 0; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic int u_audio_volume_put(struct snd_kcontrol *kcontrol, 102762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 103062306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 103162306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 103262306a36Sopenharmony_ci unsigned int val; 103362306a36Sopenharmony_ci s16 volume; 103462306a36Sopenharmony_ci unsigned long flags; 103562306a36Sopenharmony_ci int change = 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci val = ucontrol->value.integer.value[0]; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 104062306a36Sopenharmony_ci volume = (val * prm->volume_res) + prm->volume_min; 104162306a36Sopenharmony_ci volume = clamp(volume, prm->volume_min, prm->volume_max); 104262306a36Sopenharmony_ci if (volume != prm->volume) { 104362306a36Sopenharmony_ci prm->volume = volume; 104462306a36Sopenharmony_ci change = 1; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (change && audio_dev->notify) 104962306a36Sopenharmony_ci audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return change; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int get_max_srate(const int *srates) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int i, max_srate = 0; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci for (i = 0; i < UAC_MAX_RATES; i++) { 105962306a36Sopenharmony_ci if (srates[i] == 0) 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci if (srates[i] > max_srate) 106262306a36Sopenharmony_ci max_srate = srates[i]; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci return max_srate; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic int get_min_srate(const int *srates) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci int i, min_srate = INT_MAX; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci for (i = 0; i < UAC_MAX_RATES; i++) { 107262306a36Sopenharmony_ci if (srates[i] == 0) 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci if (srates[i] < min_srate) 107562306a36Sopenharmony_ci min_srate = srates[i]; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci return min_srate; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int u_audio_rate_info(struct snd_kcontrol *kcontrol, 108162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci const int *srates; 108462306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 108562306a36Sopenharmony_ci struct snd_uac_chip *uac = prm->uac; 108662306a36Sopenharmony_ci struct g_audio *audio_dev = uac->audio_dev; 108762306a36Sopenharmony_ci struct uac_params *params = &audio_dev->params; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 109062306a36Sopenharmony_ci uinfo->count = 1; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (prm == &uac->c_prm) 109362306a36Sopenharmony_ci srates = params->c_srates; 109462306a36Sopenharmony_ci else 109562306a36Sopenharmony_ci srates = params->p_srates; 109662306a36Sopenharmony_ci uinfo->value.integer.min = get_min_srate(srates); 109762306a36Sopenharmony_ci uinfo->value.integer.max = get_max_srate(srates); 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int u_audio_rate_get(struct snd_kcontrol *kcontrol, 110262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); 110562306a36Sopenharmony_ci unsigned long flags; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci spin_lock_irqsave(&prm->lock, flags); 110862306a36Sopenharmony_ci if (prm->active) 110962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = prm->srate; 111062306a36Sopenharmony_ci else 111162306a36Sopenharmony_ci /* not active: reporting zero rate */ 111262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 111362306a36Sopenharmony_ci spin_unlock_irqrestore(&prm->lock, flags); 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic struct snd_kcontrol_new u_audio_controls[] = { 111862306a36Sopenharmony_ci [UAC_FBACK_CTRL] { 111962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 112062306a36Sopenharmony_ci .name = "Capture Pitch 1000000", 112162306a36Sopenharmony_ci .info = u_audio_pitch_info, 112262306a36Sopenharmony_ci .get = u_audio_pitch_get, 112362306a36Sopenharmony_ci .put = u_audio_pitch_put, 112462306a36Sopenharmony_ci }, 112562306a36Sopenharmony_ci [UAC_P_PITCH_CTRL] { 112662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 112762306a36Sopenharmony_ci .name = "Playback Pitch 1000000", 112862306a36Sopenharmony_ci .info = u_audio_pitch_info, 112962306a36Sopenharmony_ci .get = u_audio_pitch_get, 113062306a36Sopenharmony_ci .put = u_audio_pitch_put, 113162306a36Sopenharmony_ci }, 113262306a36Sopenharmony_ci [UAC_MUTE_CTRL] { 113362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 113462306a36Sopenharmony_ci .name = "", /* will be filled later */ 113562306a36Sopenharmony_ci .info = u_audio_mute_info, 113662306a36Sopenharmony_ci .get = u_audio_mute_get, 113762306a36Sopenharmony_ci .put = u_audio_mute_put, 113862306a36Sopenharmony_ci }, 113962306a36Sopenharmony_ci [UAC_VOLUME_CTRL] { 114062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 114162306a36Sopenharmony_ci .name = "", /* will be filled later */ 114262306a36Sopenharmony_ci .info = u_audio_volume_info, 114362306a36Sopenharmony_ci .get = u_audio_volume_get, 114462306a36Sopenharmony_ci .put = u_audio_volume_put, 114562306a36Sopenharmony_ci }, 114662306a36Sopenharmony_ci [UAC_RATE_CTRL] { 114762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 114862306a36Sopenharmony_ci .name = "", /* will be filled later */ 114962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 115062306a36Sopenharmony_ci .info = u_audio_rate_info, 115162306a36Sopenharmony_ci .get = u_audio_rate_get, 115262306a36Sopenharmony_ci }, 115362306a36Sopenharmony_ci}; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ciint g_audio_setup(struct g_audio *g_audio, const char *pcm_name, 115662306a36Sopenharmony_ci const char *card_name) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct snd_uac_chip *uac; 115962306a36Sopenharmony_ci struct snd_card *card; 116062306a36Sopenharmony_ci struct snd_pcm *pcm; 116162306a36Sopenharmony_ci struct snd_kcontrol *kctl; 116262306a36Sopenharmony_ci struct uac_params *params; 116362306a36Sopenharmony_ci int p_chmask, c_chmask; 116462306a36Sopenharmony_ci int i, err; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (!g_audio) 116762306a36Sopenharmony_ci return -EINVAL; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci uac = kzalloc(sizeof(*uac), GFP_KERNEL); 117062306a36Sopenharmony_ci if (!uac) 117162306a36Sopenharmony_ci return -ENOMEM; 117262306a36Sopenharmony_ci g_audio->uac = uac; 117362306a36Sopenharmony_ci uac->audio_dev = g_audio; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci params = &g_audio->params; 117662306a36Sopenharmony_ci p_chmask = params->p_chmask; 117762306a36Sopenharmony_ci c_chmask = params->c_chmask; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (c_chmask) { 118062306a36Sopenharmony_ci struct uac_rtd_params *prm = &uac->c_prm; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci spin_lock_init(&prm->lock); 118362306a36Sopenharmony_ci uac->c_prm.uac = uac; 118462306a36Sopenharmony_ci prm->max_psize = g_audio->out_ep_maxpsize; 118562306a36Sopenharmony_ci prm->srate = params->c_srates[0]; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci prm->reqs = kcalloc(params->req_number, 118862306a36Sopenharmony_ci sizeof(struct usb_request *), 118962306a36Sopenharmony_ci GFP_KERNEL); 119062306a36Sopenharmony_ci if (!prm->reqs) { 119162306a36Sopenharmony_ci err = -ENOMEM; 119262306a36Sopenharmony_ci goto fail; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci prm->rbuf = kcalloc(params->req_number, prm->max_psize, 119662306a36Sopenharmony_ci GFP_KERNEL); 119762306a36Sopenharmony_ci if (!prm->rbuf) { 119862306a36Sopenharmony_ci prm->max_psize = 0; 119962306a36Sopenharmony_ci err = -ENOMEM; 120062306a36Sopenharmony_ci goto fail; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (p_chmask) { 120562306a36Sopenharmony_ci struct uac_rtd_params *prm = &uac->p_prm; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci spin_lock_init(&prm->lock); 120862306a36Sopenharmony_ci uac->p_prm.uac = uac; 120962306a36Sopenharmony_ci prm->max_psize = g_audio->in_ep_maxpsize; 121062306a36Sopenharmony_ci prm->srate = params->p_srates[0]; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci prm->reqs = kcalloc(params->req_number, 121362306a36Sopenharmony_ci sizeof(struct usb_request *), 121462306a36Sopenharmony_ci GFP_KERNEL); 121562306a36Sopenharmony_ci if (!prm->reqs) { 121662306a36Sopenharmony_ci err = -ENOMEM; 121762306a36Sopenharmony_ci goto fail; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci prm->rbuf = kcalloc(params->req_number, prm->max_psize, 122162306a36Sopenharmony_ci GFP_KERNEL); 122262306a36Sopenharmony_ci if (!prm->rbuf) { 122362306a36Sopenharmony_ci prm->max_psize = 0; 122462306a36Sopenharmony_ci err = -ENOMEM; 122562306a36Sopenharmony_ci goto fail; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* Choose any slot, with no id */ 123062306a36Sopenharmony_ci err = snd_card_new(&g_audio->gadget->dev, 123162306a36Sopenharmony_ci -1, NULL, THIS_MODULE, 0, &card); 123262306a36Sopenharmony_ci if (err < 0) 123362306a36Sopenharmony_ci goto fail; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci uac->card = card; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * Create first PCM device 123962306a36Sopenharmony_ci * Create a substream only for non-zero channel streams 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci err = snd_pcm_new(uac->card, pcm_name, 0, 124262306a36Sopenharmony_ci p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); 124362306a36Sopenharmony_ci if (err < 0) 124462306a36Sopenharmony_ci goto snd_fail; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci strscpy(pcm->name, pcm_name, sizeof(pcm->name)); 124762306a36Sopenharmony_ci pcm->private_data = uac; 124862306a36Sopenharmony_ci uac->pcm = pcm; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); 125162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* 125462306a36Sopenharmony_ci * Create mixer and controls 125562306a36Sopenharmony_ci * Create only if it's required on USB side 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci if ((c_chmask && g_audio->in_ep_fback) 125862306a36Sopenharmony_ci || (p_chmask && params->p_fu.id) 125962306a36Sopenharmony_ci || (c_chmask && params->c_fu.id)) 126062306a36Sopenharmony_ci strscpy(card->mixername, card_name, sizeof(card->driver)); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (c_chmask && g_audio->in_ep_fback) { 126362306a36Sopenharmony_ci kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], 126462306a36Sopenharmony_ci &uac->c_prm); 126562306a36Sopenharmony_ci if (!kctl) { 126662306a36Sopenharmony_ci err = -ENOMEM; 126762306a36Sopenharmony_ci goto snd_fail; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci kctl->id.device = pcm->device; 127162306a36Sopenharmony_ci kctl->id.subdevice = 0; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 127462306a36Sopenharmony_ci if (err < 0) 127562306a36Sopenharmony_ci goto snd_fail; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (p_chmask) { 127962306a36Sopenharmony_ci kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL], 128062306a36Sopenharmony_ci &uac->p_prm); 128162306a36Sopenharmony_ci if (!kctl) { 128262306a36Sopenharmony_ci err = -ENOMEM; 128362306a36Sopenharmony_ci goto snd_fail; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci kctl->id.device = pcm->device; 128762306a36Sopenharmony_ci kctl->id.subdevice = 0; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 129062306a36Sopenharmony_ci if (err < 0) 129162306a36Sopenharmony_ci goto snd_fail; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { 129562306a36Sopenharmony_ci struct uac_rtd_params *prm; 129662306a36Sopenharmony_ci struct uac_fu_params *fu; 129762306a36Sopenharmony_ci char ctrl_name[24]; 129862306a36Sopenharmony_ci char *direction; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (!pcm->streams[i].substream_count) 130162306a36Sopenharmony_ci continue; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (i == SNDRV_PCM_STREAM_PLAYBACK) { 130462306a36Sopenharmony_ci prm = &uac->p_prm; 130562306a36Sopenharmony_ci fu = ¶ms->p_fu; 130662306a36Sopenharmony_ci direction = "Playback"; 130762306a36Sopenharmony_ci } else { 130862306a36Sopenharmony_ci prm = &uac->c_prm; 130962306a36Sopenharmony_ci fu = ¶ms->c_fu; 131062306a36Sopenharmony_ci direction = "Capture"; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci prm->fu_id = fu->id; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (fu->mute_present) { 131662306a36Sopenharmony_ci snprintf(ctrl_name, sizeof(ctrl_name), 131762306a36Sopenharmony_ci "PCM %s Switch", direction); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], 132262306a36Sopenharmony_ci prm); 132362306a36Sopenharmony_ci if (!kctl) { 132462306a36Sopenharmony_ci err = -ENOMEM; 132562306a36Sopenharmony_ci goto snd_fail; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci kctl->id.device = pcm->device; 132962306a36Sopenharmony_ci kctl->id.subdevice = 0; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 133262306a36Sopenharmony_ci if (err < 0) 133362306a36Sopenharmony_ci goto snd_fail; 133462306a36Sopenharmony_ci prm->snd_kctl_mute = kctl; 133562306a36Sopenharmony_ci prm->mute = 0; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci if (fu->volume_present) { 133962306a36Sopenharmony_ci snprintf(ctrl_name, sizeof(ctrl_name), 134062306a36Sopenharmony_ci "PCM %s Volume", direction); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], 134562306a36Sopenharmony_ci prm); 134662306a36Sopenharmony_ci if (!kctl) { 134762306a36Sopenharmony_ci err = -ENOMEM; 134862306a36Sopenharmony_ci goto snd_fail; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci kctl->id.device = pcm->device; 135262306a36Sopenharmony_ci kctl->id.subdevice = 0; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci kctl->tlv.c = u_audio_volume_tlv; 135662306a36Sopenharmony_ci kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | 135762306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 136062306a36Sopenharmony_ci if (err < 0) 136162306a36Sopenharmony_ci goto snd_fail; 136262306a36Sopenharmony_ci prm->snd_kctl_volume = kctl; 136362306a36Sopenharmony_ci prm->volume = fu->volume_max; 136462306a36Sopenharmony_ci prm->volume_max = fu->volume_max; 136562306a36Sopenharmony_ci prm->volume_min = fu->volume_min; 136662306a36Sopenharmony_ci prm->volume_res = fu->volume_res; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci /* Add rate control */ 137062306a36Sopenharmony_ci snprintf(ctrl_name, sizeof(ctrl_name), 137162306a36Sopenharmony_ci "%s Rate", direction); 137262306a36Sopenharmony_ci u_audio_controls[UAC_RATE_CTRL].name = ctrl_name; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm); 137562306a36Sopenharmony_ci if (!kctl) { 137662306a36Sopenharmony_ci err = -ENOMEM; 137762306a36Sopenharmony_ci goto snd_fail; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci kctl->id.device = pcm->device; 138162306a36Sopenharmony_ci kctl->id.subdevice = 0; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 138462306a36Sopenharmony_ci if (err < 0) 138562306a36Sopenharmony_ci goto snd_fail; 138662306a36Sopenharmony_ci prm->snd_kctl_rate = kctl; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci strscpy(card->driver, card_name, sizeof(card->driver)); 139062306a36Sopenharmony_ci strscpy(card->shortname, card_name, sizeof(card->shortname)); 139162306a36Sopenharmony_ci sprintf(card->longname, "%s %i", card_name, card->dev->id); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 139462306a36Sopenharmony_ci NULL, 0, BUFF_SIZE_MAX); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci err = snd_card_register(card); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (!err) 139962306a36Sopenharmony_ci return 0; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cisnd_fail: 140262306a36Sopenharmony_ci snd_card_free(card); 140362306a36Sopenharmony_cifail: 140462306a36Sopenharmony_ci kfree(uac->p_prm.reqs); 140562306a36Sopenharmony_ci kfree(uac->c_prm.reqs); 140662306a36Sopenharmony_ci kfree(uac->p_prm.rbuf); 140762306a36Sopenharmony_ci kfree(uac->c_prm.rbuf); 140862306a36Sopenharmony_ci kfree(uac); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return err; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(g_audio_setup); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_civoid g_audio_cleanup(struct g_audio *g_audio) 141562306a36Sopenharmony_ci{ 141662306a36Sopenharmony_ci struct snd_uac_chip *uac; 141762306a36Sopenharmony_ci struct snd_card *card; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (!g_audio || !g_audio->uac) 142062306a36Sopenharmony_ci return; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci uac = g_audio->uac; 142362306a36Sopenharmony_ci card = uac->card; 142462306a36Sopenharmony_ci if (card) 142562306a36Sopenharmony_ci snd_card_free_when_closed(card); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci kfree(uac->p_prm.reqs); 142862306a36Sopenharmony_ci kfree(uac->c_prm.reqs); 142962306a36Sopenharmony_ci kfree(uac->p_prm.rbuf); 143062306a36Sopenharmony_ci kfree(uac->c_prm.rbuf); 143162306a36Sopenharmony_ci kfree(uac); 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(g_audio_cleanup); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 143662306a36Sopenharmony_ciMODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities"); 143762306a36Sopenharmony_ciMODULE_AUTHOR("Ruslan Bilovol"); 1438