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