1205859Sjoel/*-
2162886Snetchild * Copyright (c) 2006 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
3159687Snetchild * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
4159687Snetchild * All rights reserved.
5159687Snetchild *
6159687Snetchild * Redistribution and use in source and binary forms, with or without
7159687Snetchild * modification, are permitted provided that the following conditions
8159687Snetchild * are met:
9159687Snetchild * 1. Redistributions of source code must retain the above copyright
10159687Snetchild *    notice, this list of conditions and the following disclaimer.
11159687Snetchild * 2. Redistributions in binary form must reproduce the above copyright
12159687Snetchild *    notice, this list of conditions and the following disclaimer in the
13159687Snetchild *    documentation and/or other materials provided with the distribution.
14159687Snetchild *
15159687Snetchild * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16159687Snetchild * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17159687Snetchild * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18159687Snetchild * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19159687Snetchild * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20159687Snetchild * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21159687Snetchild * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22159687Snetchild * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
23159687Snetchild * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24162886Snetchild * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25159687Snetchild * SUCH DAMAGE.
26159687Snetchild *
27159687Snetchild */
28159687Snetchild
29170031Sjoel/*
30170031Sjoel * Konstantin Dimitrov's thanks list:
31170031Sjoel *
32170031Sjoel * A huge thanks goes to Spas Filipov for his friendship, support and his
33170031Sjoel * generous gift - an 'Audiotrak Prodigy HD2' audio card! I also want to
34170031Sjoel * thank Keiichi Iwasaki and his parents, because they helped Spas to get
35170031Sjoel * the card from Japan! Having hardware sample of Prodigy HD2 made adding
36170031Sjoel * support for that great card very easy and real fun and pleasure.
37170031Sjoel *
38170031Sjoel */
39170031Sjoel
40193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
41193640Sariff#include "opt_snd.h"
42193640Sariff#endif
43193640Sariff
44159687Snetchild#include <dev/sound/pcm/sound.h>
45159687Snetchild#include <dev/sound/pcm/ac97.h>
46162886Snetchild#include <dev/sound/pci/spicds.h>
47162886Snetchild#include <dev/sound/pci/envy24ht.h>
48159687Snetchild
49159689Snetchild#include <dev/pci/pcireg.h>
50159689Snetchild#include <dev/pci/pcivar.h>
51159687Snetchild
52159687Snetchild#include "mixer_if.h"
53159687Snetchild
54165306SariffSND_DECLARE_FILE("$FreeBSD$");
55165306Sariff
56227293Sedstatic MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio");
57159687Snetchild
58159687Snetchild/* -------------------------------------------------------------------- */
59159687Snetchild
60159687Snetchildstruct sc_info;
61159687Snetchild
62162886Snetchild#define ENVY24HT_PLAY_CHNUM 8
63162886Snetchild#define ENVY24HT_REC_CHNUM 2
64162886Snetchild#define ENVY24HT_PLAY_BUFUNIT (4 /* byte/sample */ * 8 /* channel */)
65162886Snetchild#define ENVY24HT_REC_BUFUNIT  (4 /* byte/sample */ * 2 /* channel */)
66162886Snetchild#define ENVY24HT_SAMPLE_NUM   4096
67159687Snetchild
68162886Snetchild#define ENVY24HT_TIMEOUT 1000
69159687Snetchild
70193640Sariff#define ENVY24HT_DEFAULT_FORMAT	SND_FORMAT(AFMT_S16_LE, 2, 0)
71159687Snetchild
72162886Snetchild#define ENVY24HT_NAMELEN 32
73159687Snetchild
74162886Snetchildstruct envy24ht_sample {
75159689Snetchild        volatile u_int32_t buffer;
76159689Snetchild};
77159687Snetchild
78162886Snetchildtypedef struct envy24ht_sample sample32_t;
79159689Snetchild
80159687Snetchild/* channel registers */
81159687Snetchildstruct sc_chinfo {
82159687Snetchild	struct snd_dbuf		*buffer;
83159687Snetchild	struct pcm_channel	*channel;
84159687Snetchild	struct sc_info		*parent;
85159687Snetchild	int			dir;
86159687Snetchild	unsigned		num; /* hw channel number */
87159687Snetchild
88159687Snetchild	/* channel information */
89159687Snetchild	u_int32_t		format;
90159687Snetchild	u_int32_t		speed;
91159687Snetchild	u_int32_t		blk; /* hw block size(dword) */
92159687Snetchild
93159687Snetchild	/* format conversion structure */
94159687Snetchild	u_int8_t		*data;
95159687Snetchild	unsigned int		size; /* data buffer size(byte) */
96159687Snetchild	int			unit; /* sample size(byte) */
97159687Snetchild	unsigned int		offset; /* samples number offset */
98159687Snetchild	void			(*emldma)(struct sc_chinfo *);
99159687Snetchild
100159687Snetchild	/* flags */
101159687Snetchild	int			run;
102159687Snetchild};
103159687Snetchild
104159687Snetchild/* codec interface entrys */
105159687Snetchildstruct codec_entry {
106159687Snetchild	void *(*create)(device_t dev, void *devinfo, int dir, int num);
107159687Snetchild	void (*destroy)(void *codec);
108159687Snetchild	void (*init)(void *codec);
109159687Snetchild	void (*reinit)(void *codec);
110159687Snetchild	void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right);
111159687Snetchild	void (*setrate)(void *codec, int which, int rate);
112159687Snetchild};
113159687Snetchild
114159687Snetchild/* system configuration information */
115159687Snetchildstruct cfg_info {
116159687Snetchild	char *name;
117159687Snetchild	u_int16_t subvendor, subdevice;
118159687Snetchild	u_int8_t scfg, acl, i2s, spdif;
119162886Snetchild	u_int32_t gpiomask, gpiostate, gpiodir;
120162886Snetchild	u_int32_t cdti, cclk, cs;
121162886Snetchild	u_int8_t cif, type, free;
122159687Snetchild	struct codec_entry *codec;
123159687Snetchild};
124159687Snetchild
125159687Snetchild/* device private data */
126159687Snetchildstruct sc_info {
127159687Snetchild	device_t	dev;
128166713Sariff	struct mtx	*lock;
129159687Snetchild
130159687Snetchild	/* Control/Status registor */
131159687Snetchild	struct resource *cs;
132159687Snetchild	int		csid;
133159687Snetchild	bus_space_tag_t cst;
134159687Snetchild	bus_space_handle_t csh;
135159687Snetchild	/* MultiTrack registor */
136159687Snetchild	struct resource *mt;
137159687Snetchild	int		mtid;
138159687Snetchild	bus_space_tag_t mtt;
139159687Snetchild	bus_space_handle_t mth;
140159687Snetchild	/* DMA tag */
141159687Snetchild	bus_dma_tag_t dmat;
142159687Snetchild	/* IRQ resource */
143159687Snetchild	struct resource *irq;
144159687Snetchild	int		irqid;
145159687Snetchild	void		*ih;
146159687Snetchild
147159687Snetchild	/* system configuration data */
148159687Snetchild	struct cfg_info *cfg;
149159687Snetchild
150159687Snetchild	/* ADC/DAC number and info */
151159687Snetchild	int		adcn, dacn;
152159687Snetchild	void		*adc[4], *dac[4];
153159687Snetchild
154159687Snetchild	/* mixer control data */
155159687Snetchild	u_int32_t	src;
156162886Snetchild	u_int8_t	left[ENVY24HT_CHAN_NUM];
157162886Snetchild	u_int8_t	right[ENVY24HT_CHAN_NUM];
158159687Snetchild
159159687Snetchild	/* Play/Record DMA fifo */
160159687Snetchild	sample32_t	*pbuf;
161159687Snetchild	sample32_t	*rbuf;
162159687Snetchild	u_int32_t	psize, rsize; /* DMA buffer size(byte) */
163159687Snetchild	u_int16_t	blk[2]; /* transfer check blocksize(dword) */
164159687Snetchild	bus_dmamap_t	pmap, rmap;
165159687Snetchild
166159687Snetchild	/* current status */
167159687Snetchild	u_int32_t	speed;
168159687Snetchild	int		run[2];
169159687Snetchild	u_int16_t	intr[2];
170159687Snetchild	struct pcmchan_caps	caps[2];
171159687Snetchild
172159687Snetchild	/* channel info table */
173159687Snetchild	unsigned	chnum;
174159687Snetchild	struct sc_chinfo chan[11];
175159687Snetchild};
176159687Snetchild
177159687Snetchild/* -------------------------------------------------------------------- */
178159687Snetchild
179159687Snetchild/*
180159687Snetchild * prototypes
181159687Snetchild */
182159687Snetchild
183159687Snetchild/* DMA emulator */
184162886Snetchildstatic void envy24ht_p8u(struct sc_chinfo *);
185162886Snetchildstatic void envy24ht_p16sl(struct sc_chinfo *);
186162886Snetchildstatic void envy24ht_p32sl(struct sc_chinfo *);
187162886Snetchildstatic void envy24ht_r16sl(struct sc_chinfo *);
188162886Snetchildstatic void envy24ht_r32sl(struct sc_chinfo *);
189159687Snetchild
190159687Snetchild/* channel interface */
191162886Snetchildstatic void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
192162886Snetchildstatic int envy24htchan_setformat(kobj_t, void *, u_int32_t);
193193640Sariffstatic u_int32_t envy24htchan_setspeed(kobj_t, void *, u_int32_t);
194193640Sariffstatic u_int32_t envy24htchan_setblocksize(kobj_t, void *, u_int32_t);
195162886Snetchildstatic int envy24htchan_trigger(kobj_t, void *, int);
196193640Sariffstatic u_int32_t envy24htchan_getptr(kobj_t, void *);
197162886Snetchildstatic struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *);
198159687Snetchild
199159687Snetchild/* mixer interface */
200162886Snetchildstatic int envy24htmixer_init(struct snd_mixer *);
201162886Snetchildstatic int envy24htmixer_reinit(struct snd_mixer *);
202162886Snetchildstatic int envy24htmixer_uninit(struct snd_mixer *);
203162886Snetchildstatic int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned);
204162886Snetchildstatic u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t);
205159687Snetchild
206162886Snetchild/* SPI codec access interface */
207162886Snetchildstatic void *envy24ht_spi_create(device_t, void *, int, int);
208162886Snetchildstatic void envy24ht_spi_destroy(void *);
209162886Snetchildstatic void envy24ht_spi_init(void *);
210162886Snetchildstatic void envy24ht_spi_reinit(void *);
211162886Snetchildstatic void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int);
212159687Snetchild
213159687Snetchild/* -------------------------------------------------------------------- */
214159687Snetchild
215159687Snetchild/*
216159687Snetchild  system constant tables
217159687Snetchild*/
218159687Snetchild
219159687Snetchild/* API -> hardware channel map */
220162886Snetchildstatic unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = {
221162886Snetchild	ENVY24HT_CHAN_PLAY_DAC1,  /* 1 */
222162886Snetchild	ENVY24HT_CHAN_PLAY_DAC2,  /* 2 */
223162886Snetchild	ENVY24HT_CHAN_PLAY_DAC3,  /* 3 */
224162886Snetchild	ENVY24HT_CHAN_PLAY_DAC4,  /* 4 */
225162886Snetchild	ENVY24HT_CHAN_PLAY_SPDIF, /* 0 */
226162886Snetchild	ENVY24HT_CHAN_REC_MIX,    /* 5 */
227162886Snetchild	ENVY24HT_CHAN_REC_SPDIF,  /* 6 */
228162886Snetchild	ENVY24HT_CHAN_REC_ADC1,   /* 7 */
229162886Snetchild	ENVY24HT_CHAN_REC_ADC2,   /* 8 */
230162886Snetchild	ENVY24HT_CHAN_REC_ADC3,   /* 9 */
231162886Snetchild	ENVY24HT_CHAN_REC_ADC4,   /* 10 */
232159687Snetchild};
233159687Snetchild
234159687Snetchild/* mixer -> API channel map. see above */
235162886Snetchildstatic int envy24ht_mixmap[] = {
236159687Snetchild	-1, /* Master output level. It is depend on codec support */
237159687Snetchild	-1, /* Treble level of all output channels */
238159687Snetchild	-1, /* Bass level of all output channels */
239159687Snetchild	-1, /* Volume of synthesier input */
240159687Snetchild	0,  /* Output level for the audio device */
241159687Snetchild	-1, /* Output level for the PC speaker */
242159687Snetchild	7,  /* line in jack */
243159687Snetchild	-1, /* microphone jack */
244159687Snetchild	-1, /* CD audio input */
245159687Snetchild	-1, /* Recording monitor */
246159687Snetchild	1,  /* alternative codec */
247159687Snetchild	-1, /* global recording level */
248159687Snetchild	-1, /* Input gain */
249159687Snetchild	-1, /* Output gain */
250159687Snetchild	8,  /* Input source 1 */
251159687Snetchild	9,  /* Input source 2 */
252159687Snetchild	10, /* Input source 3 */
253159687Snetchild	6,  /* Digital (input) 1 */
254159687Snetchild	-1, /* Digital (input) 2 */
255159687Snetchild	-1, /* Digital (input) 3 */
256159687Snetchild	-1, /* Phone input */
257159687Snetchild	-1, /* Phone output */
258159687Snetchild	-1, /* Video/TV (audio) in */
259159687Snetchild	-1, /* Radio in */
260159687Snetchild	-1, /* Monitor volume */
261159687Snetchild};
262159687Snetchild
263159687Snetchild/* variable rate audio */
264162886Snetchildstatic u_int32_t envy24ht_speed[] = {
265170031Sjoel    192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
266159687Snetchild    12000, 11025, 9600, 8000, 0
267159687Snetchild};
268159687Snetchild
269159687Snetchild/* known boards configuration */
270162886Snetchildstatic struct codec_entry spi_codec = {
271162886Snetchild	envy24ht_spi_create,
272162886Snetchild	envy24ht_spi_destroy,
273162886Snetchild	envy24ht_spi_init,
274162886Snetchild	envy24ht_spi_reinit,
275162886Snetchild	envy24ht_spi_setvolume,
276159687Snetchild	NULL, /* setrate */
277159687Snetchild};
278159687Snetchild
279159687Snetchildstatic struct cfg_info cfg_table[] = {
280159687Snetchild	{
281162886Snetchild		"Envy24HT audio (Terratec Aureon 7.1 Space)",
282162886Snetchild		0x153b, 0x1145,
283162886Snetchild		0x0b, 0x80, 0xfc, 0xc3,
284162886Snetchild		0x21efff, 0x7fffff, 0x5e1000,
285162886Snetchild		0x40000, 0x80000, 0x1000, 0x00, 0x02,
286162886Snetchild		0,
287162886Snetchild		&spi_codec,
288159687Snetchild	},
289162886Snetchild        {
290162886Snetchild                "Envy24HT audio (Terratec Aureon 5.1 Sky)",
291162886Snetchild                0x153b, 0x1147,
292162886Snetchild                0x0a, 0x80, 0xfc, 0xc3,
293162886Snetchild                0x21efff, 0x7fffff, 0x5e1000,
294162886Snetchild                0x40000, 0x80000, 0x1000, 0x00, 0x02,
295162886Snetchild                0,
296162886Snetchild                &spi_codec,
297162886Snetchild        },
298162886Snetchild	        {
299162886Snetchild                "Envy24HT audio (Terratec Aureon 7.1 Universe)",
300162886Snetchild                0x153b, 0x1153,
301162886Snetchild                0x0b, 0x80, 0xfc, 0xc3,
302162886Snetchild                0x21efff, 0x7fffff, 0x5e1000,
303162886Snetchild                0x40000, 0x80000, 0x1000, 0x00, 0x02,
304162886Snetchild                0,
305162886Snetchild                &spi_codec,
306162886Snetchild        },
307162886Snetchild        {
308162886Snetchild                "Envy24HT audio (AudioTrak Prodigy 7.1)",
309162886Snetchild                0x4933, 0x4553,
310162886Snetchild                0x0b, 0x80, 0xfc, 0xc3,
311162886Snetchild                0x21efff, 0x7fffff, 0x5e1000,
312162886Snetchild                0x40000, 0x80000, 0x1000, 0x00, 0x02,
313162886Snetchild                0,
314162886Snetchild                &spi_codec,
315162886Snetchild        },
316162886Snetchild        {
317162886Snetchild                "Envy24HT audio (Terratec PHASE 28)",
318162886Snetchild                0x153b, 0x1149,
319162886Snetchild                0x0b, 0x80, 0xfc, 0xc3,
320162886Snetchild                0x21efff, 0x7fffff, 0x5e1000,
321162886Snetchild                0x40000, 0x80000, 0x1000, 0x00, 0x02,
322162886Snetchild                0,
323162886Snetchild                &spi_codec,
324162886Snetchild        },
325162886Snetchild        {
326165306Sariff                "Envy24HT-S audio (Terratec PHASE 22)",
327162886Snetchild                0x153b, 0x1150,
328162886Snetchild                0x10, 0x80, 0xf0, 0xc3,
329162886Snetchild                0x7ffbc7, 0x7fffff, 0x438,
330188480Snetchild                0x10, 0x20, 0x400, 0x01, 0x00,
331162886Snetchild                0,
332162886Snetchild                &spi_codec,
333162886Snetchild        },
334162886Snetchild        {
335162886Snetchild                "Envy24HT audio (AudioTrak Prodigy 7.1 LT)",
336162886Snetchild                0x3132, 0x4154,
337170031Sjoel                0x4b, 0x80, 0xfc, 0xc3,
338162886Snetchild                0x7ff8ff, 0x7fffff, 0x700,
339162886Snetchild                0x400, 0x200, 0x100, 0x00, 0x02,
340162886Snetchild                0,
341162886Snetchild                &spi_codec,
342162886Snetchild        },
343162886Snetchild        {
344170031Sjoel                "Envy24HT audio (AudioTrak Prodigy 7.1 XT)",
345170031Sjoel                0x3136, 0x4154,
346170031Sjoel                0x4b, 0x80, 0xfc, 0xc3,
347170031Sjoel                0x7ff8ff, 0x7fffff, 0x700,
348170031Sjoel                0x400, 0x200, 0x100, 0x00, 0x02,
349170031Sjoel                0,
350170031Sjoel                &spi_codec,
351170031Sjoel        },
352170031Sjoel        {
353162886Snetchild                "Envy24HT audio (M-Audio Revolution 7.1)",
354162886Snetchild                0x1412, 0x3630,
355162886Snetchild                0x43, 0x80, 0xf8, 0xc1,
356188480Snetchild                0x3fff85, 0x400072, 0x4000fa,
357162886Snetchild                0x08, 0x02, 0x20, 0x00, 0x04,
358162886Snetchild                0,
359162886Snetchild                &spi_codec,
360162886Snetchild        },
361162886Snetchild        {
362165306Sariff                "Envy24GT audio (M-Audio Revolution 5.1)",
363162886Snetchild                0x1412, 0x3631,
364162886Snetchild                0x42, 0x80, 0xf8, 0xc1,
365188480Snetchild                0x3fff05, 0x4000f0, 0x4000fa,
366170031Sjoel                0x08, 0x02, 0x10, 0x00, 0x03,
367162886Snetchild                0,
368162886Snetchild                &spi_codec,
369162886Snetchild        },
370162886Snetchild        {
371162886Snetchild                "Envy24HT audio (M-Audio Audiophile 192)",
372162886Snetchild                0x1412, 0x3632,
373162886Snetchild                0x68, 0x80, 0xf8, 0xc3,
374162886Snetchild                0x45, 0x4000b5, 0x7fffba,
375162886Snetchild                0x08, 0x02, 0x10, 0x00, 0x03,
376162886Snetchild                0,
377162886Snetchild                &spi_codec,
378162886Snetchild        },
379170031Sjoel        {
380170031Sjoel                "Envy24HT audio (AudioTrak Prodigy HD2)",
381170031Sjoel                0x3137, 0x4154,
382170031Sjoel                0x68, 0x80, 0x78, 0xc3,
383170031Sjoel                0xfff8ff, 0x200700, 0xdfffff,
384170031Sjoel                0x400, 0x200, 0x100, 0x00, 0x05,
385170031Sjoel                0,
386170031Sjoel                &spi_codec,
387170031Sjoel        },
388170031Sjoel        {
389170031Sjoel                "Envy24HT audio (ESI Juli@)",
390170031Sjoel                0x3031, 0x4553,
391170031Sjoel                0x20, 0x80, 0xf8, 0xc3,
392170031Sjoel                0x7fff9f, 0x8016, 0x7fff9f,
393170031Sjoel                0x08, 0x02, 0x10, 0x00, 0x03,
394170031Sjoel                0,
395170031Sjoel                &spi_codec,
396170031Sjoel        },
397159687Snetchild	{
398188480Snetchild                "Envy24HT-S audio (Terrasoniq TS22PCI)",
399188480Snetchild                0x153b, 0x117b,
400188480Snetchild                0x10, 0x80, 0xf0, 0xc3,
401188480Snetchild                0x7ffbc7, 0x7fffff, 0x438,
402188480Snetchild                0x10, 0x20, 0x400, 0x01, 0x00,
403188480Snetchild                0,
404188480Snetchild                &spi_codec,
405188480Snetchild	},
406188480Snetchild	{
407162886Snetchild		"Envy24HT audio (Generic)",
408159687Snetchild		0, 0,
409162886Snetchild		0x0b, 0x80, 0xfc, 0xc3,
410162886Snetchild		0x21efff, 0x7fffff, 0x5e1000,
411162886Snetchild                0x40000, 0x80000, 0x1000, 0x00, 0x02,
412162886Snetchild		0,
413162886Snetchild		&spi_codec, /* default codec routines */
414159687Snetchild	}
415159687Snetchild};
416159687Snetchild
417162886Snetchildstatic u_int32_t envy24ht_recfmt[] = {
418193640Sariff	SND_FORMAT(AFMT_S16_LE, 2, 0),
419193640Sariff	SND_FORMAT(AFMT_S32_LE, 2, 0),
420159687Snetchild	0
421159687Snetchild};
422162886Snetchildstatic struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0};
423159687Snetchild
424162886Snetchildstatic u_int32_t envy24ht_playfmt[] = {
425193640Sariff	SND_FORMAT(AFMT_U8, 2, 0),
426193640Sariff	SND_FORMAT(AFMT_S16_LE, 2, 0),
427193640Sariff	SND_FORMAT(AFMT_S32_LE, 2, 0),
428159687Snetchild	0
429159687Snetchild};
430159687Snetchild
431170031Sjoelstatic struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0};
432159687Snetchild
433162886Snetchildstruct envy24ht_emldma {
434159687Snetchild	u_int32_t	format;
435159687Snetchild	void		(*emldma)(struct sc_chinfo *);
436159687Snetchild	int		unit;
437159687Snetchild};
438159687Snetchild
439162886Snetchildstatic struct envy24ht_emldma envy24ht_pemltab[] = {
440193640Sariff	{SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2},
441193640Sariff	{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4},
442193640Sariff	{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8},
443159687Snetchild	{0, NULL, 0}
444159687Snetchild};
445159687Snetchild
446162886Snetchildstatic struct envy24ht_emldma envy24ht_remltab[] = {
447193640Sariff	{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4},
448193640Sariff	{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8},
449159687Snetchild	{0, NULL, 0}
450159687Snetchild};
451159687Snetchild
452159687Snetchild/* -------------------------------------------------------------------- */
453159687Snetchild
454159687Snetchild/* common routines */
455159687Snetchildstatic u_int32_t
456162886Snetchildenvy24ht_rdcs(struct sc_info *sc, int regno, int size)
457159687Snetchild{
458159687Snetchild	switch (size) {
459159687Snetchild	case 1:
460159687Snetchild		return bus_space_read_1(sc->cst, sc->csh, regno);
461159687Snetchild	case 2:
462159687Snetchild		return bus_space_read_2(sc->cst, sc->csh, regno);
463159687Snetchild	case 4:
464159687Snetchild		return bus_space_read_4(sc->cst, sc->csh, regno);
465159687Snetchild	default:
466159687Snetchild		return 0xffffffff;
467159687Snetchild	}
468159687Snetchild}
469159687Snetchild
470159687Snetchildstatic void
471162886Snetchildenvy24ht_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size)
472159687Snetchild{
473159687Snetchild	switch (size) {
474159687Snetchild	case 1:
475159687Snetchild		bus_space_write_1(sc->cst, sc->csh, regno, data);
476159687Snetchild		break;
477159687Snetchild	case 2:
478159687Snetchild		bus_space_write_2(sc->cst, sc->csh, regno, data);
479159687Snetchild		break;
480159687Snetchild	case 4:
481159687Snetchild		bus_space_write_4(sc->cst, sc->csh, regno, data);
482159687Snetchild		break;
483159687Snetchild	}
484159687Snetchild}
485159687Snetchild
486159687Snetchildstatic u_int32_t
487162886Snetchildenvy24ht_rdmt(struct sc_info *sc, int regno, int size)
488159687Snetchild{
489159687Snetchild	switch (size) {
490159687Snetchild	case 1:
491159687Snetchild		return bus_space_read_1(sc->mtt, sc->mth, regno);
492159687Snetchild	case 2:
493159687Snetchild		return bus_space_read_2(sc->mtt, sc->mth, regno);
494159687Snetchild	case 4:
495159687Snetchild		return bus_space_read_4(sc->mtt, sc->mth, regno);
496159687Snetchild	default:
497159687Snetchild		return 0xffffffff;
498159687Snetchild	}
499159687Snetchild}
500159687Snetchild
501159687Snetchildstatic void
502162886Snetchildenvy24ht_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size)
503159687Snetchild{
504159687Snetchild	switch (size) {
505159687Snetchild	case 1:
506159687Snetchild		bus_space_write_1(sc->mtt, sc->mth, regno, data);
507159687Snetchild		break;
508159687Snetchild	case 2:
509159687Snetchild		bus_space_write_2(sc->mtt, sc->mth, regno, data);
510159687Snetchild		break;
511159687Snetchild	case 4:
512159687Snetchild		bus_space_write_4(sc->mtt, sc->mth, regno, data);
513159687Snetchild		break;
514159687Snetchild	}
515159687Snetchild}
516159687Snetchild
517159687Snetchild/* -------------------------------------------------------------------- */
518159687Snetchild
519159687Snetchild/* I2C port/E2PROM access routines */
520159687Snetchild
521159687Snetchildstatic int
522162886Snetchildenvy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr)
523159687Snetchild{
524159687Snetchild	u_int32_t data;
525159687Snetchild	int i;
526159687Snetchild
527159687Snetchild#if(0)
528162886Snetchild	device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
529159687Snetchild#endif
530162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
531162886Snetchild		data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
532162886Snetchild		if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
533159687Snetchild			break;
534159687Snetchild		DELAY(32); /* 31.25kHz */
535159687Snetchild	}
536162886Snetchild	if (i == ENVY24HT_TIMEOUT) {
537159687Snetchild		return -1;
538159687Snetchild	}
539162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
540162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
541162886Snetchild	    (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1);
542162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
543162886Snetchild		data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
544162886Snetchild		if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
545159687Snetchild			break;
546159687Snetchild		DELAY(32); /* 31.25kHz */
547159687Snetchild	}
548162886Snetchild	if (i == ENVY24HT_TIMEOUT) {
549159687Snetchild		return -1;
550159687Snetchild	}
551162886Snetchild	data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1);
552159687Snetchild
553159687Snetchild#if(0)
554162886Snetchild	device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data);
555159687Snetchild#endif
556159687Snetchild	return (int)data;
557159687Snetchild}
558159687Snetchild
559159687Snetchildstatic int
560162886Snetchildenvy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data)
561159687Snetchild{
562159687Snetchild	u_int32_t tmp;
563159687Snetchild	int i;
564159687Snetchild
565159687Snetchild#if(0)
566162886Snetchild	device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr);
567159687Snetchild#endif
568162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
569162886Snetchild		tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
570162886Snetchild		if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
571159687Snetchild			break;
572159687Snetchild		DELAY(32); /* 31.25kHz */
573159687Snetchild	}
574162886Snetchild	if (i == ENVY24HT_TIMEOUT) {
575159687Snetchild		return -1;
576159687Snetchild	}
577162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1);
578162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1);
579162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV,
580162886Snetchild	    (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1);
581162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
582162886Snetchild		data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
583162886Snetchild		if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0)
584159687Snetchild			break;
585159687Snetchild		DELAY(32); /* 31.25kHz */
586159687Snetchild	}
587162886Snetchild	if (i == ENVY24HT_TIMEOUT) {
588159687Snetchild		return -1;
589159687Snetchild	}
590159687Snetchild
591159687Snetchild	return 0;
592159687Snetchild}
593159687Snetchild
594159687Snetchildstatic int
595162886Snetchildenvy24ht_rdrom(struct sc_info *sc, u_int32_t addr)
596159687Snetchild{
597159687Snetchild	u_int32_t data;
598159687Snetchild
599159687Snetchild#if(0)
600162886Snetchild	device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr);
601159687Snetchild#endif
602162886Snetchild	data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1);
603162886Snetchild	if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) {
604159687Snetchild#if(0)
605162886Snetchild		device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n");
606159687Snetchild#endif
607159687Snetchild		return -1;
608159687Snetchild	}
609159687Snetchild
610162886Snetchild	return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr);
611159687Snetchild}
612159687Snetchild
613159687Snetchildstatic struct cfg_info *
614162886Snetchildenvy24ht_rom2cfg(struct sc_info *sc)
615159687Snetchild{
616159687Snetchild	struct cfg_info *buff;
617159687Snetchild	int size;
618159687Snetchild	int i;
619159687Snetchild
620159687Snetchild#if(0)
621162886Snetchild	device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n");
622159687Snetchild#endif
623162886Snetchild	size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE);
624170031Sjoel	if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) {
625159687Snetchild#if(0)
626162886Snetchild		device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size);
627159687Snetchild#endif
628162886Snetchild        buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
629162886Snetchild        if (buff == NULL) {
630162886Snetchild#if(0)
631162886Snetchild                device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n");
632162886Snetchild#endif
633162886Snetchild                return NULL;
634162886Snetchild        }
635162886Snetchild        buff->free = 1;
636162886Snetchild
637162886Snetchild	/* no valid e2prom, using default values */
638162886Snetchild        buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
639162886Snetchild        buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
640162886Snetchild        buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
641162886Snetchild        buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
642162886Snetchild        buff->scfg = 0x0b;
643162886Snetchild        buff->acl = 0x80;
644162886Snetchild        buff->i2s = 0xfc;
645162886Snetchild        buff->spdif = 0xc3;
646162886Snetchild        buff->gpiomask = 0x21efff;
647162886Snetchild        buff->gpiostate = 0x7fffff;
648162886Snetchild        buff->gpiodir = 0x5e1000;
649162886Snetchild	buff->cdti = 0x40000;
650162886Snetchild	buff->cclk = 0x80000;
651162886Snetchild	buff->cs = 0x1000;
652162886Snetchild	buff->cif = 0x00;
653162886Snetchild	buff->type = 0x02;
654162886Snetchild
655162886Snetchild        for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0;
656162886Snetchildi++)
657162886Snetchild                if (cfg_table[i].subvendor == buff->subvendor &&
658162886Snetchild                    cfg_table[i].subdevice == buff->subdevice)
659162886Snetchild                        break;
660162886Snetchild        buff->name = cfg_table[i].name;
661162886Snetchild        buff->codec = cfg_table[i].codec;
662162886Snetchild
663162886Snetchild		return buff;
664162886Snetchild#if 0
665159687Snetchild		return NULL;
666162886Snetchild#endif
667159687Snetchild	}
668162886Snetchild	buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
669159687Snetchild	if (buff == NULL) {
670159687Snetchild#if(0)
671162886Snetchild		device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n");
672159687Snetchild#endif
673159687Snetchild		return NULL;
674159687Snetchild	}
675159687Snetchild	buff->free = 1;
676159687Snetchild
677162886Snetchild	buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8;
678162886Snetchild	buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1);
679162886Snetchild	buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8;
680162886Snetchild	buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1);
681162886Snetchild	buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG);
682162886Snetchild	buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL);
683162886Snetchild	buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S);
684162886Snetchild	buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF);
685162886Snetchild	buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \
686162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \
687162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16;
688162886Snetchild	buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \
689162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \
690162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16;
691162886Snetchild	buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \
692162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \
693162886Snetchild	envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16;
694159687Snetchild
695159687Snetchild	for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++)
696159687Snetchild		if (cfg_table[i].subvendor == buff->subvendor &&
697159687Snetchild		    cfg_table[i].subdevice == buff->subdevice)
698159687Snetchild			break;
699159687Snetchild	buff->name = cfg_table[i].name;
700159687Snetchild	buff->codec = cfg_table[i].codec;
701159687Snetchild
702159687Snetchild	return buff;
703159687Snetchild}
704159687Snetchild
705159687Snetchildstatic void
706162886Snetchildenvy24ht_cfgfree(struct cfg_info *cfg) {
707159687Snetchild	if (cfg == NULL)
708159687Snetchild		return;
709159687Snetchild	if (cfg->free)
710162886Snetchild		free(cfg, M_ENVY24HT);
711159687Snetchild	return;
712159687Snetchild}
713159687Snetchild
714159687Snetchild/* -------------------------------------------------------------------- */
715159687Snetchild
716159687Snetchild/* AC'97 codec access routines */
717159687Snetchild
718159689Snetchild#if 0
719159687Snetchildstatic int
720162886Snetchildenvy24ht_coldcd(struct sc_info *sc)
721159687Snetchild{
722159687Snetchild	u_int32_t data;
723159687Snetchild	int i;
724159687Snetchild
725159687Snetchild#if(0)
726162886Snetchild	device_printf(sc->dev, "envy24ht_coldcd()\n");
727159687Snetchild#endif
728162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1);
729159687Snetchild	DELAY(10);
730162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
731159687Snetchild	DELAY(1000);
732162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
733162886Snetchild		data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
734162886Snetchild		if (data & ENVY24HT_MT_AC97CMD_RDY) {
735159687Snetchild			return 0;
736159687Snetchild		}
737159687Snetchild	}
738159687Snetchild
739159687Snetchild	return -1;
740159687Snetchild}
741159687Snetchild
742159687Snetchildstatic int
743162886Snetchildenvy24ht_slavecd(struct sc_info *sc)
744159687Snetchild{
745159687Snetchild	u_int32_t data;
746159687Snetchild	int i;
747159687Snetchild
748159687Snetchild#if(0)
749162886Snetchild	device_printf(sc->dev, "envy24ht_slavecd()\n");
750159687Snetchild#endif
751162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD,
752162886Snetchild	    ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1);
753159687Snetchild	DELAY(10);
754162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1);
755159687Snetchild	DELAY(1000);
756162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
757162886Snetchild		data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
758162886Snetchild		if (data & ENVY24HT_MT_AC97CMD_RDY) {
759159687Snetchild			return 0;
760159687Snetchild		}
761159687Snetchild	}
762159687Snetchild
763159687Snetchild	return -1;
764159687Snetchild}
765159687Snetchild
766159687Snetchildstatic int
767162886Snetchildenvy24ht_rdcd(kobj_t obj, void *devinfo, int regno)
768159687Snetchild{
769159687Snetchild	struct sc_info *sc = (struct sc_info *)devinfo;
770159687Snetchild	u_int32_t data;
771159687Snetchild	int i;
772159687Snetchild
773159687Snetchild#if(0)
774162886Snetchild	device_printf(sc->dev, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno);
775159687Snetchild#endif
776162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
777162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1);
778162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
779162886Snetchild		data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
780162886Snetchild		if ((data & ENVY24HT_MT_AC97CMD_RD) == 0)
781159687Snetchild			break;
782159687Snetchild	}
783162886Snetchild	data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2);
784159687Snetchild
785159687Snetchild#if(0)
786162886Snetchild	device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data);
787159687Snetchild#endif
788159687Snetchild	return (int)data;
789159687Snetchild}
790159687Snetchild
791159687Snetchildstatic int
792162886Snetchildenvy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
793159687Snetchild{
794159687Snetchild	struct sc_info *sc = (struct sc_info *)devinfo;
795159687Snetchild	u_int32_t cmd;
796159687Snetchild	int i;
797159687Snetchild
798159687Snetchild#if(0)
799162886Snetchild	device_printf(sc->dev, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data);
800159687Snetchild#endif
801162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1);
802162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2);
803162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1);
804162886Snetchild	for (i = 0; i < ENVY24HT_TIMEOUT; i++) {
805162886Snetchild		cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1);
806162886Snetchild		if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0)
807159687Snetchild			break;
808159687Snetchild	}
809159687Snetchild
810159687Snetchild	return 0;
811159687Snetchild}
812159687Snetchild
813162886Snetchildstatic kobj_method_t envy24ht_ac97_methods[] = {
814162886Snetchild	KOBJMETHOD(ac97_read,	envy24ht_rdcd),
815162886Snetchild	KOBJMETHOD(ac97_write,	envy24ht_wrcd),
816193640Sariff	KOBJMETHOD_END
817159687Snetchild};
818162886SnetchildAC97_DECLARE(envy24ht_ac97);
819159689Snetchild#endif
820159687Snetchild
821159687Snetchild/* -------------------------------------------------------------------- */
822159687Snetchild
823159687Snetchild/* GPIO access routines */
824159687Snetchild
825159687Snetchildstatic u_int32_t
826162886Snetchildenvy24ht_gpiord(struct sc_info *sc)
827159687Snetchild{
828162886Snetchild	if (sc->cfg->subvendor == 0x153b  && sc->cfg->subdevice == 0x1150)
829162886Snetchild	return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2);
830162886Snetchild	else
831162886Snetchild	return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2));
832159687Snetchild}
833159687Snetchild
834159687Snetchildstatic void
835162886Snetchildenvy24ht_gpiowr(struct sc_info *sc, u_int32_t data)
836159687Snetchild{
837159687Snetchild#if(0)
838162886Snetchild	device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF);
839159687Snetchild	return;
840159687Snetchild#endif
841162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2);
842162886Snetchild	if (sc->cfg->subdevice != 0x1150)
843162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1);
844159687Snetchild	return;
845159687Snetchild}
846159687Snetchild
847159689Snetchild#if 0
848159687Snetchildstatic u_int32_t
849162886Snetchildenvy24ht_gpiogetmask(struct sc_info *sc)
850159687Snetchild{
851162886Snetchild	return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2));
852159687Snetchild}
853159689Snetchild#endif
854159687Snetchild
855159687Snetchildstatic void
856162886Snetchildenvy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask)
857159687Snetchild{
858162886Snetchild        envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2);
859162886Snetchild	if (sc->cfg->subdevice != 0x1150)
860162886Snetchild        envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1);
861159687Snetchild	return;
862159687Snetchild}
863159687Snetchild
864159689Snetchild#if 0
865159687Snetchildstatic u_int32_t
866162886Snetchildenvy24ht_gpiogetdir(struct sc_info *sc)
867159687Snetchild{
868170031Sjoel	return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4);
869159687Snetchild}
870159689Snetchild#endif
871159687Snetchild
872159687Snetchildstatic void
873162886Snetchildenvy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir)
874159687Snetchild{
875162886Snetchild	if (sc->cfg->subvendor == 0x153b  && sc->cfg->subdevice == 0x1150)
876162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2);
877162886Snetchild	else
878162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4);
879159687Snetchild	return;
880159687Snetchild}
881159687Snetchild
882159687Snetchild/* -------------------------------------------------------------------- */
883159687Snetchild
884162886Snetchild/* SPI codec access interface routine */
885159687Snetchild
886162886Snetchildstruct envy24ht_spi_codec {
887162886Snetchild	struct spicds_info *info;
888159687Snetchild	struct sc_info *parent;
889159687Snetchild	int dir;
890159687Snetchild	int num;
891159687Snetchild	int cs, cclk, cdti;
892159687Snetchild};
893159687Snetchild
894159687Snetchildstatic void
895162886Snetchildenvy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti)
896159687Snetchild{
897159687Snetchild	u_int32_t data = 0;
898162886Snetchild	struct envy24ht_spi_codec *ptr = codec;
899159687Snetchild
900159687Snetchild#if(0)
901159687Snetchild	device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti);
902159687Snetchild#endif
903162886Snetchild	data = envy24ht_gpiord(ptr->parent);
904159687Snetchild	data &= ~(ptr->cs | ptr->cclk | ptr->cdti);
905159687Snetchild	if (cs) data += ptr->cs;
906159687Snetchild	if (cclk) data += ptr->cclk;
907159687Snetchild	if (cdti) data += ptr->cdti;
908162886Snetchild	envy24ht_gpiowr(ptr->parent, data);
909159687Snetchild	return;
910159687Snetchild}
911159687Snetchild
912159687Snetchildstatic void *
913162886Snetchildenvy24ht_spi_create(device_t dev, void *info, int dir, int num)
914159687Snetchild{
915159687Snetchild	struct sc_info *sc = info;
916162886Snetchild	struct envy24ht_spi_codec *buff = NULL;
917159687Snetchild
918159687Snetchild#if(0)
919162886Snetchild	device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num);
920159687Snetchild#endif
921159687Snetchild
922162886Snetchild	buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT);
923159687Snetchild	if (buff == NULL)
924159687Snetchild		return NULL;
925159687Snetchild
926160796Snetchild	if (dir == PCMDIR_REC && sc->adc[num] != NULL)
927162886Snetchild		buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info;
928160796Snetchild	else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL)
929162886Snetchild		buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info;
930159687Snetchild	else
931162886Snetchild		buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl);
932159687Snetchild	if (buff->info == NULL) {
933162886Snetchild		free(buff, M_ENVY24HT);
934159687Snetchild		return NULL;
935159687Snetchild	}
936159687Snetchild
937159687Snetchild	buff->parent = sc;
938159687Snetchild	buff->dir = dir;
939159687Snetchild	buff->num = num;
940159687Snetchild
941159687Snetchild	return (void *)buff;
942159687Snetchild}
943159687Snetchild
944159687Snetchildstatic void
945162886Snetchildenvy24ht_spi_destroy(void *codec)
946159687Snetchild{
947162886Snetchild	struct envy24ht_spi_codec *ptr = codec;
948159687Snetchild	if (ptr == NULL)
949159687Snetchild		return;
950159687Snetchild#if(0)
951162886Snetchild	device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n");
952159687Snetchild#endif
953159687Snetchild
954159687Snetchild	if (ptr->dir == PCMDIR_PLAY) {
955162886Snetchild		if (ptr->parent->dac[ptr->num] != NULL)
956162886Snetchild			spicds_destroy(ptr->info);
957159687Snetchild	}
958159687Snetchild	else {
959162886Snetchild		if (ptr->parent->adc[ptr->num] != NULL)
960162886Snetchild			spicds_destroy(ptr->info);
961159687Snetchild	}
962159687Snetchild
963162886Snetchild	free(codec, M_ENVY24HT);
964159687Snetchild}
965159687Snetchild
966159687Snetchildstatic void
967162886Snetchildenvy24ht_spi_init(void *codec)
968159687Snetchild{
969162886Snetchild	struct envy24ht_spi_codec *ptr = codec;
970159687Snetchild	if (ptr == NULL)
971159687Snetchild		return;
972159687Snetchild#if(0)
973162886Snetchild	device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n");
974159687Snetchild#endif
975162886Snetchild        ptr->cs = ptr->parent->cfg->cs;
976162886Snetchild	ptr->cclk = ptr->parent->cfg->cclk;
977162886Snetchild	ptr->cdti =  ptr->parent->cfg->cdti;
978162886Snetchild	spicds_settype(ptr->info, ptr->parent->cfg->type);
979162886Snetchild	spicds_setcif(ptr->info, ptr->parent->cfg->cif);
980162886Snetchild	if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \
981162886Snetchild	ptr->parent->cfg->type == SPICDS_TYPE_AK4528) {
982162886Snetchild	spicds_setformat(ptr->info,
983162886Snetchild	    AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X);
984169745Sjoel	spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF);
985162886Snetchild	}
986159687Snetchild
987162886Snetchild	/* for the time being, init only first codec */
988159687Snetchild	if (ptr->num == 0)
989162886Snetchild	spicds_init(ptr->info);
990159687Snetchild}
991159687Snetchild
992159687Snetchildstatic void
993162886Snetchildenvy24ht_spi_reinit(void *codec)
994159687Snetchild{
995162886Snetchild	struct envy24ht_spi_codec *ptr = codec;
996159687Snetchild	if (ptr == NULL)
997159687Snetchild		return;
998159687Snetchild#if(0)
999162886Snetchild	device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n");
1000159687Snetchild#endif
1001159687Snetchild
1002162886Snetchild	spicds_reinit(ptr->info);
1003159687Snetchild}
1004159687Snetchild
1005159687Snetchildstatic void
1006162886Snetchildenvy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right)
1007159687Snetchild{
1008162886Snetchild	struct envy24ht_spi_codec *ptr = codec;
1009159687Snetchild	if (ptr == NULL)
1010159687Snetchild		return;
1011159687Snetchild#if(0)
1012162886Snetchild	device_printf(ptr->parent->dev, "envy24ht_spi_set()\n");
1013159687Snetchild#endif
1014159687Snetchild
1015162886Snetchild	spicds_set(ptr->info, dir, left, right);
1016159687Snetchild}
1017159687Snetchild
1018159687Snetchild/* -------------------------------------------------------------------- */
1019159687Snetchild
1020159687Snetchild/* hardware access routeines */
1021159687Snetchild
1022159687Snetchildstatic struct {
1023159687Snetchild	u_int32_t speed;
1024159687Snetchild	u_int32_t code;
1025162886Snetchild} envy24ht_speedtab[] = {
1026162886Snetchild	{48000, ENVY24HT_MT_RATE_48000},
1027162886Snetchild	{24000, ENVY24HT_MT_RATE_24000},
1028162886Snetchild	{12000, ENVY24HT_MT_RATE_12000},
1029162886Snetchild	{9600, ENVY24HT_MT_RATE_9600},
1030162886Snetchild	{32000, ENVY24HT_MT_RATE_32000},
1031162886Snetchild	{16000, ENVY24HT_MT_RATE_16000},
1032162886Snetchild	{8000, ENVY24HT_MT_RATE_8000},
1033162886Snetchild	{96000, ENVY24HT_MT_RATE_96000},
1034170031Sjoel	{192000, ENVY24HT_MT_RATE_192000},
1035162886Snetchild	{64000, ENVY24HT_MT_RATE_64000},
1036162886Snetchild	{44100, ENVY24HT_MT_RATE_44100},
1037162886Snetchild	{22050, ENVY24HT_MT_RATE_22050},
1038162886Snetchild	{11025, ENVY24HT_MT_RATE_11025},
1039162886Snetchild	{88200, ENVY24HT_MT_RATE_88200},
1040170031Sjoel	{176400, ENVY24HT_MT_RATE_176400},
1041159687Snetchild	{0, 0x10}
1042159687Snetchild};
1043159687Snetchild
1044193640Sariffstatic u_int32_t
1045162886Snetchildenvy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
1046170031Sjoel	u_int32_t code, i2sfmt;
1047159687Snetchild	int i = 0;
1048159687Snetchild
1049159687Snetchild#if(0)
1050162886Snetchild	device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed);
1051159687Snetchild	if (speed == 0) {
1052162886Snetchild		code = ENVY24HT_MT_RATE_SPDIF; /* external master clock */
1053162886Snetchild		envy24ht_slavecd(sc);
1054159687Snetchild	}
1055159687Snetchild	else {
1056162886Snetchild#endif
1057162886Snetchild		for (i = 0; envy24ht_speedtab[i].speed != 0; i++) {
1058162886Snetchild			if (envy24ht_speedtab[i].speed == speed)
1059159687Snetchild				break;
1060159687Snetchild		}
1061162886Snetchild		code = envy24ht_speedtab[i].code;
1062162886Snetchild#if 0
1063159687Snetchild	}
1064162886Snetchild	device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code);
1065159687Snetchild#endif
1066159687Snetchild	if (code < 0x10) {
1067162886Snetchild		envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1);
1068170031Sjoel		if ((((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) == 0x00) && (code == ENVY24HT_MT_RATE_192000)) || \
1069170031Sjoel									    (code == ENVY24HT_MT_RATE_176400)) {
1070170031Sjoel			i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
1071170031Sjoel			i2sfmt |= ENVY24HT_MT_I2S_MLR128;
1072170031Sjoel			envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
1073170031Sjoel		}
1074170031Sjoel		else {
1075170031Sjoel			i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1);
1076170031Sjoel			i2sfmt &= ~ENVY24HT_MT_I2S_MLR128;
1077170031Sjoel			envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1);
1078170031Sjoel		}
1079162886Snetchild		code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1);
1080162886Snetchild		code &= ENVY24HT_MT_RATE_MASK;
1081162886Snetchild		for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) {
1082162886Snetchild			if (envy24ht_speedtab[i].code == code)
1083159687Snetchild				break;
1084159687Snetchild		}
1085162886Snetchild		speed = envy24ht_speedtab[i].speed;
1086159687Snetchild	}
1087159687Snetchild	else
1088159687Snetchild		speed = 0;
1089159687Snetchild
1090159687Snetchild#if(0)
1091162886Snetchild	device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed);
1092159687Snetchild#endif
1093159687Snetchild	return speed;
1094159687Snetchild}
1095159687Snetchild
1096159687Snetchildstatic void
1097162886Snetchildenvy24ht_setvolume(struct sc_info *sc, unsigned ch)
1098159687Snetchild{
1099159687Snetchild#if(0)
1100162886Snetchild	device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch);
1101162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
1102162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2);
1103162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
1104162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2);
1105159687Snetchild#endif
1106159687Snetchild}
1107159687Snetchild
1108159687Snetchildstatic void
1109162886Snetchildenvy24ht_mutevolume(struct sc_info *sc, unsigned ch)
1110159687Snetchild{
1111162886Snetchild#if 0
1112159687Snetchild	u_int32_t vol;
1113159687Snetchild
1114162886Snetchild	device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch);
1115162886Snetchild	vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE;
1116162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1);
1117162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
1118162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1);
1119162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2);
1120159687Snetchild#endif
1121159687Snetchild}
1122159687Snetchild
1123159687Snetchildstatic u_int32_t
1124162886Snetchildenvy24ht_gethwptr(struct sc_info *sc, int dir)
1125159687Snetchild{
1126159687Snetchild	int unit, regno;
1127159687Snetchild	u_int32_t ptr, rtn;
1128159687Snetchild
1129159687Snetchild#if(0)
1130162886Snetchild	device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir);
1131159687Snetchild#endif
1132159687Snetchild	if (dir == PCMDIR_PLAY) {
1133159687Snetchild		rtn = sc->psize / 4;
1134162886Snetchild		unit = ENVY24HT_PLAY_BUFUNIT / 4;
1135162886Snetchild		regno = ENVY24HT_MT_PCNT;
1136159687Snetchild	}
1137159687Snetchild	else {
1138159687Snetchild		rtn = sc->rsize / 4;
1139162886Snetchild		unit = ENVY24HT_REC_BUFUNIT / 4;
1140162886Snetchild		regno = ENVY24HT_MT_RCNT;
1141159687Snetchild	}
1142159687Snetchild
1143162886Snetchild	ptr = envy24ht_rdmt(sc, regno, 2);
1144159687Snetchild	rtn -= (ptr + 1);
1145159687Snetchild	rtn /= unit;
1146159687Snetchild
1147159687Snetchild#if(0)
1148162886Snetchild	device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn);
1149159687Snetchild#endif
1150159687Snetchild	return rtn;
1151159687Snetchild}
1152159687Snetchild
1153159687Snetchildstatic void
1154162886Snetchildenvy24ht_updintr(struct sc_info *sc, int dir)
1155159687Snetchild{
1156159687Snetchild	int regptr, regintr;
1157159687Snetchild	u_int32_t mask, intr;
1158159687Snetchild	u_int32_t ptr, size, cnt;
1159159687Snetchild	u_int16_t blk;
1160159687Snetchild
1161159687Snetchild#if(0)
1162162886Snetchild	device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir);
1163159687Snetchild#endif
1164159687Snetchild	if (dir == PCMDIR_PLAY) {
1165159687Snetchild		blk = sc->blk[0];
1166159687Snetchild		size = sc->psize / 4;
1167162886Snetchild		regptr = ENVY24HT_MT_PCNT;
1168162886Snetchild		regintr = ENVY24HT_MT_PTERM;
1169162886Snetchild		mask = ~ENVY24HT_MT_INT_PMASK;
1170159687Snetchild	}
1171159687Snetchild	else {
1172159687Snetchild		blk = sc->blk[1];
1173159687Snetchild		size = sc->rsize / 4;
1174162886Snetchild		regptr = ENVY24HT_MT_RCNT;
1175162886Snetchild		regintr = ENVY24HT_MT_RTERM;
1176162886Snetchild		mask = ~ENVY24HT_MT_INT_RMASK;
1177159687Snetchild	}
1178159687Snetchild
1179162886Snetchild	ptr = size - envy24ht_rdmt(sc, regptr, 2) - 1;
1180159687Snetchild	/*
1181159687Snetchild	cnt = blk - ptr % blk - 1;
1182159687Snetchild	if (cnt == 0)
1183159687Snetchild		cnt = blk - 1;
1184159687Snetchild	*/
1185159687Snetchild	cnt = blk - 1;
1186159687Snetchild#if(0)
1187162886Snetchild	device_printf(sc->dev, "envy24ht_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt);
1188159687Snetchild#endif
1189162886Snetchild	envy24ht_wrmt(sc, regintr, cnt, 2);
1190162886Snetchild	intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
1191159687Snetchild#if(0)
1192162886Snetchild	device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask);
1193159687Snetchild#endif
1194162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1);
1195159687Snetchild#if(0)
1196162886Snetchild	device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n",
1197162886Snetchild		      envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1));
1198159687Snetchild#endif
1199159687Snetchild
1200159687Snetchild	return;
1201159687Snetchild}
1202159687Snetchild
1203159689Snetchild#if 0
1204159687Snetchildstatic void
1205162886Snetchildenvy24ht_maskintr(struct sc_info *sc, int dir)
1206159687Snetchild{
1207159687Snetchild	u_int32_t mask, intr;
1208159687Snetchild
1209159687Snetchild#if(0)
1210162886Snetchild	device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir);
1211159687Snetchild#endif
1212159687Snetchild	if (dir == PCMDIR_PLAY)
1213162886Snetchild		mask = ENVY24HT_MT_INT_PMASK;
1214159687Snetchild	else
1215162886Snetchild		mask = ENVY24HT_MT_INT_RMASK;
1216162886Snetchild	intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1);
1217162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1);
1218159687Snetchild
1219159687Snetchild	return;
1220159687Snetchild}
1221159689Snetchild#endif
1222159687Snetchild
1223159687Snetchildstatic int
1224162886Snetchildenvy24ht_checkintr(struct sc_info *sc, int dir)
1225159687Snetchild{
1226159687Snetchild	u_int32_t mask, stat, intr, rtn;
1227159687Snetchild
1228159687Snetchild#if(0)
1229162886Snetchild	device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir);
1230159687Snetchild#endif
1231162886Snetchild	intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1);
1232159687Snetchild	if (dir == PCMDIR_PLAY) {
1233162886Snetchild		if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) {
1234162886Snetchild			mask = ~ENVY24HT_MT_INT_RSTAT;
1235162886Snetchild			envy24ht_wrmt(sc, 0x1a, 0x01, 1);
1236162886Snetchild			envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1);
1237162886Snetchild			stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
1238162886Snetchild			envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1);
1239159687Snetchild		}
1240159687Snetchild	}
1241159687Snetchild	else {
1242162886Snetchild		if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) {
1243162886Snetchild			mask = ~ENVY24HT_MT_INT_PSTAT;
1244162886Snetchild#if 0
1245162886Snetchild			stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK;
1246162886Snetchild#endif
1247162886Snetchild			envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1);
1248162886Snetchild			stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1);
1249162886Snetchild			envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1);
1250159687Snetchild		}
1251159687Snetchild	}
1252159687Snetchild
1253159687Snetchild	return rtn;
1254159687Snetchild}
1255159687Snetchild
1256159687Snetchildstatic void
1257162886Snetchildenvy24ht_start(struct sc_info *sc, int dir)
1258159687Snetchild{
1259159687Snetchild	u_int32_t stat, sw;
1260159687Snetchild
1261159687Snetchild#if(0)
1262162886Snetchild	device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir);
1263159687Snetchild#endif
1264159687Snetchild	if (dir == PCMDIR_PLAY)
1265162886Snetchild		sw = ENVY24HT_MT_PCTL_PSTART;
1266159687Snetchild	else
1267162886Snetchild		sw = ENVY24HT_MT_PCTL_RSTART;
1268159687Snetchild
1269162886Snetchild	stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
1270162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1);
1271159687Snetchild#if(0)
1272159687Snetchild	DELAY(100);
1273162886Snetchild	device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4));
1274162886Snetchild	device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2));
1275159687Snetchild#endif
1276159687Snetchild
1277159687Snetchild	return;
1278159687Snetchild}
1279159687Snetchild
1280159687Snetchildstatic void
1281162886Snetchildenvy24ht_stop(struct sc_info *sc, int dir)
1282159687Snetchild{
1283159687Snetchild	u_int32_t stat, sw;
1284159687Snetchild
1285159687Snetchild#if(0)
1286162886Snetchild	device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir);
1287159687Snetchild#endif
1288159687Snetchild	if (dir == PCMDIR_PLAY)
1289162886Snetchild		sw = ~ENVY24HT_MT_PCTL_PSTART;
1290159687Snetchild	else
1291162886Snetchild		sw = ~ENVY24HT_MT_PCTL_RSTART;
1292159687Snetchild
1293162886Snetchild	stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1);
1294162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1);
1295159687Snetchild
1296159687Snetchild	return;
1297159687Snetchild}
1298159687Snetchild
1299162886Snetchild#if 0
1300159687Snetchildstatic int
1301162886Snetchildenvy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev)
1302159687Snetchild{
1303159687Snetchild	return 0;
1304159687Snetchild}
1305162886Snetchild#endif
1306159687Snetchild
1307159687Snetchild/* -------------------------------------------------------------------- */
1308159687Snetchild
1309159687Snetchild/* buffer copy routines */
1310159687Snetchildstatic void
1311162886Snetchildenvy24ht_p32sl(struct sc_chinfo *ch)
1312159687Snetchild{
1313159687Snetchild	int length;
1314159687Snetchild	sample32_t *dmabuf;
1315159687Snetchild	u_int32_t *data;
1316159687Snetchild	int src, dst, ssize, dsize, slot;
1317159687Snetchild	int i;
1318159687Snetchild
1319159687Snetchild	length = sndbuf_getready(ch->buffer) / 8;
1320159687Snetchild	dmabuf = ch->parent->pbuf;
1321159687Snetchild	data = (u_int32_t *)ch->data;
1322159687Snetchild	src = sndbuf_getreadyptr(ch->buffer) / 4;
1323159687Snetchild	dst = src / 2 + ch->offset;
1324159687Snetchild	ssize = ch->size / 4;
1325159687Snetchild	dsize = ch->size / 8;
1326159687Snetchild	slot = ch->num * 2;
1327159687Snetchild
1328159687Snetchild	for (i = 0; i < length; i++) {
1329162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src];
1330162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1];
1331159687Snetchild		dst++;
1332159687Snetchild		dst %= dsize;
1333159687Snetchild		src += 2;
1334159687Snetchild		src %= ssize;
1335159687Snetchild	}
1336159687Snetchild
1337159687Snetchild	return;
1338159687Snetchild}
1339159687Snetchild
1340159687Snetchildstatic void
1341162886Snetchildenvy24ht_p16sl(struct sc_chinfo *ch)
1342159687Snetchild{
1343159687Snetchild	int length;
1344159687Snetchild	sample32_t *dmabuf;
1345159687Snetchild	u_int16_t *data;
1346159687Snetchild	int src, dst, ssize, dsize, slot;
1347159687Snetchild	int i;
1348159687Snetchild
1349159687Snetchild#if(0)
1350162886Snetchild	device_printf(ch->parent->dev, "envy24ht_p16sl()\n");
1351159687Snetchild#endif
1352159687Snetchild	length = sndbuf_getready(ch->buffer) / 4;
1353159687Snetchild	dmabuf = ch->parent->pbuf;
1354159687Snetchild	data = (u_int16_t *)ch->data;
1355159687Snetchild	src = sndbuf_getreadyptr(ch->buffer) / 2;
1356159687Snetchild	dst = src / 2 + ch->offset;
1357159687Snetchild	ssize = ch->size / 2;
1358159687Snetchild	dsize = ch->size / 4;
1359159687Snetchild	slot = ch->num * 2;
1360159687Snetchild#if(0)
1361162886Snetchild	device_printf(ch->parent->dev, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length);
1362159687Snetchild#endif
1363159687Snetchild
1364159687Snetchild	for (i = 0; i < length; i++) {
1365162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16;
1366162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16;
1367159687Snetchild#if(0)
1368159687Snetchild		if (i < 16) {
1369162886Snetchild			printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]);
1370162886Snetchild			printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]);
1371159687Snetchild		}
1372159687Snetchild#endif
1373159687Snetchild		dst++;
1374159687Snetchild		dst %= dsize;
1375159687Snetchild		src += 2;
1376159687Snetchild		src %= ssize;
1377159687Snetchild	}
1378159687Snetchild#if(0)
1379159687Snetchild	printf("\n");
1380159687Snetchild#endif
1381159687Snetchild
1382159687Snetchild	return;
1383159687Snetchild}
1384159687Snetchild
1385159687Snetchildstatic void
1386162886Snetchildenvy24ht_p8u(struct sc_chinfo *ch)
1387159687Snetchild{
1388159687Snetchild	int length;
1389159687Snetchild	sample32_t *dmabuf;
1390159687Snetchild	u_int8_t *data;
1391159687Snetchild	int src, dst, ssize, dsize, slot;
1392159687Snetchild	int i;
1393159687Snetchild
1394159687Snetchild	length = sndbuf_getready(ch->buffer) / 2;
1395159687Snetchild	dmabuf = ch->parent->pbuf;
1396159687Snetchild	data = (u_int8_t *)ch->data;
1397159687Snetchild	src = sndbuf_getreadyptr(ch->buffer);
1398159687Snetchild	dst = src / 2 + ch->offset;
1399159687Snetchild	ssize = ch->size;
1400159687Snetchild	dsize = ch->size / 4;
1401159687Snetchild	slot = ch->num * 2;
1402159687Snetchild
1403159687Snetchild	for (i = 0; i < length; i++) {
1404162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24;
1405162886Snetchild		dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24;
1406159687Snetchild		dst++;
1407159687Snetchild		dst %= dsize;
1408159687Snetchild		src += 2;
1409159687Snetchild		src %= ssize;
1410159687Snetchild	}
1411159687Snetchild
1412159687Snetchild	return;
1413159687Snetchild}
1414159687Snetchild
1415159687Snetchildstatic void
1416162886Snetchildenvy24ht_r32sl(struct sc_chinfo *ch)
1417159687Snetchild{
1418159687Snetchild	int length;
1419159687Snetchild	sample32_t *dmabuf;
1420159687Snetchild	u_int32_t *data;
1421159687Snetchild	int src, dst, ssize, dsize, slot;
1422159687Snetchild	int i;
1423159687Snetchild
1424159687Snetchild	length = sndbuf_getfree(ch->buffer) / 8;
1425159687Snetchild	dmabuf = ch->parent->rbuf;
1426159687Snetchild	data = (u_int32_t *)ch->data;
1427159687Snetchild	dst = sndbuf_getfreeptr(ch->buffer) / 4;
1428159687Snetchild	src = dst / 2 + ch->offset;
1429159687Snetchild	dsize = ch->size / 4;
1430159687Snetchild	ssize = ch->size / 8;
1431162886Snetchild	slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2;
1432159687Snetchild
1433159687Snetchild	for (i = 0; i < length; i++) {
1434162886Snetchild		data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
1435162886Snetchild		data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
1436159687Snetchild		dst += 2;
1437159687Snetchild		dst %= dsize;
1438159687Snetchild		src++;
1439159687Snetchild		src %= ssize;
1440159687Snetchild	}
1441159687Snetchild
1442159687Snetchild	return;
1443159687Snetchild}
1444159687Snetchild
1445159687Snetchildstatic void
1446162886Snetchildenvy24ht_r16sl(struct sc_chinfo *ch)
1447159687Snetchild{
1448159687Snetchild	int length;
1449159687Snetchild	sample32_t *dmabuf;
1450159689Snetchild	u_int16_t *data;
1451159687Snetchild	int src, dst, ssize, dsize, slot;
1452159687Snetchild	int i;
1453159687Snetchild
1454159687Snetchild	length = sndbuf_getfree(ch->buffer) / 4;
1455159687Snetchild	dmabuf = ch->parent->rbuf;
1456159687Snetchild	data = (u_int16_t *)ch->data;
1457159687Snetchild	dst = sndbuf_getfreeptr(ch->buffer) / 2;
1458159687Snetchild	src = dst / 2 + ch->offset;
1459159687Snetchild	dsize = ch->size / 2;
1460159687Snetchild	ssize = ch->size / 8;
1461162886Snetchild	slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2;
1462159687Snetchild
1463159687Snetchild	for (i = 0; i < length; i++) {
1464162886Snetchild		data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer;
1465162886Snetchild		data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer;
1466159687Snetchild		dst += 2;
1467159687Snetchild		dst %= dsize;
1468159687Snetchild		src++;
1469159687Snetchild		src %= ssize;
1470159687Snetchild	}
1471159687Snetchild
1472159687Snetchild	return;
1473159687Snetchild}
1474159687Snetchild
1475159687Snetchild/* -------------------------------------------------------------------- */
1476159687Snetchild
1477159687Snetchild/* channel interface */
1478159687Snetchildstatic void *
1479162886Snetchildenvy24htchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
1480159687Snetchild{
1481159687Snetchild	struct sc_info	*sc = (struct sc_info *)devinfo;
1482159687Snetchild	struct sc_chinfo *ch;
1483159687Snetchild	unsigned num;
1484159687Snetchild
1485159687Snetchild#if(0)
1486162886Snetchild	device_printf(sc->dev, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir);
1487159687Snetchild#endif
1488159687Snetchild	snd_mtxlock(sc->lock);
1489162886Snetchild#if 0
1490162886Snetchild	if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) ||
1491162886Snetchild	    (sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) {
1492159687Snetchild		snd_mtxunlock(sc->lock);
1493159687Snetchild		return NULL;
1494159687Snetchild	}
1495162886Snetchild#endif
1496159687Snetchild	num = sc->chnum;
1497159687Snetchild
1498159687Snetchild	ch = &sc->chan[num];
1499162886Snetchild	ch->size = 8 * ENVY24HT_SAMPLE_NUM;
1500162886Snetchild	ch->data = malloc(ch->size, M_ENVY24HT, M_NOWAIT);
1501159687Snetchild	if (ch->data == NULL) {
1502159687Snetchild		ch->size = 0;
1503159687Snetchild		ch = NULL;
1504159687Snetchild	}
1505159687Snetchild	else {
1506159687Snetchild		ch->buffer = b;
1507159687Snetchild		ch->channel = c;
1508159687Snetchild		ch->parent = sc;
1509159687Snetchild		ch->dir = dir;
1510159687Snetchild		/* set channel map */
1511162886Snetchild		ch->num = envy24ht_chanmap[num];
1512165306Sariff		snd_mtxunlock(sc->lock);
1513159687Snetchild		sndbuf_setup(ch->buffer, ch->data, ch->size);
1514165306Sariff		snd_mtxlock(sc->lock);
1515159687Snetchild		/* these 2 values are dummy */
1516159687Snetchild		ch->unit = 4;
1517159687Snetchild		ch->blk = 10240;
1518159687Snetchild	}
1519159687Snetchild	snd_mtxunlock(sc->lock);
1520159687Snetchild
1521159687Snetchild	return ch;
1522159687Snetchild}
1523159687Snetchild
1524159687Snetchildstatic int
1525162886Snetchildenvy24htchan_free(kobj_t obj, void *data)
1526159687Snetchild{
1527159687Snetchild	struct sc_chinfo *ch = data;
1528159687Snetchild	struct sc_info *sc = ch->parent;
1529159687Snetchild
1530159687Snetchild#if(0)
1531162886Snetchild	device_printf(sc->dev, "envy24htchan_free()\n");
1532159687Snetchild#endif
1533159687Snetchild	snd_mtxlock(sc->lock);
1534159687Snetchild	if (ch->data != NULL) {
1535162886Snetchild		free(ch->data, M_ENVY24HT);
1536159687Snetchild		ch->data = NULL;
1537159687Snetchild	}
1538159687Snetchild	snd_mtxunlock(sc->lock);
1539159687Snetchild
1540159687Snetchild	return 0;
1541159687Snetchild}
1542159687Snetchild
1543159687Snetchildstatic int
1544162886Snetchildenvy24htchan_setformat(kobj_t obj, void *data, u_int32_t format)
1545159687Snetchild{
1546159687Snetchild	struct sc_chinfo *ch = data;
1547159687Snetchild	struct sc_info *sc = ch->parent;
1548162886Snetchild	struct envy24ht_emldma *emltab;
1549165306Sariff	/* unsigned int bcnt, bsize; */
1550159687Snetchild	int i;
1551159687Snetchild
1552159687Snetchild#if(0)
1553162886Snetchild	device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format);
1554159687Snetchild#endif
1555159687Snetchild	snd_mtxlock(sc->lock);
1556159687Snetchild	/* check and get format related information */
1557159687Snetchild	if (ch->dir == PCMDIR_PLAY)
1558162886Snetchild		emltab = envy24ht_pemltab;
1559159687Snetchild	else
1560162886Snetchild		emltab = envy24ht_remltab;
1561159687Snetchild	if (emltab == NULL) {
1562159687Snetchild		snd_mtxunlock(sc->lock);
1563159687Snetchild		return -1;
1564159687Snetchild	}
1565159687Snetchild	for (i = 0; emltab[i].format != 0; i++)
1566159687Snetchild		if (emltab[i].format == format)
1567159687Snetchild			break;
1568159687Snetchild	if (emltab[i].format == 0) {
1569159687Snetchild		snd_mtxunlock(sc->lock);
1570159687Snetchild		return -1;
1571159687Snetchild	}
1572159687Snetchild
1573159687Snetchild	/* set format information */
1574159687Snetchild	ch->format = format;
1575159687Snetchild	ch->emldma = emltab[i].emldma;
1576159687Snetchild	if (ch->unit > emltab[i].unit)
1577159687Snetchild		ch->blk *= ch->unit / emltab[i].unit;
1578159687Snetchild	else
1579159687Snetchild		ch->blk /= emltab[i].unit / ch->unit;
1580159687Snetchild	ch->unit = emltab[i].unit;
1581159687Snetchild
1582159687Snetchild	/* set channel buffer information */
1583162886Snetchild	ch->size = ch->unit * ENVY24HT_SAMPLE_NUM;
1584165306Sariff#if 0
1585159687Snetchild	if (ch->dir == PCMDIR_PLAY)
1586162886Snetchild		bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
1587159687Snetchild	else
1588162886Snetchild		bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
1589159687Snetchild	bsize *= ch->unit;
1590159687Snetchild	bcnt = ch->size / bsize;
1591159687Snetchild	sndbuf_resize(ch->buffer, bcnt, bsize);
1592165306Sariff#endif
1593159687Snetchild	snd_mtxunlock(sc->lock);
1594159687Snetchild
1595159687Snetchild#if(0)
1596162886Snetchild	device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0);
1597159687Snetchild#endif
1598159687Snetchild	return 0;
1599159687Snetchild}
1600159687Snetchild
1601159687Snetchild/*
1602159687Snetchild  IMPLEMENT NOTICE: In this driver, setspeed function only do setting
1603159687Snetchild  of speed information value. And real hardware speed setting is done
1604162886Snetchild  at start triggered(see envy24htchan_trigger()). So, at this function
1605159687Snetchild  is called, any value that ENVY24 can use is able to set. But, at
1606159687Snetchild  start triggerd, some other channel is running, and that channel's
1607159687Snetchild  speed isn't same with, then trigger function will fail.
1608159687Snetchild*/
1609193640Sariffstatic u_int32_t
1610162886Snetchildenvy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
1611159687Snetchild{
1612159687Snetchild	struct sc_chinfo *ch = data;
1613159687Snetchild	u_int32_t val, prev;
1614159687Snetchild	int i;
1615159687Snetchild
1616159687Snetchild#if(0)
1617162886Snetchild	device_printf(ch->parent->dev, "envy24htchan_setspeed(obj, data, %d)\n", speed);
1618159687Snetchild#endif
1619159687Snetchild	prev = 0x7fffffff;
1620162886Snetchild	for (i = 0; (val = envy24ht_speed[i]) != 0; i++) {
1621159687Snetchild		if (abs(val - speed) < abs(prev - speed))
1622159687Snetchild			prev = val;
1623159687Snetchild		else
1624159687Snetchild			break;
1625159687Snetchild	}
1626159687Snetchild	ch->speed = prev;
1627159687Snetchild
1628159687Snetchild#if(0)
1629162886Snetchild	device_printf(ch->parent->dev, "envy24htchan_setspeed(): return %d\n", ch->speed);
1630159687Snetchild#endif
1631159687Snetchild	return ch->speed;
1632159687Snetchild}
1633159687Snetchild
1634193640Sariffstatic u_int32_t
1635162886Snetchildenvy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1636159687Snetchild{
1637159687Snetchild	struct sc_chinfo *ch = data;
1638165306Sariff	/* struct sc_info *sc = ch->parent; */
1639159687Snetchild	u_int32_t size, prev;
1640165306Sariff	unsigned int bcnt, bsize;
1641159687Snetchild
1642159687Snetchild#if(0)
1643162886Snetchild	device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize);
1644159687Snetchild#endif
1645159687Snetchild	prev = 0x7fffffff;
1646165306Sariff	/* snd_mtxlock(sc->lock); */
1647159687Snetchild	for (size = ch->size / 2; size > 0; size /= 2) {
1648159687Snetchild		if (abs(size - blocksize) < abs(prev - blocksize))
1649159687Snetchild			prev = size;
1650159687Snetchild		else
1651159687Snetchild			break;
1652159687Snetchild	}
1653159687Snetchild
1654159687Snetchild	ch->blk = prev / ch->unit;
1655159687Snetchild	if (ch->dir == PCMDIR_PLAY)
1656162886Snetchild		ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4;
1657159687Snetchild	else
1658162886Snetchild		ch->blk *= ENVY24HT_REC_BUFUNIT / 4;
1659165306Sariff        /* set channel buffer information */
1660165306Sariff        /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */
1661165306Sariff        if (ch->dir == PCMDIR_PLAY)
1662165306Sariff                bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
1663165306Sariff        else
1664165306Sariff                bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
1665165306Sariff        bsize *= ch->unit;
1666165306Sariff        bcnt = ch->size / bsize;
1667165306Sariff        sndbuf_resize(ch->buffer, bcnt, bsize);
1668165306Sariff	/* snd_mtxunlock(sc->lock); */
1669159687Snetchild
1670159687Snetchild#if(0)
1671162886Snetchild	device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev);
1672159687Snetchild#endif
1673159687Snetchild	return prev;
1674159687Snetchild}
1675159687Snetchild
1676159687Snetchild/* semantic note: must start at beginning of buffer */
1677159687Snetchildstatic int
1678162886Snetchildenvy24htchan_trigger(kobj_t obj, void *data, int go)
1679159687Snetchild{
1680159687Snetchild	struct sc_chinfo *ch = data;
1681159687Snetchild	struct sc_info *sc = ch->parent;
1682159687Snetchild	u_int32_t ptr;
1683159687Snetchild	int slot;
1684191310Sstas	int error = 0;
1685160796Snetchild#if 0
1686159687Snetchild	int i;
1687159687Snetchild
1688162886Snetchild	device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go);
1689159687Snetchild#endif
1690159687Snetchild	snd_mtxlock(sc->lock);
1691159687Snetchild	if (ch->dir == PCMDIR_PLAY)
1692159687Snetchild		slot = 0;
1693159687Snetchild	else
1694159687Snetchild		slot = 1;
1695159687Snetchild	switch (go) {
1696159687Snetchild	case PCMTRIG_START:
1697159687Snetchild#if(0)
1698162886Snetchild		device_printf(sc->dev, "envy24htchan_trigger(): start\n");
1699159687Snetchild#endif
1700159687Snetchild		/* check or set channel speed */
1701159687Snetchild		if (sc->run[0] == 0 && sc->run[1] == 0) {
1702162886Snetchild			sc->speed = envy24ht_setspeed(sc, ch->speed);
1703159687Snetchild			sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed;
1704159687Snetchild			sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed;
1705159687Snetchild		}
1706191310Sstas		else if (ch->speed != 0 && ch->speed != sc->speed) {
1707191310Sstas			error = -1;
1708191310Sstas			goto fail;
1709191310Sstas		}
1710159687Snetchild		if (ch->speed == 0)
1711159687Snetchild			ch->channel->speed = sc->speed;
1712159687Snetchild		/* start or enable channel */
1713159687Snetchild		sc->run[slot]++;
1714159687Snetchild		if (sc->run[slot] == 1) {
1715159687Snetchild			/* first channel */
1716159687Snetchild			ch->offset = 0;
1717159687Snetchild			sc->blk[slot] = ch->blk;
1718159687Snetchild		}
1719159687Snetchild		else {
1720162886Snetchild			ptr = envy24ht_gethwptr(sc, ch->dir);
1721159687Snetchild			ch->offset = ((ptr / ch->blk + 1) * ch->blk %
1722159687Snetchild			    (ch->size / 4)) * 4 / ch->unit;
1723159687Snetchild			if (ch->blk < sc->blk[slot])
1724159687Snetchild				sc->blk[slot] = ch->blk;
1725159687Snetchild		}
1726159687Snetchild		if (ch->dir == PCMDIR_PLAY) {
1727159687Snetchild			ch->emldma(ch);
1728162886Snetchild			envy24ht_setvolume(sc, ch->num);
1729159687Snetchild		}
1730162886Snetchild		envy24ht_updintr(sc, ch->dir);
1731159687Snetchild		if (sc->run[slot] == 1)
1732162886Snetchild			envy24ht_start(sc, ch->dir);
1733159687Snetchild		ch->run = 1;
1734159687Snetchild		break;
1735159687Snetchild	case PCMTRIG_EMLDMAWR:
1736159687Snetchild#if(0)
1737162886Snetchild		device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n");
1738159687Snetchild#endif
1739191310Sstas		if (ch->run != 1) {
1740191310Sstas			error = -1;
1741191310Sstas			goto fail;
1742191310Sstas		}
1743159687Snetchild		ch->emldma(ch);
1744159687Snetchild		break;
1745159687Snetchild	case PCMTRIG_EMLDMARD:
1746159687Snetchild#if(0)
1747162886Snetchild		device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n");
1748159687Snetchild#endif
1749191310Sstas		if (ch->run != 1) {
1750191310Sstas			error = -1;
1751191310Sstas			goto fail;
1752191310Sstas		}
1753159687Snetchild		ch->emldma(ch);
1754159687Snetchild		break;
1755159687Snetchild	case PCMTRIG_ABORT:
1756170031Sjoel		if (ch->run) {
1757159687Snetchild#if(0)
1758162886Snetchild		device_printf(sc->dev, "envy24htchan_trigger(): abort\n");
1759159687Snetchild#endif
1760159687Snetchild		ch->run = 0;
1761159687Snetchild		sc->run[slot]--;
1762159687Snetchild		if (ch->dir == PCMDIR_PLAY)
1763162886Snetchild			envy24ht_mutevolume(sc, ch->num);
1764159687Snetchild		if (sc->run[slot] == 0) {
1765162886Snetchild			envy24ht_stop(sc, ch->dir);
1766159687Snetchild			sc->intr[slot] = 0;
1767159687Snetchild		}
1768162886Snetchild/*		else if (ch->blk == sc->blk[slot]) {
1769162886Snetchild			sc->blk[slot] = ENVY24HT_SAMPLE_NUM / 2;
1770162886Snetchild			for (i = 0; i < ENVY24HT_CHAN_NUM; i++) {
1771159687Snetchild				if (sc->chan[i].dir == ch->dir &&
1772159687Snetchild				    sc->chan[i].run == 1 &&
1773159687Snetchild				    sc->chan[i].blk < sc->blk[slot])
1774159687Snetchild					sc->blk[slot] = sc->chan[i].blk;
1775159687Snetchild			}
1776159687Snetchild			if (ch->blk != sc->blk[slot])
1777162886Snetchild				envy24ht_updintr(sc, ch->dir);
1778162886Snetchild		}*/
1779170031Sjoel		}
1780159687Snetchild		break;
1781159687Snetchild	}
1782191310Sstasfail:
1783159687Snetchild	snd_mtxunlock(sc->lock);
1784191310Sstas	return (error);
1785159687Snetchild}
1786159687Snetchild
1787193640Sariffstatic u_int32_t
1788162886Snetchildenvy24htchan_getptr(kobj_t obj, void *data)
1789159687Snetchild{
1790159687Snetchild	struct sc_chinfo *ch = data;
1791159687Snetchild	struct sc_info *sc = ch->parent;
1792193640Sariff	u_int32_t ptr, rtn;
1793159687Snetchild
1794159687Snetchild#if(0)
1795162886Snetchild	device_printf(sc->dev, "envy24htchan_getptr()\n");
1796159687Snetchild#endif
1797159687Snetchild	snd_mtxlock(sc->lock);
1798162886Snetchild	ptr = envy24ht_gethwptr(sc, ch->dir);
1799159687Snetchild	rtn = ptr * ch->unit;
1800159687Snetchild	snd_mtxunlock(sc->lock);
1801159687Snetchild
1802159687Snetchild#if(0)
1803162886Snetchild	device_printf(sc->dev, "envy24htchan_getptr(): return %d\n",
1804159687Snetchild	    rtn);
1805159687Snetchild#endif
1806159687Snetchild	return rtn;
1807159687Snetchild}
1808159687Snetchild
1809159687Snetchildstatic struct pcmchan_caps *
1810162886Snetchildenvy24htchan_getcaps(kobj_t obj, void *data)
1811159687Snetchild{
1812159687Snetchild	struct sc_chinfo *ch = data;
1813159687Snetchild	struct sc_info *sc = ch->parent;
1814159687Snetchild	struct pcmchan_caps *rtn;
1815159687Snetchild
1816159687Snetchild#if(0)
1817162886Snetchild	device_printf(sc->dev, "envy24htchan_getcaps()\n");
1818159687Snetchild#endif
1819159687Snetchild	snd_mtxlock(sc->lock);
1820159687Snetchild	if (ch->dir == PCMDIR_PLAY) {
1821159687Snetchild		if (sc->run[0] == 0)
1822162886Snetchild			rtn = &envy24ht_playcaps;
1823159687Snetchild		else
1824159687Snetchild			rtn = &sc->caps[0];
1825159687Snetchild	}
1826159687Snetchild	else {
1827159687Snetchild		if (sc->run[1] == 0)
1828162886Snetchild			rtn = &envy24ht_reccaps;
1829159687Snetchild		else
1830159687Snetchild			rtn = &sc->caps[1];
1831159687Snetchild	}
1832159687Snetchild	snd_mtxunlock(sc->lock);
1833159687Snetchild
1834159687Snetchild	return rtn;
1835159687Snetchild}
1836159687Snetchild
1837162886Snetchildstatic kobj_method_t envy24htchan_methods[] = {
1838162886Snetchild	KOBJMETHOD(channel_init,		envy24htchan_init),
1839162886Snetchild	KOBJMETHOD(channel_free,		envy24htchan_free),
1840162886Snetchild	KOBJMETHOD(channel_setformat,		envy24htchan_setformat),
1841162886Snetchild	KOBJMETHOD(channel_setspeed,		envy24htchan_setspeed),
1842162886Snetchild	KOBJMETHOD(channel_setblocksize,	envy24htchan_setblocksize),
1843162886Snetchild	KOBJMETHOD(channel_trigger,		envy24htchan_trigger),
1844162886Snetchild	KOBJMETHOD(channel_getptr,		envy24htchan_getptr),
1845162886Snetchild	KOBJMETHOD(channel_getcaps,		envy24htchan_getcaps),
1846193640Sariff	KOBJMETHOD_END
1847159687Snetchild};
1848162886SnetchildCHANNEL_DECLARE(envy24htchan);
1849159687Snetchild
1850159687Snetchild/* -------------------------------------------------------------------- */
1851159687Snetchild
1852159687Snetchild/* mixer interface */
1853159687Snetchild
1854159687Snetchildstatic int
1855162886Snetchildenvy24htmixer_init(struct snd_mixer *m)
1856159687Snetchild{
1857159687Snetchild	struct sc_info *sc = mix_getdevinfo(m);
1858159687Snetchild
1859159687Snetchild#if(0)
1860162886Snetchild	device_printf(sc->dev, "envy24htmixer_init()\n");
1861159687Snetchild#endif
1862159687Snetchild	if (sc == NULL)
1863159687Snetchild		return -1;
1864159687Snetchild
1865159687Snetchild	/* set volume control rate */
1866159687Snetchild	snd_mtxlock(sc->lock);
1867162886Snetchild#if 0
1868162886Snetchild	envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */
1869162886Snetchild#endif
1870159687Snetchild
1871170207Sariff	pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
1872170031Sjoel
1873170065Sariff	mix_setdevs(m, ENVY24HT_MIX_MASK);
1874170065Sariff	mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK);
1875170065Sariff
1876159687Snetchild	snd_mtxunlock(sc->lock);
1877159687Snetchild
1878159687Snetchild	return 0;
1879159687Snetchild}
1880159687Snetchild
1881159687Snetchildstatic int
1882162886Snetchildenvy24htmixer_reinit(struct snd_mixer *m)
1883159687Snetchild{
1884159687Snetchild	struct sc_info *sc = mix_getdevinfo(m);
1885159687Snetchild
1886159687Snetchild	if (sc == NULL)
1887159687Snetchild		return -1;
1888159687Snetchild#if(0)
1889162886Snetchild	device_printf(sc->dev, "envy24htmixer_reinit()\n");
1890159687Snetchild#endif
1891159687Snetchild
1892159687Snetchild	return 0;
1893159687Snetchild}
1894159687Snetchild
1895159687Snetchildstatic int
1896162886Snetchildenvy24htmixer_uninit(struct snd_mixer *m)
1897159687Snetchild{
1898159687Snetchild	struct sc_info *sc = mix_getdevinfo(m);
1899159687Snetchild
1900159687Snetchild	if (sc == NULL)
1901159687Snetchild		return -1;
1902159687Snetchild#if(0)
1903162886Snetchild	device_printf(sc->dev, "envy24htmixer_uninit()\n");
1904159687Snetchild#endif
1905159687Snetchild
1906159687Snetchild	return 0;
1907159687Snetchild}
1908159687Snetchild
1909159687Snetchildstatic int
1910162886Snetchildenvy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
1911159687Snetchild{
1912159687Snetchild	struct sc_info *sc = mix_getdevinfo(m);
1913162886Snetchild	int ch = envy24ht_mixmap[dev];
1914159687Snetchild	int hwch;
1915159687Snetchild	int i;
1916159687Snetchild
1917159687Snetchild	if (sc == NULL)
1918159687Snetchild		return -1;
1919159687Snetchild	if (dev == 0 && sc->cfg->codec->setvolume == NULL)
1920159687Snetchild		return -1;
1921159687Snetchild	if (dev != 0 && ch == -1)
1922159687Snetchild		return -1;
1923162886Snetchild	hwch = envy24ht_chanmap[ch];
1924159687Snetchild#if(0)
1925162886Snetchild	device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n",
1926159687Snetchild	    dev, left, right);
1927159687Snetchild#endif
1928159687Snetchild
1929159687Snetchild	snd_mtxlock(sc->lock);
1930159687Snetchild	if (dev == 0) {
1931159687Snetchild		for (i = 0; i < sc->dacn; i++) {
1932159687Snetchild			sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right);
1933159687Snetchild		}
1934159687Snetchild	}
1935159687Snetchild	else {
1936159687Snetchild		/* set volume value for hardware */
1937162886Snetchild		if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN)
1938162886Snetchild			sc->left[hwch] = ENVY24HT_VOL_MUTE;
1939162886Snetchild		if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN)
1940162886Snetchild			sc->right[hwch] = ENVY24HT_VOL_MUTE;
1941159687Snetchild
1942159687Snetchild		/* set volume for record channel and running play channel */
1943162886Snetchild		if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run)
1944162886Snetchild			envy24ht_setvolume(sc, hwch);
1945159687Snetchild	}
1946159687Snetchild	snd_mtxunlock(sc->lock);
1947159687Snetchild
1948159687Snetchild	return right << 8 | left;
1949159687Snetchild}
1950159687Snetchild
1951159687Snetchildstatic u_int32_t
1952162886Snetchildenvy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
1953159687Snetchild{
1954159687Snetchild	struct sc_info *sc = mix_getdevinfo(m);
1955162886Snetchild	int ch = envy24ht_mixmap[src];
1956159687Snetchild#if(0)
1957162886Snetchild	device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src);
1958159687Snetchild#endif
1959159687Snetchild
1960162886Snetchild	if (ch > ENVY24HT_CHAN_PLAY_SPDIF)
1961159687Snetchild		sc->src = ch;
1962159687Snetchild	return src;
1963159687Snetchild}
1964159687Snetchild
1965162886Snetchildstatic kobj_method_t envy24htmixer_methods[] = {
1966162886Snetchild	KOBJMETHOD(mixer_init,		envy24htmixer_init),
1967162886Snetchild	KOBJMETHOD(mixer_reinit,	envy24htmixer_reinit),
1968162886Snetchild	KOBJMETHOD(mixer_uninit,	envy24htmixer_uninit),
1969162886Snetchild	KOBJMETHOD(mixer_set,		envy24htmixer_set),
1970162886Snetchild	KOBJMETHOD(mixer_setrecsrc,	envy24htmixer_setrecsrc),
1971193640Sariff	KOBJMETHOD_END
1972159687Snetchild};
1973162886SnetchildMIXER_DECLARE(envy24htmixer);
1974159687Snetchild
1975159687Snetchild/* -------------------------------------------------------------------- */
1976159687Snetchild
1977159687Snetchild/* The interrupt handler */
1978159687Snetchildstatic void
1979162886Snetchildenvy24ht_intr(void *p)
1980159687Snetchild{
1981159687Snetchild	struct sc_info *sc = (struct sc_info *)p;
1982159687Snetchild	struct sc_chinfo *ch;
1983159687Snetchild	u_int32_t ptr, dsize, feed;
1984159687Snetchild	int i;
1985159687Snetchild
1986159687Snetchild#if(0)
1987162886Snetchild	device_printf(sc->dev, "envy24ht_intr()\n");
1988159687Snetchild#endif
1989159687Snetchild	snd_mtxlock(sc->lock);
1990162886Snetchild	if (envy24ht_checkintr(sc, PCMDIR_PLAY)) {
1991159687Snetchild#if(0)
1992162886Snetchild		device_printf(sc->dev, "envy24ht_intr(): play\n");
1993159687Snetchild#endif
1994159687Snetchild		dsize = sc->psize / 4;
1995162886Snetchild		ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1;
1996159687Snetchild#if(0)
1997162886Snetchild		device_printf(sc->dev, "envy24ht_intr(): ptr = %d-->", ptr);
1998159687Snetchild#endif
1999159687Snetchild		ptr -= ptr % sc->blk[0];
2000159687Snetchild		feed = (ptr + dsize - sc->intr[0]) % dsize;
2001159687Snetchild#if(0)
2002159687Snetchild		printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed);
2003159687Snetchild#endif
2004162886Snetchild		for (i = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) {
2005159687Snetchild			ch = &sc->chan[i];
2006159687Snetchild#if(0)
2007159687Snetchild			if (ch->run)
2008162886Snetchild				device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk);
2009159687Snetchild#endif
2010165306Sariff			if (ch->run && ch->blk <= feed) {
2011165306Sariff				snd_mtxunlock(sc->lock);
2012159687Snetchild				chn_intr(ch->channel);
2013165306Sariff				snd_mtxlock(sc->lock);
2014165306Sariff			}
2015159687Snetchild		}
2016159687Snetchild		sc->intr[0] = ptr;
2017162886Snetchild		envy24ht_updintr(sc, PCMDIR_PLAY);
2018159687Snetchild	}
2019162886Snetchild	if (envy24ht_checkintr(sc, PCMDIR_REC)) {
2020159687Snetchild#if(0)
2021162886Snetchild		device_printf(sc->dev, "envy24ht_intr(): rec\n");
2022159687Snetchild#endif
2023159687Snetchild		dsize = sc->rsize / 4;
2024162886Snetchild		ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1;
2025159687Snetchild		ptr -= ptr % sc->blk[1];
2026159687Snetchild		feed = (ptr + dsize - sc->intr[1]) % dsize;
2027162886Snetchild		for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) {
2028159687Snetchild			ch = &sc->chan[i];
2029165306Sariff			if (ch->run && ch->blk <= feed) {
2030165306Sariff				snd_mtxunlock(sc->lock);
2031159687Snetchild				chn_intr(ch->channel);
2032165306Sariff				snd_mtxlock(sc->lock);
2033165306Sariff			}
2034159687Snetchild		}
2035159687Snetchild		sc->intr[1] = ptr;
2036162886Snetchild		envy24ht_updintr(sc, PCMDIR_REC);
2037159687Snetchild	}
2038159687Snetchild	snd_mtxunlock(sc->lock);
2039159687Snetchild
2040159687Snetchild	return;
2041159687Snetchild}
2042159687Snetchild
2043159687Snetchild/*
2044159687Snetchild * Probe and attach the card
2045159687Snetchild */
2046159687Snetchild
2047159687Snetchildstatic int
2048162886Snetchildenvy24ht_pci_probe(device_t dev)
2049159687Snetchild{
2050159687Snetchild	u_int16_t sv, sd;
2051159687Snetchild	int i;
2052159687Snetchild
2053159687Snetchild#if(0)
2054162886Snetchild	printf("envy24ht_pci_probe()\n");
2055159687Snetchild#endif
2056162886Snetchild	if (pci_get_device(dev) == PCID_ENVY24HT &&
2057159687Snetchild	    pci_get_vendor(dev) == PCIV_ENVY24) {
2058159687Snetchild		sv = pci_get_subvendor(dev);
2059159687Snetchild		sd = pci_get_subdevice(dev);
2060159687Snetchild		for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
2061159687Snetchild			if (cfg_table[i].subvendor == sv &&
2062159687Snetchild			    cfg_table[i].subdevice == sd) {
2063159687Snetchild				break;
2064159687Snetchild			}
2065159687Snetchild		}
2066159687Snetchild		device_set_desc(dev, cfg_table[i].name);
2067159687Snetchild#if(0)
2068162886Snetchild		printf("envy24ht_pci_probe(): return 0\n");
2069159687Snetchild#endif
2070159687Snetchild		return 0;
2071159687Snetchild	}
2072159687Snetchild	else {
2073159687Snetchild#if(0)
2074162886Snetchild		printf("envy24ht_pci_probe(): return ENXIO\n");
2075159687Snetchild#endif
2076159687Snetchild		return ENXIO;
2077159687Snetchild	}
2078159687Snetchild}
2079159687Snetchild
2080159687Snetchildstatic void
2081162886Snetchildenvy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2082159687Snetchild{
2083250286Smav	struct sc_info *sc = arg;
2084159687Snetchild
2085159687Snetchild#if(0)
2086162886Snetchild	device_printf(sc->dev, "envy24ht_dmapsetmap()\n");
2087159687Snetchild	if (bootverbose) {
2088162886Snetchild		printf("envy24ht(play): setmap %lx, %lx; ",
2089159687Snetchild		    (unsigned long)segs->ds_addr,
2090159687Snetchild		    (unsigned long)segs->ds_len);
2091159687Snetchild	}
2092165306Sariff#endif
2093250286Smav	envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, (uint32_t)segs->ds_addr, 4);
2094250286Smav	envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, (uint32_t)(segs->ds_len / 4 - 1), 2);
2095159687Snetchild}
2096159687Snetchild
2097159687Snetchildstatic void
2098162886Snetchildenvy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2099159687Snetchild{
2100250286Smav	struct sc_info *sc = arg;
2101159687Snetchild
2102159687Snetchild#if(0)
2103162886Snetchild	device_printf(sc->dev, "envy24ht_dmarsetmap()\n");
2104159687Snetchild	if (bootverbose) {
2105162886Snetchild		printf("envy24ht(record): setmap %lx, %lx; ",
2106159687Snetchild		    (unsigned long)segs->ds_addr,
2107159687Snetchild		    (unsigned long)segs->ds_len);
2108159687Snetchild	}
2109165306Sariff#endif
2110250286Smav	envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, (uint32_t)segs->ds_addr, 4);
2111250286Smav	envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, (uint32_t)(segs->ds_len / 4 - 1), 2);
2112159687Snetchild}
2113159687Snetchild
2114159687Snetchildstatic void
2115162886Snetchildenvy24ht_dmafree(struct sc_info *sc)
2116159687Snetchild{
2117159687Snetchild#if(0)
2118162886Snetchild	device_printf(sc->dev, "envy24ht_dmafree():");
2119159687Snetchild	if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap);
2120159687Snetchild	else printf(" sc->rmap(null)");
2121159687Snetchild	if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap);
2122159687Snetchild	else printf(" sc->pmap(null)");
2123159687Snetchild	if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf);
2124159687Snetchild	else printf(" sc->rbuf(null)");
2125159687Snetchild	if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf);
2126159687Snetchild	else printf(" sc->pbuf(null)\n");
2127159687Snetchild#endif
2128159687Snetchild#if(0)
2129159687Snetchild	if (sc->rmap)
2130159687Snetchild		bus_dmamap_unload(sc->dmat, sc->rmap);
2131159687Snetchild	if (sc->pmap)
2132159687Snetchild		bus_dmamap_unload(sc->dmat, sc->pmap);
2133159687Snetchild	if (sc->rbuf)
2134159687Snetchild		bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
2135159687Snetchild	if (sc->pbuf)
2136159687Snetchild		bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
2137159687Snetchild#else
2138159687Snetchild	bus_dmamap_unload(sc->dmat, sc->rmap);
2139159687Snetchild	bus_dmamap_unload(sc->dmat, sc->pmap);
2140159687Snetchild	bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
2141159687Snetchild	bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
2142159687Snetchild#endif
2143159687Snetchild
2144159687Snetchild	sc->rmap = sc->pmap = NULL;
2145159687Snetchild	sc->pbuf = NULL;
2146159687Snetchild	sc->rbuf = NULL;
2147159687Snetchild
2148159687Snetchild	return;
2149159687Snetchild}
2150159687Snetchild
2151159687Snetchildstatic int
2152162886Snetchildenvy24ht_dmainit(struct sc_info *sc)
2153159687Snetchild{
2154159687Snetchild
2155159687Snetchild#if(0)
2156162886Snetchild	device_printf(sc->dev, "envy24ht_dmainit()\n");
2157159687Snetchild#endif
2158159687Snetchild	/* init values */
2159162886Snetchild	sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM;
2160162886Snetchild	sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM;
2161159687Snetchild	sc->pbuf = NULL;
2162159687Snetchild	sc->rbuf = NULL;
2163159687Snetchild	sc->pmap = sc->rmap = NULL;
2164159687Snetchild	sc->blk[0] = sc->blk[1] = 0;
2165159687Snetchild
2166159687Snetchild	/* allocate DMA buffer */
2167159687Snetchild#if(0)
2168162886Snetchild	device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->pbuf\n");
2169159687Snetchild#endif
2170159687Snetchild	if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap))
2171159687Snetchild		goto bad;
2172159687Snetchild#if(0)
2173162886Snetchild	device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->rbuf\n");
2174159687Snetchild#endif
2175159687Snetchild	if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap))
2176159687Snetchild		goto bad;
2177159687Snetchild#if(0)
2178162886Snetchild	device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n");
2179159687Snetchild#endif
2180250286Smav	if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, BUS_DMA_NOWAIT))
2181159687Snetchild		goto bad;
2182159687Snetchild#if(0)
2183162886Snetchild	device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n");
2184159687Snetchild#endif
2185250286Smav	if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, BUS_DMA_NOWAIT))
2186159687Snetchild		goto bad;
2187159687Snetchild	bzero(sc->pbuf, sc->psize);
2188159687Snetchild	bzero(sc->rbuf, sc->rsize);
2189159687Snetchild
2190159687Snetchild	return 0;
2191159687Snetchild bad:
2192162886Snetchild	envy24ht_dmafree(sc);
2193159687Snetchild	return ENOSPC;
2194159687Snetchild}
2195159687Snetchild
2196159687Snetchildstatic void
2197162886Snetchildenvy24ht_putcfg(struct sc_info *sc)
2198159687Snetchild{
2199159689Snetchild	device_printf(sc->dev, "system configuration\n");
2200159687Snetchild	printf("  SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n",
2201159687Snetchild	    sc->cfg->subvendor, sc->cfg->subdevice);
2202159687Snetchild	printf("  XIN2 Clock Source: ");
2203162886Snetchild	switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) {
2204159687Snetchild	case 0x00:
2205162886Snetchild		printf("24.576MHz(96kHz*256)\n");
2206159687Snetchild		break;
2207159687Snetchild	case 0x40:
2208162886Snetchild		printf("49.152MHz(192kHz*256)\n");
2209159687Snetchild		break;
2210159687Snetchild	case 0x80:
2211162886Snetchild		printf("reserved\n");
2212159687Snetchild		break;
2213159687Snetchild	default:
2214159687Snetchild		printf("illeagal system setting\n");
2215159687Snetchild	}
2216159687Snetchild	printf("  MPU-401 UART(s) #: ");
2217162886Snetchild	if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU)
2218159687Snetchild		printf("1\n");
2219159687Snetchild	else
2220162886Snetchild		printf("not implemented\n");
2221162886Snetchild        switch (sc->adcn) {
2222213779Srpaulo        case 0x01:
2223213779Srpaulo	case 0x02:
2224162886Snetchild                printf("  ADC #: ");
2225162886Snetchild                printf("%d\n", sc->adcn);
2226162886Snetchild                break;
2227162886Snetchild        case 0x03:
2228162886Snetchild                printf("  ADC #: ");
2229162886Snetchild                printf("%d", 1);
2230162886Snetchild                printf(" and SPDIF receiver connected\n");
2231162886Snetchild                break;
2232162886Snetchild        default:
2233162886Snetchild                printf("  no physical inputs\n");
2234162886Snetchild        }
2235159687Snetchild	printf("  DAC #: ");
2236159687Snetchild	printf("%d\n", sc->dacn);
2237159687Snetchild	printf("  Multi-track converter type: ");
2238162886Snetchild	if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) {
2239159687Snetchild		printf("AC'97(SDATA_OUT:");
2240162886Snetchild		if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE)
2241159687Snetchild			printf("packed");
2242159687Snetchild		else
2243159687Snetchild			printf("split");
2244159687Snetchild		printf(")\n");
2245159687Snetchild	}
2246159687Snetchild	else {
2247159687Snetchild		printf("I2S(");
2248162886Snetchild		if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL)
2249159687Snetchild			printf("with volume, ");
2250162886Snetchild                if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ)
2251162886Snetchild                        printf("192KHz support, ");
2252162886Snetchild                else
2253162886Snetchild                if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ)
2254162886Snetchild                        printf("192KHz support, ");
2255162886Snetchild                else
2256162886Snetchild                        printf("48KHz support, ");
2257162886Snetchild		switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) {
2258162886Snetchild		case ENVY24HT_CCSM_I2S_16BIT:
2259159687Snetchild			printf("16bit resolution, ");
2260159687Snetchild			break;
2261162886Snetchild		case ENVY24HT_CCSM_I2S_18BIT:
2262159687Snetchild			printf("18bit resolution, ");
2263159687Snetchild			break;
2264162886Snetchild		case ENVY24HT_CCSM_I2S_20BIT:
2265159687Snetchild			printf("20bit resolution, ");
2266159687Snetchild			break;
2267162886Snetchild		case ENVY24HT_CCSM_I2S_24BIT:
2268159687Snetchild			printf("24bit resolution, ");
2269159687Snetchild			break;
2270159687Snetchild		}
2271162886Snetchild		printf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID);
2272159687Snetchild	}
2273159687Snetchild	printf("  S/PDIF(IN/OUT): ");
2274162886Snetchild	if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN)
2275159687Snetchild		printf("1/");
2276159687Snetchild	else
2277159687Snetchild		printf("0/");
2278162886Snetchild	if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT)
2279159687Snetchild		printf("1 ");
2280159687Snetchild	else
2281159687Snetchild		printf("0 ");
2282162886Snetchild	if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT))
2283162886Snetchild		printf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_ID) >> 2);
2284159687Snetchild	printf("  GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n",
2285159687Snetchild	    sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate);
2286159687Snetchild}
2287159687Snetchild
2288159687Snetchildstatic int
2289162886Snetchildenvy24ht_init(struct sc_info *sc)
2290159687Snetchild{
2291159687Snetchild	u_int32_t data;
2292159687Snetchild#if(0)
2293159687Snetchild	int rtn;
2294159687Snetchild#endif
2295159687Snetchild	int i;
2296159687Snetchild	u_int32_t sv, sd;
2297159687Snetchild
2298159687Snetchild
2299159687Snetchild#if(0)
2300162886Snetchild	device_printf(sc->dev, "envy24ht_init()\n");
2301159687Snetchild#endif
2302159687Snetchild
2303159687Snetchild	/* reset chip */
2304162886Snetchild#if 0
2305162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1);
2306159687Snetchild	DELAY(200);
2307162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1);
2308159687Snetchild	DELAY(200);
2309159687Snetchild
2310159687Snetchild	/* legacy hardware disable */
2311159687Snetchild	data = pci_read_config(sc->dev, PCIR_LAC, 2);
2312159687Snetchild	data |= PCIM_LAC_DISABLE;
2313159687Snetchild	pci_write_config(sc->dev, PCIR_LAC, data, 2);
2314162886Snetchild#endif
2315159687Snetchild
2316159687Snetchild	/* check system configuration */
2317159687Snetchild	sc->cfg = NULL;
2318159687Snetchild	for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) {
2319159687Snetchild		/* 1st: search configuration from table */
2320159687Snetchild		sv = pci_get_subvendor(sc->dev);
2321159687Snetchild		sd = pci_get_subdevice(sc->dev);
2322159687Snetchild		if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) {
2323159687Snetchild#if(0)
2324159687Snetchild			device_printf(sc->dev, "Set configuration from table\n");
2325159687Snetchild#endif
2326159687Snetchild			sc->cfg = &cfg_table[i];
2327159687Snetchild			break;
2328159687Snetchild		}
2329159687Snetchild	}
2330159687Snetchild	if (sc->cfg == NULL) {
2331159687Snetchild		/* 2nd: read configuration from table */
2332162886Snetchild		sc->cfg = envy24ht_rom2cfg(sc);
2333159687Snetchild	}
2334165306Sariff	sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1; /* need to be fixed */
2335162886Snetchild	sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1;
2336159687Snetchild
2337159687Snetchild	if (1 /* bootverbose */) {
2338162886Snetchild		envy24ht_putcfg(sc);
2339159687Snetchild	}
2340159687Snetchild
2341159687Snetchild	/* set system configuration */
2342162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1);
2343162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1);
2344162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1);
2345162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1);
2346162886Snetchild	envy24ht_gpiosetmask(sc, sc->cfg->gpiomask);
2347162886Snetchild	envy24ht_gpiosetdir(sc, sc->cfg->gpiodir);
2348162886Snetchild	envy24ht_gpiowr(sc, sc->cfg->gpiostate);
2349170031Sjoel
2350170031Sjoel	if ((sc->cfg->subvendor == 0x3031) && (sc->cfg->subdevice == 0x4553)) {
2351170031Sjoel		envy24ht_wri2c(sc, 0x22, 0x00, 0x07);
2352170031Sjoel		envy24ht_wri2c(sc, 0x22, 0x04, 0x5f | 0x80);
2353170031Sjoel		envy24ht_wri2c(sc, 0x22, 0x05, 0x5f | 0x80);
2354170031Sjoel	}
2355162886Snetchild
2356159687Snetchild	for (i = 0; i < sc->adcn; i++) {
2357159687Snetchild		sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i);
2358159687Snetchild		sc->cfg->codec->init(sc->adc[i]);
2359159687Snetchild	}
2360159687Snetchild	for (i = 0; i < sc->dacn; i++) {
2361159687Snetchild		sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i);
2362159687Snetchild		sc->cfg->codec->init(sc->dac[i]);
2363159687Snetchild	}
2364159687Snetchild
2365159687Snetchild	/* initialize DMA buffer */
2366159687Snetchild#if(0)
2367162886Snetchild	device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n");
2368159687Snetchild#endif
2369162886Snetchild	if (envy24ht_dmainit(sc))
2370159687Snetchild		return ENOSPC;
2371159687Snetchild
2372159687Snetchild	/* initialize status */
2373159687Snetchild	sc->run[0] = sc->run[1] = 0;
2374159687Snetchild	sc->intr[0] = sc->intr[1] = 0;
2375159687Snetchild	sc->speed = 0;
2376162886Snetchild	sc->caps[0].fmtlist = envy24ht_playfmt;
2377162886Snetchild	sc->caps[1].fmtlist = envy24ht_recfmt;
2378159687Snetchild
2379159687Snetchild	/* set channel router */
2380162886Snetchild#if 0
2381162886Snetchild	envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
2382162886Snetchild	envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0);
2383162886Snetchild	envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0);
2384162886Snetchild#endif
2385159687Snetchild
2386159687Snetchild	/* set macro interrupt mask */
2387162886Snetchild	data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
2388162886Snetchild	envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1);
2389162886Snetchild	data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1);
2390159687Snetchild#if(0)
2391162886Snetchild	device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data);
2392159687Snetchild#endif
2393159687Snetchild
2394159687Snetchild	return 0;
2395159687Snetchild}
2396159687Snetchild
2397159687Snetchildstatic int
2398166919Sariffenvy24ht_alloc_resource(struct sc_info *sc)
2399159687Snetchild{
2400159687Snetchild	/* allocate I/O port resource */
2401159687Snetchild	sc->csid = PCIR_CCS;
2402159687Snetchild	sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2403159687Snetchild	    &sc->csid, 0, ~0, 1, RF_ACTIVE);
2404162886Snetchild	sc->mtid = ENVY24HT_PCIR_MT;
2405159687Snetchild	sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT,
2406159687Snetchild	    &sc->mtid, 0, ~0, 1, RF_ACTIVE);
2407162886Snetchild	if (!sc->cs || !sc->mt) {
2408159687Snetchild		device_printf(sc->dev, "unable to map IO port space\n");
2409159687Snetchild		return ENXIO;
2410159687Snetchild	}
2411159687Snetchild	sc->cst = rman_get_bustag(sc->cs);
2412159687Snetchild	sc->csh = rman_get_bushandle(sc->cs);
2413159687Snetchild	sc->mtt = rman_get_bustag(sc->mt);
2414159687Snetchild	sc->mth = rman_get_bushandle(sc->mt);
2415159687Snetchild#if(0)
2416159687Snetchild	device_printf(sc->dev,
2417162886Snetchild	    "IO port register values\nCCS: 0x%lx\nMT: 0x%lx\n",
2418159687Snetchild	    pci_read_config(sc->dev, PCIR_CCS, 4),
2419159687Snetchild	    pci_read_config(sc->dev, PCIR_MT, 4));
2420159687Snetchild#endif
2421159687Snetchild
2422172568Skevlo	/* allocate interrupt resource */
2423159687Snetchild	sc->irqid = 0;
2424159687Snetchild	sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
2425159687Snetchild				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
2426159687Snetchild	if (!sc->irq ||
2427250286Smav	    snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24ht_intr, sc, &sc->ih)) {
2428159687Snetchild		device_printf(sc->dev, "unable to map interrupt\n");
2429159687Snetchild		return ENXIO;
2430159687Snetchild	}
2431159687Snetchild
2432159687Snetchild	/* allocate DMA resource */
2433166919Sariff	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev),
2434166919Sariff	    /*alignment*/4,
2435166904Snetchild	    /*boundary*/0,
2436250286Smav	    /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
2437250286Smav	    /*highaddr*/BUS_SPACE_MAXADDR,
2438159687Snetchild	    /*filter*/NULL, /*filterarg*/NULL,
2439159687Snetchild	    /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24,
2440159687Snetchild	    /*nsegments*/1, /*maxsegsz*/0x3ffff,
2441250286Smav	    /*flags*/0, /*lockfunc*/NULL,
2442250286Smav	    /*lockarg*/NULL, &sc->dmat) != 0) {
2443159687Snetchild		device_printf(sc->dev, "unable to create dma tag\n");
2444159687Snetchild		return ENXIO;
2445159687Snetchild	}
2446159687Snetchild
2447159687Snetchild	return 0;
2448159687Snetchild}
2449159687Snetchild
2450159687Snetchildstatic int
2451162886Snetchildenvy24ht_pci_attach(device_t dev)
2452159687Snetchild{
2453159687Snetchild	struct sc_info 		*sc;
2454159687Snetchild	char 			status[SND_STATUSLEN];
2455159687Snetchild	int			err = 0;
2456159687Snetchild	int			i;
2457159687Snetchild
2458159687Snetchild#if(0)
2459162886Snetchild	device_printf(dev, "envy24ht_pci_attach()\n");
2460159687Snetchild#endif
2461159687Snetchild	/* get sc_info data area */
2462162886Snetchild	if ((sc = malloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) {
2463159687Snetchild		device_printf(dev, "cannot allocate softc\n");
2464159687Snetchild		return ENXIO;
2465159687Snetchild	}
2466159687Snetchild
2467159687Snetchild	bzero(sc, sizeof(*sc));
2468167608Sariff	sc->lock = snd_mtxcreate(device_get_nameunit(dev),
2469167608Sariff	    "snd_envy24ht softc");
2470159687Snetchild	sc->dev = dev;
2471159687Snetchild
2472159687Snetchild	/* initialize PCI interface */
2473254263Sscottl	pci_enable_busmaster(dev);
2474159687Snetchild
2475159687Snetchild	/* allocate resources */
2476166919Sariff	err = envy24ht_alloc_resource(sc);
2477159689Snetchild	if (err) {
2478159687Snetchild		device_printf(dev, "unable to allocate system resources\n");
2479159687Snetchild		goto bad;
2480159687Snetchild	}
2481159687Snetchild
2482159687Snetchild	/* initialize card */
2483162886Snetchild	err = envy24ht_init(sc);
2484159689Snetchild	if (err) {
2485159687Snetchild		device_printf(dev, "unable to initialize the card\n");
2486159687Snetchild		goto bad;
2487159687Snetchild	}
2488159687Snetchild
2489159687Snetchild	/* set multi track mixer */
2490162886Snetchild	mixer_init(dev, &envy24htmixer_class, sc);
2491159687Snetchild
2492159687Snetchild	/* set channel information */
2493165306Sariff	/* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */
2494165306Sariff	err = pcm_register(dev, sc, 1, 2 + sc->adcn);
2495159689Snetchild	if (err)
2496159687Snetchild		goto bad;
2497159687Snetchild	sc->chnum = 0;
2498165306Sariff	/* for (i = 0; i < 5; i++) { */
2499162886Snetchild		pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc);
2500159687Snetchild		sc->chnum++;
2501165306Sariff	/* } */
2502159687Snetchild	for (i = 0; i < 2 + sc->adcn; i++) {
2503162886Snetchild		pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc);
2504159687Snetchild		sc->chnum++;
2505159687Snetchild	}
2506159687Snetchild
2507159687Snetchild	/* set status iformation */
2508159687Snetchild	snprintf(status, SND_STATUSLEN,
2509162886Snetchild	    "at io 0x%lx:%ld,0x%lx:%ld irq %ld",
2510159687Snetchild	    rman_get_start(sc->cs),
2511159687Snetchild	    rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1,
2512159687Snetchild	    rman_get_start(sc->mt),
2513159687Snetchild	    rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
2514159687Snetchild	    rman_get_start(sc->irq));
2515159687Snetchild	pcm_setstatus(dev, status);
2516159687Snetchild
2517159687Snetchild	return 0;
2518159687Snetchild
2519159687Snetchildbad:
2520159687Snetchild	if (sc->ih)
2521159687Snetchild		bus_teardown_intr(dev, sc->irq, sc->ih);
2522159687Snetchild	if (sc->irq)
2523159687Snetchild		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
2524162886Snetchild	envy24ht_dmafree(sc);
2525159687Snetchild	if (sc->dmat)
2526159687Snetchild		bus_dma_tag_destroy(sc->dmat);
2527162886Snetchild        if (sc->cfg->codec->destroy != NULL) {
2528160796Snetchild                for (i = 0; i < sc->adcn; i++)
2529160796Snetchild                        sc->cfg->codec->destroy(sc->adc[i]);
2530160796Snetchild                for (i = 0; i < sc->dacn; i++)
2531160796Snetchild                        sc->cfg->codec->destroy(sc->dac[i]);
2532160796Snetchild        }
2533162886Snetchild	envy24ht_cfgfree(sc->cfg);
2534159687Snetchild	if (sc->cs)
2535159687Snetchild		bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
2536159687Snetchild	if (sc->mt)
2537159687Snetchild		bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
2538159687Snetchild	if (sc->lock)
2539159687Snetchild		snd_mtxfree(sc->lock);
2540162886Snetchild	free(sc, M_ENVY24HT);
2541159687Snetchild	return err;
2542159687Snetchild}
2543159687Snetchild
2544159687Snetchildstatic int
2545162886Snetchildenvy24ht_pci_detach(device_t dev)
2546159687Snetchild{
2547159687Snetchild	struct sc_info *sc;
2548159687Snetchild	int r;
2549159687Snetchild	int i;
2550159687Snetchild
2551159687Snetchild#if(0)
2552162886Snetchild	device_printf(dev, "envy24ht_pci_detach()\n");
2553159687Snetchild#endif
2554159687Snetchild	sc = pcm_getdevinfo(dev);
2555159687Snetchild	if (sc == NULL)
2556159687Snetchild		return 0;
2557159687Snetchild	r = pcm_unregister(dev);
2558159687Snetchild	if (r)
2559159687Snetchild		return r;
2560159687Snetchild
2561162886Snetchild	envy24ht_dmafree(sc);
2562159687Snetchild	if (sc->cfg->codec->destroy != NULL) {
2563159687Snetchild		for (i = 0; i < sc->adcn; i++)
2564159687Snetchild			sc->cfg->codec->destroy(sc->adc[i]);
2565159687Snetchild		for (i = 0; i < sc->dacn; i++)
2566159687Snetchild			sc->cfg->codec->destroy(sc->dac[i]);
2567159687Snetchild	}
2568162886Snetchild	envy24ht_cfgfree(sc->cfg);
2569159687Snetchild	bus_dma_tag_destroy(sc->dmat);
2570159687Snetchild	bus_teardown_intr(dev, sc->irq, sc->ih);
2571159687Snetchild	bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
2572159687Snetchild	bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs);
2573159687Snetchild	bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt);
2574159687Snetchild	snd_mtxfree(sc->lock);
2575162886Snetchild	free(sc, M_ENVY24HT);
2576159687Snetchild	return 0;
2577159687Snetchild}
2578159687Snetchild
2579162886Snetchildstatic device_method_t envy24ht_methods[] = {
2580159687Snetchild	/* Device interface */
2581162886Snetchild	DEVMETHOD(device_probe,		envy24ht_pci_probe),
2582162886Snetchild	DEVMETHOD(device_attach,	envy24ht_pci_attach),
2583162886Snetchild	DEVMETHOD(device_detach,	envy24ht_pci_detach),
2584159687Snetchild	{ 0, 0 }
2585159687Snetchild};
2586159687Snetchild
2587162886Snetchildstatic driver_t envy24ht_driver = {
2588159687Snetchild	"pcm",
2589162886Snetchild	envy24ht_methods,
2590159687Snetchild#if __FreeBSD_version > 500000
2591159687Snetchild	PCM_SOFTC_SIZE,
2592159687Snetchild#else
2593159687Snetchild	sizeof(struct snddev_info),
2594159687Snetchild#endif
2595159687Snetchild};
2596159687Snetchild
2597162886SnetchildDRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, pcm_devclass, 0, 0);
2598162886SnetchildMODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
2599168883SariffMODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1);
2600162886SnetchildMODULE_VERSION(snd_envy24ht, 1);
2601