fm801.c revision 70134
165205Scg/*
265205Scg * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
365205Scg * All rights reserved.
465205Scg *
565205Scg * Redistribution and use in source and binary forms, with or without
665205Scg * modification, are permitted provided that the following conditions
765205Scg * are met:
865205Scg * 1. Redistributions of source code must retain the above copyright
965205Scg *    notice, this list of conditions and the following disclaimer.
1065205Scg * 2. Redistributions in binary form must reproduce the above copyright
1165205Scg *    notice, this list of conditions and the following disclaimer in the
1265205Scg *    documentation and/or other materials provided with the distribution.
1365205Scg *
1465205Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
1565205Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1665205Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1765205Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1865205Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1965205Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2065205Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2165205Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2265205Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2365205Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2465205Scg * SUCH DAMAGE.
2565205Scg *
2665205Scg * $FreeBSD: head/sys/dev/sound/pci/fm801.c 70134 2000-12-18 01:36:41Z cg $
2765205Scg */
2865205Scg
2965205Scg#include <dev/sound/pcm/sound.h>
3065205Scg#include <dev/sound/pcm/ac97.h>
3165205Scg#include <pci/pcireg.h>
3265205Scg#include <pci/pcivar.h>
3365205Scg
3465205Scg#define PCI_VENDOR_FORTEMEDIA	0x1319
3565205Scg#define PCI_DEVICE_FORTEMEDIA1	0x08011319
3665205Scg#define PCI_DEVICE_FORTEMEDIA2	0x08021319	/* ??? have no idea what's this... */
3765205Scg
3865205Scg#define FM_PCM_VOLUME           0x00
3965205Scg#define FM_FM_VOLUME            0x02
4065205Scg#define FM_I2S_VOLUME           0x04
4165205Scg#define FM_RECORD_SOURCE        0x06
4265205Scg
4365205Scg#define FM_PLAY_CTL             0x08
4465205Scg#define  FM_PLAY_RATE_MASK              0x0f00
4565205Scg#define  FM_PLAY_BUF1_LAST              0x0001
4665205Scg#define  FM_PLAY_BUF2_LAST              0x0002
4765205Scg#define  FM_PLAY_START                  0x0020
4865205Scg#define  FM_PLAY_PAUSE                  0x0040
4965205Scg#define  FM_PLAY_STOPNOW                0x0080
5065205Scg#define  FM_PLAY_16BIT                  0x4000
5165205Scg#define  FM_PLAY_STEREO                 0x8000
5265205Scg
5365205Scg#define FM_PLAY_DMALEN          0x0a
5465205Scg#define FM_PLAY_DMABUF1         0x0c
5565205Scg#define FM_PLAY_DMABUF2         0x10
5665205Scg
5765205Scg
5865205Scg#define FM_REC_CTL              0x14
5965205Scg#define  FM_REC_RATE_MASK               0x0f00
6065205Scg#define  FM_REC_BUF1_LAST               0x0001
6165205Scg#define  FM_REC_BUF2_LAST               0x0002
6265205Scg#define  FM_REC_START                   0x0020
6365205Scg#define  FM_REC_PAUSE                   0x0040
6465205Scg#define  FM_REC_STOPNOW                 0x0080
6565205Scg#define  FM_REC_16BIT                   0x4000
6665205Scg#define  FM_REC_STEREO                  0x8000
6765205Scg
6865205Scg
6965205Scg#define FM_REC_DMALEN           0x16
7065205Scg#define FM_REC_DMABUF1          0x18
7165205Scg#define FM_REC_DMABUF2          0x1c
7265205Scg
7365205Scg#define FM_CODEC_CTL            0x22
7465205Scg#define FM_VOLUME               0x26
7565205Scg#define  FM_VOLUME_MUTE                 0x8000
7665205Scg
7765205Scg#define FM_CODEC_CMD            0x2a
7865205Scg#define  FM_CODEC_CMD_READ              0x0080
7965205Scg#define  FM_CODEC_CMD_VALID             0x0100
8065205Scg#define  FM_CODEC_CMD_BUSY              0x0200
8165205Scg
8265205Scg#define FM_CODEC_DATA           0x2c
8365205Scg
8465205Scg#define FM_IO_CTL               0x52
8565205Scg#define FM_CARD_CTL             0x54
8665205Scg
8765205Scg#define FM_INTMASK              0x56
8865205Scg#define  FM_INTMASK_PLAY                0x0001
8965205Scg#define  FM_INTMASK_REC                 0x0002
9065205Scg#define  FM_INTMASK_VOL                 0x0040
9165205Scg#define  FM_INTMASK_MPU                 0x0080
9265205Scg
9365205Scg#define FM_INTSTATUS            0x5a
9465205Scg#define  FM_INTSTATUS_PLAY              0x0100
9565205Scg#define  FM_INTSTATUS_REC               0x0200
9665205Scg#define  FM_INTSTATUS_VOL               0x4000
9765205Scg#define  FM_INTSTATUS_MPU               0x8000
9865205Scg
9965205Scg#define FM801_BUFFSIZE 1024*4	/* Other values do not work!!! */
10065205Scg
10165205Scg/* debug purposes */
10265205Scg#define DPRINT	 if(0) printf
10365205Scg
10465205Scg/*
10565205Scgstatic int fm801ch_setup(pcm_channel *c);
10665205Scg*/
10765205Scg
10865205Scgstatic u_int32_t fmts[] = {
10965205Scg	AFMT_U8,
11065205Scg	AFMT_STEREO | AFMT_U8,
11165205Scg	AFMT_S16_LE,
11265644Scg	AFMT_STEREO | AFMT_S16_LE,
11365205Scg	0
11465205Scg};
11565205Scg
11665205Scgstatic pcmchan_caps fm801ch_caps = {
11765205Scg	4000, 48000,
11865205Scg	fmts, 0
11965205Scg};
12065205Scg
12165205Scgstruct fm801_info;
12265205Scg
12365205Scgstruct fm801_chinfo {
12465205Scg	struct fm801_info 	*parent;
12565205Scg	pcm_channel 		*channel;
12665205Scg	snd_dbuf 		*buffer;
12765205Scg	u_int32_t 		spd, dir, fmt;	/* speed, direction, format */
12865205Scg	u_int32_t		shift;
12965205Scg};
13065205Scg
13165205Scgstruct fm801_info {
13265205Scg	int 			type;
13365205Scg	bus_space_tag_t 	st;
13465205Scg	bus_space_handle_t 	sh;
13565205Scg	bus_dma_tag_t   	parent_dmat;
13665205Scg
13765205Scg	device_t 		dev;
13865205Scg	int 			num;
13965205Scg	u_int32_t 		unit;
14065205Scg
14165205Scg	struct resource 	*reg, *irq;
14265205Scg	int             	regtype, regid, irqid;
14365205Scg	void            	*ih;
14465205Scg
14565205Scg	u_int32_t		play_flip,
14665205Scg				play_nextblk,
14765205Scg				play_start,
14865205Scg				play_blksize,
14965205Scg				play_fmt,
15065205Scg				play_shift,
15165205Scg				play_size;
15265205Scg
15365205Scg	u_int32_t		rec_flip,
15465205Scg				rec_nextblk,
15565205Scg				rec_start,
15665205Scg				rec_blksize,
15765205Scg				rec_fmt,
15865205Scg				rec_shift,
15965205Scg				rec_size;
16065205Scg
16165205Scg	struct fm801_chinfo 	pch, rch;
16265205Scg};
16365205Scg
16465205Scg/* Bus Read / Write routines */
16565205Scgstatic u_int32_t
16665205Scgfm801_rd(struct fm801_info *fm801, int regno, int size)
16765205Scg{
16865205Scg	switch(size) {
16965205Scg	case 1:
17065205Scg		return (bus_space_read_1(fm801->st, fm801->sh, regno));
17165205Scg	case 2:
17265205Scg		return (bus_space_read_2(fm801->st, fm801->sh, regno));
17365205Scg	case 4:
17465205Scg		return (bus_space_read_4(fm801->st, fm801->sh, regno));
17565205Scg	default:
17665205Scg		return 0xffffffff;
17765205Scg	}
17865205Scg}
17965205Scg
18065205Scgstatic void
18165205Scgfm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
18265205Scg{
18365205Scg	switch(size) {
18465205Scg	case 1:
18565205Scg		return bus_space_write_1(fm801->st, fm801->sh, regno, data);
18665205Scg	case 2:
18765205Scg		return bus_space_write_2(fm801->st, fm801->sh, regno, data);
18865205Scg	case 4:
18965205Scg		return bus_space_write_4(fm801->st, fm801->sh, regno, data);
19065205Scg	default:
19165205Scg		return;
19265205Scg	}
19365205Scg}
19465205Scg
19570134Scg/* -------------------------------------------------------------------- */
19665205Scg/*
19765205Scg *  ac97 codec routines
19865205Scg */
19965205Scg#define TIMO 50
20070134Scgstatic int
20170134Scgfm801_rdcd(kobj_t obj, void *devinfo, int regno)
20265205Scg{
20365205Scg	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
20465205Scg	int i;
20565340Scg
20665205Scg	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
20765205Scg		DELAY(10000);
20865205Scg		DPRINT("fm801 rdcd: 1 - DELAY\n");
20965205Scg	}
21065205Scg	if (i >= TIMO) {
21165205Scg		printf("fm801 rdcd: codec busy\n");
21265205Scg		return 0;
21365205Scg	}
21465340Scg
21565205Scg	fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
21665205Scg
21765205Scg	for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
21865205Scg	{
21965205Scg		DELAY(10000);
22065205Scg		DPRINT("fm801 rdcd: 2 - DELAY\n");
22165205Scg	}
22265205Scg	if (i >= TIMO) {
22365205Scg		printf("fm801 rdcd: write codec invalid\n");
22465205Scg		return 0;
22565205Scg	}
22665340Scg
22765205Scg	return fm801_rd(fm801,FM_CODEC_DATA,2);
22865205Scg}
22965205Scg
23070134Scgstatic int
23170134Scgfm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
23265205Scg{
23365205Scg	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
23465205Scg	int i;
23565340Scg
23665205Scg	DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
23765340Scg/*
23865205Scg	if(regno == AC97_REG_RECSEL)	return;
23965340Scg*/
24065205Scg	/* Poll until codec is ready */
24165205Scg	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
24265205Scg		DELAY(10000);
24365205Scg		DPRINT("fm801 rdcd: 1 - DELAY\n");
24465205Scg	}
24565205Scg	if (i >= TIMO) {
24665205Scg		printf("fm801 wrcd: read codec busy\n");
24770134Scg		return -1;
24865205Scg	}
24965340Scg
25065205Scg	fm801_wr(fm801,FM_CODEC_DATA,data, 2);
25165205Scg	fm801_wr(fm801,FM_CODEC_CMD, regno,2);
25265340Scg
25365205Scg	/* wait until codec is ready */
25465205Scg	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
25565205Scg		DELAY(10000);
25665205Scg		DPRINT("fm801 wrcd: 2 - DELAY\n");
25765205Scg	}
25865205Scg	if (i >= TIMO) {
25965205Scg		printf("fm801 wrcd: read codec busy\n");
26070134Scg		return -1;
26165205Scg	}
26265205Scg	DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
26370134Scg	return 0;
26465205Scg}
26565205Scg
26670134Scgstatic kobj_method_t fm801_ac97_methods[] = {
26770134Scg    	KOBJMETHOD(ac97_read,		fm801_rdcd),
26870134Scg    	KOBJMETHOD(ac97_write,		fm801_wrcd),
26970134Scg	{ 0, 0 }
27070134Scg};
27170134ScgAC97_DECLARE(fm801_ac97);
27270134Scg
27370134Scg/* -------------------------------------------------------------------- */
27470134Scg
27565340Scg/*
27665340Scg * The interrupt handler
27765205Scg */
27865205Scgstatic void
27965205Scgfm801_intr(void *p)
28065205Scg{
28165205Scg	struct fm801_info 	*fm801 = (struct fm801_info *)p;
28265205Scg	u_int32_t       	intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
28365205Scg	struct fm801_chinfo	*ch = &(fm801->pch);
28465205Scg	snd_dbuf 		*b = ch->buffer;
28565340Scg
28665205Scg	DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
28765205Scg	DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n",
28865205Scg		b->rp,b->rl, b->fp,b->fl, b->blksz);
28965340Scg
29065205Scg	if(intsrc & FM_INTSTATUS_PLAY) {
29165205Scg		fm801->play_flip++;
29265205Scg		if(fm801->play_flip & 1) {
29365205Scg			fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
29465205Scg		} else
29565205Scg			fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
29665205Scg		chn_intr(fm801->pch.channel);
29765205Scg	}
29865340Scg
29965205Scg	if(intsrc & FM_INTSTATUS_REC) {
30065205Scg		fm801->rec_flip++;
30165205Scg		if(fm801->rec_flip & 1) {
30265205Scg			fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
30365205Scg		} else
30465205Scg			fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
30565205Scg		chn_intr(fm801->rch.channel);
30665205Scg	}
30765340Scg
30865205Scg	if ( intsrc & FM_INTSTATUS_MPU ) {
30965205Scg		/* This is a TODOish thing... */
31065205Scg		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
31165205Scg	}
31265340Scg
31365205Scg	if ( intsrc & FM_INTSTATUS_VOL ) {
31465205Scg		/* This is a TODOish thing... */
31565205Scg		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
31665205Scg	}
31765340Scg
31865205Scg	DPRINT("fm801_intr clear\n\n");
31965205Scg	fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
32065205Scg}
32165205Scg
32270134Scg/* -------------------------------------------------------------------- */
32365205Scg/* channel interface */
32465205Scgstatic void *
32570134Scgfm801ch_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
32665205Scg{
32765205Scg	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
32865205Scg	struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
32965340Scg
33065205Scg	DPRINT("fm801ch_init, direction = %d\n", dir);
33165205Scg	ch->parent = fm801;
33265205Scg	ch->channel = c;
33365205Scg	ch->buffer = b;
33465205Scg	ch->buffer->bufsize = FM801_BUFFSIZE;
33565205Scg	ch->dir = dir;
33665205Scg	if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL;
33765205Scg	return (void *)ch;
33865205Scg}
33965205Scg
34065205Scgstatic int
34170134Scgfm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
34265205Scg{
34365205Scg	struct fm801_chinfo *ch = data;
34465205Scg	struct fm801_info *fm801 = ch->parent;
34565340Scg
34665205Scg	DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
34765205Scg		(format & AFMT_STEREO)?"stereo":"mono",
34865205Scg		(format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
34965205Scg		(format & AFMT_SIGNED)? "signed":"unsigned",
35065205Scg		(format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
35165340Scg
35265205Scg	if(ch->dir == PCMDIR_PLAY) {
35365205Scg		fm801->play_fmt =  (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
35465205Scg		fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
35565205Scg		return 0;
35665205Scg	}
35765340Scg
35865205Scg	if(ch->dir == PCMDIR_REC ) {
35965205Scg		fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
36065205Scg		fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
36165205Scg		return 0;
36265205Scg	}
36365340Scg
36465205Scg	return 0;
36565205Scg}
36665205Scg
36765205Scgstruct {
36865205Scg	int limit;
36965205Scg	int rate;
37065205Scg} fm801_rates[11] = {
37165340Scg	{  6600,  5500 },
37265340Scg	{  8750,  8000 },
37365340Scg	{ 10250,  9600 },
37465340Scg	{ 13200, 11025 },
37565340Scg	{ 17500, 16000 },
37665340Scg	{ 20500, 19200 },
37765340Scg	{ 26500, 22050 },
37865340Scg	{ 35000, 32000 },
37965340Scg	{ 41000, 38400 },
38065340Scg	{ 46000, 44100 },
38165340Scg	{ 48000, 48000 },
38265205Scg/* anything above -> 48000 */
38365205Scg};
38465205Scg
38565205Scgstatic int
38670134Scgfm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
38765205Scg{
38865205Scg	struct fm801_chinfo *ch = data;
38965205Scg	struct fm801_info *fm801 = ch->parent;
39065205Scg	register int i;
39165340Scg
39265340Scg
39365205Scg	for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
39465340Scg
39565205Scg	if(ch->dir == PCMDIR_PLAY) {
39665205Scg		fm801->pch.spd = fm801_rates[i].rate;
39765205Scg		fm801->play_shift = (i<<8);
39865205Scg		fm801->play_shift &= FM_PLAY_RATE_MASK;
39965205Scg	}
40065340Scg
40165205Scg	if(ch->dir == PCMDIR_REC ) {
40265205Scg		fm801->rch.spd = fm801_rates[i].rate;
40365205Scg		fm801->rec_shift = (i<<8);
40465205Scg		fm801->rec_shift &= FM_REC_RATE_MASK;
40565205Scg	}
40665340Scg
40765205Scg	ch->spd = fm801_rates[i].rate;
40865340Scg
40965205Scg	return fm801_rates[i].rate;
41065205Scg}
41165205Scg
41265205Scgstatic int
41370134Scgfm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
41465205Scg{
41565205Scg	struct fm801_chinfo *ch = data;
41665205Scg	struct fm801_info *fm801 = ch->parent;
41765340Scg
41865205Scg	if(ch->dir == PCMDIR_PLAY) {
41965205Scg		if(fm801->play_flip) return fm801->play_blksize;
42065205Scg		fm801->play_blksize = blocksize;
42165205Scg	}
42265340Scg
42365205Scg	if(ch->dir == PCMDIR_REC) {
42465205Scg		if(fm801->rec_flip) return fm801->rec_blksize;
42565205Scg		fm801->rec_blksize = blocksize;
42665205Scg	}
42765340Scg
42865205Scg	DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
42965205Scg
43065205Scg	return blocksize;
43165205Scg}
43265205Scg
43365205Scgstatic int
43470134Scgfm801ch_trigger(kobj_t obj, void *data, int go)
43565205Scg{
43665205Scg	struct fm801_chinfo *ch = data;
43765205Scg	struct fm801_info *fm801 = ch->parent;
43865205Scg	u_int32_t baseaddr = vtophys(ch->buffer->buf);
43965205Scg	snd_dbuf *b = ch->buffer;
44065205Scg	u_int32_t k1;
44165340Scg
44265205Scg	DPRINT("fm801ch_trigger go %d , ", go);
44365205Scg	DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n",
44465205Scg		b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz);
44565340Scg
44665205Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
44765205Scg		return 0;
44865205Scg	}
44965340Scg
45065205Scg	if (ch->dir == PCMDIR_PLAY) {
45165205Scg		if (go == PCMTRIG_START) {
45265340Scg
45365205Scg			fm801->play_start = baseaddr;
45465205Scg			fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
45565205Scg			fm801->play_flip = 0;
45665205Scg			fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
45765205Scg			fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
45865205Scg			fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
45965205Scg			fm801_wr(fm801, FM_PLAY_CTL,
46065340Scg					FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
46165205Scg					2 );
46265205Scg			} else {
46365205Scg			fm801->play_flip = 0;
46465205Scg			k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
46565205Scg			fm801_wr(fm801, FM_PLAY_CTL,
46665205Scg				(k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
46765205Scg				FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
46865205Scg		}
46965205Scg	} else if(ch->dir == PCMDIR_REC) {
47065205Scg		if (go == PCMTRIG_START) {
47165205Scg			fm801->rec_start = baseaddr;
47265205Scg			fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
47365205Scg			fm801->rec_flip = 0;
47465205Scg			fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
47565205Scg			fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
47665205Scg			fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
47765340Scg			fm801_wr(fm801, FM_REC_CTL,
47865340Scg					FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
47965205Scg					2 );
48065205Scg			} else {
48165205Scg			fm801->rec_flip = 0;
48265205Scg			k1 = fm801_rd(fm801, FM_REC_CTL,2);
48365205Scg			fm801_wr(fm801, FM_REC_CTL,
48465205Scg				(k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
48565205Scg				FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
48665205Scg		}
48765205Scg	}
48865340Scg
48965205Scg	return 0;
49065205Scg}
49165205Scg
49265205Scg/* Almost ALSA copy */
49365205Scgstatic int
49470134Scgfm801ch_getptr(kobj_t obj, void *data)
49565205Scg{
49665205Scg	struct fm801_chinfo *ch = data;
49765205Scg	struct fm801_info *fm801 = ch->parent;
49865205Scg	int result = 0;
49965205Scg	snd_dbuf *b = ch->buffer;
50065340Scg
50165205Scg	if (ch->dir == PCMDIR_PLAY) {
50265340Scg		result = fm801_rd(fm801,
50365340Scg			(fm801->play_flip&1) ?
50465205Scg			FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
50565205Scg	}
50665340Scg
50765205Scg	if (ch->dir == PCMDIR_REC) {
50865205Scg		result = fm801_rd(fm801,
50965205Scg			(fm801->rec_flip&1) ?
51065205Scg			FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
51165340Scg	}
51265340Scg
51365205Scg	DPRINT("fm801ch_getptr:%d,  rp %d, rl %d, fp %d fl %d\n",
51465340Scg	                result, b->rp,b->rl, b->fp,b->fl);
51565205Scg
51665205Scg	return result;
51765205Scg}
51865205Scg
51965205Scgstatic pcmchan_caps *
52070134Scgfm801ch_getcaps(kobj_t obj, void *data)
52165205Scg{
52265205Scg	return &fm801ch_caps;
52365205Scg}
52465205Scg
52570134Scgstatic kobj_method_t fm801ch_methods[] = {
52670134Scg    	KOBJMETHOD(channel_init,		fm801ch_init),
52770134Scg    	KOBJMETHOD(channel_setformat,		fm801ch_setformat),
52870134Scg    	KOBJMETHOD(channel_setspeed,		fm801ch_setspeed),
52970134Scg    	KOBJMETHOD(channel_setblocksize,	fm801ch_setblocksize),
53070134Scg    	KOBJMETHOD(channel_trigger,		fm801ch_trigger),
53170134Scg    	KOBJMETHOD(channel_getptr,		fm801ch_getptr),
53270134Scg    	KOBJMETHOD(channel_getcaps,		fm801ch_getcaps),
53370134Scg	{ 0, 0 }
53470134Scg};
53570134ScgCHANNEL_DECLARE(fm801ch);
53670134Scg
53770134Scg/* -------------------------------------------------------------------- */
53870134Scg
53970134Scg/*
54070134Scg *  Init routine is taken from an original NetBSD driver
54170134Scg */
54270134Scgstatic int
54370134Scgfm801_init(struct fm801_info *fm801)
54470134Scg{
54570134Scg	u_int32_t k1;
54670134Scg
54770134Scg	/* reset codec */
54870134Scg	fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
54970134Scg	DELAY(100000);
55070134Scg	fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
55170134Scg	DELAY(100000);
55270134Scg
55370134Scg	fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
55470134Scg	fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
55570134Scg	fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
55670134Scg	fm801_wr(fm801, 0x40,0x107f,2);	/* enable legacy audio */
55770134Scg
55870134Scg	fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
55970134Scg
56070134Scg	/* Unmask playback, record and mpu interrupts, mask the rest */
56170134Scg	k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
56270134Scg	fm801_wr(fm801, FM_INTMASK,
56370134Scg		(k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
56470134Scg		FM_INTMASK_VOL,2);
56570134Scg	fm801_wr(fm801, FM_INTSTATUS,
56670134Scg		FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
56770134Scg		FM_INTSTATUS_VOL,2);
56870134Scg
56970134Scg	DPRINT("FM801 init Ok\n");
57070134Scg	return 0;
57170134Scg}
57270134Scg
57370134Scgstatic int
57470134Scgfm801_pci_attach(device_t dev)
57570134Scg{
57670134Scg	u_int32_t 		data;
57770134Scg	struct ac97_info 	*codec = 0;
57870134Scg	struct fm801_info 	*fm801;
57970134Scg	int 			i;
58070134Scg	int 			mapped = 0;
58170134Scg	char 			status[SND_STATUSLEN];
58270134Scg
58370134Scg	if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) {
58470134Scg		device_printf(dev, "cannot allocate softc\n");
58570134Scg		return ENXIO;
58670134Scg	}
58770134Scg
58870134Scg	bzero(fm801, sizeof(*fm801));
58970134Scg	fm801->type = pci_get_devid(dev);
59070134Scg
59170134Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
59270134Scg	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
59370134Scg	pci_write_config(dev, PCIR_COMMAND, data, 2);
59470134Scg	data = pci_read_config(dev, PCIR_COMMAND, 2);
59570134Scg
59670134Scg	for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
59770134Scg		fm801->regid = PCIR_MAPS + i*4;
59870134Scg		fm801->regtype = SYS_RES_MEMORY;
59970134Scg		fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
60070134Scg						0, ~0, 1, RF_ACTIVE);
60170134Scg		if(!fm801->reg)
60270134Scg		{
60370134Scg			fm801->regtype = SYS_RES_IOPORT;
60470134Scg			fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
60570134Scg						0, ~0, 1, RF_ACTIVE);
60670134Scg		}
60770134Scg
60870134Scg		if(fm801->reg) {
60970134Scg			fm801->st = rman_get_bustag(fm801->reg);
61070134Scg			fm801->sh = rman_get_bushandle(fm801->reg);
61170134Scg			mapped++;
61270134Scg		}
61370134Scg	}
61470134Scg
61570134Scg	if (mapped == 0) {
61670134Scg		device_printf(dev, "unable to map register space\n");
61770134Scg		goto oops;
61870134Scg	}
61970134Scg
62070134Scg	fm801_init(fm801);
62170134Scg
62270134Scg	codec = AC97_CREATE(dev, fm801, fm801_ac97);
62370134Scg	if (codec == NULL) goto oops;
62470134Scg
62570134Scg	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
62670134Scg
62770134Scg	fm801->irqid = 0;
62870134Scg	fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid,
62970134Scg				0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
63070134Scg	if (!fm801->irq ||
63170134Scg		bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY,
63270134Scg					fm801_intr, fm801, &fm801->ih)) {
63370134Scg		device_printf(dev, "unable to map interrupt\n");
63470134Scg		goto oops;
63570134Scg	}
63670134Scg
63770134Scg	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
63870134Scg		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
63970134Scg		/*highaddr*/BUS_SPACE_MAXADDR,
64070134Scg		/*filter*/NULL, /*filterarg*/NULL,
64170134Scg		/*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
64270134Scg		/*flags*/0, &fm801->parent_dmat) != 0) {
64370134Scg		device_printf(dev, "unable to create dma tag\n");
64470134Scg		goto oops;
64570134Scg	}
64670134Scg
64770134Scg	snprintf(status, 64, "at %s 0x%lx irq %ld",
64870134Scg		(fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
64970134Scg		rman_get_start(fm801->reg), rman_get_start(fm801->irq));
65070134Scg
65170134Scg#define FM801_MAXPLAYCH	1
65270134Scg	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
65370134Scg	pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
65470134Scg	pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
65570134Scg	pcm_setstatus(dev, status);
65670134Scg
65770134Scg	return 0;
65870134Scg
65970134Scgoops:
66070134Scg	if (codec) ac97_destroy(codec);
66170134Scg	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
66270134Scg	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
66370134Scg	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
66470134Scg	if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
66570134Scg	free(fm801, M_DEVBUF);
66670134Scg	return ENXIO;
66770134Scg}
66870134Scg
66970134Scgstatic int
67070134Scgfm801_pci_detach(device_t dev)
67170134Scg{
67270134Scg	int r;
67370134Scg	struct fm801_info *fm801;
67470134Scg
67570134Scg	DPRINT("Forte Media FM801 detach\n");
67670134Scg
67770134Scg	r = pcm_unregister(dev);
67870134Scg	if (r)
67970134Scg		return r;
68070134Scg
68170134Scg	fm801 = pcm_getdevinfo(dev);
68270134Scg	bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
68370134Scg	bus_teardown_intr(dev, fm801->irq, fm801->ih);
68470134Scg	bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
68570134Scg	bus_dma_tag_destroy(fm801->parent_dmat);
68670134Scg	free(fm801, M_DEVBUF);
68770134Scg	return 0;
68870134Scg}
68970134Scg
69070134Scgstatic int
69170134Scgfm801_pci_probe( device_t dev )
69270134Scg{
69370134Scg	int id;
69470134Scg	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
69570134Scg		device_set_desc(dev, "Forte Media FM801 Audio Controller");
69670134Scg		return 0;
69770134Scg	}
69870134Scg/*
69970134Scg	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
70070134Scg		device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
70170134Scg		return ENXIO;
70270134Scg	}
70370134Scg*/
70470134Scg	return ENXIO;
70570134Scg}
70670134Scg
70765205Scgstatic device_method_t fm801_methods[] = {
70865205Scg	/* Device interface */
70965205Scg	DEVMETHOD(device_probe,		fm801_pci_probe),
71065205Scg	DEVMETHOD(device_attach,	fm801_pci_attach),
71165205Scg	DEVMETHOD(device_detach,	fm801_pci_detach),
71265205Scg	{ 0, 0}
71365205Scg};
71465205Scg
71565205Scgstatic driver_t fm801_driver = {
71665205Scg	"pcm",
71765205Scg	fm801_methods,
71865205Scg	sizeof(snddev_info),
71965205Scg};
72065205Scg
72165205Scgstatic devclass_t pcm_devclass;
72265205Scg
72365644ScgDRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0);
724