envy24.c revision 165306
1/*
2 * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <dev/sound/pcm/sound.h>
29#include <dev/sound/pcm/ac97.h>
30#include <dev/sound/pci/spicds.h>
31#include <dev/sound/pci/envy24.h>
32
33#include <dev/pci/pcireg.h>
34#include <dev/pci/pcivar.h>
35
36#include "mixer_if.h"
37
38SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/envy24.c 165306 2006-12-17 16:06:45Z ariff $");
39
40MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio");
41
42/* -------------------------------------------------------------------- */
43
44struct sc_info;
45
46#define ENVY24_PLAY_CHNUM 10
47#define ENVY24_REC_CHNUM 12
48#define ENVY24_PLAY_BUFUNIT (4 /* byte/sample */ * 10 /* channel */)
49#define ENVY24_REC_BUFUNIT  (4 /* byte/sample */ * 12 /* channel */)
50#define ENVY24_SAMPLE_NUM   4096
51
52#define ENVY24_TIMEOUT 1000
53
54#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
55
56#define ENVY24_NAMELEN 32
57
58struct envy24_sample {
59        volatile u_int32_t buffer;
60};
61
62typedef struct envy24_sample sample32_t;
63
64/* channel registers */
65struct sc_chinfo {
66	struct snd_dbuf		*buffer;
67	struct pcm_channel	*channel;
68	struct sc_info		*parent;
69	int			dir;
70	unsigned		num; /* hw channel number */
71
72	/* channel information */
73	u_int32_t		format;
74	u_int32_t		speed;
75	u_int32_t		blk; /* hw block size(dword) */
76
77	/* format conversion structure */
78	u_int8_t		*data;
79	unsigned int		size; /* data buffer size(byte) */
80	int			unit; /* sample size(byte) */
81	unsigned int		offset; /* samples number offset */
82	void			(*emldma)(struct sc_chinfo *);
83
84	/* flags */
85	int			run;
86};
87
88/* codec interface entrys */
89struct codec_entry {
90	void *(*create)(device_t dev, void *devinfo, int dir, int num);
91	void (*destroy)(void *codec);
92	void (*init)(void *codec);
93	void (*reinit)(void *codec);
94	void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right);
95	void (*setrate)(void *codec, int which, int rate);
96};
97
98/* system configuration information */
99struct cfg_info {
100	char *name;
101	u_int16_t subvendor, subdevice;
102	u_int8_t scfg, acl, i2s, spdif;
103	u_int8_t gpiomask, gpiostate, gpiodir;
104	u_int8_t cdti, cclk, cs, cif, type;
105	u_int8_t free;
106	struct codec_entry *codec;
107};
108
109/* device private data */
110struct sc_info {
111	device_t	dev;
112	void		*lock;
113
114	/* Control/Status registor */
115	struct resource *cs;
116	int		csid;
117	bus_space_tag_t cst;
118	bus_space_handle_t csh;
119	/* DDMA registor */
120	struct resource *ddma;
121	int		ddmaid;
122	bus_space_tag_t ddmat;
123	bus_space_handle_t ddmah;
124	/* Consumer Section DMA Channel Registers */
125	struct resource *ds;
126	int		dsid;
127	bus_space_tag_t dst;
128	bus_space_handle_t dsh;
129	/* MultiTrack registor */
130	struct resource *mt;
131	int		mtid;
132	bus_space_tag_t mtt;
133	bus_space_handle_t mth;
134	/* DMA tag */
135	bus_dma_tag_t dmat;
136	/* IRQ resource */
137	struct resource *irq;
138	int		irqid;
139	void		*ih;
140
141	/* system configuration data */
142	struct cfg_info *cfg;
143
144	/* ADC/DAC number and info */
145	int		adcn, dacn;
146	void		*adc[4], *dac[4];
147
148	/* mixer control data */
149	u_int32_t	src;
150	u_int8_t	left[ENVY24_CHAN_NUM];
151	u_int8_t	right[ENVY24_CHAN_NUM];
152
153	/* Play/Record DMA fifo */
154	sample32_t	*pbuf;
155	sample32_t	*rbuf;
156	u_int32_t	psize, rsize; /* DMA buffer size(byte) */
157	u_int16_t	blk[2]; /* transfer check blocksize(dword) */
158	bus_dmamap_t	pmap, rmap;
159
160	/* current status */
161	u_int32_t	speed;
162	int		run[2];
163	u_int16_t	intr[2];
164	struct pcmchan_caps	caps[2];
165
166	/* channel info table */
167	unsigned	chnum;
168	struct sc_chinfo chan[11];
169};
170
171/* -------------------------------------------------------------------- */
172
173/*
174 * prototypes
175 */
176
177/* DMA emulator */
178static void envy24_p8u(struct sc_chinfo *);
179static void envy24_p16sl(struct sc_chinfo *);
180static void envy24_p32sl(struct sc_chinfo *);
181static void envy24_r16sl(struct sc_chinfo *);
182static void envy24_r32sl(struct sc_chinfo *);
183
184/* channel interface */
185static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
186static int envy24chan_setformat(kobj_t, void *, u_int32_t);
187static int envy24chan_setspeed(kobj_t, void *, u_int32_t);
188static int envy24chan_setblocksize(kobj_t, void *, u_int32_t);
189static int envy24chan_trigger(kobj_t, void *, int);
190static int envy24chan_getptr(kobj_t, void *);
191static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *);
192
193/* mixer interface */
194static int envy24mixer_init(struct snd_mixer *);
195static int envy24mixer_reinit(struct snd_mixer *);
196static int envy24mixer_uninit(struct snd_mixer *);
197static int envy24mixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
198static u_int32_t envy24mixer_setrecsrc(struct snd_mixer *, u_int32_t);
199
200/* M-Audio Delta series AK4524 access interface */
201static void *envy24_delta_ak4524_create(device_t, void *, int, int);
202static void envy24_delta_ak4524_destroy(void *);
203static void envy24_delta_ak4524_init(void *);
204static void envy24_delta_ak4524_reinit(void *);
205static void envy24_delta_ak4524_setvolume(void *, int, unsigned int, unsigned int);
206
207/* -------------------------------------------------------------------- */
208
209/*
210  system constant tables
211*/
212
213/* API -> hardware channel map */
214static unsigned envy24_chanmap[ENVY24_CHAN_NUM] = {
215	ENVY24_CHAN_PLAY_SPDIF, /* 0 */
216	ENVY24_CHAN_PLAY_DAC1,  /* 1 */
217	ENVY24_CHAN_PLAY_DAC2,  /* 2 */
218	ENVY24_CHAN_PLAY_DAC3,  /* 3 */
219	ENVY24_CHAN_PLAY_DAC4,  /* 4 */
220	ENVY24_CHAN_REC_MIX,    /* 5 */
221	ENVY24_CHAN_REC_SPDIF,  /* 6 */
222	ENVY24_CHAN_REC_ADC1,   /* 7 */
223	ENVY24_CHAN_REC_ADC2,   /* 8 */
224	ENVY24_CHAN_REC_ADC3,   /* 9 */
225	ENVY24_CHAN_REC_ADC4,   /* 10 */
226};
227
228/* mixer -> API channel map. see above */
229static int envy24_mixmap[] = {
230	-1, /* Master output level. It is depend on codec support */
231	-1, /* Treble level of all output channels */
232	-1, /* Bass level of all output channels */
233	-1, /* Volume of synthesier input */
234	0,  /* Output level for the audio device */
235	-1, /* Output level for the PC speaker */
236	7,  /* line in jack */
237	-1, /* microphone jack */
238	-1, /* CD audio input */
239	-1, /* Recording monitor */
240	1,  /* alternative codec */
241	-1, /* global recording level */
242	-1, /* Input gain */
243	-1, /* Output gain */
244	8,  /* Input source 1 */
245	9,  /* Input source 2 */
246	10, /* Input source 3 */
247	6,  /* Digital (input) 1 */
248	-1, /* Digital (input) 2 */
249	-1, /* Digital (input) 3 */
250	-1, /* Phone input */
251	-1, /* Phone output */
252	-1, /* Video/TV (audio) in */
253	-1, /* Radio in */
254	-1, /* Monitor volume */
255};
256
257/* variable rate audio */
258static u_int32_t envy24_speed[] = {
259    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
260    12000, 11025, 9600, 8000, 0
261};
262
263/* known boards configuration */
264static struct codec_entry delta_codec = {
265	envy24_delta_ak4524_create,
266	envy24_delta_ak4524_destroy,
267	envy24_delta_ak4524_init,
268	envy24_delta_ak4524_reinit,
269	envy24_delta_ak4524_setvolume,
270	NULL, /* setrate */
271};
272
273static struct cfg_info cfg_table[] = {
274	{
275		"Envy24 audio (M Audio Delta Dio 2496)",
276		0x1412, 0xd631,
277		0x10, 0x80, 0xf0, 0x03,
278		0xff, 0x00, 0x00,
279		0x10, 0x20, 0x40, 0x00, 0x00,
280		0x00,
281		&delta_codec,
282	},
283	{
284		"Envy24 audio (Terratec DMX 6fire)",
285		0x153b, 0x1138,
286		0x2f, 0x80, 0xf0, 0x03,
287		0xc0, 0xff, 0x7f,
288		0x10, 0x20, 0x01, 0x01, 0x00,
289		0x00,
290 		&delta_codec,
291 	},
292	{
293		"Envy24 audio (M Audio Audiophile 2496)",
294		0x1412, 0xd634,
295		0x10, 0x80, 0x72, 0x03,
296		0x04, 0xfe, 0xfb,
297		0x08, 0x02, 0x20, 0x00, 0x01,
298		0x00,
299 		&delta_codec,
300 	},
301	{
302		"Envy24 audio (Generic)",
303		0, 0,
304		0x0f, 0x00, 0x01, 0x03,
305		0xff, 0x00, 0x00,
306		0x10, 0x20, 0x40, 0x00, 0x00,
307		0x00,
308		&delta_codec, /* default codec routines */
309	}
310};
311
312static u_int32_t envy24_recfmt[] = {
313	AFMT_STEREO | AFMT_S16_LE,
314	AFMT_STEREO | AFMT_S32_LE,
315	0
316};
317static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0};
318
319static u_int32_t envy24_playfmt[] = {
320	AFMT_STEREO | AFMT_U8,
321	AFMT_STEREO | AFMT_S16_LE,
322	AFMT_STEREO | AFMT_S32_LE,
323	0
324};
325
326static struct pcmchan_caps envy24_playcaps = {8000, 96000, envy24_playfmt, 0};
327
328struct envy24_emldma {
329	u_int32_t	format;
330	void		(*emldma)(struct sc_chinfo *);
331	int		unit;
332};
333
334static struct envy24_emldma envy24_pemltab[] = {
335	{AFMT_STEREO | AFMT_U8, envy24_p8u, 2},
336	{AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4},
337	{AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8},
338	{0, NULL, 0}
339};
340
341static struct envy24_emldma envy24_remltab[] = {
342	{AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4},
343	{AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8},
344	{0, NULL, 0}
345};
346
347/* -------------------------------------------------------------------- */
348
349/* common routines */
350static u_int32_t
351envy24_rdcs(struct sc_info *sc, int regno, int size)
352{
353	switch (size) {
354	case 1:
355		return bus_space_read_1(sc->cst, sc->csh, regno);
356	case 2:
357		return bus_space_read_2(sc->cst, sc->csh, regno);
358	case 4:
359		return bus_space_read_4(sc->cst, sc->csh, regno);
360	default:
361		return 0xffffffff;
362	}
363}
364
365static void
366envy24_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size)
367{
368	switch (size) {
369	case 1:
370		bus_space_write_1(sc->cst, sc->csh, regno, data);
371		break;
372	case 2:
373		bus_space_write_2(sc->cst, sc->csh, regno, data);
374		break;
375	case 4:
376		bus_space_write_4(sc->cst, sc->csh, regno, data);
377		break;
378	}
379}
380
381static u_int32_t
382envy24_rdmt(struct sc_info *sc, int regno, int size)
383{
384	switch (size) {
385	case 1:
386		return bus_space_read_1(sc->mtt, sc->mth, regno);
387	case 2:
388		return bus_space_read_2(sc->mtt, sc->mth, regno);
389	case 4:
390		return bus_space_read_4(sc->mtt, sc->mth, regno);
391	default:
392		return 0xffffffff;
393	}
394}
395
396static void
397envy24_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size)
398{
399	switch (size) {
400	case 1:
401		bus_space_write_1(sc->mtt, sc->mth, regno, data);
402		break;
403	case 2:
404		bus_space_write_2(sc->mtt, sc->mth, regno, data);
405		break;
406	case 4:
407		bus_space_write_4(sc->mtt, sc->mth, regno, data);
408		break;
409	}
410}
411
412static u_int32_t
413envy24_rdci(struct sc_info *sc, int regno)
414{
415	envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
416	return envy24_rdcs(sc, ENVY24_CCS_DATA, 1);
417}
418
419static void
420envy24_wrci(struct sc_info *sc, int regno, u_int32_t data)
421{
422	envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1);
423	envy24_wrcs(sc, ENVY24_CCS_DATA, data, 1);
424}
425
426/* -------------------------------------------------------------------- */
427
428/* I2C port/E2PROM access routines */
429
430static int
431envy24_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
432{
433	u_int32_t data;
434	int i;
435
436#if(0)
437	device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
438#endif
439	for (i = 0; i < ENVY24_TIMEOUT; i++) {
440		data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
441		if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
442			break;
443		DELAY(32); /* 31.25kHz */
444	}
445	if (i == ENVY24_TIMEOUT) {
446		return -1;
447	}
448	envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
449	envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
450	    (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_RD, 1);
451	for (i = 0; i < ENVY24_TIMEOUT; i++) {
452		data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
453		if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
454			break;
455		DELAY(32); /* 31.25kHz */
456	}
457	if (i == ENVY24_TIMEOUT) {
458		return -1;
459	}
460	data = envy24_rdcs(sc, ENVY24_CCS_I2CDATA, 1);
461
462#if(0)
463	device_printf(sc->dev, "envy24_rdi2c(): return 0x%x\n", data);
464#endif
465	return (int)data;
466}
467
468#if 0
469static int
470envy24_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
471{
472	u_int32_t tmp;
473	int i;
474
475#if(0)
476	device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
477#endif
478	for (i = 0; i < ENVY24_TIMEOUT; i++) {
479		tmp = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
480		if ((tmp & ENVY24_CCS_I2CSTAT_BSY) == 0)
481			break;
482		DELAY(32); /* 31.25kHz */
483	}
484	if (i == ENVY24_TIMEOUT) {
485		return -1;
486	}
487	envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1);
488	envy24_wrcs(sc, ENVY24_CCS_I2CDATA, data, 1);
489	envy24_wrcs(sc, ENVY24_CCS_I2CDEV,
490	    (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_WR, 1);
491	for (i = 0; i < ENVY24_TIMEOUT; i++) {
492		data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
493		if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0)
494			break;
495		DELAY(32); /* 31.25kHz */
496	}
497	if (i == ENVY24_TIMEOUT) {
498		return -1;
499	}
500
501	return 0;
502}
503#endif
504
505static int
506envy24_rdrom(struct sc_info *sc, u_int32_t addr)
507{
508	u_int32_t data;
509
510#if(0)
511	device_printf(sc->dev, "envy24_rdrom(sc, 0x%02x)\n", addr);
512#endif
513	data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1);
514	if ((data & ENVY24_CCS_I2CSTAT_ROM) == 0) {
515#if(0)
516		device_printf(sc->dev, "envy24_rdrom(): E2PROM not presented\n");
517#endif
518		return -1;
519	}
520
521	return envy24_rdi2c(sc, ENVY24_CCS_I2CDEV_ROM, addr);
522}
523
524static struct cfg_info *
525envy24_rom2cfg(struct sc_info *sc)
526{
527	struct cfg_info *buff;
528	int size;
529	int i;
530
531#if(0)
532	device_printf(sc->dev, "envy24_rom2cfg(sc)\n");
533#endif
534	size = envy24_rdrom(sc, ENVY24_E2PROM_SIZE);
535	if (size < ENVY24_E2PROM_GPIODIR + 1) {
536#if(0)
537		device_printf(sc->dev, "envy24_rom2cfg(): ENVY24_E2PROM_SIZE-->%d\n", size);
538#endif
539		return NULL;
540	}
541	buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
542	if (buff == NULL) {
543#if(0)
544		device_printf(sc->dev, "envy24_rom2cfg(): malloc()\n");
545#endif
546		return NULL;
547	}
548	buff->free = 1;
549
550	buff->subvendor = envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR) << 8;
551	buff->subvendor += envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR + 1);
552	buff->subdevice = envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE) << 8;
553	buff->subdevice += envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE + 1);
554	buff->scfg = envy24_rdrom(sc, ENVY24_E2PROM_SCFG);
555	buff->acl = envy24_rdrom(sc, ENVY24_E2PROM_ACL);
556	buff->i2s = envy24_rdrom(sc, ENVY24_E2PROM_I2S);
557	buff->spdif = envy24_rdrom(sc, ENVY24_E2PROM_SPDIF);
558	buff->gpiomask = envy24_rdrom(sc, ENVY24_E2PROM_GPIOMASK);
559	buff->gpiostate = envy24_rdrom(sc, ENVY24_E2PROM_GPIOSTATE);
560	buff->gpiodir = envy24_rdrom(sc, ENVY24_E2PROM_GPIODIR);
561
562	for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++)
563		if (cfg_table[i].subvendor == buff->subvendor &&
564		    cfg_table[i].subdevice == buff->subdevice)
565			break;
566	buff->name = cfg_table[i].name;
567	buff->codec = cfg_table[i].codec;
568
569	return buff;
570}
571
572static void
573envy24_cfgfree(struct cfg_info *cfg) {
574	if (cfg == NULL)
575		return;
576	if (cfg->free)
577		free(cfg, M_ENVY24);
578	return;
579}
580
581/* -------------------------------------------------------------------- */
582
583/* AC'97 codec access routines */
584
585#if 0
586static int
587envy24_coldcd(struct sc_info *sc)
588{
589	u_int32_t data;
590	int i;
591
592#if(0)
593	device_printf(sc->dev, "envy24_coldcd()\n");
594#endif
595	envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_CLD, 1);
596	DELAY(10);
597	envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
598	DELAY(1000);
599	for (i = 0; i < ENVY24_TIMEOUT; i++) {
600		data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
601		if (data & ENVY24_MT_AC97CMD_RDY) {
602			return 0;
603		}
604	}
605
606	return -1;
607}
608#endif
609
610static int
611envy24_slavecd(struct sc_info *sc)
612{
613	u_int32_t data;
614	int i;
615
616#if(0)
617	device_printf(sc->dev, "envy24_slavecd()\n");
618#endif
619	envy24_wrmt(sc, ENVY24_MT_AC97CMD,
620	    ENVY24_MT_AC97CMD_CLD | ENVY24_MT_AC97CMD_WRM, 1);
621	DELAY(10);
622	envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1);
623	DELAY(1000);
624	for (i = 0; i < ENVY24_TIMEOUT; i++) {
625		data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
626		if (data & ENVY24_MT_AC97CMD_RDY) {
627			return 0;
628		}
629	}
630
631	return -1;
632}
633
634#if 0
635static int
636envy24_rdcd(kobj_t obj, void *devinfo, int regno)
637{
638	struct sc_info *sc = (struct sc_info *)devinfo;
639	u_int32_t data;
640	int i;
641
642#if(0)
643	device_printf(sc->dev, "envy24_rdcd(obj, sc, 0x%02x)\n", regno);
644#endif
645	envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
646	envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_RD, 1);
647	for (i = 0; i < ENVY24_TIMEOUT; i++) {
648		data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
649		if ((data & ENVY24_MT_AC97CMD_RD) == 0)
650			break;
651	}
652	data = envy24_rdmt(sc, ENVY24_MT_AC97DLO, 2);
653
654#if(0)
655	device_printf(sc->dev, "envy24_rdcd(): return 0x%x\n", data);
656#endif
657	return (int)data;
658}
659
660static int
661envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
662{
663	struct sc_info *sc = (struct sc_info *)devinfo;
664	u_int32_t cmd;
665	int i;
666
667#if(0)
668	device_printf(sc->dev, "envy24_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
669#endif
670	envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1);
671	envy24_wrmt(sc, ENVY24_MT_AC97DLO, (u_int32_t)data, 2);
672	envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_WR, 1);
673	for (i = 0; i < ENVY24_TIMEOUT; i++) {
674		cmd = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1);
675		if ((cmd & ENVY24_MT_AC97CMD_WR) == 0)
676			break;
677	}
678
679	return 0;
680}
681
682static kobj_method_t envy24_ac97_methods[] = {
683	KOBJMETHOD(ac97_read,	envy24_rdcd),
684	KOBJMETHOD(ac97_write,	envy24_wrcd),
685	{0, 0}
686};
687AC97_DECLARE(envy24_ac97);
688#endif
689
690/* -------------------------------------------------------------------- */
691
692/* GPIO access routines */
693
694static u_int32_t
695envy24_gpiord(struct sc_info *sc)
696{
697	return envy24_rdci(sc, ENVY24_CCI_GPIODAT);
698}
699
700static void
701envy24_gpiowr(struct sc_info *sc, u_int32_t data)
702{
703#if(0)
704	device_printf(sc->dev, "envy24_gpiowr(sc, 0x%02x)\n", data & 0xff);
705	return;
706#endif
707	envy24_wrci(sc, ENVY24_CCI_GPIODAT, data);
708	return;
709}
710
711#if 0
712static u_int32_t
713envy24_gpiogetmask(struct sc_info *sc)
714{
715	return envy24_rdci(sc, ENVY24_CCI_GPIOMASK);
716}
717#endif
718
719static void
720envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask)
721{
722	envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask);
723	return;
724}
725
726#if 0
727static u_int32_t
728envy24_gpiogetdir(struct sc_info *sc)
729{
730	return envy24_rdci(sc, ENVY24_CCI_GPIOCTL);
731}
732#endif
733
734static void
735envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir)
736{
737	envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir);
738	return;
739}
740
741/* -------------------------------------------------------------------- */
742
743/* M-Audio Delta series AK4524 access interface routine */
744
745struct envy24_delta_ak4524_codec {
746	struct spicds_info *info;
747	struct sc_info *parent;
748	int dir;
749	int num;
750	int cs, cclk, cdti;
751};
752
753static void
754envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
755{
756	u_int32_t data = 0;
757	struct envy24_delta_ak4524_codec *ptr = codec;
758
759#if(0)
760	device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
761#endif
762	data = envy24_gpiord(ptr->parent);
763	data &= ~(ptr->cs | ptr->cclk | ptr->cdti);
764	if (cs) data += ptr->cs;
765	if (cclk) data += ptr->cclk;
766	if (cdti) data += ptr->cdti;
767	envy24_gpiowr(ptr->parent, data);
768	return;
769}
770
771static void *
772envy24_delta_ak4524_create(device_t dev, void *info, int dir, int num)
773{
774	struct sc_info *sc = info;
775	struct envy24_delta_ak4524_codec *buff = NULL;
776
777#if(0)
778	device_printf(sc->dev, "envy24_delta_ak4524_create(dev, sc, %d, %d)\n", dir, num);
779#endif
780
781	buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT);
782	if (buff == NULL)
783		return NULL;
784
785	if (dir == PCMDIR_REC && sc->adc[num] != NULL)
786		buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info;
787	else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL)
788		buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info;
789	else
790		buff->info = spicds_create(dev, buff, num, envy24_delta_ak4524_ctl);
791	if (buff->info == NULL) {
792		free(buff, M_ENVY24);
793		return NULL;
794	}
795
796	buff->parent = sc;
797	buff->dir = dir;
798	buff->num = num;
799
800	return (void *)buff;
801}
802
803static void
804envy24_delta_ak4524_destroy(void *codec)
805{
806	struct envy24_delta_ak4524_codec *ptr = codec;
807	if (ptr == NULL)
808		return;
809#if(0)
810	device_printf(ptr->parent->dev, "envy24_delta_ak4524_destroy()\n");
811#endif
812
813	if (ptr->dir == PCMDIR_PLAY) {
814		if (ptr->parent->dac[ptr->num] != NULL)
815			spicds_destroy(ptr->info);
816	}
817	else {
818		if (ptr->parent->adc[ptr->num] != NULL)
819			spicds_destroy(ptr->info);
820	}
821
822	free(codec, M_ENVY24);
823}
824
825static void
826envy24_delta_ak4524_init(void *codec)
827{
828#if 0
829	u_int32_t gpiomask, gpiodir;
830#endif
831	struct envy24_delta_ak4524_codec *ptr = codec;
832	if (ptr == NULL)
833		return;
834#if(0)
835	device_printf(ptr->parent->dev, "envy24_delta_ak4524_init()\n");
836#endif
837
838	/*
839	gpiomask = envy24_gpiogetmask(ptr->parent);
840	gpiomask &= ~(ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1);
841	envy24_gpiosetmask(ptr->parent, gpiomask);
842	gpiodir = envy24_gpiogetdir(ptr->parent);
843	gpiodir |= ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1;
844	envy24_gpiosetdir(ptr->parent, gpiodir);
845	*/
846	ptr->cs = ptr->parent->cfg->cs;
847#if 0
848	envy24_gpiosetmask(ptr->parent, ENVY24_GPIO_CS8414_STATUS);
849	envy24_gpiosetdir(ptr->parent, ~ENVY24_GPIO_CS8414_STATUS);
850	if (ptr->num == 0)
851		ptr->cs = ENVY24_GPIO_AK4524_CS0;
852	else
853		ptr->cs = ENVY24_GPIO_AK4524_CS1;
854	ptr->cclk = ENVY24_GPIO_AK4524_CCLK;
855#endif
856	ptr->cclk = ptr->parent->cfg->cclk;
857	ptr->cdti = ptr->parent->cfg->cdti;
858	spicds_settype(ptr->info,  ptr->parent->cfg->type);
859	spicds_setcif(ptr->info, ptr->parent->cfg->cif);
860	spicds_setformat(ptr->info,
861	    AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
862	spicds_setdvc(ptr->info, 0);
863	/* for the time being, init only first codec */
864	if (ptr->num == 0)
865		spicds_init(ptr->info);
866}
867
868static void
869envy24_delta_ak4524_reinit(void *codec)
870{
871	struct envy24_delta_ak4524_codec *ptr = codec;
872	if (ptr == NULL)
873		return;
874#if(0)
875	device_printf(ptr->parent->dev, "envy24_delta_ak4524_reinit()\n");
876#endif
877
878	spicds_reinit(ptr->info);
879}
880
881static void
882envy24_delta_ak4524_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
883{
884	struct envy24_delta_ak4524_codec *ptr = codec;
885	if (ptr == NULL)
886		return;
887#if(0)
888	device_printf(ptr->parent->dev, "envy24_delta_ak4524_set()\n");
889#endif
890
891	spicds_set(ptr->info, dir, left, right);
892}
893
894/*
895  There is no need for AK452[48] codec to set sample rate
896  static void
897  envy24_delta_ak4524_setrate(struct envy24_delta_ak4524_codec *codec, int which, int rate)
898  {
899  }
900*/
901
902/* -------------------------------------------------------------------- */
903
904/* hardware access routeines */
905
906static struct {
907	u_int32_t speed;
908	u_int32_t code;
909} envy24_speedtab[] = {
910	{48000, ENVY24_MT_RATE_48000},
911	{24000, ENVY24_MT_RATE_24000},
912	{12000, ENVY24_MT_RATE_12000},
913	{9600, ENVY24_MT_RATE_9600},
914	{32000, ENVY24_MT_RATE_32000},
915	{16000, ENVY24_MT_RATE_16000},
916	{8000, ENVY24_MT_RATE_8000},
917	{96000, ENVY24_MT_RATE_96000},
918	{64000, ENVY24_MT_RATE_64000},
919	{44100, ENVY24_MT_RATE_44100},
920	{22050, ENVY24_MT_RATE_22050},
921	{11025, ENVY24_MT_RATE_11025},
922	{88200, ENVY24_MT_RATE_88200},
923	{0, 0x10}
924};
925
926static int
927envy24_setspeed(struct sc_info *sc, u_int32_t speed) {
928	u_int32_t code;
929	int i = 0;
930
931#if(0)
932	device_printf(sc->dev, "envy24_setspeed(sc, %d)\n", speed);
933#endif
934	if (speed == 0) {
935		code = ENVY24_MT_RATE_SPDIF; /* external master clock */
936		envy24_slavecd(sc);
937	}
938	else {
939		for (i = 0; envy24_speedtab[i].speed != 0; i++) {
940			if (envy24_speedtab[i].speed == speed)
941				break;
942		}
943		code = envy24_speedtab[i].code;
944	}
945#if(0)
946	device_printf(sc->dev, "envy24_setspeed(): speed %d/code 0x%04x\n", envy24_speedtab[i].speed, code);
947#endif
948	if (code < 0x10) {
949		envy24_wrmt(sc, ENVY24_MT_RATE, code, 1);
950		code = envy24_rdmt(sc, ENVY24_MT_RATE, 1);
951		code &= ENVY24_MT_RATE_MASK;
952		for (i = 0; envy24_speedtab[i].code < 0x10; i++) {
953			if (envy24_speedtab[i].code == code)
954				break;
955		}
956		speed = envy24_speedtab[i].speed;
957	}
958	else
959		speed = 0;
960
961#if(0)
962	device_printf(sc->dev, "envy24_setspeed(): return %d\n", speed);
963#endif
964	return speed;
965}
966
967static void
968envy24_setvolume(struct sc_info *sc, unsigned ch)
969{
970#if(0)
971	device_printf(sc->dev, "envy24_setvolume(sc, %d)\n", ch);
972#endif
973if (sc->cfg->subvendor==0x153b  && sc->cfg->subdevice==0x1138 ) {
974        envy24_wrmt(sc, ENVY24_MT_VOLIDX, 16, 1);
975        envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2);
976        envy24_wrmt(sc, ENVY24_MT_VOLIDX, 17, 1);
977        envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2);
978	}
979
980	envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
981	envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
982	envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
983	envy24_wrmt(sc, ENVY24_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
984}
985
986static void
987envy24_mutevolume(struct sc_info *sc, unsigned ch)
988{
989	u_int32_t vol;
990
991#if(0)
992	device_printf(sc->dev, "envy24_mutevolume(sc, %d)\n", ch);
993#endif
994	vol = ENVY24_VOL_MUTE << 8 | ENVY24_VOL_MUTE;
995	envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1);
996	envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
997	envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1);
998	envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2);
999}
1000
1001static u_int32_t
1002envy24_gethwptr(struct sc_info *sc, int dir)
1003{
1004	int unit, regno;
1005	u_int32_t ptr, rtn;
1006
1007#if(0)
1008	device_printf(sc->dev, "envy24_gethwptr(sc, %d)\n", dir);
1009#endif
1010	if (dir == PCMDIR_PLAY) {
1011		rtn = sc->psize / 4;
1012		unit = ENVY24_PLAY_BUFUNIT / 4;
1013		regno = ENVY24_MT_PCNT;
1014	}
1015	else {
1016		rtn = sc->rsize / 4;
1017		unit = ENVY24_REC_BUFUNIT / 4;
1018		regno = ENVY24_MT_RCNT;
1019	}
1020
1021	ptr = envy24_rdmt(sc, regno, 2);
1022	rtn -= (ptr + 1);
1023	rtn /= unit;
1024
1025#if(0)
1026	device_printf(sc->dev, "envy24_gethwptr(): return %d\n", rtn);
1027#endif
1028	return rtn;
1029}
1030
1031static void
1032envy24_updintr(struct sc_info *sc, int dir)
1033{
1034	int regptr, regintr;
1035	u_int32_t mask, intr;
1036	u_int32_t ptr, size, cnt;
1037	u_int16_t blk;
1038
1039#if(0)
1040	device_printf(sc->dev, "envy24_updintr(sc, %d)\n", dir);
1041#endif
1042	if (dir == PCMDIR_PLAY) {
1043		blk = sc->blk[0];
1044		size = sc->psize / 4;
1045		regptr = ENVY24_MT_PCNT;
1046		regintr = ENVY24_MT_PTERM;
1047		mask = ~ENVY24_MT_INT_PMASK;
1048	}
1049	else {
1050		blk = sc->blk[1];
1051		size = sc->rsize / 4;
1052		regptr = ENVY24_MT_RCNT;
1053		regintr = ENVY24_MT_RTERM;
1054		mask = ~ENVY24_MT_INT_RMASK;
1055	}
1056
1057	ptr = size - envy24_rdmt(sc, regptr, 2) - 1;
1058	/*
1059	cnt = blk - ptr % blk - 1;
1060	if (cnt == 0)
1061		cnt = blk - 1;
1062	*/
1063	cnt = blk - 1;
1064#if(0)
1065	device_printf(sc->dev, "envy24_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt);
1066#endif
1067	envy24_wrmt(sc, regintr, cnt, 2);
1068	intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
1069#if(0)
1070	device_printf(sc->dev, "envy24_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
1071#endif
1072	envy24_wrmt(sc, ENVY24_MT_INT, intr & mask, 1);
1073#if(0)
1074	device_printf(sc->dev, "envy24_updintr():INT-->0x%02x\n",
1075		      envy24_rdmt(sc, ENVY24_MT_INT, 1));
1076#endif
1077
1078	return;
1079}
1080
1081#if 0
1082static void
1083envy24_maskintr(struct sc_info *sc, int dir)
1084{
1085	u_int32_t mask, intr;
1086
1087#if(0)
1088	device_printf(sc->dev, "envy24_maskintr(sc, %d)\n", dir);
1089#endif
1090	if (dir == PCMDIR_PLAY)
1091		mask = ENVY24_MT_INT_PMASK;
1092	else
1093		mask = ENVY24_MT_INT_RMASK;
1094	intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
1095	envy24_wrmt(sc, ENVY24_MT_INT, intr | mask, 1);
1096
1097	return;
1098}
1099#endif
1100
1101static int
1102envy24_checkintr(struct sc_info *sc, int dir)
1103{
1104	u_int32_t mask, stat, intr, rtn;
1105
1106#if(0)
1107	device_printf(sc->dev, "envy24_checkintr(sc, %d)\n", dir);
1108#endif
1109	intr = envy24_rdmt(sc, ENVY24_MT_INT, 1);
1110	if (dir == PCMDIR_PLAY) {
1111		if ((rtn = intr & ENVY24_MT_INT_PSTAT) != 0) {
1112			mask = ~ENVY24_MT_INT_RSTAT;
1113			stat = ENVY24_MT_INT_PSTAT | ENVY24_MT_INT_PMASK;
1114			envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
1115		}
1116	}
1117	else {
1118		if ((rtn = intr & ENVY24_MT_INT_RSTAT) != 0) {
1119			mask = ~ENVY24_MT_INT_PSTAT;
1120			stat = ENVY24_MT_INT_RSTAT | ENVY24_MT_INT_RMASK;
1121			envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1);
1122		}
1123	}
1124
1125	return rtn;
1126}
1127
1128static void
1129envy24_start(struct sc_info *sc, int dir)
1130{
1131	u_int32_t stat, sw;
1132
1133#if(0)
1134	device_printf(sc->dev, "envy24_start(sc, %d)\n", dir);
1135#endif
1136	if (dir == PCMDIR_PLAY)
1137		sw = ENVY24_MT_PCTL_PSTART;
1138	else
1139		sw = ENVY24_MT_PCTL_RSTART;
1140
1141	stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
1142	envy24_wrmt(sc, ENVY24_MT_PCTL, stat | sw, 1);
1143#if(0)
1144	DELAY(100);
1145	device_printf(sc->dev, "PADDR:0x%08x\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
1146	device_printf(sc->dev, "PCNT:%ld\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
1147#endif
1148
1149	return;
1150}
1151
1152static void
1153envy24_stop(struct sc_info *sc, int dir)
1154{
1155	u_int32_t stat, sw;
1156
1157#if(0)
1158	device_printf(sc->dev, "envy24_stop(sc, %d)\n", dir);
1159#endif
1160	if (dir == PCMDIR_PLAY)
1161		sw = ~ENVY24_MT_PCTL_PSTART;
1162	else
1163		sw = ~ENVY24_MT_PCTL_RSTART;
1164
1165	stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1);
1166	envy24_wrmt(sc, ENVY24_MT_PCTL, stat & sw, 1);
1167
1168	return;
1169}
1170
1171static int
1172envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev)
1173{
1174	u_int32_t reg, mask;
1175	u_int32_t left, right;
1176
1177#if(0)
1178	device_printf(sc->dev, "envy24_route(sc, %d, %d, %d, %d)\n",
1179	    dac, class, adc, rev);
1180#endif
1181	/* parameter pattern check */
1182	if (dac < 0 || ENVY24_ROUTE_DAC_SPDIF < dac)
1183		return -1;
1184	if (class == ENVY24_ROUTE_CLASS_MIX &&
1185	    (dac != ENVY24_ROUTE_DAC_1 && dac != ENVY24_ROUTE_DAC_SPDIF))
1186		return -1;
1187	if (rev) {
1188		left = ENVY24_ROUTE_RIGHT;
1189		right = ENVY24_ROUTE_LEFT;
1190	}
1191	else {
1192		left = ENVY24_ROUTE_LEFT;
1193		right = ENVY24_ROUTE_RIGHT;
1194	}
1195
1196	if (dac == ENVY24_ROUTE_DAC_SPDIF) {
1197		reg = class | class << 2 |
1198			((adc << 1 | left) | left << 3) << 8 |
1199			((adc << 1 | right) | right << 3) << 12;
1200#if(0)
1201		device_printf(sc->dev, "envy24_route(): MT_SPDOUT-->0x%04x\n", reg);
1202#endif
1203		envy24_wrmt(sc, ENVY24_MT_SPDOUT, reg, 2);
1204	}
1205	else {
1206		mask = ~(0x0303 << dac * 2);
1207		reg = envy24_rdmt(sc, ENVY24_MT_PSDOUT, 2);
1208		reg = (reg & mask) | ((class | class << 8) << dac * 2);
1209#if(0)
1210		device_printf(sc->dev, "envy24_route(): MT_PSDOUT-->0x%04x\n", reg);
1211#endif
1212		envy24_wrmt(sc, ENVY24_MT_PSDOUT, reg, 2);
1213		mask = ~(0xff << dac * 8);
1214		reg = envy24_rdmt(sc, ENVY24_MT_RECORD, 4);
1215		reg = (reg & mask) |
1216			(((adc << 1 | left) | left << 3) |
1217			 ((adc << 1 | right) | right << 3) << 4) << dac * 8;
1218#if(0)
1219		device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg);
1220#endif
1221		envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4);
1222	}
1223
1224	return 0;
1225}
1226
1227/* -------------------------------------------------------------------- */
1228
1229/* buffer copy routines */
1230static void
1231envy24_p32sl(struct sc_chinfo *ch)
1232{
1233	int length;
1234	sample32_t *dmabuf;
1235	u_int32_t *data;
1236	int src, dst, ssize, dsize, slot;
1237	int i;
1238
1239	length = sndbuf_getready(ch->buffer) / 8;
1240	dmabuf = ch->parent->pbuf;
1241	data = (u_int32_t *)ch->data;
1242	src = sndbuf_getreadyptr(ch->buffer) / 4;
1243	dst = src / 2 + ch->offset;
1244	ssize = ch->size / 4;
1245	dsize = ch->size / 8;
1246	slot = ch->num * 2;
1247
1248	for (i = 0; i < length; i++) {
1249		dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = data[src];
1250		dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = data[src + 1];
1251		dst++;
1252		dst %= dsize;
1253		src += 2;
1254		src %= ssize;
1255	}
1256
1257	return;
1258}
1259
1260static void
1261envy24_p16sl(struct sc_chinfo *ch)
1262{
1263	int length;
1264	sample32_t *dmabuf;
1265	u_int16_t *data;
1266	int src, dst, ssize, dsize, slot;
1267	int i;
1268
1269#if(0)
1270	device_printf(ch->parent->dev, "envy24_p16sl()\n");
1271#endif
1272	length = sndbuf_getready(ch->buffer) / 4;
1273	dmabuf = ch->parent->pbuf;
1274	data = (u_int16_t *)ch->data;
1275	src = sndbuf_getreadyptr(ch->buffer) / 2;
1276	dst = src / 2 + ch->offset;
1277	ssize = ch->size / 2;
1278	dsize = ch->size / 4;
1279	slot = ch->num * 2;
1280#if(0)
1281	device_printf(ch->parent->dev, "envy24_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
1282#endif
1283
1284	for (i = 0; i < length; i++) {
1285		dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16;
1286		dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16;
1287#if(0)
1288		if (i < 16) {
1289			printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot]);
1290			printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1]);
1291		}
1292#endif
1293		dst++;
1294		dst %= dsize;
1295		src += 2;
1296		src %= ssize;
1297	}
1298#if(0)
1299	printf("\n");
1300#endif
1301
1302	return;
1303}
1304
1305static void
1306envy24_p8u(struct sc_chinfo *ch)
1307{
1308	int length;
1309	sample32_t *dmabuf;
1310	u_int8_t *data;
1311	int src, dst, ssize, dsize, slot;
1312	int i;
1313
1314	length = sndbuf_getready(ch->buffer) / 2;
1315	dmabuf = ch->parent->pbuf;
1316	data = (u_int8_t *)ch->data;
1317	src = sndbuf_getreadyptr(ch->buffer);
1318	dst = src / 2 + ch->offset;
1319	ssize = ch->size;
1320	dsize = ch->size / 4;
1321	slot = ch->num * 2;
1322
1323	for (i = 0; i < length; i++) {
1324		dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24;
1325		dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
1326		dst++;
1327		dst %= dsize;
1328		src += 2;
1329		src %= ssize;
1330	}
1331
1332	return;
1333}
1334
1335static void
1336envy24_r32sl(struct sc_chinfo *ch)
1337{
1338	int length;
1339	sample32_t *dmabuf;
1340	u_int32_t *data;
1341	int src, dst, ssize, dsize, slot;
1342	int i;
1343
1344	length = sndbuf_getfree(ch->buffer) / 8;
1345	dmabuf = ch->parent->rbuf;
1346	data = (u_int32_t *)ch->data;
1347	dst = sndbuf_getfreeptr(ch->buffer) / 4;
1348	src = dst / 2 + ch->offset;
1349	dsize = ch->size / 4;
1350	ssize = ch->size / 8;
1351	slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
1352
1353	for (i = 0; i < length; i++) {
1354		data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer;
1355		data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer;
1356		dst += 2;
1357		dst %= dsize;
1358		src++;
1359		src %= ssize;
1360	}
1361
1362	return;
1363}
1364
1365static void
1366envy24_r16sl(struct sc_chinfo *ch)
1367{
1368	int length;
1369	sample32_t *dmabuf;
1370	u_int16_t *data;
1371	int src, dst, ssize, dsize, slot;
1372	int i;
1373
1374	length = sndbuf_getfree(ch->buffer) / 4;
1375	dmabuf = ch->parent->rbuf;
1376	data = (u_int16_t *)ch->data;
1377	dst = sndbuf_getfreeptr(ch->buffer) / 2;
1378	src = dst / 2 + ch->offset;
1379	dsize = ch->size / 2;
1380	ssize = ch->size / 8;
1381	slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2;
1382
1383	for (i = 0; i < length; i++) {
1384		data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer;
1385		data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer;
1386		dst += 2;
1387		dst %= dsize;
1388		src++;
1389		src %= ssize;
1390	}
1391
1392	return;
1393}
1394
1395/* -------------------------------------------------------------------- */
1396
1397/* channel interface */
1398static void *
1399envy24chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
1400{
1401	struct sc_info	*sc = (struct sc_info *)devinfo;
1402	struct sc_chinfo *ch;
1403	unsigned num;
1404
1405#if(0)
1406	device_printf(sc->dev, "envy24chan_init(obj, devinfo, b, c, %d)\n", dir);
1407#endif
1408	snd_mtxlock(sc->lock);
1409	if ((sc->chnum > ENVY24_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
1410	    (sc->chnum < ENVY24_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
1411		snd_mtxunlock(sc->lock);
1412		return NULL;
1413	}
1414	num = sc->chnum;
1415
1416	ch = &sc->chan[num];
1417	ch->size = 8 * ENVY24_SAMPLE_NUM;
1418	ch->data = malloc(ch->size, M_ENVY24, M_NOWAIT);
1419	if (ch->data == NULL) {
1420		ch->size = 0;
1421		ch = NULL;
1422	}
1423	else {
1424		ch->buffer = b;
1425		ch->channel = c;
1426		ch->parent = sc;
1427		ch->dir = dir;
1428		/* set channel map */
1429		ch->num = envy24_chanmap[num];
1430		snd_mtxunlock(sc->lock);
1431		sndbuf_setup(ch->buffer, ch->data, ch->size);
1432		snd_mtxlock(sc->lock);
1433		/* these 2 values are dummy */
1434		ch->unit = 4;
1435		ch->blk = 10240;
1436	}
1437	snd_mtxunlock(sc->lock);
1438
1439	return ch;
1440}
1441
1442static int
1443envy24chan_free(kobj_t obj, void *data)
1444{
1445	struct sc_chinfo *ch = data;
1446	struct sc_info *sc = ch->parent;
1447
1448#if(0)
1449	device_printf(sc->dev, "envy24chan_free()\n");
1450#endif
1451	snd_mtxlock(sc->lock);
1452	if (ch->data != NULL) {
1453		free(ch->data, M_ENVY24);
1454		ch->data = NULL;
1455	}
1456	snd_mtxunlock(sc->lock);
1457
1458	return 0;
1459}
1460
1461static int
1462envy24chan_setformat(kobj_t obj, void *data, u_int32_t format)
1463{
1464	struct sc_chinfo *ch = data;
1465	struct sc_info *sc = ch->parent;
1466	struct envy24_emldma *emltab;
1467	/* unsigned int bcnt, bsize; */
1468	int i;
1469
1470#if(0)
1471	device_printf(sc->dev, "envy24chan_setformat(obj, data, 0x%08x)\n", format);
1472#endif
1473	snd_mtxlock(sc->lock);
1474	/* check and get format related information */
1475	if (ch->dir == PCMDIR_PLAY)
1476		emltab = envy24_pemltab;
1477	else
1478		emltab = envy24_remltab;
1479	if (emltab == NULL) {
1480		snd_mtxunlock(sc->lock);
1481		return -1;
1482	}
1483	for (i = 0; emltab[i].format != 0; i++)
1484		if (emltab[i].format == format)
1485			break;
1486	if (emltab[i].format == 0) {
1487		snd_mtxunlock(sc->lock);
1488		return -1;
1489	}
1490
1491	/* set format information */
1492	ch->format = format;
1493	ch->emldma = emltab[i].emldma;
1494	if (ch->unit > emltab[i].unit)
1495		ch->blk *= ch->unit / emltab[i].unit;
1496	else
1497		ch->blk /= emltab[i].unit / ch->unit;
1498	ch->unit = emltab[i].unit;
1499
1500	/* set channel buffer information */
1501	ch->size = ch->unit * ENVY24_SAMPLE_NUM;
1502#if 0
1503	if (ch->dir == PCMDIR_PLAY)
1504		bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
1505	else
1506		bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
1507	bsize *= ch->unit;
1508	bcnt = ch->size / bsize;
1509	sndbuf_resize(ch->buffer, bcnt, bsize);
1510#endif
1511	snd_mtxunlock(sc->lock);
1512
1513#if(0)
1514	device_printf(sc->dev, "envy24chan_setformat(): return 0x%08x\n", 0);
1515#endif
1516	return 0;
1517}
1518
1519/*
1520  IMPLEMENT NOTICE: In this driver, setspeed function only do setting
1521  of speed information value. And real hardware speed setting is done
1522  at start triggered(see envy24chan_trigger()). So, at this function
1523  is called, any value that ENVY24 can use is able to set. But, at
1524  start triggerd, some other channel is running, and that channel's
1525  speed isn't same with, then trigger function will fail.
1526*/
1527static int
1528envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
1529{
1530	struct sc_chinfo *ch = data;
1531	u_int32_t val, prev;
1532	int i;
1533
1534#if(0)
1535	device_printf(ch->parent->dev, "envy24chan_setspeed(obj, data, %d)\n", speed);
1536#endif
1537	prev = 0x7fffffff;
1538	for (i = 0; (val = envy24_speed[i]) != 0; i++) {
1539		if (abs(val - speed) < abs(prev - speed))
1540			prev = val;
1541		else
1542			break;
1543	}
1544	ch->speed = prev;
1545
1546#if(0)
1547	device_printf(ch->parent->dev, "envy24chan_setspeed(): return %d\n", ch->speed);
1548#endif
1549	return ch->speed;
1550}
1551
1552static int
1553envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1554{
1555	struct sc_chinfo *ch = data;
1556	/* struct sc_info *sc = ch->parent; */
1557	u_int32_t size, prev;
1558        unsigned int bcnt, bsize;
1559
1560#if(0)
1561	device_printf(sc->dev, "envy24chan_setblocksize(obj, data, %d)\n", blocksize);
1562#endif
1563	prev = 0x7fffffff;
1564	/* snd_mtxlock(sc->lock); */
1565	for (size = ch->size / 2; size > 0; size /= 2) {
1566		if (abs(size - blocksize) < abs(prev - blocksize))
1567			prev = size;
1568		else
1569			break;
1570	}
1571
1572	ch->blk = prev / ch->unit;
1573	if (ch->dir == PCMDIR_PLAY)
1574		ch->blk *= ENVY24_PLAY_BUFUNIT / 4;
1575	else
1576		ch->blk *= ENVY24_REC_BUFUNIT / 4;
1577	/* set channel buffer information */
1578	/* ch->size = ch->unit * ENVY24_SAMPLE_NUM; */
1579        if (ch->dir == PCMDIR_PLAY)
1580                bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
1581        else
1582                bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
1583        bsize *= ch->unit;
1584        bcnt = ch->size / bsize;
1585        sndbuf_resize(ch->buffer, bcnt, bsize);
1586	/* snd_mtxunlock(sc->lock); */
1587
1588#if(0)
1589	device_printf(sc->dev, "envy24chan_setblocksize(): return %d\n", prev);
1590#endif
1591	return prev;
1592}
1593
1594/* semantic note: must start at beginning of buffer */
1595static int
1596envy24chan_trigger(kobj_t obj, void *data, int go)
1597{
1598	struct sc_chinfo *ch = data;
1599	struct sc_info *sc = ch->parent;
1600	u_int32_t ptr;
1601	int slot;
1602#if 0
1603	int i;
1604
1605	device_printf(sc->dev, "envy24chan_trigger(obj, data, %d)\n", go);
1606#endif
1607	snd_mtxlock(sc->lock);
1608	if (ch->dir == PCMDIR_PLAY)
1609		slot = 0;
1610	else
1611		slot = 1;
1612	switch (go) {
1613	case PCMTRIG_START:
1614#if(0)
1615		device_printf(sc->dev, "envy24chan_trigger(): start\n");
1616#endif
1617		/* check or set channel speed */
1618		if (sc->run[0] == 0 && sc->run[1] == 0) {
1619			sc->speed = envy24_setspeed(sc, ch->speed);
1620			sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed;
1621			sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed;
1622		}
1623		else if (ch->speed != 0 && ch->speed != sc->speed)
1624			return -1;
1625		if (ch->speed == 0)
1626			ch->channel->speed = sc->speed;
1627		/* start or enable channel */
1628		sc->run[slot]++;
1629		if (sc->run[slot] == 1) {
1630			/* first channel */
1631			ch->offset = 0;
1632			sc->blk[slot] = ch->blk;
1633		}
1634		else {
1635			ptr = envy24_gethwptr(sc, ch->dir);
1636			ch->offset = ((ptr / ch->blk + 1) * ch->blk %
1637			    (ch->size / 4)) * 4 / ch->unit;
1638			if (ch->blk < sc->blk[slot])
1639				sc->blk[slot] = ch->blk;
1640		}
1641		if (ch->dir == PCMDIR_PLAY) {
1642			ch->emldma(ch);
1643			envy24_setvolume(sc, ch->num);
1644		}
1645		envy24_updintr(sc, ch->dir);
1646		if (sc->run[slot] == 1)
1647			envy24_start(sc, ch->dir);
1648		ch->run = 1;
1649		break;
1650	case PCMTRIG_EMLDMAWR:
1651#if(0)
1652		device_printf(sc->dev, "envy24chan_trigger(): emldmawr\n");
1653#endif
1654		if (ch->run != 1)
1655			return -1;
1656		ch->emldma(ch);
1657		break;
1658	case PCMTRIG_EMLDMARD:
1659#if(0)
1660		device_printf(sc->dev, "envy24chan_trigger(): emldmard\n");
1661#endif
1662		if (ch->run != 1)
1663			return -1;
1664		ch->emldma(ch);
1665		break;
1666	case PCMTRIG_ABORT:
1667#if(0)
1668		device_printf(sc->dev, "envy24chan_trigger(): abort\n");
1669#endif
1670		ch->run = 0;
1671		sc->run[slot]--;
1672		if (ch->dir == PCMDIR_PLAY)
1673			envy24_mutevolume(sc, ch->num);
1674		if (sc->run[slot] == 0) {
1675			envy24_stop(sc, ch->dir);
1676			sc->intr[slot] = 0;
1677		}
1678#if 0
1679		else if (ch->blk == sc->blk[slot]) {
1680			sc->blk[slot] = ENVY24_SAMPLE_NUM / 2;
1681			for (i = 0; i < ENVY24_CHAN_NUM; i++) {
1682				if (sc->chan[i].dir == ch->dir &&
1683				    sc->chan[i].run == 1 &&
1684				    sc->chan[i].blk < sc->blk[slot])
1685					sc->blk[slot] = sc->chan[i].blk;
1686			}
1687			if (ch->blk != sc->blk[slot])
1688				envy24_updintr(sc, ch->dir);
1689		}
1690#endif
1691		break;
1692	}
1693	snd_mtxunlock(sc->lock);
1694
1695	return 0;
1696}
1697
1698static int
1699envy24chan_getptr(kobj_t obj, void *data)
1700{
1701	struct sc_chinfo *ch = data;
1702	struct sc_info *sc = ch->parent;
1703	u_int32_t ptr;
1704	int rtn;
1705
1706#if(0)
1707	device_printf(sc->dev, "envy24chan_getptr()\n");
1708#endif
1709	snd_mtxlock(sc->lock);
1710	ptr = envy24_gethwptr(sc, ch->dir);
1711	rtn = ptr * ch->unit;
1712	snd_mtxunlock(sc->lock);
1713
1714#if(0)
1715	device_printf(sc->dev, "envy24chan_getptr(): return %d\n",
1716	    rtn);
1717#endif
1718	return rtn;
1719}
1720
1721static struct pcmchan_caps *
1722envy24chan_getcaps(kobj_t obj, void *data)
1723{
1724	struct sc_chinfo *ch = data;
1725	struct sc_info *sc = ch->parent;
1726	struct pcmchan_caps *rtn;
1727
1728#if(0)
1729	device_printf(sc->dev, "envy24chan_getcaps()\n");
1730#endif
1731	snd_mtxlock(sc->lock);
1732	if (ch->dir == PCMDIR_PLAY) {
1733		if (sc->run[0] == 0)
1734			rtn = &envy24_playcaps;
1735		else
1736			rtn = &sc->caps[0];
1737	}
1738	else {
1739		if (sc->run[1] == 0)
1740			rtn = &envy24_reccaps;
1741		else
1742			rtn = &sc->caps[1];
1743	}
1744	snd_mtxunlock(sc->lock);
1745
1746	return rtn;
1747}
1748
1749static kobj_method_t envy24chan_methods[] = {
1750	KOBJMETHOD(channel_init,		envy24chan_init),
1751	KOBJMETHOD(channel_free,		envy24chan_free),
1752	KOBJMETHOD(channel_setformat,		envy24chan_setformat),
1753	KOBJMETHOD(channel_setspeed,		envy24chan_setspeed),
1754	KOBJMETHOD(channel_setblocksize,	envy24chan_setblocksize),
1755	KOBJMETHOD(channel_trigger,		envy24chan_trigger),
1756	KOBJMETHOD(channel_getptr,		envy24chan_getptr),
1757	KOBJMETHOD(channel_getcaps,		envy24chan_getcaps),
1758	{ 0, 0 }
1759};
1760CHANNEL_DECLARE(envy24chan);
1761
1762/* -------------------------------------------------------------------- */
1763
1764/* mixer interface */
1765
1766static int
1767envy24mixer_init(struct snd_mixer *m)
1768{
1769	struct sc_info *sc = mix_getdevinfo(m);
1770
1771#if(0)
1772	device_printf(sc->dev, "envy24mixer_init()\n");
1773#endif
1774	if (sc == NULL)
1775		return -1;
1776
1777	/* set volume control rate */
1778	snd_mtxlock(sc->lock);
1779	envy24_wrmt(sc, ENVY24_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */
1780
1781	mix_setdevs(m, ENVY24_MIX_MASK);
1782	mix_setrecdevs(m, ENVY24_MIX_REC_MASK);
1783	snd_mtxunlock(sc->lock);
1784
1785	return 0;
1786}
1787
1788static int
1789envy24mixer_reinit(struct snd_mixer *m)
1790{
1791	struct sc_info *sc = mix_getdevinfo(m);
1792
1793	if (sc == NULL)
1794		return -1;
1795#if(0)
1796	device_printf(sc->dev, "envy24mixer_reinit()\n");
1797#endif
1798
1799	return 0;
1800}
1801
1802static int
1803envy24mixer_uninit(struct snd_mixer *m)
1804{
1805	struct sc_info *sc = mix_getdevinfo(m);
1806
1807	if (sc == NULL)
1808		return -1;
1809#if(0)
1810	device_printf(sc->dev, "envy24mixer_uninit()\n");
1811#endif
1812
1813	return 0;
1814}
1815
1816static int
1817envy24mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1818{
1819	struct sc_info *sc = mix_getdevinfo(m);
1820	int ch = envy24_mixmap[dev];
1821	int hwch;
1822	int i;
1823
1824	if (sc == NULL)
1825		return -1;
1826	if (dev == 0 && sc->cfg->codec->setvolume == NULL)
1827		return -1;
1828	if (dev != 0 && ch == -1)
1829		return -1;
1830	hwch = envy24_chanmap[ch];
1831#if(0)
1832	device_printf(sc->dev, "envy24mixer_set(m, %d, %d, %d)\n",
1833	    dev, left, right);
1834#endif
1835
1836	snd_mtxlock(sc->lock);
1837	if (dev == 0) {
1838		for (i = 0; i < sc->dacn; i++) {
1839			sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
1840		}
1841	}
1842	else {
1843		/* set volume value for hardware */
1844		if ((sc->left[hwch] = 100 - left) > ENVY24_VOL_MIN)
1845			sc->left[hwch] = ENVY24_VOL_MUTE;
1846		if ((sc->right[hwch] = 100 - right) > ENVY24_VOL_MIN)
1847			sc->right[hwch] = ENVY24_VOL_MUTE;
1848
1849		/* set volume for record channel and running play channel */
1850		if (hwch > ENVY24_CHAN_PLAY_SPDIF || sc->chan[ch].run)
1851			envy24_setvolume(sc, hwch);
1852	}
1853	snd_mtxunlock(sc->lock);
1854
1855	return right << 8 | left;
1856}
1857
1858static u_int32_t
1859envy24mixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
1860{
1861	struct sc_info *sc = mix_getdevinfo(m);
1862	int ch = envy24_mixmap[src];
1863#if(0)
1864	device_printf(sc->dev, "envy24mixer_setrecsrc(m, %d)\n", src);
1865#endif
1866
1867	if (ch > ENVY24_CHAN_PLAY_SPDIF)
1868		sc->src = ch;
1869	return src;
1870}
1871
1872static kobj_method_t envy24mixer_methods[] = {
1873	KOBJMETHOD(mixer_init,		envy24mixer_init),
1874	KOBJMETHOD(mixer_reinit,	envy24mixer_reinit),
1875	KOBJMETHOD(mixer_uninit,	envy24mixer_uninit),
1876	KOBJMETHOD(mixer_set,		envy24mixer_set),
1877	KOBJMETHOD(mixer_setrecsrc,	envy24mixer_setrecsrc),
1878	{ 0, 0 }
1879};
1880MIXER_DECLARE(envy24mixer);
1881
1882/* -------------------------------------------------------------------- */
1883
1884/* The interrupt handler */
1885static void
1886envy24_intr(void *p)
1887{
1888	struct sc_info *sc = (struct sc_info *)p;
1889	struct sc_chinfo *ch;
1890	u_int32_t ptr, dsize, feed;
1891	int i;
1892
1893#if(0)
1894	device_printf(sc->dev, "envy24_intr()\n");
1895#endif
1896	snd_mtxlock(sc->lock);
1897	if (envy24_checkintr(sc, PCMDIR_PLAY)) {
1898#if(0)
1899		device_printf(sc->dev, "envy24_intr(): play\n");
1900#endif
1901		dsize = sc->psize / 4;
1902		ptr = dsize - envy24_rdmt(sc, ENVY24_MT_PCNT, 2) - 1;
1903#if(0)
1904		device_printf(sc->dev, "envy24_intr(): ptr = %d-->", ptr);
1905#endif
1906		ptr -= ptr % sc->blk[0];
1907		feed = (ptr + dsize - sc->intr[0]) % dsize;
1908#if(0)
1909		printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed);
1910#endif
1911		for (i = ENVY24_CHAN_PLAY_DAC1; i <= ENVY24_CHAN_PLAY_SPDIF; i++) {
1912			ch = &sc->chan[i];
1913#if(0)
1914			if (ch->run)
1915				device_printf(sc->dev, "envy24_intr(): chan[%d].blk = %d\n", i, ch->blk);
1916#endif
1917			if (ch->run && ch->blk <= feed) {
1918				snd_mtxunlock(sc->lock);
1919				chn_intr(ch->channel);
1920				snd_mtxlock(sc->lock);
1921			}
1922		}
1923		sc->intr[0] = ptr;
1924		envy24_updintr(sc, PCMDIR_PLAY);
1925	}
1926	if (envy24_checkintr(sc, PCMDIR_REC)) {
1927#if(0)
1928		device_printf(sc->dev, "envy24_intr(): rec\n");
1929#endif
1930		dsize = sc->rsize / 4;
1931		ptr = dsize - envy24_rdmt(sc, ENVY24_MT_RCNT, 2) - 1;
1932		ptr -= ptr % sc->blk[1];
1933		feed = (ptr + dsize - sc->intr[1]) % dsize;
1934		for (i = ENVY24_CHAN_REC_ADC1; i <= ENVY24_CHAN_REC_SPDIF; i++) {
1935			ch = &sc->chan[i];
1936			if (ch->run && ch->blk <= feed) {
1937				snd_mtxunlock(sc->lock);
1938				chn_intr(ch->channel);
1939				snd_mtxlock(sc->lock);
1940			}
1941		}
1942		sc->intr[1] = ptr;
1943		envy24_updintr(sc, PCMDIR_REC);
1944	}
1945	snd_mtxunlock(sc->lock);
1946
1947	return;
1948}
1949
1950/*
1951 * Probe and attach the card
1952 */
1953
1954static int
1955envy24_pci_probe(device_t dev)
1956{
1957	u_int16_t sv, sd;
1958	int i;
1959
1960#if(0)
1961	printf("envy24_pci_probe()\n");
1962#endif
1963	if (pci_get_device(dev) == PCID_ENVY24 &&
1964	    pci_get_vendor(dev) == PCIV_ENVY24) {
1965		sv = pci_get_subvendor(dev);
1966		sd = pci_get_subdevice(dev);
1967		for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
1968			if (cfg_table[i].subvendor == sv &&
1969			    cfg_table[i].subdevice == sd) {
1970				break;
1971			}
1972		}
1973		device_set_desc(dev, cfg_table[i].name);
1974#if(0)
1975		printf("envy24_pci_probe(): return 0\n");
1976#endif
1977		return 0;
1978	}
1979	else {
1980#if(0)
1981		printf("envy24_pci_probe(): return ENXIO\n");
1982#endif
1983		return ENXIO;
1984	}
1985}
1986
1987static void
1988envy24_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1989{
1990	/* struct sc_info *sc = (struct sc_info *)arg; */
1991
1992#if(0)
1993	device_printf(sc->dev, "envy24_dmapsetmap()\n");
1994	if (bootverbose) {
1995		printf("envy24(play): setmap %lx, %lx; ",
1996		    (unsigned long)segs->ds_addr,
1997		    (unsigned long)segs->ds_len);
1998		printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap));
1999	}
2000#endif
2001}
2002
2003static void
2004envy24_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2005{
2006	/* struct sc_info *sc = (struct sc_info *)arg; */
2007
2008#if(0)
2009	device_printf(sc->dev, "envy24_dmarsetmap()\n");
2010	if (bootverbose) {
2011		printf("envy24(record): setmap %lx, %lx; ",
2012		    (unsigned long)segs->ds_addr,
2013		    (unsigned long)segs->ds_len);
2014		printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap));
2015	}
2016#endif
2017}
2018
2019static void
2020envy24_dmafree(struct sc_info *sc)
2021{
2022#if(0)
2023	device_printf(sc->dev, "envy24_dmafree():");
2024	if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap);
2025	else printf(" sc->rmap(null)");
2026	if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap);
2027	else printf(" sc->pmap(null)");
2028	if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf);
2029	else printf(" sc->rbuf(null)");
2030	if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf);
2031	else printf(" sc->pbuf(null)\n");
2032#endif
2033#if(0)
2034	if (sc->rmap)
2035		bus_dmamap_unload(sc->dmat, sc->rmap);
2036	if (sc->pmap)
2037		bus_dmamap_unload(sc->dmat, sc->pmap);
2038	if (sc->rbuf)
2039		bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
2040	if (sc->pbuf)
2041		bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
2042#else
2043	bus_dmamap_unload(sc->dmat, sc->rmap);
2044	bus_dmamap_unload(sc->dmat, sc->pmap);
2045	bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
2046	bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
2047#endif
2048
2049	sc->rmap = sc->pmap = NULL;
2050	sc->pbuf = NULL;
2051	sc->rbuf = NULL;
2052
2053	return;
2054}
2055
2056static int
2057envy24_dmainit(struct sc_info *sc)
2058{
2059	u_int32_t addr;
2060
2061#if(0)
2062	device_printf(sc->dev, "envy24_dmainit()\n");
2063#endif
2064	/* init values */
2065	sc->psize = ENVY24_PLAY_BUFUNIT * ENVY24_SAMPLE_NUM;
2066	sc->rsize = ENVY24_REC_BUFUNIT * ENVY24_SAMPLE_NUM;
2067	sc->pbuf = NULL;
2068	sc->rbuf = NULL;
2069	sc->pmap = sc->rmap = NULL;
2070	sc->blk[0] = sc->blk[1] = 0;
2071
2072	/* allocate DMA buffer */
2073#if(0)
2074	device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->pbuf\n");
2075#endif
2076	if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap))
2077		goto bad;
2078#if(0)
2079	device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->rbuf\n");
2080#endif
2081	if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap))
2082		goto bad;
2083#if(0)
2084	device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->pmap\n");
2085#endif
2086	if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24_dmapsetmap, sc, 0))
2087		goto bad;
2088#if(0)
2089	device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->rmap\n");
2090#endif
2091	if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24_dmarsetmap, sc, 0))
2092		goto bad;
2093	bzero(sc->pbuf, sc->psize);
2094	bzero(sc->rbuf, sc->rsize);
2095
2096	/* set values to register */
2097	addr = vtophys(sc->pbuf);
2098#if(0)
2099	device_printf(sc->dev, "pbuf(0x%08x)\n", addr);
2100#endif
2101	envy24_wrmt(sc, ENVY24_MT_PADDR, addr, 4);
2102#if(0)
2103	device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4));
2104	device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1);
2105#endif
2106	envy24_wrmt(sc, ENVY24_MT_PCNT, sc->psize / 4 - 1, 2);
2107#if(0)
2108	device_printf(sc->dev, "PCNT-->(%ld)\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2));
2109#endif
2110	addr = vtophys(sc->rbuf);
2111	envy24_wrmt(sc, ENVY24_MT_RADDR, addr, 4);
2112	envy24_wrmt(sc, ENVY24_MT_RCNT, sc->rsize / 4 - 1, 2);
2113
2114	return 0;
2115 bad:
2116	envy24_dmafree(sc);
2117	return ENOSPC;
2118}
2119
2120static void
2121envy24_putcfg(struct sc_info *sc)
2122{
2123	device_printf(sc->dev, "system configuration\n");
2124	printf("  SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
2125	    sc->cfg->subvendor, sc->cfg->subdevice);
2126	printf("  XIN2 Clock Source: ");
2127	switch (sc->cfg->scfg & PCIM_SCFG_XIN2) {
2128	case 0x00:
2129		printf("22.5792MHz(44.1kHz*512)\n");
2130		break;
2131	case 0x40:
2132		printf("16.9344MHz(44.1kHz*384)\n");
2133		break;
2134	case 0x80:
2135		printf("from external clock synthesizer chip\n");
2136		break;
2137	default:
2138		printf("illeagal system setting\n");
2139	}
2140	printf("  MPU-401 UART(s) #: ");
2141	if (sc->cfg->scfg & PCIM_SCFG_MPU)
2142		printf("2\n");
2143	else
2144		printf("1\n");
2145	printf("  AC'97 codec: ");
2146	if (sc->cfg->scfg & PCIM_SCFG_AC97)
2147		printf("not exist\n");
2148	else
2149		printf("exist\n");
2150	printf("  ADC #: ");
2151	printf("%d\n", sc->adcn);
2152	printf("  DAC #: ");
2153	printf("%d\n", sc->dacn);
2154	printf("  Multi-track converter type: ");
2155	if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) {
2156		printf("AC'97(SDATA_OUT:");
2157		if (sc->cfg->acl & PCIM_ACL_OMODE)
2158			printf("packed");
2159		else
2160			printf("split");
2161		printf("|SDATA_IN:");
2162		if (sc->cfg->acl & PCIM_ACL_IMODE)
2163			printf("packed");
2164		else
2165			printf("split");
2166		printf(")\n");
2167	}
2168	else {
2169		printf("I2S(");
2170		if (sc->cfg->i2s & PCIM_I2S_VOL)
2171			printf("with volume, ");
2172		if (sc->cfg->i2s & PCIM_I2S_96KHZ)
2173			printf("96KHz support, ");
2174		switch (sc->cfg->i2s & PCIM_I2S_RES) {
2175		case PCIM_I2S_16BIT:
2176			printf("16bit resolution, ");
2177			break;
2178		case PCIM_I2S_18BIT:
2179			printf("18bit resolution, ");
2180			break;
2181		case PCIM_I2S_20BIT:
2182			printf("20bit resolution, ");
2183			break;
2184		case PCIM_I2S_24BIT:
2185			printf("24bit resolution, ");
2186			break;
2187		}
2188		printf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID);
2189	}
2190	printf("  S/PDIF(IN/OUT): ");
2191	if (sc->cfg->spdif & PCIM_SPDIF_IN)
2192		printf("1/");
2193	else
2194		printf("0/");
2195	if (sc->cfg->spdif & PCIM_SPDIF_OUT)
2196		printf("1 ");
2197	else
2198		printf("0 ");
2199	if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT))
2200		printf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2);
2201	printf("  GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n",
2202	    sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate);
2203}
2204
2205static int
2206envy24_init(struct sc_info *sc)
2207{
2208	u_int32_t data;
2209#if(0)
2210	int rtn;
2211#endif
2212	int i;
2213	u_int32_t sv, sd;
2214
2215
2216#if(0)
2217	device_printf(sc->dev, "envy24_init()\n");
2218#endif
2219
2220	/* reset chip */
2221	envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_RESET | ENVY24_CCS_CTL_NATIVE, 1);
2222	DELAY(200);
2223	envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_NATIVE, 1);
2224	DELAY(200);
2225
2226	/* legacy hardware disable */
2227	data = pci_read_config(sc->dev, PCIR_LAC, 2);
2228	data |= PCIM_LAC_DISABLE;
2229	pci_write_config(sc->dev, PCIR_LAC, data, 2);
2230
2231	/* check system configuration */
2232	sc->cfg = NULL;
2233	for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
2234		/* 1st: search configuration from table */
2235		sv = pci_get_subvendor(sc->dev);
2236		sd = pci_get_subdevice(sc->dev);
2237		if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) {
2238#if(0)
2239			device_printf(sc->dev, "Set configuration from table\n");
2240#endif
2241			sc->cfg = &cfg_table[i];
2242			break;
2243		}
2244	}
2245	if (sc->cfg == NULL) {
2246		/* 2nd: read configuration from table */
2247		sc->cfg = envy24_rom2cfg(sc);
2248	}
2249	sc->adcn = ((sc->cfg->scfg & PCIM_SCFG_ADC) >> 2) + 1;
2250	sc->dacn = (sc->cfg->scfg & PCIM_SCFG_DAC) + 1;
2251
2252	if (1 /* bootverbose */) {
2253		envy24_putcfg(sc);
2254	}
2255
2256	/* set system configuration */
2257	pci_write_config(sc->dev, PCIR_SCFG, sc->cfg->scfg, 1);
2258	pci_write_config(sc->dev, PCIR_ACL, sc->cfg->acl, 1);
2259	pci_write_config(sc->dev, PCIR_I2S, sc->cfg->i2s, 1);
2260	pci_write_config(sc->dev, PCIR_SPDIF, sc->cfg->spdif, 1);
2261	envy24_gpiosetmask(sc, sc->cfg->gpiomask);
2262	envy24_gpiosetdir(sc, sc->cfg->gpiodir);
2263	envy24_gpiowr(sc, sc->cfg->gpiostate);
2264	for (i = 0; i < sc->adcn; i++) {
2265		sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i);
2266		sc->cfg->codec->init(sc->adc[i]);
2267	}
2268	for (i = 0; i < sc->dacn; i++) {
2269		sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i);
2270		sc->cfg->codec->init(sc->dac[i]);
2271	}
2272
2273	/* initialize DMA buffer */
2274#if(0)
2275	device_printf(sc->dev, "envy24_init(): initialize DMA buffer\n");
2276#endif
2277	if (envy24_dmainit(sc))
2278		return ENOSPC;
2279
2280	/* initialize status */
2281	sc->run[0] = sc->run[1] = 0;
2282	sc->intr[0] = sc->intr[1] = 0;
2283	sc->speed = 0;
2284	sc->caps[0].fmtlist = envy24_playfmt;
2285	sc->caps[1].fmtlist = envy24_recfmt;
2286
2287	/* set channel router */
2288	envy24_route(sc, ENVY24_ROUTE_DAC_1, ENVY24_ROUTE_CLASS_MIX, 0, 0);
2289	envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_DMA, 0, 0);
2290	/* envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_MIX, 0, 0); */
2291
2292	/* set macro interrupt mask */
2293	data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
2294	envy24_wrcs(sc, ENVY24_CCS_IMASK, data & ~ENVY24_CCS_IMASK_PMT, 1);
2295	data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1);
2296#if(0)
2297	device_printf(sc->dev, "envy24_init(): CCS_IMASK-->0x%02x\n", data);
2298#endif
2299
2300	return 0;
2301}
2302
2303static int
2304envy24_alloc_resource(struct sc_info *sc)
2305{
2306	/* allocate I/O port resource */
2307	sc->csid = PCIR_CCS;
2308	sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2309	    &sc->csid, 0, ~0, 1, RF_ACTIVE);
2310	sc->ddmaid = PCIR_DDMA;
2311	sc->ddma = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2312	    &sc->ddmaid, 0, ~0, 1, RF_ACTIVE);
2313	sc->dsid = PCIR_DS;
2314	sc->ds = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2315	    &sc->dsid, 0, ~0, 1, RF_ACTIVE);
2316	sc->mtid = PCIR_MT;
2317	sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2318	    &sc->mtid, 0, ~0, 1, RF_ACTIVE);
2319	if (!sc->cs || !sc->ddma || !sc->ds || !sc->mt) {
2320		device_printf(sc->dev, "unable to map IO port space\n");
2321		return ENXIO;
2322	}
2323	sc->cst = rman_get_bustag(sc->cs);
2324	sc->csh = rman_get_bushandle(sc->cs);
2325	sc->ddmat = rman_get_bustag(sc->ddma);
2326	sc->ddmah = rman_get_bushandle(sc->ddma);
2327	sc->dst = rman_get_bustag(sc->ds);
2328	sc->dsh = rman_get_bushandle(sc->ds);
2329	sc->mtt = rman_get_bustag(sc->mt);
2330	sc->mth = rman_get_bushandle(sc->mt);
2331#if(0)
2332	device_printf(sc->dev,
2333	    "IO port register values\nCCS: 0x%lx\nDDMA: 0x%lx\nDS: 0x%lx\nMT: 0x%lx\n",
2334	    pci_read_config(sc->dev, PCIR_CCS, 4),
2335	    pci_read_config(sc->dev, PCIR_DDMA, 4),
2336	    pci_read_config(sc->dev, PCIR_DS, 4),
2337	    pci_read_config(sc->dev, PCIR_MT, 4));
2338#endif
2339
2340	/* allocate interupt resource */
2341	sc->irqid = 0;
2342	sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
2343				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
2344	if (!sc->irq ||
2345	    snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24_intr, sc, &sc->ih)) {
2346		device_printf(sc->dev, "unable to map interrupt\n");
2347		return ENXIO;
2348	}
2349
2350	/* allocate DMA resource */
2351	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/4, /*boundary*/0,
2352	    /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24,
2353	    /*highaddr*/BUS_SPACE_MAXADDR_ENVY24,
2354	    /*filter*/NULL, /*filterarg*/NULL,
2355	    /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24,
2356	    /*nsegments*/1, /*maxsegsz*/0x3ffff,
2357	    /*flags*/0, /*lockfunc*/busdma_lock_mutex,
2358	    /*lockarg*/&Giant, &sc->dmat) != 0) {
2359		device_printf(sc->dev, "unable to create dma tag\n");
2360		return ENXIO;
2361	}
2362
2363	return 0;
2364}
2365
2366static int
2367envy24_pci_attach(device_t dev)
2368{
2369	u_int32_t		data;
2370	struct sc_info 		*sc;
2371	char 			status[SND_STATUSLEN];
2372	char			name[ENVY24_NAMELEN];
2373	int			err = 0;
2374	int			i;
2375
2376#if(0)
2377	device_printf(dev, "envy24_pci_attach()\n");
2378#endif
2379	/* get sc_info data area */
2380	if ((sc = malloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) {
2381		device_printf(dev, "cannot allocate softc\n");
2382		return ENXIO;
2383	}
2384
2385	bzero(sc, sizeof(*sc));
2386	snprintf(name, ENVY24_NAMELEN, "%s:envy24", device_get_nameunit(dev));
2387	sc->lock = snd_mtxcreate(name, name);
2388	sc->dev = dev;
2389
2390	/* initialize PCI interface */
2391	data = pci_read_config(dev, PCIR_COMMAND, 2);
2392	data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
2393	pci_write_config(dev, PCIR_COMMAND, data, 2);
2394	data = pci_read_config(dev, PCIR_COMMAND, 2);
2395
2396	/* allocate resources */
2397	err = envy24_alloc_resource(sc);
2398	if (err) {
2399		device_printf(dev, "unable to allocate system resources\n");
2400		goto bad;
2401	}
2402
2403	/* initialize card */
2404	err = envy24_init(sc);
2405	if (err) {
2406		device_printf(dev, "unable to initialize the card\n");
2407		goto bad;
2408	}
2409
2410	/* set multi track mixer */
2411	mixer_init(dev, &envy24mixer_class, sc);
2412
2413	/* set channel information */
2414	err = pcm_register(dev, sc, 5, 2 + sc->adcn);
2415	if (err)
2416		goto bad;
2417	sc->chnum = 0;
2418	for (i = 0; i < 5; i++) {
2419		pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
2420		sc->chnum++;
2421	}
2422	for (i = 0; i < 2 + sc->adcn; i++) {
2423		pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc);
2424		sc->chnum++;
2425	}
2426
2427	/* set status iformation */
2428	snprintf(status, SND_STATUSLEN,
2429	    "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld",
2430	    rman_get_start(sc->cs),
2431	    rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
2432	    rman_get_start(sc->ddma),
2433	    rman_get_end(sc->ddma) - rman_get_start(sc->ddma) + 1,
2434	    rman_get_start(sc->ds),
2435	    rman_get_end(sc->ds) - rman_get_start(sc->ds) + 1,
2436	    rman_get_start(sc->mt),
2437	    rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
2438	    rman_get_start(sc->irq));
2439	pcm_setstatus(dev, status);
2440
2441	return 0;
2442
2443bad:
2444	if (sc->ih)
2445		bus_teardown_intr(dev, sc->irq, sc->ih);
2446	if (sc->irq)
2447		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
2448	envy24_dmafree(sc);
2449	if (sc->dmat)
2450		bus_dma_tag_destroy(sc->dmat);
2451	if (sc->cfg->codec->destroy != NULL) {
2452                for (i = 0; i < sc->adcn; i++)
2453                        sc->cfg->codec->destroy(sc->adc[i]);
2454                for (i = 0; i < sc->dacn; i++)
2455                        sc->cfg->codec->destroy(sc->dac[i]);
2456        }
2457	envy24_cfgfree(sc->cfg);
2458	if (sc->cs)
2459		bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
2460	if (sc->ddma)
2461		bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
2462	if (sc->ds)
2463		bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
2464	if (sc->mt)
2465		bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
2466	if (sc->lock)
2467		snd_mtxfree(sc->lock);
2468	free(sc, M_ENVY24);
2469	return err;
2470}
2471
2472static int
2473envy24_pci_detach(device_t dev)
2474{
2475	struct sc_info *sc;
2476	int r;
2477	int i;
2478
2479#if(0)
2480	device_printf(dev, "envy24_pci_detach()\n");
2481#endif
2482	sc = pcm_getdevinfo(dev);
2483	if (sc == NULL)
2484		return 0;
2485	r = pcm_unregister(dev);
2486	if (r)
2487		return r;
2488
2489	envy24_dmafree(sc);
2490	if (sc->cfg->codec->destroy != NULL) {
2491		for (i = 0; i < sc->adcn; i++)
2492			sc->cfg->codec->destroy(sc->adc[i]);
2493		for (i = 0; i < sc->dacn; i++)
2494			sc->cfg->codec->destroy(sc->dac[i]);
2495	}
2496	envy24_cfgfree(sc->cfg);
2497	bus_dma_tag_destroy(sc->dmat);
2498	bus_teardown_intr(dev, sc->irq, sc->ih);
2499	bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
2500	bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
2501	bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma);
2502	bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds);
2503	bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
2504	snd_mtxfree(sc->lock);
2505	free(sc, M_ENVY24);
2506	return 0;
2507}
2508
2509static device_method_t envy24_methods[] = {
2510	/* Device interface */
2511	DEVMETHOD(device_probe,		envy24_pci_probe),
2512	DEVMETHOD(device_attach,	envy24_pci_attach),
2513	DEVMETHOD(device_detach,	envy24_pci_detach),
2514	{ 0, 0 }
2515};
2516
2517static driver_t envy24_driver = {
2518	"pcm",
2519	envy24_methods,
2520#if __FreeBSD_version > 500000
2521	PCM_SOFTC_SIZE,
2522#else
2523	sizeof(struct snddev_info),
2524#endif
2525};
2526
2527DRIVER_MODULE(snd_envy24, pci, envy24_driver, pcm_devclass, 0, 0);
2528MODULE_DEPEND(snd_envy24, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
2529MODULE_DEPEND(snd_envy24, snd_spicds, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
2530MODULE_VERSION(snd_envy24, 1);
2531