fm801.c revision 117126
11541Srgrimes/*
21541Srgrimes * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
31541Srgrimes * All rights reserved.
41541Srgrimes *
551138Salfred * Redistribution and use in source and binary forms, with or without
691694Srwatson * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
1164002Speter *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241541Srgrimes * SUCH DAMAGE.
251541Srgrimes */
261541Srgrimes
271541Srgrimes#include <dev/sound/pcm/sound.h>
281541Srgrimes#include <dev/sound/pcm/ac97.h>
291541Srgrimes#include <pci/pcireg.h>
301541Srgrimes#include <pci/pcivar.h>
311541Srgrimes
321541SrgrimesSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/fm801.c 117126 2003-07-01 15:52:06Z scottl $");
331541Srgrimes
341541Srgrimes#define PCI_VENDOR_FORTEMEDIA	0x1319
351541Srgrimes#define PCI_DEVICE_FORTEMEDIA1	0x08011319
361541Srgrimes#define PCI_DEVICE_FORTEMEDIA2	0x08021319	/* ??? have no idea what's this... */
371541Srgrimes
381541Srgrimes#define FM_PCM_VOLUME           0x00
391541Srgrimes#define FM_FM_VOLUME            0x02
401541Srgrimes#define FM_I2S_VOLUME           0x04
411541Srgrimes#define FM_RECORD_SOURCE        0x06
421541Srgrimes
431541Srgrimes#define FM_PLAY_CTL             0x08
441541Srgrimes#define  FM_PLAY_RATE_MASK              0x0f00
451541Srgrimes#define  FM_PLAY_BUF1_LAST              0x0001
461541Srgrimes#define  FM_PLAY_BUF2_LAST              0x0002
471541Srgrimes#define  FM_PLAY_START                  0x0020
481541Srgrimes#define  FM_PLAY_PAUSE                  0x0040
491541Srgrimes#define  FM_PLAY_STOPNOW                0x0080
501541Srgrimes#define  FM_PLAY_16BIT                  0x4000
511541Srgrimes#define  FM_PLAY_STEREO                 0x8000
521541Srgrimes
531541Srgrimes#define FM_PLAY_DMALEN          0x0a
541541Srgrimes#define FM_PLAY_DMABUF1         0x0c
551541Srgrimes#define FM_PLAY_DMABUF2         0x10
5652150Smarcel
571541Srgrimes
5852150Smarcel#define FM_REC_CTL              0x14
591541Srgrimes#define  FM_REC_RATE_MASK               0x0f00
601541Srgrimes#define  FM_REC_BUF1_LAST               0x0001
611541Srgrimes#define  FM_REC_BUF2_LAST               0x0002
6252150Smarcel#define  FM_REC_START                   0x0020
631541Srgrimes#define  FM_REC_PAUSE                   0x0040
641541Srgrimes#define  FM_REC_STOPNOW                 0x0080
651541Srgrimes#define  FM_REC_16BIT                   0x4000
661541Srgrimes#define  FM_REC_STEREO                  0x8000
671541Srgrimes
681541Srgrimes
691541Srgrimes#define FM_REC_DMALEN           0x16
701541Srgrimes#define FM_REC_DMABUF1          0x18
711541Srgrimes#define FM_REC_DMABUF2          0x1c
721541Srgrimes
731541Srgrimes#define FM_CODEC_CTL            0x22
741541Srgrimes#define FM_VOLUME               0x26
751541Srgrimes#define  FM_VOLUME_MUTE                 0x8000
761541Srgrimes
771541Srgrimes#define FM_CODEC_CMD            0x2a
781541Srgrimes#define  FM_CODEC_CMD_READ              0x0080
791541Srgrimes#define  FM_CODEC_CMD_VALID             0x0100
801541Srgrimes#define  FM_CODEC_CMD_BUSY              0x0200
811541Srgrimes
821541Srgrimes#define FM_CODEC_DATA           0x2c
831541Srgrimes
841541Srgrimes#define FM_IO_CTL               0x52
851541Srgrimes#define FM_CARD_CTL             0x54
861541Srgrimes
871541Srgrimes#define FM_INTMASK              0x56
881541Srgrimes#define  FM_INTMASK_PLAY                0x0001
891541Srgrimes#define  FM_INTMASK_REC                 0x0002
901541Srgrimes#define  FM_INTMASK_VOL                 0x0040
911541Srgrimes#define  FM_INTMASK_MPU                 0x0080
921541Srgrimes
931541Srgrimes#define FM_INTSTATUS            0x5a
941541Srgrimes#define  FM_INTSTATUS_PLAY              0x0100
951541Srgrimes#define  FM_INTSTATUS_REC               0x0200
961541Srgrimes#define  FM_INTSTATUS_VOL               0x4000
971541Srgrimes#define  FM_INTSTATUS_MPU               0x8000
981541Srgrimes
991541Srgrimes#define FM801_DEFAULT_BUFSZ	4096	/* Other values do not work!!! */
1001541Srgrimes
1011541Srgrimes/* debug purposes */
1021541Srgrimes#define DPRINT	 if(0) printf
1031541Srgrimes
1041541Srgrimes/*
1051541Srgrimesstatic int fm801ch_setup(struct pcm_channel *c);
1061541Srgrimes*/
1071541Srgrimes
1081541Srgrimesstatic u_int32_t fmts[] = {
1091541Srgrimes	AFMT_U8,
1101541Srgrimes	AFMT_STEREO | AFMT_U8,
1111541Srgrimes	AFMT_S16_LE,
1121541Srgrimes	AFMT_STEREO | AFMT_S16_LE,
11390074Sbde	0
1141541Srgrimes};
1151541Srgrimes
1161541Srgrimesstatic struct pcmchan_caps fm801ch_caps = {
1171541Srgrimes	4000, 48000,
1181541Srgrimes	fmts, 0
1191541Srgrimes};
1201541Srgrimes
12152150Smarcelstruct fm801_info;
1221541Srgrimes
1231541Srgrimesstruct fm801_chinfo {
1241541Srgrimes	struct fm801_info	*parent;
1251541Srgrimes	struct pcm_channel	*channel;
1261541Srgrimes	struct snd_dbuf		*buffer;
1271541Srgrimes	u_int32_t		spd, dir, fmt;  /* speed, direction, format */
1281541Srgrimes	u_int32_t		shift;
12914220Speter};
1301541Srgrimes
1311541Srgrimesstruct fm801_info {
1321541Srgrimes	int			type;
1331541Srgrimes	bus_space_tag_t		st;
1341541Srgrimes	bus_space_handle_t	sh;
1351541Srgrimes	bus_dma_tag_t		parent_dmat;
1368019Sache
1378019Sache	device_t		dev;
1381541Srgrimes	int			num;
1391541Srgrimes	u_int32_t		unit;
1401541Srgrimes
1411541Srgrimes	struct resource		*reg, *irq;
1421541Srgrimes	int			regtype, regid, irqid;
1431541Srgrimes	void			*ih;
1441541Srgrimes
1451541Srgrimes	u_int32_t		play_flip,
1461541Srgrimes				play_nextblk,
1471541Srgrimes				play_start,
1481541Srgrimes				play_blksize,
1491541Srgrimes				play_fmt,
1501541Srgrimes				play_shift,
1511541Srgrimes				play_size;
1521541Srgrimes
1531541Srgrimes	u_int32_t		rec_flip,
1541541Srgrimes				rec_nextblk,
1551541Srgrimes				rec_start,
1561541Srgrimes				rec_blksize,
1571541Srgrimes				rec_fmt,
1581541Srgrimes				rec_shift,
1591541Srgrimes				rec_size;
1601541Srgrimes
16114220Speter	unsigned int		bufsz;
16214220Speter
16314220Speter	struct fm801_chinfo	pch, rch;
1641541Srgrimes
1651541Srgrimes	device_t		radio;
1661541Srgrimes};
1671541Srgrimes
1681541Srgrimes/* Bus Read / Write routines */
1691541Srgrimesstatic u_int32_t
1701541Srgrimesfm801_rd(struct fm801_info *fm801, int regno, int size)
1711541Srgrimes{
1721549Srgrimes	switch(size) {
1731549Srgrimes	case 1:
1741549Srgrimes		return (bus_space_read_1(fm801->st, fm801->sh, regno));
1751549Srgrimes	case 2:
1762442Sdg		return (bus_space_read_2(fm801->st, fm801->sh, regno));
1771541Srgrimes	case 4:
1781541Srgrimes		return (bus_space_read_4(fm801->st, fm801->sh, regno));
1792729Sdfr	default:
1802729Sdfr		return 0xffffffff;
1811541Srgrimes	}
1821541Srgrimes}
18345065Salc
18445065Salcstatic void
1852858Swollmanfm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
1862297Swollman{
18714220Speter
18814220Speter	switch(size) {
18914220Speter	case 1:
1901541Srgrimes		bus_space_write_1(fm801->st, fm801->sh, regno, data);
1911541Srgrimes		break;
1921541Srgrimes	case 2:
1931541Srgrimes		bus_space_write_2(fm801->st, fm801->sh, regno, data);
19432889Sphk		break;
19532889Sphk	case 4:
19632889Sphk		bus_space_write_4(fm801->st, fm801->sh, regno, data);
19732889Sphk		break;
1981541Srgrimes	}
1991541Srgrimes}
2001541Srgrimes
2011541Srgrimes/* -------------------------------------------------------------------- */
2021541Srgrimes/*
2031541Srgrimes *  ac97 codec routines
2041541Srgrimes */
2051541Srgrimes#define TIMO 50
2061541Srgrimesstatic int
2071541Srgrimesfm801_rdcd(kobj_t obj, void *devinfo, int regno)
2081541Srgrimes{
2091541Srgrimes	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
2101541Srgrimes	int i;
2111541Srgrimes
2121541Srgrimes	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
2131541Srgrimes		DELAY(10000);
2141541Srgrimes		DPRINT("fm801 rdcd: 1 - DELAY\n");
21535938Sdyson	}
21635938Sdyson	if (i >= TIMO) {
21728400Speter		printf("fm801 rdcd: codec busy\n");
21825582Speter		return 0;
21929349Speter	}
2202124Sdg
2212124Sdg	fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
2222124Sdg
2232124Sdg	for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
2242124Sdg	{
2252124Sdg		DELAY(10000);
2262124Sdg		DPRINT("fm801 rdcd: 2 - DELAY\n");
2272124Sdg	}
2282124Sdg	if (i >= TIMO) {
2292124Sdg		printf("fm801 rdcd: write codec invalid\n");
23012865Speter		return 0;
23112865Speter	}
23212865Speter
23359829Speter	return fm801_rd(fm801,FM_CODEC_DATA,2);
23412865Speter}
23512865Speter
23612865Speterstatic int
23712865Speterfm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
23812865Speter{
23912865Speter	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
24012865Speter	int i;
24112865Speter
24225582Speter	DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
24325582Speter/*
24425582Speter	if(regno == AC97_REG_RECSEL)	return;
24525582Speter*/
24625582Speter	/* Poll until codec is ready */
24725582Speter	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
24825582Speter		DELAY(10000);
24925582Speter		DPRINT("fm801 rdcd: 1 - DELAY\n");
25025582Speter	}
25114220Speter	if (i >= TIMO) {
25214220Speter		printf("fm801 wrcd: read codec busy\n");
25314220Speter		return -1;
25414220Speter	}
25514220Speter
25614220Speter	fm801_wr(fm801,FM_CODEC_DATA,data, 2);
25714220Speter	fm801_wr(fm801,FM_CODEC_CMD, regno,2);
25814220Speter
25914220Speter	/* wait until codec is ready */
26014220Speter	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
26114220Speter		DELAY(10000);
26229349Speter		DPRINT("fm801 wrcd: 2 - DELAY\n");
26324452Speter	}
26424440Speter	if (i >= TIMO) {
26525537Sdfr		printf("fm801 wrcd: read codec busy\n");
26625537Sdfr		return -1;
26725537Sdfr	}
26825537Sdfr	DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
26925537Sdfr	return 0;
27025537Sdfr}
27125537Sdfr
27225537Sdfrstatic kobj_method_t fm801_ac97_methods[] = {
27325537Sdfr    	KOBJMETHOD(ac97_read,		fm801_rdcd),
27425537Sdfr    	KOBJMETHOD(ac97_write,		fm801_wrcd),
27525537Sdfr	{ 0, 0 }
27625537Sdfr};
27725537SdfrAC97_DECLARE(fm801_ac97);
27825537Sdfr
27925537Sdfr/* -------------------------------------------------------------------- */
28025537Sdfr
28125537Sdfr/*
28235938Sdyson * The interrupt handler
28325537Sdfr */
28435938Sdysonstatic void
28535938Sdysonfm801_intr(void *p)
28635938Sdyson{
28735938Sdyson	struct fm801_info 	*fm801 = (struct fm801_info *)p;
28835938Sdyson	u_int32_t       	intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
28935938Sdyson
29035938Sdyson	DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
29125537Sdfr
29225537Sdfr	if(intsrc & FM_INTSTATUS_PLAY) {
29325537Sdfr		fm801->play_flip++;
29425537Sdfr		if(fm801->play_flip & 1) {
29525537Sdfr			fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
29625537Sdfr		} else
29725537Sdfr			fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
29825537Sdfr		chn_intr(fm801->pch.channel);
29925537Sdfr	}
30025537Sdfr
30125537Sdfr	if(intsrc & FM_INTSTATUS_REC) {
30225537Sdfr		fm801->rec_flip++;
30325537Sdfr		if(fm801->rec_flip & 1) {
30425537Sdfr			fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
30525537Sdfr		} else
30625537Sdfr			fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
30751138Salfred		chn_intr(fm801->rch.channel);
30851138Salfred	}
30951138Salfred
31025537Sdfr	if ( intsrc & FM_INTSTATUS_MPU ) {
31125537Sdfr		/* This is a TODOish thing... */
31225537Sdfr		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
31325537Sdfr	}
31425537Sdfr
31525537Sdfr	if ( intsrc & FM_INTSTATUS_VOL ) {
31625537Sdfr		/* This is a TODOish thing... */
31725537Sdfr		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
31825537Sdfr	}
31925537Sdfr
32028400Speter	DPRINT("fm801_intr clear\n\n");
32156115Speter	fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
32256115Speter}
32336034Speter
32426671Sdyson/* -------------------------------------------------------------------- */
32526671Sdyson/* channel interface */
32626671Sdysonstatic void *
32726671Sdysonfm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
32826671Sdyson{
32926671Sdyson	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
33026671Sdyson	struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
33126671Sdyson
33269514Sjake	DPRINT("fm801ch_init, direction = %d\n", dir);
33369514Sjake	ch->parent = fm801;
33426671Sdyson	ch->channel = c;
33526671Sdyson	ch->buffer = b;
33629391Sphk	ch->dir = dir;
33734925Sdufault	if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) == -1) return NULL;
33834925Sdufault	return (void *)ch;
33934925Sdufault}
34034925Sdufault
34134925Sdufaultstatic int
34234925Sdufaultfm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
34334925Sdufault{
34434925Sdufault	struct fm801_chinfo *ch = data;
34535938Sdyson	struct fm801_info *fm801 = ch->parent;
34640931Sdg
34741089Speter	DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
34846155Sphk		(format & AFMT_STEREO)?"stereo":"mono",
34949420Sjkh		(format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
35051791Smarcel		(format & AFMT_SIGNED)? "signed":"unsigned",
35151791Smarcel		(format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
35251791Smarcel
35351791Smarcel	if(ch->dir == PCMDIR_PLAY) {
35451791Smarcel		fm801->play_fmt =  (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
35551791Smarcel		fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
35651791Smarcel		return 0;
35756271Srwatson	}
35856271Srwatson
35956271Srwatson	if(ch->dir == PCMDIR_REC ) {
36056271Srwatson		fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
36156271Srwatson		fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
36256271Srwatson		return 0;
36356271Srwatson	}
36456271Srwatson
36554803Srwatson	return 0;
36654803Srwatson}
36754803Srwatson
36854803Srwatsonstruct {
36955943Sjasone	int limit;
37056115Speter	int rate;
37156115Speter} fm801_rates[11] = {
37259288Sjlemon	{  6600,  5500 },
37359288Sjlemon	{  8750,  8000 },
37461719Srwatson	{ 10250,  9600 },
37561719Srwatson	{ 13200, 11025 },
37663083Srwatson	{ 17500, 16000 },
37763083Srwatson	{ 20500, 19200 },
37863083Srwatson	{ 26500, 22050 },
37963083Srwatson	{ 35000, 32000 },
38069449Salfred	{ 41000, 38400 },
38175039Srwatson	{ 46000, 44100 },
38275039Srwatson	{ 48000, 48000 },
38375039Srwatson/* anything above -> 48000 */
38475427Srwatson};
38583652Speter
38683796Srwatsonstatic int
38784884Srwatsonfm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
38885891Sphk{
38990889Sjulian	struct fm801_chinfo *ch = data;
39090889Sjulian	struct fm801_info *fm801 = ch->parent;
39190889Sjulian	register int i;
39290889Sjulian
39390889Sjulian
39491694Srwatson	for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
39591694Srwatson
39691694Srwatson	if(ch->dir == PCMDIR_PLAY) {
39791694Srwatson		fm801->pch.spd = fm801_rates[i].rate;
39891694Srwatson		fm801->play_shift = (i<<8);
39991694Srwatson		fm801->play_shift &= FM_PLAY_RATE_MASK;
4001541Srgrimes	}
401
402	if(ch->dir == PCMDIR_REC ) {
403		fm801->rch.spd = fm801_rates[i].rate;
404		fm801->rec_shift = (i<<8);
405		fm801->rec_shift &= FM_REC_RATE_MASK;
406	}
407
408	ch->spd = fm801_rates[i].rate;
409
410	return fm801_rates[i].rate;
411}
412
413static int
414fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
415{
416	struct fm801_chinfo *ch = data;
417	struct fm801_info *fm801 = ch->parent;
418
419	if(ch->dir == PCMDIR_PLAY) {
420		if(fm801->play_flip) return fm801->play_blksize;
421		fm801->play_blksize = blocksize;
422	}
423
424	if(ch->dir == PCMDIR_REC) {
425		if(fm801->rec_flip) return fm801->rec_blksize;
426		fm801->rec_blksize = blocksize;
427	}
428
429	DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
430
431	return blocksize;
432}
433
434static int
435fm801ch_trigger(kobj_t obj, void *data, int go)
436{
437	struct fm801_chinfo *ch = data;
438	struct fm801_info *fm801 = ch->parent;
439	u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer);
440	u_int32_t k1;
441
442	DPRINT("fm801ch_trigger go %d , ", go);
443
444	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
445		return 0;
446	}
447
448	if (ch->dir == PCMDIR_PLAY) {
449		if (go == PCMTRIG_START) {
450
451			fm801->play_start = baseaddr;
452			fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
453			fm801->play_flip = 0;
454			fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
455			fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
456			fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
457			fm801_wr(fm801, FM_PLAY_CTL,
458					FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
459					2 );
460			} else {
461			fm801->play_flip = 0;
462			k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
463			fm801_wr(fm801, FM_PLAY_CTL,
464				(k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
465				FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
466		}
467	} else if(ch->dir == PCMDIR_REC) {
468		if (go == PCMTRIG_START) {
469			fm801->rec_start = baseaddr;
470			fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
471			fm801->rec_flip = 0;
472			fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
473			fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
474			fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
475			fm801_wr(fm801, FM_REC_CTL,
476					FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
477					2 );
478			} else {
479			fm801->rec_flip = 0;
480			k1 = fm801_rd(fm801, FM_REC_CTL,2);
481			fm801_wr(fm801, FM_REC_CTL,
482				(k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
483				FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
484		}
485	}
486
487	return 0;
488}
489
490/* Almost ALSA copy */
491static int
492fm801ch_getptr(kobj_t obj, void *data)
493{
494	struct fm801_chinfo *ch = data;
495	struct fm801_info *fm801 = ch->parent;
496	int result = 0;
497
498	if (ch->dir == PCMDIR_PLAY) {
499		result = fm801_rd(fm801,
500			(fm801->play_flip&1) ?
501			FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
502	}
503
504	if (ch->dir == PCMDIR_REC) {
505		result = fm801_rd(fm801,
506			(fm801->rec_flip&1) ?
507			FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
508	}
509
510	return result;
511}
512
513static struct pcmchan_caps *
514fm801ch_getcaps(kobj_t obj, void *data)
515{
516	return &fm801ch_caps;
517}
518
519static kobj_method_t fm801ch_methods[] = {
520    	KOBJMETHOD(channel_init,		fm801ch_init),
521    	KOBJMETHOD(channel_setformat,		fm801ch_setformat),
522    	KOBJMETHOD(channel_setspeed,		fm801ch_setspeed),
523    	KOBJMETHOD(channel_setblocksize,	fm801ch_setblocksize),
524    	KOBJMETHOD(channel_trigger,		fm801ch_trigger),
525    	KOBJMETHOD(channel_getptr,		fm801ch_getptr),
526    	KOBJMETHOD(channel_getcaps,		fm801ch_getcaps),
527	{ 0, 0 }
528};
529CHANNEL_DECLARE(fm801ch);
530
531/* -------------------------------------------------------------------- */
532
533/*
534 *  Init routine is taken from an original NetBSD driver
535 */
536static int
537fm801_init(struct fm801_info *fm801)
538{
539	u_int32_t k1;
540
541	/* reset codec */
542	fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
543	DELAY(100000);
544	fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
545	DELAY(100000);
546
547	fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
548	fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
549	fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
550	fm801_wr(fm801, 0x40,0x107f,2);	/* enable legacy audio */
551
552	fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
553
554	/* Unmask playback, record and mpu interrupts, mask the rest */
555	k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
556	fm801_wr(fm801, FM_INTMASK,
557		(k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
558		FM_INTMASK_VOL,2);
559	fm801_wr(fm801, FM_INTSTATUS,
560		FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
561		FM_INTSTATUS_VOL,2);
562
563	DPRINT("FM801 init Ok\n");
564	return 0;
565}
566
567static int
568fm801_pci_attach(device_t dev)
569{
570	u_int32_t 		data;
571	struct ac97_info 	*codec = 0;
572	struct fm801_info 	*fm801;
573	int 			i;
574	int 			mapped = 0;
575	char 			status[SND_STATUSLEN];
576
577	if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
578		device_printf(dev, "cannot allocate softc\n");
579		return ENXIO;
580	}
581
582	fm801->type = pci_get_devid(dev);
583
584	data = pci_read_config(dev, PCIR_COMMAND, 2);
585	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
586	pci_write_config(dev, PCIR_COMMAND, data, 2);
587	data = pci_read_config(dev, PCIR_COMMAND, 2);
588
589	for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
590		fm801->regid = PCIR_MAPS + i*4;
591		fm801->regtype = SYS_RES_MEMORY;
592		fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
593						0, ~0, 1, RF_ACTIVE);
594		if(!fm801->reg)
595		{
596			fm801->regtype = SYS_RES_IOPORT;
597			fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
598						0, ~0, 1, RF_ACTIVE);
599		}
600
601		if(fm801->reg) {
602			fm801->st = rman_get_bustag(fm801->reg);
603			fm801->sh = rman_get_bushandle(fm801->reg);
604			mapped++;
605		}
606	}
607
608	if (mapped == 0) {
609		device_printf(dev, "unable to map register space\n");
610		goto oops;
611	}
612
613	fm801->bufsz = pcm_getbuffersize(dev, 4096, FM801_DEFAULT_BUFSZ, 65536);
614
615	fm801_init(fm801);
616
617	codec = AC97_CREATE(dev, fm801, fm801_ac97);
618	if (codec == NULL) goto oops;
619
620	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
621
622	fm801->irqid = 0;
623	fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid,
624				0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
625	if (!fm801->irq || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) {
626		device_printf(dev, "unable to map interrupt\n");
627		goto oops;
628	}
629
630	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
631		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
632		/*highaddr*/BUS_SPACE_MAXADDR,
633		/*filter*/NULL, /*filterarg*/NULL,
634		/*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
635		/*flags*/0, /*lockfunc*/busdma_lock_mutex,
636		/*lockarg*/&Giant, &fm801->parent_dmat) != 0) {
637		device_printf(dev, "unable to create dma tag\n");
638		goto oops;
639	}
640
641	snprintf(status, 64, "at %s 0x%lx irq %ld",
642		(fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
643		rman_get_start(fm801->reg), rman_get_start(fm801->irq));
644
645#define FM801_MAXPLAYCH	1
646	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
647	pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
648	pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
649	pcm_setstatus(dev, status);
650
651	fm801->radio = device_add_child(dev, "radio", -1);
652	bus_generic_attach(dev);
653
654	return 0;
655
656oops:
657	if (codec) ac97_destroy(codec);
658	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
659	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
660	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
661	if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
662	free(fm801, M_DEVBUF);
663	return ENXIO;
664}
665
666static int
667fm801_pci_detach(device_t dev)
668{
669	int r;
670	struct fm801_info *fm801;
671
672	DPRINT("Forte Media FM801 detach\n");
673
674	fm801 = pcm_getdevinfo(dev);
675
676	r = bus_generic_detach(dev);
677	if (r)
678		return r;
679	if (fm801->radio != NULL) {
680		r = device_delete_child(dev, fm801->radio);
681		if (r)
682			return r;
683		fm801->radio = NULL;
684	}
685
686	r = pcm_unregister(dev);
687	if (r)
688		return r;
689
690	bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
691	bus_teardown_intr(dev, fm801->irq, fm801->ih);
692	bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
693	bus_dma_tag_destroy(fm801->parent_dmat);
694	free(fm801, M_DEVBUF);
695	return 0;
696}
697
698static int
699fm801_pci_probe( device_t dev )
700{
701	u_int32_t data;
702	int id, regtype, regid, result;
703	struct resource *reg;
704	bus_space_tag_t st;
705	bus_space_handle_t sh;
706
707	result = ENXIO;
708
709	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
710		data = pci_read_config(dev, PCIR_COMMAND, 2);
711		data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN);
712		pci_write_config(dev, PCIR_COMMAND, data, 2);
713		data = pci_read_config(dev, PCIR_COMMAND, 2);
714
715		regid = PCIR_MAPS;
716		regtype = SYS_RES_IOPORT;
717		reg = bus_alloc_resource(dev, regtype, &regid, 0, ~0, 1,
718		    RF_ACTIVE);
719
720		if (reg == NULL)
721			return ENXIO;
722
723		st = rman_get_bustag(reg);
724		sh = rman_get_bushandle(reg);
725		/*
726		 * XXX: quick check that device actually has sound capabilities.
727		 * The problem is that some cards built around FM801 chip only
728		 * have radio tuner onboard, but no sound capabilities. There
729		 * is no "official" way to quickly check this, because all
730		 * IDs are exactly the same. The only difference is 0x28
731		 * device control register, described in FM801 specification
732		 * as "SRC/Mixer Test Control/DFC Status", but without
733		 * any more detailed explanation. According to specs, and
734		 * available sample cards (SF256-PCP-R and SF256-PCS-R) its
735		 * power-on value should be `0', while on AC97-less tuner
736		 * card (SF64-PCR) it was 0x80.
737		 */
738		if (bus_space_read_1(st, sh, 0x28) == 0) {
739			device_set_desc(dev,
740			    "Forte Media FM801 Audio Controller");
741			result = 0;
742		}
743
744		bus_release_resource(dev, regtype, regid, reg);
745	}
746/*
747	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
748		device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
749		return ENXIO;
750	}
751*/
752	return (result);
753}
754
755static struct resource *
756fm801_alloc_resource(device_t bus, device_t child, int type, int *rid,
757		     u_long start, u_long end, u_long count, u_int flags)
758{
759	struct fm801_info *fm801;
760
761	fm801 = pcm_getdevinfo(bus);
762
763	if (type == SYS_RES_IOPORT && *rid == PCIR_MAPS)
764		return (fm801->reg);
765
766	return (NULL);
767}
768
769static int
770fm801_release_resource(device_t bus, device_t child, int type, int rid,
771		       struct resource *r)
772{
773	return (0);
774}
775
776static device_method_t fm801_methods[] = {
777	/* Device interface */
778	DEVMETHOD(device_probe,		fm801_pci_probe),
779	DEVMETHOD(device_attach,	fm801_pci_attach),
780	DEVMETHOD(device_detach,	fm801_pci_detach),
781	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
782	DEVMETHOD(device_suspend,	bus_generic_suspend),
783	DEVMETHOD(device_resume,	bus_generic_resume),
784
785	/* Bus interface */
786	DEVMETHOD(bus_print_child,	bus_generic_print_child),
787	DEVMETHOD(bus_alloc_resource,	fm801_alloc_resource),
788	DEVMETHOD(bus_release_resource,	fm801_release_resource),
789	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
790	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
791	{ 0, 0}
792};
793
794static driver_t fm801_driver = {
795	"pcm",
796	fm801_methods,
797	PCM_SOFTC_SIZE,
798};
799
800DRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0);
801MODULE_DEPEND(snd_fm801, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
802MODULE_VERSION(snd_fm801, 1);
803