1 /*-
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.
25 */
26
27 #include <dev/sound/pcm/sound.h>
28 #include <dev/sound/pcm/ac97.h>
29 #include <dev/pci/pcireg.h>
30 #include <dev/pci/pcivar.h>
31
32 SND_DECLARE_FILE("$FreeBSD$");
33
34 #define PCI_VENDOR_FORTEMEDIA 0x1319
35 #define PCI_DEVICE_FORTEMEDIA1 0x08011319
36 #define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* ??? have no idea what's this... */
37
38 #define FM_PCM_VOLUME 0x00
39 #define FM_FM_VOLUME 0x02
40 #define FM_I2S_VOLUME 0x04
41 #define FM_RECORD_SOURCE 0x06
42
43 #define FM_PLAY_CTL 0x08
44 #define FM_PLAY_RATE_MASK 0x0f00
45 #define FM_PLAY_BUF1_LAST 0x0001
46 #define FM_PLAY_BUF2_LAST 0x0002
47 #define FM_PLAY_START 0x0020
48 #define FM_PLAY_PAUSE 0x0040
49 #define FM_PLAY_STOPNOW 0x0080
50 #define FM_PLAY_16BIT 0x4000
51 #define FM_PLAY_STEREO 0x8000
52
53 #define FM_PLAY_DMALEN 0x0a
54 #define FM_PLAY_DMABUF1 0x0c
55 #define FM_PLAY_DMABUF2 0x10
56
57
58 #define FM_REC_CTL 0x14
59 #define FM_REC_RATE_MASK 0x0f00
60 #define FM_REC_BUF1_LAST 0x0001
61 #define FM_REC_BUF2_LAST 0x0002
62 #define FM_REC_START 0x0020
63 #define FM_REC_PAUSE 0x0040
64 #define FM_REC_STOPNOW 0x0080
65 #define FM_REC_16BIT 0x4000
66 #define FM_REC_STEREO 0x8000
67
68
69 #define FM_REC_DMALEN 0x16
70 #define FM_REC_DMABUF1 0x18
71 #define FM_REC_DMABUF2 0x1c
72
73 #define FM_CODEC_CTL 0x22
74 #define FM_VOLUME 0x26
75 #define FM_VOLUME_MUTE 0x8000
76
77 #define FM_CODEC_CMD 0x2a
78 #define FM_CODEC_CMD_READ 0x0080
79 #define FM_CODEC_CMD_VALID 0x0100
80 #define FM_CODEC_CMD_BUSY 0x0200
81
82 #define FM_CODEC_DATA 0x2c
83
84 #define FM_IO_CTL 0x52
85 #define FM_CARD_CTL 0x54
86
87 #define FM_INTMASK 0x56
88 #define FM_INTMASK_PLAY 0x0001
89 #define FM_INTMASK_REC 0x0002
90 #define FM_INTMASK_VOL 0x0040
91 #define FM_INTMASK_MPU 0x0080
92
93 #define FM_INTSTATUS 0x5a
94 #define FM_INTSTATUS_PLAY 0x0100
95 #define FM_INTSTATUS_REC 0x0200
96 #define FM_INTSTATUS_VOL 0x4000
97 #define FM_INTSTATUS_MPU 0x8000
98
99 #define FM801_DEFAULT_BUFSZ 4096 /* Other values do not work!!! */
100
101 /* debug purposes */
102 #define DPRINT if(0) printf
103
104 /*
105 static int fm801ch_setup(struct pcm_channel *c);
106 */
107
108 static u_int32_t fmts[] = {
109 AFMT_U8,
110 AFMT_STEREO | AFMT_U8,
111 AFMT_S16_LE,
112 AFMT_STEREO | AFMT_S16_LE,
113 0
114 };
115
116 static struct pcmchan_caps fm801ch_caps = {
117 4000, 48000,
118 fmts, 0
119 };
120
121 struct fm801_info;
122
123 struct fm801_chinfo {
124 struct fm801_info *parent;
125 struct pcm_channel *channel;
126 struct snd_dbuf *buffer;
127 u_int32_t spd, dir, fmt; /* speed, direction, format */
128 u_int32_t shift;
129 };
130
131 struct fm801_info {
132 int type;
133 bus_space_tag_t st;
134 bus_space_handle_t sh;
135 bus_dma_tag_t parent_dmat;
136
137 device_t dev;
138 int num;
139 u_int32_t unit;
140
141 struct resource *reg, *irq;
142 int regtype, regid, irqid;
143 void *ih;
144
145 u_int32_t play_flip,
146 play_nextblk,
147 play_start,
148 play_blksize,
149 play_fmt,
150 play_shift,
151 play_size;
152
153 u_int32_t rec_flip,
154 rec_nextblk,
155 rec_start,
156 rec_blksize,
157 rec_fmt,
158 rec_shift,
159 rec_size;
160
161 unsigned int bufsz;
162
163 struct fm801_chinfo pch, rch;
164
165 device_t radio;
166 };
167
168 /* Bus Read / Write routines */
169 static u_int32_t
170 fm801_rd(struct fm801_info *fm801, int regno, int size)
171 {
172 switch(size) {
173 case 1:
174 return (bus_space_read_1(fm801->st, fm801->sh, regno));
175 case 2:
176 return (bus_space_read_2(fm801->st, fm801->sh, regno));
177 case 4:
178 return (bus_space_read_4(fm801->st, fm801->sh, regno));
179 default:
180 return 0xffffffff;
181 }
182 }
183
184 static void
185 fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
186 {
187
188 switch(size) {
189 case 1:
190 bus_space_write_1(fm801->st, fm801->sh, regno, data);
191 break;
192 case 2:
193 bus_space_write_2(fm801->st, fm801->sh, regno, data);
194 break;
195 case 4:
196 bus_space_write_4(fm801->st, fm801->sh, regno, data);
197 break;
198 }
199 }
200
201 /* -------------------------------------------------------------------- */
202 /*
203 * ac97 codec routines
204 */
205 #define TIMO 50
206 static int
207 fm801_rdcd(kobj_t obj, void *devinfo, int regno)
208 {
209 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
210 int i;
211
212 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
213 DELAY(10000);
214 DPRINT("fm801 rdcd: 1 - DELAY\n");
215 }
216 if (i >= TIMO) {
217 printf("fm801 rdcd: codec busy\n");
218 return 0;
219 }
220
221 fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
222
223 for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
224 {
225 DELAY(10000);
226 DPRINT("fm801 rdcd: 2 - DELAY\n");
227 }
228 if (i >= TIMO) {
229 printf("fm801 rdcd: write codec invalid\n");
230 return 0;
231 }
232
233 return fm801_rd(fm801,FM_CODEC_DATA,2);
234 }
235
236 static int
237 fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
238 {
239 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
240 int i;
241
242 DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
243 /*
244 if(regno == AC97_REG_RECSEL) return;
245 */
246 /* Poll until codec is ready */
247 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
248 DELAY(10000);
249 DPRINT("fm801 rdcd: 1 - DELAY\n");
250 }
251 if (i >= TIMO) {
252 printf("fm801 wrcd: read codec busy\n");
253 return -1;
254 }
255
256 fm801_wr(fm801,FM_CODEC_DATA,data, 2);
257 fm801_wr(fm801,FM_CODEC_CMD, regno,2);
258
259 /* wait until codec is ready */
260 for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
261 DELAY(10000);
262 DPRINT("fm801 wrcd: 2 - DELAY\n");
263 }
264 if (i >= TIMO) {
265 printf("fm801 wrcd: read codec busy\n");
266 return -1;
267 }
268 DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
269 return 0;
270 }
271
272 static kobj_method_t fm801_ac97_methods[] = {
273 KOBJMETHOD(ac97_read, fm801_rdcd),
274 KOBJMETHOD(ac97_write, fm801_wrcd),
275 { 0, 0 }
276 };
277 AC97_DECLARE(fm801_ac97);
278
279 /* -------------------------------------------------------------------- */
280
281 /*
282 * The interrupt handler
283 */
284 static void
285 fm801_intr(void *p)
286 {
287 struct fm801_info *fm801 = (struct fm801_info *)p;
288 u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
289
290 DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
291
292 if(intsrc & FM_INTSTATUS_PLAY) {
293 fm801->play_flip++;
294 if(fm801->play_flip & 1) {
295 fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
296 } else
297 fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
298 chn_intr(fm801->pch.channel);
299 }
300
301 if(intsrc & FM_INTSTATUS_REC) {
302 fm801->rec_flip++;
303 if(fm801->rec_flip & 1) {
304 fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
305 } else
306 fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
307 chn_intr(fm801->rch.channel);
308 }
309
310 if ( intsrc & FM_INTSTATUS_MPU ) {
311 /* This is a TODOish thing... */
312 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
313 }
314
315 if ( intsrc & FM_INTSTATUS_VOL ) {
316 /* This is a TODOish thing... */
317 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
318 }
319
320 DPRINT("fm801_intr clear\n\n");
321 fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
322 }
323
324 /* -------------------------------------------------------------------- */
325 /* channel interface */
326 static void *
327 fm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
328 {
329 struct fm801_info *fm801 = (struct fm801_info *)devinfo;
330 struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
331
332 DPRINT("fm801ch_init, direction = %d\n", dir);
333 ch->parent = fm801;
334 ch->channel = c;
335 ch->buffer = b;
336 ch->dir = dir;
337 if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) != 0)
338 return NULL;
339 return (void *)ch;
340 }
341
342 static int
343 fm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
344 {
345 struct fm801_chinfo *ch = data;
346 struct fm801_info *fm801 = ch->parent;
347
348 DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
349 (format & AFMT_STEREO)?"stereo":"mono",
350 (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
351 (format & AFMT_SIGNED)? "signed":"unsigned",
352 (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
353
354 if(ch->dir == PCMDIR_PLAY) {
355 fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
356 fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
357 return 0;
358 }
359
360 if(ch->dir == PCMDIR_REC ) {
361 fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
362 fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
363 return 0;
364 }
365
366 return 0;
367 }
368
369 struct {
370 int limit;
371 int rate;
372 } fm801_rates[11] = {
373 { 6600, 5500 },
374 { 8750, 8000 },
375 { 10250, 9600 },
376 { 13200, 11025 },
377 { 17500, 16000 },
378 { 20500, 19200 },
379 { 26500, 22050 },
380 { 35000, 32000 },
381 { 41000, 38400 },
382 { 46000, 44100 },
383 { 48000, 48000 },
384 /* anything above -> 48000 */
385 };
386
387 static int
388 fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
389 {
390 struct fm801_chinfo *ch = data;
391 struct fm801_info *fm801 = ch->parent;
392 register int i;
393
394
395 for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
396
397 if(ch->dir == PCMDIR_PLAY) {
398 fm801->pch.spd = fm801_rates[i].rate;
399 fm801->play_shift = (i<<8);
400 fm801->play_shift &= FM_PLAY_RATE_MASK;
401 }
402
403 if(ch->dir == PCMDIR_REC ) {
404 fm801->rch.spd = fm801_rates[i].rate;
405 fm801->rec_shift = (i<<8);
406 fm801->rec_shift &= FM_REC_RATE_MASK;
407 }
408
409 ch->spd = fm801_rates[i].rate;
410
411 return fm801_rates[i].rate;
412 }
413
414 static int
415 fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
416 {
417 struct fm801_chinfo *ch = data;
418 struct fm801_info *fm801 = ch->parent;
419
420 if(ch->dir == PCMDIR_PLAY) {
421 if(fm801->play_flip) return fm801->play_blksize;
422 fm801->play_blksize = blocksize;
423 }
424
425 if(ch->dir == PCMDIR_REC) {
426 if(fm801->rec_flip) return fm801->rec_blksize;
427 fm801->rec_blksize = blocksize;
428 }
429
430 DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
431
432 return blocksize;
433 }
434
435 static int
436 fm801ch_trigger(kobj_t obj, void *data, int go)
437 {
438 struct fm801_chinfo *ch = data;
439 struct fm801_info *fm801 = ch->parent;
440 u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer);
441 u_int32_t k1;
442
443 DPRINT("fm801ch_trigger go %d , ", go);
444
445 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
446 return 0;
447 }
448
449 if (ch->dir == PCMDIR_PLAY) {
450 if (go == PCMTRIG_START) {
451
452 fm801->play_start = baseaddr;
453 fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
454 fm801->play_flip = 0;
455 fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
456 fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
457 fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
458 fm801_wr(fm801, FM_PLAY_CTL,
459 FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
460 2 );
461 } else {
462 fm801->play_flip = 0;
463 k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
464 fm801_wr(fm801, FM_PLAY_CTL,
465 (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
466 FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
467 }
468 } else if(ch->dir == PCMDIR_REC) {
469 if (go == PCMTRIG_START) {
470 fm801->rec_start = baseaddr;
471 fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
472 fm801->rec_flip = 0;
473 fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
474 fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
475 fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
476 fm801_wr(fm801, FM_REC_CTL,
477 FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
478 2 );
479 } else {
480 fm801->rec_flip = 0;
481 k1 = fm801_rd(fm801, FM_REC_CTL,2);
482 fm801_wr(fm801, FM_REC_CTL,
483 (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
484 FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
485 }
486 }
487
488 return 0;
489 }
490
491 /* Almost ALSA copy */
492 static int
493 fm801ch_getptr(kobj_t obj, void *data)
494 {
495 struct fm801_chinfo *ch = data;
496 struct fm801_info *fm801 = ch->parent;
497 int result = 0;
498
499 if (ch->dir == PCMDIR_PLAY) {
500 result = fm801_rd(fm801,
501 (fm801->play_flip&1) ?
502 FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
503 }
504
505 if (ch->dir == PCMDIR_REC) {
506 result = fm801_rd(fm801,
507 (fm801->rec_flip&1) ?
508 FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
509 }
510
511 return result;
512 }
513
514 static struct pcmchan_caps *
515 fm801ch_getcaps(kobj_t obj, void *data)
516 {
517 return &fm801ch_caps;
518 }
519
520 static kobj_method_t fm801ch_methods[] = {
521 KOBJMETHOD(channel_init, fm801ch_init),
522 KOBJMETHOD(channel_setformat, fm801ch_setformat),
523 KOBJMETHOD(channel_setspeed, fm801ch_setspeed),
524 KOBJMETHOD(channel_setblocksize, fm801ch_setblocksize),
525 KOBJMETHOD(channel_trigger, fm801ch_trigger),
526 KOBJMETHOD(channel_getptr, fm801ch_getptr),
527 KOBJMETHOD(channel_getcaps, fm801ch_getcaps),
528 { 0, 0 }
529 };
530 CHANNEL_DECLARE(fm801ch);
531
532 /* -------------------------------------------------------------------- */
533
534 /*
535 * Init routine is taken from an original NetBSD driver
536 */
537 static int
538 fm801_init(struct fm801_info *fm801)
539 {
540 u_int32_t k1;
541
542 /* reset codec */
543 fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
544 DELAY(100000);
545 fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
546 DELAY(100000);
547
548 fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
549 fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
550 fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
551 fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */
552
553 fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
554
555 /* Unmask playback, record and mpu interrupts, mask the rest */
556 k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
557 fm801_wr(fm801, FM_INTMASK,
558 (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
559 FM_INTMASK_VOL,2);
560 fm801_wr(fm801, FM_INTSTATUS,
561 FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
562 FM_INTSTATUS_VOL,2);
563
564 DPRINT("FM801 init Ok\n");
565 return 0;
566 }
567
568 static int
569 fm801_pci_attach(device_t dev)
570 {
571 u_int32_t data;
572 struct ac97_info *codec = 0;
573 struct fm801_info *fm801;
574 int i;
575 int mapped = 0;
576 char status[SND_STATUSLEN];
577
578 if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
579 device_printf(dev, "cannot allocate softc\n");
580 return ENXIO;
581 }
582
583 fm801->type = pci_get_devid(dev);
584
585 data = pci_read_config(dev, PCIR_COMMAND, 2);
586 data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
587 pci_write_config(dev, PCIR_COMMAND, data, 2);
588 data = pci_read_config(dev, PCIR_COMMAND, 2);
589
590 for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
591 fm801->regid = PCIR_BAR(i);
592 fm801->regtype = SYS_RES_MEMORY;
593 fm801->reg = bus_alloc_resource_any(dev, fm801->regtype,
594 &fm801->regid, RF_ACTIVE);
595 if(!fm801->reg)
596 {
597 fm801->regtype = SYS_RES_IOPORT;
598 fm801->reg = bus_alloc_resource_any(dev,
599 fm801->regtype,
600 &fm801->regid,
601 RF_ACTIVE);
602 }
603
604 if(fm801->reg) {
605 fm801->st = rman_get_bustag(fm801->reg);
606 fm801->sh = rman_get_bushandle(fm801->reg);
607 mapped++;
608 }
609 }
610
611 if (mapped == 0) {
612 device_printf(dev, "unable to map register space\n");
613 goto oops;
614 }
615
616 fm801->bufsz = pcm_getbuffersize(dev, 4096, FM801_DEFAULT_BUFSZ, 65536);
617
618 fm801_init(fm801);
619
620 codec = AC97_CREATE(dev, fm801, fm801_ac97);
621 if (codec == NULL) goto oops;
622
623 if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
624
625 fm801->irqid = 0;
626 fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid,
627 RF_ACTIVE | RF_SHAREABLE);
628 if (!fm801->irq || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) {
629 device_printf(dev, "unable to map interrupt\n");
630 goto oops;
631 }
632
633 if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
634 /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
635 /*highaddr*/BUS_SPACE_MAXADDR,
636 /*filter*/NULL, /*filterarg*/NULL,
637 /*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
638 /*flags*/0, /*lockfunc*/busdma_lock_mutex,
639 /*lockarg*/&Giant, &fm801->parent_dmat) != 0) {
640 device_printf(dev, "unable to create dma tag\n");
641 goto oops;
642 }
643
644 snprintf(status, 64, "at %s 0x%lx irq %ld %s",
645 (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
646 rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801));
647
648 #define FM801_MAXPLAYCH 1
649 if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
650 pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
651 pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
652 pcm_setstatus(dev, status);
653
654 fm801->radio = device_add_child(dev, "radio", -1);
655 bus_generic_attach(dev);
656
657 return 0;
658
659 oops:
660 if (codec) ac97_destroy(codec);
661 if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
662 if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
663 if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
664 if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
665 free(fm801, M_DEVBUF);
666 return ENXIO;
667 }
668
669 static int
670 fm801_pci_detach(device_t dev)
671 {
672 int r;
673 struct fm801_info *fm801;
674
675 DPRINT("Forte Media FM801 detach\n");
676
677 fm801 = pcm_getdevinfo(dev);
678
679 r = bus_generic_detach(dev);
680 if (r)
681 return r;
682 if (fm801->radio != NULL) {
683 r = device_delete_child(dev, fm801->radio);
684 if (r)
685 return r;
686 fm801->radio = NULL;
687 }
688
689 r = pcm_unregister(dev);
690 if (r)
691 return r;
692
693 bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
694 bus_teardown_intr(dev, fm801->irq, fm801->ih);
695 bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
696 bus_dma_tag_destroy(fm801->parent_dmat);
697 free(fm801, M_DEVBUF);
698 return 0;
699 }
700
701 static int
702 fm801_pci_probe( device_t dev )
703 {
704 int id;
705
706 if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
707 device_set_desc(dev, "Forte Media FM801 Audio Controller");
708 return 0;
709 }
710 /*
711 if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
712 device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
713 return ENXIO;
714 }
715 */
716 return ENXIO;
717 }
718
719 static struct resource *
720 fm801_alloc_resource(device_t bus, device_t child, int type, int *rid,
721 u_long start, u_long end, u_long count, u_int flags)
722 {
723 struct fm801_info *fm801;
724
725 fm801 = pcm_getdevinfo(bus);
726
727 if (type == SYS_RES_IOPORT && *rid == PCIR_BAR(0))
728 return (fm801->reg);
729
730 return (NULL);
731 }
732
733 static int
734 fm801_release_resource(device_t bus, device_t child, int type, int rid,
735 struct resource *r)
736 {
737 return (0);
738 }
739
740 static device_method_t fm801_methods[] = {
741 /* Device interface */
742 DEVMETHOD(device_probe, fm801_pci_probe),
743 DEVMETHOD(device_attach, fm801_pci_attach),
744 DEVMETHOD(device_detach, fm801_pci_detach),
745 DEVMETHOD(device_shutdown, bus_generic_shutdown),
746 DEVMETHOD(device_suspend, bus_generic_suspend),
747 DEVMETHOD(device_resume, bus_generic_resume),
748
749 /* Bus interface */
750 DEVMETHOD(bus_print_child, bus_generic_print_child),
751 DEVMETHOD(bus_alloc_resource, fm801_alloc_resource),
752 DEVMETHOD(bus_release_resource, fm801_release_resource),
753 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
754 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
755 { 0, 0}
756 };
757
758 static driver_t fm801_driver = {
759 "pcm",
760 fm801_methods,
761 PCM_SOFTC_SIZE,
762 };
763
764 DRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0);
765 MODULE_DEPEND(snd_fm801, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
766 MODULE_VERSION(snd_fm801, 1);
Cache object: 81a519aff3dd634c75e0701f950aa946
|