xref: /dragonfly/sys/bus/u4b/audio/uaudio_pcm.c (revision 3b964699)
1a963377aSSascha Wildner /* $FreeBSD: head/sys/dev/sound/usb/uaudio_pcm.c 246128 2013-01-30 18:01:20Z sbz $ */
209b9c6f2SSascha Wildner 
309b9c6f2SSascha Wildner /*-
409b9c6f2SSascha Wildner  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
509b9c6f2SSascha Wildner  * Copyright (c) 2006 Hans Petter Selasky
609b9c6f2SSascha Wildner  *
709b9c6f2SSascha Wildner  * Redistribution and use in source and binary forms, with or without
809b9c6f2SSascha Wildner  * modification, are permitted provided that the following conditions
909b9c6f2SSascha Wildner  * are met:
1009b9c6f2SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1109b9c6f2SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1209b9c6f2SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1309b9c6f2SSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1409b9c6f2SSascha Wildner  *    documentation and/or other materials provided with the distribution.
1509b9c6f2SSascha Wildner  *
1609b9c6f2SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1709b9c6f2SSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1809b9c6f2SSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1909b9c6f2SSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2009b9c6f2SSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2109b9c6f2SSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2209b9c6f2SSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2309b9c6f2SSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2409b9c6f2SSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2509b9c6f2SSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2609b9c6f2SSascha Wildner  * SUCH DAMAGE.
2709b9c6f2SSascha Wildner  */
2809b9c6f2SSascha Wildner 
2909b9c6f2SSascha Wildner 
3009b9c6f2SSascha Wildner #include "opt_snd.h"
3109b9c6f2SSascha Wildner 
3209b9c6f2SSascha Wildner #include <dev/sound/pcm/sound.h>
3309b9c6f2SSascha Wildner #include <dev/sound/chip.h>
345a82fc1aSSascha Wildner #include <bus/u4b/audio/uaudio.h>
3509b9c6f2SSascha Wildner 
3609b9c6f2SSascha Wildner #include "mixer_if.h"
3709b9c6f2SSascha Wildner 
3809b9c6f2SSascha Wildner /************************************************************/
3909b9c6f2SSascha Wildner static void *
ua_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)4009b9c6f2SSascha Wildner ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
4109b9c6f2SSascha Wildner {
4209b9c6f2SSascha Wildner 	return (uaudio_chan_init(devinfo, b, c, dir));
4309b9c6f2SSascha Wildner }
4409b9c6f2SSascha Wildner 
4509b9c6f2SSascha Wildner static int
ua_chan_free(kobj_t obj,void * data)4609b9c6f2SSascha Wildner ua_chan_free(kobj_t obj, void *data)
4709b9c6f2SSascha Wildner {
4809b9c6f2SSascha Wildner 	return (uaudio_chan_free(data));
4909b9c6f2SSascha Wildner }
5009b9c6f2SSascha Wildner 
5109b9c6f2SSascha Wildner static int
ua_chan_setformat(kobj_t obj,void * data,uint32_t format)5209b9c6f2SSascha Wildner ua_chan_setformat(kobj_t obj, void *data, uint32_t format)
5309b9c6f2SSascha Wildner {
5409b9c6f2SSascha Wildner 	/*
5509b9c6f2SSascha Wildner 	 * At this point, no need to query as we
5609b9c6f2SSascha Wildner 	 * shouldn't select an unsorted format
5709b9c6f2SSascha Wildner 	 */
5809b9c6f2SSascha Wildner 	return (uaudio_chan_set_param_format(data, format));
5909b9c6f2SSascha Wildner }
6009b9c6f2SSascha Wildner 
6109b9c6f2SSascha Wildner static uint32_t
ua_chan_setspeed(kobj_t obj,void * data,uint32_t speed)6209b9c6f2SSascha Wildner ua_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
6309b9c6f2SSascha Wildner {
6409b9c6f2SSascha Wildner 	return (uaudio_chan_set_param_speed(data, speed));
6509b9c6f2SSascha Wildner }
6609b9c6f2SSascha Wildner 
6709b9c6f2SSascha Wildner static uint32_t
ua_chan_setblocksize(kobj_t obj,void * data,uint32_t blocksize)6809b9c6f2SSascha Wildner ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
6909b9c6f2SSascha Wildner {
7009b9c6f2SSascha Wildner 	return (uaudio_chan_set_param_blocksize(data, blocksize));
7109b9c6f2SSascha Wildner }
7209b9c6f2SSascha Wildner 
7309b9c6f2SSascha Wildner static int
ua_chan_setfragments(kobj_t obj,void * data,uint32_t blocksize,uint32_t blockcount)7409b9c6f2SSascha Wildner ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount)
7509b9c6f2SSascha Wildner {
7609b9c6f2SSascha Wildner 	return (uaudio_chan_set_param_fragments(data, blocksize, blockcount));
7709b9c6f2SSascha Wildner }
7809b9c6f2SSascha Wildner 
7909b9c6f2SSascha Wildner static int
ua_chan_trigger(kobj_t obj,void * data,int go)8009b9c6f2SSascha Wildner ua_chan_trigger(kobj_t obj, void *data, int go)
8109b9c6f2SSascha Wildner {
825e4edd23SMatthew Dillon 	if (PCMTRIG_COMMON(go)) {
8309b9c6f2SSascha Wildner 		if (go == PCMTRIG_START) {
845e4edd23SMatthew Dillon 			uaudio_chan_start(data);
8509b9c6f2SSascha Wildner 		} else {
865e4edd23SMatthew Dillon 			uaudio_chan_stop(data);
8709b9c6f2SSascha Wildner 		}
8809b9c6f2SSascha Wildner 	}
895e4edd23SMatthew Dillon 	return (0);
905e4edd23SMatthew Dillon }
9109b9c6f2SSascha Wildner 
9209b9c6f2SSascha Wildner static uint32_t
ua_chan_getptr(kobj_t obj,void * data)9309b9c6f2SSascha Wildner ua_chan_getptr(kobj_t obj, void *data)
9409b9c6f2SSascha Wildner {
9509b9c6f2SSascha Wildner 	return (uaudio_chan_getptr(data));
9609b9c6f2SSascha Wildner }
9709b9c6f2SSascha Wildner 
9809b9c6f2SSascha Wildner static struct pcmchan_caps *
ua_chan_getcaps(kobj_t obj,void * data)9909b9c6f2SSascha Wildner ua_chan_getcaps(kobj_t obj, void *data)
10009b9c6f2SSascha Wildner {
10109b9c6f2SSascha Wildner 	return (uaudio_chan_getcaps(data));
10209b9c6f2SSascha Wildner }
10309b9c6f2SSascha Wildner 
10409b9c6f2SSascha Wildner static struct pcmchan_matrix *
ua_chan_getmatrix(kobj_t obj,void * data,uint32_t format)10509b9c6f2SSascha Wildner ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format)
10609b9c6f2SSascha Wildner {
10709b9c6f2SSascha Wildner 	return (uaudio_chan_getmatrix(data, format));
10809b9c6f2SSascha Wildner }
10909b9c6f2SSascha Wildner 
11009b9c6f2SSascha Wildner static kobj_method_t ua_chan_methods[] = {
11109b9c6f2SSascha Wildner 	KOBJMETHOD(channel_init, ua_chan_init),
11209b9c6f2SSascha Wildner 	KOBJMETHOD(channel_free, ua_chan_free),
11309b9c6f2SSascha Wildner 	KOBJMETHOD(channel_setformat, ua_chan_setformat),
11409b9c6f2SSascha Wildner 	KOBJMETHOD(channel_setspeed, ua_chan_setspeed),
11509b9c6f2SSascha Wildner 	KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize),
11609b9c6f2SSascha Wildner 	KOBJMETHOD(channel_setfragments, ua_chan_setfragments),
11709b9c6f2SSascha Wildner 	KOBJMETHOD(channel_trigger, ua_chan_trigger),
11809b9c6f2SSascha Wildner 	KOBJMETHOD(channel_getptr, ua_chan_getptr),
11909b9c6f2SSascha Wildner 	KOBJMETHOD(channel_getcaps, ua_chan_getcaps),
12009b9c6f2SSascha Wildner 	KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix),
12109b9c6f2SSascha Wildner 	KOBJMETHOD_END
12209b9c6f2SSascha Wildner };
12309b9c6f2SSascha Wildner 
12409b9c6f2SSascha Wildner CHANNEL_DECLARE(ua_chan);
12509b9c6f2SSascha Wildner 
12609b9c6f2SSascha Wildner /************************************************************/
12709b9c6f2SSascha Wildner static int
ua_mixer_init(struct snd_mixer * m)12809b9c6f2SSascha Wildner ua_mixer_init(struct snd_mixer *m)
12909b9c6f2SSascha Wildner {
13009b9c6f2SSascha Wildner 	return (uaudio_mixer_init_sub(mix_getdevinfo(m), m));
13109b9c6f2SSascha Wildner }
13209b9c6f2SSascha Wildner 
13309b9c6f2SSascha Wildner static int
ua_mixer_set(struct snd_mixer * m,unsigned type,unsigned left,unsigned right)13409b9c6f2SSascha Wildner ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right)
13509b9c6f2SSascha Wildner {
13647b126bbSFrançois Tigeot 	struct lock *lock = mixer_get_lock(m);
13747b126bbSFrançois Tigeot 	uint8_t do_unlock;
13847b126bbSFrançois Tigeot 
139a963377aSSascha Wildner 	if (lockowned(lock)) {
14047b126bbSFrançois Tigeot 		do_unlock = 0;
14147b126bbSFrançois Tigeot 	} else {
14247b126bbSFrançois Tigeot 		do_unlock = 1;
14347b126bbSFrançois Tigeot 		lockmgr(lock, LK_EXCLUSIVE);
14447b126bbSFrançois Tigeot 	}
14509b9c6f2SSascha Wildner 	uaudio_mixer_set(mix_getdevinfo(m), type, left, right);
14647b126bbSFrançois Tigeot 	if (do_unlock) {
14747b126bbSFrançois Tigeot 		lockmgr(lock, LK_RELEASE);
14847b126bbSFrançois Tigeot 	}
14909b9c6f2SSascha Wildner 	return (left | (right << 8));
15009b9c6f2SSascha Wildner }
15109b9c6f2SSascha Wildner 
15209b9c6f2SSascha Wildner static uint32_t
ua_mixer_setrecsrc(struct snd_mixer * m,uint32_t src)15309b9c6f2SSascha Wildner ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
15409b9c6f2SSascha Wildner {
15547b126bbSFrançois Tigeot 	struct lock *lock = mixer_get_lock(m);
15609b9c6f2SSascha Wildner 	int retval;
15747b126bbSFrançois Tigeot 	uint8_t do_unlock;
15847b126bbSFrançois Tigeot 
159a963377aSSascha Wildner 	if (lockowned(lock)) {
16047b126bbSFrançois Tigeot 		do_unlock = 0;
16147b126bbSFrançois Tigeot 	} else {
16247b126bbSFrançois Tigeot 		do_unlock = 1;
16347b126bbSFrançois Tigeot 		lockmgr(lock, LK_EXCLUSIVE);
16447b126bbSFrançois Tigeot 	}
16509b9c6f2SSascha Wildner 	retval = uaudio_mixer_setrecsrc(mix_getdevinfo(m), src);
16647b126bbSFrançois Tigeot 	if (do_unlock) {
16747b126bbSFrançois Tigeot 		lockmgr(lock, LK_RELEASE);
16847b126bbSFrançois Tigeot 	}
16909b9c6f2SSascha Wildner 	return (retval);
17009b9c6f2SSascha Wildner }
17109b9c6f2SSascha Wildner 
17209b9c6f2SSascha Wildner static int
ua_mixer_uninit(struct snd_mixer * m)17309b9c6f2SSascha Wildner ua_mixer_uninit(struct snd_mixer *m)
17409b9c6f2SSascha Wildner {
17509b9c6f2SSascha Wildner 	return (uaudio_mixer_uninit_sub(mix_getdevinfo(m)));
17609b9c6f2SSascha Wildner }
17709b9c6f2SSascha Wildner 
17809b9c6f2SSascha Wildner static kobj_method_t ua_mixer_methods[] = {
17909b9c6f2SSascha Wildner 	KOBJMETHOD(mixer_init, ua_mixer_init),
18009b9c6f2SSascha Wildner 	KOBJMETHOD(mixer_uninit, ua_mixer_uninit),
18109b9c6f2SSascha Wildner 	KOBJMETHOD(mixer_set, ua_mixer_set),
18209b9c6f2SSascha Wildner 	KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc),
18309b9c6f2SSascha Wildner 	KOBJMETHOD_END
18409b9c6f2SSascha Wildner };
18509b9c6f2SSascha Wildner 
18609b9c6f2SSascha Wildner MIXER_DECLARE(ua_mixer);
18709b9c6f2SSascha Wildner /************************************************************/
18809b9c6f2SSascha Wildner 
18909b9c6f2SSascha Wildner 
19009b9c6f2SSascha Wildner static int
ua_probe(device_t dev)19109b9c6f2SSascha Wildner ua_probe(device_t dev)
19209b9c6f2SSascha Wildner {
19309b9c6f2SSascha Wildner 	struct sndcard_func *func;
19409b9c6f2SSascha Wildner 
19509b9c6f2SSascha Wildner 	/* the parent device has already been probed */
19609b9c6f2SSascha Wildner 
19709b9c6f2SSascha Wildner 	func = device_get_ivars(dev);
19809b9c6f2SSascha Wildner 
19909b9c6f2SSascha Wildner 	if ((func == NULL) ||
20009b9c6f2SSascha Wildner 	    (func->func != SCF_PCM)) {
20109b9c6f2SSascha Wildner 		return (ENXIO);
20209b9c6f2SSascha Wildner 	}
20309b9c6f2SSascha Wildner 	device_set_desc(dev, "USB audio");
20409b9c6f2SSascha Wildner 
20509b9c6f2SSascha Wildner 	return (BUS_PROBE_DEFAULT);
20609b9c6f2SSascha Wildner }
20709b9c6f2SSascha Wildner 
20809b9c6f2SSascha Wildner static int
ua_attach(device_t dev)20909b9c6f2SSascha Wildner ua_attach(device_t dev)
21009b9c6f2SSascha Wildner {
21109b9c6f2SSascha Wildner 	return (uaudio_attach_sub(dev, &ua_mixer_class, &ua_chan_class));
21209b9c6f2SSascha Wildner }
21309b9c6f2SSascha Wildner 
21409b9c6f2SSascha Wildner static int
ua_detach(device_t dev)21509b9c6f2SSascha Wildner ua_detach(device_t dev)
21609b9c6f2SSascha Wildner {
21709b9c6f2SSascha Wildner 	return (uaudio_detach_sub(dev));
21809b9c6f2SSascha Wildner }
21909b9c6f2SSascha Wildner 
22009b9c6f2SSascha Wildner /************************************************************/
22109b9c6f2SSascha Wildner 
22209b9c6f2SSascha Wildner static device_method_t ua_pcm_methods[] = {
22309b9c6f2SSascha Wildner 	/* Device interface */
22409b9c6f2SSascha Wildner 	DEVMETHOD(device_probe, ua_probe),
22509b9c6f2SSascha Wildner 	DEVMETHOD(device_attach, ua_attach),
22609b9c6f2SSascha Wildner 	DEVMETHOD(device_detach, ua_detach),
22709b9c6f2SSascha Wildner 
228d3c9c58eSSascha Wildner 	DEVMETHOD_END
22909b9c6f2SSascha Wildner };
23009b9c6f2SSascha Wildner 
23109b9c6f2SSascha Wildner static driver_t ua_pcm_driver = {
23209b9c6f2SSascha Wildner 	"pcm",
23309b9c6f2SSascha Wildner 	ua_pcm_methods,
23409b9c6f2SSascha Wildner 	PCM_SOFTC_SIZE,
23509b9c6f2SSascha Wildner };
23609b9c6f2SSascha Wildner 
237*aa6ac96eSSascha Wildner DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, NULL, NULL);
23809b9c6f2SSascha Wildner MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1);
23909b9c6f2SSascha Wildner MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
24009b9c6f2SSascha Wildner MODULE_VERSION(ua_pcm, 1);
241