1139749Simp/*-
275885Sorion * Copyright (c) 2001 Orion Hodson <oho@acm.org>
375885Sorion * All rights reserved.
475885Sorion *
575885Sorion * Redistribution and use in source and binary forms, with or without
675885Sorion * modification, are permitted provided that the following conditions
775885Sorion * are met:
875885Sorion * 1. Redistributions of source code must retain the above copyright
975885Sorion *    notice, this list of conditions and the following disclaimer.
1075885Sorion * 2. Redistributions in binary form must reproduce the above copyright
1175885Sorion *    notice, this list of conditions and the following disclaimer in the
1275885Sorion *    documentation and/or other materials provided with the distribution.
1375885Sorion *
1475885Sorion * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575885Sorion * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675885Sorion * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775885Sorion * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875885Sorion * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975885Sorion * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075885Sorion * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175885Sorion * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
2275885Sorion * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375885Sorion * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
2475885Sorion * SUCH DAMAGE.
2575885Sorion */
2675885Sorion
2775885Sorion/*
2875885Sorion * als4000.c - driver for the Avance Logic ALS 4000 chipset.
2975885Sorion *
30108533Sschweikh * The ALS4000 is effectively an SB16 with a PCI interface.
3175885Sorion *
3275885Sorion * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
3378362Scg * SB16 register descriptions.
3475885Sorion */
3575885Sorion
36193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
37193640Sariff#include "opt_snd.h"
38193640Sariff#endif
39193640Sariff
4075885Sorion#include <dev/sound/pcm/sound.h>
4175885Sorion#include <dev/sound/isa/sb.h>
4275885Sorion#include <dev/sound/pci/als4000.h>
4375885Sorion
44119287Simp#include <dev/pci/pcireg.h>
45119287Simp#include <dev/pci/pcivar.h>
4675885Sorion
4775885Sorion#include "mixer_if.h"
4875885Sorion
4982180ScgSND_DECLARE_FILE("$FreeBSD$");
5082180Scg
5175885Sorion/* Debugging macro's */
5275885Sorion#undef DEB
5375885Sorion#ifndef DEB
5475885Sorion#define DEB(x)  /* x */
5575885Sorion#endif /* DEB */
5675885Sorion
5784771Sorion#define ALS_DEFAULT_BUFSZ 16384
5884771Sorion
5975885Sorion/* ------------------------------------------------------------------------- */
6075885Sorion/* Structures */
6175885Sorion
6275885Sorionstruct sc_info;
6375885Sorion
6475885Sorionstruct sc_chinfo {
6575885Sorion	struct sc_info		*parent;
6675885Sorion	struct pcm_channel	*channel;
6775885Sorion	struct snd_dbuf		*buffer;
6875885Sorion	u_int32_t		format, speed, phys_buf, bps;
6975885Sorion	u_int32_t		dma_active:1, dma_was_active:1;
7075885Sorion	u_int8_t		gcr_fifo_status;
7175885Sorion	int			dir;
7275885Sorion};
7375885Sorion
7475885Sorionstruct sc_info {
7575885Sorion	device_t		dev;
7675885Sorion	bus_space_tag_t		st;
7775885Sorion	bus_space_handle_t	sh;
7875885Sorion	bus_dma_tag_t		parent_dmat;
7975885Sorion	struct resource		*reg, *irq;
8075885Sorion	int			regid, irqid;
8175885Sorion	void			*ih;
82148588Snetchild	struct mtx		*lock;
8384771Sorion
8484771Sorion	unsigned int		bufsz;
8575885Sorion	struct sc_chinfo	pch, rch;
8675885Sorion};
8775885Sorion
8875885Sorion/* Channel caps */
8975885Sorion
9075885Sorionstatic u_int32_t als_format[] = {
91193640Sariff        SND_FORMAT(AFMT_U8, 1, 0),
92193640Sariff        SND_FORMAT(AFMT_U8, 2, 0),
93193640Sariff        SND_FORMAT(AFMT_S16_LE, 1, 0),
94193640Sariff        SND_FORMAT(AFMT_S16_LE, 2, 0),
9575885Sorion        0
9675885Sorion};
9775885Sorion
98148588Snetchild/*
99148588Snetchild * I don't believe this rotten soundcard can do 48k, really,
100148588Snetchild * trust me.
101148588Snetchild */
102148588Snetchildstatic struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 };
10375885Sorion
10475885Sorion/* ------------------------------------------------------------------------- */
10575885Sorion/* Register Utilities */
10675885Sorion
10778362Scgstatic u_int32_t
10875885Sorionals_gcr_rd(struct sc_info *sc, int index)
10975885Sorion{
11075885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
11175885Sorion	return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA);
11275885Sorion}
11375885Sorion
11475885Sorionstatic void
11575885Sorionals_gcr_wr(struct sc_info *sc, int index, int data)
11675885Sorion{
11775885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
11875885Sorion	bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data);
11975885Sorion}
12075885Sorion
12175885Sorionstatic u_int8_t
12275885Sorionals_intr_rd(struct sc_info *sc)
12375885Sorion{
12475885Sorion	return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ);
12575885Sorion}
12675885Sorion
12775885Sorionstatic void
12875885Sorionals_intr_wr(struct sc_info *sc, u_int8_t data)
12975885Sorion{
13075885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data);
13175885Sorion}
13275885Sorion
13375885Sorionstatic u_int8_t
13475885Sorionals_mix_rd(struct sc_info *sc, u_int8_t index)
13575885Sorion{
13675885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
13775885Sorion	return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA);
13875885Sorion}
13975885Sorion
14075885Sorionstatic void
14175885Sorionals_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data)
14275885Sorion{
14375885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
14475885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data);
14575885Sorion}
14675885Sorion
14775885Sorionstatic void
14875885Sorionals_esp_wr(struct sc_info *sc, u_int8_t data)
14975885Sorion{
15075885Sorion	u_int32_t	tries, v;
15175885Sorion
15275885Sorion	tries = 1000;
15375885Sorion	do {
15475885Sorion		v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS);
15575885Sorion		if (~v & 0x80)
15675885Sorion			break;
15775885Sorion		DELAY(20);
15875885Sorion	} while (--tries != 0);
15975885Sorion
16078362Scg	if (tries == 0)
16175885Sorion		device_printf(sc->dev, "als_esp_wr timeout");
16278362Scg
16375885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
16475885Sorion}
16575885Sorion
16675885Sorionstatic int
16775885Sorionals_esp_reset(struct sc_info *sc)
16875885Sorion{
16975885Sorion	u_int32_t	tries, u, v;
17075885Sorion
17175885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1);
17275885Sorion	DELAY(10);
17375885Sorion	bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0);
17475885Sorion	DELAY(30);
17575885Sorion
17675885Sorion	tries = 1000;
17775885Sorion	do {
17875885Sorion		u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
17975885Sorion		if (u & 0x80) {
18075885Sorion			v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
18178362Scg			if (v == 0xaa)
18275885Sorion				return 0;
18378362Scg			else
18475885Sorion				break;
18575885Sorion		}
18675885Sorion		DELAY(20);
18775885Sorion	} while (--tries != 0);
18875885Sorion
18978362Scg	if (tries == 0)
19075885Sorion		device_printf(sc->dev, "als_esp_reset timeout");
19175885Sorion	return 1;
19275885Sorion}
19375885Sorion
19475885Sorionstatic u_int8_t
19575885Sorionals_ack_read(struct sc_info *sc, u_int8_t addr)
19675885Sorion{
19775885Sorion	u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr);
19875885Sorion	return r;
19975885Sorion}
20075885Sorion
20175885Sorion/* ------------------------------------------------------------------------- */
20275885Sorion/* Common pcm channel implementation */
20375885Sorion
20475885Sorionstatic void *
20578362Scgalschan_init(kobj_t obj, void *devinfo,
20675885Sorion	     struct snd_dbuf *b, struct pcm_channel *c, int dir)
20775885Sorion{
20875885Sorion	struct	sc_info	*sc = devinfo;
20975885Sorion	struct	sc_chinfo *ch;
21075885Sorion
211148588Snetchild	snd_mtxlock(sc->lock);
21275885Sorion	if (dir == PCMDIR_PLAY) {
21375885Sorion		ch = &sc->pch;
21475885Sorion		ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
21575885Sorion	} else {
21675885Sorion		ch = &sc->rch;
21775885Sorion		ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS;
21875885Sorion	}
21975885Sorion	ch->dir = dir;
22075885Sorion	ch->parent = sc;
22175885Sorion	ch->channel = c;
22275885Sorion	ch->bps = 1;
223193640Sariff	ch->format = SND_FORMAT(AFMT_U8, 1, 0);
22475885Sorion	ch->speed = DSP_DEFAULT_SPEED;
22575885Sorion	ch->buffer = b;
226148592Snetchild	snd_mtxunlock(sc->lock);
227148592Snetchild
228168847Sariff	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0)
22975885Sorion		return NULL;
230148592Snetchild
23175885Sorion	return ch;
23275885Sorion}
23375885Sorion
23475885Sorionstatic int
23578362Scgalschan_setformat(kobj_t obj, void *data, u_int32_t format)
23675885Sorion{
23775885Sorion	struct	sc_chinfo *ch = data;
23875885Sorion
23975885Sorion	ch->format = format;
24075885Sorion	return 0;
24175885Sorion}
24275885Sorion
243193640Sariffstatic u_int32_t
24475885Sorionalschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
24575885Sorion{
24675885Sorion	struct	sc_chinfo *ch = data, *other;
24775885Sorion	struct  sc_info *sc = ch->parent;
24875885Sorion
24975885Sorion	other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
25078362Scg
25175885Sorion	/* Deny request if other dma channel is active */
25275885Sorion	if (other->dma_active) {
25375885Sorion		ch->speed = other->speed;
25475885Sorion		return other->speed;
25575885Sorion	}
25675885Sorion
25775885Sorion	ch->speed = speed;
25875885Sorion	return speed;
25975885Sorion}
26075885Sorion
261193640Sariffstatic u_int32_t
26275885Sorionalschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
26375885Sorion{
26475885Sorion	struct	sc_chinfo *ch = data;
26584771Sorion	struct	sc_info *sc = ch->parent;
26675885Sorion
26784771Sorion	if (blocksize > sc->bufsz / 2) {
26884771Sorion		blocksize = sc->bufsz / 2;
26975885Sorion	}
27075885Sorion	sndbuf_resize(ch->buffer, 2, blocksize);
27182834Sorion	return blocksize;
27275885Sorion}
27375885Sorion
274193640Sariffstatic u_int32_t
27575885Sorionalschan_getptr(kobj_t obj, void *data)
27675885Sorion{
27775885Sorion	struct sc_chinfo *ch = data;
278150980Snetchild	struct sc_info *sc = ch->parent;
27975885Sorion	int32_t pos, sz;
28075885Sorion
281150980Snetchild	snd_mtxlock(sc->lock);
28275885Sorion	pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
283150980Snetchild	snd_mtxunlock(sc->lock);
28475885Sorion	sz  = sndbuf_getsize(ch->buffer);
28575885Sorion	return (2 * sz - pos - 1) % sz;
28675885Sorion}
28775885Sorion
28875885Sorionstatic struct pcmchan_caps*
28975885Sorionalschan_getcaps(kobj_t obj, void *data)
29075885Sorion{
29175885Sorion	return &als_caps;
29275885Sorion}
29375885Sorion
29475885Sorionstatic void
29575885Sorionals_set_speed(struct sc_chinfo *ch)
29675885Sorion{
29775885Sorion	struct sc_info *sc = ch->parent;
29875885Sorion	struct sc_chinfo *other;
29975885Sorion
30075885Sorion	other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
30175885Sorion	if (other->dma_active == 0) {
30275885Sorion		als_esp_wr(sc, ALS_ESP_SAMPLE_RATE);
30375885Sorion		als_esp_wr(sc, ch->speed >> 8);
30475885Sorion		als_esp_wr(sc, ch->speed & 0xff);
30575885Sorion	} else {
30678362Scg		DEB(printf("speed locked at %d (tried %d)\n",
30775885Sorion			   other->speed, ch->speed));
30875885Sorion	}
30975885Sorion}
31075885Sorion
31175885Sorion/* ------------------------------------------------------------------------- */
31275885Sorion/* Playback channel implementation */
31375885Sorion
31475885Sorion#define ALS_8BIT_CMD(x, y)  { (x), (y), DSP_DMA8,  DSP_CMD_DMAPAUSE_8  }
31575885Sorion#define ALS_16BIT_CMD(x, y) { (x), (y),	DSP_DMA16, DSP_CMD_DMAPAUSE_16 }
31675885Sorion
31775885Sorionstruct playback_command {
31875885Sorion	u_int32_t pcm_format;	/* newpcm format */
31975885Sorion	u_int8_t  format_val;	/* sb16 format value */
32075885Sorion	u_int8_t  dma_prog;	/* sb16 dma program */
32175885Sorion	u_int8_t  dma_stop;	/* sb16 stop register */
32275885Sorion} static const playback_cmds[] = {
323193640Sariff	ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 1, 0), DSP_MODE_U8MONO),
324193640Sariff	ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 2, 0), DSP_MODE_U8STEREO),
325193640Sariff	ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_MODE_S16MONO),
326193640Sariff	ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 2, 0), DSP_MODE_S16STEREO),
32775885Sorion};
32875885Sorion
32975885Sorionstatic const struct playback_command*
33075885Sorionals_get_playback_command(u_int32_t format)
33175885Sorion{
33275885Sorion	u_int32_t i, n;
33375885Sorion
33475885Sorion	n = sizeof(playback_cmds) / sizeof(playback_cmds[0]);
33575885Sorion	for (i = 0; i < n; i++) {
33675885Sorion		if (playback_cmds[i].pcm_format == format) {
33775885Sorion			return &playback_cmds[i];
33875885Sorion		}
33975885Sorion	}
34078362Scg	DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
34175885Sorion		   format));
34275885Sorion	return &playback_cmds[0];
34375885Sorion}
34475885Sorion
34575885Sorionstatic void
34675885Sorionals_playback_start(struct sc_chinfo *ch)
34775885Sorion{
34875885Sorion	const struct playback_command *p;
34975885Sorion	struct	sc_info *sc = ch->parent;
35075885Sorion	u_int32_t	buf, bufsz, count, dma_prog;
35175885Sorion
352111183Scognet	buf = sndbuf_getbufaddr(ch->buffer);
35375885Sorion	bufsz = sndbuf_getsize(ch->buffer);
35475885Sorion	count = bufsz / 2;
35575885Sorion	if (ch->format & AFMT_16BIT)
35675885Sorion		count /= 2;
35775885Sorion	count--;
35875885Sorion
35975885Sorion	als_esp_wr(sc, DSP_CMD_SPKON);
36075885Sorion	als_set_speed(ch);
36175885Sorion
36275885Sorion	als_gcr_wr(sc, ALS_GCR_DMA0_START, buf);
36375885Sorion	als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000);
36475885Sorion
36575885Sorion	p = als_get_playback_command(ch->format);
36675885Sorion	dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON;
36775885Sorion
36875885Sorion	als_esp_wr(sc, dma_prog);
36975885Sorion	als_esp_wr(sc, p->format_val);
37075885Sorion	als_esp_wr(sc, count & 0xff);
37175885Sorion	als_esp_wr(sc, count >> 8);
37278362Scg
37375885Sorion	ch->dma_active = 1;
37475885Sorion}
37575885Sorion
37675885Sorionstatic int
37775885Sorionals_playback_stop(struct sc_chinfo *ch)
37875885Sorion{
37975885Sorion	const struct playback_command *p;
38075885Sorion	struct sc_info *sc = ch->parent;
38175885Sorion	u_int32_t active;
38275885Sorion
38375885Sorion	active = ch->dma_active;
38475885Sorion	if (active) {
38575885Sorion		p = als_get_playback_command(ch->format);
38675885Sorion		als_esp_wr(sc, p->dma_stop);
38775885Sorion	}
38875885Sorion	ch->dma_active = 0;
38975885Sorion	return active;
39075885Sorion}
39175885Sorion
39275885Sorionstatic int
39375885Sorionalspchan_trigger(kobj_t obj, void *data, int go)
39475885Sorion{
39575885Sorion	struct	sc_chinfo *ch = data;
396150980Snetchild	struct sc_info *sc = ch->parent;
39775885Sorion
398170521Sariff	if (!PCMTRIG_COMMON(go))
399170521Sariff		return 0;
400170521Sariff
401150980Snetchild	snd_mtxlock(sc->lock);
40275885Sorion	switch(go) {
40375885Sorion	case PCMTRIG_START:
40475885Sorion		als_playback_start(ch);
40575885Sorion		break;
406170521Sariff	case PCMTRIG_STOP:
40775885Sorion	case PCMTRIG_ABORT:
40875885Sorion		als_playback_stop(ch);
40975885Sorion		break;
410170521Sariff	default:
411170521Sariff		break;
41275885Sorion	}
413150980Snetchild	snd_mtxunlock(sc->lock);
41475885Sorion	return 0;
41575885Sorion}
41675885Sorion
41775885Sorionstatic kobj_method_t alspchan_methods[] = {
41875885Sorion	KOBJMETHOD(channel_init,		alschan_init),
41975885Sorion	KOBJMETHOD(channel_setformat,		alschan_setformat),
42075885Sorion	KOBJMETHOD(channel_setspeed,		alschan_setspeed),
42175885Sorion	KOBJMETHOD(channel_setblocksize,	alschan_setblocksize),
42275885Sorion	KOBJMETHOD(channel_trigger,		alspchan_trigger),
42375885Sorion	KOBJMETHOD(channel_getptr,		alschan_getptr),
42475885Sorion	KOBJMETHOD(channel_getcaps,		alschan_getcaps),
425193640Sariff	KOBJMETHOD_END
42675885Sorion};
42775885SorionCHANNEL_DECLARE(alspchan);
42875885Sorion
42975885Sorion/* ------------------------------------------------------------------------- */
43075885Sorion/* Capture channel implementation */
43175885Sorion
43275885Sorionstatic u_int8_t
43375885Sorionals_get_fifo_format(struct sc_info *sc, u_int32_t format)
43475885Sorion{
43575885Sorion	switch (format) {
436193640Sariff	case SND_FORMAT(AFMT_U8, 1, 0):
43775885Sorion		return ALS_FIFO1_8BIT;
438193640Sariff	case SND_FORMAT(AFMT_U8, 2, 0):
43975885Sorion		return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
440193640Sariff	case SND_FORMAT(AFMT_S16_LE, 1, 0):
44175885Sorion		return ALS_FIFO1_SIGNED;
442193640Sariff	case SND_FORMAT(AFMT_S16_LE, 2, 0):
44375885Sorion		return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
44475885Sorion	}
44575885Sorion	device_printf(sc->dev, "format not found: 0x%08x\n", format);
44675885Sorion	return ALS_FIFO1_8BIT;
44775885Sorion}
44875885Sorion
44975885Sorionstatic void
45075885Sorionals_capture_start(struct sc_chinfo *ch)
45175885Sorion{
45275885Sorion	struct	sc_info *sc = ch->parent;
45375885Sorion	u_int32_t	buf, bufsz, count, dma_prog;
45475885Sorion
455111183Scognet	buf = sndbuf_getbufaddr(ch->buffer);
45675885Sorion	bufsz = sndbuf_getsize(ch->buffer);
45775885Sorion	count = bufsz / 2;
45875885Sorion	if (ch->format & AFMT_16BIT)
45975885Sorion		count /= 2;
46075885Sorion	count--;
46175885Sorion
46275885Sorion	als_esp_wr(sc, DSP_CMD_SPKON);
46375885Sorion	als_set_speed(ch);
46475885Sorion
46575885Sorion	als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf);
46675885Sorion	als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1));
46775885Sorion
46875885Sorion	als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff);
46975885Sorion	als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8);
47075885Sorion
47175885Sorion	dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format);
47275885Sorion	als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog);
47375885Sorion
47475885Sorion	ch->dma_active = 1;
47575885Sorion}
47675885Sorion
47775885Sorionstatic int
47875885Sorionals_capture_stop(struct sc_chinfo *ch)
47975885Sorion{
48075885Sorion	struct sc_info *sc = ch->parent;
48175885Sorion	u_int32_t active;
48275885Sorion
48375885Sorion	active = ch->dma_active;
48475885Sorion	if (active) {
48575885Sorion		als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP);
48675885Sorion	}
48775885Sorion	ch->dma_active = 0;
48875885Sorion	return active;
48975885Sorion}
49075885Sorion
49175885Sorionstatic int
49275885Sorionalsrchan_trigger(kobj_t obj, void *data, int go)
49375885Sorion{
49475885Sorion	struct	sc_chinfo *ch = data;
495150980Snetchild	struct sc_info *sc = ch->parent;
49675885Sorion
497150980Snetchild	snd_mtxlock(sc->lock);
49875885Sorion	switch(go) {
49975885Sorion	case PCMTRIG_START:
50075885Sorion		als_capture_start(ch);
50175885Sorion		break;
502170521Sariff	case PCMTRIG_STOP:
50375885Sorion	case PCMTRIG_ABORT:
50475885Sorion		als_capture_stop(ch);
50575885Sorion		break;
50675885Sorion	}
507150980Snetchild	snd_mtxunlock(sc->lock);
50875885Sorion	return 0;
50975885Sorion}
51075885Sorion
51175885Sorionstatic kobj_method_t alsrchan_methods[] = {
51275885Sorion	KOBJMETHOD(channel_init,		alschan_init),
51375885Sorion	KOBJMETHOD(channel_setformat,		alschan_setformat),
51475885Sorion	KOBJMETHOD(channel_setspeed,		alschan_setspeed),
51575885Sorion	KOBJMETHOD(channel_setblocksize,	alschan_setblocksize),
51675885Sorion	KOBJMETHOD(channel_trigger,		alsrchan_trigger),
51775885Sorion	KOBJMETHOD(channel_getptr,		alschan_getptr),
51875885Sorion	KOBJMETHOD(channel_getcaps,		alschan_getcaps),
519193640Sariff	KOBJMETHOD_END
52075885Sorion};
52175885SorionCHANNEL_DECLARE(alsrchan);
52275885Sorion
52375885Sorion/* ------------------------------------------------------------------------- */
52475885Sorion/* Mixer related */
52575885Sorion
52675885Sorion/*
52775885Sorion * ALS4000 has an sb16 mixer, with some additional controls that we do
52875885Sorion * not yet a means to support.
52975885Sorion */
53075885Sorion
53175885Sorionstruct sb16props {
53275885Sorion	u_int8_t lreg;
53375885Sorion	u_int8_t rreg;
53475885Sorion	u_int8_t bits;
53575885Sorion	u_int8_t oselect;
53675885Sorion	u_int8_t iselect; /* left input mask */
53775885Sorion} static const amt[SOUND_MIXER_NRDEVICES] = {
53875885Sorion	[SOUND_MIXER_VOLUME]  = { 0x30, 0x31, 5, 0x00, 0x00 },
53975885Sorion	[SOUND_MIXER_PCM]     = { 0x32, 0x33, 5, 0x00, 0x00 },
54075885Sorion	[SOUND_MIXER_SYNTH]   = { 0x34, 0x35, 5, 0x60, 0x40 },
54175885Sorion	[SOUND_MIXER_CD]      = { 0x36, 0x37, 5, 0x06, 0x04 },
54275885Sorion	[SOUND_MIXER_LINE]    = { 0x38, 0x39, 5, 0x18, 0x10 },
54375885Sorion	[SOUND_MIXER_MIC]     = { 0x3a, 0x00, 5, 0x01, 0x01 },
54475885Sorion	[SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 },
54575885Sorion	[SOUND_MIXER_IGAIN]   = { 0x3f, 0x40, 2, 0x00, 0x00 },
54675885Sorion	[SOUND_MIXER_OGAIN]   = { 0x41, 0x42, 2, 0x00, 0x00 },
54775885Sorion	/* The following have register values but no h/w implementation */
54875885Sorion	[SOUND_MIXER_TREBLE]  = { 0x44, 0x45, 4, 0x00, 0x00 },
54975885Sorion	[SOUND_MIXER_BASS]    = { 0x46, 0x47, 4, 0x00, 0x00 }
55075885Sorion};
55175885Sorion
55275885Sorionstatic int
55378362Scgalsmix_init(struct snd_mixer *m)
55475885Sorion{
55575885Sorion	u_int32_t i, v;
55675885Sorion
55775885Sorion	for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
55875885Sorion		if (amt[i].bits) v |= 1 << i;
55975885Sorion	}
56075885Sorion	mix_setdevs(m, v);
56175885Sorion
56275885Sorion	for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
56375885Sorion		if (amt[i].iselect) v |= 1 << i;
56478362Scg	}
56575885Sorion	mix_setrecdevs(m, v);
56675885Sorion	return 0;
56775885Sorion}
56875885Sorion
56975885Sorionstatic int
57075885Sorionalsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
57175885Sorion{
57275885Sorion	struct sc_info *sc = mix_getdevinfo(m);
57375885Sorion	u_int32_t r, l, v, mask;
57475885Sorion
57575885Sorion	/* Fill upper n bits in mask with 1's */
57675885Sorion	mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits);
57775885Sorion
57875885Sorion	l = (left * mask / 100) & mask;
57975885Sorion	v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
58075885Sorion	als_mix_wr(sc, amt[dev].lreg, l | v);
58178362Scg
58275885Sorion	if (amt[dev].rreg) {
58375885Sorion		r = (right * mask / 100) & mask;
58475885Sorion		v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
58575885Sorion		als_mix_wr(sc, amt[dev].rreg, r | v);
58675885Sorion	} else {
58775885Sorion		r = 0;
58875885Sorion	}
58978362Scg
59075885Sorion	/* Zero gain does not mute channel from output, but this does. */
59175885Sorion	v = als_mix_rd(sc, SB16_OMASK);
59275885Sorion	if (l == 0 && r == 0) {
59375885Sorion		v &= ~amt[dev].oselect;
59475885Sorion	} else {
59575885Sorion		v |= amt[dev].oselect;
59675885Sorion	}
59775885Sorion	als_mix_wr(sc, SB16_OMASK, v);
59875885Sorion	return 0;
59975885Sorion}
60075885Sorion
601193640Sariffstatic u_int32_t
60275885Sorionalsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
60375885Sorion{
60475885Sorion	struct sc_info *sc = mix_getdevinfo(m);
605152151Sariff	u_int32_t i, l, r;
60675885Sorion
60775885Sorion	for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
60875885Sorion		if (src & (1 << i)) {
609152151Sariff			if (amt[i].iselect == 1) {	/* microphone */
610152151Sariff				l |= amt[i].iselect;
611152151Sariff				r |= amt[i].iselect;
612152151Sariff			} else {
613152151Sariff				l |= amt[i].iselect;
614152151Sariff				r |= amt[i].iselect >> 1;
615152151Sariff			}
61675885Sorion		}
61775885Sorion	}
61875885Sorion
619152151Sariff	als_mix_wr(sc, SB16_IMASK_L, l);
620152151Sariff	als_mix_wr(sc, SB16_IMASK_R, r);
62175885Sorion	return src;
62275885Sorion}
62375885Sorion
62475885Sorionstatic kobj_method_t als_mixer_methods[] = {
62575885Sorion	KOBJMETHOD(mixer_init,		alsmix_init),
62675885Sorion	KOBJMETHOD(mixer_set,		alsmix_set),
62775885Sorion	KOBJMETHOD(mixer_setrecsrc,	alsmix_setrecsrc),
628193640Sariff	KOBJMETHOD_END
62975885Sorion};
63075885SorionMIXER_DECLARE(als_mixer);
63175885Sorion
63275885Sorion/* ------------------------------------------------------------------------- */
63375885Sorion/* Interrupt Handler */
63475885Sorion
63575885Sorionstatic void
63675885Sorionals_intr(void *p)
63775885Sorion{
63875885Sorion	struct sc_info *sc = (struct sc_info *)p;
63975885Sorion	u_int8_t intr, sb_status;
64078362Scg
641148588Snetchild	snd_mtxlock(sc->lock);
64275885Sorion	intr = als_intr_rd(sc);
64378362Scg
644148588Snetchild	if (intr & 0x80) {
645148588Snetchild		snd_mtxunlock(sc->lock);
64675885Sorion		chn_intr(sc->pch.channel);
647148588Snetchild		snd_mtxlock(sc->lock);
648148588Snetchild	}
64978362Scg
650148588Snetchild	if (intr & 0x40) {
651148588Snetchild		snd_mtxunlock(sc->lock);
65275885Sorion		chn_intr(sc->rch.channel);
653148588Snetchild		snd_mtxlock(sc->lock);
654148588Snetchild	}
65578362Scg
65675885Sorion	/* ACK interrupt in PCI core */
65775885Sorion	als_intr_wr(sc, intr);
65875885Sorion
65975885Sorion	/* ACK interrupt in SB core */
66075885Sorion	sb_status = als_mix_rd(sc, IRQ_STAT);
66175885Sorion
66275885Sorion	if (sb_status & ALS_IRQ_STATUS8)
66375885Sorion		als_ack_read(sc, ALS_ESP_RD_STATUS8);
66475885Sorion	if (sb_status & ALS_IRQ_STATUS16)
66575885Sorion		als_ack_read(sc, ALS_ESP_RD_STATUS16);
66675885Sorion	if (sb_status & ALS_IRQ_MPUIN)
66775885Sorion		als_ack_read(sc, ALS_MIDI_DATA);
66875885Sorion	if (sb_status & ALS_IRQ_CR1E)
66975885Sorion		als_ack_read(sc, ALS_CR1E_ACK_PORT);
670148588Snetchild
671148588Snetchild	snd_mtxunlock(sc->lock);
67275885Sorion	return;
67378362Scg}
67475885Sorion
67575885Sorion/* ------------------------------------------------------------------------- */
67675885Sorion/* H/W initialization */
67775885Sorion
67875885Sorionstatic int
67975885Sorionals_init(struct sc_info *sc)
68075885Sorion{
68175885Sorion	u_int32_t i, v;
68275885Sorion
68375885Sorion	/* Reset Chip */
68475885Sorion	if (als_esp_reset(sc)) {
68575885Sorion		return 1;
68675885Sorion	}
68775885Sorion
68875885Sorion	/* Enable write on DMA_SETUP register */
68975885Sorion	v = als_mix_rd(sc, ALS_SB16_CONFIG);
69075885Sorion	als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
69178362Scg
69275885Sorion	/* Select DMA0 */
69375885Sorion	als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
69478362Scg
69575885Sorion	/* Disable write on DMA_SETUP register */
69675885Sorion	als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
69775885Sorion
69875885Sorion	/* Enable interrupts */
69975885Sorion	v  = als_gcr_rd(sc, ALS_GCR_MISC);
70075885Sorion	als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000);
70175885Sorion
70275885Sorion	/* Black out GCR DMA registers */
70375885Sorion	for (i = 0x91; i <= 0x96; i++) {
70475885Sorion		als_gcr_wr(sc, i, 0);
70575885Sorion	}
70675885Sorion
70775885Sorion	/* Emulation mode */
70875885Sorion	v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION);
70975885Sorion	als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v);
71075885Sorion	DEB(printf("GCR_DMA_EMULATION 0x%08x\n", v));
71175885Sorion	return 0;
71275885Sorion}
71375885Sorion
71475885Sorionstatic void
71575885Sorionals_uninit(struct sc_info *sc)
71675885Sorion{
71775885Sorion	/* Disable interrupts */
71875885Sorion	als_gcr_wr(sc, ALS_GCR_MISC, 0);
71975885Sorion}
72075885Sorion
72175885Sorion/* ------------------------------------------------------------------------- */
72275885Sorion/* Probe and attach card */
72375885Sorion
72475885Sorionstatic int
72575885Sorionals_pci_probe(device_t dev)
72675885Sorion{
72775885Sorion	if (pci_get_devid(dev) == ALS_PCI_ID0) {
72875885Sorion		device_set_desc(dev, "Avance Logic ALS4000");
729142890Simp		return BUS_PROBE_DEFAULT;
73075885Sorion	}
73175885Sorion	return ENXIO;
73275885Sorion}
73375885Sorion
73475885Sorionstatic void
73575885Sorionals_resource_free(device_t dev, struct sc_info *sc)
73675885Sorion{
73775885Sorion	if (sc->reg) {
73875885Sorion		bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
73975885Sorion		sc->reg = 0;
74075885Sorion	}
74175885Sorion	if (sc->ih) {
74275885Sorion		bus_teardown_intr(dev, sc->irq, sc->ih);
74375885Sorion		sc->ih = 0;
74475885Sorion	}
74575885Sorion	if (sc->irq) {
74675885Sorion		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
74775885Sorion		sc->irq = 0;
74875885Sorion	}
74975885Sorion	if (sc->parent_dmat) {
75075885Sorion		bus_dma_tag_destroy(sc->parent_dmat);
75175885Sorion		sc->parent_dmat = 0;
75275885Sorion	}
753148588Snetchild	if (sc->lock) {
754148588Snetchild		snd_mtxfree(sc->lock);
755148588Snetchild		sc->lock = NULL;
756148588Snetchild	}
75775885Sorion}
75875885Sorion
75975885Sorionstatic int
76075885Sorionals_resource_grab(device_t dev, struct sc_info *sc)
76175885Sorion{
762119690Sjhb	sc->regid = PCIR_BAR(0);
76375885Sorion	sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0,
76475885Sorion				     ALS_CONFIG_SPACE_BYTES, RF_ACTIVE);
76575885Sorion	if (sc->reg == 0) {
76675885Sorion		device_printf(dev, "unable to allocate register space\n");
76775885Sorion		goto bad;
76875885Sorion	}
76975885Sorion	sc->st = rman_get_bustag(sc->reg);
77075885Sorion	sc->sh = rman_get_bushandle(sc->reg);
77175885Sorion
772127135Snjl	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
773127135Snjl					 RF_ACTIVE | RF_SHAREABLE);
77475885Sorion	if (sc->irq == 0) {
77575885Sorion		device_printf(dev, "unable to allocate interrupt\n");
77675885Sorion		goto bad;
77775885Sorion	}
77875885Sorion
779148588Snetchild	if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
78075885Sorion			   sc, &sc->ih)) {
78175885Sorion		device_printf(dev, "unable to setup interrupt\n");
78275885Sorion		goto bad;
78375885Sorion	}
78475885Sorion
78584771Sorion	sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536);
78684771Sorion
787166904Snetchild	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev),
78875885Sorion			       /*alignment*/2, /*boundary*/0,
78975885Sorion			       /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
79075885Sorion			       /*highaddr*/BUS_SPACE_MAXADDR,
79175885Sorion			       /*filter*/NULL, /*filterarg*/NULL,
79284771Sorion			       /*maxsize*/sc->bufsz,
79375885Sorion			       /*nsegments*/1, /*maxsegz*/0x3ffff,
794148588Snetchild			       /*flags*/0, /*lockfunc*/NULL,
795148588Snetchild			       /*lockarg*/NULL, &sc->parent_dmat) != 0) {
79675885Sorion		device_printf(dev, "unable to create dma tag\n");
79775885Sorion		goto bad;
79875885Sorion	}
79975885Sorion	return 0;
80075885Sorion bad:
80175885Sorion	als_resource_free(dev, sc);
80275885Sorion	return ENXIO;
80375885Sorion}
80475885Sorion
80575885Sorionstatic int
80675885Sorionals_pci_attach(device_t dev)
80775885Sorion{
80875885Sorion	struct sc_info *sc;
80975885Sorion	char status[SND_STATUSLEN];
81075885Sorion
811170873Sariff	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
812167608Sariff	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_als4000 softc");
81375885Sorion	sc->dev = dev;
81475885Sorion
815254306Sscottl	pci_enable_busmaster(dev);
81675885Sorion	/*
81775885Sorion	 * By default the power to the various components on the
81875885Sorion         * ALS4000 is entirely controlled by the pci powerstate.  We
81975885Sorion         * could attempt finer grained control by setting GCR6.31.
82075885Sorion	 */
82175885Sorion#if __FreeBSD_version > 500000
82275885Sorion	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
82375885Sorion		/* Reset the power state. */
82475885Sorion		device_printf(dev, "chip is in D%d power mode "
82575885Sorion			      "-- setting to D0\n", pci_get_powerstate(dev));
82675885Sorion		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
82775885Sorion	}
82875885Sorion#else
82975885Sorion	data = pci_read_config(dev, ALS_PCI_POWERREG, 2);
83075885Sorion	if ((data & 0x03) != 0) {
83175885Sorion		device_printf(dev, "chip is in D%d power mode "
83275885Sorion			      "-- setting to D0\n", data & 0x03);
83375885Sorion		data &= ~0x03;
83475885Sorion		pci_write_config(dev, ALS_PCI_POWERREG, data, 2);
83575885Sorion	}
83675885Sorion#endif
83775885Sorion
83875885Sorion	if (als_resource_grab(dev, sc)) {
83975885Sorion		device_printf(dev, "failed to allocate resources\n");
84075885Sorion		goto bad_attach;
84175885Sorion	}
84275885Sorion
84375885Sorion	if (als_init(sc)) {
84475885Sorion		device_printf(dev, "failed to initialize hardware\n");
84575885Sorion		goto bad_attach;
84675885Sorion	}
84775885Sorion
84875885Sorion	if (mixer_init(dev, &als_mixer_class, sc)) {
84975885Sorion		device_printf(dev, "failed to initialize mixer\n");
85075885Sorion		goto bad_attach;
85175885Sorion	}
85275885Sorion
85375885Sorion	if (pcm_register(dev, sc, 1, 1)) {
85475885Sorion		device_printf(dev, "failed to register pcm entries\n");
85575885Sorion		goto bad_attach;
85675885Sorion	}
85775885Sorion
85875885Sorion	pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
85975885Sorion	pcm_addchan(dev, PCMDIR_REC,  &alsrchan_class, sc);
86075885Sorion
861126695Smatk	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
862126695Smatk		 rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000));
86375885Sorion	pcm_setstatus(dev, status);
86475885Sorion	return 0;
86575885Sorion
86675885Sorion bad_attach:
86775885Sorion	als_resource_free(dev, sc);
86875885Sorion	free(sc, M_DEVBUF);
86975885Sorion	return ENXIO;
87075885Sorion}
87175885Sorion
87275885Sorionstatic int
87375885Sorionals_pci_detach(device_t dev)
87475885Sorion{
87575885Sorion	struct sc_info *sc;
87675885Sorion	int r;
87775885Sorion
87875885Sorion	r = pcm_unregister(dev);
87975885Sorion	if (r)
88075885Sorion		return r;
88175885Sorion
88275885Sorion	sc = pcm_getdevinfo(dev);
88375885Sorion	als_uninit(sc);
88475885Sorion	als_resource_free(dev, sc);
88575885Sorion	free(sc, M_DEVBUF);
88675885Sorion	return 0;
88775885Sorion}
88875885Sorion
88975885Sorionstatic int
89075885Sorionals_pci_suspend(device_t dev)
89175885Sorion{
89275885Sorion	struct sc_info *sc = pcm_getdevinfo(dev);
89375885Sorion
894150980Snetchild	snd_mtxlock(sc->lock);
89575885Sorion	sc->pch.dma_was_active = als_playback_stop(&sc->pch);
89675885Sorion	sc->rch.dma_was_active = als_capture_stop(&sc->rch);
89775885Sorion	als_uninit(sc);
898150980Snetchild	snd_mtxunlock(sc->lock);
89975885Sorion	return 0;
90075885Sorion}
90175885Sorion
90275885Sorionstatic int
90375885Sorionals_pci_resume(device_t dev)
90475885Sorion{
90575885Sorion	struct sc_info *sc = pcm_getdevinfo(dev);
90678362Scg
907148588Snetchild
908150980Snetchild	snd_mtxlock(sc->lock);
90975885Sorion	if (als_init(sc) != 0) {
91075885Sorion		device_printf(dev, "unable to reinitialize the card\n");
911150980Snetchild		snd_mtxunlock(sc->lock);
91275885Sorion		return ENXIO;
91378362Scg	}
91475885Sorion
91575885Sorion	if (mixer_reinit(dev) != 0) {
91675885Sorion		device_printf(dev, "unable to reinitialize the mixer\n");
917150980Snetchild		snd_mtxunlock(sc->lock);
91875885Sorion		return ENXIO;
91975885Sorion	}
92075885Sorion
92175885Sorion	if (sc->pch.dma_was_active) {
92275885Sorion		als_playback_start(&sc->pch);
92375885Sorion	}
92475885Sorion
92575885Sorion	if (sc->rch.dma_was_active) {
92675885Sorion		als_capture_start(&sc->rch);
92775885Sorion	}
928150980Snetchild	snd_mtxunlock(sc->lock);
929148588Snetchild
93075885Sorion	return 0;
93175885Sorion}
93275885Sorion
93375885Sorionstatic device_method_t als_methods[] = {
93475885Sorion	/* Device interface */
93575885Sorion	DEVMETHOD(device_probe,		als_pci_probe),
93675885Sorion	DEVMETHOD(device_attach,	als_pci_attach),
93775885Sorion	DEVMETHOD(device_detach,	als_pci_detach),
93875885Sorion	DEVMETHOD(device_suspend,	als_pci_suspend),
93975885Sorion	DEVMETHOD(device_resume,	als_pci_resume),
94075885Sorion	{ 0, 0 }
94175885Sorion};
94275885Sorion
94375885Sorionstatic driver_t als_driver = {
94475885Sorion	"pcm",
94575885Sorion	als_methods,
94682180Scg	PCM_SOFTC_SIZE,
94775885Sorion};
94875885Sorion
94985442SjhbDRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0);
950132236StanimuraMODULE_DEPEND(snd_als4000, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
95185442SjhbMODULE_VERSION(snd_als4000, 1);
952