t4dwave.c revision 50733
150724Scg/*
250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
350724Scg * All rights reserved.
450724Scg *
550724Scg * Redistribution and use in source and binary forms, with or without
650724Scg * modification, are permitted provided that the following conditions
750724Scg * are met:
850724Scg * 1. Redistributions of source code must retain the above copyright
950724Scg *    notice, this list of conditions and the following disclaimer.
1050724Scg * 2. Redistributions in binary form must reproduce the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer in the
1250724Scg *    documentation and/or other materials provided with the distribution.
1350724Scg *
1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
2450724Scg * SUCH DAMAGE.
2550724Scg *
2650733Speter * $FreeBSD: head/sys/dev/sound/pci/t4dwave.c 50733 1999-09-01 06:58:27Z peter $
2750724Scg */
2850724Scg
2950724Scg#include "pci.h"
3050724Scg#include "pcm.h"
3150724Scg
3250724Scg#include <dev/pcm/sound.h>
3350724Scg#include <dev/pcm/ac97.h>
3450724Scg#include <dev/pcm/pci/t4dwave.h>
3550724Scg
3650724Scg#include <pci/pcireg.h>
3750724Scg#include <pci/pcivar.h>
3850724Scg
3950724Scg#if NPCI != 0
4050724Scg
4150724Scg/* -------------------------------------------------------------------- */
4250724Scg
4350724Scgstruct tr_info;
4450724Scg
4550724Scg/* channel registers */
4650724Scgstruct tr_chinfo {
4750724Scg	u_int32_t cso, alpha, fms, fmc, ec;
4850724Scg	u_int32_t lba;
4950724Scg	u_int32_t eso, delta;
5050724Scg	u_int32_t rvol, cvol;
5150724Scg	u_int32_t gvsel, pan, vol, ctrl;
5250724Scg	int index;
5350724Scg	snd_dbuf *buffer;
5450724Scg	pcm_channel *channel;
5550724Scg	struct tr_info *parent;
5650724Scg};
5750724Scg
5850724Scg/* device private data */
5950724Scgstruct tr_info {
6050724Scg	u_int32_t type;
6150724Scg
6250724Scg	bus_space_tag_t st;
6350724Scg	bus_space_handle_t sh;
6450724Scg	bus_dma_tag_t parent_dmat;
6550724Scg
6650724Scg	struct resource *reg, *irq;
6750724Scg	int		regtype, regid, irqid;
6850724Scg	void		*ih;
6950724Scg
7050724Scg	u_int32_t playchns;
7150724Scg	struct tr_chinfo chinfo[TR_MAXPLAYCH];
7250724Scg	struct tr_chinfo recchinfo;
7350724Scg};
7450724Scg
7550724Scg/* -------------------------------------------------------------------- */
7650724Scg
7750724Scg/*
7850724Scg * prototypes
7950724Scg */
8050724Scg
8150724Scg/* channel interface */
8250724Scgstatic void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
8350724Scgstatic int trchan_setdir(void *data, int dir);
8450724Scgstatic int trchan_setformat(void *data, u_int32_t format);
8550724Scgstatic int trchan_setspeed(void *data, u_int32_t speed);
8650724Scgstatic int trchan_setblocksize(void *data, u_int32_t blocksize);
8750724Scgstatic int trchan_trigger(void *data, int go);
8850724Scgstatic int trchan_getptr(void *data);
8950724Scgstatic pcmchan_caps *trchan_getcaps(void *data);
9050724Scg
9150724Scg/* talk to the codec - called from ac97.c */
9250724Scgstatic u_int32_t tr_rdcd(void *, int);
9350724Scgstatic void  	 tr_wrcd(void *, int, u_int32_t);
9450724Scg
9550724Scg/* stuff */
9650724Scgstatic int       tr_init(struct tr_info *);
9750724Scgstatic void      tr_intr(void *);
9850724Scg
9950724Scg/* talk to the card */
10050724Scgstatic u_int32_t tr_rd(struct tr_info *, int, int);
10150724Scgstatic void 	 tr_wr(struct tr_info *, int, u_int32_t, int);
10250724Scg
10350724Scg/* manipulate playback channels */
10450724Scgstatic void 	 tr_clrint(struct tr_info *, char);
10550724Scgstatic void 	 tr_enaint(struct tr_info *, char, int);
10650724Scgstatic u_int32_t tr_testint(struct tr_info *, char);
10750724Scgstatic void	 tr_rdch(struct tr_info *, char, struct tr_chinfo *);
10850724Scgstatic void	 tr_wrch(struct tr_info *, char, struct tr_chinfo *);
10950724Scgstatic void 	 tr_selch(struct tr_info *, char);
11050724Scgstatic void 	 tr_startch(struct tr_info *, char);
11150724Scgstatic void 	 tr_stopch(struct tr_info *, char);
11250724Scg
11350724Scg/* -------------------------------------------------------------------- */
11450724Scg
11550724Scgstatic pcmchan_caps tr_reccaps = {
11650724Scg	4000, 48000,
11750724Scg	AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
11850724Scg	AFMT_STEREO | AFMT_S16_LE
11950724Scg};
12050724Scg
12150724Scgstatic pcmchan_caps tr_playcaps = {
12250724Scg	4000, 48000,
12350724Scg	AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
12450724Scg	AFMT_U16_LE
12550724Scg};
12650724Scg
12750724Scgstatic pcm_channel tr_chantemplate = {
12850724Scg	trchan_init,
12950724Scg	trchan_setdir,
13050724Scg	trchan_setformat,
13150724Scg	trchan_setspeed,
13250724Scg	trchan_setblocksize,
13350724Scg	trchan_trigger,
13450724Scg	trchan_getptr,
13550724Scg	trchan_getcaps,
13650724Scg};
13750724Scg
13850724Scg/* -------------------------------------------------------------------- */
13950724Scg
14050724Scgstatic u_int32_t
14150724Scgtr_fmttobits(u_int32_t fmt)
14250724Scg{
14350724Scg	u_int32_t bits = 0;
14450724Scg	bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
14550724Scg	bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0;
14650724Scg	bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0;
14750724Scg	return bits;
14850724Scg}
14950724Scg
15050724Scg/* Hardware */
15150724Scg
15250724Scgstatic u_int32_t
15350724Scgtr_rd(struct tr_info *tr, int regno, int size)
15450724Scg{
15550724Scg	switch(size) {
15650724Scg	case 1:
15750724Scg		return bus_space_read_1(tr->st, tr->sh, regno);
15850724Scg	case 2:
15950724Scg		return bus_space_read_2(tr->st, tr->sh, regno);
16050724Scg	case 4:
16150724Scg		return bus_space_read_4(tr->st, tr->sh, regno);
16250724Scg	default:
16350724Scg		return 0xffffffff;
16450724Scg	}
16550724Scg}
16650724Scg
16750724Scgstatic void
16850724Scgtr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
16950724Scg{
17050724Scg	switch(size) {
17150724Scg	case 1:
17250724Scg		bus_space_write_1(tr->st, tr->sh, regno, data);
17350724Scg		break;
17450724Scg	case 2:
17550724Scg		bus_space_write_2(tr->st, tr->sh, regno, data);
17650724Scg		break;
17750724Scg	case 4:
17850724Scg		bus_space_write_4(tr->st, tr->sh, regno, data);
17950724Scg		break;
18050724Scg	}
18150724Scg}
18250724Scg
18350724Scg/* ac97 codec */
18450724Scg
18550724Scgstatic u_int32_t
18650724Scgtr_rdcd(void *devinfo, int regno)
18750724Scg{
18850724Scg	struct tr_info *tr = (struct tr_info *)devinfo;
18950724Scg	int i, j, treg, trw;
19050724Scg
19150724Scg	switch (tr->type) {
19250724Scg	case TDX_PCI_ID:
19350724Scg		treg=TDX_REG_CODECRD;
19450724Scg		trw=TDX_CDC_RWSTAT;
19550724Scg		break;
19650724Scg	case TNX_PCI_ID:
19750724Scg		treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
19850724Scg		trw=TNX_CDC_RWSTAT;
19950724Scg		break;
20050724Scg	default:
20150724Scg		printf("!!! tr_rdcd defaulted !!!\n");
20250724Scg		return 0xffffffff;
20350724Scg	}
20450724Scg
20550724Scg	regno &= 0x7f;
20650724Scg	tr_wr(tr, treg, regno | trw, 4);
20750724Scg	j=trw;
20850724Scg	for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
20950724Scg	if (i == 0) printf("codec timeout during read of register %x\n", regno);
21050724Scg	return (j >> TR_CDC_DATA) & 0xffff;
21150724Scg}
21250724Scg
21350724Scgstatic void
21450724Scgtr_wrcd(void *devinfo, int regno, u_int32_t data)
21550724Scg{
21650724Scg	struct tr_info *tr = (struct tr_info *)devinfo;
21750724Scg	int i, j, treg, trw;
21850724Scg
21950724Scg	switch (tr->type) {
22050724Scg	case TDX_PCI_ID:
22150724Scg		treg=TDX_REG_CODECWR;
22250724Scg		trw=TDX_CDC_RWSTAT;
22350724Scg		break;
22450724Scg	case TNX_PCI_ID:
22550724Scg		treg=TNX_REG_CODECWR;
22650724Scg		trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
22750724Scg		break;
22850724Scg	default:
22950724Scg		printf("!!! tr_wrcd defaulted !!!");
23050724Scg		return;
23150724Scg	}
23250724Scg
23350724Scg	regno &= 0x7f;
23450724Scg#if 0
23550724Scg	printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
23650724Scg#endif
23750724Scg	j=trw;
23850724Scg	for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
23950724Scg	tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
24050724Scg#if 0
24150724Scg	printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
24250724Scg#endif
24350724Scg	if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
24450724Scg}
24550724Scg
24650724Scg/* playback channel interrupts */
24750724Scg
24850724Scgstatic u_int32_t
24950724Scgtr_testint(struct tr_info *tr, char channel)
25050724Scg{
25150724Scg	return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
25250724Scg	             4) & (1<<(channel & 0x1f));
25350724Scg}
25450724Scg
25550724Scgstatic void
25650724Scgtr_clrint(struct tr_info *tr, char channel)
25750724Scg{
25850724Scg	tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
25950724Scg	      1<<(channel & 0x1f), 4);
26050724Scg}
26150724Scg
26250724Scgstatic void
26350724Scgtr_enaint(struct tr_info *tr, char channel, int enable)
26450724Scg{
26550724Scg	u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA;
26650724Scg	u_int32_t i = tr_rd(tr, reg, 4);
26750724Scg	channel &= 0x1f;
26850724Scg	i &= ~(1 << channel);
26950724Scg	i |= (enable? 1 : 0) << channel;
27050724Scg	tr_clrint(tr, channel);
27150724Scg	tr_wr(tr, reg, i, 4);
27250724Scg}
27350724Scg
27450724Scg/* playback channels */
27550724Scg
27650724Scgstatic void
27750724Scgtr_selch(struct tr_info *tr, char channel)
27850724Scg{
27950724Scg	int i=tr_rd(tr, TR_REG_CIR, 4);
28050724Scg	i &= ~TR_CIR_MASK;
28150724Scg	i |= channel & 0x3f;
28250724Scg	tr_wr(tr, TR_REG_CIR, i, 4);
28350724Scg}
28450724Scg
28550724Scgstatic void
28650724Scgtr_startch(struct tr_info *tr, char channel)
28750724Scg{
28850724Scg	tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA,
28950724Scg	      1<<(channel & 0x1f), 4);
29050724Scg}
29150724Scg
29250724Scgstatic void
29350724Scgtr_stopch(struct tr_info *tr, char channel)
29450724Scg{
29550724Scg	tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA,
29650724Scg	      1<<(channel & 0x1f), 4);
29750724Scg}
29850724Scg
29950724Scgstatic void
30050724Scgtr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
30150724Scg{
30250724Scg	u_int32_t cr[TR_CHN_REGS], i;
30350724Scg
30450724Scg	ch->gvsel 	&= 0x00000001;
30550724Scg	ch->fmc		&= 0x00000003;
30650724Scg	ch->fms		&= 0x0000000f;
30750724Scg	ch->ctrl	&= 0x0000000f;
30850724Scg	ch->pan 	&= 0x0000007f;
30950724Scg	ch->rvol	&= 0x0000007f;
31050724Scg	ch->cvol 	&= 0x0000007f;
31150724Scg	ch->vol		&= 0x000000ff;
31250724Scg	ch->ec		&= 0x00000fff;
31350724Scg	ch->alpha	&= 0x00000fff;
31450724Scg	ch->delta	&= 0x0000ffff;
31550724Scg	ch->lba		&= 0x3fffffff;
31650724Scg
31750724Scg	cr[1]=ch->lba;
31850724Scg	cr[3]=(ch->rvol<<7) | (ch->cvol);
31950724Scg	cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec);
32050724Scg
32150724Scg	switch (tr->type) {
32250724Scg	case TDX_PCI_ID:
32350724Scg		ch->cso &= 0x0000ffff;
32450724Scg		ch->eso &= 0x0000ffff;
32550724Scg		cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
32650724Scg		cr[2]=(ch->eso<<16) | (ch->delta);
32750724Scg		cr[3]|=0x0000c000;
32850724Scg		break;
32950724Scg	case TNX_PCI_ID:
33050724Scg		ch->cso &= 0x00ffffff;
33150724Scg		ch->eso &= 0x00ffffff;
33250724Scg		cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
33350724Scg		cr[2]=((ch->delta>>16)<<24) | (ch->eso);
33450724Scg		cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
33550724Scg		break;
33650724Scg	}
33750724Scg	tr_selch(tr, channel);
33850724Scg	for (i=0; i<TR_CHN_REGS; i++)
33950724Scg		tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
34050724Scg}
34150724Scg
34250724Scgstatic void
34350724Scgtr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
34450724Scg{
34550724Scg	u_int32_t cr[5], i;
34650724Scg	tr_selch(tr, channel);
34750724Scg	for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
34850724Scg	ch->lba=	(cr[1] & 0x3fffffff);
34950724Scg	ch->fmc=	(cr[3] & 0x0000c000) >> 14;
35050724Scg	ch->rvol=	(cr[3] & 0x00003f80) >> 7;
35150724Scg	ch->cvol=	(cr[3] & 0x0000007f);
35250724Scg	ch->gvsel=	(cr[4] & 0x80000000) >> 31;
35350724Scg	ch->pan=	(cr[4] & 0x7f000000) >> 24;
35450724Scg	ch->vol=	(cr[4] & 0x00ff0000) >> 16;
35550724Scg	ch->ctrl=	(cr[4] & 0x0000f000) >> 12;
35650724Scg	ch->ec=		(cr[4] & 0x00000fff);
35750724Scg	switch(tr->type) {
35850724Scg	case TDX_PCI_ID:
35950724Scg		ch->cso=	(cr[0] & 0xffff0000) >> 16;
36050724Scg		ch->alpha=	(cr[0] & 0x0000fff0) >> 4;
36150724Scg		ch->fms=	(cr[0] & 0x0000000f);
36250724Scg		ch->eso=	(cr[2] & 0xffff0000) >> 16;
36350724Scg		ch->delta=	(cr[2] & 0x0000ffff);
36450724Scg		break;
36550724Scg	case TNX_PCI_ID:
36650724Scg		ch->cso=	(cr[0] & 0x00ffffff);
36750724Scg		ch->eso=	(cr[2] & 0x00ffffff);
36850724Scg		ch->delta=	((cr[2] & 0xff000000) >> 16) |
36950724Scg				((cr[0] & 0xff000000) >> 24);
37050724Scg		ch->alpha=	(cr[3] & 0xfff00000) >> 20;
37150724Scg		ch->fms=	(cr[3] & 0x000f0000) >> 16;
37250724Scg		break;
37350724Scg	}
37450724Scg}
37550724Scg
37650724Scg/* channel interface */
37750724Scg
37850724Scgvoid *
37950724Scgtrchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
38050724Scg{
38150724Scg	struct tr_info *tr = devinfo;
38250724Scg	struct tr_chinfo *ch;
38350724Scg	if (dir == PCMDIR_PLAY) {
38450724Scg		ch = &tr->chinfo[tr->playchns];
38550724Scg		ch->index = tr->playchns++;
38650724Scg	} else {
38750724Scg		ch = &tr->recchinfo;
38850724Scg		ch->index = -1;
38950724Scg	}
39050724Scg	ch->buffer = b;
39150724Scg	ch->buffer->bufsize = TR_BUFFSIZE;
39250724Scg	ch->parent = tr;
39350724Scg	ch->channel = c;
39450724Scg	if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL;
39550724Scg	else return ch;
39650724Scg}
39750724Scg
39850724Scgstatic int
39950724Scgtrchan_setdir(void *data, int dir)
40050724Scg{
40150724Scg	struct tr_chinfo *ch = data;
40250724Scg	struct tr_info *tr = ch->parent;
40350724Scg	if (dir == PCMDIR_PLAY && ch->index >= 0) {
40450724Scg		ch->fmc = ch->fms = ch->ec = ch->alpha = 0;
40550724Scg		ch->lba = vtophys(ch->buffer->buf);
40650724Scg		ch->cso = 0;
40750724Scg		ch->eso = ch->buffer->bufsize - 1;
40850724Scg		ch->rvol = ch->cvol = 0;
40950724Scg		ch->gvsel = 0;
41050724Scg		ch->pan = 0;
41150724Scg		ch->vol = 0;
41250724Scg		ch->ctrl = 0x01;
41350724Scg		ch->delta = 0;
41450724Scg		tr_wrch(tr, ch->index, ch);
41550724Scg		tr_enaint(tr, ch->index, 1);
41650724Scg	} else if (dir == PCMDIR_REC && ch->index == -1) {
41750724Scg		/* set up dma mode regs */
41850724Scg		u_int32_t i;
41950724Scg		tr_wr(tr, TR_REG_DMAR15, 0, 1);
42050724Scg		i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
42150724Scg		tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
42250724Scg		/* set up base address */
42350724Scg	   	tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4);
42450724Scg		/* set up buffer size */
42550724Scg		i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
42650724Scg		tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4);
42750724Scg	} else return -1;
42850724Scg	return 0;
42950724Scg}
43050724Scg
43150724Scgstatic int
43250724Scgtrchan_setformat(void *data, u_int32_t format)
43350724Scg{
43450724Scg	struct tr_chinfo *ch = data;
43550724Scg	struct tr_info *tr = ch->parent;
43650724Scg	u_int32_t bits = tr_fmttobits(format);
43750724Scg
43850724Scg	if (ch->index >= 0) {
43950724Scg		tr_rdch(tr, ch->index, ch);
44050724Scg		ch->eso = (ch->buffer->bufsize / ch->buffer->sample_size) - 1;
44150724Scg		ch->ctrl = bits | 0x01;
44250724Scg   		tr_wrch(tr, ch->index, ch);
44350724Scg	} else {
44450724Scg		u_int32_t i;
44550724Scg		/* set # of samples between interrupts */
44650724Scg		i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1;
44750724Scg		tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
44850724Scg		/* set sample format */
44950724Scg		i = 0x18 | (bits << 4);
45050724Scg		tr_wr(tr, TR_REG_SBCTRL, i, 1);
45150724Scg	}
45250724Scg	return 0;
45350724Scg}
45450724Scg
45550724Scgstatic int
45650724Scgtrchan_setspeed(void *data, u_int32_t speed)
45750724Scg{
45850724Scg	struct tr_chinfo *ch = data;
45950724Scg	struct tr_info *tr = ch->parent;
46050724Scg
46150724Scg	if (ch->index >= 0) {
46250724Scg		tr_rdch(tr, ch->index, ch);
46350724Scg		ch->delta = (speed << 12) / 48000;
46450724Scg   		tr_wrch(tr, ch->index, ch);
46550724Scg		return (ch->delta * 48000) >> 12;
46650724Scg	} else {
46750724Scg		/* setup speed */
46850724Scg		ch->delta = (48000 << 12) / speed;
46950724Scg		tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
47050724Scg		return (48000 << 12) / ch->delta;
47150724Scg	}
47250724Scg	return 0;
47350724Scg}
47450724Scg
47550724Scgstatic int
47650724Scgtrchan_setblocksize(void *data, u_int32_t blocksize)
47750724Scg{
47850724Scg	struct tr_chinfo *ch = data;
47950724Scg	return ch->buffer->bufsize / 2;
48050724Scg}
48150724Scg
48250724Scgstatic int
48350724Scgtrchan_trigger(void *data, int go)
48450724Scg{
48550724Scg	struct tr_chinfo *ch = data;
48650724Scg	struct tr_info *tr = ch->parent;
48750724Scg	if (ch->index >= 0) {
48850724Scg		if (go == PCMTRIG_START) tr_startch(tr, ch->index);
48950724Scg		else tr_stopch(tr, ch->index);
49050724Scg	} else {
49150724Scg		u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7;
49250724Scg		tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1);
49350724Scg	}
49450724Scg	return 0;
49550724Scg}
49650724Scg
49750724Scgstatic int
49850724Scgtrchan_getptr(void *data)
49950724Scg{
50050724Scg	struct tr_chinfo *ch = data;
50150724Scg	struct tr_info *tr = ch->parent;
50250724Scg	if (ch->index >= 0) {
50350724Scg		tr_rdch(tr, ch->index, ch);
50450724Scg		return ch->cso * ch->buffer->sample_size;
50550724Scg	} else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf);
50650724Scg}
50750724Scg
50850724Scgstatic pcmchan_caps *
50950724Scgtrchan_getcaps(void *data)
51050724Scg{
51150724Scg	struct tr_chinfo *ch = data;
51250724Scg	return (ch->index >= 0)? &tr_playcaps : &tr_reccaps;
51350724Scg}
51450724Scg
51550724Scg/* The interrupt handler */
51650724Scg
51750724Scgstatic void
51850724Scgtr_intr(void *p)
51950724Scg{
52050724Scg	struct tr_info *tr = (struct tr_info *)p;
52150724Scg	u_int32_t	intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
52250724Scg
52350724Scg	if (intsrc & TR_INT_ADDR) {
52450724Scg		int i;
52550724Scg		for (i = 0; i < tr->playchns; i++) {
52650724Scg			if (tr_testint(tr, i)) {
52750724Scg				chn_intr(tr->chinfo[i].channel);
52850724Scg				tr_clrint(tr, i);
52950724Scg			}
53050724Scg		}
53150724Scg	}
53250724Scg	if (intsrc & TR_INT_SB) {
53350724Scg		chn_intr(tr->recchinfo.channel);
53450724Scg		tr_rd(tr, TR_REG_SBR9, 1);
53550724Scg		tr_rd(tr, TR_REG_SBR10, 1);
53650724Scg	}
53750724Scg}
53850724Scg
53950724Scg/* -------------------------------------------------------------------- */
54050724Scg
54150724Scg/*
54250724Scg * Probe and attach the card
54350724Scg */
54450724Scg
54550724Scgstatic int
54650724Scgtr_init(struct tr_info *tr)
54750724Scg{
54850724Scg	if (tr->type == TDX_PCI_ID) {
54950724Scg		tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
55050724Scg	} else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
55150724Scg
55250724Scg	tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
55350724Scg	tr->playchns = 0;
55450724Scg	return 0;
55550724Scg}
55650724Scg
55750724Scgstatic int
55850724Scgtr_pci_probe(device_t dev)
55950724Scg{
56050724Scg	if (pci_get_devid(dev) == TDX_PCI_ID) {
56150724Scg		device_set_desc(dev, "Trident 4DWave DX");
56250724Scg		return 0;
56350724Scg	}
56450724Scg	if (pci_get_devid(dev) == TNX_PCI_ID) {
56550724Scg		device_set_desc(dev, "Trident 4DWave NX");
56650724Scg		return 0;
56750724Scg	}
56850724Scg
56950724Scg	return ENXIO;
57050724Scg}
57150724Scg
57250724Scgstatic int
57350724Scgtr_pci_attach(device_t dev)
57450724Scg{
57550724Scg	snddev_info    *d;
57650724Scg	u_int32_t	data;
57750724Scg	struct tr_info *tr;
57850724Scg	struct ac97_info *codec;
57950724Scg	int		i;
58050724Scg	int		mapped;
58150724Scg	char 		status[SND_STATUSLEN];
58250724Scg
58350724Scg	d = device_get_softc(dev);
58450724Scg	if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
58550724Scg		device_printf(dev, "cannot allocate softc\n");
58650724Scg		return ENXIO;
58750724Scg	}
58850724Scg
58950724Scg	bzero(tr, sizeof(*tr));
59050724Scg	tr->type = pci_get_devid(dev);
59150724Scg
59250724Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
59350724Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
59450724Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
59550724Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
59650724Scg
59750724Scg	mapped = 0;
59850724Scg	/* XXX dfr: is this strictly necessary? */
59950724Scg	for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
60050724Scg		tr->regid = PCIR_MAPS + i*4;
60150724Scg		tr->regtype = SYS_RES_MEMORY;
60250724Scg		tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid,
60350724Scg					     0, ~0, 1, RF_ACTIVE);
60450724Scg		if (!tr->reg) {
60550724Scg			tr->regtype = SYS_RES_IOPORT;
60650724Scg			tr->reg = bus_alloc_resource(dev, tr->regtype,
60750724Scg						     &tr->regid, 0, ~0, 1,
60850724Scg						     RF_ACTIVE);
60950724Scg		}
61050724Scg		if (tr->reg) {
61150724Scg			tr->st = rman_get_bustag(tr->reg);
61250724Scg			tr->sh = rman_get_bushandle(tr->reg);
61350724Scg			mapped++;
61450724Scg		}
61550724Scg	}
61650724Scg
61750724Scg	if (mapped == 0) {
61850724Scg		device_printf(dev, "unable to map register space\n");
61950724Scg		goto bad;
62050724Scg	}
62150724Scg
62250724Scg	if (tr_init(tr) == -1) {
62350724Scg		device_printf(dev, "unable to initialize the card\n");
62450724Scg		goto bad;
62550724Scg	}
62650724Scg
62750724Scg	codec = ac97_create(tr, tr_rdcd, tr_wrcd);
62850724Scg	if (codec == NULL) goto bad;
62950724Scg	mixer_init(d, &ac97_mixer, codec);
63050724Scg
63150724Scg	tr->irqid = 0;
63250724Scg	tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
63350724Scg				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
63450724Scg	if (!tr->irq ||
63550724Scg	    bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) {
63650724Scg		device_printf(dev, "unable to map interrupt\n");
63750724Scg		goto bad;
63850724Scg	}
63950724Scg
64050724Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
64150724Scg		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
64250724Scg		/*highaddr*/BUS_SPACE_MAXADDR,
64350724Scg		/*filter*/NULL, /*filterarg*/NULL,
64450724Scg		/*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
64550724Scg		/*flags*/0, &tr->parent_dmat) != 0) {
64650724Scg		device_printf(dev, "unable to create dma tag\n");
64750724Scg		goto bad;
64850724Scg	}
64950724Scg
65050724Scg	snprintf(status, 64, "at %s 0x%lx irq %ld",
65150724Scg		 (tr->regtype == SYS_RES_IOPORT)? "io" : "memory",
65250724Scg		 rman_get_start(tr->reg), rman_get_start(tr->irq));
65350724Scg
65450724Scg	if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
65550724Scg	pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr);
65650724Scg	for (i = 0; i < TR_MAXPLAYCH; i++)
65750724Scg		pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr);
65850724Scg	pcm_setstatus(dev, status);
65950724Scg
66050724Scg	return 0;
66150724Scg
66250724Scgbad:
66350724Scg	if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
66450724Scg	if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
66550724Scg	if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
66650724Scg	free(tr, M_DEVBUF);
66750724Scg	return ENXIO;
66850724Scg}
66950724Scg
67050724Scgstatic device_method_t tr_methods[] = {
67150724Scg	/* Device interface */
67250724Scg	DEVMETHOD(device_probe,		tr_pci_probe),
67350724Scg	DEVMETHOD(device_attach,	tr_pci_attach),
67450724Scg
67550724Scg	{ 0, 0 }
67650724Scg};
67750724Scg
67850724Scgstatic driver_t tr_driver = {
67950724Scg	"pcm",
68050724Scg	tr_methods,
68150724Scg	sizeof(snddev_info),
68250724Scg};
68350724Scg
68450724Scgstatic devclass_t pcm_devclass;
68550724Scg
68650724ScgDRIVER_MODULE(tr, pci, tr_driver, pcm_devclass, 0, 0);
68750724Scg
68850724Scg#endif /* NPCI != 0 */
689