kernel: Remove unused file.
[dragonfly.git] / sys / dev / sound / pci / fm801.c
CommitLineData
558a398b 1/*-
984263bc
MD
2 * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
1de703da 25 *
558a398b
SS
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 $
984263bc
MD
28 */
29
30#include <dev/sound/pcm/sound.h>
31#include <dev/sound/pcm/ac97.h>
1f2de5d4
MD
32#include <bus/pci/pcireg.h>
33#include <bus/pci/pcivar.h>
984263bc 34
558a398b 35SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.10 2007/01/04 21:47:02 corecode Exp $");
984263bc
MD
36
37#define PCI_VENDOR_FORTEMEDIA 0x1319
38#define PCI_DEVICE_FORTEMEDIA1 0x08011319
39#define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* ??? have no idea what's this... */
40
41#define FM_PCM_VOLUME 0x00
42#define FM_FM_VOLUME 0x02
43#define FM_I2S_VOLUME 0x04
44#define FM_RECORD_SOURCE 0x06
45
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
55
56#define FM_PLAY_DMALEN 0x0a
57#define FM_PLAY_DMABUF1 0x0c
58#define FM_PLAY_DMABUF2 0x10
59
60
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
70
71
72#define FM_REC_DMALEN 0x16
73#define FM_REC_DMABUF1 0x18
74#define FM_REC_DMABUF2 0x1c
75
76#define FM_CODEC_CTL 0x22
77#define FM_VOLUME 0x26
78#define FM_VOLUME_MUTE 0x8000
79
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
84
85#define FM_CODEC_DATA 0x2c
86
87#define FM_IO_CTL 0x52
88#define FM_CARD_CTL 0x54
89
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
95
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
101
102#define FM801_DEFAULT_BUFSZ 4096 /* Other values do not work!!! */
103
104/* debug purposes */
e3869ec7 105#define DPRINT if(0) kprintf
984263bc
MD
106
107/*
108static int fm801ch_setup(struct pcm_channel *c);
109*/
110
111static u_int32_t fmts[] = {
112 AFMT_U8,
113 AFMT_STEREO | AFMT_U8,
114 AFMT_S16_LE,
115 AFMT_STEREO | AFMT_S16_LE,
116 0
117};
118
119static struct pcmchan_caps fm801ch_caps = {
558a398b 120 5500, 48000,
984263bc
MD
121 fmts, 0
122};
123
124struct fm801_info;
125
126struct fm801_chinfo {
558a398b
SS
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 */
984263bc
MD
131 u_int32_t shift;
132};
133
134struct fm801_info {
558a398b
SS
135 int type;
136 bus_space_tag_t st;
137 bus_space_handle_t sh;
138 bus_dma_tag_t parent_dmat;
984263bc 139
558a398b
SS
140 device_t dev;
141 int num;
142 u_int32_t unit;
984263bc 143
558a398b
SS
144 struct resource *reg, *irq;
145 int regtype, regid, irqid;
146 void *ih;
984263bc
MD
147
148 u_int32_t play_flip,
149 play_nextblk,
150 play_start,
151 play_blksize,
152 play_fmt,
153 play_shift,
154 play_size;
155
156 u_int32_t rec_flip,
157 rec_nextblk,
158 rec_start,
159 rec_blksize,
160 rec_fmt,
161 rec_shift,
162 rec_size;
163
558a398b 164 unsigned int bufsz;
984263bc
MD
165
166 struct fm801_chinfo pch, rch;
167
168 device_t radio;
169};
170
171/* Bus Read / Write routines */
172static u_int32_t
173fm801_rd(struct fm801_info *fm801, int regno, int size)
174{
175 switch(size) {
176 case 1:
177 return (bus_space_read_1(fm801->st, fm801->sh, regno));
178 case 2:
179 return (bus_space_read_2(fm801->st, fm801->sh, regno));
180 case 4:
181 return (bus_space_read_4(fm801->st, fm801->sh, regno));
182 default:
183 return 0xffffffff;
184 }
185}
186
187static void
188fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
189{
190
191 switch(size) {
192 case 1:
193 bus_space_write_1(fm801->st, fm801->sh, regno, data);
194 break;
195 case 2:
196 bus_space_write_2(fm801->st, fm801->sh, regno, data);
197 break;
198 case 4:
199 bus_space_write_4(fm801->st, fm801->sh, regno, data);
200 break;
201 }
202}
203
204/* -------------------------------------------------------------------- */
205/*
206 * ac97 codec routines
207 */
208#define TIMO 50
209static int
210fm801_rdcd(kobj_t obj, void *devinfo, int regno)
211{
212 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
213 int i;
214
215 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
216 DELAY(10000);
217 DPRINT("fm801 rdcd: 1 - DELAY\n");
218 }
219 if (i >= TIMO) {
e3869ec7 220 kprintf("fm801 rdcd: codec busy\n");
984263bc
MD
221 return 0;
222 }
223
224 fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
225
226 for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
227 {
228 DELAY(10000);
229 DPRINT("fm801 rdcd: 2 - DELAY\n");
230 }
231 if (i >= TIMO) {
e3869ec7 232 kprintf("fm801 rdcd: write codec invalid\n");
984263bc
MD
233 return 0;
234 }
235
236 return fm801_rd(fm801,FM_CODEC_DATA,2);
237}
238
239static int
240fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
241{
242 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
243 int i;
244
245 DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
246/*
247 if(regno == AC97_REG_RECSEL) return;
248*/
249 /* Poll until codec is ready */
250 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
251 DELAY(10000);
252 DPRINT("fm801 rdcd: 1 - DELAY\n");
253 }
254 if (i >= TIMO) {
e3869ec7 255 kprintf("fm801 wrcd: read codec busy\n");
984263bc
MD
256 return -1;
257 }
258
259 fm801_wr(fm801,FM_CODEC_DATA,data, 2);
260 fm801_wr(fm801,FM_CODEC_CMD, regno,2);
261
262 /* wait until codec is ready */
263 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
264 DELAY(10000);
265 DPRINT("fm801 wrcd: 2 - DELAY\n");
266 }
267 if (i >= TIMO) {
e3869ec7 268 kprintf("fm801 wrcd: read codec busy\n");
984263bc
MD
269 return -1;
270 }
271 DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
272 return 0;
273}
274
275static kobj_method_t fm801_ac97_methods[] = {
276 KOBJMETHOD(ac97_read, fm801_rdcd),
277 KOBJMETHOD(ac97_write, fm801_wrcd),
278 { 0, 0 }
279};
280AC97_DECLARE(fm801_ac97);
281
282/* -------------------------------------------------------------------- */
283
284/*
285 * The interrupt handler
286 */
287static void
288fm801_intr(void *p)
289{
290 struct fm801_info *fm801 = (struct fm801_info *)p;
291 u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
292
293 DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
294
295 if(intsrc & FM_INTSTATUS_PLAY) {
296 fm801->play_flip++;
297 if(fm801->play_flip & 1) {
298 fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
299 } else
300 fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
301 chn_intr(fm801->pch.channel);
302 }
303
304 if(intsrc & FM_INTSTATUS_REC) {
305 fm801->rec_flip++;
306 if(fm801->rec_flip & 1) {
307 fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
308 } else
309 fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
310 chn_intr(fm801->rch.channel);
311 }
312
313 if ( intsrc & FM_INTSTATUS_MPU ) {
314 /* This is a TODOish thing... */
315 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
316 }
317
318 if ( intsrc & FM_INTSTATUS_VOL ) {
319 /* This is a TODOish thing... */
320 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
321 }
322
323 DPRINT("fm801_intr clear\n\n");
324 fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
325}
326
327/* -------------------------------------------------------------------- */
328/* channel interface */
329static void *
330fm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
331{
332 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
333 struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
334
335 DPRINT("fm801ch_init, direction = %d\n", dir);
336 ch->parent = fm801;
337 ch->channel = c;
338 ch->buffer = b;
339 ch->dir = dir;
558a398b
SS
340 if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) != 0)
341 return NULL;
984263bc
MD
342 return (void *)ch;
343}
344
345static int
346fm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
347{
348 struct fm801_chinfo *ch = data;
349 struct fm801_info *fm801 = ch->parent;
350
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" );
356
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;
360 return 0;
361 }
362
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;
366 return 0;
367 }
368
369 return 0;
370}
371
372struct {
373 int limit;
374 int rate;
375} fm801_rates[11] = {
376 { 6600, 5500 },
377 { 8750, 8000 },
378 { 10250, 9600 },
379 { 13200, 11025 },
380 { 17500, 16000 },
381 { 20500, 19200 },
382 { 26500, 22050 },
383 { 35000, 32000 },
384 { 41000, 38400 },
385 { 46000, 44100 },
386 { 48000, 48000 },
387/* anything above -> 48000 */
388};
389
390static int
391fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
392{
393 struct fm801_chinfo *ch = data;
394 struct fm801_info *fm801 = ch->parent;
558a398b 395 register int i;
984263bc
MD
396
397
398 for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
399
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;
404 }
405
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;
410 }
411
412 ch->spd = fm801_rates[i].rate;
413
414 return fm801_rates[i].rate;
415}
416
417static int
418fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
419{
420 struct fm801_chinfo *ch = data;
421 struct fm801_info *fm801 = ch->parent;
422
423 if(ch->dir == PCMDIR_PLAY) {
424 if(fm801->play_flip) return fm801->play_blksize;
425 fm801->play_blksize = blocksize;
426 }
427
428 if(ch->dir == PCMDIR_REC) {
429 if(fm801->rec_flip) return fm801->rec_blksize;
430 fm801->rec_blksize = blocksize;
431 }
432
433 DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
434
435 return blocksize;
436}
437
438static int
439fm801ch_trigger(kobj_t obj, void *data, int go)
440{
441 struct fm801_chinfo *ch = data;
442 struct fm801_info *fm801 = ch->parent;
558a398b 443 u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer);
984263bc
MD
444 u_int32_t k1;
445
446 DPRINT("fm801ch_trigger go %d , ", go);
447
448 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
449 return 0;
450 }
451
452 if (ch->dir == PCMDIR_PLAY) {
453 if (go == PCMTRIG_START) {
454
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,
463 2 );
464 } else {
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 );
470 }
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;
475 fm801->rec_flip = 0;
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,
481 2 );
482 } else {
483 fm801->rec_flip = 0;
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);
488 }
489 }
490
491 return 0;
492}
493
494/* Almost ALSA copy */
495static int
496fm801ch_getptr(kobj_t obj, void *data)
497{
498 struct fm801_chinfo *ch = data;
499 struct fm801_info *fm801 = ch->parent;
500 int result = 0;
501
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;
506 }
507
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;
512 }
513
514 return result;
515}
516
517static struct pcmchan_caps *
518fm801ch_getcaps(kobj_t obj, void *data)
519{
520 return &fm801ch_caps;
521}
522
523static 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),
531 { 0, 0 }
532};
533CHANNEL_DECLARE(fm801ch);
534
535/* -------------------------------------------------------------------- */
536
537/*
538 * Init routine is taken from an original NetBSD driver
539 */
540static int
541fm801_init(struct fm801_info *fm801)
542{
543 u_int32_t k1;
544
545 /* reset codec */
546 fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
547 DELAY(100000);
548 fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
549 DELAY(100000);
550
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 */
555
556 fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
557
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)) |
562 FM_INTMASK_VOL,2);
563 fm801_wr(fm801, FM_INTSTATUS,
564 FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
565 FM_INTSTATUS_VOL,2);
566
567 DPRINT("FM801 init Ok\n");
568 return 0;
569}
570
571static int
572fm801_pci_attach(device_t dev)
573{
574 u_int32_t data;
575 struct ac97_info *codec = 0;
576 struct fm801_info *fm801;
577 int i;
578 int mapped = 0;
579 char status[SND_STATUSLEN];
580
7522187f 581 fm801 = kmalloc(sizeof(*fm801), M_DEVBUF, M_WAITOK | M_ZERO);
984263bc
MD
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++) {
558a398b 590 fm801->regid = PCIR_BAR(i);
984263bc 591 fm801->regtype = SYS_RES_MEMORY;
558a398b
SS
592 fm801->reg = bus_alloc_resource_any(dev, fm801->regtype,
593 &fm801->regid, RF_ACTIVE);
984263bc
MD
594 if(!fm801->reg)
595 {
596 fm801->regtype = SYS_RES_IOPORT;
558a398b
SS
597 fm801->reg = bus_alloc_resource_any(dev,
598 fm801->regtype,
599 &fm801->regid,
600 RF_ACTIVE);
984263bc
MD
601 }
602
603 if(fm801->reg) {
604 fm801->st = rman_get_bustag(fm801->reg);
605 fm801->sh = rman_get_bushandle(fm801->reg);
606 mapped++;
607 }
608 }
609
610 if (mapped == 0) {
611 device_printf(dev, "unable to map register space\n");
612 goto oops;
613 }
614
615 fm801->bufsz = pcm_getbuffersize(dev, 4096, FM801_DEFAULT_BUFSZ, 65536);
616
617 fm801_init(fm801);
618
619 codec = AC97_CREATE(dev, fm801, fm801_ac97);
620 if (codec == NULL) goto oops;
621
622 if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
623
624 fm801->irqid = 0;
558a398b
SS
625 fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid,
626 RF_ACTIVE | RF_SHAREABLE);
627 if (!fm801->irq || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) {
984263bc
MD
628 device_printf(dev, "unable to map interrupt\n");
629 goto oops;
630 }
631
632 if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
633 /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
634 /*highaddr*/BUS_SPACE_MAXADDR,
635 /*filter*/NULL, /*filterarg*/NULL,
636 /*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
558a398b
SS
637 /*flags*/0,
638 &fm801->parent_dmat) != 0) {
984263bc
MD
639 device_printf(dev, "unable to create dma tag\n");
640 goto oops;
641 }
642
558a398b 643 ksnprintf(status, 64, "at %s 0x%lx irq %ld %s",
984263bc 644 (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
558a398b 645 rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801));
984263bc
MD
646
647#define FM801_MAXPLAYCH 1
648 if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
649 pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
650 pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
651 pcm_setstatus(dev, status);
652
653 fm801->radio = device_add_child(dev, "radio", -1);
654 bus_generic_attach(dev);
655
656 return 0;
657
658oops:
659 if (codec) ac97_destroy(codec);
660 if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
661 if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
662 if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
663 if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
efda3bd0 664 kfree(fm801, M_DEVBUF);
984263bc
MD
665 return ENXIO;
666}
667
668static int
669fm801_pci_detach(device_t dev)
670{
671 int r;
672 struct fm801_info *fm801;
673
674 DPRINT("Forte Media FM801 detach\n");
675
676 fm801 = pcm_getdevinfo(dev);
677
678 r = bus_generic_detach(dev);
679 if (r)
680 return r;
681 if (fm801->radio != NULL) {
682 r = device_delete_child(dev, fm801->radio);
683 if (r)
684 return r;
685 fm801->radio = NULL;
686 }
687
688 r = pcm_unregister(dev);
689 if (r)
690 return r;
691
692 bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
693 bus_teardown_intr(dev, fm801->irq, fm801->ih);
694 bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
695 bus_dma_tag_destroy(fm801->parent_dmat);
efda3bd0 696 kfree(fm801, M_DEVBUF);
984263bc
MD
697 return 0;
698}
699
700static int
701fm801_pci_probe( device_t dev )
702{
558a398b 703 int id;
984263bc 704
558a398b
SS
705 if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
706 device_set_desc(dev, "Forte Media FM801 Audio Controller");
707 return BUS_PROBE_DEFAULT;
984263bc
MD
708 }
709/*
710 if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
711 device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
712 return ENXIO;
713 }
714*/
558a398b 715 return ENXIO;
984263bc
MD
716}
717
718static struct resource *
719fm801_alloc_resource(device_t bus, device_t child, int type, int *rid,
720 u_long start, u_long end, u_long count, u_int flags)
721{
722 struct fm801_info *fm801;
723
724 fm801 = pcm_getdevinfo(bus);
725
558a398b 726 if (type == SYS_RES_IOPORT && *rid == PCIR_BAR(0))
984263bc
MD
727 return (fm801->reg);
728
729 return (NULL);
730}
731
732static int
733fm801_release_resource(device_t bus, device_t child, int type, int rid,
734 struct resource *r)
735{
736 return (0);
737}
738
739static device_method_t fm801_methods[] = {
740 /* Device interface */
741 DEVMETHOD(device_probe, fm801_pci_probe),
742 DEVMETHOD(device_attach, fm801_pci_attach),
743 DEVMETHOD(device_detach, fm801_pci_detach),
744 DEVMETHOD(device_shutdown, bus_generic_shutdown),
745 DEVMETHOD(device_suspend, bus_generic_suspend),
746 DEVMETHOD(device_resume, bus_generic_resume),
747
748 /* Bus interface */
749 DEVMETHOD(bus_print_child, bus_generic_print_child),
750 DEVMETHOD(bus_alloc_resource, fm801_alloc_resource),
751 DEVMETHOD(bus_release_resource, fm801_release_resource),
752 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
753 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
754 { 0, 0}
755};
756
757static driver_t fm801_driver = {
758 "pcm",
759 fm801_methods,
760 PCM_SOFTC_SIZE,
761};
762
763DRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0);
558a398b 764MODULE_DEPEND(snd_fm801, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
984263bc 765MODULE_VERSION(snd_fm801, 1);