xref: /openbmc/linux/sound/usb/usx2y/usx2yhwdeppcm.c (revision 7f927fcc)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
31da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
41da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
51da177e4SLinus Torvalds  *   (at your option) any later version.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
81da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
91da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
101da177e4SLinus Torvalds  *   GNU General Public License for more details.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
131da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
141da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* USX2Y "rawusb" aka hwdep_pcm implementation
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds  Its usb's unableness to atomically handle power of 2 period sized data chuncs
201da177e4SLinus Torvalds  at standard samplerates,
211da177e4SLinus Torvalds  what led to this part of the usx2y module:
221da177e4SLinus Torvalds  It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
231da177e4SLinus Torvalds  The pair uses a hardware dependant alsa-device for mmaped pcm transport.
241da177e4SLinus Torvalds  Advantage achieved:
251da177e4SLinus Torvalds          The usb_hc moves pcm data from/into memory via DMA.
261da177e4SLinus Torvalds          That memory is mmaped by jack's usx2y driver.
271da177e4SLinus Torvalds          Jack's usx2y driver is the first/last to read/write pcm data.
281da177e4SLinus Torvalds          Read/write is a combination of power of 2 period shaping and
291da177e4SLinus Torvalds          float/int conversation.
301da177e4SLinus Torvalds          Compared to mainline alsa/jack we leave out power of 2 period shaping inside
311da177e4SLinus Torvalds          snd-usb-usx2y which needs memcpy() and additional buffers.
321da177e4SLinus Torvalds          As a side effect possible unwanted pcm-data coruption resulting of
331da177e4SLinus Torvalds          standard alsa's snd-usb-usx2y period shaping scheme falls away.
341da177e4SLinus Torvalds          Result is sane jack operation at buffering schemes down to 128frames,
351da177e4SLinus Torvalds          2 periods.
361da177e4SLinus Torvalds          plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
371da177e4SLinus Torvalds          cost of easier triggered i.e. aeolus xruns (128 or 256frames,
381da177e4SLinus Torvalds          2periods works but is useless cause of crackling).
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds  This is a first "proof of concept" implementation.
411da177e4SLinus Torvalds  Later, funcionalities should migrate to more apropriate places:
421da177e4SLinus Torvalds  Userland:
431da177e4SLinus Torvalds  - The jackd could mmap its float-pcm buffers directly from alsa-lib.
441da177e4SLinus Torvalds  - alsa-lib could provide power of 2 period sized shaping combined with int/float
451da177e4SLinus Torvalds    conversation.
461da177e4SLinus Torvalds    Currently the usx2y jack driver provides above 2 services.
471da177e4SLinus Torvalds  Kernel:
481da177e4SLinus Torvalds  - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
491da177e4SLinus Torvalds    devices can use it.
501da177e4SLinus Torvalds    Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
511da177e4SLinus Torvalds */
521da177e4SLinus Torvalds 
53b27c187fSNishanth Aravamudan #include <linux/delay.h>
541da177e4SLinus Torvalds #include "usbusx2yaudio.c"
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) &&  USX2Y_NRPACKS == 1)
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds #include <sound/hwdep.h>
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 
61bbe85bbdSTakashi Iwai static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	struct urb	*urb = subs->completed_urb;
64bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
651da177e4SLinus Torvalds 	int 		i, lens = 0, hwptr_done = subs->hwptr_done;
66bbe85bbdSTakashi Iwai 	struct usX2Ydev	*usX2Y = subs->usX2Y;
671da177e4SLinus Torvalds 	if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
681da177e4SLinus Torvalds 		int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
691da177e4SLinus Torvalds 		if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
701da177e4SLinus Torvalds 			head = 0;
711da177e4SLinus Torvalds 		usX2Y->hwdep_pcm_shm->capture_iso_start = head;
721da177e4SLinus Torvalds 		snd_printdd("cap start %i\n", head);
731da177e4SLinus Torvalds 	}
741da177e4SLinus Torvalds 	for (i = 0; i < nr_of_packs(); i++) {
751da177e4SLinus Torvalds 		if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
76d3d579f8STakashi Iwai 			snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
771da177e4SLinus Torvalds 			return urb->iso_frame_desc[i].status;
781da177e4SLinus Torvalds 		}
791da177e4SLinus Torvalds 		lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 	if ((hwptr_done += lens) >= runtime->buffer_size)
821da177e4SLinus Torvalds 		hwptr_done -= runtime->buffer_size;
831da177e4SLinus Torvalds 	subs->hwptr_done = hwptr_done;
841da177e4SLinus Torvalds 	subs->transfer_done += lens;
851da177e4SLinus Torvalds 	/* update the pointer, call callback if necessary */
861da177e4SLinus Torvalds 	if (subs->transfer_done >= runtime->period_size) {
871da177e4SLinus Torvalds 		subs->transfer_done -= runtime->period_size;
881da177e4SLinus Torvalds 		snd_pcm_period_elapsed(subs->pcm_substream);
891da177e4SLinus Torvalds 	}
901da177e4SLinus Torvalds 	return 0;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
93bbe85bbdSTakashi Iwai static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
94bbe85bbdSTakashi Iwai 					      struct usX2Ydev * usX2Y)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	return (runtime->buffer_size * 1000) / usX2Y->rate + 1;	//FIXME: so far only correct period_size == 2^x ?
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds /*
1001da177e4SLinus Torvalds  * prepare urb for playback data pipe
1011da177e4SLinus Torvalds  *
1021da177e4SLinus Torvalds  * we copy the data directly from the pcm buffer.
1031da177e4SLinus Torvalds  * the current position to be copied is held in hwptr field.
1041da177e4SLinus Torvalds  * since a urb can handle only a single linear buffer, if the total
1051da177e4SLinus Torvalds  * transferred area overflows the buffer boundary, we cannot send
1061da177e4SLinus Torvalds  * it directly from the buffer.  thus the data is once copied to
1071da177e4SLinus Torvalds  * a temporary buffer and urb points to that.
1081da177e4SLinus Torvalds  */
109bbe85bbdSTakashi Iwai static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
1101da177e4SLinus Torvalds 					struct urb *urb)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds 	int count, counts, pack;
113bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = subs->usX2Y;
1141da177e4SLinus Torvalds 	struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
115bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	if (0 > shm->playback_iso_start) {
1181da177e4SLinus Torvalds 		shm->playback_iso_start = shm->captured_iso_head -
1191da177e4SLinus Torvalds 			usX2Y_iso_frames_per_buffer(runtime, usX2Y);
1201da177e4SLinus Torvalds 		if (0 > shm->playback_iso_start)
1211da177e4SLinus Torvalds 			shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
1221da177e4SLinus Torvalds 		shm->playback_iso_head = shm->playback_iso_start;
1231da177e4SLinus Torvalds 	}
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	count = 0;
1261da177e4SLinus Torvalds 	for (pack = 0; pack < nr_of_packs(); pack++) {
1271da177e4SLinus Torvalds 		/* calculate the size of a packet */
1281da177e4SLinus Torvalds 		counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
1291da177e4SLinus Torvalds 		if (counts < 43 || counts > 50) {
130d3d579f8STakashi Iwai 			snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
1311da177e4SLinus Torvalds 			return -EPIPE;
1321da177e4SLinus Torvalds 		}
1331da177e4SLinus Torvalds 		/* set up descriptor */
1341da177e4SLinus Torvalds 		urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
1351da177e4SLinus Torvalds 		urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
1361da177e4SLinus Torvalds 		if (atomic_read(&subs->state) != state_RUNNING)
1371da177e4SLinus Torvalds 			memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
1381da177e4SLinus Torvalds 			       urb->iso_frame_desc[pack].length);
1391da177e4SLinus Torvalds 		if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
1401da177e4SLinus Torvalds 			shm->playback_iso_head = 0;
1411da177e4SLinus Torvalds 		count += counts;
1421da177e4SLinus Torvalds 	}
1431da177e4SLinus Torvalds 	urb->transfer_buffer_length = count * usX2Y->stride;
1441da177e4SLinus Torvalds 	return 0;
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 
148bbe85bbdSTakashi Iwai static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
149bbe85bbdSTakashi Iwai 						     struct urb *urb)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds 	int pack;
1521da177e4SLinus Torvalds 	for (pack = 0; pack < nr_of_packs(); ++pack) {
1531da177e4SLinus Torvalds 		struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
1541da177e4SLinus Torvalds 		if (NULL != subs) {
155bbe85bbdSTakashi Iwai 			struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
1561da177e4SLinus Torvalds 			int head = shm->captured_iso_head + 1;
1571da177e4SLinus Torvalds 			if (head >= ARRAY_SIZE(shm->captured_iso))
1581da177e4SLinus Torvalds 				head = 0;
1591da177e4SLinus Torvalds 			shm->captured_iso[head].frame = urb->start_frame + pack;
1601da177e4SLinus Torvalds 			shm->captured_iso[head].offset = desc->offset;
1611da177e4SLinus Torvalds 			shm->captured_iso[head].length = desc->actual_length;
1621da177e4SLinus Torvalds 			shm->captured_iso_head = head;
1631da177e4SLinus Torvalds 			shm->captured_iso_frames++;
1641da177e4SLinus Torvalds 		}
1651da177e4SLinus Torvalds 		if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
1661da177e4SLinus Torvalds 		    desc->length >= SSS)
1671da177e4SLinus Torvalds 			desc->offset -= (SSS - desc->length);
1681da177e4SLinus Torvalds 	}
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
171bbe85bbdSTakashi Iwai static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
172bbe85bbdSTakashi Iwai 						 struct snd_usX2Y_substream *capsubs2,
173bbe85bbdSTakashi Iwai 						 struct snd_usX2Y_substream *playbacksubs,
174bbe85bbdSTakashi Iwai 						 int frame)
1751da177e4SLinus Torvalds {
1761da177e4SLinus Torvalds 	int err, state;
1771da177e4SLinus Torvalds 	struct urb *urb = playbacksubs->completed_urb;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	state = atomic_read(&playbacksubs->state);
1801da177e4SLinus Torvalds 	if (NULL != urb) {
1811da177e4SLinus Torvalds 		if (state == state_RUNNING)
1821da177e4SLinus Torvalds 			usX2Y_urb_play_retire(playbacksubs, urb);
183cb432379STakashi Iwai 		else if (state >= state_PRERUNNING)
1841da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1851da177e4SLinus Torvalds 	} else {
1861da177e4SLinus Torvalds 		switch (state) {
1871da177e4SLinus Torvalds 		case state_STARTING1:
1881da177e4SLinus Torvalds 			urb = playbacksubs->urb[0];
1891da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1901da177e4SLinus Torvalds 			break;
1911da177e4SLinus Torvalds 		case state_STARTING2:
1921da177e4SLinus Torvalds 			urb = playbacksubs->urb[1];
1931da177e4SLinus Torvalds 			atomic_inc(&playbacksubs->state);
1941da177e4SLinus Torvalds 			break;
1951da177e4SLinus Torvalds 		}
1961da177e4SLinus Torvalds 	}
1971da177e4SLinus Torvalds 	if (urb) {
1981da177e4SLinus Torvalds 		if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
1991da177e4SLinus Torvalds 		    (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
2001da177e4SLinus Torvalds 			return err;
2011da177e4SLinus Torvalds 		}
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	playbacksubs->completed_urb = NULL;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	state = atomic_read(&capsubs->state);
2071da177e4SLinus Torvalds 	if (state >= state_PREPARED) {
2081da177e4SLinus Torvalds 		if (state == state_RUNNING) {
2091da177e4SLinus Torvalds 			if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
2101da177e4SLinus Torvalds 				return err;
211cb432379STakashi Iwai 		} else if (state >= state_PRERUNNING)
2121da177e4SLinus Torvalds 			atomic_inc(&capsubs->state);
2131da177e4SLinus Torvalds 		usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
2141da177e4SLinus Torvalds 		if (NULL != capsubs2)
2151da177e4SLinus Torvalds 			usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
2161da177e4SLinus Torvalds 		if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
2171da177e4SLinus Torvalds 			return err;
2181da177e4SLinus Torvalds 		if (NULL != capsubs2)
2191da177e4SLinus Torvalds 			if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
2201da177e4SLinus Torvalds 				return err;
2211da177e4SLinus Torvalds 	}
2221da177e4SLinus Torvalds 	capsubs->completed_urb = NULL;
2231da177e4SLinus Torvalds 	if (NULL != capsubs2)
2241da177e4SLinus Torvalds 		capsubs2->completed_urb = NULL;
2251da177e4SLinus Torvalds 	return 0;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
2301da177e4SLinus Torvalds {
231bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *subs = urb->context;
232bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = subs->usX2Y;
233bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
236bbe85bbdSTakashi Iwai 		snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
237bbe85bbdSTakashi Iwai 			    usb_get_current_frame_number(usX2Y->chip.dev),
238bbe85bbdSTakashi Iwai 			    subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
239bbe85bbdSTakashi Iwai 			    urb->status, urb->start_frame);
2401da177e4SLinus Torvalds 		return;
2411da177e4SLinus Torvalds 	}
2421da177e4SLinus Torvalds 	if (unlikely(urb->status)) {
2431da177e4SLinus Torvalds 		usX2Y_error_urb_status(usX2Y, subs, urb);
2441da177e4SLinus Torvalds 		return;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
2471da177e4SLinus Torvalds 		subs->completed_urb = urb;
2481da177e4SLinus Torvalds 	else {
2491da177e4SLinus Torvalds 		usX2Y_error_sequence(usX2Y, subs, urb);
2501da177e4SLinus Torvalds 		return;
2511da177e4SLinus Torvalds 	}
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
2541da177e4SLinus Torvalds 	capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
2551da177e4SLinus Torvalds 	playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
2561da177e4SLinus Torvalds 	if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
2571da177e4SLinus Torvalds 	    (NULL == capsubs2 || capsubs2->completed_urb) &&
2581da177e4SLinus Torvalds 	    (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
2591da177e4SLinus Torvalds 		if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
2601da177e4SLinus Torvalds 			if (nr_of_packs() <= urb->start_frame &&
2611da177e4SLinus Torvalds 			    urb->start_frame <= (2 * nr_of_packs() - 1))	// uhci and ohci
2621da177e4SLinus Torvalds 				usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
2631da177e4SLinus Torvalds 			else
2641da177e4SLinus Torvalds 				usX2Y->wait_iso_frame +=  nr_of_packs();
2651da177e4SLinus Torvalds 		} else {
2661da177e4SLinus Torvalds 			snd_printdd("\n");
2671da177e4SLinus Torvalds 			usX2Y_clients_stop(usX2Y);
2681da177e4SLinus Torvalds 		}
2691da177e4SLinus Torvalds 	}
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds static void usX2Y_hwdep_urb_release(struct urb **urb)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	usb_kill_urb(*urb);
2761da177e4SLinus Torvalds 	usb_free_urb(*urb);
2771da177e4SLinus Torvalds 	*urb = NULL;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds /*
2811da177e4SLinus Torvalds  * release a substream
2821da177e4SLinus Torvalds  */
283bbe85bbdSTakashi Iwai static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
2841da177e4SLinus Torvalds {
2851da177e4SLinus Torvalds 	int i;
2861da177e4SLinus Torvalds 	snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
2871da177e4SLinus Torvalds 	for (i = 0; i < NRURBS; i++)
2881da177e4SLinus Torvalds 		usX2Y_hwdep_urb_release(subs->urb + i);
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
291bbe85bbdSTakashi Iwai static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
2941da177e4SLinus Torvalds 	usX2Y->prepare_subs = NULL;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
2981da177e4SLinus Torvalds {
299bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *subs = urb->context;
300bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = subs->usX2Y;
301bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
3021da177e4SLinus Torvalds 	if (NULL != prepare_subs &&
3031da177e4SLinus Torvalds 	    urb->start_frame == prepare_subs->urb[0]->start_frame) {
3041da177e4SLinus Torvalds 		atomic_inc(&prepare_subs->state);
3051da177e4SLinus Torvalds 		if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
306bbe85bbdSTakashi Iwai 			struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
3071da177e4SLinus Torvalds 			if (cap_subs2 != NULL)
3081da177e4SLinus Torvalds 				atomic_inc(&cap_subs2->state);
3091da177e4SLinus Torvalds 		}
3101da177e4SLinus Torvalds 		usX2Y_usbpcm_subs_startup_finish(usX2Y);
3111da177e4SLinus Torvalds 		wake_up(&usX2Y->prepare_wait_queue);
3121da177e4SLinus Torvalds 	}
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	i_usX2Y_usbpcm_urb_complete(urb, regs);
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds /*
3181da177e4SLinus Torvalds  * initialize a substream's urbs
3191da177e4SLinus Torvalds  */
320bbe85bbdSTakashi Iwai static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
3211da177e4SLinus Torvalds {
3221da177e4SLinus Torvalds 	int i;
3231da177e4SLinus Torvalds 	unsigned int pipe;
3241da177e4SLinus Torvalds 	int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
3251da177e4SLinus Torvalds 	struct usb_device *dev = subs->usX2Y->chip.dev;
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
3281da177e4SLinus Torvalds 			usb_rcvisocpipe(dev, subs->endpoint);
3291da177e4SLinus Torvalds 	subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
3301da177e4SLinus Torvalds 	if (!subs->maxpacksize)
3311da177e4SLinus Torvalds 		return -EINVAL;
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	/* allocate and initialize data urbs */
3341da177e4SLinus Torvalds 	for (i = 0; i < NRURBS; i++) {
3351da177e4SLinus Torvalds 		struct urb **purb = subs->urb + i;
3361da177e4SLinus Torvalds 		if (*purb) {
3371da177e4SLinus Torvalds 			usb_kill_urb(*purb);
3381da177e4SLinus Torvalds 			continue;
3391da177e4SLinus Torvalds 		}
3401da177e4SLinus Torvalds 		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
3411da177e4SLinus Torvalds 		if (NULL == *purb) {
3421da177e4SLinus Torvalds 			usX2Y_usbpcm_urbs_release(subs);
3431da177e4SLinus Torvalds 			return -ENOMEM;
3441da177e4SLinus Torvalds 		}
3451da177e4SLinus Torvalds 		(*purb)->transfer_buffer = is_playback ?
3461da177e4SLinus Torvalds 			subs->usX2Y->hwdep_pcm_shm->playback : (
3471da177e4SLinus Torvalds 				subs->endpoint == 0x8 ?
3481da177e4SLinus Torvalds 				subs->usX2Y->hwdep_pcm_shm->capture0x8 :
3491da177e4SLinus Torvalds 				subs->usX2Y->hwdep_pcm_shm->capture0xA);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 		(*purb)->dev = dev;
3521da177e4SLinus Torvalds 		(*purb)->pipe = pipe;
3531da177e4SLinus Torvalds 		(*purb)->number_of_packets = nr_of_packs();
3541da177e4SLinus Torvalds 		(*purb)->context = subs;
3551da177e4SLinus Torvalds 		(*purb)->interval = 1;
3561da177e4SLinus Torvalds 		(*purb)->complete = i_usX2Y_usbpcm_subs_startup;
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds 	return 0;
3591da177e4SLinus Torvalds }
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds /*
3621da177e4SLinus Torvalds  * free the buffer
3631da177e4SLinus Torvalds  */
364bbe85bbdSTakashi Iwai static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
3651da177e4SLinus Torvalds {
366bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
367bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *subs = runtime->private_data,
3681da177e4SLinus Torvalds 		*cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
36912aa7579SIngo Molnar 	mutex_lock(&subs->usX2Y->prepare_mutex);
3701da177e4SLinus Torvalds 	snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
373bbe85bbdSTakashi Iwai 		struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
3741da177e4SLinus Torvalds 		atomic_set(&subs->state, state_STOPPED);
3751da177e4SLinus Torvalds 		usX2Y_usbpcm_urbs_release(subs);
3761da177e4SLinus Torvalds 		if (!cap_subs->pcm_substream ||
3771da177e4SLinus Torvalds 		    !cap_subs->pcm_substream->runtime ||
3781da177e4SLinus Torvalds 		    !cap_subs->pcm_substream->runtime->status ||
3791da177e4SLinus Torvalds 		    cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
3801da177e4SLinus Torvalds 			atomic_set(&cap_subs->state, state_STOPPED);
3811da177e4SLinus Torvalds 			if (NULL != cap_subs2)
3821da177e4SLinus Torvalds 				atomic_set(&cap_subs2->state, state_STOPPED);
3831da177e4SLinus Torvalds 			usX2Y_usbpcm_urbs_release(cap_subs);
3841da177e4SLinus Torvalds 			if (NULL != cap_subs2)
3851da177e4SLinus Torvalds 				usX2Y_usbpcm_urbs_release(cap_subs2);
3861da177e4SLinus Torvalds 		}
3871da177e4SLinus Torvalds 	} else {
388bbe85bbdSTakashi Iwai 		struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
3891da177e4SLinus Torvalds 		if (atomic_read(&playback_subs->state) < state_PREPARED) {
3901da177e4SLinus Torvalds 			atomic_set(&subs->state, state_STOPPED);
3911da177e4SLinus Torvalds 			if (NULL != cap_subs2)
3921da177e4SLinus Torvalds 				atomic_set(&cap_subs2->state, state_STOPPED);
3931da177e4SLinus Torvalds 			usX2Y_usbpcm_urbs_release(subs);
3941da177e4SLinus Torvalds 			if (NULL != cap_subs2)
3951da177e4SLinus Torvalds 				usX2Y_usbpcm_urbs_release(cap_subs2);
3961da177e4SLinus Torvalds 		}
3971da177e4SLinus Torvalds 	}
39812aa7579SIngo Molnar 	mutex_unlock(&subs->usX2Y->prepare_mutex);
3991da177e4SLinus Torvalds 	return snd_pcm_lib_free_pages(substream);
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
402bbe85bbdSTakashi Iwai static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
4031da177e4SLinus Torvalds {
404bbe85bbdSTakashi Iwai 	struct usX2Ydev * usX2Y = subs->usX2Y;
4051da177e4SLinus Torvalds 	usX2Y->prepare_subs = subs;
4061da177e4SLinus Torvalds 	subs->urb[0]->start_frame = -1;
4077f927fccSAlexey Dobriyan 	smp_wmb();	// Make sure above modifications are seen by i_usX2Y_subs_startup()
4081da177e4SLinus Torvalds 	usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
411bbe85bbdSTakashi Iwai static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	int	p, u, err,
4141da177e4SLinus Torvalds 		stream = subs->pcm_substream->stream;
415bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = subs->usX2Y;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	if (SNDRV_PCM_STREAM_CAPTURE == stream) {
4181da177e4SLinus Torvalds 		usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
4191da177e4SLinus Torvalds 		usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
4201da177e4SLinus Torvalds 	}
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	for (p = 0; 3 >= (stream + p); p += 2) {
423bbe85bbdSTakashi Iwai 		struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
4241da177e4SLinus Torvalds 		if (subs != NULL) {
4251da177e4SLinus Torvalds 			if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
4261da177e4SLinus Torvalds 				return err;
4271da177e4SLinus Torvalds 			subs->completed_urb = NULL;
4281da177e4SLinus Torvalds 		}
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	for (p = 0; p < 4; p++) {
432bbe85bbdSTakashi Iwai 		struct snd_usX2Y_substream *subs = usX2Y->subs[p];
4331da177e4SLinus Torvalds 		if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
4341da177e4SLinus Torvalds 			goto start;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 	usX2Y->wait_iso_frame = -1;
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds  start:
4391da177e4SLinus Torvalds 	usX2Y_usbpcm_subs_startup(subs);
4401da177e4SLinus Torvalds 	for (u = 0; u < NRURBS; u++) {
4411da177e4SLinus Torvalds 		for (p = 0; 3 >= (stream + p); p += 2) {
442bbe85bbdSTakashi Iwai 			struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
4431da177e4SLinus Torvalds 			if (subs != NULL) {
4441da177e4SLinus Torvalds 				struct urb *urb = subs->urb[u];
4451da177e4SLinus Torvalds 				if (usb_pipein(urb->pipe)) {
4461da177e4SLinus Torvalds 					unsigned long pack;
4471da177e4SLinus Torvalds 					if (0 == u)
4481da177e4SLinus Torvalds 						atomic_set(&subs->state, state_STARTING3);
4491da177e4SLinus Torvalds 					urb->dev = usX2Y->chip.dev;
4501da177e4SLinus Torvalds 					urb->transfer_flags = URB_ISO_ASAP;
4511da177e4SLinus Torvalds 					for (pack = 0; pack < nr_of_packs(); pack++) {
4521da177e4SLinus Torvalds 						urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
4531da177e4SLinus Torvalds 						urb->iso_frame_desc[pack].length = subs->maxpacksize;
4541da177e4SLinus Torvalds 					}
4551da177e4SLinus Torvalds 					urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
4561da177e4SLinus Torvalds 					if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
4571da177e4SLinus Torvalds 						snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
4581da177e4SLinus Torvalds 						err = -EPIPE;
4591da177e4SLinus Torvalds 						goto cleanup;
4601da177e4SLinus Torvalds 					}  else {
4611da177e4SLinus Torvalds 						snd_printdd("%i\n", urb->start_frame);
4621da177e4SLinus Torvalds 						if (0 > usX2Y->wait_iso_frame)
4631da177e4SLinus Torvalds 							usX2Y->wait_iso_frame = urb->start_frame;
4641da177e4SLinus Torvalds 					}
4651da177e4SLinus Torvalds 					urb->transfer_flags = 0;
4661da177e4SLinus Torvalds 				} else {
4671da177e4SLinus Torvalds 					atomic_set(&subs->state, state_STARTING1);
4681da177e4SLinus Torvalds 					break;
4691da177e4SLinus Torvalds 				}
4701da177e4SLinus Torvalds 			}
4711da177e4SLinus Torvalds 		}
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 	err = 0;
4741da177e4SLinus Torvalds 	wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
4751da177e4SLinus Torvalds 	if (atomic_read(&subs->state) != state_PREPARED)
4761da177e4SLinus Torvalds 		err = -EPIPE;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds  cleanup:
4791da177e4SLinus Torvalds 	if (err) {
4801da177e4SLinus Torvalds 		usX2Y_subs_startup_finish(usX2Y);	// Call it now
4811da177e4SLinus Torvalds 		usX2Y_clients_stop(usX2Y);		// something is completely wroong > stop evrything
4821da177e4SLinus Torvalds 	}
4831da177e4SLinus Torvalds 	return err;
4841da177e4SLinus Torvalds }
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds /*
4871da177e4SLinus Torvalds  * prepare callback
4881da177e4SLinus Torvalds  *
4891da177e4SLinus Torvalds  * set format and initialize urbs
4901da177e4SLinus Torvalds  */
491bbe85bbdSTakashi Iwai static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
4921da177e4SLinus Torvalds {
493bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
494bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *subs = runtime->private_data;
495bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = subs->usX2Y;
496bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
4971da177e4SLinus Torvalds 	int err = 0;
4981da177e4SLinus Torvalds 	snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	if (NULL == usX2Y->hwdep_pcm_shm) {
501bbe85bbdSTakashi Iwai 		if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
5021da177e4SLinus Torvalds 			return -ENOMEM;
503bbe85bbdSTakashi Iwai 		memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
5041da177e4SLinus Torvalds 	}
5051da177e4SLinus Torvalds 
50612aa7579SIngo Molnar 	mutex_lock(&usX2Y->prepare_mutex);
5071da177e4SLinus Torvalds 	usX2Y_subs_prepare(subs);
5081da177e4SLinus Torvalds // Start hardware streams
5091da177e4SLinus Torvalds // SyncStream first....
5101da177e4SLinus Torvalds 	if (atomic_read(&capsubs->state) < state_PREPARED) {
5111da177e4SLinus Torvalds 		if (usX2Y->format != runtime->format)
5121da177e4SLinus Torvalds 			if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
5131da177e4SLinus Torvalds 				goto up_prepare_mutex;
5141da177e4SLinus Torvalds 		if (usX2Y->rate != runtime->rate)
5151da177e4SLinus Torvalds 			if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
5161da177e4SLinus Torvalds 				goto up_prepare_mutex;
517bbe85bbdSTakashi Iwai 		snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
518bbe85bbdSTakashi Iwai 			    "self" : "playpipe");
5191da177e4SLinus Torvalds 		if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
5201da177e4SLinus Torvalds 			goto up_prepare_mutex;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 	if (subs != capsubs) {
5241da177e4SLinus Torvalds 		usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
5251da177e4SLinus Torvalds 		if (atomic_read(&subs->state) < state_PREPARED) {
526bbe85bbdSTakashi Iwai 			while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
527bbe85bbdSTakashi Iwai 			       usX2Y->hwdep_pcm_shm->captured_iso_frames) {
528bbe85bbdSTakashi Iwai 				snd_printdd("Wait: iso_frames_per_buffer=%i,"
529bbe85bbdSTakashi Iwai 					    "captured_iso_frames=%i\n",
530bbe85bbdSTakashi Iwai 					    usX2Y_iso_frames_per_buffer(runtime, usX2Y),
531bbe85bbdSTakashi Iwai 					    usX2Y->hwdep_pcm_shm->captured_iso_frames);
532b27c187fSNishanth Aravamudan 				if (msleep_interruptible(10)) {
5331da177e4SLinus Torvalds 					err = -ERESTARTSYS;
5341da177e4SLinus Torvalds 					goto up_prepare_mutex;
5351da177e4SLinus Torvalds 				}
5361da177e4SLinus Torvalds 			}
5371da177e4SLinus Torvalds 			if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
5381da177e4SLinus Torvalds 				goto up_prepare_mutex;
5391da177e4SLinus Torvalds 		}
540bbe85bbdSTakashi Iwai 		snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
541bbe85bbdSTakashi Iwai 			    usX2Y_iso_frames_per_buffer(runtime, usX2Y),
542bbe85bbdSTakashi Iwai 			    usX2Y->hwdep_pcm_shm->captured_iso_frames);
5431da177e4SLinus Torvalds 	} else
5441da177e4SLinus Torvalds 		usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds  up_prepare_mutex:
54712aa7579SIngo Molnar 	mutex_unlock(&usX2Y->prepare_mutex);
5481da177e4SLinus Torvalds 	return err;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
551bbe85bbdSTakashi Iwai static struct snd_pcm_hardware snd_usX2Y_4c =
5521da177e4SLinus Torvalds {
5531da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
5541da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
5551da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID),
5561da177e4SLinus Torvalds 	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
5571da177e4SLinus Torvalds 	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
5581da177e4SLinus Torvalds 	.rate_min =                44100,
5591da177e4SLinus Torvalds 	.rate_max =                48000,
5601da177e4SLinus Torvalds 	.channels_min =            2,
5611da177e4SLinus Torvalds 	.channels_max =            4,
5621da177e4SLinus Torvalds 	.buffer_bytes_max =	(2*128*1024),
5631da177e4SLinus Torvalds 	.period_bytes_min =	64,
5641da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
5651da177e4SLinus Torvalds 	.periods_min =		2,
5661da177e4SLinus Torvalds 	.periods_max =		1024,
5671da177e4SLinus Torvalds 	.fifo_size =              0
5681da177e4SLinus Torvalds };
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 
572bbe85bbdSTakashi Iwai static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
5731da177e4SLinus Torvalds {
574bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream	*subs = ((struct snd_usX2Y_substream **)
5751da177e4SLinus Torvalds 					 snd_pcm_substream_chip(substream))[substream->stream];
576bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime	*runtime = substream->runtime;
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 	if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
5791da177e4SLinus Torvalds 		return -EBUSY;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
5821da177e4SLinus Torvalds 		(subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
5831da177e4SLinus Torvalds 	runtime->private_data = subs;
5841da177e4SLinus Torvalds 	subs->pcm_substream = substream;
5851da177e4SLinus Torvalds 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
5861da177e4SLinus Torvalds 	return 0;
5871da177e4SLinus Torvalds }
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 
590bbe85bbdSTakashi Iwai static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
5911da177e4SLinus Torvalds {
592bbe85bbdSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
593bbe85bbdSTakashi Iwai 	struct snd_usX2Y_substream *subs = runtime->private_data;
594cb432379STakashi Iwai 
5951da177e4SLinus Torvalds 	subs->pcm_substream = NULL;
596cb432379STakashi Iwai 	return 0;
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 
600bbe85bbdSTakashi Iwai static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
6011da177e4SLinus Torvalds {
6021da177e4SLinus Torvalds 	.open =		snd_usX2Y_usbpcm_open,
6031da177e4SLinus Torvalds 	.close =	snd_usX2Y_usbpcm_close,
6041da177e4SLinus Torvalds 	.ioctl =	snd_pcm_lib_ioctl,
6051da177e4SLinus Torvalds 	.hw_params =	snd_usX2Y_pcm_hw_params,
6061da177e4SLinus Torvalds 	.hw_free =	snd_usX2Y_usbpcm_hw_free,
6071da177e4SLinus Torvalds 	.prepare =	snd_usX2Y_usbpcm_prepare,
6081da177e4SLinus Torvalds 	.trigger =	snd_usX2Y_pcm_trigger,
6091da177e4SLinus Torvalds 	.pointer =	snd_usX2Y_pcm_pointer,
6101da177e4SLinus Torvalds };
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 
613bbe85bbdSTakashi Iwai static int usX2Y_pcms_lock_check(struct snd_card *card)
6141da177e4SLinus Torvalds {
6151da177e4SLinus Torvalds 	struct list_head *list;
616bbe85bbdSTakashi Iwai 	struct snd_device *dev;
617bbe85bbdSTakashi Iwai 	struct snd_pcm *pcm;
6181da177e4SLinus Torvalds 	int err = 0;
6191da177e4SLinus Torvalds 	list_for_each(list, &card->devices) {
6201da177e4SLinus Torvalds 		dev = snd_device(list);
6211da177e4SLinus Torvalds 		if (dev->type != SNDRV_DEV_PCM)
6221da177e4SLinus Torvalds 			continue;
6231da177e4SLinus Torvalds 		pcm = dev->device_data;
62412aa7579SIngo Molnar 		mutex_lock(&pcm->open_mutex);
6251da177e4SLinus Torvalds 	}
6261da177e4SLinus Torvalds 	list_for_each(list, &card->devices) {
6271da177e4SLinus Torvalds 		int s;
6281da177e4SLinus Torvalds 		dev = snd_device(list);
6291da177e4SLinus Torvalds 		if (dev->type != SNDRV_DEV_PCM)
6301da177e4SLinus Torvalds 			continue;
6311da177e4SLinus Torvalds 		pcm = dev->device_data;
6321da177e4SLinus Torvalds 		for (s = 0; s < 2; ++s) {
633bbe85bbdSTakashi Iwai 			struct snd_pcm_substream *substream;
6341da177e4SLinus Torvalds 			substream = pcm->streams[s].substream;
635443feb88SKarsten Wiese 			if (substream && substream->ffile != NULL)
6361da177e4SLinus Torvalds 				err = -EBUSY;
6371da177e4SLinus Torvalds 		}
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds 	return err;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 
643bbe85bbdSTakashi Iwai static void usX2Y_pcms_unlock(struct snd_card *card)
6441da177e4SLinus Torvalds {
6451da177e4SLinus Torvalds 	struct list_head *list;
646bbe85bbdSTakashi Iwai 	struct snd_device *dev;
647bbe85bbdSTakashi Iwai 	struct snd_pcm *pcm;
6481da177e4SLinus Torvalds 	list_for_each(list, &card->devices) {
6491da177e4SLinus Torvalds 		dev = snd_device(list);
6501da177e4SLinus Torvalds 		if (dev->type != SNDRV_DEV_PCM)
6511da177e4SLinus Torvalds 			continue;
6521da177e4SLinus Torvalds 		pcm = dev->device_data;
65312aa7579SIngo Molnar 		mutex_unlock(&pcm->open_mutex);
6541da177e4SLinus Torvalds 	}
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 
658bbe85bbdSTakashi Iwai static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
6591da177e4SLinus Torvalds {
6601da177e4SLinus Torvalds 	// we need to be the first
661bbe85bbdSTakashi Iwai 	struct snd_card *card = hw->card;
6621da177e4SLinus Torvalds 	int err = usX2Y_pcms_lock_check(card);
6631da177e4SLinus Torvalds 	if (0 == err)
6641da177e4SLinus Torvalds 		usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
6651da177e4SLinus Torvalds 	usX2Y_pcms_unlock(card);
6661da177e4SLinus Torvalds 	return err;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 
670bbe85bbdSTakashi Iwai static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
6711da177e4SLinus Torvalds {
672bbe85bbdSTakashi Iwai 	struct snd_card *card = hw->card;
6731da177e4SLinus Torvalds 	int err = usX2Y_pcms_lock_check(card);
6741da177e4SLinus Torvalds 	if (0 == err)
6751da177e4SLinus Torvalds 		usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
6761da177e4SLinus Torvalds 	usX2Y_pcms_unlock(card);
6771da177e4SLinus Torvalds 	return err;
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
6821da177e4SLinus Torvalds {
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
6871da177e4SLinus Torvalds {
6881da177e4SLinus Torvalds }
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
6921da177e4SLinus Torvalds {
6931da177e4SLinus Torvalds 	unsigned long offset;
6941da177e4SLinus Torvalds 	struct page *page;
6951da177e4SLinus Torvalds 	void *vaddr;
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	offset = area->vm_pgoff << PAGE_SHIFT;
6981da177e4SLinus Torvalds 	offset += address - area->vm_start;
6991da177e4SLinus Torvalds 	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
700bbe85bbdSTakashi Iwai 	vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
7011da177e4SLinus Torvalds 	page = virt_to_page(vaddr);
7021cdca61bSHugh Dickins 	get_page(page);
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	if (type)
7051da177e4SLinus Torvalds 		*type = VM_FAULT_MINOR;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	return page;
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
7121da177e4SLinus Torvalds 	.open = snd_usX2Y_hwdep_pcm_vm_open,
7131da177e4SLinus Torvalds 	.close = snd_usX2Y_hwdep_pcm_vm_close,
7141da177e4SLinus Torvalds 	.nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
7151da177e4SLinus Torvalds };
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 
718bbe85bbdSTakashi Iwai static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
7191da177e4SLinus Torvalds {
7201da177e4SLinus Torvalds 	unsigned long	size = (unsigned long)(area->vm_end - area->vm_start);
721bbe85bbdSTakashi Iwai 	struct usX2Ydev	*usX2Y = hw->private_data;
7221da177e4SLinus Torvalds 
723cb432379STakashi Iwai 	if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
7241da177e4SLinus Torvalds 		return -EBUSY;
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	/* if userspace tries to mmap beyond end of our buffer, fail */
727bbe85bbdSTakashi Iwai 	if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
728bbe85bbdSTakashi Iwai 		snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
7291da177e4SLinus Torvalds 		return -EINVAL;
7301da177e4SLinus Torvalds 	}
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	if (!usX2Y->hwdep_pcm_shm) {
7331da177e4SLinus Torvalds 		return -ENODEV;
7341da177e4SLinus Torvalds 	}
7351da177e4SLinus Torvalds 	area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
7361da177e4SLinus Torvalds 	area->vm_flags |= VM_RESERVED;
7371da177e4SLinus Torvalds 	area->vm_private_data = hw->private_data;
7381da177e4SLinus Torvalds 	return 0;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 
742bbe85bbdSTakashi Iwai static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
7431da177e4SLinus Torvalds {
744bbe85bbdSTakashi Iwai 	struct usX2Ydev *usX2Y = hwdep->private_data;
7451da177e4SLinus Torvalds 	if (NULL != usX2Y->hwdep_pcm_shm)
746bbe85bbdSTakashi Iwai 		snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 
750bbe85bbdSTakashi Iwai int usX2Y_hwdep_pcm_new(struct snd_card *card)
7511da177e4SLinus Torvalds {
7521da177e4SLinus Torvalds 	int err;
753bbe85bbdSTakashi Iwai 	struct snd_hwdep *hw;
754bbe85bbdSTakashi Iwai 	struct snd_pcm *pcm;
7551da177e4SLinus Torvalds 	struct usb_device *dev = usX2Y(card)->chip.dev;
7561da177e4SLinus Torvalds 	if (1 != nr_of_packs())
7571da177e4SLinus Torvalds 		return 0;
7581da177e4SLinus Torvalds 
759cb432379STakashi Iwai 	if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
7601da177e4SLinus Torvalds 		return err;
761cb432379STakashi Iwai 
7621da177e4SLinus Torvalds 	hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
7631da177e4SLinus Torvalds 	hw->private_data = usX2Y(card);
7641da177e4SLinus Torvalds 	hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
7651da177e4SLinus Torvalds 	hw->ops.open = snd_usX2Y_hwdep_pcm_open;
7661da177e4SLinus Torvalds 	hw->ops.release = snd_usX2Y_hwdep_pcm_release;
7671da177e4SLinus Torvalds 	hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
7681da177e4SLinus Torvalds 	hw->exclusive = 1;
7691da177e4SLinus Torvalds 	sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 	err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
7721da177e4SLinus Torvalds 	if (err < 0) {
7731da177e4SLinus Torvalds 		return err;
7741da177e4SLinus Torvalds 	}
7751da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
7761da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds 	pcm->private_data = usX2Y(card)->subs;
7791da177e4SLinus Torvalds 	pcm->info_flags = 0;
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
7821da177e4SLinus Torvalds 	if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
7831da177e4SLinus Torvalds 						     SNDRV_DMA_TYPE_CONTINUOUS,
7841da177e4SLinus Torvalds 						     snd_dma_continuous_data(GFP_KERNEL),
7851da177e4SLinus Torvalds 						     64*1024, 128*1024)) ||
7861da177e4SLinus Torvalds 	    0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
7871da177e4SLinus Torvalds 	    					     SNDRV_DMA_TYPE_CONTINUOUS,
7881da177e4SLinus Torvalds 	    					     snd_dma_continuous_data(GFP_KERNEL),
7891da177e4SLinus Torvalds 						     64*1024, 128*1024))) {
7901da177e4SLinus Torvalds 		return err;
7911da177e4SLinus Torvalds 	}
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	return 0;
7951da177e4SLinus Torvalds }
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds #else
7981da177e4SLinus Torvalds 
799bbe85bbdSTakashi Iwai int usX2Y_hwdep_pcm_new(struct snd_card *card)
8001da177e4SLinus Torvalds {
8011da177e4SLinus Torvalds 	return 0;
8021da177e4SLinus Torvalds }
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds #endif
805