als4000.c revision 78362
1/*
2 * Copyright (c) 2001 Orion Hodson <oho@acm.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/sound/pci/als4000.c 78362 2001-06-16 21:25:10Z cg $
27 */
28
29/*
30 * als4000.c - driver for the Avance Logic ALS 4000 chipset.
31 *
32 * The ALS4000 is a effectively an SB16 with a PCI interface.
33 *
34 * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
35 * SB16 register descriptions.
36 */
37
38#include <dev/sound/pcm/sound.h>
39#include <dev/sound/isa/sb.h>
40#include <dev/sound/pci/als4000.h>
41
42#include <pci/pcireg.h>
43#include <pci/pcivar.h>
44
45#include "mixer_if.h"
46
47/* Debugging macro's */
48#undef DEB
49#ifndef DEB
50#define DEB(x)  /* x */
51#endif /* DEB */
52
53/* ------------------------------------------------------------------------- */
54/* Structures */
55
56struct sc_info;
57
58struct sc_chinfo {
59	struct sc_info		*parent;
60	struct pcm_channel	*channel;
61	struct snd_dbuf		*buffer;
62	u_int32_t		format, speed, phys_buf, bps;
63	u_int32_t		dma_active:1, dma_was_active:1;
64	u_int8_t		gcr_fifo_status;
65	int			dir;
66};
67
68struct sc_info {
69	device_t		dev;
70	bus_space_tag_t		st;
71	bus_space_handle_t	sh;
72	bus_dma_tag_t		parent_dmat;
73	struct resource		*reg, *irq;
74	int			regid, irqid;
75	void			*ih;
76	struct sc_chinfo	pch, rch;
77};
78
79/* Channel caps */
80
81static u_int32_t als_format[] = {
82        AFMT_U8,
83        AFMT_STEREO | AFMT_U8,
84        AFMT_S16_LE,
85        AFMT_STEREO | AFMT_S16_LE,
86        0
87};
88
89static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
90
91/* ------------------------------------------------------------------------- */
92/* Register Utilities */
93
94static u_int32_t
95als_gcr_rd(struct sc_info *sc, int index)
96{
97	bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
98	return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA);
99}
100
101static void
102als_gcr_wr(struct sc_info *sc, int index, int data)
103{
104	bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
105	bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data);
106}
107
108static u_int8_t
109als_intr_rd(struct sc_info *sc)
110{
111	return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ);
112}
113
114static void
115als_intr_wr(struct sc_info *sc, u_int8_t data)
116{
117	bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data);
118}
119
120static u_int8_t
121als_mix_rd(struct sc_info *sc, u_int8_t index)
122{
123	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
124	return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA);
125}
126
127static void
128als_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data)
129{
130	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
131	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data);
132}
133
134static void
135als_esp_wr(struct sc_info *sc, u_int8_t data)
136{
137	u_int32_t	tries, v;
138
139	tries = 1000;
140	do {
141		v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS);
142		if (~v & 0x80)
143			break;
144		DELAY(20);
145	} while (--tries != 0);
146
147	if (tries == 0)
148		device_printf(sc->dev, "als_esp_wr timeout");
149
150	bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
151}
152
153static int
154als_esp_reset(struct sc_info *sc)
155{
156	u_int32_t	tries, u, v;
157
158	bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1);
159	DELAY(10);
160	bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0);
161	DELAY(30);
162
163	tries = 1000;
164	do {
165		u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
166		if (u & 0x80) {
167			v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
168			if (v == 0xaa)
169				return 0;
170			else
171				break;
172		}
173		DELAY(20);
174	} while (--tries != 0);
175
176	if (tries == 0)
177		device_printf(sc->dev, "als_esp_reset timeout");
178	return 1;
179}
180
181static u_int8_t
182als_ack_read(struct sc_info *sc, u_int8_t addr)
183{
184	u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr);
185	return r;
186}
187
188/* ------------------------------------------------------------------------- */
189/* Common pcm channel implementation */
190
191static void *
192alschan_init(kobj_t obj, void *devinfo,
193	     struct snd_dbuf *b, struct pcm_channel *c, int dir)
194{
195	struct	sc_info	*sc = devinfo;
196	struct	sc_chinfo *ch;
197
198	if (dir == PCMDIR_PLAY) {
199		ch = &sc->pch;
200		ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
201	} else {
202		ch = &sc->rch;
203		ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS;
204	}
205	ch->dir = dir;
206	ch->parent = sc;
207	ch->channel = c;
208	ch->bps = 1;
209	ch->format = AFMT_U8;
210	ch->speed = DSP_DEFAULT_SPEED;
211	ch->buffer = b;
212	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, ALS_BUFFER_SIZE) != 0) {
213		return NULL;
214	}
215	return ch;
216}
217
218static int
219alschan_setformat(kobj_t obj, void *data, u_int32_t format)
220{
221	struct	sc_chinfo *ch = data;
222
223	ch->format = format;
224	return 0;
225}
226
227static int
228alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
229{
230	struct	sc_chinfo *ch = data, *other;
231	struct  sc_info *sc = ch->parent;
232
233	other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
234
235	/* Deny request if other dma channel is active */
236	if (other->dma_active) {
237		ch->speed = other->speed;
238		return other->speed;
239	}
240
241	ch->speed = speed;
242	return speed;
243}
244
245static int
246alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
247{
248	struct	sc_chinfo *ch = data;
249
250	if (blocksize > ALS_BUFFER_SIZE / 2) {
251		blocksize = ALS_BUFFER_SIZE / 2;
252	}
253	sndbuf_resize(ch->buffer, 2, blocksize);
254	return sndbuf_getsize(ch->buffer);
255}
256
257static int
258alschan_getptr(kobj_t obj, void *data)
259{
260	struct sc_chinfo *ch = data;
261	int32_t pos, sz;
262
263	pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
264	sz  = sndbuf_getsize(ch->buffer);
265	return (2 * sz - pos - 1) % sz;
266}
267
268static struct pcmchan_caps*
269alschan_getcaps(kobj_t obj, void *data)
270{
271	return &als_caps;
272}
273
274static void
275als_set_speed(struct sc_chinfo *ch)
276{
277	struct sc_info *sc = ch->parent;
278	struct sc_chinfo *other;
279
280	other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
281	if (other->dma_active == 0) {
282		als_esp_wr(sc, ALS_ESP_SAMPLE_RATE);
283		als_esp_wr(sc, ch->speed >> 8);
284		als_esp_wr(sc, ch->speed & 0xff);
285	} else {
286		DEB(printf("speed locked at %d (tried %d)\n",
287			   other->speed, ch->speed));
288	}
289}
290
291/* ------------------------------------------------------------------------- */
292/* Playback channel implementation */
293
294#define ALS_8BIT_CMD(x, y)  { (x), (y), DSP_DMA8,  DSP_CMD_DMAPAUSE_8  }
295#define ALS_16BIT_CMD(x, y) { (x), (y),	DSP_DMA16, DSP_CMD_DMAPAUSE_16 }
296
297struct playback_command {
298	u_int32_t pcm_format;	/* newpcm format */
299	u_int8_t  format_val;	/* sb16 format value */
300	u_int8_t  dma_prog;	/* sb16 dma program */
301	u_int8_t  dma_stop;	/* sb16 stop register */
302} static const playback_cmds[] = {
303	ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO),
304	ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO),
305	ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO),
306	ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO),
307};
308
309static const struct playback_command*
310als_get_playback_command(u_int32_t format)
311{
312	u_int32_t i, n;
313
314	n = sizeof(playback_cmds) / sizeof(playback_cmds[0]);
315	for (i = 0; i < n; i++) {
316		if (playback_cmds[i].pcm_format == format) {
317			return &playback_cmds[i];
318		}
319	}
320	DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
321		   format));
322	return &playback_cmds[0];
323}
324
325static void
326als_playback_start(struct sc_chinfo *ch)
327{
328	const struct playback_command *p;
329	struct	sc_info *sc = ch->parent;
330	u_int32_t	buf, bufsz, count, dma_prog;
331
332	buf = vtophys(sndbuf_getbuf(ch->buffer));
333	bufsz = sndbuf_getsize(ch->buffer);
334	count = bufsz / 2;
335	if (ch->format & AFMT_16BIT)
336		count /= 2;
337	count--;
338
339	als_esp_wr(sc, DSP_CMD_SPKON);
340	als_set_speed(ch);
341
342	als_gcr_wr(sc, ALS_GCR_DMA0_START, buf);
343	als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000);
344
345	p = als_get_playback_command(ch->format);
346	dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON;
347
348	als_esp_wr(sc, dma_prog);
349	als_esp_wr(sc, p->format_val);
350	als_esp_wr(sc, count & 0xff);
351	als_esp_wr(sc, count >> 8);
352
353	ch->dma_active = 1;
354}
355
356static int
357als_playback_stop(struct sc_chinfo *ch)
358{
359	const struct playback_command *p;
360	struct sc_info *sc = ch->parent;
361	u_int32_t active;
362
363	active = ch->dma_active;
364	if (active) {
365		p = als_get_playback_command(ch->format);
366		als_esp_wr(sc, p->dma_stop);
367	}
368	ch->dma_active = 0;
369	return active;
370}
371
372static int
373alspchan_trigger(kobj_t obj, void *data, int go)
374{
375	struct	sc_chinfo *ch = data;
376
377	switch(go) {
378	case PCMTRIG_START:
379		als_playback_start(ch);
380		break;
381	case PCMTRIG_ABORT:
382		als_playback_stop(ch);
383		break;
384	}
385	return 0;
386}
387
388static kobj_method_t alspchan_methods[] = {
389	KOBJMETHOD(channel_init,		alschan_init),
390	KOBJMETHOD(channel_setformat,		alschan_setformat),
391	KOBJMETHOD(channel_setspeed,		alschan_setspeed),
392	KOBJMETHOD(channel_setblocksize,	alschan_setblocksize),
393	KOBJMETHOD(channel_trigger,		alspchan_trigger),
394	KOBJMETHOD(channel_getptr,		alschan_getptr),
395	KOBJMETHOD(channel_getcaps,		alschan_getcaps),
396	{ 0, 0 }
397};
398CHANNEL_DECLARE(alspchan);
399
400/* ------------------------------------------------------------------------- */
401/* Capture channel implementation */
402
403static u_int8_t
404als_get_fifo_format(struct sc_info *sc, u_int32_t format)
405{
406	switch (format) {
407	case AFMT_U8:
408		return ALS_FIFO1_8BIT;
409	case AFMT_U8 | AFMT_STEREO:
410		return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
411	case AFMT_S16_LE:
412		return ALS_FIFO1_SIGNED;
413	case AFMT_S16_LE | AFMT_STEREO:
414		return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
415	}
416	device_printf(sc->dev, "format not found: 0x%08x\n", format);
417	return ALS_FIFO1_8BIT;
418}
419
420static void
421als_capture_start(struct sc_chinfo *ch)
422{
423	struct	sc_info *sc = ch->parent;
424	u_int32_t	buf, bufsz, count, dma_prog;
425
426	buf = vtophys(sndbuf_getbuf(ch->buffer));
427	bufsz = sndbuf_getsize(ch->buffer);
428	count = bufsz / 2;
429	if (ch->format & AFMT_16BIT)
430		count /= 2;
431	count--;
432
433	als_esp_wr(sc, DSP_CMD_SPKON);
434	als_set_speed(ch);
435
436	als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf);
437	als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1));
438
439	als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff);
440	als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8);
441
442	dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format);
443	als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog);
444
445	ch->dma_active = 1;
446}
447
448static int
449als_capture_stop(struct sc_chinfo *ch)
450{
451	struct sc_info *sc = ch->parent;
452	u_int32_t active;
453
454	active = ch->dma_active;
455	if (active) {
456		als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP);
457	}
458	ch->dma_active = 0;
459	return active;
460}
461
462static int
463alsrchan_trigger(kobj_t obj, void *data, int go)
464{
465	struct	sc_chinfo *ch = data;
466
467	switch(go) {
468	case PCMTRIG_START:
469		als_capture_start(ch);
470		break;
471	case PCMTRIG_ABORT:
472		als_capture_stop(ch);
473		break;
474	}
475	return 0;
476}
477
478static kobj_method_t alsrchan_methods[] = {
479	KOBJMETHOD(channel_init,		alschan_init),
480	KOBJMETHOD(channel_setformat,		alschan_setformat),
481	KOBJMETHOD(channel_setspeed,		alschan_setspeed),
482	KOBJMETHOD(channel_setblocksize,	alschan_setblocksize),
483	KOBJMETHOD(channel_trigger,		alsrchan_trigger),
484	KOBJMETHOD(channel_getptr,		alschan_getptr),
485	KOBJMETHOD(channel_getcaps,		alschan_getcaps),
486	{ 0, 0 }
487};
488CHANNEL_DECLARE(alsrchan);
489
490/* ------------------------------------------------------------------------- */
491/* Mixer related */
492
493/*
494 * ALS4000 has an sb16 mixer, with some additional controls that we do
495 * not yet a means to support.
496 */
497
498struct sb16props {
499	u_int8_t lreg;
500	u_int8_t rreg;
501	u_int8_t bits;
502	u_int8_t oselect;
503	u_int8_t iselect; /* left input mask */
504} static const amt[SOUND_MIXER_NRDEVICES] = {
505	[SOUND_MIXER_VOLUME]  = { 0x30, 0x31, 5, 0x00, 0x00 },
506	[SOUND_MIXER_PCM]     = { 0x32, 0x33, 5, 0x00, 0x00 },
507	[SOUND_MIXER_SYNTH]   = { 0x34, 0x35, 5, 0x60, 0x40 },
508	[SOUND_MIXER_CD]      = { 0x36, 0x37, 5, 0x06, 0x04 },
509	[SOUND_MIXER_LINE]    = { 0x38, 0x39, 5, 0x18, 0x10 },
510	[SOUND_MIXER_MIC]     = { 0x3a, 0x00, 5, 0x01, 0x01 },
511	[SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 },
512	[SOUND_MIXER_IGAIN]   = { 0x3f, 0x40, 2, 0x00, 0x00 },
513	[SOUND_MIXER_OGAIN]   = { 0x41, 0x42, 2, 0x00, 0x00 },
514	/* The following have register values but no h/w implementation */
515	[SOUND_MIXER_TREBLE]  = { 0x44, 0x45, 4, 0x00, 0x00 },
516	[SOUND_MIXER_BASS]    = { 0x46, 0x47, 4, 0x00, 0x00 }
517};
518
519static int
520alsmix_init(struct snd_mixer *m)
521{
522	u_int32_t i, v;
523
524	for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
525		if (amt[i].bits) v |= 1 << i;
526	}
527	mix_setdevs(m, v);
528
529	for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
530		if (amt[i].iselect) v |= 1 << i;
531	}
532	mix_setrecdevs(m, v);
533	return 0;
534}
535
536static int
537alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
538{
539	struct sc_info *sc = mix_getdevinfo(m);
540	u_int32_t r, l, v, mask;
541
542	/* Fill upper n bits in mask with 1's */
543	mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits);
544
545	l = (left * mask / 100) & mask;
546	v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
547	als_mix_wr(sc, amt[dev].lreg, l | v);
548
549	if (amt[dev].rreg) {
550		r = (right * mask / 100) & mask;
551		v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
552		als_mix_wr(sc, amt[dev].rreg, r | v);
553	} else {
554		r = 0;
555	}
556
557	/* Zero gain does not mute channel from output, but this does. */
558	v = als_mix_rd(sc, SB16_OMASK);
559	if (l == 0 && r == 0) {
560		v &= ~amt[dev].oselect;
561	} else {
562		v |= amt[dev].oselect;
563	}
564	als_mix_wr(sc, SB16_OMASK, v);
565	return 0;
566}
567
568static int
569alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
570{
571	struct sc_info *sc = mix_getdevinfo(m);
572	u_int32_t i, l, r;
573
574	for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
575		if (src & (1 << i)) {
576			l |= amt[i].iselect;
577			r |= amt[i].iselect << 1;
578		}
579	}
580
581	als_mix_wr(sc, SB16_IMASK_L, l);
582	als_mix_wr(sc, SB16_IMASK_R, r);
583	return src;
584}
585
586static kobj_method_t als_mixer_methods[] = {
587	KOBJMETHOD(mixer_init,		alsmix_init),
588	KOBJMETHOD(mixer_set,		alsmix_set),
589	KOBJMETHOD(mixer_setrecsrc,	alsmix_setrecsrc),
590	{ 0, 0 }
591};
592MIXER_DECLARE(als_mixer);
593
594/* ------------------------------------------------------------------------- */
595/* Interrupt Handler */
596
597static void
598als_intr(void *p)
599{
600	struct sc_info *sc = (struct sc_info *)p;
601	u_int8_t intr, sb_status;
602
603	intr = als_intr_rd(sc);
604
605	if (intr & 0x80)
606		chn_intr(sc->pch.channel);
607
608	if (intr & 0x40)
609		chn_intr(sc->rch.channel);
610
611	/* ACK interrupt in PCI core */
612	als_intr_wr(sc, intr);
613
614	/* ACK interrupt in SB core */
615	sb_status = als_mix_rd(sc, IRQ_STAT);
616
617	if (sb_status & ALS_IRQ_STATUS8)
618		als_ack_read(sc, ALS_ESP_RD_STATUS8);
619	if (sb_status & ALS_IRQ_STATUS16)
620		als_ack_read(sc, ALS_ESP_RD_STATUS16);
621	if (sb_status & ALS_IRQ_MPUIN)
622		als_ack_read(sc, ALS_MIDI_DATA);
623	if (sb_status & ALS_IRQ_CR1E)
624		als_ack_read(sc, ALS_CR1E_ACK_PORT);
625	return;
626}
627
628/* ------------------------------------------------------------------------- */
629/* H/W initialization */
630
631static int
632als_init(struct sc_info *sc)
633{
634	u_int32_t i, v;
635
636	/* Reset Chip */
637	if (als_esp_reset(sc)) {
638		return 1;
639	}
640
641	/* Enable write on DMA_SETUP register */
642	v = als_mix_rd(sc, ALS_SB16_CONFIG);
643	als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
644
645	/* Select DMA0 */
646	als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
647
648	/* Disable write on DMA_SETUP register */
649	als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
650
651	/* Enable interrupts */
652	v  = als_gcr_rd(sc, ALS_GCR_MISC);
653	als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000);
654
655	/* Black out GCR DMA registers */
656	for (i = 0x91; i <= 0x96; i++) {
657		als_gcr_wr(sc, i, 0);
658	}
659
660	/* Emulation mode */
661	v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION);
662	als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v);
663	DEB(printf("GCR_DMA_EMULATION 0x%08x\n", v));
664	return 0;
665}
666
667static void
668als_uninit(struct sc_info *sc)
669{
670	/* Disable interrupts */
671	als_gcr_wr(sc, ALS_GCR_MISC, 0);
672}
673
674/* ------------------------------------------------------------------------- */
675/* Probe and attach card */
676
677static int
678als_pci_probe(device_t dev)
679{
680	if (pci_get_devid(dev) == ALS_PCI_ID0) {
681		device_set_desc(dev, "Avance Logic ALS4000");
682		return 0;
683	}
684	return ENXIO;
685}
686
687static void
688als_resource_free(device_t dev, struct sc_info *sc)
689{
690	if (sc->reg) {
691		bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
692		sc->reg = 0;
693	}
694	if (sc->ih) {
695		bus_teardown_intr(dev, sc->irq, sc->ih);
696		sc->ih = 0;
697	}
698	if (sc->irq) {
699		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
700		sc->irq = 0;
701	}
702	if (sc->parent_dmat) {
703		bus_dma_tag_destroy(sc->parent_dmat);
704		sc->parent_dmat = 0;
705	}
706}
707
708static int
709als_resource_grab(device_t dev, struct sc_info *sc)
710{
711	sc->regid = PCIR_MAPS;
712	sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0,
713				     ALS_CONFIG_SPACE_BYTES, RF_ACTIVE);
714	if (sc->reg == 0) {
715		device_printf(dev, "unable to allocate register space\n");
716		goto bad;
717	}
718	sc->st = rman_get_bustag(sc->reg);
719	sc->sh = rman_get_bushandle(sc->reg);
720
721	sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1,
722				     RF_ACTIVE | RF_SHAREABLE);
723	if (sc->irq == 0) {
724		device_printf(dev, "unable to allocate interrupt\n");
725		goto bad;
726	}
727
728	if (bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, als_intr,
729			   sc, &sc->ih)) {
730		device_printf(dev, "unable to setup interrupt\n");
731		goto bad;
732	}
733
734	if (bus_dma_tag_create(/*parent*/NULL,
735			       /*alignment*/2, /*boundary*/0,
736			       /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
737			       /*highaddr*/BUS_SPACE_MAXADDR,
738			       /*filter*/NULL, /*filterarg*/NULL,
739			       /*maxsize*/ALS_BUFFER_SIZE,
740			       /*nsegments*/1, /*maxsegz*/0x3ffff,
741			       /*flags*/0, &sc->parent_dmat) != 0) {
742		device_printf(dev, "unable to create dma tag\n");
743		goto bad;
744	}
745	return 0;
746 bad:
747	als_resource_free(dev, sc);
748	return ENXIO;
749}
750
751static int
752als_pci_attach(device_t dev)
753{
754	struct sc_info *sc;
755	u_int32_t data;
756	char status[SND_STATUSLEN];
757
758	if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) {
759		device_printf(dev, "cannot allocate softc\n");
760		return ENXIO;
761	}
762
763	bzero(sc, sizeof(*sc));
764	sc->dev = dev;
765
766	data = pci_read_config(dev, PCIR_COMMAND, 2);
767	data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
768	pci_write_config(dev, PCIR_COMMAND, data, 2);
769	/*
770	 * By default the power to the various components on the
771         * ALS4000 is entirely controlled by the pci powerstate.  We
772         * could attempt finer grained control by setting GCR6.31.
773	 */
774#if __FreeBSD_version > 500000
775	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
776		/* Reset the power state. */
777		device_printf(dev, "chip is in D%d power mode "
778			      "-- setting to D0\n", pci_get_powerstate(dev));
779		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
780	}
781#else
782	data = pci_read_config(dev, ALS_PCI_POWERREG, 2);
783	if ((data & 0x03) != 0) {
784		device_printf(dev, "chip is in D%d power mode "
785			      "-- setting to D0\n", data & 0x03);
786		data &= ~0x03;
787		pci_write_config(dev, ALS_PCI_POWERREG, data, 2);
788	}
789#endif
790
791	if (als_resource_grab(dev, sc)) {
792		device_printf(dev, "failed to allocate resources\n");
793		goto bad_attach;
794	}
795
796	if (als_init(sc)) {
797		device_printf(dev, "failed to initialize hardware\n");
798		goto bad_attach;
799	}
800
801	if (mixer_init(dev, &als_mixer_class, sc)) {
802		device_printf(dev, "failed to initialize mixer\n");
803		goto bad_attach;
804	}
805
806	if (pcm_register(dev, sc, 1, 1)) {
807		device_printf(dev, "failed to register pcm entries\n");
808		goto bad_attach;
809	}
810
811	pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
812	pcm_addchan(dev, PCMDIR_REC,  &alsrchan_class, sc);
813
814	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld",
815		 rman_get_start(sc->reg), rman_get_start(sc->irq));
816	pcm_setstatus(dev, status);
817	return 0;
818
819 bad_attach:
820	als_resource_free(dev, sc);
821	free(sc, M_DEVBUF);
822	return ENXIO;
823}
824
825static int
826als_pci_detach(device_t dev)
827{
828	struct sc_info *sc;
829	int r;
830
831	r = pcm_unregister(dev);
832	if (r)
833		return r;
834
835	sc = pcm_getdevinfo(dev);
836	als_uninit(sc);
837	als_resource_free(dev, sc);
838	free(sc, M_DEVBUF);
839	return 0;
840}
841
842static int
843als_pci_suspend(device_t dev)
844{
845	struct sc_info *sc = pcm_getdevinfo(dev);
846
847	sc->pch.dma_was_active = als_playback_stop(&sc->pch);
848	sc->rch.dma_was_active = als_capture_stop(&sc->rch);
849	als_uninit(sc);
850	return 0;
851}
852
853static int
854als_pci_resume(device_t dev)
855{
856	struct sc_info *sc = pcm_getdevinfo(dev);
857
858	if (als_init(sc) != 0) {
859		device_printf(dev, "unable to reinitialize the card\n");
860		return ENXIO;
861	}
862
863	if (mixer_reinit(dev) != 0) {
864		device_printf(dev, "unable to reinitialize the mixer\n");
865		return ENXIO;
866	}
867
868	if (sc->pch.dma_was_active) {
869		als_playback_start(&sc->pch);
870	}
871
872	if (sc->rch.dma_was_active) {
873		als_capture_start(&sc->rch);
874	}
875	return 0;
876}
877
878static device_method_t als_methods[] = {
879	/* Device interface */
880	DEVMETHOD(device_probe,		als_pci_probe),
881	DEVMETHOD(device_attach,	als_pci_attach),
882	DEVMETHOD(device_detach,	als_pci_detach),
883	DEVMETHOD(device_suspend,	als_pci_suspend),
884	DEVMETHOD(device_resume,	als_pci_resume),
885	{ 0, 0 }
886};
887
888static driver_t als_driver = {
889	"pcm",
890	als_methods,
891	sizeof(struct snddev_info),
892};
893
894DRIVER_MODULE(snd_als, pci, als_driver, pcm_devclass, 0, 0);
895MODULE_DEPEND(snd_als, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
896MODULE_VERSION(snd_als, 1);
897