Annotation of sys/dev/pci/fms.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: fms.c,v 1.17 2005/04/16 21:57:23 mickey Exp $ */
2: /* $NetBSD: fms.c,v 1.5.4.1 2000/06/30 16:27:50 simonb Exp $ */
3:
4: /*-
5: * Copyright (c) 1999 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Witold J. Wnuk.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: /*
41: * Forte Media FM801 Audio Device Driver
42: */
43:
44: #include "radio.h"
45:
46: #include <sys/param.h>
47: #include <sys/systm.h>
48: #include <sys/kernel.h>
49: #include <sys/malloc.h>
50: #include <sys/device.h>
51: #include <sys/audioio.h>
52:
53: #include <machine/bus.h>
54: #include <machine/cpu.h>
55:
56: #include <dev/pci/pcidevs.h>
57: #include <dev/pci/pcivar.h>
58:
59: #include <dev/audio_if.h>
60: #include <dev/mulaw.h>
61: #include <dev/auconv.h>
62:
63: #include <dev/ic/ac97.h>
64: #if 0
65: #include <dev/ic/mpuvar.h>
66: #endif
67:
68: #include <dev/pci/fmsreg.h>
69: #include <dev/pci/fmsvar.h>
70:
71:
72: struct fms_dma {
73: struct fms_dma *next;
74: caddr_t addr;
75: size_t size;
76: bus_dmamap_t map;
77: bus_dma_segment_t seg;
78: };
79:
80:
81:
82: int fms_match(struct device *, void *, void *);
83: void fms_attach(struct device *, struct device *, void *);
84: int fms_intr(void *);
85:
86: int fms_open(void *, int);
87: void fms_close(void *);
88: int fms_query_encoding(void *, struct audio_encoding *);
89: int fms_set_params(void *, int, int, struct audio_params *,
90: struct audio_params *);
91: int fms_round_blocksize(void *, int);
92: int fms_halt_output(void *);
93: int fms_halt_input(void *);
94: int fms_getdev(void *, struct audio_device *);
95: int fms_set_port(void *, mixer_ctrl_t *);
96: int fms_get_port(void *, mixer_ctrl_t *);
97: int fms_query_devinfo(void *, mixer_devinfo_t *);
98: void *fms_malloc(void *, int, size_t, int, int);
99: void fms_free(void *, void *, int);
100: paddr_t fms_mappage(void *, void *, off_t, int);
101: int fms_get_props(void *);
102: int fms_trigger_output(void *, void *, void *, int, void (*)(void *),
103: void *, struct audio_params *);
104: int fms_trigger_input(void *, void *, void *, int, void (*)(void *),
105: void *, struct audio_params *);
106:
107: struct cfdriver fms_cd = {
108: NULL, "fms", DV_DULL
109: };
110:
111: struct cfattach fms_ca = {
112: sizeof (struct fms_softc), fms_match, fms_attach
113: };
114:
115: struct audio_device fms_device = {
116: "Forte Media 801",
117: "1.0",
118: "fms"
119: };
120:
121:
122: struct audio_hw_if fms_hw_if = {
123: fms_open,
124: fms_close,
125: NULL,
126: fms_query_encoding,
127: fms_set_params,
128: fms_round_blocksize,
129: NULL,
130: NULL,
131: NULL,
132: NULL,
133: NULL,
134: fms_halt_output,
135: fms_halt_input,
136: NULL,
137: fms_getdev,
138: NULL,
139: fms_set_port,
140: fms_get_port,
141: fms_query_devinfo,
142: fms_malloc,
143: fms_free,
144: NULL,
145: fms_mappage,
146: fms_get_props,
147: fms_trigger_output,
148: fms_trigger_input
149: };
150:
151: int fms_attach_codec(void *, struct ac97_codec_if *);
152: int fms_read_codec(void *, u_int8_t, u_int16_t *);
153: int fms_write_codec(void *, u_int8_t, u_int16_t);
154: void fms_reset_codec(void *);
155:
156: int fms_allocmem(struct fms_softc *, size_t, size_t,
157: struct fms_dma *);
158: int fms_freemem(struct fms_softc *, struct fms_dma *);
159:
160: int
161: fms_match(parent, match, aux)
162: struct device *parent;
163: void *match;
164: void *aux;
165: {
166: struct pci_attach_args *pa = (struct pci_attach_args *) aux;
167:
168: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_FORTEMEDIA &&
169: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_FORTEMEDIA_FM801)
170: return (1);
171: return (0);
172: }
173:
174: void
175: fms_attach(parent, self, aux)
176: struct device *parent;
177: struct device *self;
178: void *aux;
179: {
180: struct pci_attach_args *pa = aux;
181: struct fms_softc *sc = (struct fms_softc *) self;
182: struct audio_attach_args aa;
183: pci_chipset_tag_t pc = pa->pa_pc;
184: pcitag_t pt = pa->pa_tag;
185: pci_intr_handle_t ih;
186: bus_size_t iosize;
187: const char *intrstr;
188: u_int16_t k1;
189: int i;
190:
191: if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
192: &sc->sc_ioh, NULL, &iosize, 0)) {
193: printf(": can't map i/o space\n");
194: return;
195: }
196:
197: if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x30, 2,
198: &sc->sc_mpu_ioh)) {
199: printf(": can't get mpu subregion handle\n");
200: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
201: return;
202: }
203:
204: if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x68, 4,
205: &sc->sc_opl_ioh)) {
206: printf(": can't get opl subregion handle\n");
207: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
208: return;
209: }
210:
211: if (pci_intr_map(pa, &ih)) {
212: printf(": couldn't map interrupt\n");
213: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
214: return;
215: }
216: intrstr = pci_intr_string(pc, ih);
217:
218: sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, fms_intr, sc,
219: sc->sc_dev.dv_xname);
220: if (sc->sc_ih == NULL) {
221: printf(": couldn't establish interrupt");
222: if (intrstr != NULL)
223: printf(" at %s", intrstr);
224: printf("\n");
225: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
226: return;
227: }
228:
229: printf(": %s\n", intrstr);
230:
231: sc->sc_dmat = pa->pa_dmat;
232:
233: /* Disable legacy audio (SBPro compatibility) */
234: pci_conf_write(pc, pt, 0x40, 0);
235:
236: /* Reset codec and AC'97 */
237: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020);
238: delay(2); /* > 1us according to AC'97 documentation */
239: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000);
240: delay(1); /* > 168.2ns according to AC'97 documentation */
241:
242: /* Set up volume */
243: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PCM_VOLUME, 0x0808);
244: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_FM_VOLUME, 0x0808);
245: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_I2S_VOLUME, 0x0808);
246:
247: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_RECORD_SOURCE, 0x0000);
248:
249: /* Unmask playback, record and mpu interrupts, mask the rest */
250: k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK);
251: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK,
252: (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
253: FM_INTMASK_VOL);
254: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS,
255: FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
256: FM_INTSTATUS_VOL);
257:
258: #if NRADIO > 0
259: fmsradio_attach(sc);
260: #endif /* NRADIO > 0 */
261:
262: sc->host_if.arg = sc;
263: sc->host_if.attach = fms_attach_codec;
264: sc->host_if.read = fms_read_codec;
265: sc->host_if.write = fms_write_codec;
266: sc->host_if.reset = fms_reset_codec;
267:
268: if (ac97_attach(&sc->host_if) != 0)
269: return;
270:
271: /* Turn mute off */
272: for (i = 0; i < 3; i++) {
273: static struct {
274: char *class, *device;
275: } d[] = {
276: { AudioCoutputs, AudioNmaster },
277: { AudioCinputs, AudioNdac },
278: { AudioCrecord, AudioNvolume }
279: };
280: struct mixer_ctrl ctl;
281:
282: ctl.type = AUDIO_MIXER_ENUM;
283: ctl.un.ord = 0;
284: ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
285: d[i].class, d[i].device, AudioNmute);
286: fms_set_port(sc, &ctl);
287: }
288:
289: audio_attach_mi(&fms_hw_if, sc, &sc->sc_dev);
290:
291: aa.type = AUDIODEV_TYPE_OPL;
292: aa.hwif = NULL;
293: aa.hdl = NULL;
294: config_found(&sc->sc_dev, &aa, audioprint);
295:
296: aa.type = AUDIODEV_TYPE_MPU;
297: aa.hwif = NULL;
298: aa.hdl = NULL;
299: sc->sc_mpu_dev = config_found(&sc->sc_dev, &aa, audioprint);
300: }
301:
302: /*
303: * Each AC-link frame takes 20.8us, data should be ready in next frame,
304: * we allow more than two.
305: */
306: #define TIMO 50
307: int
308: fms_read_codec(addr, reg, val)
309: void *addr;
310: u_int8_t reg;
311: u_int16_t *val;
312: {
313: struct fms_softc *sc = addr;
314: int i;
315:
316: /* Poll until codec is ready */
317: for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh,
318: FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++)
319: delay(1);
320: if (i >= TIMO) {
321: printf("fms: codec busy\n");
322: return 1;
323: }
324:
325: /* Write register index, read access */
326: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD,
327: reg | FM_CODEC_CMD_READ);
328:
329: /* Poll until we have valid data */
330: for (i = 0; i < TIMO && !(bus_space_read_2(sc->sc_iot, sc->sc_ioh,
331: FM_CODEC_CMD) & FM_CODEC_CMD_VALID); i++)
332: delay(1);
333: if (i >= TIMO) {
334: printf("fms: no data from codec\n");
335: return 1;
336: }
337:
338: /* Read data */
339: *val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA);
340: return 0;
341: }
342:
343: int
344: fms_write_codec(addr, reg, val)
345: void *addr;
346: u_int8_t reg;
347: u_int16_t val;
348: {
349: struct fms_softc *sc = addr;
350: int i;
351:
352: /* Poll until codec is ready */
353: for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh,
354: FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++)
355: delay(1);
356: if (i >= TIMO) {
357: printf("fms: codec busy\n");
358: return 1;
359: }
360:
361: /* Write data */
362: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA, val);
363: /* Write index register, write access */
364: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, reg);
365: return 0;
366: }
367: #undef TIMO
368:
369: int
370: fms_attach_codec(addr, cif)
371: void *addr;
372: struct ac97_codec_if *cif;
373: {
374: struct fms_softc *sc = addr;
375:
376: sc->codec_if = cif;
377: return 0;
378: }
379:
380: /* Cold Reset */
381: void
382: fms_reset_codec(addr)
383: void *addr;
384: {
385: struct fms_softc *sc = addr;
386: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020);
387: delay(2);
388: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000);
389: delay(1);
390: }
391:
392: int
393: fms_intr(arg)
394: void *arg;
395: {
396: struct fms_softc *sc = arg;
397: u_int16_t istat;
398:
399: istat = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS);
400:
401: if (istat & FM_INTSTATUS_PLAY) {
402: if ((sc->sc_play_nextblk += sc->sc_play_blksize) >=
403: sc->sc_play_end)
404: sc->sc_play_nextblk = sc->sc_play_start;
405:
406: bus_space_write_4(sc->sc_iot, sc->sc_ioh,
407: sc->sc_play_flip++ & 1 ?
408: FM_PLAY_DMABUF2 : FM_PLAY_DMABUF1, sc->sc_play_nextblk);
409:
410: if (sc->sc_pintr)
411: sc->sc_pintr(sc->sc_parg);
412: else
413: printf("unexpected play intr\n");
414: }
415:
416: if (istat & FM_INTSTATUS_REC) {
417: if ((sc->sc_rec_nextblk += sc->sc_rec_blksize) >=
418: sc->sc_rec_end)
419: sc->sc_rec_nextblk = sc->sc_rec_start;
420:
421: bus_space_write_4(sc->sc_iot, sc->sc_ioh,
422: sc->sc_rec_flip++ & 1 ?
423: FM_REC_DMABUF2 : FM_REC_DMABUF1, sc->sc_rec_nextblk);
424:
425: if (sc->sc_rintr)
426: sc->sc_rintr(sc->sc_rarg);
427: else
428: printf("unexpected rec intr\n");
429: }
430:
431: #if 0
432: if (istat & FM_INTSTATUS_MPU)
433: mpu_intr(sc->sc_mpu_dev);
434: #endif
435:
436: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS,
437: istat & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC));
438:
439: return 1;
440: }
441:
442: int
443: fms_open(addr, flags)
444: void *addr;
445: int flags;
446: {
447: /* UNUSED struct fms_softc *sc = addr;*/
448:
449: return 0;
450: }
451:
452: void
453: fms_close(addr)
454: void *addr;
455: {
456: /* UNUSED struct fms_softc *sc = addr;*/
457: }
458:
459: int
460: fms_query_encoding(addr, fp)
461: void *addr;
462: struct audio_encoding *fp;
463: {
464:
465: switch (fp->index) {
466: case 0:
467: strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
468: fp->encoding = AUDIO_ENCODING_ULAW;
469: fp->precision = 8;
470: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
471: return 0;
472: case 1:
473: strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
474: fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
475: fp->precision = 16;
476: fp->flags = 0;
477: return 0;
478: case 2:
479: strlcpy(fp->name, AudioEulinear, sizeof fp->name);
480: fp->encoding = AUDIO_ENCODING_ULINEAR;
481: fp->precision = 8;
482: fp->flags = 0;
483: return 0;
484: case 3:
485: strlcpy(fp->name, AudioEalaw, sizeof fp->name);
486: fp->encoding = AUDIO_ENCODING_ALAW;
487: fp->precision = 8;
488: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
489: return 0;
490: case 4:
491: strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
492: fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
493: fp->precision = 16;
494: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
495: return 0;
496: case 5:
497: strlcpy(fp->name, AudioEslinear, sizeof fp->name);
498: fp->encoding = AUDIO_ENCODING_SLINEAR;
499: fp->precision = 8;
500: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
501: return 0;
502: case 6:
503: strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
504: fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
505: fp->precision = 16;
506: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
507: return 0;
508: case 7:
509: strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
510: fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
511: fp->precision = 16;
512: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
513: return 0;
514: default:
515: return EINVAL;
516: }
517: }
518:
519: /*
520: * Range below -limit- is set to -rate-
521: * What a pity FM801 does not have 24000
522: * 24000 -> 22050 sounds rather poor
523: */
524: struct {
525: int limit;
526: int rate;
527: } fms_rates[11] = {
528: { 6600, 5500 },
529: { 8750, 8000 },
530: { 10250, 9600 },
531: { 13200, 11025 },
532: { 17500, 16000 },
533: { 20500, 19200 },
534: { 26500, 22050 },
535: { 35000, 32000 },
536: { 41000, 38400 },
537: { 46000, 44100 },
538: { 48000, 48000 },
539: /* anything above -> 48000 */
540: };
541:
542: int
543: fms_set_params(addr, setmode, usemode, play, rec)
544: void *addr;
545: int setmode, usemode;
546: struct audio_params *play, *rec;
547: {
548: struct fms_softc *sc = addr;
549: int i;
550:
551: if (setmode & AUMODE_PLAY) {
552: play->factor = 1;
553: play->sw_code = 0;
554: switch(play->encoding) {
555: case AUDIO_ENCODING_ULAW:
556: play->factor = 2;
557: play->sw_code = mulaw_to_slinear16_le;
558: break;
559: case AUDIO_ENCODING_SLINEAR_LE:
560: if (play->precision == 8)
561: play->sw_code = change_sign8;
562: break;
563: case AUDIO_ENCODING_ULINEAR_LE:
564: if (play->precision == 16)
565: play->sw_code = change_sign16_le;
566: break;
567: case AUDIO_ENCODING_ALAW:
568: play->factor = 2;
569: play->sw_code = alaw_to_slinear16_le;
570: break;
571: case AUDIO_ENCODING_SLINEAR_BE:
572: if (play->precision == 16)
573: play->sw_code = swap_bytes;
574: else
575: play->sw_code = change_sign8;
576: break;
577: case AUDIO_ENCODING_ULINEAR_BE:
578: if (play->precision == 16)
579: play->sw_code = change_sign16_swap_bytes_le;
580: break;
581: default:
582: return EINVAL;
583: }
584: for (i = 0; i < 10 && play->sample_rate > fms_rates[i].limit;
585: i++)
586: ;
587: play->sample_rate = fms_rates[i].rate;
588: sc->sc_play_reg = (play->channels == 2 ? FM_PLAY_STEREO : 0) |
589: (play->precision * play->factor == 16 ? FM_PLAY_16BIT : 0) |
590: (i << 8);
591: }
592:
593: if (setmode & AUMODE_RECORD) {
594:
595: rec->factor = 1;
596: rec->sw_code = 0;
597: switch(rec->encoding) {
598: case AUDIO_ENCODING_ULAW:
599: rec->sw_code = ulinear8_to_mulaw;
600: break;
601: case AUDIO_ENCODING_SLINEAR_LE:
602: if (rec->precision == 8)
603: rec->sw_code = change_sign8;
604: break;
605: case AUDIO_ENCODING_ULINEAR_LE:
606: if (rec->precision == 16)
607: rec->sw_code = change_sign16_le;
608: break;
609: case AUDIO_ENCODING_ALAW:
610: rec->sw_code = ulinear8_to_alaw;
611: break;
612: case AUDIO_ENCODING_SLINEAR_BE:
613: if (play->precision == 16)
614: play->sw_code = swap_bytes;
615: else
616: play->sw_code = change_sign8;
617: break;
618: case AUDIO_ENCODING_ULINEAR_BE:
619: if (play->precision == 16)
620: play->sw_code = swap_bytes_change_sign16_le;
621: break;
622: default:
623: return EINVAL;
624: }
625: for (i = 0; i < 10 && rec->sample_rate > fms_rates[i].limit;
626: i++)
627: ;
628: rec->sample_rate = fms_rates[i].rate;
629: sc->sc_rec_reg =
630: (rec->channels == 2 ? FM_REC_STEREO : 0) |
631: (rec->precision * rec->factor == 16 ? FM_REC_16BIT : 0) |
632: (i << 8);
633: }
634:
635: return 0;
636: }
637:
638: int
639: fms_round_blocksize(addr, blk)
640: void *addr;
641: int blk;
642: {
643: return (blk + 0xf) & ~0xf;
644: }
645:
646: int
647: fms_halt_output(addr)
648: void *addr;
649: {
650: struct fms_softc *sc = addr;
651: u_int16_t k1;
652:
653: k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL);
654: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL,
655: (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
656: FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST);
657:
658: return 0;
659: }
660:
661: int
662: fms_halt_input(addr)
663: void *addr;
664: {
665: struct fms_softc *sc = addr;
666: u_int16_t k1;
667:
668: k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL);
669: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL,
670: (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
671: FM_REC_BUF1_LAST | FM_REC_BUF2_LAST);
672:
673: return 0;
674: }
675:
676: int
677: fms_getdev(addr, retp)
678: void *addr;
679: struct audio_device *retp;
680: {
681: *retp = fms_device;
682: return 0;
683: }
684:
685: int
686: fms_set_port(addr, cp)
687: void *addr;
688: mixer_ctrl_t *cp;
689: {
690: struct fms_softc *sc = addr;
691:
692: return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
693: }
694:
695: int
696: fms_get_port(addr, cp)
697: void *addr;
698: mixer_ctrl_t *cp;
699: {
700: struct fms_softc *sc = addr;
701:
702: return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
703: }
704:
705: void *
706: fms_malloc(addr, direction, size, pool, flags)
707: void *addr;
708: int direction;
709: size_t size;
710: int pool, flags;
711: {
712: struct fms_softc *sc = addr;
713: struct fms_dma *p;
714: int error;
715: int rseg;
716:
717: p = malloc(sizeof(*p), pool, flags);
718: if (!p)
719: return 0;
720:
721: p->size = size;
722: if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &p->seg, 1,
723: &rseg, BUS_DMA_NOWAIT)) != 0) {
724: printf("%s: unable to allocate dma, error = %d\n",
725: sc->sc_dev.dv_xname, error);
726: goto fail_alloc;
727: }
728:
729: if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
730: BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
731: printf("%s: unable to map dma, error = %d\n",
732: sc->sc_dev.dv_xname, error);
733: goto fail_map;
734: }
735:
736: if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
737: BUS_DMA_NOWAIT, &p->map)) != 0) {
738: printf("%s: unable to create dma map, error = %d\n",
739: sc->sc_dev.dv_xname, error);
740: goto fail_create;
741: }
742:
743: if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
744: BUS_DMA_NOWAIT)) != 0) {
745: printf("%s: unable to load dma map, error = %d\n",
746: sc->sc_dev.dv_xname, error);
747: goto fail_load;
748: }
749:
750: p->next = sc->sc_dmas;
751: sc->sc_dmas = p;
752:
753: return p->addr;
754:
755:
756: fail_load:
757: bus_dmamap_destroy(sc->sc_dmat, p->map);
758: fail_create:
759: bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
760: fail_map:
761: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
762: fail_alloc:
763: free(p, pool);
764: return 0;
765: }
766:
767: void
768: fms_free(addr, ptr, pool)
769: void *addr;
770: void *ptr;
771: int pool;
772: {
773: struct fms_softc *sc = addr;
774: struct fms_dma **pp, *p;
775:
776: for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
777: if (p->addr == ptr) {
778: bus_dmamap_unload(sc->sc_dmat, p->map);
779: bus_dmamap_destroy(sc->sc_dmat, p->map);
780: bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
781: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
782:
783: *pp = p->next;
784: free(p, pool);
785: return;
786: }
787:
788: panic("fms_free: trying to free unallocated memory");
789: }
790:
791: paddr_t
792: fms_mappage(addr, mem, off, prot)
793: void *addr;
794: void *mem;
795: off_t off;
796: int prot;
797: {
798: struct fms_softc *sc = addr;
799: struct fms_dma *p;
800:
801: if (off < 0)
802: return -1;
803:
804: for (p = sc->sc_dmas; p && p->addr != mem; p = p->next)
805: ;
806: if (!p)
807: return -1;
808:
809: return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot,
810: BUS_DMA_WAITOK);
811: }
812:
813: int
814: fms_get_props(addr)
815: void *addr;
816: {
817: return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
818: AUDIO_PROP_FULLDUPLEX;
819: }
820:
821: int
822: fms_query_devinfo(addr, dip)
823: void *addr;
824: mixer_devinfo_t *dip;
825: {
826: struct fms_softc *sc = addr;
827:
828: return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
829: }
830:
831: int
832: fms_trigger_output(addr, start, end, blksize, intr, arg, param)
833: void *addr;
834: void *start, *end;
835: int blksize;
836: void (*intr)(void *);
837: void *arg;
838: struct audio_params *param;
839: {
840: struct fms_softc *sc = addr;
841: struct fms_dma *p;
842:
843: sc->sc_pintr = intr;
844: sc->sc_parg = arg;
845:
846: for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
847: ;
848:
849: if (!p)
850: panic("fms_trigger_output: request with bad start "
851: "address (%p)", start);
852:
853: sc->sc_play_start = p->map->dm_segs[0].ds_addr;
854: sc->sc_play_end = sc->sc_play_start + ((char *)end - (char *)start);
855: sc->sc_play_blksize = blksize;
856: sc->sc_play_nextblk = sc->sc_play_start + sc->sc_play_blksize;
857: sc->sc_play_flip = 0;
858: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMALEN, blksize - 1);
859: bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF1,
860: sc->sc_play_start);
861: bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF2,
862: sc->sc_play_nextblk);
863: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL,
864: FM_PLAY_START | FM_PLAY_STOPNOW | sc->sc_play_reg);
865: return 0;
866: }
867:
868:
869: int
870: fms_trigger_input(addr, start, end, blksize, intr, arg, param)
871: void *addr;
872: void *start, *end;
873: int blksize;
874: void (*intr)(void *);
875: void *arg;
876: struct audio_params *param;
877: {
878: struct fms_softc *sc = addr;
879: struct fms_dma *p;
880:
881: sc->sc_rintr = intr;
882: sc->sc_rarg = arg;
883:
884: for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
885: ;
886:
887: if (!p)
888: panic("fms_trigger_input: request with bad start "
889: "address (%p)", start);
890:
891: sc->sc_rec_start = p->map->dm_segs[0].ds_addr;
892: sc->sc_rec_end = sc->sc_rec_start + ((char *)end - (char *)start);
893: sc->sc_rec_blksize = blksize;
894: sc->sc_rec_nextblk = sc->sc_rec_start + sc->sc_rec_blksize;
895: sc->sc_rec_flip = 0;
896: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_DMALEN, blksize - 1);
897: bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF1,
898: sc->sc_rec_start);
899: bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF2,
900: sc->sc_rec_nextblk);
901: bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL,
902: FM_REC_START | FM_REC_STOPNOW | sc->sc_rec_reg);
903: return 0;
904: }
905:
906:
CVSweb