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 = &params->p_fu;
130502de698cSRuslan Bilovol 			direction = "Playback";
130602de698cSRuslan Bilovol 		} else {
130702de698cSRuslan Bilovol 			prm = &uac->c_prm;
130802de698cSRuslan Bilovol 			fu = &params->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