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