t4dwave.c revision 74763
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 74763 2001-03-24 23:10:29Z cg $
2750724Scg */
2850724Scg
2953465Scg#include <dev/sound/pcm/sound.h>
3053465Scg#include <dev/sound/pcm/ac97.h>
3153465Scg#include <dev/sound/pci/t4dwave.h>
3250724Scg
3350724Scg#include <pci/pcireg.h>
3450724Scg#include <pci/pcivar.h>
3550724Scg
3650724Scg/* -------------------------------------------------------------------- */
3750724Scg
3854459Scg#define TDX_PCI_ID 	0x20001023
3954459Scg#define TNX_PCI_ID 	0x20011023
4054459Scg
4171503Scg#define TR_BUFFSIZE 	0x1000
4254459Scg#define TR_TIMEOUT_CDC	0xffff
4354459Scg#define TR_MAXPLAYCH	4
4454459Scg
4550724Scgstruct tr_info;
4650724Scg
4750724Scg/* channel registers */
4850724Scgstruct tr_chinfo {
4950724Scg	u_int32_t cso, alpha, fms, fmc, ec;
5050724Scg	u_int32_t lba;
5150724Scg	u_int32_t eso, delta;
5250724Scg	u_int32_t rvol, cvol;
5350724Scg	u_int32_t gvsel, pan, vol, ctrl;
5471503Scg	int index, bufhalf;
5574763Scg	struct snd_dbuf *buffer;
5674763Scg	struct pcm_channel *channel;
5750724Scg	struct tr_info *parent;
5850724Scg};
5950724Scg
6070325Scgstruct tr_rchinfo {
6170325Scg	u_int32_t delta;
6274763Scg	struct snd_dbuf *buffer;
6374763Scg	struct pcm_channel *channel;
6470325Scg	struct tr_info *parent;
6570325Scg};
6670325Scg
6750724Scg/* device private data */
6850724Scgstruct tr_info {
6950724Scg	u_int32_t type;
7050724Scg
7150724Scg	bus_space_tag_t st;
7250724Scg	bus_space_handle_t sh;
7350724Scg	bus_dma_tag_t parent_dmat;
7450724Scg
7550724Scg	struct resource *reg, *irq;
7650724Scg	int		regtype, regid, irqid;
7750724Scg	void		*ih;
7850724Scg
7974763Scg	void *lock;
8074763Scg
8150724Scg	u_int32_t playchns;
8250724Scg	struct tr_chinfo chinfo[TR_MAXPLAYCH];
8370325Scg	struct tr_rchinfo recchinfo;
8450724Scg};
8550724Scg
8650724Scg/* -------------------------------------------------------------------- */
8750724Scg
8864881Scgstatic u_int32_t tr_recfmt[] = {
8964881Scg	AFMT_U8,
9064881Scg	AFMT_STEREO | AFMT_U8,
9164881Scg	AFMT_S8,
9264881Scg	AFMT_STEREO | AFMT_S8,
9364881Scg	AFMT_S16_LE,
9464881Scg	AFMT_STEREO | AFMT_S16_LE,
9564881Scg	AFMT_U16_LE,
9664881Scg	AFMT_STEREO | AFMT_U16_LE,
9764881Scg	0
9850724Scg};
9974763Scgstatic struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0};
10050724Scg
10164881Scgstatic u_int32_t tr_playfmt[] = {
10264881Scg	AFMT_U8,
10364881Scg	AFMT_STEREO | AFMT_U8,
10464881Scg	AFMT_S8,
10564881Scg	AFMT_STEREO | AFMT_S8,
10664881Scg	AFMT_S16_LE,
10764881Scg	AFMT_STEREO | AFMT_S16_LE,
10864881Scg	AFMT_U16_LE,
10964881Scg	AFMT_STEREO | AFMT_U16_LE,
11064881Scg	0
11150724Scg};
11274763Scgstatic struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0};
11350724Scg
11450724Scg/* -------------------------------------------------------------------- */
11550724Scg
11650724Scg/* Hardware */
11750724Scg
11850724Scgstatic u_int32_t
11950724Scgtr_rd(struct tr_info *tr, int regno, int size)
12050724Scg{
12150724Scg	switch(size) {
12250724Scg	case 1:
12350724Scg		return bus_space_read_1(tr->st, tr->sh, regno);
12450724Scg	case 2:
12550724Scg		return bus_space_read_2(tr->st, tr->sh, regno);
12650724Scg	case 4:
12750724Scg		return bus_space_read_4(tr->st, tr->sh, regno);
12850724Scg	default:
12950724Scg		return 0xffffffff;
13050724Scg	}
13150724Scg}
13250724Scg
13350724Scgstatic void
13450724Scgtr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
13550724Scg{
13650724Scg	switch(size) {
13750724Scg	case 1:
13850724Scg		bus_space_write_1(tr->st, tr->sh, regno, data);
13950724Scg		break;
14050724Scg	case 2:
14150724Scg		bus_space_write_2(tr->st, tr->sh, regno, data);
14250724Scg		break;
14350724Scg	case 4:
14450724Scg		bus_space_write_4(tr->st, tr->sh, regno, data);
14550724Scg		break;
14650724Scg	}
14750724Scg}
14850724Scg
14970134Scg/* -------------------------------------------------------------------- */
15050724Scg/* ac97 codec */
15150724Scg
15270134Scgstatic int
15370134Scgtr_rdcd(kobj_t obj, void *devinfo, int regno)
15450724Scg{
15550724Scg	struct tr_info *tr = (struct tr_info *)devinfo;
15650724Scg	int i, j, treg, trw;
15750724Scg
15850724Scg	switch (tr->type) {
15950724Scg	case TDX_PCI_ID:
16050724Scg		treg=TDX_REG_CODECRD;
16150724Scg		trw=TDX_CDC_RWSTAT;
16250724Scg		break;
16350724Scg	case TNX_PCI_ID:
16450724Scg		treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
16550724Scg		trw=TNX_CDC_RWSTAT;
16650724Scg		break;
16750724Scg	default:
16850724Scg		printf("!!! tr_rdcd defaulted !!!\n");
16970134Scg		return -1;
17050724Scg	}
17150724Scg
17250724Scg	regno &= 0x7f;
17374763Scg	snd_mtxlock(tr->lock);
17450724Scg	tr_wr(tr, treg, regno | trw, 4);
17550724Scg	j=trw;
17650724Scg	for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
17774763Scg	snd_mtxunlock(tr->lock);
17850724Scg	if (i == 0) printf("codec timeout during read of register %x\n", regno);
17950724Scg	return (j >> TR_CDC_DATA) & 0xffff;
18050724Scg}
18150724Scg
18270134Scgstatic int
18370134Scgtr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
18450724Scg{
18550724Scg	struct tr_info *tr = (struct tr_info *)devinfo;
18650724Scg	int i, j, treg, trw;
18750724Scg
18850724Scg	switch (tr->type) {
18950724Scg	case TDX_PCI_ID:
19050724Scg		treg=TDX_REG_CODECWR;
19150724Scg		trw=TDX_CDC_RWSTAT;
19250724Scg		break;
19350724Scg	case TNX_PCI_ID:
19450724Scg		treg=TNX_REG_CODECWR;
19550724Scg		trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
19650724Scg		break;
19750724Scg	default:
19850724Scg		printf("!!! tr_wrcd defaulted !!!");
19970134Scg		return -1;
20050724Scg	}
20150724Scg
20250724Scg	regno &= 0x7f;
20350724Scg#if 0
20450724Scg	printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
20550724Scg#endif
20650724Scg	j=trw;
20774763Scg	snd_mtxlock(tr->lock);
20850724Scg	for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
20950724Scg	tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
21050724Scg#if 0
21150724Scg	printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
21250724Scg#endif
21374763Scg	snd_mtxunlock(tr->lock);
21450724Scg	if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
21570134Scg	return (i > 0)? 0 : -1;
21650724Scg}
21750724Scg
21870134Scgstatic kobj_method_t tr_ac97_methods[] = {
21970134Scg    	KOBJMETHOD(ac97_read,		tr_rdcd),
22070134Scg    	KOBJMETHOD(ac97_write,		tr_wrcd),
22170134Scg	{ 0, 0 }
22270134Scg};
22370134ScgAC97_DECLARE(tr_ac97);
22470134Scg
22570134Scg/* -------------------------------------------------------------------- */
22650724Scg/* playback channel interrupts */
22750724Scg
22871503Scg#if 0
22950724Scgstatic u_int32_t
23070325Scgtr_testint(struct tr_chinfo *ch)
23150724Scg{
23270325Scg	struct tr_info *tr = ch->parent;
23370325Scg	int bank, chan;
23470325Scg
23570325Scg	bank = (ch->index & 0x20) ? 1 : 0;
23670325Scg	chan = ch->index & 0x1f;
23770325Scg	return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan);
23850724Scg}
23971503Scg#endif
24050724Scg
24150724Scgstatic void
24270325Scgtr_clrint(struct tr_chinfo *ch)
24350724Scg{
24470325Scg	struct tr_info *tr = ch->parent;
24570325Scg	int bank, chan;
24670325Scg
24770325Scg	bank = (ch->index & 0x20) ? 1 : 0;
24870325Scg	chan = ch->index & 0x1f;
24970325Scg	tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4);
25050724Scg}
25150724Scg
25250724Scgstatic void
25370325Scgtr_enaint(struct tr_chinfo *ch, int enable)
25450724Scg{
25570325Scg	struct tr_info *tr = ch->parent;
25670325Scg       	u_int32_t i, reg;
25770325Scg	int bank, chan;
25870325Scg
25974763Scg	snd_mtxlock(tr->lock);
26070325Scg	bank = (ch->index & 0x20) ? 1 : 0;
26170325Scg	chan = ch->index & 0x1f;
26270325Scg	reg = bank? TR_REG_INTENB : TR_REG_INTENA;
26370325Scg
26470325Scg	i = tr_rd(tr, reg, 4);
26570325Scg	i &= ~(1 << chan);
26670325Scg	i |= (enable? 1 : 0) << chan;
26770325Scg
26870325Scg	tr_clrint(ch);
26950724Scg	tr_wr(tr, reg, i, 4);
27074763Scg	snd_mtxunlock(tr->lock);
27150724Scg}
27250724Scg
27350724Scg/* playback channels */
27450724Scg
27550724Scgstatic void
27670325Scgtr_selch(struct tr_chinfo *ch)
27750724Scg{
27870325Scg	struct tr_info *tr = ch->parent;
27970325Scg	int i;
28070325Scg
28170325Scg	i = tr_rd(tr, TR_REG_CIR, 4);
28250724Scg	i &= ~TR_CIR_MASK;
28370325Scg	i |= ch->index & 0x3f;
28450724Scg	tr_wr(tr, TR_REG_CIR, i, 4);
28550724Scg}
28650724Scg
28750724Scgstatic void
28870325Scgtr_startch(struct tr_chinfo *ch)
28950724Scg{
29070325Scg	struct tr_info *tr = ch->parent;
29170325Scg	int bank, chan;
29270325Scg
29370325Scg	bank = (ch->index & 0x20) ? 1 : 0;
29470325Scg	chan = ch->index & 0x1f;
29570325Scg	tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4);
29650724Scg}
29750724Scg
29850724Scgstatic void
29970325Scgtr_stopch(struct tr_chinfo *ch)
30050724Scg{
30170325Scg	struct tr_info *tr = ch->parent;
30270325Scg	int bank, chan;
30370325Scg
30470325Scg	bank = (ch->index & 0x20) ? 1 : 0;
30570325Scg	chan = ch->index & 0x1f;
30670325Scg	tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4);
30750724Scg}
30850724Scg
30950724Scgstatic void
31070325Scgtr_wrch(struct tr_chinfo *ch)
31150724Scg{
31270325Scg	struct tr_info *tr = ch->parent;
31350724Scg	u_int32_t cr[TR_CHN_REGS], i;
31450724Scg
31550724Scg	ch->gvsel 	&= 0x00000001;
31650724Scg	ch->fmc		&= 0x00000003;
31750724Scg	ch->fms		&= 0x0000000f;
31850724Scg	ch->ctrl	&= 0x0000000f;
31950724Scg	ch->pan 	&= 0x0000007f;
32050724Scg	ch->rvol	&= 0x0000007f;
32150724Scg	ch->cvol 	&= 0x0000007f;
32250724Scg	ch->vol		&= 0x000000ff;
32350724Scg	ch->ec		&= 0x00000fff;
32450724Scg	ch->alpha	&= 0x00000fff;
32550724Scg	ch->delta	&= 0x0000ffff;
32650724Scg	ch->lba		&= 0x3fffffff;
32750724Scg
32850724Scg	cr[1]=ch->lba;
32971503Scg	cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol);
33071503Scg	cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec);
33150724Scg
33250724Scg	switch (tr->type) {
33350724Scg	case TDX_PCI_ID:
33450724Scg		ch->cso &= 0x0000ffff;
33550724Scg		ch->eso &= 0x0000ffff;
33650724Scg		cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
33750724Scg		cr[2]=(ch->eso<<16) | (ch->delta);
33850724Scg		break;
33950724Scg	case TNX_PCI_ID:
34050724Scg		ch->cso &= 0x00ffffff;
34150724Scg		ch->eso &= 0x00ffffff;
34250724Scg		cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
34350724Scg		cr[2]=((ch->delta>>16)<<24) | (ch->eso);
34450724Scg		cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
34550724Scg		break;
34650724Scg	}
34774763Scg	snd_mtxlock(tr->lock);
34870325Scg	tr_selch(ch);
34950724Scg	for (i=0; i<TR_CHN_REGS; i++)
35050724Scg		tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
35174763Scg	snd_mtxunlock(tr->lock);
35250724Scg}
35350724Scg
35450724Scgstatic void
35570325Scgtr_rdch(struct tr_chinfo *ch)
35650724Scg{
35770325Scg	struct tr_info *tr = ch->parent;
35850724Scg	u_int32_t cr[5], i;
35970325Scg
36074763Scg	snd_mtxlock(tr->lock);
36170325Scg	tr_selch(ch);
36270325Scg	for (i=0; i<5; i++)
36370325Scg		cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
36474763Scg	snd_mtxunlock(tr->lock);
36570325Scg
36670325Scg
36750724Scg	ch->lba=	(cr[1] & 0x3fffffff);
36850724Scg	ch->fmc=	(cr[3] & 0x0000c000) >> 14;
36950724Scg	ch->rvol=	(cr[3] & 0x00003f80) >> 7;
37050724Scg	ch->cvol=	(cr[3] & 0x0000007f);
37150724Scg	ch->gvsel=	(cr[4] & 0x80000000) >> 31;
37250724Scg	ch->pan=	(cr[4] & 0x7f000000) >> 24;
37350724Scg	ch->vol=	(cr[4] & 0x00ff0000) >> 16;
37450724Scg	ch->ctrl=	(cr[4] & 0x0000f000) >> 12;
37550724Scg	ch->ec=		(cr[4] & 0x00000fff);
37650724Scg	switch(tr->type) {
37750724Scg	case TDX_PCI_ID:
37850724Scg		ch->cso=	(cr[0] & 0xffff0000) >> 16;
37950724Scg		ch->alpha=	(cr[0] & 0x0000fff0) >> 4;
38050724Scg		ch->fms=	(cr[0] & 0x0000000f);
38150724Scg		ch->eso=	(cr[2] & 0xffff0000) >> 16;
38250724Scg		ch->delta=	(cr[2] & 0x0000ffff);
38350724Scg		break;
38450724Scg	case TNX_PCI_ID:
38550724Scg		ch->cso=	(cr[0] & 0x00ffffff);
38650724Scg		ch->eso=	(cr[2] & 0x00ffffff);
38770325Scg		ch->delta=	((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24);
38850724Scg		ch->alpha=	(cr[3] & 0xfff00000) >> 20;
38950724Scg		ch->fms=	(cr[3] & 0x000f0000) >> 16;
39050724Scg		break;
39150724Scg	}
39250724Scg}
39350724Scg
39471503Scgstatic u_int32_t
39571503Scgtr_fmttobits(u_int32_t fmt)
39671503Scg{
39771503Scg	u_int32_t bits;
39871503Scg
39971503Scg	bits = 0;
40071503Scg	bits |= (fmt & AFMT_SIGNED)? 0x2 : 0;
40171503Scg	bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
40271503Scg	bits |= (fmt & AFMT_16BIT)? 0x8 : 0;
40371503Scg
40471503Scg	return bits;
40571503Scg}
40671503Scg
40770134Scg/* -------------------------------------------------------------------- */
40850724Scg/* channel interface */
40950724Scg
41070134Scgstatic void *
41174763Scgtrpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
41250724Scg{
41350724Scg	struct tr_info *tr = devinfo;
41450724Scg	struct tr_chinfo *ch;
41570325Scg
41670325Scg	KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction"));
41770325Scg	ch = &tr->chinfo[tr->playchns];
41870325Scg	ch->index = tr->playchns++;
41950724Scg	ch->buffer = b;
42050724Scg	ch->parent = tr;
42150724Scg	ch->channel = c;
42270325Scg	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1)
42370325Scg		return NULL;
42470325Scg
42570325Scg	return ch;
42650724Scg}
42750724Scg
42850724Scgstatic int
42970325Scgtrpchan_setformat(kobj_t obj, void *data, u_int32_t format)
43050724Scg{
43150724Scg	struct tr_chinfo *ch = data;
43270325Scg
43370325Scg	ch->ctrl = tr_fmttobits(format) | 0x01;
43470325Scg
43570325Scg	return 0;
43670325Scg}
43770325Scg
43870325Scgstatic int
43970325Scgtrpchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
44070325Scg{
44170325Scg	struct tr_chinfo *ch = data;
44270325Scg
44370325Scg	ch->delta = (speed << 12) / 48000;
44470325Scg	return (ch->delta * 48000) >> 12;
44570325Scg}
44670325Scg
44770325Scgstatic int
44870325Scgtrpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
44970325Scg{
45070325Scg	struct tr_chinfo *ch = data;
45170325Scg
45270325Scg	sndbuf_resize(ch->buffer, 2, blocksize);
45370325Scg	return blocksize;
45470325Scg}
45570325Scg
45670325Scgstatic int
45770325Scgtrpchan_trigger(kobj_t obj, void *data, int go)
45870325Scg{
45970325Scg	struct tr_chinfo *ch = data;
46070325Scg
46170325Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
46270325Scg		return 0;
46370325Scg
46470325Scg	if (go == PCMTRIG_START) {
46571503Scg		ch->fmc = 3;
46671503Scg		ch->fms = 0;
46771503Scg		ch->ec = 0;
46871503Scg		ch->alpha = 0;
46970291Scg		ch->lba = vtophys(sndbuf_getbuf(ch->buffer));
47050724Scg		ch->cso = 0;
47170325Scg		ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1;
47271503Scg		ch->rvol = ch->cvol = 0x7f;
47350724Scg		ch->gvsel = 0;
47450724Scg		ch->pan = 0;
47550724Scg		ch->vol = 0;
47671503Scg		ch->bufhalf = 0;
47770325Scg   		tr_wrch(ch);
47870325Scg		tr_enaint(ch, 1);
47970325Scg		tr_startch(ch);
48070325Scg	} else
48170325Scg		tr_stopch(ch);
48270325Scg
48350724Scg	return 0;
48450724Scg}
48550724Scg
48650724Scgstatic int
48770325Scgtrpchan_getptr(kobj_t obj, void *data)
48850724Scg{
48950724Scg	struct tr_chinfo *ch = data;
49070325Scg
49170325Scg	tr_rdch(ch);
49270325Scg	return ch->cso * sndbuf_getbps(ch->buffer);
49370325Scg}
49470325Scg
49574763Scgstatic struct pcmchan_caps *
49670325Scgtrpchan_getcaps(kobj_t obj, void *data)
49770325Scg{
49870325Scg	return &tr_playcaps;
49970325Scg}
50070325Scg
50170325Scgstatic kobj_method_t trpchan_methods[] = {
50270325Scg    	KOBJMETHOD(channel_init,		trpchan_init),
50370325Scg    	KOBJMETHOD(channel_setformat,		trpchan_setformat),
50470325Scg    	KOBJMETHOD(channel_setspeed,		trpchan_setspeed),
50570325Scg    	KOBJMETHOD(channel_setblocksize,	trpchan_setblocksize),
50670325Scg    	KOBJMETHOD(channel_trigger,		trpchan_trigger),
50770325Scg    	KOBJMETHOD(channel_getptr,		trpchan_getptr),
50870325Scg    	KOBJMETHOD(channel_getcaps,		trpchan_getcaps),
50970325Scg	{ 0, 0 }
51070325Scg};
51170325ScgCHANNEL_DECLARE(trpchan);
51270325Scg
51370325Scg/* -------------------------------------------------------------------- */
51470325Scg/* rec channel interface */
51570325Scg
51670325Scgstatic void *
51774763Scgtrrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
51870325Scg{
51970325Scg	struct tr_info *tr = devinfo;
52070325Scg	struct tr_rchinfo *ch;
52170325Scg
52270325Scg	KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction"));
52370325Scg	ch = &tr->recchinfo;
52470325Scg	ch->buffer = b;
52570325Scg	ch->parent = tr;
52670325Scg	ch->channel = c;
52770325Scg	if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1)
52870325Scg		return NULL;
52970325Scg
53070325Scg	return ch;
53170325Scg}
53270325Scg
53370325Scgstatic int
53470325Scgtrrchan_setformat(kobj_t obj, void *data, u_int32_t format)
53570325Scg{
53670325Scg	struct tr_rchinfo *ch = data;
53750724Scg	struct tr_info *tr = ch->parent;
53870325Scg	u_int32_t i, bits;
53950724Scg
54070325Scg	bits = tr_fmttobits(format);
54170325Scg	/* set # of samples between interrupts */
54271503Scg	i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1;
54370325Scg	tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
54470325Scg	/* set sample format */
54570325Scg	i = 0x18 | (bits << 4);
54670325Scg	tr_wr(tr, TR_REG_SBCTRL, i, 1);
54770325Scg
54850724Scg	return 0;
54970325Scg
55050724Scg}
55150724Scg
55250724Scgstatic int
55370325Scgtrrchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
55450724Scg{
55570325Scg	struct tr_rchinfo *ch = data;
55650724Scg	struct tr_info *tr = ch->parent;
55750724Scg
55870325Scg	/* setup speed */
55970325Scg	ch->delta = (48000 << 12) / speed;
56070325Scg	tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
56170325Scg
56270325Scg	/* return closest possible speed */
56370325Scg	return (48000 << 12) / ch->delta;
56450724Scg}
56550724Scg
56650724Scgstatic int
56770325Scgtrrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
56850724Scg{
56970325Scg	struct tr_rchinfo *ch = data;
57070325Scg
57170325Scg	sndbuf_resize(ch->buffer, 2, blocksize);
57270325Scg
57370325Scg	return blocksize;
57450724Scg}
57550724Scg
57650724Scgstatic int
57770325Scgtrrchan_trigger(kobj_t obj, void *data, int go)
57850724Scg{
57970325Scg	struct tr_rchinfo *ch = data;
58050724Scg	struct tr_info *tr = ch->parent;
58170325Scg	u_int32_t i;
58259323Scg
58360958Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
58460958Scg		return 0;
58560958Scg
58670325Scg	if (go == PCMTRIG_START) {
58770325Scg		/* set up dma mode regs */
58870325Scg		tr_wr(tr, TR_REG_DMAR15, 0, 1);
58970325Scg		i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
59070325Scg		tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
59170325Scg		/* set up base address */
59270325Scg	   	tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4);
59370325Scg		/* set up buffer size */
59470325Scg		i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
59570325Scg		tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4);
59670325Scg		/* start */
59770325Scg		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1);
59870325Scg	} else
59970325Scg		tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1);
60070325Scg
60170325Scg	/* return 0 if ok */
60250724Scg	return 0;
60350724Scg}
60450724Scg
60550724Scgstatic int
60670325Scgtrrchan_getptr(kobj_t obj, void *data)
60750724Scg{
60870325Scg 	struct tr_rchinfo *ch = data;
60950724Scg	struct tr_info *tr = ch->parent;
61060958Scg
61170325Scg	/* return current byte offset of channel */
61270325Scg	return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer));
61350724Scg}
61450724Scg
61574763Scgstatic struct pcmchan_caps *
61670325Scgtrrchan_getcaps(kobj_t obj, void *data)
61750724Scg{
61870325Scg	return &tr_reccaps;
61950724Scg}
62050724Scg
62170325Scgstatic kobj_method_t trrchan_methods[] = {
62270325Scg    	KOBJMETHOD(channel_init,		trrchan_init),
62370325Scg    	KOBJMETHOD(channel_setformat,		trrchan_setformat),
62470325Scg    	KOBJMETHOD(channel_setspeed,		trrchan_setspeed),
62570325Scg    	KOBJMETHOD(channel_setblocksize,	trrchan_setblocksize),
62670325Scg    	KOBJMETHOD(channel_trigger,		trrchan_trigger),
62770325Scg    	KOBJMETHOD(channel_getptr,		trrchan_getptr),
62870325Scg    	KOBJMETHOD(channel_getcaps,		trrchan_getcaps),
62970134Scg	{ 0, 0 }
63070134Scg};
63170325ScgCHANNEL_DECLARE(trrchan);
63270134Scg
63370134Scg/* -------------------------------------------------------------------- */
63450724Scg/* The interrupt handler */
63550724Scg
63650724Scgstatic void
63750724Scgtr_intr(void *p)
63850724Scg{
63950724Scg	struct tr_info *tr = (struct tr_info *)p;
64070325Scg	struct tr_chinfo *ch;
64171503Scg	u_int32_t active, mask, bufhalf, chnum, intsrc;
64271503Scg	int tmp;
64350724Scg
64470325Scg	intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
64550724Scg	if (intsrc & TR_INT_ADDR) {
64671503Scg		chnum = 0;
64771503Scg		while (chnum < 64) {
64871503Scg			mask = 0x00000001;
64971503Scg			active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4);
65071503Scg			bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4);
65171503Scg			if (active) {
65271503Scg				do {
65371503Scg					if (active & mask) {
65471503Scg						tmp = (bufhalf & mask)? 1 : 0;
65571503Scg						if (chnum < tr->playchns) {
65671503Scg							ch = &tr->chinfo[chnum];
65771503Scg							/* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */
65871503Scg							if (ch->bufhalf != tmp) {
65971503Scg								chn_intr(ch->channel);
66071503Scg								ch->bufhalf = tmp;
66174763Scg							}
66271503Scg						}
66371503Scg					}
66471503Scg					chnum++;
66571503Scg					mask <<= 1;
66671503Scg				} while (chnum & 31);
66771503Scg			} else
66871503Scg				chnum += 32;
66971503Scg
67071503Scg			tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4);
67150724Scg		}
67250724Scg	}
67350724Scg	if (intsrc & TR_INT_SB) {
67450724Scg		chn_intr(tr->recchinfo.channel);
67550724Scg		tr_rd(tr, TR_REG_SBR9, 1);
67650724Scg		tr_rd(tr, TR_REG_SBR10, 1);
67750724Scg	}
67850724Scg}
67950724Scg
68050724Scg/* -------------------------------------------------------------------- */
68150724Scg
68250724Scg/*
68350724Scg * Probe and attach the card
68450724Scg */
68550724Scg
68650724Scgstatic int
68750724Scgtr_init(struct tr_info *tr)
68850724Scg{
68950724Scg	if (tr->type == TDX_PCI_ID) {
69050724Scg		tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
69150724Scg	} else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
69250724Scg
69350724Scg	tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
69450724Scg	tr->playchns = 0;
69550724Scg	return 0;
69650724Scg}
69750724Scg
69850724Scgstatic int
69950724Scgtr_pci_probe(device_t dev)
70050724Scg{
70150724Scg	if (pci_get_devid(dev) == TDX_PCI_ID) {
70250724Scg		device_set_desc(dev, "Trident 4DWave DX");
70350724Scg		return 0;
70450724Scg	}
70550724Scg	if (pci_get_devid(dev) == TNX_PCI_ID) {
70650724Scg		device_set_desc(dev, "Trident 4DWave NX");
70750724Scg		return 0;
70850724Scg	}
70950724Scg
71050724Scg	return ENXIO;
71150724Scg}
71250724Scg
71350724Scgstatic int
71450724Scgtr_pci_attach(device_t dev)
71550724Scg{
71650724Scg	u_int32_t	data;
71750724Scg	struct tr_info *tr;
71866012Scg	struct ac97_info *codec = 0;
71950724Scg	int		i;
72050724Scg	char 		status[SND_STATUSLEN];
72150724Scg
72250724Scg	if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
72350724Scg		device_printf(dev, "cannot allocate softc\n");
72450724Scg		return ENXIO;
72550724Scg	}
72650724Scg
72750724Scg	bzero(tr, sizeof(*tr));
72850724Scg	tr->type = pci_get_devid(dev);
72974763Scg	tr->lock = snd_mtxcreate(device_get_nameunit(dev));
73050724Scg
73150724Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
73250724Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
73350724Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
73450724Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
73550724Scg
73670325Scg	tr->regid = PCIR_MAPS;
73770325Scg	tr->regtype = SYS_RES_IOPORT;
73870325Scg	tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE);
73970325Scg	if (tr->reg) {
74070325Scg		tr->st = rman_get_bustag(tr->reg);
74170325Scg		tr->sh = rman_get_bushandle(tr->reg);
74270325Scg	} else {
74350724Scg		device_printf(dev, "unable to map register space\n");
74450724Scg		goto bad;
74550724Scg	}
74650724Scg
74750724Scg	if (tr_init(tr) == -1) {
74850724Scg		device_printf(dev, "unable to initialize the card\n");
74950724Scg		goto bad;
75050724Scg	}
75150724Scg
75270134Scg	codec = AC97_CREATE(dev, tr, tr_ac97);
75350724Scg	if (codec == NULL) goto bad;
75470134Scg	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
75550724Scg
75650724Scg	tr->irqid = 0;
75750724Scg	tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
75850724Scg				 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
75974763Scg	if (!tr->irq || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih)) {
76050724Scg		device_printf(dev, "unable to map interrupt\n");
76150724Scg		goto bad;
76250724Scg	}
76350724Scg
76450724Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
76550724Scg		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
76650724Scg		/*highaddr*/BUS_SPACE_MAXADDR,
76750724Scg		/*filter*/NULL, /*filterarg*/NULL,
76850724Scg		/*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
76950724Scg		/*flags*/0, &tr->parent_dmat) != 0) {
77050724Scg		device_printf(dev, "unable to create dma tag\n");
77150724Scg		goto bad;
77250724Scg	}
77350724Scg
77470325Scg	snprintf(status, 64, "at io 0x%lx irq %ld",
77550724Scg		 rman_get_start(tr->reg), rman_get_start(tr->irq));
77650724Scg
77750724Scg	if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
77870325Scg	pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr);
77950724Scg	for (i = 0; i < TR_MAXPLAYCH; i++)
78070325Scg		pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr);
78150724Scg	pcm_setstatus(dev, status);
78250724Scg
78350724Scg	return 0;
78450724Scg
78550724Scgbad:
78665644Scg	if (codec) ac97_destroy(codec);
78750724Scg	if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
78850724Scg	if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
78950724Scg	if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
79065644Scg	if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat);
79174763Scg	if (tr->lock) snd_mtxfree(tr->lock);
79250724Scg	free(tr, M_DEVBUF);
79350724Scg	return ENXIO;
79450724Scg}
79550724Scg
79665644Scgstatic int
79765644Scgtr_pci_detach(device_t dev)
79865644Scg{
79965644Scg	int r;
80065644Scg	struct tr_info *tr;
80165644Scg
80265644Scg	r = pcm_unregister(dev);
80365644Scg	if (r)
80465644Scg		return r;
80565644Scg
80665644Scg	tr = pcm_getdevinfo(dev);
80765644Scg	bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
80865644Scg	bus_teardown_intr(dev, tr->irq, tr->ih);
80965644Scg	bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
81065644Scg	bus_dma_tag_destroy(tr->parent_dmat);
81174763Scg	snd_mtxfree(tr->lock);
81265644Scg	free(tr, M_DEVBUF);
81365644Scg
81465644Scg	return 0;
81565644Scg}
81665644Scg
81750724Scgstatic device_method_t tr_methods[] = {
81850724Scg	/* Device interface */
81950724Scg	DEVMETHOD(device_probe,		tr_pci_probe),
82050724Scg	DEVMETHOD(device_attach,	tr_pci_attach),
82165644Scg	DEVMETHOD(device_detach,	tr_pci_detach),
82250724Scg
82350724Scg	{ 0, 0 }
82450724Scg};
82550724Scg
82650724Scgstatic driver_t tr_driver = {
82750724Scg	"pcm",
82850724Scg	tr_methods,
82974763Scg	sizeof(struct snddev_info),
83050724Scg};
83150724Scg
83250724Scgstatic devclass_t pcm_devclass;
83350724Scg
83462483ScgDRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0);
83562483ScgMODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
83662483ScgMODULE_VERSION(snd_t4dwave, 1);
837