fms.c revision 1.29
1214501Srpaulo/*	$OpenBSD: fms.c,v 1.29 2016/09/19 06:46:44 ratchov Exp $ */
2214501Srpaulo/*	$NetBSD: fms.c,v 1.5.4.1 2000/06/30 16:27:50 simonb Exp $	*/
3214501Srpaulo
4214501Srpaulo/*-
5214501Srpaulo * Copyright (c) 1999 The NetBSD Foundation, Inc.
6214501Srpaulo * All rights reserved.
7214501Srpaulo *
8214501Srpaulo * This code is derived from software contributed to The NetBSD Foundation
9214501Srpaulo * by Witold J. Wnuk.
10214501Srpaulo *
11214501Srpaulo * Redistribution and use in source and binary forms, with or without
12214501Srpaulo * modification, are permitted provided that the following conditions
13214501Srpaulo * are met:
14214501Srpaulo * 1. Redistributions of source code must retain the above copyright
15214501Srpaulo *    notice, this list of conditions and the following disclaimer.
16214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
17214501Srpaulo *    notice, this list of conditions and the following disclaimer in the
18214501Srpaulo *    documentation and/or other materials provided with the distribution.
19214501Srpaulo *
20214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21214501Srpaulo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22214501Srpaulo * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23214501Srpaulo * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24214501Srpaulo * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25214501Srpaulo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26214501Srpaulo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27214501Srpaulo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28214501Srpaulo * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29214501Srpaulo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30214501Srpaulo * POSSIBILITY OF SUCH DAMAGE.
31214501Srpaulo */
32214501Srpaulo
33214501Srpaulo/*
34214501Srpaulo * Forte Media FM801 Audio Device Driver
35214501Srpaulo */
36214501Srpaulo
37214501Srpaulo#include "radio.h"
38214501Srpaulo
39214501Srpaulo#include <sys/param.h>
40214501Srpaulo#include <sys/systm.h>
41214501Srpaulo#include <sys/kernel.h>
42214501Srpaulo#include <sys/malloc.h>
43214501Srpaulo#include <sys/device.h>
44214501Srpaulo#include <sys/audioio.h>
45214501Srpaulo
46214501Srpaulo#include <machine/bus.h>
47214501Srpaulo#include <machine/cpu.h>
48214501Srpaulo
49214501Srpaulo#include <dev/pci/pcidevs.h>
50214501Srpaulo#include <dev/pci/pcivar.h>
51214501Srpaulo
52214501Srpaulo#include <dev/audio_if.h>
53214501Srpaulo#include <dev/ic/ac97.h>
54214501Srpaulo#if 0
55214501Srpaulo#include <dev/ic/mpuvar.h>
56214501Srpaulo#endif
57214501Srpaulo
58214501Srpaulo#include <dev/pci/fmsreg.h>
59214501Srpaulo#include <dev/pci/fmsvar.h>
60214501Srpaulo
61214501Srpaulo
62214501Srpaulostruct fms_dma {
63214501Srpaulo	struct fms_dma *next;
64214501Srpaulo	caddr_t addr;
65214501Srpaulo	size_t size;
66214501Srpaulo	bus_dmamap_t map;
67214501Srpaulo	bus_dma_segment_t seg;
68214501Srpaulo};
69214501Srpaulo
70214501Srpaulo
71214501Srpaulo
72214501Srpauloint	fms_match(struct device *, void *, void *);
73214501Srpaulovoid	fms_attach(struct device *, struct device *, void *);
74214501Srpauloint	fms_intr(void *);
75214501Srpaulo
76214501Srpauloint	fms_open(void *, int);
77214501Srpaulovoid	fms_close(void *);
78214501Srpauloint	fms_set_params(void *, int, int, struct audio_params *,
79214501Srpaulo			    struct audio_params *);
80214501Srpauloint	fms_round_blocksize(void *, int);
81214501Srpauloint	fms_halt_output(void *);
82214501Srpauloint	fms_halt_input(void *);
83214501Srpauloint	fms_set_port(void *, mixer_ctrl_t *);
84214501Srpauloint	fms_get_port(void *, mixer_ctrl_t *);
85214501Srpauloint	fms_query_devinfo(void *, mixer_devinfo_t *);
86214501Srpaulovoid	*fms_malloc(void *, int, size_t, int, int);
87214501Srpaulovoid	fms_free(void *, void *, int);
88214501Srpauloint	fms_get_props(void *);
89214501Srpauloint	fms_trigger_output(void *, void *, void *, int, void (*)(void *),
90214501Srpaulo			   void *, struct audio_params *);
91214501Srpauloint	fms_trigger_input(void *, void *, void *, int, void (*)(void *),
92214501Srpaulo			  void *, struct audio_params *);
93214501Srpaulo
94214501Srpaulostruct  cfdriver fms_cd = {
95214501Srpaulo	NULL, "fms", DV_DULL
96214501Srpaulo};
97214501Srpaulo
98214501Srpaulostruct cfattach fms_ca = {
99214501Srpaulo	sizeof (struct fms_softc), fms_match, fms_attach
100214501Srpaulo};
101214501Srpaulo
102214501Srpaulostruct audio_hw_if fms_hw_if = {
103214501Srpaulo	fms_open,
104214501Srpaulo	fms_close,
105214501Srpaulo	fms_set_params,
106214501Srpaulo	fms_round_blocksize,
107214501Srpaulo	NULL,
108214501Srpaulo	NULL,
109214501Srpaulo	NULL,
110214501Srpaulo	NULL,
111214501Srpaulo	NULL,
112214501Srpaulo	fms_halt_output,
113214501Srpaulo	fms_halt_input,
114214501Srpaulo	NULL,
115214501Srpaulo	NULL,
116214501Srpaulo	fms_set_port,
117214501Srpaulo	fms_get_port,
118214501Srpaulo	fms_query_devinfo,
119214501Srpaulo	fms_malloc,
120214501Srpaulo	fms_free,
121214501Srpaulo	NULL,
122214501Srpaulo	fms_get_props,
123214501Srpaulo	fms_trigger_output,
124214501Srpaulo	fms_trigger_input
125214501Srpaulo};
126214501Srpaulo
127214501Srpauloint	fms_attach_codec(void *, struct ac97_codec_if *);
128214501Srpauloint	fms_read_codec(void *, u_int8_t, u_int16_t *);
129214501Srpauloint	fms_write_codec(void *, u_int8_t, u_int16_t);
130214501Srpaulovoid	fms_reset_codec(void *);
131214501Srpaulo
132214501Srpauloint	fms_allocmem(struct fms_softc *, size_t, size_t,
133214501Srpaulo			  struct fms_dma *);
134214501Srpauloint	fms_freemem(struct fms_softc *, struct fms_dma *);
135214501Srpaulo
136214501Srpauloint
137214501Srpaulofms_match(struct device *parent, void *match, void *aux)
138214501Srpaulo{
139214501Srpaulo	struct pci_attach_args *pa = (struct pci_attach_args *) aux;
140214501Srpaulo
141214501Srpaulo	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_FORTEMEDIA &&
142214501Srpaulo	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_FORTEMEDIA_FM801)
143214501Srpaulo		return (1);
144214501Srpaulo	return (0);
145214501Srpaulo}
146214501Srpaulo
147214501Srpaulovoid
148214501Srpaulofms_attach(struct device *parent, struct device *self, void *aux)
149214501Srpaulo{
150214501Srpaulo	struct pci_attach_args *pa = aux;
151214501Srpaulo	struct fms_softc *sc = (struct fms_softc *) self;
152214501Srpaulo	struct audio_attach_args aa;
153214501Srpaulo	pci_chipset_tag_t pc = pa->pa_pc;
154214501Srpaulo	pcitag_t pt = pa->pa_tag;
155214501Srpaulo	pci_intr_handle_t ih;
156214501Srpaulo	bus_size_t iosize;
157214501Srpaulo	const char *intrstr;
158214501Srpaulo	u_int16_t k1;
159214501Srpaulo	int i;
160214501Srpaulo
161214501Srpaulo	if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
162214501Srpaulo	    &sc->sc_ioh, NULL, &iosize, 0)) {
163214501Srpaulo		printf(": can't map i/o space\n");
164214501Srpaulo		return;
165214501Srpaulo	}
166214501Srpaulo
167214501Srpaulo	if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x30, 2,
168214501Srpaulo	    &sc->sc_mpu_ioh)) {
169214501Srpaulo		printf(": can't get mpu subregion handle\n");
170214501Srpaulo		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
171214501Srpaulo		return;
172214501Srpaulo	}
173214501Srpaulo
174214501Srpaulo	if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x68, 4,
175214501Srpaulo	    &sc->sc_opl_ioh)) {
176214501Srpaulo		printf(": can't get opl subregion handle\n");
177214501Srpaulo		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
178214501Srpaulo		return;
179214501Srpaulo	}
180214501Srpaulo
181214501Srpaulo	if (pci_intr_map(pa, &ih)) {
182214501Srpaulo		printf(": couldn't map interrupt\n");
183214501Srpaulo		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
184214501Srpaulo		return;
185214501Srpaulo	}
186214501Srpaulo	intrstr = pci_intr_string(pc, ih);
187214501Srpaulo
188214501Srpaulo	sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO | IPL_MPSAFE,
189214501Srpaulo	    fms_intr, sc, sc->sc_dev.dv_xname);
190214501Srpaulo	if (sc->sc_ih == NULL) {
191214501Srpaulo		printf(": couldn't establish interrupt");
192214501Srpaulo		if (intrstr != NULL)
193214501Srpaulo			printf(" at %s", intrstr);
194214501Srpaulo		printf("\n");
195214501Srpaulo		bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
196214501Srpaulo		return;
197214501Srpaulo	}
198214501Srpaulo
199214501Srpaulo	printf(": %s\n", intrstr);
200214501Srpaulo
201214501Srpaulo	sc->sc_dmat = pa->pa_dmat;
202214501Srpaulo
203214501Srpaulo	/* Disable legacy audio (SBPro compatibility) */
204214501Srpaulo	pci_conf_write(pc, pt, 0x40, 0);
205214501Srpaulo
206214501Srpaulo	/* Reset codec and AC'97 */
207214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020);
208214501Srpaulo	delay(2);		/* > 1us according to AC'97 documentation */
209214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000);
210214501Srpaulo	delay(1);		/* > 168.2ns according to AC'97 documentation */
211214501Srpaulo
212214501Srpaulo	/* Set up volume */
213214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PCM_VOLUME, 0x0808);
214214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_FM_VOLUME, 0x0808);
215214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_I2S_VOLUME, 0x0808);
216214501Srpaulo
217214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_RECORD_SOURCE, 0x0000);
218214501Srpaulo
219214501Srpaulo	/* Unmask playback, record and mpu interrupts, mask the rest */
220214501Srpaulo	k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK);
221214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK,
222214501Srpaulo	    (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
223214501Srpaulo	     FM_INTMASK_VOL);
224214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS,
225214501Srpaulo	    FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
226214501Srpaulo	    FM_INTSTATUS_VOL);
227214501Srpaulo
228214501Srpaulo#if NRADIO > 0
229214501Srpaulo	fmsradio_attach(sc);
230214501Srpaulo#endif /* NRADIO > 0 */
231214501Srpaulo
232214501Srpaulo	sc->host_if.arg = sc;
233214501Srpaulo	sc->host_if.attach = fms_attach_codec;
234214501Srpaulo	sc->host_if.read = fms_read_codec;
235214501Srpaulo	sc->host_if.write = fms_write_codec;
236214501Srpaulo	sc->host_if.reset = fms_reset_codec;
237214501Srpaulo
238214501Srpaulo	if (ac97_attach(&sc->host_if) != 0)
239214501Srpaulo		return;
240214501Srpaulo
241214501Srpaulo	/* Turn mute off */
242214501Srpaulo	for (i = 0; i < 3; i++) {
243214501Srpaulo		static struct {
244214501Srpaulo			char *class, *device;
245214501Srpaulo		} d[] = {
246214501Srpaulo			{ AudioCoutputs, AudioNmaster },
247214501Srpaulo			{ AudioCinputs, AudioNdac },
248214501Srpaulo			{ AudioCrecord, AudioNvolume }
249214501Srpaulo		};
250214501Srpaulo		struct mixer_ctrl ctl;
251214501Srpaulo
252214501Srpaulo		ctl.type = AUDIO_MIXER_ENUM;
253214501Srpaulo		ctl.un.ord = 0;
254214501Srpaulo		ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
255214501Srpaulo			d[i].class, d[i].device, AudioNmute);
256214501Srpaulo		fms_set_port(sc, &ctl);
257214501Srpaulo	}
258214501Srpaulo
259214501Srpaulo	audio_attach_mi(&fms_hw_if, sc, &sc->sc_dev);
260214501Srpaulo
261214501Srpaulo	aa.type = AUDIODEV_TYPE_OPL;
262214501Srpaulo	aa.hwif = NULL;
263214501Srpaulo	aa.hdl = NULL;
264214501Srpaulo	config_found(&sc->sc_dev, &aa, audioprint);
265214501Srpaulo
266214501Srpaulo	aa.type = AUDIODEV_TYPE_MPU;
267214501Srpaulo	aa.hwif = NULL;
268214501Srpaulo	aa.hdl = NULL;
269214501Srpaulo	sc->sc_mpu_dev = config_found(&sc->sc_dev, &aa, audioprint);
270214501Srpaulo}
271214501Srpaulo
272214501Srpaulo/*
273214501Srpaulo * Each AC-link frame takes 20.8us, data should be ready in next frame,
274214501Srpaulo * we allow more than two.
275214501Srpaulo */
276214501Srpaulo#define TIMO 50
277214501Srpauloint
278214501Srpaulofms_read_codec(void *addr, u_int8_t reg, u_int16_t *val)
279214501Srpaulo{
280214501Srpaulo	struct fms_softc *sc = addr;
281214501Srpaulo	int i;
282214501Srpaulo
283214501Srpaulo	/* Poll until codec is ready */
284214501Srpaulo	for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh,
285214501Srpaulo		 FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++)
286214501Srpaulo		delay(1);
287214501Srpaulo	if (i >= TIMO) {
288214501Srpaulo		printf("fms: codec busy\n");
289214501Srpaulo		return 1;
290214501Srpaulo	}
291214501Srpaulo
292214501Srpaulo	/* Write register index, read access */
293214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD,
294214501Srpaulo			  reg | FM_CODEC_CMD_READ);
295214501Srpaulo
296214501Srpaulo	/* Poll until we have valid data */
297214501Srpaulo	for (i = 0; i < TIMO && !(bus_space_read_2(sc->sc_iot, sc->sc_ioh,
298214501Srpaulo		 FM_CODEC_CMD) & FM_CODEC_CMD_VALID); i++)
299214501Srpaulo		delay(1);
300214501Srpaulo	if (i >= TIMO) {
301214501Srpaulo		printf("fms: no data from codec\n");
302214501Srpaulo		return 1;
303214501Srpaulo	}
304214501Srpaulo
305214501Srpaulo	/* Read data */
306214501Srpaulo	*val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA);
307214501Srpaulo	return 0;
308214501Srpaulo}
309214501Srpaulo
310214501Srpauloint
311214501Srpaulofms_write_codec(void *addr, u_int8_t reg, u_int16_t val)
312214501Srpaulo{
313214501Srpaulo	struct fms_softc *sc = addr;
314214501Srpaulo	int i;
315214501Srpaulo
316214501Srpaulo	/* Poll until codec is ready */
317214501Srpaulo	for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh,
318214501Srpaulo		 FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++)
319214501Srpaulo		delay(1);
320214501Srpaulo	if (i >= TIMO) {
321214501Srpaulo		printf("fms: codec busy\n");
322214501Srpaulo		return 1;
323214501Srpaulo	}
324214501Srpaulo
325214501Srpaulo	/* Write data */
326214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA, val);
327214501Srpaulo	/* Write index register, write access */
328214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, reg);
329214501Srpaulo	return 0;
330214501Srpaulo}
331214501Srpaulo#undef TIMO
332214501Srpaulo
333214501Srpauloint
334214501Srpaulofms_attach_codec(void *addr, struct ac97_codec_if *cif)
335214501Srpaulo{
336214501Srpaulo	struct fms_softc *sc = addr;
337214501Srpaulo
338214501Srpaulo	sc->codec_if = cif;
339214501Srpaulo	return 0;
340214501Srpaulo}
341214501Srpaulo
342214501Srpaulo/* Cold Reset */
343214501Srpaulovoid
344214501Srpaulofms_reset_codec(void *addr)
345214501Srpaulo{
346214501Srpaulo	struct fms_softc *sc = addr;
347214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020);
348214501Srpaulo	delay(2);
349214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000);
350214501Srpaulo	delay(1);
351214501Srpaulo}
352214501Srpaulo
353214501Srpauloint
354214501Srpaulofms_intr(void *arg)
355214501Srpaulo{
356214501Srpaulo	struct fms_softc *sc = arg;
357214501Srpaulo	u_int16_t istat;
358214501Srpaulo
359214501Srpaulo	mtx_enter(&audio_lock);
360214501Srpaulo	istat = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS);
361214501Srpaulo
362214501Srpaulo	if (istat & FM_INTSTATUS_PLAY) {
363214501Srpaulo		if ((sc->sc_play_nextblk += sc->sc_play_blksize) >=
364214501Srpaulo		     sc->sc_play_end)
365214501Srpaulo			sc->sc_play_nextblk = sc->sc_play_start;
366214501Srpaulo
367214501Srpaulo		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
368214501Srpaulo		    sc->sc_play_flip++ & 1 ?
369214501Srpaulo		    FM_PLAY_DMABUF2 : FM_PLAY_DMABUF1, sc->sc_play_nextblk);
370214501Srpaulo
371214501Srpaulo		if (sc->sc_pintr)
372214501Srpaulo			sc->sc_pintr(sc->sc_parg);
373214501Srpaulo		else
374214501Srpaulo			printf("unexpected play intr\n");
375214501Srpaulo	}
376214501Srpaulo
377214501Srpaulo	if (istat & FM_INTSTATUS_REC) {
378214501Srpaulo		if ((sc->sc_rec_nextblk += sc->sc_rec_blksize) >=
379214501Srpaulo		     sc->sc_rec_end)
380214501Srpaulo			sc->sc_rec_nextblk = sc->sc_rec_start;
381214501Srpaulo
382214501Srpaulo		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
383214501Srpaulo		    sc->sc_rec_flip++ & 1 ?
384214501Srpaulo		    FM_REC_DMABUF2 : FM_REC_DMABUF1, sc->sc_rec_nextblk);
385214501Srpaulo
386214501Srpaulo		if (sc->sc_rintr)
387214501Srpaulo			sc->sc_rintr(sc->sc_rarg);
388214501Srpaulo		else
389214501Srpaulo			printf("unexpected rec intr\n");
390214501Srpaulo	}
391214501Srpaulo
392214501Srpaulo#if 0
393214501Srpaulo	if (istat & FM_INTSTATUS_MPU)
394214501Srpaulo		mpu_intr(sc->sc_mpu_dev);
395214501Srpaulo#endif
396214501Srpaulo
397214501Srpaulo	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS,
398			  istat & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC));
399	mtx_leave(&audio_lock);
400	return 1;
401}
402
403int
404fms_open(void *addr, int flags)
405{
406	/* UNUSED struct fms_softc *sc = addr;*/
407
408	return 0;
409}
410
411void
412fms_close(void *addr)
413{
414	/* UNUSED struct fms_softc *sc = addr;*/
415}
416
417/*
418 * Range below -limit- is set to -rate-
419 * What a pity FM801 does not have 24000
420 * 24000 -> 22050 sounds rather poor
421 */
422struct {
423	int limit;
424	int rate;
425} fms_rates[11] = {
426	{  6600,  5500 },
427	{  8750,  8000 },
428	{ 10250,  9600 },
429	{ 13200, 11025 },
430	{ 17500, 16000 },
431	{ 20500, 19200 },
432	{ 26500, 22050 },
433	{ 35000, 32000 },
434	{ 41000, 38400 },
435	{ 46000, 44100 },
436	{ 48000, 48000 },
437	/* anything above -> 48000 */
438};
439
440int
441fms_set_params(void *addr, int setmode, int usemode, struct audio_params *play,
442    struct audio_params *rec)
443{
444	struct fms_softc *sc = addr;
445	int i;
446
447	if (setmode & AUMODE_PLAY) {
448		switch(play->encoding) {
449		case AUDIO_ENCODING_SLINEAR_LE:
450			if (play->precision != 16)
451				return EINVAL;
452			break;
453		case AUDIO_ENCODING_ULINEAR_LE:
454		case AUDIO_ENCODING_ULINEAR_BE:
455			if (play->precision != 8)
456				return EINVAL;
457			break;
458		default:
459			return EINVAL;
460		}
461		play->bps = AUDIO_BPS(play->precision);
462		play->msb = 1;
463
464		for (i = 0; i < 10 && play->sample_rate > fms_rates[i].limit;
465		     i++)
466			;
467		play->sample_rate = fms_rates[i].rate;
468		sc->sc_play_reg = (play->channels == 2 ? FM_PLAY_STEREO : 0) |
469		    (play->precision == 16 ? FM_PLAY_16BIT : 0) |
470		    (i << 8);
471	}
472
473	if (setmode & AUMODE_RECORD) {
474
475		switch(rec->encoding) {
476		case AUDIO_ENCODING_SLINEAR_LE:
477			if (rec->precision != 16)
478				return EINVAL;
479			break;
480		case AUDIO_ENCODING_ULINEAR_LE:
481		case AUDIO_ENCODING_ULINEAR_BE:
482			if (rec->precision != 8)
483				return EINVAL;
484			break;
485		default:
486			return EINVAL;
487		}
488		rec->bps = AUDIO_BPS(rec->precision);
489		rec->msb = 1;
490
491		for (i = 0; i < 10 && rec->sample_rate > fms_rates[i].limit;
492		     i++)
493			;
494		rec->sample_rate = fms_rates[i].rate;
495		sc->sc_rec_reg =
496		    (rec->channels == 2 ? FM_REC_STEREO : 0) |
497		    (rec->precision == 16 ? FM_REC_16BIT : 0) |
498		    (i << 8);
499	}
500
501	return 0;
502}
503
504int
505fms_round_blocksize(void *addr, int blk)
506{
507	return (blk + 0xf) & ~0xf;
508}
509
510int
511fms_halt_output(void *addr)
512{
513	struct fms_softc *sc = addr;
514	u_int16_t k1;
515
516	mtx_enter(&audio_lock);
517	k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL);
518	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL,
519			  (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
520			  FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST);
521	mtx_leave(&audio_lock);
522	return 0;
523}
524
525int
526fms_halt_input(void *addr)
527{
528	struct fms_softc *sc = addr;
529	u_int16_t k1;
530
531	mtx_enter(&audio_lock);
532	k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL);
533	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL,
534			  (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
535			  FM_REC_BUF1_LAST | FM_REC_BUF2_LAST);
536	mtx_leave(&audio_lock);
537	return 0;
538}
539
540int
541fms_set_port(void *addr, mixer_ctrl_t *cp)
542{
543	struct fms_softc *sc = addr;
544
545	return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
546}
547
548int
549fms_get_port(void *addr, mixer_ctrl_t *cp)
550{
551	struct fms_softc *sc = addr;
552
553	return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
554}
555
556void *
557fms_malloc(void *addr, int direction, size_t size, int pool, int flags)
558{
559	struct fms_softc *sc = addr;
560	struct fms_dma *p;
561	int error;
562	int rseg;
563
564	p = malloc(sizeof(*p), pool, flags);
565	if (!p)
566		return 0;
567
568	p->size = size;
569	if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &p->seg, 1,
570				      &rseg, BUS_DMA_NOWAIT)) != 0) {
571		printf("%s: unable to allocate dma, error = %d\n",
572		       sc->sc_dev.dv_xname, error);
573		goto fail_alloc;
574	}
575
576	if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
577				    BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
578		printf("%s: unable to map dma, error = %d\n",
579		       sc->sc_dev.dv_xname, error);
580		goto fail_map;
581	}
582
583	if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
584				       BUS_DMA_NOWAIT, &p->map)) != 0) {
585		printf("%s: unable to create dma map, error = %d\n",
586		       sc->sc_dev.dv_xname, error);
587		goto fail_create;
588	}
589
590	if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
591				     BUS_DMA_NOWAIT)) != 0) {
592		printf("%s: unable to load dma map, error = %d\n",
593		       sc->sc_dev.dv_xname, error);
594		goto fail_load;
595	}
596
597	p->next = sc->sc_dmas;
598	sc->sc_dmas = p;
599
600	return p->addr;
601
602
603fail_load:
604	bus_dmamap_destroy(sc->sc_dmat, p->map);
605fail_create:
606	bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
607fail_map:
608	bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
609fail_alloc:
610	free(p, pool, 0);
611	return 0;
612}
613
614void
615fms_free(void *addr, void *ptr, int pool)
616{
617	struct fms_softc *sc = addr;
618	struct fms_dma **pp, *p;
619
620	for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
621		if (p->addr == ptr) {
622			bus_dmamap_unload(sc->sc_dmat, p->map);
623			bus_dmamap_destroy(sc->sc_dmat, p->map);
624			bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
625			bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
626
627			*pp = p->next;
628			free(p, pool, 0);
629			return;
630		}
631
632	panic("fms_free: trying to free unallocated memory");
633}
634
635int
636fms_get_props(void *addr)
637{
638	return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
639	       AUDIO_PROP_FULLDUPLEX;
640}
641
642int
643fms_query_devinfo(void *addr, mixer_devinfo_t *dip)
644{
645	struct fms_softc *sc = addr;
646
647	return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
648}
649
650int
651fms_trigger_output(void *addr, void *start, void *end, int blksize,
652    void (*intr)(void *), void *arg, struct audio_params *param)
653{
654	struct fms_softc *sc = addr;
655	struct fms_dma *p;
656
657	sc->sc_pintr = intr;
658	sc->sc_parg = arg;
659
660	for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
661		;
662
663	if (!p)
664		panic("fms_trigger_output: request with bad start "
665		      "address (%p)", start);
666
667	sc->sc_play_start = p->map->dm_segs[0].ds_addr;
668	sc->sc_play_end = sc->sc_play_start + ((char *)end - (char *)start);
669	sc->sc_play_blksize = blksize;
670	sc->sc_play_nextblk = sc->sc_play_start + sc->sc_play_blksize;
671	sc->sc_play_flip = 0;
672	mtx_enter(&audio_lock);
673	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMALEN, blksize - 1);
674	bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF1,
675			  sc->sc_play_start);
676	bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF2,
677			  sc->sc_play_nextblk);
678	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL,
679			  FM_PLAY_START | FM_PLAY_STOPNOW | sc->sc_play_reg);
680	mtx_leave(&audio_lock);
681	return 0;
682}
683
684
685int
686fms_trigger_input(void *addr, void *start, void *end, int blksize,
687    void (*intr)(void *), void *arg, struct audio_params *param)
688{
689	struct fms_softc *sc = addr;
690	struct fms_dma *p;
691
692	sc->sc_rintr = intr;
693	sc->sc_rarg = arg;
694
695	for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
696		;
697
698	if (!p)
699		panic("fms_trigger_input: request with bad start "
700		      "address (%p)", start);
701
702	sc->sc_rec_start = p->map->dm_segs[0].ds_addr;
703	sc->sc_rec_end = sc->sc_rec_start + ((char *)end - (char *)start);
704	sc->sc_rec_blksize = blksize;
705	sc->sc_rec_nextblk = sc->sc_rec_start + sc->sc_rec_blksize;
706	sc->sc_rec_flip = 0;
707	mtx_enter(&audio_lock);
708	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_DMALEN, blksize - 1);
709	bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF1,
710			  sc->sc_rec_start);
711	bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF2,
712			  sc->sc_rec_nextblk);
713	bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL,
714			  FM_REC_START | FM_REC_STOPNOW | sc->sc_rec_reg);
715	mtx_leave(&audio_lock);
716	return 0;
717}
718