via82c686.c revision 64439
164439Scg/*
264439Scg * Copyright (c) 2000 David Jones <dej@ox.org>
364439Scg * All rights reserved.
464439Scg *
564439Scg * Redistribution and use in source and binary forms, with or without
664439Scg * modification, are permitted provided that the following conditions
764439Scg * are met:
864439Scg * 1. Redistributions of source code must retain the above copyright
964439Scg *    notice, this list of conditions and the following disclaimer.
1064439Scg * 2. Redistributions in binary form must reproduce the above copyright
1164439Scg *    notice, this list of conditions and the following disclaimer in the
1264439Scg *    documentation and/or other materials provided with the distribution.
1364439Scg *
1464439Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1564439Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1664439Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1764439Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1864439Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1964439Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2064439Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2164439Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2264439Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2364439Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2464439Scg * SUCH DAMAGE.
2564439Scg *
2664439Scg * $FreeBSD: head/sys/dev/sound/pci/via82c686.c 64439 2000-08-09 00:55:55Z cg $
2764439Scg */
2864439Scg
2964439Scg#include <dev/sound/pcm/sound.h>
3064439Scg#include <dev/sound/pcm/ac97.h>
3164439Scg
3264439Scg#include <pci/pcireg.h>
3364439Scg#include <pci/pcivar.h>
3464439Scg#include <sys/sysctl.h>
3564439Scg
3664439Scg#include <dev/sound/pci/via82c686.h>
3764439Scg
3864439Scg#define VIA_PCI_ID 0x30581106
3964439Scg#define	NSEGS		16	/* Number of segments in SGD table */
4064439Scg
4164439Scg#define SEGS_PER_CHAN	(NSEGS/2)
4264439Scg
4364439Scg#undef DEB
4464439Scg#define DEB(x)
4564439Scg
4664439Scgstruct via_info;
4764439Scg
4864439Scgstruct via_chinfo {
4964439Scg	struct via_info *parent;
5064439Scg	pcm_channel *channel;
5164439Scg	snd_dbuf *buffer;
5264439Scg	int dir;
5364439Scg};
5464439Scg
5564439Scgstruct via_info {
5664439Scg	bus_space_tag_t st;
5764439Scg	bus_space_handle_t sh;
5864439Scg	bus_dma_tag_t	parent_dmat;
5964439Scg	bus_dma_tag_t	sgd_dmat;
6064439Scg
6164439Scg	struct via_chinfo pch, rch;
6264439Scg	struct via_dma_op *sgd_table;
6364439Scg	u_int16_t	codec_caps;
6464439Scg};
6564439Scg
6664439Scgstatic u_int32_t via_rd(struct via_info *via, int regno, int size);
6764439Scgstatic void via_wr(struct via_info *, int regno, u_int32_t data, int size);
6864439Scg
6964439Scgint via_waitready_codec(struct via_info *via);
7064439Scgint via_waitvalid_codec(struct via_info *via);
7164439Scgu_int32_t via_read_codec(void *addr, int reg);
7264439Scgvoid via_write_codec(void *addr, int reg, u_int32_t val);
7364439Scg
7464439Scgstatic void via_intr(void *);
7564439Scgbus_dmamap_callback_t dma_cb;
7664439Scg
7764439Scg
7864439Scg/* channel interface */
7964439Scgstatic void *viachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
8064439Scgstatic int viachan_setdir(void *data, int dir);
8164439Scgstatic int viachan_setformat(void *data, u_int32_t format);
8264439Scgstatic int viachan_setspeed(void *data, u_int32_t speed);
8364439Scgstatic int viachan_setblocksize(void *data, u_int32_t blocksize);
8464439Scgstatic int viachan_trigger(void *data, int go);
8564439Scgstatic int viachan_getptr(void *data);
8664439Scgstatic pcmchan_caps *viachan_getcaps(void *data);
8764439Scg
8864439Scgstatic pcmchan_caps via_playcaps = {
8964439Scg	4000, 48000,
9064439Scg	AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
9164439Scg	AFMT_STEREO | AFMT_S16_LE
9264439Scg};
9364439Scg
9464439Scgstatic pcmchan_caps via_reccaps = {
9564439Scg	4000, 48000,
9664439Scg	AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
9764439Scg	AFMT_STEREO | AFMT_S16_LE
9864439Scg};
9964439Scg
10064439Scgstatic pcm_channel via_chantemplate = {
10164439Scg	viachan_init,
10264439Scg	viachan_setdir,
10364439Scg	viachan_setformat,
10464439Scg	viachan_setspeed,
10564439Scg	viachan_setblocksize,
10664439Scg	viachan_trigger,
10764439Scg	viachan_getptr,
10864439Scg	viachan_getcaps,
10964439Scg};
11064439Scg
11164439Scg
11264439Scg/*
11364439Scg *  Probe and attach the card
11464439Scg */
11564439Scgstatic int
11664439Scgvia_probe(device_t dev)
11764439Scg{
11864439Scg	if (pci_get_devid(dev) == VIA_PCI_ID) {
11964439Scg	    device_set_desc(dev, "VIA VT82C686A AC'97 Audio");
12064439Scg	    return 0;
12164439Scg	}
12264439Scg	return ENXIO;
12364439Scg}
12464439Scg
12564439Scg
12664439Scgvoid dma_cb(void *p, bus_dma_segment_t *bds, int a, int b)
12764439Scg{
12864439Scg}
12964439Scg
13064439Scg
13164439Scgstatic int
13264439Scgvia_attach(device_t dev)
13364439Scg{
13464439Scg	snddev_info	*d;
13564439Scg	struct via_info *via = 0;
13664439Scg	struct ac97_info *codec;
13764439Scg	char		status[SND_STATUSLEN];
13864439Scg
13964439Scg	u_int32_t	data;
14064439Scg	struct resource *reg = 0;
14164439Scg	int		regid;
14264439Scg	struct resource *irq = 0;
14364439Scg	void		*ih = 0;
14464439Scg	int		irqid;
14564439Scg
14664439Scg	u_int16_t	v;
14764439Scg	bus_dmamap_t	sgd_dma_map;
14864439Scg
14964439Scg	d = device_get_softc(dev);
15064439Scg	if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT)) == NULL) {
15164439Scg		device_printf(dev, "cannot allocate softc\n");
15264439Scg		return ENXIO;
15364439Scg	}
15464439Scg	bzero(via, sizeof *via);
15564439Scg
15664439Scg	/* Get resources */
15764439Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
15864439Scg	data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN);
15964439Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
16064439Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
16164439Scg
16264439Scg	pci_write_config(dev, VIA_PCICONF_MISC,
16364439Scg		VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD |
16464439Scg		VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1);
16564439Scg
16664439Scg	regid = PCIR_MAPS;
16764439Scg	reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &regid,
16864439Scg		0, ~0, 1, RF_ACTIVE);
16964439Scg	if (!reg) {
17064439Scg		device_printf(dev, "via: Cannot allocate bus resource.");
17164439Scg		goto bad;
17264439Scg	}
17364439Scg	via->st = rman_get_bustag(reg);
17464439Scg	via->sh = rman_get_bushandle(reg);
17564439Scg
17664439Scg	irqid = 0;
17764439Scg	irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
17864439Scg		0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
17964439Scg	if (!irq
18064439Scg	    || bus_setup_intr(dev, irq, INTR_TYPE_TTY, via_intr, via, &ih)){
18164439Scg		device_printf(dev, "unable to map interrupt\n");
18264439Scg		goto bad;
18364439Scg	}
18464439Scg
18564439Scg	via_wr(via, VIA_PLAY_MODE,
18664439Scg		VIA_RPMODE_AUTOSTART |
18764439Scg		VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1);
18864439Scg	via_wr(via, VIA_RECORD_MODE,
18964439Scg		VIA_RPMODE_AUTOSTART |
19064439Scg		VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1);
19164439Scg
19264439Scg	codec = ac97_create(dev, via, NULL,
19364439Scg		via_read_codec, via_write_codec);
19464439Scg	if (!codec) goto bad;
19564439Scg
19664439Scg	mixer_init(d, &ac97_mixer, codec);
19764439Scg
19864439Scg	/*
19964439Scg	 *  The mixer init resets the codec.  So enabling VRA must be done
20064439Scg	 *  afterwards.
20164439Scg	 */
20264439Scg	v = via_read_codec(via, AC97_REG_EXT_AUDIO_ID);
20364439Scg	v &= (AC97_ENAB_VRA | AC97_ENAB_MICVRA);
20464439Scg	via_write_codec(via, AC97_REG_EXT_AUDIO_STAT, v);
20564439Scg	via->codec_caps = v;
20664439Scg	{
20764439Scg	v = via_read_codec(via, AC97_REG_EXT_AUDIO_STAT);
20864439Scg	DEB(printf("init: codec stat: %d\n", v));
20964439Scg	}
21064439Scg
21164439Scg	if (!(v & AC97_CODEC_DOES_VRA)) {
21264439Scg		/* no VRA => can do only 48 kbps */
21364439Scg		via_playcaps.minspeed = 48000;
21464439Scg		via_reccaps.minspeed = 48000;
21564439Scg	}
21664439Scg
21764439Scg	/* DMA tag for buffers */
21864439Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
21964439Scg		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
22064439Scg		/*highaddr*/BUS_SPACE_MAXADDR,
22164439Scg		/*filter*/NULL, /*filterarg*/NULL,
22264439Scg		/*maxsize*/VIA_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
22364439Scg		/*flags*/0, &via->parent_dmat) != 0) {
22464439Scg		device_printf(dev, "unable to create dma tag\n");
22564439Scg		goto bad;
22664439Scg	}
22764439Scg
22864439Scg	/*
22964439Scg	 *  DMA tag for SGD table.  The 686 uses scatter/gather DMA and
23064439Scg	 *  requires a list in memory of work to do.  We need only 16 bytes
23164439Scg	 *  for this list, and it is wasteful to allocate 16K.
23264439Scg	 */
23364439Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
23464439Scg		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
23564439Scg		/*highaddr*/BUS_SPACE_MAXADDR,
23664439Scg		/*filter*/NULL, /*filterarg*/NULL,
23764439Scg		/*maxsize*/NSEGS * sizeof(struct via_dma_op),
23864439Scg		/*nsegments*/1, /*maxsegz*/0x3ffff,
23964439Scg		/*flags*/0, &via->sgd_dmat) != 0) {
24064439Scg		device_printf(dev, "unable to create dma tag\n");
24164439Scg		goto bad;
24264439Scg	}
24364439Scg
24464439Scg	if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table,
24564439Scg		BUS_DMA_NOWAIT, &sgd_dma_map) == -1) goto bad;
24664439Scg	if (bus_dmamap_load(via->sgd_dmat, sgd_dma_map, via->sgd_table,
24764439Scg		NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) goto bad;
24864439Scg
24964439Scg	snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld",
25064439Scg		rman_get_start(reg), rman_get_start(irq));
25164439Scg
25264439Scg	/* Register */
25364439Scg	if (pcm_register(dev, via, 1, 1)) goto bad;
25464439Scg	pcm_addchan(dev, PCMDIR_PLAY, &via_chantemplate, via);
25564439Scg	pcm_addchan(dev, PCMDIR_REC, &via_chantemplate, via);
25664439Scg	pcm_setstatus(dev, status);
25764439Scg	return 0;
25864439Scgbad:
25964439Scg	if (via) free(via, M_DEVBUF);
26064439Scg	bus_release_resource(dev, SYS_RES_IOPORT, regid, reg);
26164439Scg	if (ih) bus_teardown_intr(dev, irq, ih);
26264439Scg	if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
26364439Scg	return ENXIO;
26464439Scg}
26564439Scg
26664439Scg
26764439Scgstatic device_method_t via_methods[] = {
26864439Scg	DEVMETHOD(device_probe,		via_probe),
26964439Scg	DEVMETHOD(device_attach,	via_attach),
27064439Scg	{ 0, 0}
27164439Scg};
27264439Scg
27364439Scgstatic driver_t via_driver = {
27464439Scg	"pcm",
27564439Scg	via_methods,
27664439Scg	sizeof(snddev_info),
27764439Scg};
27864439Scg
27964439Scgstatic devclass_t pcm_devclass;
28064439Scg
28164439ScgDRIVER_MODULE(via, pci, via_driver, pcm_devclass, 0, 0);
28264439ScgMODULE_DEPEND(via, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
28364439ScgMODULE_VERSION(via, 1);
28464439Scg
28564439Scg
28664439Scgstatic u_int32_t
28764439Scgvia_rd(struct via_info *via, int regno, int size)
28864439Scg{
28964439Scg
29064439Scg	switch (size) {
29164439Scg	case 1:
29264439Scg		return bus_space_read_1(via->st, via->sh, regno);
29364439Scg	case 2:
29464439Scg		return bus_space_read_2(via->st, via->sh, regno);
29564439Scg	case 4:
29664439Scg		return bus_space_read_4(via->st, via->sh, regno);
29764439Scg	default:
29864439Scg		return 0xFFFFFFFF;
29964439Scg	}
30064439Scg}
30164439Scg
30264439Scg
30364439Scgstatic void
30464439Scgvia_wr(struct via_info *via, int regno, u_int32_t data, int size)
30564439Scg{
30664439Scg
30764439Scg	switch (size) {
30864439Scg	case 1:
30964439Scg		bus_space_write_1(via->st, via->sh, regno, data);
31064439Scg		break;
31164439Scg	case 2:
31264439Scg		bus_space_write_2(via->st, via->sh, regno, data);
31364439Scg		break;
31464439Scg	case 4:
31564439Scg		bus_space_write_4(via->st, via->sh, regno, data);
31664439Scg		break;
31764439Scg	}
31864439Scg}
31964439Scg
32064439Scg
32164439Scg/* Codec interface */
32264439Scgint
32364439Scgvia_waitready_codec(struct via_info *via)
32464439Scg{
32564439Scg	int i;
32664439Scg
32764439Scg	/* poll until codec not busy */
32864439Scg	for (i = 0; (i < TIMEOUT) &&
32964439Scg	    (via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_BUSY); i++)
33064439Scg		DELAY(1);
33164439Scg	if (i >= TIMEOUT) {
33264439Scg		printf("via: codec busy\n");
33364439Scg		return 1;
33464439Scg	}
33564439Scg
33664439Scg	return 0;
33764439Scg}
33864439Scg
33964439Scg
34064439Scgint
34164439Scgvia_waitvalid_codec(struct via_info *via)
34264439Scg{
34364439Scg	int i;
34464439Scg
34564439Scg	/* poll until codec valid */
34664439Scg	for (i = 0; (i < TIMEOUT) &&
34764439Scg	    !(via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_PRIVALID); i++)
34864439Scg		    DELAY(1);
34964439Scg	if (i >= TIMEOUT) {
35064439Scg		printf("via: codec invalid\n");
35164439Scg		return 1;
35264439Scg	}
35364439Scg
35464439Scg	return 0;
35564439Scg}
35664439Scg
35764439Scg
35864439Scgvoid
35964439Scgvia_write_codec(void *addr, int reg, u_int32_t val)
36064439Scg{
36164439Scg	struct via_info *via = addr;
36264439Scg
36364439Scg	if (via_waitready_codec(via)) return;
36464439Scg
36564439Scg	via_wr(via, VIA_CODEC_CTL,
36664439Scg		VIA_CODEC_PRIVALID | VIA_CODEC_INDEX(reg) | val, 4);
36764439Scg}
36864439Scg
36964439Scg
37064439Scgu_int32_t
37164439Scgvia_read_codec(void *addr, int reg)
37264439Scg{
37364439Scg	struct via_info *via = addr;
37464439Scg
37564439Scg	if (via_waitready_codec(via))
37664439Scg		return 1;
37764439Scg
37864439Scg	via_wr(via, VIA_CODEC_CTL,
37964439Scg	    VIA_CODEC_PRIVALID | VIA_CODEC_READ | VIA_CODEC_INDEX(reg),4);
38064439Scg
38164439Scg	if (via_waitready_codec(via))
38264439Scg		return 1;
38364439Scg
38464439Scg	if (via_waitvalid_codec(via))
38564439Scg		return 1;
38664439Scg
38764439Scg	return via_rd(via, VIA_CODEC_CTL, 2);
38864439Scg}
38964439Scg
39064439Scg
39164439Scg/* channel interface */
39264439Scgstatic void *
39364439Scgviachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
39464439Scg{
39564439Scg	struct via_info *via = devinfo;
39664439Scg	struct via_chinfo *ch = (dir == PCMDIR_PLAY) ? &via->pch : &via->rch;
39764439Scg
39864439Scg	ch->parent = via;
39964439Scg	ch->channel = c;
40064439Scg	ch->buffer = b;
40164439Scg	b->bufsize = VIA_BUFFSIZE;
40264439Scg
40364439Scg	if (chn_allocbuf(ch->buffer, via->parent_dmat) == -1) return NULL;
40464439Scg	return ch;
40564439Scg}
40664439Scg
40764439Scgstatic int
40864439Scgviachan_setdir(void *data, int dir)
40964439Scg{
41064439Scg	struct via_chinfo *ch = data;
41164439Scg	struct via_info *via = ch->parent;
41264439Scg	struct via_dma_op *ado;
41364439Scg	int i, chunk_size;
41464439Scg	int	phys_addr, flag;
41564439Scg
41664439Scg	ch->dir = dir;
41764439Scg	/*
41864439Scg	 *  Build the scatter/gather DMA (SGD) table.
41964439Scg	 *  There are four slots in the table: two for play, two for record.
42064439Scg	 *  This creates two half-buffers, one of which is playing; the other
42164439Scg	 *  is feeding.
42264439Scg	 */
42364439Scg	ado = via->sgd_table;
42464439Scg	chunk_size = ch->buffer->bufsize / SEGS_PER_CHAN;
42564439Scg
42664439Scg	if (dir == PCMDIR_REC) {
42764439Scg		ado += SEGS_PER_CHAN;
42864439Scg	}
42964439Scg
43064439ScgDEB(printf("SGD table located at va %p\n", ado));
43164439Scg	phys_addr = vtophys(ch->buffer->buf);
43264439Scg	for (i = 0; i < SEGS_PER_CHAN; i++) {
43364439Scg		ado->ptr = phys_addr;
43464439Scg		flag = (i == SEGS_PER_CHAN-1) ?
43564439Scg			VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
43664439Scg		ado->flags = flag | chunk_size;
43764439ScgDEB(printf("ado->ptr/flags = %x/%x\n", phys_addr, flag));
43864439Scg		phys_addr += chunk_size;
43964439Scg		ado++;
44064439Scg	}
44164439Scg	return 0;
44264439Scg}
44364439Scg
44464439Scgstatic int
44564439Scgviachan_setformat(void *data, u_int32_t format)
44664439Scg{
44764439Scg	struct via_chinfo *ch = data;
44864439Scg	struct via_info *via = ch->parent;
44964439Scg	int	mode, mode_set;
45064439Scg
45164439Scg	mode_set = 0;
45264439Scg	if (format & AFMT_STEREO)
45364439Scg		mode_set |= VIA_RPMODE_STEREO;
45464439Scg	if (format & AFMT_S16_LE)
45564439Scg		mode_set |= VIA_RPMODE_16BIT;
45664439Scg
45764439Scg	/* Set up for output format */
45864439Scg	if (ch->dir == PCMDIR_PLAY) {
45964439ScgDEB(printf("set play format: %x\n", format));
46064439Scg		mode = via_rd(via, VIA_PLAY_MODE, 1);
46164439Scg		mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO);
46264439Scg		mode |= mode_set;
46364439Scg		via_wr(via, VIA_PLAY_MODE, mode, 1);
46464439Scg	}
46564439Scg	else {
46664439ScgDEB(printf("set record format: %x\n", format));
46764439Scg		mode = via_rd(via, VIA_RECORD_MODE, 1);
46864439Scg		mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO);
46964439Scg		mode |= mode_set;
47064439Scg		via_wr(via, VIA_RECORD_MODE, mode, 1);
47164439Scg	}
47264439Scg
47364439Scg	return 0;
47464439Scg}
47564439Scg
47664439Scgstatic int
47764439Scgviachan_setspeed(void *data, u_int32_t speed)
47864439Scg{
47964439Scg	struct via_chinfo *ch = data;
48064439Scg	struct via_info *via = ch->parent;
48164439Scg
48264439Scg	/*
48364439Scg	 *  Basic AC'97 defines a 48 kHz sample rate only.  For other rates,
48464439Scg	 *  upsampling is required.
48564439Scg	 *
48664439Scg	 *  The VT82C686A does not perform upsampling, and neither do we.
48764439Scg	 *  If the codec supports variable-rate audio (i.e. does the upsampling
48864439Scg	 *  itself), then negotiate the rate with the codec.  Otherwise,
48964439Scg	 *  return 48 kHz cuz that's all you got.
49064439Scg	 */
49164439Scg	if (ch->dir == PCMDIR_PLAY) {
49264439ScgDEB(printf("requested play speed: %d\n", speed));
49364439Scg		if (via->codec_caps & AC97_CODEC_DOES_VRA) {
49464439Scg			via_write_codec(via, AC97_REG_EXT_DAC_RATE, speed);
49564439Scg			speed = via_read_codec(via, AC97_REG_EXT_DAC_RATE);
49664439Scg		}
49764439Scg		else {
49864439ScgDEB(printf("VRA not supported!\n"));
49964439Scg			speed = 48000;
50064439Scg		}
50164439ScgDEB(printf("obtained play speed: %d\n", speed));
50264439Scg	}
50364439Scg	else {
50464439ScgDEB(printf("requested record speed: %d\n", speed));
50564439Scg		if (via->codec_caps & AC97_CODEC_DOES_VRA) {
50664439Scg			via_write_codec(via, AC97_REG_EXT_ADC_RATE, speed);
50764439Scg			speed = via_read_codec(via, AC97_REG_EXT_ADC_RATE);
50864439Scg		}
50964439Scg		else {
51064439ScgDEB(printf("VRA not supported!\n"));
51164439Scg			speed = 48000;
51264439Scg		}
51364439ScgDEB(printf("obtained record speed: %d\n", speed));
51464439Scg	}
51564439Scg	return speed;
51664439Scg}
51764439Scg
51864439Scgstatic int
51964439Scgviachan_setblocksize(void *data, u_int32_t blocksize)
52064439Scg{
52164439Scg	struct via_chinfo *ch = data;
52264439Scg
52364439Scg	return ch->buffer->bufsize / 2;
52464439Scg}
52564439Scg
52664439Scgstatic int
52764439Scgviachan_trigger(void *data, int go)
52864439Scg{
52964439Scg	struct via_chinfo *ch = data;
53064439Scg	struct via_info *via = ch->parent;
53164439Scg	struct via_dma_op *ado;
53264439Scg
53364439Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0;
53464439Scg	if (ch->dir == PCMDIR_PLAY) {
53564439Scg		if (go == PCMTRIG_START) {
53664439Scg			ado = &via->sgd_table[0];
53764439ScgDEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado)));
53864439Scg			via_wr(via, VIA_PLAY_DMAOPS_BASE, vtophys(ado),4);
53964439Scg			via_wr(via, VIA_PLAY_CONTROL,
54064439Scg				VIA_RPCTRL_START, 1);
54164439Scg		}
54264439Scg		else {
54364439Scg			/* Stop DMA */
54464439Scg			via_wr(via, VIA_PLAY_CONTROL,
54564439Scg				VIA_RPCTRL_TERMINATE, 1);
54664439Scg		}
54764439Scg	} else {
54864439Scg		if (go == PCMTRIG_START) {
54964439Scg			ado = &via->sgd_table[SEGS_PER_CHAN];
55064439ScgDEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado)));
55164439Scg			via_wr(via, VIA_RECORD_DMAOPS_BASE,
55264439Scg				vtophys(ado),4);
55364439Scg			via_wr(via, VIA_RECORD_CONTROL,
55464439Scg				VIA_RPCTRL_START, 1);
55564439Scg		}
55664439Scg		else {
55764439Scg			/* Stop DMA */
55864439Scg			via_wr(via, VIA_RECORD_CONTROL,
55964439Scg				VIA_RPCTRL_TERMINATE, 1);
56064439Scg		}
56164439Scg	}
56264439Scg
56364439ScgDEB(printf("viachan_trigger: go=%d\n", go));
56464439Scg	return 0;
56564439Scg}
56664439Scg
56764439Scgstatic int
56864439Scgviachan_getptr(void *data)
56964439Scg{
57064439Scg	struct via_chinfo *ch = data;
57164439Scg	struct via_info *via = ch->parent;
57264439Scg	struct via_dma_op *ado;
57364439Scg	int	ptr, base, len, seg;
57464439Scg	int base1;
57564439Scg
57664439Scg	if (ch->dir == PCMDIR_PLAY) {
57764439Scg		ado = &via->sgd_table[0];
57864439Scg		base1 = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4);
57964439Scg		len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4);
58064439Scg		base = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4);
58164439Scg		if (base != base1) {	/* Avoid race hazzard	*/
58264439Scg			len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4);
58364439Scg		}
58464439ScgDEB(printf("viachan_getptr: len / base = %x / %x\n", len, base));
58564439Scg
58664439Scg		/* Base points to SGD segment to do, one past current */
58764439Scg
58864439Scg		/* Determine how many segments have been done */
58964439Scg		seg = (base - vtophys(ado)) / sizeof(struct via_dma_op);
59064439Scg		if (seg == 0) seg = SEGS_PER_CHAN;
59164439Scg
59264439Scg		/* Now work out offset: seg less count */
59364439Scg		ptr = seg * ch->buffer->bufsize / SEGS_PER_CHAN - len;
59464439ScgDEB(printf("return ptr=%d\n", ptr));
59564439Scg		return ptr;
59664439Scg	}
59764439Scg	else {
59864439Scg		base1 = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4);
59964439Scg		ado = &via->sgd_table[SEGS_PER_CHAN];
60064439Scg		len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4);
60164439Scg		base = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4);
60264439Scg		if (base != base1) {	/* Avoid race hazzard	*/
60364439Scg			len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4);
60464439Scg		}
60564439ScgDEB(printf("viachan_getptr: len / base = %x / %x\n", len, base));
60664439Scg
60764439Scg		/* Base points to next block to do, one past current */
60864439Scg
60964439Scg		/* Determine how many segments have been done */
61064439Scg		seg = (base - vtophys(ado)) / sizeof(struct via_dma_op);
61164439Scg		if (seg == 0) seg = SEGS_PER_CHAN;
61264439Scg
61364439Scg		/* Now work out offset: seg less count */
61464439Scg		ptr = seg * ch->buffer->bufsize / SEGS_PER_CHAN - len;
61564439Scg
61664439Scg		/* DMA appears to operate on memory 'lines' of 32 bytes	*/
61764439Scg		/* so don't return any part line - it isn't in RAM yet	*/
61864439Scg		ptr = ptr & ~0x1f;
61964439ScgDEB(printf("return ptr=%d\n", ptr));
62064439Scg		return ptr;
62164439Scg	}
62264439Scg	return 0;
62364439Scg}
62464439Scg
62564439Scgstatic pcmchan_caps *
62664439Scgviachan_getcaps(void *data)
62764439Scg{
62864439Scg	struct via_chinfo *ch = data;
62964439Scg	return (ch->dir == PCMDIR_PLAY) ? &via_playcaps : &via_reccaps;
63064439Scg}
63164439Scg
63264439Scgstatic void
63364439Scgvia_intr(void *p)
63464439Scg{
63564439Scg	struct via_info *via = p;
63664439Scg	int		st;
63764439Scg
63864439ScgDEB(printf("viachan_intr\n"));
63964439Scg	/* Read channel */
64064439Scg	st = via_rd(via, VIA_PLAY_STAT, 1);
64164439Scg	if (st & VIA_RPSTAT_INTR) {
64264439Scg		via_wr(via, VIA_PLAY_STAT, VIA_RPSTAT_INTR, 1);
64364439Scg		chn_intr(via->pch.channel);
64464439Scg	}
64564439Scg
64664439Scg	/* Write channel */
64764439Scg	st = via_rd(via, VIA_RECORD_STAT, 1);
64864439Scg	if (st & VIA_RPSTAT_INTR) {
64964439Scg		via_wr(via, VIA_RECORD_STAT, VIA_RPSTAT_INTR, 1);
65064439Scg		chn_intr(via->rch.channel);
65164439Scg	}
65264439Scg}
65364439Scg
65464439Scg
655