xref: /dragonfly/sys/dev/sound/pci/emu10kx-pcm.c (revision f354e0e6)
12a1ad637SFrançois Tigeot /*-
22a1ad637SFrançois Tigeot  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
32a1ad637SFrançois Tigeot  * Copyright (c) 2003-2007 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru>
42a1ad637SFrançois Tigeot  * All rights reserved.
52a1ad637SFrançois Tigeot  *
62a1ad637SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
72a1ad637SFrançois Tigeot  * modification, are permitted provided that the following conditions
82a1ad637SFrançois Tigeot  * are met:
92a1ad637SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
102a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
112a1ad637SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
122a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
132a1ad637SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
142a1ad637SFrançois Tigeot  *
152a1ad637SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162a1ad637SFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172a1ad637SFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182a1ad637SFrançois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192a1ad637SFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202a1ad637SFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212a1ad637SFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222a1ad637SFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
232a1ad637SFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242a1ad637SFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252a1ad637SFrançois Tigeot  * SUCH DAMAGE.
262a1ad637SFrançois Tigeot  *
272a1ad637SFrançois Tigeot  * $FreeBSD: head/sys/dev/sound/pci/emu10kx-pcm.c 246128 2013-01-30 18:01:20Z sbz $
282a1ad637SFrançois Tigeot  */
292a1ad637SFrançois Tigeot 
302a1ad637SFrançois Tigeot #include <sys/param.h>
312a1ad637SFrançois Tigeot #include <sys/types.h>
322a1ad637SFrançois Tigeot #include <sys/bus.h>
332a1ad637SFrançois Tigeot #include <sys/rman.h>
342a1ad637SFrançois Tigeot #include <sys/systm.h>
352a1ad637SFrançois Tigeot #include <sys/sbuf.h>
362a1ad637SFrançois Tigeot #include <sys/queue.h>
372a1ad637SFrançois Tigeot #include <sys/lock.h>
382a1ad637SFrançois Tigeot 
392a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
402a1ad637SFrançois Tigeot #include "opt_snd.h"
412a1ad637SFrançois Tigeot #endif
422a1ad637SFrançois Tigeot 
432a1ad637SFrançois Tigeot #include <dev/sound/chip.h>
442a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
452a1ad637SFrançois Tigeot #include <dev/sound/pcm/ac97.h>
462a1ad637SFrançois Tigeot 
472a1ad637SFrançois Tigeot #include "mixer_if.h"
482a1ad637SFrançois Tigeot 
492a1ad637SFrançois Tigeot #include <dev/sound/pci/emuxkireg.h>
502a1ad637SFrançois Tigeot #include <dev/sound/pci/emu10kx.h>
512a1ad637SFrançois Tigeot 
522a1ad637SFrançois Tigeot struct emu_pcm_pchinfo {
532a1ad637SFrançois Tigeot 	int		spd;
542a1ad637SFrançois Tigeot 	int		fmt;
552a1ad637SFrançois Tigeot 	unsigned int	blksz;
562a1ad637SFrançois Tigeot 	int		run;
572a1ad637SFrançois Tigeot 	struct emu_voice *master;
582a1ad637SFrançois Tigeot 	struct emu_voice *slave;
592a1ad637SFrançois Tigeot 	struct snd_dbuf	*buffer;
602a1ad637SFrançois Tigeot 	struct pcm_channel *channel;
612a1ad637SFrançois Tigeot 	struct emu_pcm_info *pcm;
622a1ad637SFrançois Tigeot 	int		timer;
632a1ad637SFrançois Tigeot };
642a1ad637SFrançois Tigeot 
652a1ad637SFrançois Tigeot struct emu_pcm_rchinfo {
662a1ad637SFrançois Tigeot 	int		spd;
672a1ad637SFrançois Tigeot 	int		fmt;
682a1ad637SFrançois Tigeot 	unsigned int	blksz;
692a1ad637SFrançois Tigeot 	int		run;
702a1ad637SFrançois Tigeot 	uint32_t 	idxreg;
712a1ad637SFrançois Tigeot 	uint32_t	basereg;
722a1ad637SFrançois Tigeot 	uint32_t	sizereg;
732a1ad637SFrançois Tigeot 	uint32_t	setupreg;
742a1ad637SFrançois Tigeot 	uint32_t	irqmask;
752a1ad637SFrançois Tigeot 	uint32_t	iprmask;
762a1ad637SFrançois Tigeot 	int 		ihandle;
772a1ad637SFrançois Tigeot 	struct snd_dbuf	*buffer;
782a1ad637SFrançois Tigeot 	struct pcm_channel *channel;
792a1ad637SFrançois Tigeot 	struct emu_pcm_info *pcm;
802a1ad637SFrançois Tigeot 	int		timer;
812a1ad637SFrançois Tigeot };
822a1ad637SFrançois Tigeot 
832a1ad637SFrançois Tigeot /* XXX Hardware playback channels */
842a1ad637SFrançois Tigeot #define	MAX_CHANNELS	4
852a1ad637SFrançois Tigeot 
862a1ad637SFrançois Tigeot #if MAX_CHANNELS > 13
872a1ad637SFrançois Tigeot #error	Too many hardware channels defined. 13 is the maximum
882a1ad637SFrançois Tigeot #endif
892a1ad637SFrançois Tigeot 
902a1ad637SFrançois Tigeot struct emu_pcm_info {
9167931cc4SFrançois Tigeot 	struct lock		*lock;
922a1ad637SFrançois Tigeot 	device_t		dev;		/* device information */
932a1ad637SFrançois Tigeot 	struct emu_sc_info 	*card;
942a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo	pch[MAX_CHANNELS];	/* hardware channels */
952a1ad637SFrançois Tigeot 	int			pnum;		/* next free channel number */
962a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo	rch_adc;
972a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo	rch_efx;
982a1ad637SFrançois Tigeot 	struct emu_route	rt;
992a1ad637SFrançois Tigeot 	struct emu_route	rt_mono;
1002a1ad637SFrançois Tigeot 	int			route;
1012a1ad637SFrançois Tigeot 	int			ihandle;	/* interrupt handler */
1022a1ad637SFrançois Tigeot 	unsigned int		bufsz;
1032a1ad637SFrançois Tigeot 	int			is_emu10k1;
1042a1ad637SFrançois Tigeot 	struct ac97_info	*codec;
1052a1ad637SFrançois Tigeot 	uint32_t 		ac97_state[0x7F];
1062a1ad637SFrançois Tigeot 	kobj_class_t		ac97_mixerclass;
1072a1ad637SFrançois Tigeot 	uint32_t		ac97_recdevs;
1082a1ad637SFrançois Tigeot 	uint32_t		ac97_playdevs;
1092a1ad637SFrançois Tigeot 	struct snd_mixer	*sm;
1102a1ad637SFrançois Tigeot 	int			mch_disabled;
1112a1ad637SFrançois Tigeot 	unsigned int		emu10k1_volcache[2][2];
1122a1ad637SFrançois Tigeot };
1132a1ad637SFrançois Tigeot 
1142a1ad637SFrançois Tigeot 
1152a1ad637SFrançois Tigeot static uint32_t emu_rfmt_adc[] = {
1162a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 1, 0),
1172a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 2, 0),
1182a1ad637SFrançois Tigeot 	0
1192a1ad637SFrançois Tigeot };
1202a1ad637SFrançois Tigeot static struct pcmchan_caps emu_reccaps_adc = {
1212a1ad637SFrançois Tigeot 	8000, 48000, emu_rfmt_adc, 0
1222a1ad637SFrançois Tigeot };
1232a1ad637SFrançois Tigeot 
1242a1ad637SFrançois Tigeot static uint32_t emu_rfmt_efx[] = {
1252a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 1, 0),
1262a1ad637SFrançois Tigeot 	0
1272a1ad637SFrançois Tigeot };
1282a1ad637SFrançois Tigeot 
1292a1ad637SFrançois Tigeot static struct pcmchan_caps emu_reccaps_efx_live = {
1302a1ad637SFrançois Tigeot 	48000*32, 48000*32, emu_rfmt_efx, 0
1312a1ad637SFrançois Tigeot };
1322a1ad637SFrançois Tigeot 
1332a1ad637SFrançois Tigeot static struct pcmchan_caps emu_reccaps_efx_audigy = {
1342a1ad637SFrançois Tigeot 	48000*64, 48000*64, emu_rfmt_efx, 0
1352a1ad637SFrançois Tigeot };
1362a1ad637SFrançois Tigeot 
1372a1ad637SFrançois Tigeot static int emu_rates_live[] = {
1382a1ad637SFrançois Tigeot 	48000*32
1392a1ad637SFrançois Tigeot };
1402a1ad637SFrançois Tigeot 
1412a1ad637SFrançois Tigeot static int emu_rates_audigy[] = {
1422a1ad637SFrançois Tigeot 	48000*64
1432a1ad637SFrançois Tigeot };
1442a1ad637SFrançois Tigeot 
1452a1ad637SFrançois Tigeot static uint32_t emu_pfmt[] = {
1462a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_U8, 1, 0),
1472a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_U8, 2, 0),
1482a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 1, 0),
1492a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 2, 0),
1502a1ad637SFrançois Tigeot 	0
1512a1ad637SFrançois Tigeot };
1522a1ad637SFrançois Tigeot static uint32_t emu_pfmt_mono[] = {
1532a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_U8, 1, 0),
1542a1ad637SFrançois Tigeot 	SND_FORMAT(AFMT_S16_LE, 1, 0),
1552a1ad637SFrançois Tigeot 	0
1562a1ad637SFrançois Tigeot };
1572a1ad637SFrançois Tigeot 
1582a1ad637SFrançois Tigeot static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0};
1592a1ad637SFrançois Tigeot static struct pcmchan_caps emu_playcaps_mono = {4000, 48000, emu_pfmt_mono, 0};
1602a1ad637SFrançois Tigeot 
1612a1ad637SFrançois Tigeot static int emu10k1_adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
1622a1ad637SFrançois Tigeot /* audigy supports 12kHz. */
1632a1ad637SFrançois Tigeot static int emu10k2_adcspeed[9] = {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
1642a1ad637SFrançois Tigeot 
1652a1ad637SFrançois Tigeot static uint32_t emu_pcm_intr(void *pcm, uint32_t stat);
1662a1ad637SFrançois Tigeot 
1672a1ad637SFrançois Tigeot static const struct emu_dspmix_props_k1 {
1682a1ad637SFrançois Tigeot 	uint8_t	present;
1692a1ad637SFrançois Tigeot 	uint8_t	recdev;
1702a1ad637SFrançois Tigeot 	int8_t	input;
1712a1ad637SFrançois Tigeot } dspmix_k1 [SOUND_MIXER_NRDEVICES] = {
1722a1ad637SFrançois Tigeot 	/* no mixer device for ac97 */		/* in0 AC97 */
1732a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL1] = {1, 1, 1},	/* in1 CD SPDIF */
1742a1ad637SFrançois Tigeot 	/* not connected */			/* in2 (zoom) */
1752a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL2] = {1, 1, 3},	/* in3 toslink */
1762a1ad637SFrançois Tigeot 	[SOUND_MIXER_LINE2] =    {1, 1, 4},	/* in4 Line-In2 */
1772a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL3] = {1, 1, 5},	/* in5 on-card  SPDIF */
1782a1ad637SFrançois Tigeot 	[SOUND_MIXER_LINE3] =    {1, 1, 6},	/* in6 AUX2 */
1792a1ad637SFrançois Tigeot 	/* not connected */			/* in7 */
1802a1ad637SFrançois Tigeot };
1812a1ad637SFrançois Tigeot static const struct emu_dspmix_props_k2 {
1822a1ad637SFrançois Tigeot 	uint8_t	present;
1832a1ad637SFrançois Tigeot 	uint8_t	recdev;
1842a1ad637SFrançois Tigeot 	int8_t	input;
1852a1ad637SFrançois Tigeot } dspmix_k2 [SOUND_MIXER_NRDEVICES] = {
1862a1ad637SFrançois Tigeot 	[SOUND_MIXER_VOLUME] =	{1, 0, (-1)},
1872a1ad637SFrançois Tigeot 	[SOUND_MIXER_PCM] =	{1, 0, (-1)},
1882a1ad637SFrançois Tigeot 
1892a1ad637SFrançois Tigeot 	/* no mixer device */			/* in0 AC97 */
1902a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL1] = {1, 1, 1},	/* in1 CD SPDIF */
1912a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL2] = {1, 1, 2},	/* in2 COAX SPDIF */
1922a1ad637SFrançois Tigeot 	/* not connected */			/* in3 */
1932a1ad637SFrançois Tigeot 	[SOUND_MIXER_LINE2] =    {1, 1, 4},	/* in4 Line-In2 */
1942a1ad637SFrançois Tigeot 	[SOUND_MIXER_DIGITAL3] = {1, 1, 5},	/* in5 on-card  SPDIF */
1952a1ad637SFrançois Tigeot 	[SOUND_MIXER_LINE3] =    {1, 1, 6},	/* in6 AUX2 */
1962a1ad637SFrançois Tigeot 	/* not connected */			/* in7 */
1972a1ad637SFrançois Tigeot };
1982a1ad637SFrançois Tigeot 
1992a1ad637SFrançois Tigeot static int
emu_dspmixer_init(struct snd_mixer * m)2002a1ad637SFrançois Tigeot emu_dspmixer_init(struct snd_mixer *m)
2012a1ad637SFrançois Tigeot {
2022a1ad637SFrançois Tigeot 	struct emu_pcm_info	*sc;
2032a1ad637SFrançois Tigeot 	int i;
2042a1ad637SFrançois Tigeot 	int p, r;
2052a1ad637SFrançois Tigeot 
2062a1ad637SFrançois Tigeot 	p = 0;
2072a1ad637SFrançois Tigeot 	r = 0;
2082a1ad637SFrançois Tigeot 
2092a1ad637SFrançois Tigeot 	sc = mix_getdevinfo(m);
2102a1ad637SFrançois Tigeot 
2112a1ad637SFrançois Tigeot 	if (sc->route == RT_FRONT) {
2122a1ad637SFrançois Tigeot 		/* create submixer for AC97 codec */
2132a1ad637SFrançois Tigeot 		if ((sc->ac97_mixerclass != NULL) && (sc->codec != NULL)) {
2142a1ad637SFrançois Tigeot 			sc->sm = mixer_create(sc->dev, sc->ac97_mixerclass, sc->codec, "ac97");
2152a1ad637SFrançois Tigeot 			if (sc->sm != NULL) {
2162a1ad637SFrançois Tigeot 				p = mix_getdevs(sc->sm);
2172a1ad637SFrançois Tigeot 				r = mix_getrecdevs(sc->sm);
2182a1ad637SFrançois Tigeot 			}
2192a1ad637SFrançois Tigeot 		}
2202a1ad637SFrançois Tigeot 
2212a1ad637SFrançois Tigeot 		sc->ac97_playdevs = p;
2222a1ad637SFrançois Tigeot 		sc->ac97_recdevs = r;
2232a1ad637SFrançois Tigeot 	}
2242a1ad637SFrançois Tigeot 
2252a1ad637SFrançois Tigeot 	/* This two are always here */
2262a1ad637SFrançois Tigeot 	p |= (1 << SOUND_MIXER_PCM);
2272a1ad637SFrançois Tigeot 	p |= (1 << SOUND_MIXER_VOLUME);
2282a1ad637SFrançois Tigeot 
2292a1ad637SFrançois Tigeot 	if (sc->route == RT_FRONT) {
2302a1ad637SFrançois Tigeot 		if (sc->is_emu10k1) {
2312a1ad637SFrançois Tigeot 			for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
2322a1ad637SFrançois Tigeot 				if (dspmix_k1[i].present)
2332a1ad637SFrançois Tigeot 					p |= (1 << i);
2342a1ad637SFrançois Tigeot 				if (dspmix_k1[i].recdev)
2352a1ad637SFrançois Tigeot 					r |= (1 << i);
2362a1ad637SFrançois Tigeot 			}
2372a1ad637SFrançois Tigeot 		} else {
2382a1ad637SFrançois Tigeot 			for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
2392a1ad637SFrançois Tigeot 				if (dspmix_k2[i].present)
2402a1ad637SFrançois Tigeot 					p |= (1 << i);
2412a1ad637SFrançois Tigeot 				if (dspmix_k2[i].recdev)
2422a1ad637SFrançois Tigeot 					r |= (1 << i);
2432a1ad637SFrançois Tigeot 			}
2442a1ad637SFrançois Tigeot 		}
2452a1ad637SFrançois Tigeot 	}
2462a1ad637SFrançois Tigeot 
2472a1ad637SFrançois Tigeot 	mix_setdevs(m, p);
2482a1ad637SFrançois Tigeot 	mix_setrecdevs(m, r);
2492a1ad637SFrançois Tigeot 
2502a1ad637SFrançois Tigeot 	return (0);
2512a1ad637SFrançois Tigeot }
2522a1ad637SFrançois Tigeot 
2532a1ad637SFrançois Tigeot static int
emu_dspmixer_uninit(struct snd_mixer * m)2542a1ad637SFrançois Tigeot emu_dspmixer_uninit(struct snd_mixer *m)
2552a1ad637SFrançois Tigeot {
2562a1ad637SFrançois Tigeot 	struct emu_pcm_info	*sc;
2572a1ad637SFrançois Tigeot 	int err = 0;
2582a1ad637SFrançois Tigeot 
2592a1ad637SFrançois Tigeot 	/* drop submixer for AC97 codec */
2602a1ad637SFrançois Tigeot 	sc = mix_getdevinfo(m);
2610a9407b7SSascha Wildner 	if (sc->sm != NULL) {
2622a1ad637SFrançois Tigeot 		err = mixer_delete(sc->sm);
2632a1ad637SFrançois Tigeot 		if (err)
2642a1ad637SFrançois Tigeot 			return (err);
2652a1ad637SFrançois Tigeot 		sc->sm = NULL;
2660a9407b7SSascha Wildner 	}
2672a1ad637SFrançois Tigeot 	return (0);
2682a1ad637SFrançois Tigeot }
2692a1ad637SFrançois Tigeot 
2702a1ad637SFrançois Tigeot static int
emu_dspmixer_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)2712a1ad637SFrançois Tigeot emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
2722a1ad637SFrançois Tigeot {
2732a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc;
2742a1ad637SFrançois Tigeot 
2752a1ad637SFrançois Tigeot 	sc = mix_getdevinfo(m);
2762a1ad637SFrançois Tigeot 
2772a1ad637SFrançois Tigeot 	switch (dev) {
2782a1ad637SFrançois Tigeot 	case SOUND_MIXER_VOLUME:
2792a1ad637SFrançois Tigeot 		switch (sc->route) {
2802a1ad637SFrançois Tigeot 		case RT_FRONT:
2812a1ad637SFrançois Tigeot 			if (sc->sm != NULL)
2822a1ad637SFrançois Tigeot 				mix_set(sc->sm, dev, left, right);
2832a1ad637SFrançois Tigeot 			if (sc->mch_disabled) {
2842a1ad637SFrançois Tigeot 				/* In emu10k1 case PCM volume does not affect
2852a1ad637SFrançois Tigeot 				   sound routed to rear & center/sub (it is connected
2862a1ad637SFrançois Tigeot 				   to AC97 codec). Calculate it manually. */
2872a1ad637SFrançois Tigeot 				/* This really should belong to emu10kx.c */
2882a1ad637SFrançois Tigeot 				if (sc->is_emu10k1) {
2892a1ad637SFrançois Tigeot 					sc->emu10k1_volcache[0][0] = left;
2902a1ad637SFrançois Tigeot 					left = left * sc->emu10k1_volcache[1][0] / 100;
2912a1ad637SFrançois Tigeot 					sc->emu10k1_volcache[0][1] = right;
2922a1ad637SFrançois Tigeot 					right = right * sc->emu10k1_volcache[1][1] / 100;
2932a1ad637SFrançois Tigeot 				}
2942a1ad637SFrançois Tigeot 
2952a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_MASTER_REAR_L, left);
2962a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_MASTER_REAR_R, right);
2972a1ad637SFrançois Tigeot 				if (!sc->is_emu10k1) {
2982a1ad637SFrançois Tigeot 					emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2);
2992a1ad637SFrançois Tigeot 					emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2);
3002a1ad637SFrançois Tigeot 					/* XXX side */
3012a1ad637SFrançois Tigeot 				}
3022a1ad637SFrançois Tigeot 			} /* mch disabled */
3032a1ad637SFrançois Tigeot 			break;
3042a1ad637SFrançois Tigeot 		case RT_REAR:
3052a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_MASTER_REAR_L, left);
3062a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_MASTER_REAR_R, right);
3072a1ad637SFrançois Tigeot 			break;
3082a1ad637SFrançois Tigeot 		case RT_CENTER:
3092a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2);
3102a1ad637SFrançois Tigeot 			break;
3112a1ad637SFrançois Tigeot 		case RT_SUB:
3122a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2);
3132a1ad637SFrançois Tigeot 			break;
3142a1ad637SFrançois Tigeot 		}
3152a1ad637SFrançois Tigeot 		break;
3162a1ad637SFrançois Tigeot 	case SOUND_MIXER_PCM:
3172a1ad637SFrançois Tigeot 		switch (sc->route) {
3182a1ad637SFrançois Tigeot 		case RT_FRONT:
3192a1ad637SFrançois Tigeot 			if (sc->sm != NULL)
3202a1ad637SFrançois Tigeot 				mix_set(sc->sm, dev, left, right);
3212a1ad637SFrançois Tigeot 			if (sc->mch_disabled) {
3222a1ad637SFrançois Tigeot 				/* See SOUND_MIXER_VOLUME case */
3232a1ad637SFrançois Tigeot 				if (sc->is_emu10k1) {
3242a1ad637SFrançois Tigeot 					sc->emu10k1_volcache[1][0] = left;
3252a1ad637SFrançois Tigeot 					left = left * sc->emu10k1_volcache[0][0] / 100;
3262a1ad637SFrançois Tigeot 					sc->emu10k1_volcache[1][1] = right;
3272a1ad637SFrançois Tigeot 					right = right * sc->emu10k1_volcache[0][1] / 100;
3282a1ad637SFrançois Tigeot 				}
3292a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_MASTER_REAR_L, left);
3302a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_MASTER_REAR_R, right);
3312a1ad637SFrançois Tigeot 
3322a1ad637SFrançois Tigeot 				if (!sc->is_emu10k1) {
3332a1ad637SFrançois Tigeot 					emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2);
3342a1ad637SFrançois Tigeot 					emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2);
3352a1ad637SFrançois Tigeot 					/* XXX side */
3362a1ad637SFrançois Tigeot 				}
3372a1ad637SFrançois Tigeot 			} /* mch_disabled */
3382a1ad637SFrançois Tigeot 			break;
3392a1ad637SFrançois Tigeot 		case RT_REAR:
3402a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_FX2_REAR_L, left);
3412a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_FX3_REAR_R, right);
3422a1ad637SFrançois Tigeot 			break;
3432a1ad637SFrançois Tigeot 		case RT_CENTER:
3442a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_FX4_CENTER, (left+right)/2);
3452a1ad637SFrançois Tigeot 			break;
3462a1ad637SFrançois Tigeot 		case RT_SUB:
3472a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_FX5_SUBWOOFER, (left+right)/2);
3482a1ad637SFrançois Tigeot 			break;
3492a1ad637SFrançois Tigeot 		}
3502a1ad637SFrançois Tigeot 		break;
3512a1ad637SFrançois Tigeot 	case SOUND_MIXER_DIGITAL1:	/* CD SPDIF, in1 */
3522a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN1_FRONT_L, left);
3532a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN1_FRONT_R, right);
3542a1ad637SFrançois Tigeot 		break;
3552a1ad637SFrançois Tigeot 	case SOUND_MIXER_DIGITAL2:
3562a1ad637SFrançois Tigeot 			if (sc->is_emu10k1) {
3572a1ad637SFrançois Tigeot 				/* TOSLink, in3 */
3582a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_IN3_FRONT_L, left);
3592a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_IN3_FRONT_R, right);
3602a1ad637SFrançois Tigeot 			} else {
3612a1ad637SFrançois Tigeot 				/* COAX SPDIF, in2 */
3622a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_IN2_FRONT_L, left);
3632a1ad637SFrançois Tigeot 				emumix_set_volume(sc->card, M_IN2_FRONT_R, right);
3642a1ad637SFrançois Tigeot 			}
3652a1ad637SFrançois Tigeot 		break;
3662a1ad637SFrançois Tigeot 	case SOUND_MIXER_LINE2:		/* Line-In2, in4 */
3672a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN4_FRONT_L, left);
3682a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN4_FRONT_R, right);
3692a1ad637SFrançois Tigeot 		break;
3702a1ad637SFrançois Tigeot 	case SOUND_MIXER_DIGITAL3:	/* on-card SPDIF, in5 */
3712a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN5_FRONT_L, left);
3722a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN5_FRONT_R, right);
3732a1ad637SFrançois Tigeot 		break;
3742a1ad637SFrançois Tigeot 	case SOUND_MIXER_LINE3:		/* AUX2, in6 */
3752a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN6_FRONT_L, left);
3762a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN6_FRONT_R, right);
3772a1ad637SFrançois Tigeot 		break;
3782a1ad637SFrançois Tigeot 	default:
3792a1ad637SFrançois Tigeot 		if (sc->sm != NULL) {
3802a1ad637SFrançois Tigeot 			/* XXX emumix_set_volume is not required here */
3812a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN0_FRONT_L, 100);
3822a1ad637SFrançois Tigeot 			emumix_set_volume(sc->card, M_IN0_FRONT_R, 100);
3832a1ad637SFrançois Tigeot 			mix_set(sc->sm, dev, left, right);
3842a1ad637SFrançois Tigeot 		} else
3852a1ad637SFrançois Tigeot 			device_printf(sc->dev, "mixer error: unknown device %d\n", dev);
3862a1ad637SFrançois Tigeot 	}
3872a1ad637SFrançois Tigeot 	return  (0);
3882a1ad637SFrançois Tigeot }
3892a1ad637SFrançois Tigeot 
3902a1ad637SFrançois Tigeot static u_int32_t
emu_dspmixer_setrecsrc(struct snd_mixer * m,u_int32_t src)3912a1ad637SFrançois Tigeot emu_dspmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
3922a1ad637SFrançois Tigeot {
3932a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc;
3942a1ad637SFrançois Tigeot 	int i;
3952a1ad637SFrançois Tigeot 	u_int32_t recmask;
3962a1ad637SFrançois Tigeot 	int	input[8];
3972a1ad637SFrançois Tigeot 
3982a1ad637SFrançois Tigeot 	sc = mix_getdevinfo(m);
3992a1ad637SFrançois Tigeot 	recmask = 0;
4002a1ad637SFrançois Tigeot 	for (i=0; i < 8; i++)
4012a1ad637SFrançois Tigeot 		input[i]=0;
4022a1ad637SFrançois Tigeot 
4032a1ad637SFrançois Tigeot 	if (sc->sm != NULL)
4042a1ad637SFrançois Tigeot 		if ((src & sc->ac97_recdevs) !=0)
4052a1ad637SFrançois Tigeot 			if (mix_setrecsrc(sc->sm, src & sc->ac97_recdevs) == 0) {
4062a1ad637SFrançois Tigeot 				recmask |= (src & sc->ac97_recdevs);
4072a1ad637SFrançois Tigeot 				/* Recording from AC97 codec.
4082a1ad637SFrançois Tigeot 				   Enable AC97 route to rec on DSP */
4092a1ad637SFrançois Tigeot 				input[0] = 1;
4102a1ad637SFrançois Tigeot 			}
4112a1ad637SFrançois Tigeot 	if (sc->is_emu10k1) {
4122a1ad637SFrançois Tigeot 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
4132a1ad637SFrançois Tigeot 			if (dspmix_k1[i].recdev)
4142a1ad637SFrançois Tigeot 				if ((src & (1 << i)) == ((uint32_t)1 << i)) {
4152a1ad637SFrançois Tigeot 				recmask |= (1 << i);
4162a1ad637SFrançois Tigeot 				/* enable device i */
4172a1ad637SFrançois Tigeot 				input[dspmix_k1[i].input] = 1;
4182a1ad637SFrançois Tigeot 				}
4192a1ad637SFrançois Tigeot 		}
4202a1ad637SFrançois Tigeot 	} else {
4212a1ad637SFrançois Tigeot 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
4222a1ad637SFrançois Tigeot 			if (dspmix_k2[i].recdev)
4232a1ad637SFrançois Tigeot 				if ((src & (1 << i)) == ((uint32_t)1 << i)) {
4242a1ad637SFrançois Tigeot 				recmask |= (1 << i);
4252a1ad637SFrançois Tigeot 				/* enable device i */
4262a1ad637SFrançois Tigeot 				input[dspmix_k2[i].input] = 1;
4272a1ad637SFrançois Tigeot 				}
4282a1ad637SFrançois Tigeot 		}
4292a1ad637SFrançois Tigeot 	}
4302a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN0_REC_L, input[0] == 1 ? 100 : 0);
4312a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN0_REC_R, input[0] == 1 ? 100 : 0);
4322a1ad637SFrançois Tigeot 
4332a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN1_REC_L, input[1] == 1 ? 100 : 0);
4342a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN1_REC_R, input[1] == 1 ? 100 : 0);
4352a1ad637SFrançois Tigeot 
4362a1ad637SFrançois Tigeot 	if (!sc->is_emu10k1) {
4372a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_IN2_REC_L, input[2] == 1 ? 100 : 0);
4382a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_IN2_REC_R, input[2] == 1 ? 100 : 0);
4392a1ad637SFrançois Tigeot 	}
4402a1ad637SFrançois Tigeot 
4412a1ad637SFrançois Tigeot 	if (sc->is_emu10k1) {
4422a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_IN3_REC_L, input[3] == 1 ? 100 : 0);
4432a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_IN3_REC_R, input[3] == 1 ? 100 : 0);
4442a1ad637SFrançois Tigeot 	}
4452a1ad637SFrançois Tigeot 
4462a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN4_REC_L, input[4] == 1 ? 100 : 0);
4472a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN4_REC_R, input[4] == 1 ? 100 : 0);
4482a1ad637SFrançois Tigeot 
4492a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN5_REC_L, input[5] == 1 ? 100 : 0);
4502a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN5_REC_R, input[5] == 1 ? 100 : 0);
4512a1ad637SFrançois Tigeot 
4522a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN6_REC_L, input[6] == 1 ? 100 : 0);
4532a1ad637SFrançois Tigeot 	emumix_set_volume(sc->card, M_IN6_REC_R, input[6] == 1 ? 100 : 0);
4542a1ad637SFrançois Tigeot 
4552a1ad637SFrançois Tigeot 	/* XXX check for K1/k2 differences? */
4562a1ad637SFrançois Tigeot 	if ((src & (1 << SOUND_MIXER_PCM)) == (1 << SOUND_MIXER_PCM)) {
4572a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX0_REC_L, emumix_get_volume(sc->card, M_FX0_FRONT_L));
4582a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX1_REC_R, emumix_get_volume(sc->card, M_FX1_FRONT_R));
4592a1ad637SFrançois Tigeot 	} else {
4602a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX0_REC_L, 0);
4612a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX1_REC_R, 0);
4622a1ad637SFrançois Tigeot 	}
4632a1ad637SFrançois Tigeot 
4642a1ad637SFrançois Tigeot 	return (recmask);
4652a1ad637SFrançois Tigeot }
4662a1ad637SFrançois Tigeot 
4672a1ad637SFrançois Tigeot static kobj_method_t emudspmixer_methods[] = {
4682a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_init,		emu_dspmixer_init),
4692a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_uninit,	emu_dspmixer_uninit),
4702a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_set,		emu_dspmixer_set),
4712a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_setrecsrc,	emu_dspmixer_setrecsrc),
4722a1ad637SFrançois Tigeot 	KOBJMETHOD_END
4732a1ad637SFrançois Tigeot };
4742a1ad637SFrançois Tigeot MIXER_DECLARE(emudspmixer);
4752a1ad637SFrançois Tigeot 
4762a1ad637SFrançois Tigeot static int
emu_efxmixer_init(struct snd_mixer * m)4772a1ad637SFrançois Tigeot emu_efxmixer_init(struct snd_mixer *m)
4782a1ad637SFrançois Tigeot {
4792a1ad637SFrançois Tigeot 	mix_setdevs(m, SOUND_MASK_VOLUME);
4802a1ad637SFrançois Tigeot 	mix_setrecdevs(m, SOUND_MASK_MONITOR);
4812a1ad637SFrançois Tigeot 	return (0);
4822a1ad637SFrançois Tigeot }
4832a1ad637SFrançois Tigeot 
4842a1ad637SFrançois Tigeot static int
emu_efxmixer_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)4852a1ad637SFrançois Tigeot emu_efxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
4862a1ad637SFrançois Tigeot {
4872a1ad637SFrançois Tigeot 	if (left + right == 200) return (0);
4882a1ad637SFrançois Tigeot 	return  (0);
4892a1ad637SFrançois Tigeot }
4902a1ad637SFrançois Tigeot 
4912a1ad637SFrançois Tigeot static u_int32_t
emu_efxmixer_setrecsrc(struct snd_mixer * m __unused,u_int32_t src __unused)4922a1ad637SFrançois Tigeot emu_efxmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused)
4932a1ad637SFrançois Tigeot {
4942a1ad637SFrançois Tigeot 	return (SOUND_MASK_MONITOR);
4952a1ad637SFrançois Tigeot }
4962a1ad637SFrançois Tigeot 
4972a1ad637SFrançois Tigeot static kobj_method_t emuefxmixer_methods[] = {
4982a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_init,		emu_efxmixer_init),
4992a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_set,		emu_efxmixer_set),
5002a1ad637SFrançois Tigeot 	KOBJMETHOD(mixer_setrecsrc,	emu_efxmixer_setrecsrc),
5012a1ad637SFrançois Tigeot 	KOBJMETHOD_END
5022a1ad637SFrançois Tigeot };
5032a1ad637SFrançois Tigeot MIXER_DECLARE(emuefxmixer);
5042a1ad637SFrançois Tigeot 
5052a1ad637SFrançois Tigeot /*
5062a1ad637SFrançois Tigeot  * AC97 emulation code for Audigy and later cards.
5072a1ad637SFrançois Tigeot  * Some parts of AC97 codec are not used by hardware, but can be used
5082a1ad637SFrançois Tigeot  * to change some DSP controls via AC97 mixer interface. This includes:
5092a1ad637SFrançois Tigeot  * - master volume controls MASTER_FRONT_[R|L]
5102a1ad637SFrançois Tigeot  * - pcm volume controls FX[0|1]_FRONT_[R|L]
5112a1ad637SFrançois Tigeot  * - rec volume controls MASTER_REC_[R|L]
5122a1ad637SFrançois Tigeot  * We do it because we need to put it under user control....
5132a1ad637SFrançois Tigeot  * We also keep some parts of AC97 disabled to get better sound quality
5142a1ad637SFrançois Tigeot  */
5152a1ad637SFrançois Tigeot 
5162a1ad637SFrançois Tigeot #define	AC97LEFT(x)	((x & 0x7F00)>>8)
5172a1ad637SFrançois Tigeot #define	AC97RIGHT(x)	(x & 0x007F)
5182a1ad637SFrançois Tigeot #define	AC97MUTE(x)	((x & 0x8000)>>15)
5192a1ad637SFrançois Tigeot #define	BIT4_TO100(x)	(100-(x)*100/(0x0f))
5202a1ad637SFrançois Tigeot #define	BIT6_TO100(x)	(100-(x)*100/(0x3f))
5212a1ad637SFrançois Tigeot #define	BIT4_TO255(x)	(255-(x)*255/(0x0f))
5222a1ad637SFrançois Tigeot #define	BIT6_TO255(x)	(255-(x)*255/(0x3f))
5232a1ad637SFrançois Tigeot #define	V100_TOBIT6(x)	(0x3f*(100-x)/100)
5242a1ad637SFrançois Tigeot #define	V100_TOBIT4(x)	(0x0f*(100-x)/100)
5252a1ad637SFrançois Tigeot #define	AC97ENCODE(x_muted, x_left, x_right)	(((x_muted & 1)<<15) | ((x_left & 0x3f)<<8) | (x_right & 0x3f))
5262a1ad637SFrançois Tigeot 
5272a1ad637SFrançois Tigeot static int
emu_ac97_read_emulation(struct emu_pcm_info * sc,int regno)5282a1ad637SFrançois Tigeot emu_ac97_read_emulation(struct emu_pcm_info *sc, int regno)
5292a1ad637SFrançois Tigeot {
5302a1ad637SFrançois Tigeot 	int use_ac97;
5312a1ad637SFrançois Tigeot 	int emulated;
5322a1ad637SFrançois Tigeot 	int tmp;
5332a1ad637SFrançois Tigeot 
5342a1ad637SFrançois Tigeot 	use_ac97 = 1;
5352a1ad637SFrançois Tigeot 	emulated = 0;
5362a1ad637SFrançois Tigeot 
5372a1ad637SFrançois Tigeot 	switch (regno) {
5382a1ad637SFrançois Tigeot 	case AC97_MIX_MASTER:
5392a1ad637SFrançois Tigeot 		emulated = sc->ac97_state[AC97_MIX_MASTER];
5402a1ad637SFrançois Tigeot 		use_ac97 = 0;
5412a1ad637SFrançois Tigeot 		break;
5422a1ad637SFrançois Tigeot 	case AC97_MIX_PCM:
5432a1ad637SFrançois Tigeot 		emulated = sc->ac97_state[AC97_MIX_PCM];
5442a1ad637SFrançois Tigeot 		use_ac97 = 0;
5452a1ad637SFrançois Tigeot 		break;
5462a1ad637SFrançois Tigeot 	case AC97_REG_RECSEL:
5472a1ad637SFrançois Tigeot 		emulated = 0x0505;
5482a1ad637SFrançois Tigeot 		use_ac97 = 0;
5492a1ad637SFrançois Tigeot 		break;
5502a1ad637SFrançois Tigeot 	case AC97_MIX_RGAIN:
5512a1ad637SFrançois Tigeot 		emulated = sc->ac97_state[AC97_MIX_RGAIN];
5522a1ad637SFrançois Tigeot 		use_ac97 = 0;
5532a1ad637SFrançois Tigeot 		break;
5542a1ad637SFrançois Tigeot 	}
5552a1ad637SFrançois Tigeot 
5562a1ad637SFrançois Tigeot 	emu_wr(sc->card, EMU_AC97ADDR, regno, 1);
5572a1ad637SFrançois Tigeot 	tmp = emu_rd(sc->card, EMU_AC97DATA, 2);
5582a1ad637SFrançois Tigeot 
5592a1ad637SFrançois Tigeot 	if (use_ac97)
5602a1ad637SFrançois Tigeot 		emulated = tmp;
5612a1ad637SFrançois Tigeot 
5622a1ad637SFrançois Tigeot 	return (emulated);
5632a1ad637SFrançois Tigeot }
5642a1ad637SFrançois Tigeot 
5652a1ad637SFrançois Tigeot static void
emu_ac97_write_emulation(struct emu_pcm_info * sc,int regno,uint32_t data)5662a1ad637SFrançois Tigeot emu_ac97_write_emulation(struct emu_pcm_info *sc, int regno, uint32_t data)
5672a1ad637SFrançois Tigeot {
5682a1ad637SFrançois Tigeot 	int write_ac97;
5692a1ad637SFrançois Tigeot 	int left, right;
5702a1ad637SFrançois Tigeot 	uint32_t emu_left, emu_right;
5712a1ad637SFrançois Tigeot 	int is_mute;
5722a1ad637SFrançois Tigeot 
5732a1ad637SFrançois Tigeot 	write_ac97 = 1;
5742a1ad637SFrançois Tigeot 
5752a1ad637SFrançois Tigeot 	left = AC97LEFT(data);
5762a1ad637SFrançois Tigeot 	emu_left = BIT6_TO100(left);	/* We show us as 6-bit AC97 mixer */
5772a1ad637SFrançois Tigeot 	right = AC97RIGHT(data);
5782a1ad637SFrançois Tigeot 	emu_right = BIT6_TO100(right);
5792a1ad637SFrançois Tigeot 	is_mute = AC97MUTE(data);
5802a1ad637SFrançois Tigeot 	if (is_mute)
5812a1ad637SFrançois Tigeot 		emu_left = emu_right = 0;
5822a1ad637SFrançois Tigeot 
5832a1ad637SFrançois Tigeot 	switch (regno) {
5842a1ad637SFrançois Tigeot 		/* TODO: reset emulator on AC97_RESET */
5852a1ad637SFrançois Tigeot 	case AC97_MIX_MASTER:
5862a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_MASTER_FRONT_L, emu_left);
5872a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_MASTER_FRONT_R, emu_right);
5882a1ad637SFrançois Tigeot 		sc->ac97_state[AC97_MIX_MASTER] = data & (0x8000 | 0x3f3f);
5892a1ad637SFrançois Tigeot 		data = 0x8000;	/* Mute AC97 main out */
5902a1ad637SFrançois Tigeot 		break;
5912a1ad637SFrançois Tigeot 	case AC97_MIX_PCM:	/* PCM OUT VOL */
5922a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX0_FRONT_L, emu_left);
5932a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_FX1_FRONT_R, emu_right);
5942a1ad637SFrançois Tigeot 		sc->ac97_state[AC97_MIX_PCM] = data & (0x8000 | 0x3f3f);
5952a1ad637SFrançois Tigeot 		data = 0x8000;	/* Mute AC97 PCM out */
5962a1ad637SFrançois Tigeot 		break;
5972a1ad637SFrançois Tigeot 	case AC97_REG_RECSEL:
5982a1ad637SFrançois Tigeot 		/*
5992a1ad637SFrançois Tigeot 		 * PCM recording source is set to "stereo mix" (labeled "vol"
6002a1ad637SFrançois Tigeot 		 * in mixer). There is no 'playback' from AC97 codec -
6012a1ad637SFrançois Tigeot 		 * if you want to hear anything from AC97 you have to _record_
6022a1ad637SFrançois Tigeot 		 * it. Keep things simple and record "stereo mix".
6032a1ad637SFrançois Tigeot 		 */
6042a1ad637SFrançois Tigeot 		data = 0x0505;
6052a1ad637SFrançois Tigeot 		break;
6062a1ad637SFrançois Tigeot 	case AC97_MIX_RGAIN:	/* RECORD GAIN */
6072a1ad637SFrançois Tigeot 		emu_left = BIT4_TO100(left);	/* rgain is 4-bit */
6082a1ad637SFrançois Tigeot 		emu_right = BIT4_TO100(right);
6092a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_MASTER_REC_L, 100-emu_left);
6102a1ad637SFrançois Tigeot 		emumix_set_volume(sc->card, M_MASTER_REC_R, 100-emu_right);
6112a1ad637SFrançois Tigeot 		/*
6122a1ad637SFrançois Tigeot 		 * Record gain on AC97 should stay zero to get AC97 sound on
6132a1ad637SFrançois Tigeot 		 * AC97_[RL] connectors on EMU10K2 chip. AC97 on Audigy is not
6142a1ad637SFrançois Tigeot 		 * directly connected to any output, only to EMU10K2 chip Use
6152a1ad637SFrançois Tigeot 		 * this control to set AC97 mix volume inside EMU10K2 chip
6162a1ad637SFrançois Tigeot 		 */
6172a1ad637SFrançois Tigeot 		sc->ac97_state[AC97_MIX_RGAIN] = data & (0x8000 | 0x0f0f);
6182a1ad637SFrançois Tigeot 		data = 0x0000;
6192a1ad637SFrançois Tigeot 		break;
6202a1ad637SFrançois Tigeot 	}
6212a1ad637SFrançois Tigeot 	if (write_ac97) {
6222a1ad637SFrançois Tigeot 		emu_wr(sc->card, EMU_AC97ADDR, regno, 1);
6232a1ad637SFrançois Tigeot 		emu_wr(sc->card, EMU_AC97DATA, data, 2);
6242a1ad637SFrançois Tigeot 	}
6252a1ad637SFrançois Tigeot }
6262a1ad637SFrançois Tigeot 
6272a1ad637SFrançois Tigeot static int
emu_erdcd(kobj_t obj __unused,void * devinfo,int regno)6282a1ad637SFrançois Tigeot emu_erdcd(kobj_t obj __unused, void *devinfo, int regno)
6292a1ad637SFrançois Tigeot {
6302a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo;
6312a1ad637SFrançois Tigeot 
6322a1ad637SFrançois Tigeot 	return (emu_ac97_read_emulation(sc, regno));
6332a1ad637SFrançois Tigeot }
6342a1ad637SFrançois Tigeot 
6352a1ad637SFrançois Tigeot static int
emu_ewrcd(kobj_t obj __unused,void * devinfo,int regno,uint32_t data)6362a1ad637SFrançois Tigeot emu_ewrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data)
6372a1ad637SFrançois Tigeot {
6382a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo;
6392a1ad637SFrançois Tigeot 
6402a1ad637SFrançois Tigeot 	emu_ac97_write_emulation(sc, regno, data);
6412a1ad637SFrançois Tigeot 	return (0);
6422a1ad637SFrançois Tigeot }
6432a1ad637SFrançois Tigeot 
6442a1ad637SFrançois Tigeot static kobj_method_t emu_eac97_methods[] = {
6452a1ad637SFrançois Tigeot 	KOBJMETHOD(ac97_read, emu_erdcd),
6462a1ad637SFrançois Tigeot 	KOBJMETHOD(ac97_write, emu_ewrcd),
6472a1ad637SFrançois Tigeot 	KOBJMETHOD_END
6482a1ad637SFrançois Tigeot };
6492a1ad637SFrançois Tigeot AC97_DECLARE(emu_eac97);
6502a1ad637SFrançois Tigeot 
6512a1ad637SFrançois Tigeot /* real ac97 codec */
6522a1ad637SFrançois Tigeot static int
emu_rdcd(kobj_t obj __unused,void * devinfo,int regno)6532a1ad637SFrançois Tigeot emu_rdcd(kobj_t obj __unused, void *devinfo, int regno)
6542a1ad637SFrançois Tigeot {
6552a1ad637SFrançois Tigeot 	int rd;
6562a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo;
6572a1ad637SFrançois Tigeot 
6582a1ad637SFrançois Tigeot 	KASSERT(sc->card != NULL, ("emu_rdcd: no soundcard"));
6592a1ad637SFrançois Tigeot 	emu_wr(sc->card, EMU_AC97ADDR, regno, 1);
6602a1ad637SFrançois Tigeot 	rd = emu_rd(sc->card, EMU_AC97DATA, 2);
6612a1ad637SFrançois Tigeot 	return (rd);
6622a1ad637SFrançois Tigeot }
6632a1ad637SFrançois Tigeot 
6642a1ad637SFrançois Tigeot static int
emu_wrcd(kobj_t obj __unused,void * devinfo,int regno,uint32_t data)6652a1ad637SFrançois Tigeot emu_wrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data)
6662a1ad637SFrançois Tigeot {
6672a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo;
6682a1ad637SFrançois Tigeot 
6692a1ad637SFrançois Tigeot 	KASSERT(sc->card != NULL, ("emu_wrcd: no soundcard"));
6702a1ad637SFrançois Tigeot 	emu_wr(sc->card, EMU_AC97ADDR, regno, 1);
6712a1ad637SFrançois Tigeot 	emu_wr(sc->card, EMU_AC97DATA, data, 2);
6722a1ad637SFrançois Tigeot 	return (0);
6732a1ad637SFrançois Tigeot }
6742a1ad637SFrançois Tigeot 
6752a1ad637SFrançois Tigeot static kobj_method_t emu_ac97_methods[] = {
6762a1ad637SFrançois Tigeot 	KOBJMETHOD(ac97_read, emu_rdcd),
6772a1ad637SFrançois Tigeot 	KOBJMETHOD(ac97_write, emu_wrcd),
6782a1ad637SFrançois Tigeot 	KOBJMETHOD_END
6792a1ad637SFrançois Tigeot };
6802a1ad637SFrançois Tigeot AC97_DECLARE(emu_ac97);
6812a1ad637SFrançois Tigeot 
6822a1ad637SFrançois Tigeot 
6832a1ad637SFrançois Tigeot static int
emu_k1_recval(int speed)6842a1ad637SFrançois Tigeot emu_k1_recval(int speed)
6852a1ad637SFrançois Tigeot {
6862a1ad637SFrançois Tigeot 	int val;
6872a1ad637SFrançois Tigeot 
6882a1ad637SFrançois Tigeot 	val = 0;
6892a1ad637SFrançois Tigeot 	while ((val < 7) && (speed < emu10k1_adcspeed[val]))
6902a1ad637SFrançois Tigeot 		val++;
6912a1ad637SFrançois Tigeot 	return (val);
6922a1ad637SFrançois Tigeot }
6932a1ad637SFrançois Tigeot 
6942a1ad637SFrançois Tigeot static int
emu_k2_recval(int speed)6952a1ad637SFrançois Tigeot emu_k2_recval(int speed)
6962a1ad637SFrançois Tigeot {
6972a1ad637SFrançois Tigeot 	int val;
6982a1ad637SFrançois Tigeot 
6992a1ad637SFrançois Tigeot 	val = 0;
7002a1ad637SFrançois Tigeot 	while ((val < 8) && (speed < emu10k2_adcspeed[val]))
7012a1ad637SFrançois Tigeot 		val++;
7022a1ad637SFrançois Tigeot 	return (val);
7032a1ad637SFrançois Tigeot }
7042a1ad637SFrançois Tigeot 
7052a1ad637SFrançois Tigeot static void *
emupchan_init(kobj_t obj __unused,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir __unused)7062a1ad637SFrançois Tigeot emupchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused)
7072a1ad637SFrançois Tigeot {
7082a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = devinfo;
7092a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch;
7102a1ad637SFrançois Tigeot 	void *r;
7112a1ad637SFrançois Tigeot 
7122a1ad637SFrançois Tigeot 	KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction"));
7132a1ad637SFrançois Tigeot 	KASSERT(sc->card != NULL, ("empchan_init: no soundcard"));
7142a1ad637SFrançois Tigeot 
7152a1ad637SFrançois Tigeot 
7162a1ad637SFrançois Tigeot 	if (sc->pnum >= MAX_CHANNELS)
7172a1ad637SFrançois Tigeot 		return (NULL);
7182a1ad637SFrançois Tigeot 	ch = &(sc->pch[sc->pnum++]);
7192a1ad637SFrançois Tigeot 	ch->buffer = b;
7202a1ad637SFrançois Tigeot 	ch->pcm = sc;
7212a1ad637SFrançois Tigeot 	ch->channel = c;
7222a1ad637SFrançois Tigeot 	ch->blksz = sc->bufsz;
7232a1ad637SFrançois Tigeot 	ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
7242a1ad637SFrançois Tigeot 	ch->spd = 8000;
7252a1ad637SFrançois Tigeot 	ch->master = emu_valloc(sc->card);
7262a1ad637SFrançois Tigeot 	/*
7272a1ad637SFrançois Tigeot 	 * XXX we have to allocate slave even for mono channel until we
7282a1ad637SFrançois Tigeot 	 * fix emu_vfree to handle this case.
7292a1ad637SFrançois Tigeot 	 */
7302a1ad637SFrançois Tigeot 	ch->slave = emu_valloc(sc->card);
7312a1ad637SFrançois Tigeot 	ch->timer = emu_timer_create(sc->card);
7322a1ad637SFrançois Tigeot 	r = (emu_vinit(sc->card, ch->master, ch->slave, EMU_PLAY_BUFSZ, ch->buffer)) ? NULL : ch;
7332a1ad637SFrançois Tigeot 	return (r);
7342a1ad637SFrançois Tigeot }
7352a1ad637SFrançois Tigeot 
7362a1ad637SFrançois Tigeot static int
emupchan_free(kobj_t obj __unused,void * c_devinfo)7372a1ad637SFrançois Tigeot emupchan_free(kobj_t obj __unused, void *c_devinfo)
7382a1ad637SFrançois Tigeot {
7392a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
7402a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
7412a1ad637SFrançois Tigeot 
7422a1ad637SFrançois Tigeot 	emu_timer_clear(sc->card, ch->timer);
7432a1ad637SFrançois Tigeot 	if (ch->slave != NULL)
7442a1ad637SFrançois Tigeot 		emu_vfree(sc->card, ch->slave);
7452a1ad637SFrançois Tigeot 	emu_vfree(sc->card, ch->master);
7462a1ad637SFrançois Tigeot 	return (0);
7472a1ad637SFrançois Tigeot }
7482a1ad637SFrançois Tigeot 
7492a1ad637SFrançois Tigeot static int
emupchan_setformat(kobj_t obj __unused,void * c_devinfo,uint32_t format)7502a1ad637SFrançois Tigeot emupchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format)
7512a1ad637SFrançois Tigeot {
7522a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
7532a1ad637SFrançois Tigeot 
7542a1ad637SFrançois Tigeot 	ch->fmt = format;
7552a1ad637SFrançois Tigeot 	return (0);
7562a1ad637SFrançois Tigeot }
7572a1ad637SFrançois Tigeot 
7582a1ad637SFrançois Tigeot static uint32_t
emupchan_setspeed(kobj_t obj __unused,void * c_devinfo,uint32_t speed)7592a1ad637SFrançois Tigeot emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
7602a1ad637SFrançois Tigeot {
7612a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
7622a1ad637SFrançois Tigeot 
7632a1ad637SFrançois Tigeot 	ch->spd = speed;
7642a1ad637SFrançois Tigeot 	return (ch->spd);
7652a1ad637SFrançois Tigeot }
7662a1ad637SFrançois Tigeot 
7672a1ad637SFrançois Tigeot static uint32_t
emupchan_setblocksize(kobj_t obj __unused,void * c_devinfo,uint32_t blocksize)7682a1ad637SFrançois Tigeot emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
7692a1ad637SFrançois Tigeot {
7702a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
7712a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
7722a1ad637SFrançois Tigeot 
7732a1ad637SFrançois Tigeot 	if (blocksize > ch->pcm->bufsz)
7742a1ad637SFrançois Tigeot 		blocksize = ch->pcm->bufsz;
7752a1ad637SFrançois Tigeot 	snd_mtxlock(sc->lock);
7762a1ad637SFrançois Tigeot 	ch->blksz = blocksize;
7772a1ad637SFrançois Tigeot 	emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
7782a1ad637SFrançois Tigeot 	snd_mtxunlock(sc->lock);
7792a1ad637SFrançois Tigeot 	return (ch->blksz);
7802a1ad637SFrançois Tigeot }
7812a1ad637SFrançois Tigeot 
7822a1ad637SFrançois Tigeot static int
emupchan_trigger(kobj_t obj __unused,void * c_devinfo,int go)7832a1ad637SFrançois Tigeot emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
7842a1ad637SFrançois Tigeot {
7852a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
7862a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
7872a1ad637SFrançois Tigeot 
7882a1ad637SFrançois Tigeot 	if (!PCMTRIG_COMMON(go))
7892a1ad637SFrançois Tigeot 		return (0);
7902a1ad637SFrançois Tigeot 
7912a1ad637SFrançois Tigeot 	snd_mtxlock(sc->lock); /* XXX can we trigger on parallel threads ? */
7922a1ad637SFrançois Tigeot 	if (go == PCMTRIG_START) {
7932a1ad637SFrançois Tigeot 		emu_vsetup(ch->master, ch->fmt, ch->spd);
7942a1ad637SFrançois Tigeot 		if (AFMT_CHANNEL(ch->fmt) > 1)
7952a1ad637SFrançois Tigeot 			emu_vroute(sc->card, &(sc->rt), ch->master);
7962a1ad637SFrançois Tigeot 		else
7972a1ad637SFrançois Tigeot 			emu_vroute(sc->card, &(sc->rt_mono), ch->master);
7982a1ad637SFrançois Tigeot 		emu_vwrite(sc->card, ch->master);
7992a1ad637SFrançois Tigeot 		emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
8002a1ad637SFrançois Tigeot 		emu_timer_enable(sc->card, ch->timer, 1);
8012a1ad637SFrançois Tigeot 	}
8022a1ad637SFrançois Tigeot 	/* PCM interrupt handler will handle PCMTRIG_STOP event */
8032a1ad637SFrançois Tigeot 	ch->run = (go == PCMTRIG_START) ? 1 : 0;
8042a1ad637SFrançois Tigeot 	emu_vtrigger(sc->card, ch->master, ch->run);
8052a1ad637SFrançois Tigeot 	snd_mtxunlock(sc->lock);
8062a1ad637SFrançois Tigeot 	return (0);
8072a1ad637SFrançois Tigeot }
8082a1ad637SFrançois Tigeot 
8092a1ad637SFrançois Tigeot static uint32_t
emupchan_getptr(kobj_t obj __unused,void * c_devinfo)8102a1ad637SFrançois Tigeot emupchan_getptr(kobj_t obj __unused, void *c_devinfo)
8112a1ad637SFrançois Tigeot {
8122a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
8132a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
8142a1ad637SFrançois Tigeot 	int r;
8152a1ad637SFrançois Tigeot 
8162a1ad637SFrançois Tigeot 	r = emu_vpos(sc->card, ch->master);
8172a1ad637SFrançois Tigeot 
8182a1ad637SFrançois Tigeot 	return (r);
8192a1ad637SFrançois Tigeot }
8202a1ad637SFrançois Tigeot 
8212a1ad637SFrançois Tigeot static struct pcmchan_caps *
emupchan_getcaps(kobj_t obj __unused,void * c_devinfo __unused)8222a1ad637SFrançois Tigeot emupchan_getcaps(kobj_t obj __unused, void *c_devinfo __unused)
8232a1ad637SFrançois Tigeot {
8242a1ad637SFrançois Tigeot 	struct emu_pcm_pchinfo *ch = c_devinfo;
8252a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
8262a1ad637SFrançois Tigeot 
8272a1ad637SFrançois Tigeot 	switch (sc->route) {
8282a1ad637SFrançois Tigeot 	case RT_FRONT:
8292a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
8302a1ad637SFrançois Tigeot 	case RT_REAR:
8312a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
8322a1ad637SFrançois Tigeot 	case RT_SIDE:
8332a1ad637SFrançois Tigeot 		return (&emu_playcaps);
8342a1ad637SFrançois Tigeot 		break;
8352a1ad637SFrançois Tigeot 	case RT_CENTER:
8362a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
8372a1ad637SFrançois Tigeot 	case RT_SUB:
8382a1ad637SFrançois Tigeot 		return (&emu_playcaps_mono);
8392a1ad637SFrançois Tigeot 		break;
8402a1ad637SFrançois Tigeot 	}
8412a1ad637SFrançois Tigeot 	return (NULL);
8422a1ad637SFrançois Tigeot }
8432a1ad637SFrançois Tigeot 
8442a1ad637SFrançois Tigeot static kobj_method_t emupchan_methods[] = {
8452a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_init, emupchan_init),
8462a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_free, emupchan_free),
8472a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setformat, emupchan_setformat),
8482a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setspeed, emupchan_setspeed),
8492a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setblocksize, emupchan_setblocksize),
8502a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_trigger, emupchan_trigger),
8512a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getptr, emupchan_getptr),
8522a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getcaps, emupchan_getcaps),
8532a1ad637SFrançois Tigeot 	KOBJMETHOD_END
8542a1ad637SFrançois Tigeot };
8552a1ad637SFrançois Tigeot CHANNEL_DECLARE(emupchan);
8562a1ad637SFrançois Tigeot 
8572a1ad637SFrançois Tigeot static void *
emurchan_init(kobj_t obj __unused,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir __unused)8582a1ad637SFrançois Tigeot emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused)
8592a1ad637SFrançois Tigeot {
8602a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = devinfo;
8612a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch;
8622a1ad637SFrançois Tigeot 
8632a1ad637SFrançois Tigeot 	KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction"));
8642a1ad637SFrançois Tigeot 	ch = &sc->rch_adc;
8652a1ad637SFrançois Tigeot 	ch->buffer = b;
8662a1ad637SFrançois Tigeot 	ch->pcm = sc;
8672a1ad637SFrançois Tigeot 	ch->channel = c;
8682a1ad637SFrançois Tigeot 	ch->blksz = sc->bufsz / 2; /* We rise interrupt for half-full buffer */
8692a1ad637SFrançois Tigeot 	ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
8702a1ad637SFrançois Tigeot 	ch->spd = 8000;
8712a1ad637SFrançois Tigeot 	ch->idxreg = sc->is_emu10k1 ? EMU_ADCIDX : EMU_A_ADCIDX;
8722a1ad637SFrançois Tigeot 	ch->basereg = EMU_ADCBA;
8732a1ad637SFrançois Tigeot 	ch->sizereg = EMU_ADCBS;
8742a1ad637SFrançois Tigeot 	ch->setupreg = EMU_ADCCR;
8752a1ad637SFrançois Tigeot 	ch->irqmask = EMU_INTE_ADCBUFENABLE;
8762a1ad637SFrançois Tigeot 	ch->iprmask = EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL;
8772a1ad637SFrançois Tigeot 
8782a1ad637SFrançois Tigeot 	if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0)
8792a1ad637SFrançois Tigeot 		return (NULL);
8802a1ad637SFrançois Tigeot 	else {
8812a1ad637SFrançois Tigeot 		ch->timer = emu_timer_create(sc->card);
8822a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer));
8832a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, 0);	/* off */
8842a1ad637SFrançois Tigeot 		return (ch);
8852a1ad637SFrançois Tigeot 	}
8862a1ad637SFrançois Tigeot }
8872a1ad637SFrançois Tigeot 
8882a1ad637SFrançois Tigeot static int
emurchan_free(kobj_t obj __unused,void * c_devinfo)8892a1ad637SFrançois Tigeot emurchan_free(kobj_t obj __unused, void *c_devinfo)
8902a1ad637SFrançois Tigeot {
8912a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
8922a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
8932a1ad637SFrançois Tigeot 
8942a1ad637SFrançois Tigeot 	emu_timer_clear(sc->card, ch->timer);
8952a1ad637SFrançois Tigeot 	return (0);
8962a1ad637SFrançois Tigeot }
8972a1ad637SFrançois Tigeot 
8982a1ad637SFrançois Tigeot static int
emurchan_setformat(kobj_t obj __unused,void * c_devinfo,uint32_t format)8992a1ad637SFrançois Tigeot emurchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format)
9002a1ad637SFrançois Tigeot {
9012a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
9022a1ad637SFrançois Tigeot 
9032a1ad637SFrançois Tigeot 	ch->fmt = format;
9042a1ad637SFrançois Tigeot 	return (0);
9052a1ad637SFrançois Tigeot }
9062a1ad637SFrançois Tigeot 
9072a1ad637SFrançois Tigeot static uint32_t
emurchan_setspeed(kobj_t obj __unused,void * c_devinfo,uint32_t speed)9082a1ad637SFrançois Tigeot emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
9092a1ad637SFrançois Tigeot {
9102a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
9112a1ad637SFrançois Tigeot 
9122a1ad637SFrançois Tigeot 	if (ch->pcm->is_emu10k1) {
9132a1ad637SFrançois Tigeot 		speed = emu10k1_adcspeed[emu_k1_recval(speed)];
9142a1ad637SFrançois Tigeot 	} else {
9152a1ad637SFrançois Tigeot 		speed = emu10k2_adcspeed[emu_k2_recval(speed)];
9162a1ad637SFrançois Tigeot 	}
9172a1ad637SFrançois Tigeot 	ch->spd = speed;
9182a1ad637SFrançois Tigeot 	return (ch->spd);
9192a1ad637SFrançois Tigeot }
9202a1ad637SFrançois Tigeot 
9212a1ad637SFrançois Tigeot static uint32_t
emurchan_setblocksize(kobj_t obj __unused,void * c_devinfo,uint32_t blocksize)9222a1ad637SFrançois Tigeot emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
9232a1ad637SFrançois Tigeot {
9242a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
9252a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
9262a1ad637SFrançois Tigeot 
9272a1ad637SFrançois Tigeot 	ch->blksz = blocksize;
9282a1ad637SFrançois Tigeot 	/*
9292a1ad637SFrançois Tigeot 	 * If blocksize is less than half of buffer size we will not get
9302a1ad637SFrançois Tigeot 	 * BUFHALFFULL interrupt in time and channel will need to generate
9312a1ad637SFrançois Tigeot 	 * (and use) timer interrupts. Otherwise channel will be marked dead.
9322a1ad637SFrançois Tigeot 	 */
9332a1ad637SFrançois Tigeot 	if (ch->blksz < (ch->pcm->bufsz / 2)) {
9342a1ad637SFrançois Tigeot 		emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
9352a1ad637SFrançois Tigeot 		emu_timer_enable(sc->card, ch->timer, 1);
9362a1ad637SFrançois Tigeot 	} else {
9372a1ad637SFrançois Tigeot 		emu_timer_enable(sc->card, ch->timer, 0);
9382a1ad637SFrançois Tigeot 	}
9392a1ad637SFrançois Tigeot 	return (ch->blksz);
9402a1ad637SFrançois Tigeot }
9412a1ad637SFrançois Tigeot 
9422a1ad637SFrançois Tigeot static int
emurchan_trigger(kobj_t obj __unused,void * c_devinfo,int go)9432a1ad637SFrançois Tigeot emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
9442a1ad637SFrançois Tigeot {
9452a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
9462a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
9472a1ad637SFrançois Tigeot 	uint32_t val, sz;
9482a1ad637SFrançois Tigeot 
9492a1ad637SFrançois Tigeot 	if (!PCMTRIG_COMMON(go))
9502a1ad637SFrançois Tigeot 		return (0);
9512a1ad637SFrançois Tigeot 
9522a1ad637SFrançois Tigeot 	switch (sc->bufsz) {
9532a1ad637SFrançois Tigeot 	case 4096:
9542a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_4096;
9552a1ad637SFrançois Tigeot 		break;
9562a1ad637SFrançois Tigeot 	case 8192:
9572a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_8192;
9582a1ad637SFrançois Tigeot 		break;
9592a1ad637SFrançois Tigeot 	case 16384:
9602a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_16384;
9612a1ad637SFrançois Tigeot 		break;
9622a1ad637SFrançois Tigeot 	case 32768:
9632a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_32768;
9642a1ad637SFrançois Tigeot 		break;
9652a1ad637SFrançois Tigeot 	case 65536:
9662a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_65536;
9672a1ad637SFrançois Tigeot 		break;
9682a1ad637SFrançois Tigeot 	default:
9692a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_4096;
9702a1ad637SFrançois Tigeot 	}
9712a1ad637SFrançois Tigeot 
9722a1ad637SFrançois Tigeot 	snd_mtxlock(sc->lock);
9732a1ad637SFrançois Tigeot 	switch (go) {
9742a1ad637SFrançois Tigeot 	case PCMTRIG_START:
9752a1ad637SFrançois Tigeot 		ch->run = 1;
9762a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, sz);
9772a1ad637SFrançois Tigeot 		val = sc->is_emu10k1 ? EMU_ADCCR_LCHANENABLE : EMU_A_ADCCR_LCHANENABLE;
9782a1ad637SFrançois Tigeot 		if (AFMT_CHANNEL(ch->fmt) > 1)
9792a1ad637SFrançois Tigeot 			val |= sc->is_emu10k1 ? EMU_ADCCR_RCHANENABLE : EMU_A_ADCCR_RCHANENABLE;
9802a1ad637SFrançois Tigeot 		val |= sc->is_emu10k1 ? emu_k1_recval(ch->spd) : emu_k2_recval(ch->spd);
9812a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->setupreg, 0);
9822a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->setupreg, val);
9832a1ad637SFrançois Tigeot 		ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc);
9842a1ad637SFrançois Tigeot 		break;
9852a1ad637SFrançois Tigeot 	case PCMTRIG_STOP:
9862a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
9872a1ad637SFrançois Tigeot 	case PCMTRIG_ABORT:
9882a1ad637SFrançois Tigeot 		ch->run = 0;
9892a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, 0);
9902a1ad637SFrançois Tigeot 		if (ch->setupreg)
9912a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, ch->setupreg, 0);
9922a1ad637SFrançois Tigeot 		(void)emu_intr_unregister(sc->card, ch->ihandle);
9932a1ad637SFrançois Tigeot 		break;
9942a1ad637SFrançois Tigeot 	case PCMTRIG_EMLDMAWR:
9952a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
9962a1ad637SFrançois Tigeot 	case PCMTRIG_EMLDMARD:
9972a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
9982a1ad637SFrançois Tigeot 	default:
9992a1ad637SFrançois Tigeot 		break;
10002a1ad637SFrançois Tigeot 	}
10012a1ad637SFrançois Tigeot 	snd_mtxunlock(sc->lock);
10022a1ad637SFrançois Tigeot 
10032a1ad637SFrançois Tigeot 	return (0);
10042a1ad637SFrançois Tigeot }
10052a1ad637SFrançois Tigeot 
10062a1ad637SFrançois Tigeot static uint32_t
emurchan_getptr(kobj_t obj __unused,void * c_devinfo)10072a1ad637SFrançois Tigeot emurchan_getptr(kobj_t obj __unused, void *c_devinfo)
10082a1ad637SFrançois Tigeot {
10092a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
10102a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
10112a1ad637SFrançois Tigeot 	int r;
10122a1ad637SFrançois Tigeot 
10132a1ad637SFrançois Tigeot 	r = emu_rdptr(sc->card, 0, ch->idxreg) & 0x0000ffff;
10142a1ad637SFrançois Tigeot 
10152a1ad637SFrançois Tigeot 	return (r);
10162a1ad637SFrançois Tigeot }
10172a1ad637SFrançois Tigeot 
10182a1ad637SFrançois Tigeot static struct pcmchan_caps *
emurchan_getcaps(kobj_t obj __unused,void * c_devinfo __unused)10192a1ad637SFrançois Tigeot emurchan_getcaps(kobj_t obj __unused, void *c_devinfo __unused)
10202a1ad637SFrançois Tigeot {
10212a1ad637SFrançois Tigeot 	return (&emu_reccaps_adc);
10222a1ad637SFrançois Tigeot }
10232a1ad637SFrançois Tigeot 
10242a1ad637SFrançois Tigeot static kobj_method_t emurchan_methods[] = {
10252a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_init, emurchan_init),
10262a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_free, emurchan_free),
10272a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setformat, emurchan_setformat),
10282a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setspeed, emurchan_setspeed),
10292a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setblocksize, emurchan_setblocksize),
10302a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_trigger, emurchan_trigger),
10312a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getptr, emurchan_getptr),
10322a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getcaps, emurchan_getcaps),
10332a1ad637SFrançois Tigeot 	KOBJMETHOD_END
10342a1ad637SFrançois Tigeot };
10352a1ad637SFrançois Tigeot CHANNEL_DECLARE(emurchan);
10362a1ad637SFrançois Tigeot 
10372a1ad637SFrançois Tigeot static void *
emufxrchan_init(kobj_t obj __unused,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir __unused)10382a1ad637SFrançois Tigeot emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused)
10392a1ad637SFrançois Tigeot {
10402a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = devinfo;
10412a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch;
10422a1ad637SFrançois Tigeot 
10432a1ad637SFrançois Tigeot 	KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction"));
10442a1ad637SFrançois Tigeot 
10452a1ad637SFrançois Tigeot 	if (sc == NULL) return (NULL);
10462a1ad637SFrançois Tigeot 
10472a1ad637SFrançois Tigeot 	ch = &(sc->rch_efx);
10482a1ad637SFrançois Tigeot 	ch->fmt = SND_FORMAT(AFMT_S16_LE, 1, 0);
10492a1ad637SFrançois Tigeot 	ch->spd = sc->is_emu10k1 ? 48000*32 : 48000 * 64;
10502a1ad637SFrançois Tigeot 	ch->idxreg = EMU_FXIDX;
10512a1ad637SFrançois Tigeot 	ch->basereg = EMU_FXBA;
10522a1ad637SFrançois Tigeot 	ch->sizereg = EMU_FXBS;
10532a1ad637SFrançois Tigeot 	ch->irqmask = EMU_INTE_EFXBUFENABLE;
10542a1ad637SFrançois Tigeot 	ch->iprmask = EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL;
10552a1ad637SFrançois Tigeot 	ch->buffer = b;
10562a1ad637SFrançois Tigeot 	ch->pcm = sc;
10572a1ad637SFrançois Tigeot 	ch->channel = c;
10582a1ad637SFrançois Tigeot 	ch->blksz = sc->bufsz / 2;
10592a1ad637SFrançois Tigeot 
10602a1ad637SFrançois Tigeot 	if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0)
10612a1ad637SFrançois Tigeot 		return (NULL);
10622a1ad637SFrançois Tigeot 	else {
10632a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer));
10642a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, 0);	/* off */
10652a1ad637SFrançois Tigeot 		return (ch);
10662a1ad637SFrançois Tigeot 	}
10672a1ad637SFrançois Tigeot }
10682a1ad637SFrançois Tigeot 
10692a1ad637SFrançois Tigeot static int
emufxrchan_setformat(kobj_t obj __unused,void * c_devinfo __unused,uint32_t format)10702a1ad637SFrançois Tigeot emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format)
10712a1ad637SFrançois Tigeot {
10722a1ad637SFrançois Tigeot 	if (format == SND_FORMAT(AFMT_S16_LE, 1, 0)) return (0);
10732a1ad637SFrançois Tigeot 	return (EINVAL);
10742a1ad637SFrançois Tigeot }
10752a1ad637SFrançois Tigeot 
10762a1ad637SFrançois Tigeot static uint32_t
emufxrchan_setspeed(kobj_t obj __unused,void * c_devinfo,uint32_t speed)10772a1ad637SFrançois Tigeot emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
10782a1ad637SFrançois Tigeot {
10792a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
10802a1ad637SFrançois Tigeot 
10812a1ad637SFrançois Tigeot 	/* FIXED RATE CHANNEL */
10822a1ad637SFrançois Tigeot 	return (ch->spd);
10832a1ad637SFrançois Tigeot }
10842a1ad637SFrançois Tigeot 
10852a1ad637SFrançois Tigeot static uint32_t
emufxrchan_setblocksize(kobj_t obj __unused,void * c_devinfo,uint32_t blocksize)10862a1ad637SFrançois Tigeot emufxrchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
10872a1ad637SFrançois Tigeot {
10882a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
10892a1ad637SFrançois Tigeot 
10902a1ad637SFrançois Tigeot 	ch->blksz = blocksize;
10912a1ad637SFrançois Tigeot 	/*
10922a1ad637SFrançois Tigeot 	 * XXX If blocksize is less than half of buffer size we will not get
10932a1ad637SFrançois Tigeot 	 * interrupt in time and channel will die due to interrupt timeout.
10942a1ad637SFrançois Tigeot 	 * This	should not happen with FX rchan, because it will fill buffer
10952a1ad637SFrançois Tigeot 	 * very	fast (64K buffer is 0.021seconds on Audigy).
10962a1ad637SFrançois Tigeot 	 */
10972a1ad637SFrançois Tigeot 	if (ch->blksz < (ch->pcm->bufsz / 2))
10982a1ad637SFrançois Tigeot 		ch->blksz = ch->pcm->bufsz / 2;
10992a1ad637SFrançois Tigeot 	return (ch->blksz);
11002a1ad637SFrançois Tigeot }
11012a1ad637SFrançois Tigeot 
11022a1ad637SFrançois Tigeot static int
emufxrchan_trigger(kobj_t obj __unused,void * c_devinfo,int go)11032a1ad637SFrançois Tigeot emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
11042a1ad637SFrançois Tigeot {
11052a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
11062a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
11072a1ad637SFrançois Tigeot 	uint32_t sz;
11082a1ad637SFrançois Tigeot 
11092a1ad637SFrançois Tigeot 	if (!PCMTRIG_COMMON(go))
11102a1ad637SFrançois Tigeot 		return (0);
11112a1ad637SFrançois Tigeot 
11122a1ad637SFrançois Tigeot 	switch (sc->bufsz) {
11132a1ad637SFrançois Tigeot 	case 4096:
11142a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_4096;
11152a1ad637SFrançois Tigeot 		break;
11162a1ad637SFrançois Tigeot 	case 8192:
11172a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_8192;
11182a1ad637SFrançois Tigeot 		break;
11192a1ad637SFrançois Tigeot 	case 16384:
11202a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_16384;
11212a1ad637SFrançois Tigeot 		break;
11222a1ad637SFrançois Tigeot 	case 32768:
11232a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_32768;
11242a1ad637SFrançois Tigeot 		break;
11252a1ad637SFrançois Tigeot 	case 65536:
11262a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_65536;
11272a1ad637SFrançois Tigeot 		break;
11282a1ad637SFrançois Tigeot 	default:
11292a1ad637SFrançois Tigeot 		sz = EMU_RECBS_BUFSIZE_4096;
11302a1ad637SFrançois Tigeot 	}
11312a1ad637SFrançois Tigeot 
11322a1ad637SFrançois Tigeot 	snd_mtxlock(sc->lock);
11332a1ad637SFrançois Tigeot 	switch (go) {
11342a1ad637SFrançois Tigeot 	case PCMTRIG_START:
11352a1ad637SFrançois Tigeot 		ch->run = 1;
11362a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, sz);
11372a1ad637SFrançois Tigeot 		ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc);
11382a1ad637SFrançois Tigeot 		/*
11392a1ad637SFrançois Tigeot 		 * SB Live! is limited to 32 mono channels. Audigy
11402a1ad637SFrançois Tigeot 		 * has 64 mono channels. Channels are enabled
11412a1ad637SFrançois Tigeot 		 * by setting a bit in EMU_A_FXWC[1|2] registers.
11422a1ad637SFrançois Tigeot 		 */
11432a1ad637SFrançois Tigeot 		/* XXX there is no way to demultiplex this streams for now */
11442a1ad637SFrançois Tigeot 		if (sc->is_emu10k1) {
11452a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_FXWC, 0xffffffff);
11462a1ad637SFrançois Tigeot 		} else {
11472a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_A_FXWC1, 0xffffffff);
11482a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_A_FXWC2, 0xffffffff);
11492a1ad637SFrançois Tigeot 		}
11502a1ad637SFrançois Tigeot 		break;
11512a1ad637SFrançois Tigeot 	case PCMTRIG_STOP:
11522a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
11532a1ad637SFrançois Tigeot 	case PCMTRIG_ABORT:
11542a1ad637SFrançois Tigeot 		ch->run = 0;
11552a1ad637SFrançois Tigeot 		if (sc->is_emu10k1) {
11562a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_FXWC, 0x0);
11572a1ad637SFrançois Tigeot 		} else {
11582a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_A_FXWC1, 0x0);
11592a1ad637SFrançois Tigeot 			emu_wrptr(sc->card, 0, EMU_A_FXWC2, 0x0);
11602a1ad637SFrançois Tigeot 		}
11612a1ad637SFrançois Tigeot 		emu_wrptr(sc->card, 0, ch->sizereg, 0);
11622a1ad637SFrançois Tigeot 		(void)emu_intr_unregister(sc->card, ch->ihandle);
11632a1ad637SFrançois Tigeot 		break;
11642a1ad637SFrançois Tigeot 	case PCMTRIG_EMLDMAWR:
11652a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
11662a1ad637SFrançois Tigeot 	case PCMTRIG_EMLDMARD:
11672a1ad637SFrançois Tigeot 		/* FALLTHROUGH */
11682a1ad637SFrançois Tigeot 	default:
11692a1ad637SFrançois Tigeot 		break;
11702a1ad637SFrançois Tigeot 	}
11712a1ad637SFrançois Tigeot 	snd_mtxunlock(sc->lock);
11722a1ad637SFrançois Tigeot 
11732a1ad637SFrançois Tigeot 	return (0);
11742a1ad637SFrançois Tigeot }
11752a1ad637SFrançois Tigeot 
11762a1ad637SFrançois Tigeot static uint32_t
emufxrchan_getptr(kobj_t obj __unused,void * c_devinfo)11772a1ad637SFrançois Tigeot emufxrchan_getptr(kobj_t obj __unused, void *c_devinfo)
11782a1ad637SFrançois Tigeot {
11792a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
11802a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
11812a1ad637SFrançois Tigeot 	int r;
11822a1ad637SFrançois Tigeot 
11832a1ad637SFrançois Tigeot 	r = emu_rdptr(sc->card, 0, ch->idxreg) & 0x0000ffff;
11842a1ad637SFrançois Tigeot 
11852a1ad637SFrançois Tigeot 	return (r);
11862a1ad637SFrançois Tigeot }
11872a1ad637SFrançois Tigeot 
11882a1ad637SFrançois Tigeot static struct pcmchan_caps *
emufxrchan_getcaps(kobj_t obj __unused,void * c_devinfo)11892a1ad637SFrançois Tigeot emufxrchan_getcaps(kobj_t obj __unused, void *c_devinfo)
11902a1ad637SFrançois Tigeot {
11912a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
11922a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
11932a1ad637SFrançois Tigeot 
11942a1ad637SFrançois Tigeot 	if (sc->is_emu10k1)
11952a1ad637SFrançois Tigeot 		return (&emu_reccaps_efx_live);
11962a1ad637SFrançois Tigeot 	return (&emu_reccaps_efx_audigy);
11972a1ad637SFrançois Tigeot 
11982a1ad637SFrançois Tigeot }
11992a1ad637SFrançois Tigeot 
12002a1ad637SFrançois Tigeot static int
emufxrchan_getrates(kobj_t obj __unused,void * c_devinfo,int ** rates)12012a1ad637SFrançois Tigeot emufxrchan_getrates(kobj_t obj __unused, void *c_devinfo, int **rates)
12022a1ad637SFrançois Tigeot {
12032a1ad637SFrançois Tigeot 	struct emu_pcm_rchinfo *ch = c_devinfo;
12042a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = ch->pcm;
12052a1ad637SFrançois Tigeot 
12062a1ad637SFrançois Tigeot 	if (sc->is_emu10k1)
12072a1ad637SFrançois Tigeot 		*rates = emu_rates_live;
12082a1ad637SFrançois Tigeot 	else
12092a1ad637SFrançois Tigeot 		*rates = emu_rates_audigy;
12102a1ad637SFrançois Tigeot 
12112a1ad637SFrançois Tigeot 	return 1;
12122a1ad637SFrançois Tigeot }
12132a1ad637SFrançois Tigeot 
12142a1ad637SFrançois Tigeot static kobj_method_t emufxrchan_methods[] = {
12152a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_init, emufxrchan_init),
12162a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setformat, emufxrchan_setformat),
12172a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setspeed, emufxrchan_setspeed),
12182a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_setblocksize, emufxrchan_setblocksize),
12192a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_trigger, emufxrchan_trigger),
12202a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getptr, emufxrchan_getptr),
12212a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getcaps, emufxrchan_getcaps),
12222a1ad637SFrançois Tigeot 	KOBJMETHOD(channel_getrates, emufxrchan_getrates),
12232a1ad637SFrançois Tigeot 	KOBJMETHOD_END
12242a1ad637SFrançois Tigeot };
12252a1ad637SFrançois Tigeot CHANNEL_DECLARE(emufxrchan);
12262a1ad637SFrançois Tigeot 
12272a1ad637SFrançois Tigeot 
12282a1ad637SFrançois Tigeot static uint32_t
emu_pcm_intr(void * pcm,uint32_t stat)12292a1ad637SFrançois Tigeot emu_pcm_intr(void *pcm, uint32_t stat)
12302a1ad637SFrançois Tigeot {
12312a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc = (struct emu_pcm_info *)pcm;
12322a1ad637SFrançois Tigeot 	uint32_t ack;
12332a1ad637SFrançois Tigeot 	int i;
12342a1ad637SFrançois Tigeot 
12352a1ad637SFrançois Tigeot 	ack = 0;
12362a1ad637SFrançois Tigeot 
12372a1ad637SFrançois Tigeot 	snd_mtxlock(sc->lock);
12382a1ad637SFrançois Tigeot 
12392a1ad637SFrançois Tigeot 	if (stat & EMU_IPR_INTERVALTIMER) {
12402a1ad637SFrançois Tigeot 		ack |= EMU_IPR_INTERVALTIMER;
12412a1ad637SFrançois Tigeot 		for (i = 0; i < MAX_CHANNELS; i++)
12422a1ad637SFrançois Tigeot 			if (sc->pch[i].channel) {
12432a1ad637SFrançois Tigeot 				if (sc->pch[i].run == 1) {
12442a1ad637SFrançois Tigeot 					snd_mtxunlock(sc->lock);
12452a1ad637SFrançois Tigeot 					chn_intr(sc->pch[i].channel);
12462a1ad637SFrançois Tigeot 					snd_mtxlock(sc->lock);
12472a1ad637SFrançois Tigeot 				} else
12482a1ad637SFrançois Tigeot 					emu_timer_enable(sc->card, sc->pch[i].timer, 0);
12492a1ad637SFrançois Tigeot 			}
12502a1ad637SFrançois Tigeot 		/* ADC may install timer to get low-latency interrupts */
12512a1ad637SFrançois Tigeot 		if ((sc->rch_adc.channel) && (sc->rch_adc.run)) {
12522a1ad637SFrançois Tigeot 			snd_mtxunlock(sc->lock);
12532a1ad637SFrançois Tigeot 			chn_intr(sc->rch_adc.channel);
12542a1ad637SFrançois Tigeot 			snd_mtxlock(sc->lock);
12552a1ad637SFrançois Tigeot 		}
12562a1ad637SFrançois Tigeot 		/*
12572a1ad637SFrançois Tigeot 		 * EFX does not use timer, because it will fill
12582a1ad637SFrançois Tigeot 		 * buffer at least 32x times faster than ADC.
12592a1ad637SFrançois Tigeot 		 */
12602a1ad637SFrançois Tigeot 	}
12612a1ad637SFrançois Tigeot 
12622a1ad637SFrançois Tigeot 
12632a1ad637SFrançois Tigeot 	if (stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL)) {
12642a1ad637SFrançois Tigeot 		ack |= stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL);
12652a1ad637SFrançois Tigeot 		if (sc->rch_adc.channel) {
12662a1ad637SFrançois Tigeot 			snd_mtxunlock(sc->lock);
12672a1ad637SFrançois Tigeot 			chn_intr(sc->rch_adc.channel);
12682a1ad637SFrançois Tigeot 			snd_mtxlock(sc->lock);
12692a1ad637SFrançois Tigeot 		}
12702a1ad637SFrançois Tigeot 	}
12712a1ad637SFrançois Tigeot 
12722a1ad637SFrançois Tigeot 	if (stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL)) {
12732a1ad637SFrançois Tigeot 		ack |= stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL);
12742a1ad637SFrançois Tigeot 		if (sc->rch_efx.channel) {
12752a1ad637SFrançois Tigeot 			snd_mtxunlock(sc->lock);
12762a1ad637SFrançois Tigeot 			chn_intr(sc->rch_efx.channel);
12772a1ad637SFrançois Tigeot 			snd_mtxlock(sc->lock);
12782a1ad637SFrançois Tigeot 		}
12792a1ad637SFrançois Tigeot 	}
12802a1ad637SFrançois Tigeot 	snd_mtxunlock(sc->lock);
12812a1ad637SFrançois Tigeot 
12822a1ad637SFrançois Tigeot 	return (ack);
12832a1ad637SFrançois Tigeot }
12842a1ad637SFrançois Tigeot 
12852a1ad637SFrançois Tigeot static int
emu_pcm_init(struct emu_pcm_info * sc)12862a1ad637SFrançois Tigeot emu_pcm_init(struct emu_pcm_info *sc)
12872a1ad637SFrançois Tigeot {
12882a1ad637SFrançois Tigeot 	sc->bufsz = pcm_getbuffersize(sc->dev, EMUPAGESIZE, EMU_REC_BUFSZ, EMU_MAX_BUFSZ);
12892a1ad637SFrançois Tigeot 	return (0);
12902a1ad637SFrançois Tigeot }
12912a1ad637SFrançois Tigeot 
12922a1ad637SFrançois Tigeot static int
emu_pcm_uninit(struct emu_pcm_info * sc __unused)12932a1ad637SFrançois Tigeot emu_pcm_uninit(struct emu_pcm_info *sc __unused)
12942a1ad637SFrançois Tigeot {
12952a1ad637SFrançois Tigeot 	return (0);
12962a1ad637SFrançois Tigeot }
12972a1ad637SFrançois Tigeot 
12982a1ad637SFrançois Tigeot static int
emu_pcm_probe(device_t dev)12992a1ad637SFrançois Tigeot emu_pcm_probe(device_t dev)
13002a1ad637SFrançois Tigeot {
13012a1ad637SFrançois Tigeot 	uintptr_t func, route, r;
13022a1ad637SFrançois Tigeot 	const char *rt;
13032a1ad637SFrançois Tigeot 	char buffer[255];
13042a1ad637SFrançois Tigeot 
13052a1ad637SFrançois Tigeot 	r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_FUNC, &func);
13062a1ad637SFrançois Tigeot 
13072a1ad637SFrançois Tigeot 	if (func != SCF_PCM)
13082a1ad637SFrançois Tigeot 		return (ENXIO);
13092a1ad637SFrançois Tigeot 
13102a1ad637SFrançois Tigeot 	rt = "UNKNOWN";
13112a1ad637SFrançois Tigeot 	r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ROUTE, &route);
13122a1ad637SFrançois Tigeot 	switch (route) {
13132a1ad637SFrançois Tigeot 	case RT_FRONT:
13142a1ad637SFrançois Tigeot 		rt = "front";
13152a1ad637SFrançois Tigeot 		break;
13162a1ad637SFrançois Tigeot 	case RT_REAR:
13172a1ad637SFrançois Tigeot 		rt = "rear";
13182a1ad637SFrançois Tigeot 		break;
13192a1ad637SFrançois Tigeot 	case RT_CENTER:
13202a1ad637SFrançois Tigeot 		rt = "center";
13212a1ad637SFrançois Tigeot 		break;
13222a1ad637SFrançois Tigeot 	case RT_SUB:
13232a1ad637SFrançois Tigeot 		rt = "subwoofer";
13242a1ad637SFrançois Tigeot 		break;
13252a1ad637SFrançois Tigeot 	case RT_SIDE:
13262a1ad637SFrançois Tigeot 		rt = "side";
13272a1ad637SFrançois Tigeot 		break;
13282a1ad637SFrançois Tigeot 	case RT_MCHRECORD:
13292a1ad637SFrançois Tigeot 		rt = "multichannel recording";
13302a1ad637SFrançois Tigeot 		break;
13312a1ad637SFrançois Tigeot 	}
13322a1ad637SFrançois Tigeot 
133367931cc4SFrançois Tigeot 	ksnprintf(buffer, 255, "EMU10Kx DSP %s PCM interface", rt);
13342a1ad637SFrançois Tigeot 	device_set_desc_copy(dev, buffer);
13352a1ad637SFrançois Tigeot 	return (0);
13362a1ad637SFrançois Tigeot }
13372a1ad637SFrançois Tigeot 
13382a1ad637SFrançois Tigeot static int
emu_pcm_attach(device_t dev)13392a1ad637SFrançois Tigeot emu_pcm_attach(device_t dev)
13402a1ad637SFrançois Tigeot {
13412a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc;
13422a1ad637SFrançois Tigeot 	unsigned int i;
13432a1ad637SFrançois Tigeot 	char status[SND_STATUSLEN];
13442a1ad637SFrançois Tigeot 	uint32_t inte, ipr;
13452a1ad637SFrançois Tigeot 	uintptr_t route, r, ivar;
13462a1ad637SFrançois Tigeot 
134767931cc4SFrançois Tigeot 	sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
13482a1ad637SFrançois Tigeot 	sc->card = (struct emu_sc_info *)(device_get_softc(device_get_parent(dev)));
13492a1ad637SFrançois Tigeot 	if (sc->card == NULL) {
13502a1ad637SFrançois Tigeot 		device_printf(dev, "cannot get bridge conf\n");
135167931cc4SFrançois Tigeot 		kfree(sc, M_DEVBUF);
13522a1ad637SFrançois Tigeot 		return (ENXIO);
13532a1ad637SFrançois Tigeot 	}
13542a1ad637SFrançois Tigeot 
13552a1ad637SFrançois Tigeot 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10kx pcm softc");
13562a1ad637SFrançois Tigeot 	sc->dev = dev;
13572a1ad637SFrançois Tigeot 
13582a1ad637SFrançois Tigeot 	r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &ivar);
13592a1ad637SFrançois Tigeot 	sc->is_emu10k1 = ivar ? 1 : 0;
13602a1ad637SFrançois Tigeot 
13612a1ad637SFrançois Tigeot 	r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_MCH_DISABLED, &ivar);
13622a1ad637SFrançois Tigeot 	sc->mch_disabled = ivar ? 1 : 0;
13632a1ad637SFrançois Tigeot 
13642a1ad637SFrançois Tigeot 	sc->codec = NULL;
13652a1ad637SFrançois Tigeot 
13662a1ad637SFrançois Tigeot 	for (i = 0; i < 8; i++) {
13672a1ad637SFrançois Tigeot 		sc->rt.routing_left[i] = i;
13682a1ad637SFrançois Tigeot 		sc->rt.amounts_left[i] = 0x00;
13692a1ad637SFrançois Tigeot 		sc->rt.routing_right[i] = i;
13702a1ad637SFrançois Tigeot 		sc->rt.amounts_right[i] = 0x00;
13712a1ad637SFrançois Tigeot 	}
13722a1ad637SFrançois Tigeot 
13732a1ad637SFrançois Tigeot 	for (i = 0; i < 8; i++) {
13742a1ad637SFrançois Tigeot 		sc->rt_mono.routing_left[i] = i;
13752a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[i] = 0x00;
13762a1ad637SFrançois Tigeot 		sc->rt_mono.routing_right[i] = i;
13772a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_right[i] = 0x00;
13782a1ad637SFrançois Tigeot 	}
13792a1ad637SFrançois Tigeot 
13802a1ad637SFrançois Tigeot 	sc->emu10k1_volcache[0][0] = 75;
13812a1ad637SFrançois Tigeot 	sc->emu10k1_volcache[1][0] = 75;
13822a1ad637SFrançois Tigeot 	sc->emu10k1_volcache[0][1] = 75;
13832a1ad637SFrançois Tigeot 	sc->emu10k1_volcache[1][1] = 75;
13842a1ad637SFrançois Tigeot 	r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ROUTE, &route);
13852a1ad637SFrançois Tigeot 	sc->route = route;
13862a1ad637SFrançois Tigeot 	switch (route) {
13872a1ad637SFrançois Tigeot 	case RT_FRONT:
13882a1ad637SFrançois Tigeot 		sc->rt.amounts_left[0] = 0xff;
13892a1ad637SFrançois Tigeot 		sc->rt.amounts_right[1] = 0xff;
13902a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[0] = 0xff;
13912a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[1] = 0xff;
13922a1ad637SFrançois Tigeot 		if (sc->is_emu10k1)
13932a1ad637SFrançois Tigeot 			sc->codec = AC97_CREATE(dev, sc, emu_ac97);
13942a1ad637SFrançois Tigeot 		else
13952a1ad637SFrançois Tigeot 			sc->codec = AC97_CREATE(dev, sc, emu_eac97);
13962a1ad637SFrançois Tigeot 		sc->ac97_mixerclass = NULL;
13972a1ad637SFrançois Tigeot 		if (sc->codec != NULL)
13982a1ad637SFrançois Tigeot 			sc->ac97_mixerclass = ac97_getmixerclass();
13992a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emudspmixer_class, sc)) {
14002a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize DSP mixer\n");
14012a1ad637SFrançois Tigeot 			goto bad;
14022a1ad637SFrançois Tigeot 		}
14032a1ad637SFrançois Tigeot 		break;
14042a1ad637SFrançois Tigeot 	case RT_REAR:
14052a1ad637SFrançois Tigeot 		sc->rt.amounts_left[2] = 0xff;
14062a1ad637SFrançois Tigeot 		sc->rt.amounts_right[3] = 0xff;
14072a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[2] = 0xff;
14082a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[3] = 0xff;
14092a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emudspmixer_class, sc)) {
14102a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize mixer\n");
14112a1ad637SFrançois Tigeot 			goto bad;
14122a1ad637SFrançois Tigeot 		}
14132a1ad637SFrançois Tigeot 		break;
14142a1ad637SFrançois Tigeot 	case RT_CENTER:
14152a1ad637SFrançois Tigeot 		sc->rt.amounts_left[4] = 0xff;
14162a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[4] = 0xff;
14172a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emudspmixer_class, sc)) {
14182a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize mixer\n");
14192a1ad637SFrançois Tigeot 			goto bad;
14202a1ad637SFrançois Tigeot 		}
14212a1ad637SFrançois Tigeot 		break;
14222a1ad637SFrançois Tigeot 	case RT_SUB:
14232a1ad637SFrançois Tigeot 		sc->rt.amounts_left[5] = 0xff;
14242a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[5] = 0xff;
14252a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emudspmixer_class, sc)) {
14262a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize mixer\n");
14272a1ad637SFrançois Tigeot 			goto bad;
14282a1ad637SFrançois Tigeot 		}
14292a1ad637SFrançois Tigeot 		break;
14302a1ad637SFrançois Tigeot 	case RT_SIDE:
14312a1ad637SFrançois Tigeot 		sc->rt.amounts_left[6] = 0xff;
14322a1ad637SFrançois Tigeot 		sc->rt.amounts_right[7] = 0xff;
14332a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[6] = 0xff;
14342a1ad637SFrançois Tigeot 		sc->rt_mono.amounts_left[7] = 0xff;
14352a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emudspmixer_class, sc)) {
14362a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize mixer\n");
14372a1ad637SFrançois Tigeot 			goto bad;
14382a1ad637SFrançois Tigeot 		}
14392a1ad637SFrançois Tigeot 		break;
14402a1ad637SFrançois Tigeot 	case RT_MCHRECORD:
14412a1ad637SFrançois Tigeot 		if (mixer_init(dev, &emuefxmixer_class, sc)) {
14422a1ad637SFrançois Tigeot 			device_printf(dev, "failed to initialize EFX mixer\n");
14432a1ad637SFrançois Tigeot 			goto bad;
14442a1ad637SFrançois Tigeot 		}
14452a1ad637SFrançois Tigeot 		break;
14462a1ad637SFrançois Tigeot 	default:
14472a1ad637SFrançois Tigeot 		device_printf(dev, "invalid default route\n");
14482a1ad637SFrançois Tigeot 		goto bad;
14492a1ad637SFrançois Tigeot 	}
14502a1ad637SFrançois Tigeot 
14512a1ad637SFrançois Tigeot 	inte = EMU_INTE_INTERTIMERENB;
14522a1ad637SFrançois Tigeot 	ipr = EMU_IPR_INTERVALTIMER; /* Used by playback & ADC */
14532a1ad637SFrançois Tigeot 	sc->ihandle = emu_intr_register(sc->card, inte, ipr, &emu_pcm_intr, sc);
14542a1ad637SFrançois Tigeot 
14552a1ad637SFrançois Tigeot 	if (emu_pcm_init(sc) == -1) {
14562a1ad637SFrançois Tigeot 		device_printf(dev, "unable to initialize PCM part of the card\n");
14572a1ad637SFrançois Tigeot 		goto bad;
14582a1ad637SFrançois Tigeot 	}
14592a1ad637SFrançois Tigeot 
14602a1ad637SFrançois Tigeot 	/*
14612a1ad637SFrançois Tigeot 	 * We don't register interrupt handler with snd_setup_intr
14622a1ad637SFrançois Tigeot 	 * in pcm device. Mark pcm device as MPSAFE manually.
14632a1ad637SFrançois Tigeot 	 */
14642a1ad637SFrançois Tigeot 	pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
14652a1ad637SFrançois Tigeot 
14662a1ad637SFrançois Tigeot 	/* XXX we should better get number of available channels from parent */
14672a1ad637SFrançois Tigeot 	if (pcm_register(dev, sc, (route == RT_FRONT) ? MAX_CHANNELS : 1, (route == RT_FRONT) ? 1 : 0)) {
14682a1ad637SFrançois Tigeot 		device_printf(dev, "can't register PCM channels!\n");
14692a1ad637SFrançois Tigeot 		goto bad;
14702a1ad637SFrançois Tigeot 	}
14712a1ad637SFrançois Tigeot 	sc->pnum = 0;
14722a1ad637SFrançois Tigeot 	if (route != RT_MCHRECORD)
14732a1ad637SFrançois Tigeot 		pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
14742a1ad637SFrançois Tigeot 	if (route == RT_FRONT) {
14752a1ad637SFrançois Tigeot 		for (i = 1; i < MAX_CHANNELS; i++)
14762a1ad637SFrançois Tigeot 			pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
14772a1ad637SFrançois Tigeot 		pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc);
14782a1ad637SFrançois Tigeot 	}
14792a1ad637SFrançois Tigeot 	if (route == RT_MCHRECORD)
14802a1ad637SFrançois Tigeot 		pcm_addchan(dev, PCMDIR_REC, &emufxrchan_class, sc);
14812a1ad637SFrançois Tigeot 
148267931cc4SFrançois Tigeot 	ksnprintf(status, SND_STATUSLEN, "on %s",
148367931cc4SFrançois Tigeot 		  device_get_nameunit(device_get_parent(dev)));
14842a1ad637SFrançois Tigeot 	pcm_setstatus(dev, status);
14852a1ad637SFrançois Tigeot 
14862a1ad637SFrançois Tigeot 	return (0);
14872a1ad637SFrançois Tigeot 
14882a1ad637SFrançois Tigeot bad:
14892a1ad637SFrançois Tigeot 	if (sc->codec)
14902a1ad637SFrançois Tigeot 		ac97_destroy(sc->codec);
14912a1ad637SFrançois Tigeot 	if (sc->lock)
14922a1ad637SFrançois Tigeot 		snd_mtxfree(sc->lock);
149367931cc4SFrançois Tigeot 	kfree(sc, M_DEVBUF);
14942a1ad637SFrançois Tigeot 	return (ENXIO);
14952a1ad637SFrançois Tigeot }
14962a1ad637SFrançois Tigeot 
14972a1ad637SFrançois Tigeot static int
emu_pcm_detach(device_t dev)14982a1ad637SFrançois Tigeot emu_pcm_detach(device_t dev)
14992a1ad637SFrançois Tigeot {
15002a1ad637SFrançois Tigeot 	int r;
15012a1ad637SFrançois Tigeot 	struct emu_pcm_info *sc;
15022a1ad637SFrançois Tigeot 
15032a1ad637SFrançois Tigeot 	sc = pcm_getdevinfo(dev);
15042a1ad637SFrançois Tigeot 
15052a1ad637SFrançois Tigeot 	r = pcm_unregister(dev);
15062a1ad637SFrançois Tigeot 
15072a1ad637SFrançois Tigeot 	if (r) 	return (r);
15082a1ad637SFrançois Tigeot 
15092a1ad637SFrançois Tigeot 	emu_pcm_uninit(sc);
15102a1ad637SFrançois Tigeot 
15112a1ad637SFrançois Tigeot 	if (sc->lock)
15122a1ad637SFrançois Tigeot 		snd_mtxfree(sc->lock);
151367931cc4SFrançois Tigeot 	kfree(sc, M_DEVBUF);
15142a1ad637SFrançois Tigeot 
15152a1ad637SFrançois Tigeot 	return (0);
15162a1ad637SFrançois Tigeot }
15172a1ad637SFrançois Tigeot 
15182a1ad637SFrançois Tigeot static device_method_t emu_pcm_methods[] = {
15192a1ad637SFrançois Tigeot 	DEVMETHOD(device_probe, emu_pcm_probe),
15202a1ad637SFrançois Tigeot 	DEVMETHOD(device_attach, emu_pcm_attach),
15212a1ad637SFrançois Tigeot 	DEVMETHOD(device_detach, emu_pcm_detach),
15222a1ad637SFrançois Tigeot 
15232a1ad637SFrançois Tigeot 	DEVMETHOD_END
15242a1ad637SFrançois Tigeot };
15252a1ad637SFrançois Tigeot 
15262a1ad637SFrançois Tigeot static driver_t emu_pcm_driver = {
15272a1ad637SFrançois Tigeot 	"pcm",
15282a1ad637SFrançois Tigeot 	emu_pcm_methods,
15292a1ad637SFrançois Tigeot 	PCM_SOFTC_SIZE,
15302a1ad637SFrançois Tigeot 	NULL,
15312a1ad637SFrançois Tigeot 	0,
15322a1ad637SFrançois Tigeot 	NULL
15332a1ad637SFrançois Tigeot };
1534*aa6ac96eSSascha Wildner DRIVER_MODULE(snd_emu10kx_pcm, emu10kx, emu_pcm_driver, pcm_devclass, NULL, NULL);
15352a1ad637SFrançois Tigeot MODULE_DEPEND(snd_emu10kx_pcm, snd_emu10kx, SND_EMU10KX_MINVER, SND_EMU10KX_PREFVER, SND_EMU10KX_MAXVER);
15362a1ad637SFrançois Tigeot MODULE_DEPEND(snd_emu10kx_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
15372a1ad637SFrançois Tigeot MODULE_VERSION(snd_emu10kx_pcm, SND_EMU10KX_PREFVER);
1538