2 * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/sys/dev/sound/pci/fm801.c,v 1.27.2.1 2006/01/10 01:01:24 ariff Exp $
27 * $DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.10 2007/01/04 21:47:02 corecode Exp $
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/ac97.h>
32 #include <bus/pci/pcireg.h>
33 #include <bus/pci/pcivar.h>
35 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.10 2007/01/04 21:47:02 corecode Exp $");
37 #define PCI_VENDOR_FORTEMEDIA 0x1319
38 #define PCI_DEVICE_FORTEMEDIA1 0x08011319
39 #define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* ??? have no idea what's this... */
41 #define FM_PCM_VOLUME 0x00
42 #define FM_FM_VOLUME 0x02
43 #define FM_I2S_VOLUME 0x04
44 #define FM_RECORD_SOURCE 0x06
46 #define FM_PLAY_CTL 0x08
47 #define FM_PLAY_RATE_MASK 0x0f00
48 #define FM_PLAY_BUF1_LAST 0x0001
49 #define FM_PLAY_BUF2_LAST 0x0002
50 #define FM_PLAY_START 0x0020
51 #define FM_PLAY_PAUSE 0x0040
52 #define FM_PLAY_STOPNOW 0x0080
53 #define FM_PLAY_16BIT 0x4000
54 #define FM_PLAY_STEREO 0x8000
56 #define FM_PLAY_DMALEN 0x0a
57 #define FM_PLAY_DMABUF1 0x0c
58 #define FM_PLAY_DMABUF2 0x10
61 #define FM_REC_CTL 0x14
62 #define FM_REC_RATE_MASK 0x0f00
63 #define FM_REC_BUF1_LAST 0x0001
64 #define FM_REC_BUF2_LAST 0x0002
65 #define FM_REC_START 0x0020
66 #define FM_REC_PAUSE 0x0040
67 #define FM_REC_STOPNOW 0x0080
68 #define FM_REC_16BIT 0x4000
69 #define FM_REC_STEREO 0x8000
72 #define FM_REC_DMALEN 0x16
73 #define FM_REC_DMABUF1 0x18
74 #define FM_REC_DMABUF2 0x1c
76 #define FM_CODEC_CTL 0x22
77 #define FM_VOLUME 0x26
78 #define FM_VOLUME_MUTE 0x8000
80 #define FM_CODEC_CMD 0x2a
81 #define FM_CODEC_CMD_READ 0x0080
82 #define FM_CODEC_CMD_VALID 0x0100
83 #define FM_CODEC_CMD_BUSY 0x0200
85 #define FM_CODEC_DATA 0x2c
87 #define FM_IO_CTL 0x52
88 #define FM_CARD_CTL 0x54
90 #define FM_INTMASK 0x56
91 #define FM_INTMASK_PLAY 0x0001
92 #define FM_INTMASK_REC 0x0002
93 #define FM_INTMASK_VOL 0x0040
94 #define FM_INTMASK_MPU 0x0080
96 #define FM_INTSTATUS 0x5a
97 #define FM_INTSTATUS_PLAY 0x0100
98 #define FM_INTSTATUS_REC 0x0200
99 #define FM_INTSTATUS_VOL 0x4000
100 #define FM_INTSTATUS_MPU 0x8000
102 #define FM801_DEFAULT_BUFSZ 4096 /* Other values do not work!!! */
105 #define DPRINT if(0) kprintf
108 static int fm801ch_setup(struct pcm_channel *c);
111 static u_int32_t fmts
[] = {
113 AFMT_STEREO
| AFMT_U8
,
115 AFMT_STEREO
| AFMT_S16_LE
,
119 static struct pcmchan_caps fm801ch_caps
= {
126 struct fm801_chinfo
{
127 struct fm801_info
*parent
;
128 struct pcm_channel
*channel
;
129 struct snd_dbuf
*buffer
;
130 u_int32_t spd
, dir
, fmt
; /* speed, direction, format */
137 bus_space_handle_t sh
;
138 bus_dma_tag_t parent_dmat
;
144 struct resource
*reg
, *irq
;
145 int regtype
, regid
, irqid
;
166 struct fm801_chinfo pch
, rch
;
171 /* Bus Read / Write routines */
173 fm801_rd(struct fm801_info
*fm801
, int regno
, int size
)
177 return (bus_space_read_1(fm801
->st
, fm801
->sh
, regno
));
179 return (bus_space_read_2(fm801
->st
, fm801
->sh
, regno
));
181 return (bus_space_read_4(fm801
->st
, fm801
->sh
, regno
));
188 fm801_wr(struct fm801_info
*fm801
, int regno
, u_int32_t data
, int size
)
193 bus_space_write_1(fm801
->st
, fm801
->sh
, regno
, data
);
196 bus_space_write_2(fm801
->st
, fm801
->sh
, regno
, data
);
199 bus_space_write_4(fm801
->st
, fm801
->sh
, regno
, data
);
204 /* -------------------------------------------------------------------- */
206 * ac97 codec routines
210 fm801_rdcd(kobj_t obj
, void *devinfo
, int regno
)
212 struct fm801_info
*fm801
= (struct fm801_info
*)devinfo
;
215 for (i
= 0; i
< TIMO
&& fm801_rd(fm801
,FM_CODEC_CMD
,2) & FM_CODEC_CMD_BUSY
; i
++) {
217 DPRINT("fm801 rdcd: 1 - DELAY\n");
220 kprintf("fm801 rdcd: codec busy\n");
224 fm801_wr(fm801
,FM_CODEC_CMD
, regno
|FM_CODEC_CMD_READ
,2);
226 for (i
= 0; i
< TIMO
&& !(fm801_rd(fm801
,FM_CODEC_CMD
,2) & FM_CODEC_CMD_VALID
); i
++)
229 DPRINT("fm801 rdcd: 2 - DELAY\n");
232 kprintf("fm801 rdcd: write codec invalid\n");
236 return fm801_rd(fm801
,FM_CODEC_DATA
,2);
240 fm801_wrcd(kobj_t obj
, void *devinfo
, int regno
, u_int32_t data
)
242 struct fm801_info
*fm801
= (struct fm801_info
*)devinfo
;
245 DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno
, data
);
247 if(regno == AC97_REG_RECSEL) return;
249 /* Poll until codec is ready */
250 for (i
= 0; i
< TIMO
&& fm801_rd(fm801
,FM_CODEC_CMD
,2) & FM_CODEC_CMD_BUSY
; i
++) {
252 DPRINT("fm801 rdcd: 1 - DELAY\n");
255 kprintf("fm801 wrcd: read codec busy\n");
259 fm801_wr(fm801
,FM_CODEC_DATA
,data
, 2);
260 fm801_wr(fm801
,FM_CODEC_CMD
, regno
,2);
262 /* wait until codec is ready */
263 for (i
= 0; i
< TIMO
&& fm801_rd(fm801
,FM_CODEC_CMD
,2) & FM_CODEC_CMD_BUSY
; i
++) {
265 DPRINT("fm801 wrcd: 2 - DELAY\n");
268 kprintf("fm801 wrcd: read codec busy\n");
271 DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno
, data
);
275 static kobj_method_t fm801_ac97_methods
[] = {
276 KOBJMETHOD(ac97_read
, fm801_rdcd
),
277 KOBJMETHOD(ac97_write
, fm801_wrcd
),
280 AC97_DECLARE(fm801_ac97
);
282 /* -------------------------------------------------------------------- */
285 * The interrupt handler
290 struct fm801_info
*fm801
= (struct fm801_info
*)p
;
291 u_int32_t intsrc
= fm801_rd(fm801
, FM_INTSTATUS
, 2);
293 DPRINT("\nfm801_intr intsrc 0x%x ", intsrc
);
295 if(intsrc
& FM_INTSTATUS_PLAY
) {
297 if(fm801
->play_flip
& 1) {
298 fm801_wr(fm801
, FM_PLAY_DMABUF1
, fm801
->play_start
,4);
300 fm801_wr(fm801
, FM_PLAY_DMABUF2
, fm801
->play_nextblk
,4);
301 chn_intr(fm801
->pch
.channel
);
304 if(intsrc
& FM_INTSTATUS_REC
) {
306 if(fm801
->rec_flip
& 1) {
307 fm801_wr(fm801
, FM_REC_DMABUF1
, fm801
->rec_start
,4);
309 fm801_wr(fm801
, FM_REC_DMABUF2
, fm801
->rec_nextblk
,4);
310 chn_intr(fm801
->rch
.channel
);
313 if ( intsrc
& FM_INTSTATUS_MPU
) {
314 /* This is a TODOish thing... */
315 fm801_wr(fm801
, FM_INTSTATUS
, intsrc
& FM_INTSTATUS_MPU
,2);
318 if ( intsrc
& FM_INTSTATUS_VOL
) {
319 /* This is a TODOish thing... */
320 fm801_wr(fm801
, FM_INTSTATUS
, intsrc
& FM_INTSTATUS_VOL
,2);
323 DPRINT("fm801_intr clear\n\n");
324 fm801_wr(fm801
, FM_INTSTATUS
, intsrc
& (FM_INTSTATUS_PLAY
| FM_INTSTATUS_REC
), 2);
327 /* -------------------------------------------------------------------- */
328 /* channel interface */
330 fm801ch_init(kobj_t obj
, void *devinfo
, struct snd_dbuf
*b
, struct pcm_channel
*c
, int dir
)
332 struct fm801_info
*fm801
= (struct fm801_info
*)devinfo
;
333 struct fm801_chinfo
*ch
= (dir
== PCMDIR_PLAY
)? &fm801
->pch
: &fm801
->rch
;
335 DPRINT("fm801ch_init, direction = %d\n", dir
);
340 if (sndbuf_alloc(ch
->buffer
, fm801
->parent_dmat
, fm801
->bufsz
) != 0)
346 fm801ch_setformat(kobj_t obj
, void *data
, u_int32_t format
)
348 struct fm801_chinfo
*ch
= data
;
349 struct fm801_info
*fm801
= ch
->parent
;
351 DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format
,
352 (format
& AFMT_STEREO
)?"stereo":"mono",
353 (format
& (AFMT_S16_LE
| AFMT_S16_BE
| AFMT_U16_LE
| AFMT_U16_BE
)) ? "16bit":"8bit",
354 (format
& AFMT_SIGNED
)? "signed":"unsigned",
355 (format
& AFMT_BIGENDIAN
)?"bigendiah":"littleendian" );
357 if(ch
->dir
== PCMDIR_PLAY
) {
358 fm801
->play_fmt
= (format
& AFMT_STEREO
)? FM_PLAY_STEREO
: 0;
359 fm801
->play_fmt
|= (format
& AFMT_16BIT
) ? FM_PLAY_16BIT
: 0;
363 if(ch
->dir
== PCMDIR_REC
) {
364 fm801
->rec_fmt
= (format
& AFMT_STEREO
)? FM_REC_STEREO
:0;
365 fm801
->rec_fmt
|= (format
& AFMT_16BIT
) ? FM_PLAY_16BIT
: 0;
375 } fm801_rates
[11] = {
387 /* anything above -> 48000 */
391 fm801ch_setspeed(kobj_t obj
, void *data
, u_int32_t speed
)
393 struct fm801_chinfo
*ch
= data
;
394 struct fm801_info
*fm801
= ch
->parent
;
398 for (i
= 0; i
< 10 && fm801_rates
[i
].limit
<= speed
; i
++) ;
400 if(ch
->dir
== PCMDIR_PLAY
) {
401 fm801
->pch
.spd
= fm801_rates
[i
].rate
;
402 fm801
->play_shift
= (i
<<8);
403 fm801
->play_shift
&= FM_PLAY_RATE_MASK
;
406 if(ch
->dir
== PCMDIR_REC
) {
407 fm801
->rch
.spd
= fm801_rates
[i
].rate
;
408 fm801
->rec_shift
= (i
<<8);
409 fm801
->rec_shift
&= FM_REC_RATE_MASK
;
412 ch
->spd
= fm801_rates
[i
].rate
;
414 return fm801_rates
[i
].rate
;
418 fm801ch_setblocksize(kobj_t obj
, void *data
, u_int32_t blocksize
)
420 struct fm801_chinfo
*ch
= data
;
421 struct fm801_info
*fm801
= ch
->parent
;
423 if(ch
->dir
== PCMDIR_PLAY
) {
424 if(fm801
->play_flip
) return fm801
->play_blksize
;
425 fm801
->play_blksize
= blocksize
;
428 if(ch
->dir
== PCMDIR_REC
) {
429 if(fm801
->rec_flip
) return fm801
->rec_blksize
;
430 fm801
->rec_blksize
= blocksize
;
433 DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize
, ch
->dir
);
439 fm801ch_trigger(kobj_t obj
, void *data
, int go
)
441 struct fm801_chinfo
*ch
= data
;
442 struct fm801_info
*fm801
= ch
->parent
;
443 u_int32_t baseaddr
= sndbuf_getbufaddr(ch
->buffer
);
446 DPRINT("fm801ch_trigger go %d , ", go
);
448 if (go
== PCMTRIG_EMLDMAWR
|| go
== PCMTRIG_EMLDMARD
) {
452 if (ch
->dir
== PCMDIR_PLAY
) {
453 if (go
== PCMTRIG_START
) {
455 fm801
->play_start
= baseaddr
;
456 fm801
->play_nextblk
= fm801
->play_start
+ fm801
->play_blksize
;
457 fm801
->play_flip
= 0;
458 fm801_wr(fm801
, FM_PLAY_DMALEN
, fm801
->play_blksize
- 1, 2);
459 fm801_wr(fm801
, FM_PLAY_DMABUF1
,fm801
->play_start
,4);
460 fm801_wr(fm801
, FM_PLAY_DMABUF2
,fm801
->play_nextblk
,4);
461 fm801_wr(fm801
, FM_PLAY_CTL
,
462 FM_PLAY_START
| FM_PLAY_STOPNOW
| fm801
->play_fmt
| fm801
->play_shift
,
465 fm801
->play_flip
= 0;
466 k1
= fm801_rd(fm801
, FM_PLAY_CTL
,2);
467 fm801_wr(fm801
, FM_PLAY_CTL
,
468 (k1
& ~(FM_PLAY_STOPNOW
| FM_PLAY_START
)) |
469 FM_PLAY_BUF1_LAST
| FM_PLAY_BUF2_LAST
, 2 );
471 } else if(ch
->dir
== PCMDIR_REC
) {
472 if (go
== PCMTRIG_START
) {
473 fm801
->rec_start
= baseaddr
;
474 fm801
->rec_nextblk
= fm801
->rec_start
+ fm801
->rec_blksize
;
476 fm801_wr(fm801
, FM_REC_DMALEN
, fm801
->rec_blksize
- 1, 2);
477 fm801_wr(fm801
, FM_REC_DMABUF1
,fm801
->rec_start
,4);
478 fm801_wr(fm801
, FM_REC_DMABUF2
,fm801
->rec_nextblk
,4);
479 fm801_wr(fm801
, FM_REC_CTL
,
480 FM_REC_START
| FM_REC_STOPNOW
| fm801
->rec_fmt
| fm801
->rec_shift
,
484 k1
= fm801_rd(fm801
, FM_REC_CTL
,2);
485 fm801_wr(fm801
, FM_REC_CTL
,
486 (k1
& ~(FM_REC_STOPNOW
| FM_REC_START
)) |
487 FM_REC_BUF1_LAST
| FM_REC_BUF2_LAST
, 2);
494 /* Almost ALSA copy */
496 fm801ch_getptr(kobj_t obj
, void *data
)
498 struct fm801_chinfo
*ch
= data
;
499 struct fm801_info
*fm801
= ch
->parent
;
502 if (ch
->dir
== PCMDIR_PLAY
) {
503 result
= fm801_rd(fm801
,
504 (fm801
->play_flip
&1) ?
505 FM_PLAY_DMABUF2
:FM_PLAY_DMABUF1
, 4) - fm801
->play_start
;
508 if (ch
->dir
== PCMDIR_REC
) {
509 result
= fm801_rd(fm801
,
510 (fm801
->rec_flip
&1) ?
511 FM_REC_DMABUF2
:FM_REC_DMABUF1
, 4) - fm801
->rec_start
;
517 static struct pcmchan_caps
*
518 fm801ch_getcaps(kobj_t obj
, void *data
)
520 return &fm801ch_caps
;
523 static kobj_method_t fm801ch_methods
[] = {
524 KOBJMETHOD(channel_init
, fm801ch_init
),
525 KOBJMETHOD(channel_setformat
, fm801ch_setformat
),
526 KOBJMETHOD(channel_setspeed
, fm801ch_setspeed
),
527 KOBJMETHOD(channel_setblocksize
, fm801ch_setblocksize
),
528 KOBJMETHOD(channel_trigger
, fm801ch_trigger
),
529 KOBJMETHOD(channel_getptr
, fm801ch_getptr
),
530 KOBJMETHOD(channel_getcaps
, fm801ch_getcaps
),
533 CHANNEL_DECLARE(fm801ch
);
535 /* -------------------------------------------------------------------- */
538 * Init routine is taken from an original NetBSD driver
541 fm801_init(struct fm801_info
*fm801
)
546 fm801_wr(fm801
, FM_CODEC_CTL
, 0x0020,2);
548 fm801_wr(fm801
, FM_CODEC_CTL
, 0x0000,2);
551 fm801_wr(fm801
, FM_PCM_VOLUME
, 0x0808,2);
552 fm801_wr(fm801
, FM_FM_VOLUME
, 0x0808,2);
553 fm801_wr(fm801
, FM_I2S_VOLUME
, 0x0808,2);
554 fm801_wr(fm801
, 0x40,0x107f,2); /* enable legacy audio */
556 fm801_wr((void *)fm801
, FM_RECORD_SOURCE
, 0x0000,2);
558 /* Unmask playback, record and mpu interrupts, mask the rest */
559 k1
= fm801_rd((void *)fm801
, FM_INTMASK
,2);
560 fm801_wr(fm801
, FM_INTMASK
,
561 (k1
& ~(FM_INTMASK_PLAY
| FM_INTMASK_REC
| FM_INTMASK_MPU
)) |
563 fm801_wr(fm801
, FM_INTSTATUS
,
564 FM_INTSTATUS_PLAY
| FM_INTSTATUS_REC
| FM_INTSTATUS_MPU
|
567 DPRINT("FM801 init Ok\n");
572 fm801_pci_attach(device_t dev
)
575 struct ac97_info
*codec
= 0;
576 struct fm801_info
*fm801
;
579 char status
[SND_STATUSLEN
];
581 if ((fm801
= (struct fm801_info
*)kmalloc(sizeof(*fm801
), M_DEVBUF
, M_NOWAIT
| M_ZERO
)) == NULL
) {
582 device_printf(dev
, "cannot allocate softc\n");
586 fm801
->type
= pci_get_devid(dev
);
588 data
= pci_read_config(dev
, PCIR_COMMAND
, 2);
589 data
|= (PCIM_CMD_PORTEN
|PCIM_CMD_MEMEN
|PCIM_CMD_BUSMASTEREN
);
590 pci_write_config(dev
, PCIR_COMMAND
, data
, 2);
591 data
= pci_read_config(dev
, PCIR_COMMAND
, 2);
593 for (i
= 0; (mapped
== 0) && (i
< PCI_MAXMAPS_0
); i
++) {
594 fm801
->regid
= PCIR_BAR(i
);
595 fm801
->regtype
= SYS_RES_MEMORY
;
596 fm801
->reg
= bus_alloc_resource_any(dev
, fm801
->regtype
,
597 &fm801
->regid
, RF_ACTIVE
);
600 fm801
->regtype
= SYS_RES_IOPORT
;
601 fm801
->reg
= bus_alloc_resource_any(dev
,
608 fm801
->st
= rman_get_bustag(fm801
->reg
);
609 fm801
->sh
= rman_get_bushandle(fm801
->reg
);
615 device_printf(dev
, "unable to map register space\n");
619 fm801
->bufsz
= pcm_getbuffersize(dev
, 4096, FM801_DEFAULT_BUFSZ
, 65536);
623 codec
= AC97_CREATE(dev
, fm801
, fm801_ac97
);
624 if (codec
== NULL
) goto oops
;
626 if (mixer_init(dev
, ac97_getmixerclass(), codec
) == -1) goto oops
;
629 fm801
->irq
= bus_alloc_resource_any(dev
, SYS_RES_IRQ
, &fm801
->irqid
,
630 RF_ACTIVE
| RF_SHAREABLE
);
631 if (!fm801
->irq
|| snd_setup_intr(dev
, fm801
->irq
, 0, fm801_intr
, fm801
, &fm801
->ih
)) {
632 device_printf(dev
, "unable to map interrupt\n");
636 if (bus_dma_tag_create(/*parent*/NULL
, /*alignment*/2, /*boundary*/0,
637 /*lowaddr*/BUS_SPACE_MAXADDR_32BIT
,
638 /*highaddr*/BUS_SPACE_MAXADDR
,
639 /*filter*/NULL
, /*filterarg*/NULL
,
640 /*maxsize*/fm801
->bufsz
, /*nsegments*/1, /*maxsegz*/0x3ffff,
642 &fm801
->parent_dmat
) != 0) {
643 device_printf(dev
, "unable to create dma tag\n");
647 ksnprintf(status
, 64, "at %s 0x%lx irq %ld %s",
648 (fm801
->regtype
== SYS_RES_IOPORT
)? "io" : "memory",
649 rman_get_start(fm801
->reg
), rman_get_start(fm801
->irq
),PCM_KLDSTRING(snd_fm801
));
651 #define FM801_MAXPLAYCH 1
652 if (pcm_register(dev
, fm801
, FM801_MAXPLAYCH
, 1)) goto oops
;
653 pcm_addchan(dev
, PCMDIR_PLAY
, &fm801ch_class
, fm801
);
654 pcm_addchan(dev
, PCMDIR_REC
, &fm801ch_class
, fm801
);
655 pcm_setstatus(dev
, status
);
657 fm801
->radio
= device_add_child(dev
, "radio", -1);
658 bus_generic_attach(dev
);
663 if (codec
) ac97_destroy(codec
);
664 if (fm801
->reg
) bus_release_resource(dev
, fm801
->regtype
, fm801
->regid
, fm801
->reg
);
665 if (fm801
->ih
) bus_teardown_intr(dev
, fm801
->irq
, fm801
->ih
);
666 if (fm801
->irq
) bus_release_resource(dev
, SYS_RES_IRQ
, fm801
->irqid
, fm801
->irq
);
667 if (fm801
->parent_dmat
) bus_dma_tag_destroy(fm801
->parent_dmat
);
668 kfree(fm801
, M_DEVBUF
);
673 fm801_pci_detach(device_t dev
)
676 struct fm801_info
*fm801
;
678 DPRINT("Forte Media FM801 detach\n");
680 fm801
= pcm_getdevinfo(dev
);
682 r
= bus_generic_detach(dev
);
685 if (fm801
->radio
!= NULL
) {
686 r
= device_delete_child(dev
, fm801
->radio
);
692 r
= pcm_unregister(dev
);
696 bus_release_resource(dev
, fm801
->regtype
, fm801
->regid
, fm801
->reg
);
697 bus_teardown_intr(dev
, fm801
->irq
, fm801
->ih
);
698 bus_release_resource(dev
, SYS_RES_IRQ
, fm801
->irqid
, fm801
->irq
);
699 bus_dma_tag_destroy(fm801
->parent_dmat
);
700 kfree(fm801
, M_DEVBUF
);
705 fm801_pci_probe( device_t dev
)
709 if ((id
= pci_get_devid(dev
)) == PCI_DEVICE_FORTEMEDIA1
) {
710 device_set_desc(dev
, "Forte Media FM801 Audio Controller");
711 return BUS_PROBE_DEFAULT
;
714 if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
715 device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
722 static struct resource
*
723 fm801_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
724 u_long start
, u_long end
, u_long count
, u_int flags
)
726 struct fm801_info
*fm801
;
728 fm801
= pcm_getdevinfo(bus
);
730 if (type
== SYS_RES_IOPORT
&& *rid
== PCIR_BAR(0))
737 fm801_release_resource(device_t bus
, device_t child
, int type
, int rid
,
743 static device_method_t fm801_methods
[] = {
744 /* Device interface */
745 DEVMETHOD(device_probe
, fm801_pci_probe
),
746 DEVMETHOD(device_attach
, fm801_pci_attach
),
747 DEVMETHOD(device_detach
, fm801_pci_detach
),
748 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
749 DEVMETHOD(device_suspend
, bus_generic_suspend
),
750 DEVMETHOD(device_resume
, bus_generic_resume
),
753 DEVMETHOD(bus_print_child
, bus_generic_print_child
),
754 DEVMETHOD(bus_alloc_resource
, fm801_alloc_resource
),
755 DEVMETHOD(bus_release_resource
, fm801_release_resource
),
756 DEVMETHOD(bus_activate_resource
, bus_generic_activate_resource
),
757 DEVMETHOD(bus_deactivate_resource
, bus_generic_deactivate_resource
),
761 static driver_t fm801_driver
= {
767 DRIVER_MODULE(snd_fm801
, pci
, fm801_driver
, pcm_devclass
, 0, 0);
768 MODULE_DEPEND(snd_fm801
, sound
, SOUND_MINVER
, SOUND_PREFVER
, SOUND_MAXVER
);
769 MODULE_VERSION(snd_fm801
, 1);