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 
15eb9fecb9SRuslan Bilovol #include <linux/module.h>
16eb9fecb9SRuslan Bilovol #include <sound/core.h>
17eb9fecb9SRuslan Bilovol #include <sound/pcm.h>
18eb9fecb9SRuslan Bilovol #include <sound/pcm_params.h>
19e89bb428SRuslan Bilovol #include <sound/control.h>
20eb9fecb9SRuslan Bilovol 
21eb9fecb9SRuslan Bilovol #include "u_audio.h"
22eb9fecb9SRuslan Bilovol 
23eb9fecb9SRuslan Bilovol #define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
24eb9fecb9SRuslan Bilovol #define PRD_SIZE_MAX	PAGE_SIZE
25eb9fecb9SRuslan Bilovol #define MIN_PERIODS	4
26eb9fecb9SRuslan Bilovol 
27eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */
28eb9fecb9SRuslan Bilovol struct uac_rtd_params {
29eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac; /* parent chip */
30eb9fecb9SRuslan Bilovol 	bool ep_enabled; /* if the ep is enabled */
31eb9fecb9SRuslan Bilovol 
32eb9fecb9SRuslan Bilovol 	struct snd_pcm_substream *ss;
33eb9fecb9SRuslan Bilovol 
34eb9fecb9SRuslan Bilovol 	/* Ring buffer */
35eb9fecb9SRuslan Bilovol 	ssize_t hw_ptr;
36eb9fecb9SRuslan Bilovol 
37eb9fecb9SRuslan Bilovol 	void *rbuf;
38eb9fecb9SRuslan Bilovol 
39e89bb428SRuslan Bilovol 	unsigned int pitch;	/* Stream pitch ratio to 1000000 */
40f4408a98SJonas Stenvall 	unsigned int max_psize;	/* MaxPacketSize of endpoint */
41eb9fecb9SRuslan Bilovol 
42d70f7598SJerome Brunet 	struct usb_request **reqs;
4324f779daSRuslan Bilovol 
4424f779daSRuslan Bilovol 	struct usb_request *req_fback; /* Feedback endpoint request */
4524f779daSRuslan Bilovol 	bool fb_ep_enabled; /* if the ep is enabled */
46eb9fecb9SRuslan Bilovol };
47eb9fecb9SRuslan Bilovol 
48eb9fecb9SRuslan Bilovol struct snd_uac_chip {
49eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
50eb9fecb9SRuslan Bilovol 
51eb9fecb9SRuslan Bilovol 	struct uac_rtd_params p_prm;
52eb9fecb9SRuslan Bilovol 	struct uac_rtd_params c_prm;
53eb9fecb9SRuslan Bilovol 
54eb9fecb9SRuslan Bilovol 	struct snd_card *card;
55eb9fecb9SRuslan Bilovol 	struct snd_pcm *pcm;
56eb9fecb9SRuslan Bilovol 
57eb9fecb9SRuslan Bilovol 	/* timekeeping for the playback endpoint */
58eb9fecb9SRuslan Bilovol 	unsigned int p_interval;
59eb9fecb9SRuslan Bilovol 	unsigned int p_residue;
60eb9fecb9SRuslan Bilovol 
61eb9fecb9SRuslan Bilovol 	/* pre-calculated values for playback iso completion */
62eb9fecb9SRuslan Bilovol 	unsigned int p_pktsize;
63eb9fecb9SRuslan Bilovol 	unsigned int p_pktsize_residue;
64eb9fecb9SRuslan Bilovol 	unsigned int p_framesize;
65eb9fecb9SRuslan Bilovol };
66eb9fecb9SRuslan Bilovol 
672ab3c34cSBhumika Goyal static const struct snd_pcm_hardware uac_pcm_hardware = {
68eb9fecb9SRuslan Bilovol 	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
69eb9fecb9SRuslan Bilovol 		 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
70eb9fecb9SRuslan Bilovol 		 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
71eb9fecb9SRuslan Bilovol 	.rates = SNDRV_PCM_RATE_CONTINUOUS,
72eb9fecb9SRuslan Bilovol 	.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
73eb9fecb9SRuslan Bilovol 	.buffer_bytes_max = BUFF_SIZE_MAX,
74eb9fecb9SRuslan Bilovol 	.period_bytes_max = PRD_SIZE_MAX,
75eb9fecb9SRuslan Bilovol 	.periods_min = MIN_PERIODS,
76eb9fecb9SRuslan Bilovol };
77eb9fecb9SRuslan Bilovol 
7824f779daSRuslan Bilovol static void u_audio_set_fback_frequency(enum usb_device_speed speed,
79e89bb428SRuslan Bilovol 					unsigned long long freq,
80e89bb428SRuslan Bilovol 					unsigned int pitch,
81e89bb428SRuslan Bilovol 					void *buf)
8224f779daSRuslan Bilovol {
8324f779daSRuslan Bilovol 	u32 ff = 0;
8424f779daSRuslan Bilovol 
85e89bb428SRuslan Bilovol 	/*
86e89bb428SRuslan Bilovol 	 * Because the pitch base is 1000000, the final divider here
87e89bb428SRuslan Bilovol 	 * will be 1000 * 1000000 = 1953125 << 9
88e89bb428SRuslan Bilovol 	 *
89e89bb428SRuslan Bilovol 	 * Instead of dealing with big numbers lets fold this 9 left shift
90e89bb428SRuslan Bilovol 	 */
91e89bb428SRuslan Bilovol 
9224f779daSRuslan Bilovol 	if (speed == USB_SPEED_FULL) {
9324f779daSRuslan Bilovol 		/*
9424f779daSRuslan Bilovol 		 * Full-speed feedback endpoints report frequency
95e89bb428SRuslan Bilovol 		 * in samples/frame
9624f779daSRuslan Bilovol 		 * Format is encoded in Q10.10 left-justified in the 24 bits,
9724f779daSRuslan Bilovol 		 * so that it has a Q10.14 format.
98e89bb428SRuslan Bilovol 		 *
99e89bb428SRuslan Bilovol 		 * ff = (freq << 14) / 1000
10024f779daSRuslan Bilovol 		 */
101e89bb428SRuslan Bilovol 		freq <<= 5;
10224f779daSRuslan Bilovol 	} else {
10324f779daSRuslan Bilovol 		/*
10424f779daSRuslan Bilovol 		 * High-speed feedback endpoints report frequency
10524f779daSRuslan Bilovol 		 * in samples/microframe.
10624f779daSRuslan Bilovol 		 * Format is encoded in Q12.13 fitted into four bytes so that
10724f779daSRuslan Bilovol 		 * the binary point is located between the second and the third
10824f779daSRuslan Bilovol 		 * byte fromat (that is Q16.16)
109e89bb428SRuslan Bilovol 		 *
110e89bb428SRuslan Bilovol 		 * ff = (freq << 16) / 8000
11124f779daSRuslan Bilovol 		 */
112e89bb428SRuslan Bilovol 		freq <<= 4;
11324f779daSRuslan Bilovol 	}
114e89bb428SRuslan Bilovol 
115e89bb428SRuslan Bilovol 	ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125);
116e89bb428SRuslan Bilovol 
11724f779daSRuslan Bilovol 	*(__le32 *)buf = cpu_to_le32(ff);
11824f779daSRuslan Bilovol }
11924f779daSRuslan Bilovol 
120eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
121eb9fecb9SRuslan Bilovol {
122f4408a98SJonas Stenvall 	unsigned int pending;
123eb9fecb9SRuslan Bilovol 	unsigned int hw_ptr;
124eb9fecb9SRuslan Bilovol 	int status = req->status;
125eb9fecb9SRuslan Bilovol 	struct snd_pcm_substream *substream;
12696afb54eSVladimir Zapolskiy 	struct snd_pcm_runtime *runtime;
12729865117SJerome Brunet 	struct uac_rtd_params *prm = req->context;
128eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
129eb9fecb9SRuslan Bilovol 
130eb9fecb9SRuslan Bilovol 	/* i/f shutting down */
1317de8681bSJack Pham 	if (!prm->ep_enabled) {
1327de8681bSJack Pham 		usb_ep_free_request(ep, req);
1337de8681bSJack Pham 		return;
1347de8681bSJack Pham 	}
1357de8681bSJack Pham 
1367de8681bSJack Pham 	if (req->status == -ESHUTDOWN)
137eb9fecb9SRuslan Bilovol 		return;
138eb9fecb9SRuslan Bilovol 
139eb9fecb9SRuslan Bilovol 	/*
140eb9fecb9SRuslan Bilovol 	 * We can't really do much about bad xfers.
141eb9fecb9SRuslan Bilovol 	 * Afterall, the ISOCH xfers could fail legitimately.
142eb9fecb9SRuslan Bilovol 	 */
143eb9fecb9SRuslan Bilovol 	if (status)
144eb9fecb9SRuslan Bilovol 		pr_debug("%s: iso_complete status(%d) %d/%d\n",
145eb9fecb9SRuslan Bilovol 			__func__, status, req->actual, req->length);
146eb9fecb9SRuslan Bilovol 
147eb9fecb9SRuslan Bilovol 	substream = prm->ss;
148eb9fecb9SRuslan Bilovol 
149eb9fecb9SRuslan Bilovol 	/* Do nothing if ALSA isn't active */
150eb9fecb9SRuslan Bilovol 	if (!substream)
151eb9fecb9SRuslan Bilovol 		goto exit;
152eb9fecb9SRuslan Bilovol 
153d70f7598SJerome Brunet 	snd_pcm_stream_lock(substream);
15456bc6158SVladimir Zapolskiy 
15596afb54eSVladimir Zapolskiy 	runtime = substream->runtime;
15656bc6158SVladimir Zapolskiy 	if (!runtime || !snd_pcm_running(substream)) {
157d70f7598SJerome Brunet 		snd_pcm_stream_unlock(substream);
15856bc6158SVladimir Zapolskiy 		goto exit;
15956bc6158SVladimir Zapolskiy 	}
16056bc6158SVladimir Zapolskiy 
161eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
162eb9fecb9SRuslan Bilovol 		/*
163eb9fecb9SRuslan Bilovol 		 * For each IN packet, take the quotient of the current data
164eb9fecb9SRuslan Bilovol 		 * rate and the endpoint's interval as the base packet size.
165eb9fecb9SRuslan Bilovol 		 * If there is a residue from this division, add it to the
166eb9fecb9SRuslan Bilovol 		 * residue accumulator.
167eb9fecb9SRuslan Bilovol 		 */
168eb9fecb9SRuslan Bilovol 		req->length = uac->p_pktsize;
169eb9fecb9SRuslan Bilovol 		uac->p_residue += uac->p_pktsize_residue;
170eb9fecb9SRuslan Bilovol 
171eb9fecb9SRuslan Bilovol 		/*
172eb9fecb9SRuslan Bilovol 		 * Whenever there are more bytes in the accumulator than we
173eb9fecb9SRuslan Bilovol 		 * need to add one more sample frame, increase this packet's
174eb9fecb9SRuslan Bilovol 		 * size and decrease the accumulator.
175eb9fecb9SRuslan Bilovol 		 */
176eb9fecb9SRuslan Bilovol 		if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
177eb9fecb9SRuslan Bilovol 			req->length += uac->p_framesize;
178eb9fecb9SRuslan Bilovol 			uac->p_residue -= uac->p_framesize *
179eb9fecb9SRuslan Bilovol 					   uac->p_interval;
180eb9fecb9SRuslan Bilovol 		}
181eb9fecb9SRuslan Bilovol 
182eb9fecb9SRuslan Bilovol 		req->actual = req->length;
183eb9fecb9SRuslan Bilovol 	}
184eb9fecb9SRuslan Bilovol 
185eb9fecb9SRuslan Bilovol 	hw_ptr = prm->hw_ptr;
186eb9fecb9SRuslan Bilovol 
187eb9fecb9SRuslan Bilovol 	/* Pack USB load in ALSA ring buffer */
18896afb54eSVladimir Zapolskiy 	pending = runtime->dma_bytes - hw_ptr;
189eb9fecb9SRuslan Bilovol 
190eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
191eb9fecb9SRuslan Bilovol 		if (unlikely(pending < req->actual)) {
19296afb54eSVladimir Zapolskiy 			memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
19396afb54eSVladimir Zapolskiy 			memcpy(req->buf + pending, runtime->dma_area,
194eb9fecb9SRuslan Bilovol 			       req->actual - pending);
195eb9fecb9SRuslan Bilovol 		} else {
19696afb54eSVladimir Zapolskiy 			memcpy(req->buf, runtime->dma_area + hw_ptr,
19796afb54eSVladimir Zapolskiy 			       req->actual);
198eb9fecb9SRuslan Bilovol 		}
199eb9fecb9SRuslan Bilovol 	} else {
200eb9fecb9SRuslan Bilovol 		if (unlikely(pending < req->actual)) {
20196afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
20296afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area, req->buf + pending,
203eb9fecb9SRuslan Bilovol 			       req->actual - pending);
204eb9fecb9SRuslan Bilovol 		} else {
20596afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area + hw_ptr, req->buf,
20696afb54eSVladimir Zapolskiy 			       req->actual);
207eb9fecb9SRuslan Bilovol 		}
208eb9fecb9SRuslan Bilovol 	}
209eb9fecb9SRuslan Bilovol 
2106b37bd78SJoshua Frkuska 	/* update hw_ptr after data is copied to memory */
21196afb54eSVladimir Zapolskiy 	prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
212773e53d5SVladimir Zapolskiy 	hw_ptr = prm->hw_ptr;
213d70f7598SJerome Brunet 	snd_pcm_stream_unlock(substream);
2146b37bd78SJoshua Frkuska 
215773e53d5SVladimir Zapolskiy 	if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
216773e53d5SVladimir Zapolskiy 		snd_pcm_period_elapsed(substream);
217773e53d5SVladimir Zapolskiy 
218eb9fecb9SRuslan Bilovol exit:
219eb9fecb9SRuslan Bilovol 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
220eb9fecb9SRuslan Bilovol 		dev_err(uac->card->dev, "%d Error!\n", __LINE__);
221eb9fecb9SRuslan Bilovol }
222eb9fecb9SRuslan Bilovol 
22324f779daSRuslan Bilovol static void u_audio_iso_fback_complete(struct usb_ep *ep,
22424f779daSRuslan Bilovol 				       struct usb_request *req)
22524f779daSRuslan Bilovol {
22624f779daSRuslan Bilovol 	struct uac_rtd_params *prm = req->context;
22724f779daSRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
22824f779daSRuslan Bilovol 	struct g_audio *audio_dev = uac->audio_dev;
229e89bb428SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
23024f779daSRuslan Bilovol 	int status = req->status;
23124f779daSRuslan Bilovol 
23224f779daSRuslan Bilovol 	/* i/f shutting down */
233*75432ba5SJerome Brunet 	if (!prm->fb_ep_enabled) {
234*75432ba5SJerome Brunet 		kfree(req->buf);
235*75432ba5SJerome Brunet 		usb_ep_free_request(ep, req);
236*75432ba5SJerome Brunet 		return;
237*75432ba5SJerome Brunet 	}
238*75432ba5SJerome Brunet 
239*75432ba5SJerome Brunet 	if (req->status == -ESHUTDOWN)
24024f779daSRuslan Bilovol 		return;
24124f779daSRuslan Bilovol 
24224f779daSRuslan Bilovol 	/*
24324f779daSRuslan Bilovol 	 * We can't really do much about bad xfers.
24424f779daSRuslan Bilovol 	 * Afterall, the ISOCH xfers could fail legitimately.
24524f779daSRuslan Bilovol 	 */
24624f779daSRuslan Bilovol 	if (status)
24724f779daSRuslan Bilovol 		pr_debug("%s: iso_complete status(%d) %d/%d\n",
24824f779daSRuslan Bilovol 			__func__, status, req->actual, req->length);
24924f779daSRuslan Bilovol 
25024f779daSRuslan Bilovol 	u_audio_set_fback_frequency(audio_dev->gadget->speed,
251e89bb428SRuslan Bilovol 				    params->c_srate, prm->pitch,
252e89bb428SRuslan Bilovol 				    req->buf);
25324f779daSRuslan Bilovol 
25424f779daSRuslan Bilovol 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
25524f779daSRuslan Bilovol 		dev_err(uac->card->dev, "%d Error!\n", __LINE__);
25624f779daSRuslan Bilovol }
25724f779daSRuslan Bilovol 
258eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
259eb9fecb9SRuslan Bilovol {
260eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
261eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
262eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
263eb9fecb9SRuslan Bilovol 	struct uac_params *params;
264eb9fecb9SRuslan Bilovol 	int err = 0;
265eb9fecb9SRuslan Bilovol 
266eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
267eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
268eb9fecb9SRuslan Bilovol 
269eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
270eb9fecb9SRuslan Bilovol 		prm = &uac->p_prm;
271eb9fecb9SRuslan Bilovol 	else
272eb9fecb9SRuslan Bilovol 		prm = &uac->c_prm;
273eb9fecb9SRuslan Bilovol 
274eb9fecb9SRuslan Bilovol 	/* Reset */
275eb9fecb9SRuslan Bilovol 	prm->hw_ptr = 0;
276eb9fecb9SRuslan Bilovol 
277eb9fecb9SRuslan Bilovol 	switch (cmd) {
278eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_START:
279eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_RESUME:
280eb9fecb9SRuslan Bilovol 		prm->ss = substream;
281eb9fecb9SRuslan Bilovol 		break;
282eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_STOP:
283eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_SUSPEND:
284eb9fecb9SRuslan Bilovol 		prm->ss = NULL;
285eb9fecb9SRuslan Bilovol 		break;
286eb9fecb9SRuslan Bilovol 	default:
287eb9fecb9SRuslan Bilovol 		err = -EINVAL;
288eb9fecb9SRuslan Bilovol 	}
289eb9fecb9SRuslan Bilovol 
290eb9fecb9SRuslan Bilovol 	/* Clear buffer after Play stops */
291eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
292eb9fecb9SRuslan Bilovol 		memset(prm->rbuf, 0, prm->max_psize * params->req_number);
293eb9fecb9SRuslan Bilovol 
294eb9fecb9SRuslan Bilovol 	return err;
295eb9fecb9SRuslan Bilovol }
296eb9fecb9SRuslan Bilovol 
297eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
298eb9fecb9SRuslan Bilovol {
299eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
300eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
301eb9fecb9SRuslan Bilovol 
302eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
303eb9fecb9SRuslan Bilovol 		prm = &uac->p_prm;
304eb9fecb9SRuslan Bilovol 	else
305eb9fecb9SRuslan Bilovol 		prm = &uac->c_prm;
306eb9fecb9SRuslan Bilovol 
307eb9fecb9SRuslan Bilovol 	return bytes_to_frames(substream->runtime, prm->hw_ptr);
308eb9fecb9SRuslan Bilovol }
309eb9fecb9SRuslan Bilovol 
31025dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize)
31125dbd75dSJerome Brunet {
31225dbd75dSJerome Brunet 	u64 ret;
31325dbd75dSJerome Brunet 
31425dbd75dSJerome Brunet 	switch (ssize) {
31525dbd75dSJerome Brunet 	case 3:
31625dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S24_3LE;
31725dbd75dSJerome Brunet 		break;
31825dbd75dSJerome Brunet 	case 4:
31925dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S32_LE;
32025dbd75dSJerome Brunet 		break;
32125dbd75dSJerome Brunet 	default:
32225dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S16_LE;
32325dbd75dSJerome Brunet 		break;
32425dbd75dSJerome Brunet 	}
32525dbd75dSJerome Brunet 
32625dbd75dSJerome Brunet 	return ret;
32725dbd75dSJerome Brunet }
32825dbd75dSJerome Brunet 
329eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream)
330eb9fecb9SRuslan Bilovol {
331eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
332eb9fecb9SRuslan Bilovol 	struct snd_pcm_runtime *runtime = substream->runtime;
333eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
334eb9fecb9SRuslan Bilovol 	struct uac_params *params;
335eb9fecb9SRuslan Bilovol 	int p_ssize, c_ssize;
336eb9fecb9SRuslan Bilovol 	int p_srate, c_srate;
337eb9fecb9SRuslan Bilovol 	int p_chmask, c_chmask;
338eb9fecb9SRuslan Bilovol 
339eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
340eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
341eb9fecb9SRuslan Bilovol 	p_ssize = params->p_ssize;
342eb9fecb9SRuslan Bilovol 	c_ssize = params->c_ssize;
343eb9fecb9SRuslan Bilovol 	p_srate = params->p_srate;
344eb9fecb9SRuslan Bilovol 	c_srate = params->c_srate;
345eb9fecb9SRuslan Bilovol 	p_chmask = params->p_chmask;
346eb9fecb9SRuslan Bilovol 	c_chmask = params->c_chmask;
347eb9fecb9SRuslan Bilovol 	uac->p_residue = 0;
348eb9fecb9SRuslan Bilovol 
349eb9fecb9SRuslan Bilovol 	runtime->hw = uac_pcm_hardware;
350eb9fecb9SRuslan Bilovol 
351eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
352eb9fecb9SRuslan Bilovol 		runtime->hw.rate_min = p_srate;
35325dbd75dSJerome Brunet 		runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
354eb9fecb9SRuslan Bilovol 		runtime->hw.channels_min = num_channels(p_chmask);
355eb9fecb9SRuslan Bilovol 		runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
356eb9fecb9SRuslan Bilovol 						/ runtime->hw.periods_min;
357eb9fecb9SRuslan Bilovol 	} else {
358eb9fecb9SRuslan Bilovol 		runtime->hw.rate_min = c_srate;
35925dbd75dSJerome Brunet 		runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
360eb9fecb9SRuslan Bilovol 		runtime->hw.channels_min = num_channels(c_chmask);
361eb9fecb9SRuslan Bilovol 		runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
362eb9fecb9SRuslan Bilovol 						/ runtime->hw.periods_min;
363eb9fecb9SRuslan Bilovol 	}
364eb9fecb9SRuslan Bilovol 
365eb9fecb9SRuslan Bilovol 	runtime->hw.rate_max = runtime->hw.rate_min;
366eb9fecb9SRuslan Bilovol 	runtime->hw.channels_max = runtime->hw.channels_min;
367eb9fecb9SRuslan Bilovol 
368eb9fecb9SRuslan Bilovol 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
369eb9fecb9SRuslan Bilovol 
370eb9fecb9SRuslan Bilovol 	return 0;
371eb9fecb9SRuslan Bilovol }
372eb9fecb9SRuslan Bilovol 
373eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */
374eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream)
375eb9fecb9SRuslan Bilovol {
376eb9fecb9SRuslan Bilovol 	return 0;
377eb9fecb9SRuslan Bilovol }
378eb9fecb9SRuslan Bilovol 
379640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = {
380eb9fecb9SRuslan Bilovol 	.open = uac_pcm_open,
381eb9fecb9SRuslan Bilovol 	.close = uac_pcm_null,
382eb9fecb9SRuslan Bilovol 	.trigger = uac_pcm_trigger,
383eb9fecb9SRuslan Bilovol 	.pointer = uac_pcm_pointer,
384eb9fecb9SRuslan Bilovol 	.prepare = uac_pcm_null,
385eb9fecb9SRuslan Bilovol };
386eb9fecb9SRuslan Bilovol 
387eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
388eb9fecb9SRuslan Bilovol {
389eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
390eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
391eb9fecb9SRuslan Bilovol 	struct uac_params *params;
392eb9fecb9SRuslan Bilovol 	int i;
393eb9fecb9SRuslan Bilovol 
394eb9fecb9SRuslan Bilovol 	if (!prm->ep_enabled)
395eb9fecb9SRuslan Bilovol 		return;
396eb9fecb9SRuslan Bilovol 
397eb9fecb9SRuslan Bilovol 	prm->ep_enabled = false;
398eb9fecb9SRuslan Bilovol 
399eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
400eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
401eb9fecb9SRuslan Bilovol 
402eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
40329865117SJerome Brunet 		if (prm->reqs[i]) {
40429865117SJerome Brunet 			if (usb_ep_dequeue(ep, prm->reqs[i]))
40529865117SJerome Brunet 				usb_ep_free_request(ep, prm->reqs[i]);
4067de8681bSJack Pham 			/*
4077de8681bSJack Pham 			 * If usb_ep_dequeue() cannot successfully dequeue the
4087de8681bSJack Pham 			 * request, the request will be freed by the completion
4097de8681bSJack Pham 			 * callback.
4107de8681bSJack Pham 			 */
4117de8681bSJack Pham 
41229865117SJerome Brunet 			prm->reqs[i] = NULL;
413eb9fecb9SRuslan Bilovol 		}
414eb9fecb9SRuslan Bilovol 	}
415eb9fecb9SRuslan Bilovol 
416eb9fecb9SRuslan Bilovol 	if (usb_ep_disable(ep))
417eb9fecb9SRuslan Bilovol 		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
418eb9fecb9SRuslan Bilovol }
419eb9fecb9SRuslan Bilovol 
42024f779daSRuslan Bilovol static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
42124f779daSRuslan Bilovol {
42224f779daSRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
42324f779daSRuslan Bilovol 
42424f779daSRuslan Bilovol 	if (!prm->fb_ep_enabled)
42524f779daSRuslan Bilovol 		return;
42624f779daSRuslan Bilovol 
42724f779daSRuslan Bilovol 	prm->fb_ep_enabled = false;
42824f779daSRuslan Bilovol 
42924f779daSRuslan Bilovol 	if (prm->req_fback) {
430*75432ba5SJerome Brunet 		if (usb_ep_dequeue(ep, prm->req_fback)) {
43124f779daSRuslan Bilovol 			kfree(prm->req_fback->buf);
43224f779daSRuslan Bilovol 			usb_ep_free_request(ep, prm->req_fback);
433*75432ba5SJerome Brunet 		}
43424f779daSRuslan Bilovol 		prm->req_fback = NULL;
43524f779daSRuslan Bilovol 	}
43624f779daSRuslan Bilovol 
43724f779daSRuslan Bilovol 	if (usb_ep_disable(ep))
43824f779daSRuslan Bilovol 		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
43924f779daSRuslan Bilovol }
440eb9fecb9SRuslan Bilovol 
441eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev)
442eb9fecb9SRuslan Bilovol {
443eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
444eb9fecb9SRuslan Bilovol 	struct usb_gadget *gadget = audio_dev->gadget;
445eb9fecb9SRuslan Bilovol 	struct device *dev = &gadget->dev;
44624f779daSRuslan Bilovol 	struct usb_request *req, *req_fback;
44724f779daSRuslan Bilovol 	struct usb_ep *ep, *ep_fback;
448eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
449eb9fecb9SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
450eb9fecb9SRuslan Bilovol 	int req_len, i;
451eb9fecb9SRuslan Bilovol 
452eb9fecb9SRuslan Bilovol 	ep = audio_dev->out_ep;
453eb9fecb9SRuslan Bilovol 	prm = &uac->c_prm;
454eb9fecb9SRuslan Bilovol 	config_ep_by_speed(gadget, &audio_dev->func, ep);
455904967c6SJohn Keeping 	req_len = ep->maxpacket;
456eb9fecb9SRuslan Bilovol 
457eb9fecb9SRuslan Bilovol 	prm->ep_enabled = true;
458eb9fecb9SRuslan Bilovol 	usb_ep_enable(ep);
459eb9fecb9SRuslan Bilovol 
460eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
46129865117SJerome Brunet 		if (!prm->reqs[i]) {
462eb9fecb9SRuslan Bilovol 			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
463eb9fecb9SRuslan Bilovol 			if (req == NULL)
464eb9fecb9SRuslan Bilovol 				return -ENOMEM;
465eb9fecb9SRuslan Bilovol 
46629865117SJerome Brunet 			prm->reqs[i] = req;
467eb9fecb9SRuslan Bilovol 
468eb9fecb9SRuslan Bilovol 			req->zero = 0;
46929865117SJerome Brunet 			req->context = prm;
470eb9fecb9SRuslan Bilovol 			req->length = req_len;
471eb9fecb9SRuslan Bilovol 			req->complete = u_audio_iso_complete;
472904967c6SJohn Keeping 			req->buf = prm->rbuf + i * ep->maxpacket;
473eb9fecb9SRuslan Bilovol 		}
474eb9fecb9SRuslan Bilovol 
47529865117SJerome Brunet 		if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))
476eb9fecb9SRuslan Bilovol 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
477eb9fecb9SRuslan Bilovol 	}
478eb9fecb9SRuslan Bilovol 
47924f779daSRuslan Bilovol 	ep_fback = audio_dev->in_ep_fback;
48024f779daSRuslan Bilovol 	if (!ep_fback)
48124f779daSRuslan Bilovol 		return 0;
48224f779daSRuslan Bilovol 
48324f779daSRuslan Bilovol 	/* Setup feedback endpoint */
48424f779daSRuslan Bilovol 	config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
48524f779daSRuslan Bilovol 	prm->fb_ep_enabled = true;
48624f779daSRuslan Bilovol 	usb_ep_enable(ep_fback);
48724f779daSRuslan Bilovol 	req_len = ep_fback->maxpacket;
48824f779daSRuslan Bilovol 
48924f779daSRuslan Bilovol 	req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC);
49024f779daSRuslan Bilovol 	if (req_fback == NULL)
49124f779daSRuslan Bilovol 		return -ENOMEM;
49224f779daSRuslan Bilovol 
49324f779daSRuslan Bilovol 	prm->req_fback = req_fback;
49424f779daSRuslan Bilovol 	req_fback->zero = 0;
49524f779daSRuslan Bilovol 	req_fback->context = prm;
49624f779daSRuslan Bilovol 	req_fback->length = req_len;
49724f779daSRuslan Bilovol 	req_fback->complete = u_audio_iso_fback_complete;
49824f779daSRuslan Bilovol 
49924f779daSRuslan Bilovol 	req_fback->buf = kzalloc(req_len, GFP_ATOMIC);
50024f779daSRuslan Bilovol 	if (!req_fback->buf)
50124f779daSRuslan Bilovol 		return -ENOMEM;
50224f779daSRuslan Bilovol 
50324f779daSRuslan Bilovol 	/*
50424f779daSRuslan Bilovol 	 * Configure the feedback endpoint's reported frequency.
50524f779daSRuslan Bilovol 	 * Always start with original frequency since its deviation can't
50624f779daSRuslan Bilovol 	 * be meauserd at start of playback
50724f779daSRuslan Bilovol 	 */
508e89bb428SRuslan Bilovol 	prm->pitch = 1000000;
50924f779daSRuslan Bilovol 	u_audio_set_fback_frequency(audio_dev->gadget->speed,
510e89bb428SRuslan Bilovol 				    params->c_srate, prm->pitch,
511e89bb428SRuslan Bilovol 				    req_fback->buf);
51224f779daSRuslan Bilovol 
51324f779daSRuslan Bilovol 	if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
51424f779daSRuslan Bilovol 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
51524f779daSRuslan Bilovol 
516eb9fecb9SRuslan Bilovol 	return 0;
517eb9fecb9SRuslan Bilovol }
518eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture);
519eb9fecb9SRuslan Bilovol 
520eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev)
521eb9fecb9SRuslan Bilovol {
522eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
523eb9fecb9SRuslan Bilovol 
52424f779daSRuslan Bilovol 	if (audio_dev->in_ep_fback)
52524f779daSRuslan Bilovol 		free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
526eb9fecb9SRuslan Bilovol 	free_ep(&uac->c_prm, audio_dev->out_ep);
527eb9fecb9SRuslan Bilovol }
528eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture);
529eb9fecb9SRuslan Bilovol 
530eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev)
531eb9fecb9SRuslan Bilovol {
532eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
533eb9fecb9SRuslan Bilovol 	struct usb_gadget *gadget = audio_dev->gadget;
534eb9fecb9SRuslan Bilovol 	struct device *dev = &gadget->dev;
535eb9fecb9SRuslan Bilovol 	struct usb_request *req;
536eb9fecb9SRuslan Bilovol 	struct usb_ep *ep;
537eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
538eb9fecb9SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
5396b02af34SJohn Keeping 	unsigned int factor;
540eb9fecb9SRuslan Bilovol 	const struct usb_endpoint_descriptor *ep_desc;
541eb9fecb9SRuslan Bilovol 	int req_len, i;
542eb9fecb9SRuslan Bilovol 
543eb9fecb9SRuslan Bilovol 	ep = audio_dev->in_ep;
544eb9fecb9SRuslan Bilovol 	prm = &uac->p_prm;
545eb9fecb9SRuslan Bilovol 	config_ep_by_speed(gadget, &audio_dev->func, ep);
546eb9fecb9SRuslan Bilovol 
547eb9fecb9SRuslan Bilovol 	ep_desc = ep->desc;
548eb9fecb9SRuslan Bilovol 
549eb9fecb9SRuslan Bilovol 	/* pre-calculate the playback endpoint's interval */
550eb9fecb9SRuslan Bilovol 	if (gadget->speed == USB_SPEED_FULL)
551eb9fecb9SRuslan Bilovol 		factor = 1000;
552eb9fecb9SRuslan Bilovol 	else
553eb9fecb9SRuslan Bilovol 		factor = 8000;
554eb9fecb9SRuslan Bilovol 
555eb9fecb9SRuslan Bilovol 	/* pre-compute some values for iso_complete() */
556eb9fecb9SRuslan Bilovol 	uac->p_framesize = params->p_ssize *
557eb9fecb9SRuslan Bilovol 			    num_channels(params->p_chmask);
558eb9fecb9SRuslan Bilovol 	uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
5596b02af34SJohn Keeping 	uac->p_pktsize = min_t(unsigned int,
5606b02af34SJohn Keeping 				uac->p_framesize *
5616b02af34SJohn Keeping 					(params->p_srate / uac->p_interval),
562904967c6SJohn Keeping 				ep->maxpacket);
563eb9fecb9SRuslan Bilovol 
564904967c6SJohn Keeping 	if (uac->p_pktsize < ep->maxpacket)
5656b02af34SJohn Keeping 		uac->p_pktsize_residue = uac->p_framesize *
5666b02af34SJohn Keeping 			(params->p_srate % uac->p_interval);
567eb9fecb9SRuslan Bilovol 	else
568eb9fecb9SRuslan Bilovol 		uac->p_pktsize_residue = 0;
569eb9fecb9SRuslan Bilovol 
570eb9fecb9SRuslan Bilovol 	req_len = uac->p_pktsize;
571eb9fecb9SRuslan Bilovol 	uac->p_residue = 0;
572eb9fecb9SRuslan Bilovol 
573eb9fecb9SRuslan Bilovol 	prm->ep_enabled = true;
574eb9fecb9SRuslan Bilovol 	usb_ep_enable(ep);
575eb9fecb9SRuslan Bilovol 
576eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
57729865117SJerome Brunet 		if (!prm->reqs[i]) {
578eb9fecb9SRuslan Bilovol 			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
579eb9fecb9SRuslan Bilovol 			if (req == NULL)
580eb9fecb9SRuslan Bilovol 				return -ENOMEM;
581eb9fecb9SRuslan Bilovol 
58229865117SJerome Brunet 			prm->reqs[i] = req;
583eb9fecb9SRuslan Bilovol 
584eb9fecb9SRuslan Bilovol 			req->zero = 0;
58529865117SJerome Brunet 			req->context = prm;
586eb9fecb9SRuslan Bilovol 			req->length = req_len;
587eb9fecb9SRuslan Bilovol 			req->complete = u_audio_iso_complete;
588904967c6SJohn Keeping 			req->buf = prm->rbuf + i * ep->maxpacket;
589eb9fecb9SRuslan Bilovol 		}
590eb9fecb9SRuslan Bilovol 
59129865117SJerome Brunet 		if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))
592eb9fecb9SRuslan Bilovol 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
593eb9fecb9SRuslan Bilovol 	}
594eb9fecb9SRuslan Bilovol 
595eb9fecb9SRuslan Bilovol 	return 0;
596eb9fecb9SRuslan Bilovol }
597eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback);
598eb9fecb9SRuslan Bilovol 
599eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev)
600eb9fecb9SRuslan Bilovol {
601eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
602eb9fecb9SRuslan Bilovol 
603eb9fecb9SRuslan Bilovol 	free_ep(&uac->p_prm, audio_dev->in_ep);
604eb9fecb9SRuslan Bilovol }
605eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback);
606eb9fecb9SRuslan Bilovol 
607e89bb428SRuslan Bilovol static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
608e89bb428SRuslan Bilovol 				   struct snd_ctl_elem_info *uinfo)
609e89bb428SRuslan Bilovol {
610e89bb428SRuslan Bilovol 	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
611e89bb428SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
612e89bb428SRuslan Bilovol 	struct g_audio *audio_dev = uac->audio_dev;
613e89bb428SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
614e89bb428SRuslan Bilovol 	unsigned int pitch_min, pitch_max;
615e89bb428SRuslan Bilovol 
616e89bb428SRuslan Bilovol 	pitch_min = (1000 - FBACK_SLOW_MAX) * 1000;
617e89bb428SRuslan Bilovol 	pitch_max = (1000 + params->fb_max) * 1000;
618e89bb428SRuslan Bilovol 
619e89bb428SRuslan Bilovol 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
620e89bb428SRuslan Bilovol 	uinfo->count = 1;
621e89bb428SRuslan Bilovol 	uinfo->value.integer.min = pitch_min;
622e89bb428SRuslan Bilovol 	uinfo->value.integer.max = pitch_max;
623e89bb428SRuslan Bilovol 	uinfo->value.integer.step = 1;
624e89bb428SRuslan Bilovol 	return 0;
625e89bb428SRuslan Bilovol }
626e89bb428SRuslan Bilovol 
627e89bb428SRuslan Bilovol static int u_audio_pitch_get(struct snd_kcontrol *kcontrol,
628e89bb428SRuslan Bilovol 				   struct snd_ctl_elem_value *ucontrol)
629e89bb428SRuslan Bilovol {
630e89bb428SRuslan Bilovol 	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
631e89bb428SRuslan Bilovol 
632e89bb428SRuslan Bilovol 	ucontrol->value.integer.value[0] = prm->pitch;
633e89bb428SRuslan Bilovol 
634e89bb428SRuslan Bilovol 	return 0;
635e89bb428SRuslan Bilovol }
636e89bb428SRuslan Bilovol 
637e89bb428SRuslan Bilovol static int u_audio_pitch_put(struct snd_kcontrol *kcontrol,
638e89bb428SRuslan Bilovol 				  struct snd_ctl_elem_value *ucontrol)
639e89bb428SRuslan Bilovol {
640e89bb428SRuslan Bilovol 	struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
641e89bb428SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
642e89bb428SRuslan Bilovol 	struct g_audio *audio_dev = uac->audio_dev;
643e89bb428SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
644e89bb428SRuslan Bilovol 	unsigned int val;
645e89bb428SRuslan Bilovol 	unsigned int pitch_min, pitch_max;
646e89bb428SRuslan Bilovol 	int change = 0;
647e89bb428SRuslan Bilovol 
648e89bb428SRuslan Bilovol 	pitch_min = (1000 - FBACK_SLOW_MAX) * 1000;
649e89bb428SRuslan Bilovol 	pitch_max = (1000 + params->fb_max) * 1000;
650e89bb428SRuslan Bilovol 
651e89bb428SRuslan Bilovol 	val = ucontrol->value.integer.value[0];
652e89bb428SRuslan Bilovol 
653e89bb428SRuslan Bilovol 	if (val < pitch_min)
654e89bb428SRuslan Bilovol 		val = pitch_min;
655e89bb428SRuslan Bilovol 	if (val > pitch_max)
656e89bb428SRuslan Bilovol 		val = pitch_max;
657e89bb428SRuslan Bilovol 
658e89bb428SRuslan Bilovol 	if (prm->pitch != val) {
659e89bb428SRuslan Bilovol 		prm->pitch = val;
660e89bb428SRuslan Bilovol 		change = 1;
661e89bb428SRuslan Bilovol 	}
662e89bb428SRuslan Bilovol 
663e89bb428SRuslan Bilovol 	return change;
664e89bb428SRuslan Bilovol }
665e89bb428SRuslan Bilovol 
666e89bb428SRuslan Bilovol static const struct snd_kcontrol_new u_audio_controls[]  = {
667e89bb428SRuslan Bilovol {
668e89bb428SRuslan Bilovol 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
669e89bb428SRuslan Bilovol 	.name =         "Capture Pitch 1000000",
670e89bb428SRuslan Bilovol 	.info =         u_audio_pitch_info,
671e89bb428SRuslan Bilovol 	.get =          u_audio_pitch_get,
672e89bb428SRuslan Bilovol 	.put =          u_audio_pitch_put,
673e89bb428SRuslan Bilovol },
674e89bb428SRuslan Bilovol };
675e89bb428SRuslan Bilovol 
676eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
677eb9fecb9SRuslan Bilovol 					const char *card_name)
678eb9fecb9SRuslan Bilovol {
679eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac;
680eb9fecb9SRuslan Bilovol 	struct snd_card *card;
681eb9fecb9SRuslan Bilovol 	struct snd_pcm *pcm;
682e89bb428SRuslan Bilovol 	struct snd_kcontrol *kctl;
683eb9fecb9SRuslan Bilovol 	struct uac_params *params;
684eb9fecb9SRuslan Bilovol 	int p_chmask, c_chmask;
685eb9fecb9SRuslan Bilovol 	int err;
686eb9fecb9SRuslan Bilovol 
687eb9fecb9SRuslan Bilovol 	if (!g_audio)
688eb9fecb9SRuslan Bilovol 		return -EINVAL;
689eb9fecb9SRuslan Bilovol 
690eb9fecb9SRuslan Bilovol 	uac = kzalloc(sizeof(*uac), GFP_KERNEL);
691eb9fecb9SRuslan Bilovol 	if (!uac)
692eb9fecb9SRuslan Bilovol 		return -ENOMEM;
693eb9fecb9SRuslan Bilovol 	g_audio->uac = uac;
694eb9fecb9SRuslan Bilovol 	uac->audio_dev = g_audio;
695eb9fecb9SRuslan Bilovol 
696eb9fecb9SRuslan Bilovol 	params = &g_audio->params;
697eb9fecb9SRuslan Bilovol 	p_chmask = params->p_chmask;
698eb9fecb9SRuslan Bilovol 	c_chmask = params->c_chmask;
699eb9fecb9SRuslan Bilovol 
700eb9fecb9SRuslan Bilovol 	if (c_chmask) {
701eb9fecb9SRuslan Bilovol 		struct uac_rtd_params *prm = &uac->c_prm;
702eb9fecb9SRuslan Bilovol 
703eb9fecb9SRuslan Bilovol 		uac->c_prm.uac = uac;
704eb9fecb9SRuslan Bilovol 		prm->max_psize = g_audio->out_ep_maxpsize;
705eb9fecb9SRuslan Bilovol 
70629865117SJerome Brunet 		prm->reqs = kcalloc(params->req_number,
70729865117SJerome Brunet 				    sizeof(struct usb_request *),
708eb9fecb9SRuslan Bilovol 				    GFP_KERNEL);
70929865117SJerome Brunet 		if (!prm->reqs) {
710eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
711eb9fecb9SRuslan Bilovol 			goto fail;
712eb9fecb9SRuslan Bilovol 		}
713eb9fecb9SRuslan Bilovol 
714eb9fecb9SRuslan Bilovol 		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
715eb9fecb9SRuslan Bilovol 				GFP_KERNEL);
716eb9fecb9SRuslan Bilovol 		if (!prm->rbuf) {
717eb9fecb9SRuslan Bilovol 			prm->max_psize = 0;
718eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
719eb9fecb9SRuslan Bilovol 			goto fail;
720eb9fecb9SRuslan Bilovol 		}
721eb9fecb9SRuslan Bilovol 	}
722eb9fecb9SRuslan Bilovol 
723eb9fecb9SRuslan Bilovol 	if (p_chmask) {
724eb9fecb9SRuslan Bilovol 		struct uac_rtd_params *prm = &uac->p_prm;
725eb9fecb9SRuslan Bilovol 
726eb9fecb9SRuslan Bilovol 		uac->p_prm.uac = uac;
727eb9fecb9SRuslan Bilovol 		prm->max_psize = g_audio->in_ep_maxpsize;
728eb9fecb9SRuslan Bilovol 
72929865117SJerome Brunet 		prm->reqs = kcalloc(params->req_number,
73029865117SJerome Brunet 				    sizeof(struct usb_request *),
731eb9fecb9SRuslan Bilovol 				    GFP_KERNEL);
73229865117SJerome Brunet 		if (!prm->reqs) {
733eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
734eb9fecb9SRuslan Bilovol 			goto fail;
735eb9fecb9SRuslan Bilovol 		}
736eb9fecb9SRuslan Bilovol 
737eb9fecb9SRuslan Bilovol 		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
738eb9fecb9SRuslan Bilovol 				GFP_KERNEL);
739eb9fecb9SRuslan Bilovol 		if (!prm->rbuf) {
740eb9fecb9SRuslan Bilovol 			prm->max_psize = 0;
741eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
742eb9fecb9SRuslan Bilovol 			goto fail;
743eb9fecb9SRuslan Bilovol 		}
744eb9fecb9SRuslan Bilovol 	}
745eb9fecb9SRuslan Bilovol 
746eb9fecb9SRuslan Bilovol 	/* Choose any slot, with no id */
747eb9fecb9SRuslan Bilovol 	err = snd_card_new(&g_audio->gadget->dev,
748eb9fecb9SRuslan Bilovol 			-1, NULL, THIS_MODULE, 0, &card);
749eb9fecb9SRuslan Bilovol 	if (err < 0)
750eb9fecb9SRuslan Bilovol 		goto fail;
751eb9fecb9SRuslan Bilovol 
752eb9fecb9SRuslan Bilovol 	uac->card = card;
753eb9fecb9SRuslan Bilovol 
754eb9fecb9SRuslan Bilovol 	/*
755eb9fecb9SRuslan Bilovol 	 * Create first PCM device
756eb9fecb9SRuslan Bilovol 	 * Create a substream only for non-zero channel streams
757eb9fecb9SRuslan Bilovol 	 */
758eb9fecb9SRuslan Bilovol 	err = snd_pcm_new(uac->card, pcm_name, 0,
759eb9fecb9SRuslan Bilovol 			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
760eb9fecb9SRuslan Bilovol 	if (err < 0)
761eb9fecb9SRuslan Bilovol 		goto snd_fail;
762eb9fecb9SRuslan Bilovol 
763d23922fcSRuslan Bilovol 	strscpy(pcm->name, pcm_name, sizeof(pcm->name));
764eb9fecb9SRuslan Bilovol 	pcm->private_data = uac;
765eb9fecb9SRuslan Bilovol 	uac->pcm = pcm;
766eb9fecb9SRuslan Bilovol 
767eb9fecb9SRuslan Bilovol 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
768eb9fecb9SRuslan Bilovol 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
769eb9fecb9SRuslan Bilovol 
770e89bb428SRuslan Bilovol 	if (c_chmask && g_audio->in_ep_fback) {
771e89bb428SRuslan Bilovol 		strscpy(card->mixername, card_name, sizeof(card->driver));
772e89bb428SRuslan Bilovol 
773e89bb428SRuslan Bilovol 		kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
774e89bb428SRuslan Bilovol 		if (!kctl) {
775e89bb428SRuslan Bilovol 			err = -ENOMEM;
776e89bb428SRuslan Bilovol 			goto snd_fail;
777e89bb428SRuslan Bilovol 		}
778e89bb428SRuslan Bilovol 
779e89bb428SRuslan Bilovol 		kctl->id.device = pcm->device;
780e89bb428SRuslan Bilovol 		kctl->id.subdevice = 0;
781e89bb428SRuslan Bilovol 
782e89bb428SRuslan Bilovol 		err = snd_ctl_add(card, kctl);
783e89bb428SRuslan Bilovol 		if (err < 0)
784e89bb428SRuslan Bilovol 			goto snd_fail;
785e89bb428SRuslan Bilovol 	}
786e89bb428SRuslan Bilovol 
787d23922fcSRuslan Bilovol 	strscpy(card->driver, card_name, sizeof(card->driver));
788d23922fcSRuslan Bilovol 	strscpy(card->shortname, card_name, sizeof(card->shortname));
789eb9fecb9SRuslan Bilovol 	sprintf(card->longname, "%s %i", card_name, card->dev->id);
790eb9fecb9SRuslan Bilovol 
791d27ab1e6STakashi Iwai 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
79267b2945dSTakashi Iwai 				       NULL, 0, BUFF_SIZE_MAX);
793eb9fecb9SRuslan Bilovol 
794eb9fecb9SRuslan Bilovol 	err = snd_card_register(card);
795eb9fecb9SRuslan Bilovol 
796eb9fecb9SRuslan Bilovol 	if (!err)
797eb9fecb9SRuslan Bilovol 		return 0;
798eb9fecb9SRuslan Bilovol 
799eb9fecb9SRuslan Bilovol snd_fail:
800eb9fecb9SRuslan Bilovol 	snd_card_free(card);
801eb9fecb9SRuslan Bilovol fail:
80229865117SJerome Brunet 	kfree(uac->p_prm.reqs);
80329865117SJerome Brunet 	kfree(uac->c_prm.reqs);
804eb9fecb9SRuslan Bilovol 	kfree(uac->p_prm.rbuf);
805eb9fecb9SRuslan Bilovol 	kfree(uac->c_prm.rbuf);
806eb9fecb9SRuslan Bilovol 	kfree(uac);
807eb9fecb9SRuslan Bilovol 
808eb9fecb9SRuslan Bilovol 	return err;
809eb9fecb9SRuslan Bilovol }
810eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup);
811eb9fecb9SRuslan Bilovol 
812eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio)
813eb9fecb9SRuslan Bilovol {
814eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac;
815eb9fecb9SRuslan Bilovol 	struct snd_card *card;
816eb9fecb9SRuslan Bilovol 
817eb9fecb9SRuslan Bilovol 	if (!g_audio || !g_audio->uac)
818eb9fecb9SRuslan Bilovol 		return;
819eb9fecb9SRuslan Bilovol 
820eb9fecb9SRuslan Bilovol 	uac = g_audio->uac;
821eb9fecb9SRuslan Bilovol 	card = uac->card;
822eb9fecb9SRuslan Bilovol 	if (card)
823eb9fecb9SRuslan Bilovol 		snd_card_free(card);
824eb9fecb9SRuslan Bilovol 
82529865117SJerome Brunet 	kfree(uac->p_prm.reqs);
82629865117SJerome Brunet 	kfree(uac->c_prm.reqs);
827eb9fecb9SRuslan Bilovol 	kfree(uac->p_prm.rbuf);
828eb9fecb9SRuslan Bilovol 	kfree(uac->c_prm.rbuf);
829eb9fecb9SRuslan Bilovol 	kfree(uac);
830eb9fecb9SRuslan Bilovol }
831eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup);
832eb9fecb9SRuslan Bilovol 
833eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL");
834eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
835eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol");
836