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