xref: /openbmc/linux/sound/usb/usx2y/usx2yhwdeppcm.c (revision 1c71222e)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  */
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds /* USX2Y "rawusb" aka hwdep_pcm implementation
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds  Its usb's unableness to atomically handle power of 2 period sized data chuncs
81da177e4SLinus Torvalds  at standard samplerates,
91da177e4SLinus Torvalds  what led to this part of the usx2y module:
101da177e4SLinus Torvalds  It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
1125985edcSLucas De Marchi  The pair uses a hardware dependent alsa-device for mmaped pcm transport.
121da177e4SLinus Torvalds  Advantage achieved:
131da177e4SLinus Torvalds          The usb_hc moves pcm data from/into memory via DMA.
141da177e4SLinus Torvalds          That memory is mmaped by jack's usx2y driver.
151da177e4SLinus Torvalds          Jack's usx2y driver is the first/last to read/write pcm data.
161da177e4SLinus Torvalds          Read/write is a combination of power of 2 period shaping and
171da177e4SLinus Torvalds          float/int conversation.
181da177e4SLinus Torvalds          Compared to mainline alsa/jack we leave out power of 2 period shaping inside
191da177e4SLinus Torvalds          snd-usb-usx2y which needs memcpy() and additional buffers.
201da177e4SLinus Torvalds          As a side effect possible unwanted pcm-data coruption resulting of
211da177e4SLinus Torvalds          standard alsa's snd-usb-usx2y period shaping scheme falls away.
221da177e4SLinus Torvalds          Result is sane jack operation at buffering schemes down to 128frames,
231da177e4SLinus Torvalds          2 periods.
241da177e4SLinus Torvalds          plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
251da177e4SLinus Torvalds          cost of easier triggered i.e. aeolus xruns (128 or 256frames,
261da177e4SLinus Torvalds          2periods works but is useless cause of crackling).
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds  This is a first "proof of concept" implementation.
2925985edcSLucas De Marchi  Later, functionalities should migrate to more appropriate places:
301da177e4SLinus Torvalds  Userland:
311da177e4SLinus Torvalds  - The jackd could mmap its float-pcm buffers directly from alsa-lib.
321da177e4SLinus Torvalds  - alsa-lib could provide power of 2 period sized shaping combined with int/float
331da177e4SLinus Torvalds    conversation.
341da177e4SLinus Torvalds    Currently the usx2y jack driver provides above 2 services.
351da177e4SLinus Torvalds  Kernel:
361da177e4SLinus Torvalds  - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
371da177e4SLinus Torvalds    devices can use it.
381da177e4SLinus Torvalds    Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
391da177e4SLinus Torvalds */
401da177e4SLinus Torvalds 
41b27c187fSNishanth Aravamudan #include <linux/delay.h>
425a0e3ad6STejun Heo #include <linux/gfp.h>
431da177e4SLinus Torvalds #include "usbusx2yaudio.c"
441da177e4SLinus Torvalds 
451d2019fbSNicolas Kaiser #if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #include <sound/hwdep.h>
481da177e4SLinus Torvalds 
usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream * subs)49bae3ce49STakashi Iwai static int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	struct urb	*urb = subs->completed_urb;
52bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
531da177e4SLinus Torvalds 	int		i, lens = 0, hwptr_done = subs->hwptr_done;
54bae3ce49STakashi Iwai 	struct usx2ydev	*usx2y = subs->usx2y;
55a829dd5bSTakashi Iwai 	int head;
564c0a58efSTakashi Iwai 
57a829dd5bSTakashi Iwai 	if (usx2y->hwdep_pcm_shm->capture_iso_start < 0) { //FIXME
58a829dd5bSTakashi Iwai 		head = usx2y->hwdep_pcm_shm->captured_iso_head + 1;
59bae3ce49STakashi Iwai 		if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso))
601da177e4SLinus Torvalds 			head = 0;
61bae3ce49STakashi Iwai 		usx2y->hwdep_pcm_shm->capture_iso_start = head;
621da177e4SLinus Torvalds 		snd_printdd("cap start %i\n", head);
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds 	for (i = 0; i < nr_of_packs(); i++) {
651da177e4SLinus Torvalds 		if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
664c0a58efSTakashi Iwai 			snd_printk(KERN_ERR
674c0a58efSTakashi Iwai 				   "active frame status %i. Most probably some hardware problem.\n",
684c0a58efSTakashi Iwai 				   urb->iso_frame_desc[i].status);
691da177e4SLinus Torvalds 			return urb->iso_frame_desc[i].status;
701da177e4SLinus Torvalds 		}
71bae3ce49STakashi Iwai 		lens += urb->iso_frame_desc[i].actual_length / usx2y->stride;
721da177e4SLinus Torvalds 	}
73a829dd5bSTakashi Iwai 	hwptr_done += lens;
74a829dd5bSTakashi Iwai 	if (hwptr_done >= runtime->buffer_size)
751da177e4SLinus Torvalds 		hwptr_done -= runtime->buffer_size;
761da177e4SLinus Torvalds 	subs->hwptr_done = hwptr_done;
771da177e4SLinus Torvalds 	subs->transfer_done += lens;
781da177e4SLinus Torvalds 	/* update the pointer, call callback if necessary */
791da177e4SLinus Torvalds 	if (subs->transfer_done >= runtime->period_size) {
801da177e4SLinus Torvalds 		subs->transfer_done -= runtime->period_size;
811da177e4SLinus Torvalds 		snd_pcm_period_elapsed(subs->pcm_substream);
821da177e4SLinus Torvalds 	}
831da177e4SLinus Torvalds 	return 0;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
usx2y_iso_frames_per_buffer(struct snd_pcm_runtime * runtime,struct usx2ydev * usx2y)86a829dd5bSTakashi Iwai static int usx2y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
87bae3ce49STakashi Iwai 					      struct usx2ydev *usx2y)
881da177e4SLinus Torvalds {
89bae3ce49STakashi Iwai 	return (runtime->buffer_size * 1000) / usx2y->rate + 1;	//FIXME: so far only correct period_size == 2^x ?
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds  * prepare urb for playback data pipe
941da177e4SLinus Torvalds  *
951da177e4SLinus Torvalds  * we copy the data directly from the pcm buffer.
961da177e4SLinus Torvalds  * the current position to be copied is held in hwptr field.
971da177e4SLinus Torvalds  * since a urb can handle only a single linear buffer, if the total
981da177e4SLinus Torvalds  * transferred area overflows the buffer boundary, we cannot send
991da177e4SLinus Torvalds  * it directly from the buffer.  thus the data is once copied to
1001da177e4SLinus Torvalds  * a temporary buffer and urb points to that.
1011da177e4SLinus Torvalds  */
usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream * subs,struct urb * urb)102bae3ce49STakashi Iwai static int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs,
1031da177e4SLinus Torvalds 					struct urb *urb)
1041da177e4SLinus Torvalds {
1051da177e4SLinus Torvalds 	int count, counts, pack;
106bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
107bae3ce49STakashi Iwai 	struct snd_usx2y_hwdep_pcm_shm *shm = usx2y->hwdep_pcm_shm;
108bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
1091da177e4SLinus Torvalds 
110a829dd5bSTakashi Iwai 	if (shm->playback_iso_start < 0) {
1111da177e4SLinus Torvalds 		shm->playback_iso_start = shm->captured_iso_head -
112bae3ce49STakashi Iwai 			usx2y_iso_frames_per_buffer(runtime, usx2y);
113a829dd5bSTakashi Iwai 		if (shm->playback_iso_start < 0)
1141da177e4SLinus Torvalds 			shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
1151da177e4SLinus Torvalds 		shm->playback_iso_head = shm->playback_iso_start;
1161da177e4SLinus Torvalds 	}
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	count = 0;
1191da177e4SLinus Torvalds 	for (pack = 0; pack < nr_of_packs(); pack++) {
1201da177e4SLinus Torvalds 		/* calculate the size of a packet */
121bae3ce49STakashi Iwai 		counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride;
1221da177e4SLinus Torvalds 		if (counts < 43 || counts > 50) {
123d3d579f8STakashi Iwai 			snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
1241da177e4SLinus Torvalds 			return -EPIPE;
1251da177e4SLinus Torvalds 		}
1261da177e4SLinus Torvalds 		/* set up descriptor */
1271da177e4SLinus Torvalds 		urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
1281da177e4SLinus Torvalds 		urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
129bae3ce49STakashi Iwai 		if (atomic_read(&subs->state) != STATE_RUNNING)
1301da177e4SLinus Torvalds 			memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
1311da177e4SLinus Torvalds 			       urb->iso_frame_desc[pack].length);
1321da177e4SLinus Torvalds 		if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
1331da177e4SLinus Torvalds 			shm->playback_iso_head = 0;
1341da177e4SLinus Torvalds 		count += counts;
1351da177e4SLinus Torvalds 	}
136bae3ce49STakashi Iwai 	urb->transfer_buffer_length = count * usx2y->stride;
1371da177e4SLinus Torvalds 	return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream * subs,struct urb * urb)140a829dd5bSTakashi Iwai static void usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream *subs,
141bbe85bbdSTakashi Iwai 					      struct urb *urb)
1421da177e4SLinus Torvalds {
143a829dd5bSTakashi Iwai 	struct usb_iso_packet_descriptor *desc;
144a829dd5bSTakashi Iwai 	struct snd_usx2y_hwdep_pcm_shm *shm;
145a829dd5bSTakashi Iwai 	int pack, head;
1464c0a58efSTakashi Iwai 
1471da177e4SLinus Torvalds 	for (pack = 0; pack < nr_of_packs(); ++pack) {
148a829dd5bSTakashi Iwai 		desc = urb->iso_frame_desc + pack;
149a829dd5bSTakashi Iwai 		if (subs) {
150a829dd5bSTakashi Iwai 			shm = subs->usx2y->hwdep_pcm_shm;
151a829dd5bSTakashi Iwai 			head = shm->captured_iso_head + 1;
1521da177e4SLinus Torvalds 			if (head >= ARRAY_SIZE(shm->captured_iso))
1531da177e4SLinus Torvalds 				head = 0;
1541da177e4SLinus Torvalds 			shm->captured_iso[head].frame = urb->start_frame + pack;
1551da177e4SLinus Torvalds 			shm->captured_iso[head].offset = desc->offset;
1561da177e4SLinus Torvalds 			shm->captured_iso[head].length = desc->actual_length;
1571da177e4SLinus Torvalds 			shm->captured_iso_head = head;
1581da177e4SLinus Torvalds 			shm->captured_iso_frames++;
1591da177e4SLinus Torvalds 		}
160a829dd5bSTakashi Iwai 		desc->offset += desc->length * NRURBS * nr_of_packs();
161a829dd5bSTakashi Iwai 		if (desc->offset + desc->length >= SSS)
1621da177e4SLinus Torvalds 			desc->offset -= (SSS - desc->length);
1631da177e4SLinus Torvalds 	}
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds 
usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream * capsubs,struct snd_usx2y_substream * capsubs2,struct snd_usx2y_substream * playbacksubs,int frame)166a829dd5bSTakashi Iwai static int usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream *capsubs,
167bae3ce49STakashi Iwai 					  struct snd_usx2y_substream *capsubs2,
168bae3ce49STakashi Iwai 					  struct snd_usx2y_substream *playbacksubs,
169bbe85bbdSTakashi Iwai 					  int frame)
1701da177e4SLinus Torvalds {
1711da177e4SLinus Torvalds 	int err, state;
1721da177e4SLinus Torvalds 	struct urb *urb = playbacksubs->completed_urb;
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	state = atomic_read(&playbacksubs->state);
175a829dd5bSTakashi Iwai 	if (urb) {
176bae3ce49STakashi Iwai 		if (state == STATE_RUNNING)
177bae3ce49STakashi Iwai 			usx2y_urb_play_retire(playbacksubs, urb);
178bae3ce49STakashi Iwai 		else if (state >= STATE_PRERUNNING)
1791da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1801da177e4SLinus Torvalds 	} else {
1811da177e4SLinus Torvalds 		switch (state) {
182bae3ce49STakashi Iwai 		case STATE_STARTING1:
1831da177e4SLinus Torvalds 			urb = playbacksubs->urb[0];
1841da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1851da177e4SLinus Torvalds 			break;
186bae3ce49STakashi Iwai 		case STATE_STARTING2:
1871da177e4SLinus Torvalds 			urb = playbacksubs->urb[1];
1881da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1891da177e4SLinus Torvalds 			break;
1901da177e4SLinus Torvalds 		}
1911da177e4SLinus Torvalds 	}
1921da177e4SLinus Torvalds 	if (urb) {
193a829dd5bSTakashi Iwai 		err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
194a829dd5bSTakashi Iwai 		if (err)
1951da177e4SLinus Torvalds 			return err;
196a829dd5bSTakashi Iwai 		err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
197a829dd5bSTakashi Iwai 		if (err)
198a829dd5bSTakashi Iwai 			return err;
1991da177e4SLinus Torvalds 	}
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	playbacksubs->completed_urb = NULL;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	state = atomic_read(&capsubs->state);
204bae3ce49STakashi Iwai 	if (state >= STATE_PREPARED) {
205bae3ce49STakashi Iwai 		if (state == STATE_RUNNING) {
206a829dd5bSTakashi Iwai 			err = usx2y_usbpcm_urb_capt_retire(capsubs);
207a829dd5bSTakashi Iwai 			if (err)
2081da177e4SLinus Torvalds 				return err;
209a829dd5bSTakashi Iwai 		} else if (state >= STATE_PRERUNNING) {
2101da177e4SLinus Torvalds 			atomic_inc(&capsubs->state);
211a829dd5bSTakashi Iwai 		}
212bae3ce49STakashi Iwai 		usx2y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
213a829dd5bSTakashi Iwai 		if (capsubs2)
214bae3ce49STakashi Iwai 			usx2y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
215a829dd5bSTakashi Iwai 		err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame);
216a829dd5bSTakashi Iwai 		if (err)
2171da177e4SLinus Torvalds 			return err;
218a829dd5bSTakashi Iwai 		if (capsubs2) {
219a829dd5bSTakashi Iwai 			err = usx2y_urb_submit(capsubs2, capsubs2->completed_urb, frame);
220a829dd5bSTakashi Iwai 			if (err)
2211da177e4SLinus Torvalds 				return err;
2221da177e4SLinus Torvalds 		}
223a829dd5bSTakashi Iwai 	}
2241da177e4SLinus Torvalds 	capsubs->completed_urb = NULL;
225a829dd5bSTakashi Iwai 	if (capsubs2)
2261da177e4SLinus Torvalds 		capsubs2->completed_urb = NULL;
2271da177e4SLinus Torvalds 	return 0;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
i_usx2y_usbpcm_urb_complete(struct urb * urb)230bae3ce49STakashi Iwai static void i_usx2y_usbpcm_urb_complete(struct urb *urb)
2311da177e4SLinus Torvalds {
232bae3ce49STakashi Iwai 	struct snd_usx2y_substream *subs = urb->context;
233bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
234bae3ce49STakashi Iwai 	struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs;
2351da177e4SLinus Torvalds 
236bae3ce49STakashi Iwai 	if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
237bbe85bbdSTakashi Iwai 		snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
238bae3ce49STakashi Iwai 			    usb_get_current_frame_number(usx2y->dev),
239bbe85bbdSTakashi Iwai 			    subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240bbe85bbdSTakashi Iwai 			    urb->status, urb->start_frame);
2411da177e4SLinus Torvalds 		return;
2421da177e4SLinus Torvalds 	}
2431da177e4SLinus Torvalds 	if (unlikely(urb->status)) {
244bae3ce49STakashi Iwai 		usx2y_error_urb_status(usx2y, subs, urb);
2451da177e4SLinus Torvalds 		return;
2461da177e4SLinus Torvalds 	}
2471da177e4SLinus Torvalds 
248a9d14bc0SDaniel Mack 	subs->completed_urb = urb;
249bae3ce49STakashi Iwai 	capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
250bae3ce49STakashi Iwai 	capsubs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
251bae3ce49STakashi Iwai 	playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
252bae3ce49STakashi Iwai 	if (capsubs->completed_urb && atomic_read(&capsubs->state) >= STATE_PREPARED &&
253a829dd5bSTakashi Iwai 	    (!capsubs2 || capsubs2->completed_urb) &&
254bae3ce49STakashi Iwai 	    (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) {
255a829dd5bSTakashi Iwai 		if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
256bae3ce49STakashi Iwai 			usx2y->wait_iso_frame += nr_of_packs();
257a829dd5bSTakashi Iwai 		} else {
2581da177e4SLinus Torvalds 			snd_printdd("\n");
259bae3ce49STakashi Iwai 			usx2y_clients_stop(usx2y);
2601da177e4SLinus Torvalds 		}
2611da177e4SLinus Torvalds 	}
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
usx2y_hwdep_urb_release(struct urb ** urb)264bae3ce49STakashi Iwai static void usx2y_hwdep_urb_release(struct urb **urb)
2651da177e4SLinus Torvalds {
2661da177e4SLinus Torvalds 	usb_kill_urb(*urb);
2671da177e4SLinus Torvalds 	usb_free_urb(*urb);
2681da177e4SLinus Torvalds 	*urb = NULL;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds /*
2721da177e4SLinus Torvalds  * release a substream
2731da177e4SLinus Torvalds  */
usx2y_usbpcm_urbs_release(struct snd_usx2y_substream * subs)274bae3ce49STakashi Iwai static void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs)
2751da177e4SLinus Torvalds {
2761da177e4SLinus Torvalds 	int i;
2774c0a58efSTakashi Iwai 
278bae3ce49STakashi Iwai 	snd_printdd("snd_usx2y_urbs_release() %i\n", subs->endpoint);
2791da177e4SLinus Torvalds 	for (i = 0; i < NRURBS; i++)
280bae3ce49STakashi Iwai 		usx2y_hwdep_urb_release(subs->urb + i);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
usx2y_usbpcm_subs_startup_finish(struct usx2ydev * usx2y)283bae3ce49STakashi Iwai static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y)
2841da177e4SLinus Torvalds {
285bae3ce49STakashi Iwai 	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete);
286bae3ce49STakashi Iwai 	usx2y->prepare_subs = NULL;
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
i_usx2y_usbpcm_subs_startup(struct urb * urb)289bae3ce49STakashi Iwai static void i_usx2y_usbpcm_subs_startup(struct urb *urb)
2901da177e4SLinus Torvalds {
291bae3ce49STakashi Iwai 	struct snd_usx2y_substream *subs = urb->context;
292bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
293bae3ce49STakashi Iwai 	struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs;
294a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *cap_subs2;
2954c0a58efSTakashi Iwai 
296a829dd5bSTakashi Iwai 	if (prepare_subs &&
2971da177e4SLinus Torvalds 	    urb->start_frame == prepare_subs->urb[0]->start_frame) {
2981da177e4SLinus Torvalds 		atomic_inc(&prepare_subs->state);
299bae3ce49STakashi Iwai 		if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
300a829dd5bSTakashi Iwai 			cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
301a829dd5bSTakashi Iwai 			if (cap_subs2)
3021da177e4SLinus Torvalds 				atomic_inc(&cap_subs2->state);
3031da177e4SLinus Torvalds 		}
304bae3ce49STakashi Iwai 		usx2y_usbpcm_subs_startup_finish(usx2y);
305bae3ce49STakashi Iwai 		wake_up(&usx2y->prepare_wait_queue);
3061da177e4SLinus Torvalds 	}
3071da177e4SLinus Torvalds 
308bae3ce49STakashi Iwai 	i_usx2y_usbpcm_urb_complete(urb);
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds /*
3121da177e4SLinus Torvalds  * initialize a substream's urbs
3131da177e4SLinus Torvalds  */
usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream * subs)314bae3ce49STakashi Iwai static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs)
3151da177e4SLinus Torvalds {
3161da177e4SLinus Torvalds 	int i;
3171da177e4SLinus Torvalds 	unsigned int pipe;
318bae3ce49STakashi Iwai 	int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
319bae3ce49STakashi Iwai 	struct usb_device *dev = subs->usx2y->dev;
320a829dd5bSTakashi Iwai 	struct urb **purb;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
3231da177e4SLinus Torvalds 			usb_rcvisocpipe(dev, subs->endpoint);
32480b2b03bSVincent Mailhol 	subs->maxpacksize = usb_maxpacket(dev, pipe);
3251da177e4SLinus Torvalds 	if (!subs->maxpacksize)
3261da177e4SLinus Torvalds 		return -EINVAL;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	/* allocate and initialize data urbs */
3291da177e4SLinus Torvalds 	for (i = 0; i < NRURBS; i++) {
330a829dd5bSTakashi Iwai 		purb = subs->urb + i;
3311da177e4SLinus Torvalds 		if (*purb) {
3321da177e4SLinus Torvalds 			usb_kill_urb(*purb);
3331da177e4SLinus Torvalds 			continue;
3341da177e4SLinus Torvalds 		}
3351da177e4SLinus Torvalds 		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
336a829dd5bSTakashi Iwai 		if (!*purb) {
337bae3ce49STakashi Iwai 			usx2y_usbpcm_urbs_release(subs);
3381da177e4SLinus Torvalds 			return -ENOMEM;
3391da177e4SLinus Torvalds 		}
3401da177e4SLinus Torvalds 		(*purb)->transfer_buffer = is_playback ?
341bae3ce49STakashi Iwai 			subs->usx2y->hwdep_pcm_shm->playback : (
3421da177e4SLinus Torvalds 				subs->endpoint == 0x8 ?
343bae3ce49STakashi Iwai 				subs->usx2y->hwdep_pcm_shm->capture0x8 :
344bae3ce49STakashi Iwai 				subs->usx2y->hwdep_pcm_shm->capture0xA);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 		(*purb)->dev = dev;
3471da177e4SLinus Torvalds 		(*purb)->pipe = pipe;
3481da177e4SLinus Torvalds 		(*purb)->number_of_packets = nr_of_packs();
3491da177e4SLinus Torvalds 		(*purb)->context = subs;
3501da177e4SLinus Torvalds 		(*purb)->interval = 1;
351bae3ce49STakashi Iwai 		(*purb)->complete = i_usx2y_usbpcm_subs_startup;
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 	return 0;
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds /*
3571da177e4SLinus Torvalds  * free the buffer
3581da177e4SLinus Torvalds  */
snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream * substream)359bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
3601da177e4SLinus Torvalds {
361bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
362a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *subs = runtime->private_data;
363a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *cap_subs;
364a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *playback_subs;
365a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *cap_subs2;
3664c0a58efSTakashi Iwai 
367bae3ce49STakashi Iwai 	mutex_lock(&subs->usx2y->pcm_mutex);
368a829dd5bSTakashi Iwai 	snd_printdd("%s(%p)\n", __func__, substream);
3691da177e4SLinus Torvalds 
370a829dd5bSTakashi Iwai 	cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
371a829dd5bSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
372a829dd5bSTakashi Iwai 		cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
373bae3ce49STakashi Iwai 		atomic_set(&subs->state, STATE_STOPPED);
374bae3ce49STakashi Iwai 		usx2y_usbpcm_urbs_release(subs);
3751da177e4SLinus Torvalds 		if (!cap_subs->pcm_substream ||
3761da177e4SLinus Torvalds 		    !cap_subs->pcm_substream->runtime ||
377ca4833c5STakashi Iwai 		    cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) {
378bae3ce49STakashi Iwai 			atomic_set(&cap_subs->state, STATE_STOPPED);
379a829dd5bSTakashi Iwai 			if (cap_subs2)
380bae3ce49STakashi Iwai 				atomic_set(&cap_subs2->state, STATE_STOPPED);
381bae3ce49STakashi Iwai 			usx2y_usbpcm_urbs_release(cap_subs);
382a829dd5bSTakashi Iwai 			if (cap_subs2)
383bae3ce49STakashi Iwai 				usx2y_usbpcm_urbs_release(cap_subs2);
3841da177e4SLinus Torvalds 		}
3851da177e4SLinus Torvalds 	} else {
386a829dd5bSTakashi Iwai 		playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
387bae3ce49STakashi Iwai 		if (atomic_read(&playback_subs->state) < STATE_PREPARED) {
388bae3ce49STakashi Iwai 			atomic_set(&subs->state, STATE_STOPPED);
389a829dd5bSTakashi Iwai 			if (cap_subs2)
390bae3ce49STakashi Iwai 				atomic_set(&cap_subs2->state, STATE_STOPPED);
391bae3ce49STakashi Iwai 			usx2y_usbpcm_urbs_release(subs);
392a829dd5bSTakashi Iwai 			if (cap_subs2)
393bae3ce49STakashi Iwai 				usx2y_usbpcm_urbs_release(cap_subs2);
3941da177e4SLinus Torvalds 		}
3951da177e4SLinus Torvalds 	}
396bae3ce49STakashi Iwai 	mutex_unlock(&subs->usx2y->pcm_mutex);
3973f0c972aSTakashi Iwai 	return 0;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
usx2y_usbpcm_subs_startup(struct snd_usx2y_substream * subs)400bae3ce49STakashi Iwai static void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs)
4011da177e4SLinus Torvalds {
402bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
4034c0a58efSTakashi Iwai 
404bae3ce49STakashi Iwai 	usx2y->prepare_subs = subs;
4051da177e4SLinus Torvalds 	subs->urb[0]->start_frame = -1;
406bae3ce49STakashi Iwai 	smp_wmb();	// Make sure above modifications are seen by i_usx2y_subs_startup()
407bae3ce49STakashi Iwai 	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup);
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
usx2y_usbpcm_urbs_start(struct snd_usx2y_substream * subs)410bae3ce49STakashi Iwai static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs)
4111da177e4SLinus Torvalds {
4124c0a58efSTakashi Iwai 	int	p, u, err, stream = subs->pcm_substream->stream;
413bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
414a829dd5bSTakashi Iwai 	struct urb *urb;
415a829dd5bSTakashi Iwai 	unsigned long pack;
4161da177e4SLinus Torvalds 
417a829dd5bSTakashi Iwai 	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
418bae3ce49STakashi Iwai 		usx2y->hwdep_pcm_shm->captured_iso_head = -1;
419bae3ce49STakashi Iwai 		usx2y->hwdep_pcm_shm->captured_iso_frames = 0;
4201da177e4SLinus Torvalds 	}
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	for (p = 0; 3 >= (stream + p); p += 2) {
423bae3ce49STakashi Iwai 		struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
424a829dd5bSTakashi Iwai 		if (subs) {
425a829dd5bSTakashi Iwai 			err = usx2y_usbpcm_urbs_allocate(subs);
426a829dd5bSTakashi Iwai 			if (err < 0)
4271da177e4SLinus Torvalds 				return err;
4281da177e4SLinus Torvalds 			subs->completed_urb = NULL;
4291da177e4SLinus Torvalds 		}
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	for (p = 0; p < 4; p++) {
433bae3ce49STakashi Iwai 		struct snd_usx2y_substream *subs = usx2y->subs[p];
4344c0a58efSTakashi Iwai 
435a829dd5bSTakashi Iwai 		if (subs && atomic_read(&subs->state) >= STATE_PREPARED)
4361da177e4SLinus Torvalds 			goto start;
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds  start:
440bae3ce49STakashi Iwai 	usx2y_usbpcm_subs_startup(subs);
4411da177e4SLinus Torvalds 	for (u = 0; u < NRURBS; u++) {
4421da177e4SLinus Torvalds 		for (p = 0; 3 >= (stream + p); p += 2) {
443bae3ce49STakashi Iwai 			struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
4444c0a58efSTakashi Iwai 
445a829dd5bSTakashi Iwai 			if (!subs)
446a829dd5bSTakashi Iwai 				continue;
447a829dd5bSTakashi Iwai 			urb = subs->urb[u];
4481da177e4SLinus Torvalds 			if (usb_pipein(urb->pipe)) {
449a829dd5bSTakashi Iwai 				if (!u)
450bae3ce49STakashi Iwai 					atomic_set(&subs->state, STATE_STARTING3);
451bae3ce49STakashi Iwai 				urb->dev = usx2y->dev;
4521da177e4SLinus Torvalds 				for (pack = 0; pack < nr_of_packs(); pack++) {
4531da177e4SLinus Torvalds 					urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
4541da177e4SLinus Torvalds 					urb->iso_frame_desc[pack].length = subs->maxpacksize;
4551da177e4SLinus Torvalds 				}
4561da177e4SLinus Torvalds 				urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
457a829dd5bSTakashi Iwai 				err = usb_submit_urb(urb, GFP_KERNEL);
458a829dd5bSTakashi Iwai 				if (err < 0) {
4591da177e4SLinus Torvalds 					snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
4601da177e4SLinus Torvalds 					err = -EPIPE;
4611da177e4SLinus Torvalds 					goto cleanup;
4621da177e4SLinus Torvalds 				}  else {
4631da177e4SLinus Torvalds 					snd_printdd("%i\n", urb->start_frame);
464a829dd5bSTakashi Iwai 					if (!u)
465bae3ce49STakashi Iwai 						usx2y->wait_iso_frame = urb->start_frame;
4661da177e4SLinus Torvalds 				}
4671da177e4SLinus Torvalds 				urb->transfer_flags = 0;
4681da177e4SLinus Torvalds 			} else {
469bae3ce49STakashi Iwai 				atomic_set(&subs->state, STATE_STARTING1);
4701da177e4SLinus Torvalds 				break;
4711da177e4SLinus Torvalds 			}
4721da177e4SLinus Torvalds 		}
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 	err = 0;
475a829dd5bSTakashi Iwai 	wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs);
476bae3ce49STakashi Iwai 	if (atomic_read(&subs->state) != STATE_PREPARED)
4771da177e4SLinus Torvalds 		err = -EPIPE;
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds  cleanup:
4801da177e4SLinus Torvalds 	if (err) {
481bae3ce49STakashi Iwai 		usx2y_subs_startup_finish(usx2y);	// Call it now
482ea57e874Sgushengxian 		usx2y_clients_stop(usx2y);	// something is completely wrong > stop everything
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 	return err;
4851da177e4SLinus Torvalds }
4861da177e4SLinus Torvalds 
4874e268db7STakashi Iwai #define USX2Y_HWDEP_PCM_PAGES	\
4884e268db7STakashi Iwai 	PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm))
4894e268db7STakashi Iwai 
4901da177e4SLinus Torvalds /*
4911da177e4SLinus Torvalds  * prepare callback
4921da177e4SLinus Torvalds  *
4931da177e4SLinus Torvalds  * set format and initialize urbs
4941da177e4SLinus Torvalds  */
snd_usx2y_usbpcm_prepare(struct snd_pcm_substream * substream)495bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
4961da177e4SLinus Torvalds {
497bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
498bae3ce49STakashi Iwai 	struct snd_usx2y_substream *subs = runtime->private_data;
499bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = subs->usx2y;
500bae3ce49STakashi Iwai 	struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
5011da177e4SLinus Torvalds 	int err = 0;
5024c0a58efSTakashi Iwai 
503bae3ce49STakashi Iwai 	snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream);
5041da177e4SLinus Torvalds 
505c1f24841STakashi Iwai 	mutex_lock(&usx2y->pcm_mutex);
506c1f24841STakashi Iwai 
507a829dd5bSTakashi Iwai 	if (!usx2y->hwdep_pcm_shm) {
5084e268db7STakashi Iwai 		usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES,
509734b5a0bSTakashi Iwai 							 GFP_KERNEL);
510c1f24841STakashi Iwai 		if (!usx2y->hwdep_pcm_shm) {
511c1f24841STakashi Iwai 			err = -ENOMEM;
512c1f24841STakashi Iwai 			goto up_prepare_mutex;
513c1f24841STakashi Iwai 		}
5144e268db7STakashi Iwai 		memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
5151da177e4SLinus Torvalds 	}
5161da177e4SLinus Torvalds 
517bae3ce49STakashi Iwai 	usx2y_subs_prepare(subs);
5181da177e4SLinus Torvalds 	// Start hardware streams
5191da177e4SLinus Torvalds 	// SyncStream first....
520bae3ce49STakashi Iwai 	if (atomic_read(&capsubs->state) < STATE_PREPARED) {
521a829dd5bSTakashi Iwai 		if (usx2y->format != runtime->format) {
522a829dd5bSTakashi Iwai 			err = usx2y_format_set(usx2y, runtime->format);
523a829dd5bSTakashi Iwai 			if (err < 0)
5241da177e4SLinus Torvalds 				goto up_prepare_mutex;
525a829dd5bSTakashi Iwai 		}
526a829dd5bSTakashi Iwai 		if (usx2y->rate != runtime->rate) {
527a829dd5bSTakashi Iwai 			err = usx2y_rate_set(usx2y, runtime->rate);
528a829dd5bSTakashi Iwai 			if (err < 0)
5291da177e4SLinus Torvalds 				goto up_prepare_mutex;
530a829dd5bSTakashi Iwai 		}
531bbe85bbdSTakashi Iwai 		snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
532bbe85bbdSTakashi Iwai 			    "self" : "playpipe");
533a829dd5bSTakashi Iwai 		err = usx2y_usbpcm_urbs_start(capsubs);
534a829dd5bSTakashi Iwai 		if (err < 0)
5351da177e4SLinus Torvalds 			goto up_prepare_mutex;
5361da177e4SLinus Torvalds 	}
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	if (subs != capsubs) {
539bae3ce49STakashi Iwai 		usx2y->hwdep_pcm_shm->playback_iso_start = -1;
540bae3ce49STakashi Iwai 		if (atomic_read(&subs->state) < STATE_PREPARED) {
541bae3ce49STakashi Iwai 			while (usx2y_iso_frames_per_buffer(runtime, usx2y) >
542bae3ce49STakashi Iwai 			       usx2y->hwdep_pcm_shm->captured_iso_frames) {
5434c0a58efSTakashi Iwai 				snd_printdd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
544bae3ce49STakashi Iwai 					    usx2y_iso_frames_per_buffer(runtime, usx2y),
545bae3ce49STakashi Iwai 					    usx2y->hwdep_pcm_shm->captured_iso_frames);
546b27c187fSNishanth Aravamudan 				if (msleep_interruptible(10)) {
5471da177e4SLinus Torvalds 					err = -ERESTARTSYS;
5481da177e4SLinus Torvalds 					goto up_prepare_mutex;
5491da177e4SLinus Torvalds 				}
5501da177e4SLinus Torvalds 			}
551a829dd5bSTakashi Iwai 			err = usx2y_usbpcm_urbs_start(subs);
552a829dd5bSTakashi Iwai 			if (err < 0)
5531da177e4SLinus Torvalds 				goto up_prepare_mutex;
5541da177e4SLinus Torvalds 		}
555bbe85bbdSTakashi Iwai 		snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
556bae3ce49STakashi Iwai 			    usx2y_iso_frames_per_buffer(runtime, usx2y),
557bae3ce49STakashi Iwai 			    usx2y->hwdep_pcm_shm->captured_iso_frames);
558a829dd5bSTakashi Iwai 	} else {
559bae3ce49STakashi Iwai 		usx2y->hwdep_pcm_shm->capture_iso_start = -1;
560a829dd5bSTakashi Iwai 	}
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds  up_prepare_mutex:
563bae3ce49STakashi Iwai 	mutex_unlock(&usx2y->pcm_mutex);
5641da177e4SLinus Torvalds 	return err;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
5674c0a58efSTakashi Iwai static const struct snd_pcm_hardware snd_usx2y_4c = {
5681da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
5691da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
5701da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID),
5711da177e4SLinus Torvalds 	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
5721da177e4SLinus Torvalds 	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
5731da177e4SLinus Torvalds 	.rate_min =                44100,
5741da177e4SLinus Torvalds 	.rate_max =                48000,
5751da177e4SLinus Torvalds 	.channels_min =            2,
5761da177e4SLinus Torvalds 	.channels_max =            4,
5771da177e4SLinus Torvalds 	.buffer_bytes_max =	(2*128*1024),
5781da177e4SLinus Torvalds 	.period_bytes_min =	64,
5791da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
5801da177e4SLinus Torvalds 	.periods_min =		2,
5811da177e4SLinus Torvalds 	.periods_max =		1024,
5821da177e4SLinus Torvalds 	.fifo_size =              0
5831da177e4SLinus Torvalds };
5841da177e4SLinus Torvalds 
snd_usx2y_usbpcm_open(struct snd_pcm_substream * substream)585bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream)
5861da177e4SLinus Torvalds {
587a829dd5bSTakashi Iwai 	struct snd_usx2y_substream	*subs =
588a829dd5bSTakashi Iwai 		((struct snd_usx2y_substream **)
5891da177e4SLinus Torvalds 		 snd_pcm_substream_chip(substream))[substream->stream];
590bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime	*runtime = substream->runtime;
5911da177e4SLinus Torvalds 
592bae3ce49STakashi Iwai 	if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
5931da177e4SLinus Torvalds 		return -EBUSY;
5941da177e4SLinus Torvalds 
595a829dd5bSTakashi Iwai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
596a829dd5bSTakashi Iwai 		runtime->hw = snd_usx2y_2c;
597a829dd5bSTakashi Iwai 	else
598a829dd5bSTakashi Iwai 		runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c);
5991da177e4SLinus Torvalds 	runtime->private_data = subs;
6001da177e4SLinus Torvalds 	subs->pcm_substream = substream;
6011da177e4SLinus Torvalds 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
6021da177e4SLinus Torvalds 	return 0;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
snd_usx2y_usbpcm_close(struct snd_pcm_substream * substream)605bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream)
6061da177e4SLinus Torvalds {
607bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
608bae3ce49STakashi Iwai 	struct snd_usx2y_substream *subs = runtime->private_data;
609cb432379STakashi Iwai 
6101da177e4SLinus Torvalds 	subs->pcm_substream = NULL;
611cb432379STakashi Iwai 	return 0;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
6144c0a58efSTakashi Iwai static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = {
615bae3ce49STakashi Iwai 	.open =		snd_usx2y_usbpcm_open,
616bae3ce49STakashi Iwai 	.close =	snd_usx2y_usbpcm_close,
617bae3ce49STakashi Iwai 	.hw_params =	snd_usx2y_pcm_hw_params,
618bae3ce49STakashi Iwai 	.hw_free =	snd_usx2y_usbpcm_hw_free,
619bae3ce49STakashi Iwai 	.prepare =	snd_usx2y_usbpcm_prepare,
620bae3ce49STakashi Iwai 	.trigger =	snd_usx2y_pcm_trigger,
621bae3ce49STakashi Iwai 	.pointer =	snd_usx2y_pcm_pointer,
6221da177e4SLinus Torvalds };
6231da177e4SLinus Torvalds 
usx2y_pcms_busy_check(struct snd_card * card)624bae3ce49STakashi Iwai static int usx2y_pcms_busy_check(struct snd_card *card)
6251da177e4SLinus Torvalds {
626bae3ce49STakashi Iwai 	struct usx2ydev	*dev = usx2y(card);
627a829dd5bSTakashi Iwai 	struct snd_usx2y_substream *subs;
628e2439a54STakashi Iwai 	int i;
6291da177e4SLinus Torvalds 
630e2439a54STakashi Iwai 	for (i = 0; i < dev->pcm_devs * 2; i++) {
631a829dd5bSTakashi Iwai 		subs = dev->subs[i];
632e2439a54STakashi Iwai 		if (subs && subs->pcm_substream &&
633e2439a54STakashi Iwai 		    SUBSTREAM_BUSY(subs->pcm_substream))
634e2439a54STakashi Iwai 			return -EBUSY;
6351da177e4SLinus Torvalds 	}
636e2439a54STakashi Iwai 	return 0;
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_open(struct snd_hwdep * hw,struct file * file)639bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
6401da177e4SLinus Torvalds {
641bbe85bbdSTakashi Iwai 	struct snd_card *card = hw->card;
642e2439a54STakashi Iwai 	int err;
643e2439a54STakashi Iwai 
644bae3ce49STakashi Iwai 	mutex_lock(&usx2y(card)->pcm_mutex);
645bae3ce49STakashi Iwai 	err = usx2y_pcms_busy_check(card);
646e2439a54STakashi Iwai 	if (!err)
647bae3ce49STakashi Iwai 		usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
648bae3ce49STakashi Iwai 	mutex_unlock(&usx2y(card)->pcm_mutex);
6491da177e4SLinus Torvalds 	return err;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_release(struct snd_hwdep * hw,struct file * file)652bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
6531da177e4SLinus Torvalds {
654bbe85bbdSTakashi Iwai 	struct snd_card *card = hw->card;
655e2439a54STakashi Iwai 	int err;
656e2439a54STakashi Iwai 
657bae3ce49STakashi Iwai 	mutex_lock(&usx2y(card)->pcm_mutex);
658bae3ce49STakashi Iwai 	err = usx2y_pcms_busy_check(card);
659e2439a54STakashi Iwai 	if (!err)
660bae3ce49STakashi Iwai 		usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
661bae3ce49STakashi Iwai 	mutex_unlock(&usx2y(card)->pcm_mutex);
6621da177e4SLinus Torvalds 	return err;
6631da177e4SLinus Torvalds }
6641da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct * area)665bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
6661da177e4SLinus Torvalds {
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct * area)669bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
6701da177e4SLinus Torvalds {
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault * vmf)673bae3ce49STakashi Iwai static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	unsigned long offset;
6761da177e4SLinus Torvalds 	void *vaddr;
6771da177e4SLinus Torvalds 
678eb415b8fSNick Piggin 	offset = vmf->pgoff << PAGE_SHIFT;
679bae3ce49STakashi Iwai 	vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
680eb415b8fSNick Piggin 	vmf->page = virt_to_page(vaddr);
681eb415b8fSNick Piggin 	get_page(vmf->page);
682eb415b8fSNick Piggin 	return 0;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
685bae3ce49STakashi Iwai static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = {
686bae3ce49STakashi Iwai 	.open = snd_usx2y_hwdep_pcm_vm_open,
687bae3ce49STakashi Iwai 	.close = snd_usx2y_hwdep_pcm_vm_close,
688bae3ce49STakashi Iwai 	.fault = snd_usx2y_hwdep_pcm_vm_fault,
6891da177e4SLinus Torvalds };
6901da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep * hw,struct file * filp,struct vm_area_struct * area)691bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
6921da177e4SLinus Torvalds {
6931da177e4SLinus Torvalds 	unsigned long	size = (unsigned long)(area->vm_end - area->vm_start);
694bae3ce49STakashi Iwai 	struct usx2ydev	*usx2y = hw->private_data;
6951da177e4SLinus Torvalds 
696bae3ce49STakashi Iwai 	if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT))
6971da177e4SLinus Torvalds 		return -EBUSY;
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 	/* if userspace tries to mmap beyond end of our buffer, fail */
7004e268db7STakashi Iwai 	if (size > USX2Y_HWDEP_PCM_PAGES) {
7014e268db7STakashi Iwai 		snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
7021da177e4SLinus Torvalds 		return -EINVAL;
7031da177e4SLinus Torvalds 	}
7041da177e4SLinus Torvalds 
705a829dd5bSTakashi Iwai 	if (!usx2y->hwdep_pcm_shm)
7061da177e4SLinus Torvalds 		return -ENODEV;
707a829dd5bSTakashi Iwai 
708bae3ce49STakashi Iwai 	area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops;
709*1c71222eSSuren Baghdasaryan 	vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
7101da177e4SLinus Torvalds 	area->vm_private_data = hw->private_data;
7111da177e4SLinus Torvalds 	return 0;
7121da177e4SLinus Torvalds }
7131da177e4SLinus Torvalds 
snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep * hwdep)714bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
7151da177e4SLinus Torvalds {
716bae3ce49STakashi Iwai 	struct usx2ydev *usx2y = hwdep->private_data;
7174c0a58efSTakashi Iwai 
718a829dd5bSTakashi Iwai 	if (usx2y->hwdep_pcm_shm)
7194e268db7STakashi Iwai 		free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds 
usx2y_hwdep_pcm_new(struct snd_card * card)722bae3ce49STakashi Iwai int usx2y_hwdep_pcm_new(struct snd_card *card)
7231da177e4SLinus Torvalds {
7241da177e4SLinus Torvalds 	int err;
725bbe85bbdSTakashi Iwai 	struct snd_hwdep *hw;
726bbe85bbdSTakashi Iwai 	struct snd_pcm *pcm;
727bae3ce49STakashi Iwai 	struct usb_device *dev = usx2y(card)->dev;
7284c0a58efSTakashi Iwai 
729a829dd5bSTakashi Iwai 	if (nr_of_packs() != 1)
7301da177e4SLinus Torvalds 		return 0;
7311da177e4SLinus Torvalds 
732a829dd5bSTakashi Iwai 	err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw);
733a829dd5bSTakashi Iwai 	if (err < 0)
7341da177e4SLinus Torvalds 		return err;
735cb432379STakashi Iwai 
7361da177e4SLinus Torvalds 	hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
737bae3ce49STakashi Iwai 	hw->private_data = usx2y(card);
738bae3ce49STakashi Iwai 	hw->private_free = snd_usx2y_hwdep_pcm_private_free;
739bae3ce49STakashi Iwai 	hw->ops.open = snd_usx2y_hwdep_pcm_open;
740bae3ce49STakashi Iwai 	hw->ops.release = snd_usx2y_hwdep_pcm_release;
741bae3ce49STakashi Iwai 	hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap;
7421da177e4SLinus Torvalds 	hw->exclusive = 1;
743a5f8661dSMauro Carvalho Chehab 	sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
746a829dd5bSTakashi Iwai 	if (err < 0)
7471da177e4SLinus Torvalds 		return err;
748a829dd5bSTakashi Iwai 
749bae3ce49STakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops);
750bae3ce49STakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops);
7511da177e4SLinus Torvalds 
752bae3ce49STakashi Iwai 	pcm->private_data = usx2y(card)->subs;
7531da177e4SLinus Torvalds 	pcm->info_flags = 0;
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 	sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
7563f0c972aSTakashi Iwai 	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
7571da177e4SLinus Torvalds 				   SNDRV_DMA_TYPE_CONTINUOUS,
7580af0a4feSTakashi Iwai 				   NULL,
7594d1b5303STakashi Iwai 				   64*1024, 128*1024);
7603f0c972aSTakashi Iwai 	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
7611da177e4SLinus Torvalds 				   SNDRV_DMA_TYPE_CONTINUOUS,
7620af0a4feSTakashi Iwai 				   NULL,
7634d1b5303STakashi Iwai 				   64*1024, 128*1024);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	return 0;
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds #else
7691da177e4SLinus Torvalds 
usx2y_hwdep_pcm_new(struct snd_card * card)770bae3ce49STakashi Iwai int usx2y_hwdep_pcm_new(struct snd_card *card)
7711da177e4SLinus Torvalds {
7721da177e4SLinus Torvalds 	return 0;
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds #endif
776