xref: /freebsd/sys/dev/sound/pci/emu10kx-midi.c (revision 923e0040a59af8ac2271d3e673fef1c641d0cce7)
1d056fa04SAlexander Leidinger /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4d056fa04SAlexander Leidinger  * Copyright (c) 1999 Seigo Tanimura
5498a9afeSJoel Dahl  * Copyright (c) 2003 Mathew Kanner
6d056fa04SAlexander Leidinger  * Copyright (c) 2003-2006 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru>
7d056fa04SAlexander Leidinger  * All rights reserved
8d056fa04SAlexander Leidinger  *
9d056fa04SAlexander Leidinger  * Redistribution and use in source and binary forms, with or without
10d056fa04SAlexander Leidinger  * modification, are permitted provided that the following conditions
11d056fa04SAlexander Leidinger  * are met:
12d056fa04SAlexander Leidinger  * 1. Redistributions of source code must retain the above copyright
13d056fa04SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer.
14d056fa04SAlexander Leidinger  * 2. Redistributions in binary form must reproduce the above copyright
15d056fa04SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer in the
16d056fa04SAlexander Leidinger  *    documentation and/or other materials provided with the distribution.
17d056fa04SAlexander Leidinger  *
18d056fa04SAlexander Leidinger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19d056fa04SAlexander Leidinger  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20d056fa04SAlexander Leidinger  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21d056fa04SAlexander Leidinger  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22d056fa04SAlexander Leidinger  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23d056fa04SAlexander Leidinger  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24d056fa04SAlexander Leidinger  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25d056fa04SAlexander Leidinger  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26d056fa04SAlexander Leidinger  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27d056fa04SAlexander Leidinger  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28d056fa04SAlexander Leidinger  * SUCH DAMAGE.
29d056fa04SAlexander Leidinger  */
30d056fa04SAlexander Leidinger 
31d056fa04SAlexander Leidinger #include <sys/param.h>
32d056fa04SAlexander Leidinger #include <sys/types.h>
33d056fa04SAlexander Leidinger #include <sys/bus.h>
34d056fa04SAlexander Leidinger #include <machine/bus.h>
35d056fa04SAlexander Leidinger #include <sys/rman.h>
36d056fa04SAlexander Leidinger #include <sys/systm.h>
37d056fa04SAlexander Leidinger #include <sys/sbuf.h>
38d056fa04SAlexander Leidinger #include <sys/queue.h>
39d056fa04SAlexander Leidinger #include <sys/lock.h>
40d056fa04SAlexander Leidinger #include <sys/mutex.h>
41d056fa04SAlexander Leidinger 
4290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
4390da2b28SAriff Abdullah #include "opt_snd.h"
4490da2b28SAriff Abdullah #endif
4590da2b28SAriff Abdullah 
46d056fa04SAlexander Leidinger #include <dev/sound/pcm/sound.h>
47d056fa04SAlexander Leidinger 
48d056fa04SAlexander Leidinger #include <dev/sound/midi/midi.h>
49d056fa04SAlexander Leidinger #include <dev/sound/midi/mpu401.h>
50d056fa04SAlexander Leidinger #include "mpufoi_if.h"
51d056fa04SAlexander Leidinger 
5270e0bbedSPedro F. Giffuni #include <dev/sound/pci/emuxkireg.h>
53d056fa04SAlexander Leidinger #include <dev/sound/pci/emu10kx.h>
54d056fa04SAlexander Leidinger 
55d056fa04SAlexander Leidinger struct emu_midi_softc {
56d056fa04SAlexander Leidinger 	struct mtx	mtx;
57d056fa04SAlexander Leidinger 	device_t	dev;
58d056fa04SAlexander Leidinger 	struct mpu401	*mpu;
59d056fa04SAlexander Leidinger 	mpu401_intr_t	*mpu_intr;
60d056fa04SAlexander Leidinger 	struct emu_sc_info *card;
61d056fa04SAlexander Leidinger 	int		port;			/* I/O port or I/O ptr reg */
62d056fa04SAlexander Leidinger 	int		is_emu10k1;
63d056fa04SAlexander Leidinger 	int		fflags;			/* File flags */
64d056fa04SAlexander Leidinger 	int		ihandle;		/* interrupt manager handle */
65d056fa04SAlexander Leidinger };
66d056fa04SAlexander Leidinger 
67d056fa04SAlexander Leidinger static uint32_t	emu_midi_card_intr(void *p, uint32_t arg);
68d056fa04SAlexander Leidinger 
69d056fa04SAlexander Leidinger static unsigned char
7090da2b28SAriff Abdullah emu_mread(struct mpu401 *arg __unused, void *cookie, int reg)
71d056fa04SAlexander Leidinger {
7290da2b28SAriff Abdullah 	struct emu_midi_softc *sc = cookie;
73d056fa04SAlexander Leidinger 	unsigned int d;
74d056fa04SAlexander Leidinger 
75d056fa04SAlexander Leidinger 	d = 0;
76d056fa04SAlexander Leidinger 	if (sc->is_emu10k1)
77d056fa04SAlexander Leidinger 		d = emu_rd(sc->card, 0x18 + reg, 1);
78d056fa04SAlexander Leidinger 	else
79d056fa04SAlexander Leidinger 		d = emu_rdptr(sc->card, 0, sc->port + reg);
80d056fa04SAlexander Leidinger 
81d056fa04SAlexander Leidinger 	return (d);
82d056fa04SAlexander Leidinger }
83d056fa04SAlexander Leidinger 
84d056fa04SAlexander Leidinger static void
8590da2b28SAriff Abdullah emu_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b)
86d056fa04SAlexander Leidinger {
8790da2b28SAriff Abdullah 	struct emu_midi_softc *sc = cookie;
88d056fa04SAlexander Leidinger 
89d056fa04SAlexander Leidinger 	if (sc->is_emu10k1)
90d056fa04SAlexander Leidinger 		emu_wr(sc->card, 0x18 + reg, b, 1);
91d056fa04SAlexander Leidinger 	else
92d056fa04SAlexander Leidinger 		emu_wrptr(sc->card, 0, sc->port + reg, b);
93d056fa04SAlexander Leidinger }
94d056fa04SAlexander Leidinger 
95d056fa04SAlexander Leidinger static int
9690da2b28SAriff Abdullah emu_muninit(struct mpu401 *arg __unused, void *cookie)
97d056fa04SAlexander Leidinger {
9890da2b28SAriff Abdullah 	struct emu_midi_softc *sc = cookie;
99d056fa04SAlexander Leidinger 
100d056fa04SAlexander Leidinger 	mtx_lock(&sc->mtx);
101d056fa04SAlexander Leidinger 	sc->mpu_intr = NULL;
102d056fa04SAlexander Leidinger 	mtx_unlock(&sc->mtx);
103d056fa04SAlexander Leidinger 
104d056fa04SAlexander Leidinger 	return (0);
105d056fa04SAlexander Leidinger }
106d056fa04SAlexander Leidinger 
107d056fa04SAlexander Leidinger static kobj_method_t emu_mpu_methods[] = {
108d056fa04SAlexander Leidinger 	KOBJMETHOD(mpufoi_read, emu_mread),
109d056fa04SAlexander Leidinger 	KOBJMETHOD(mpufoi_write, emu_mwrite),
110d056fa04SAlexander Leidinger 	KOBJMETHOD(mpufoi_uninit, emu_muninit),
11190da2b28SAriff Abdullah 	KOBJMETHOD_END
112d056fa04SAlexander Leidinger };
11375d7240eSAlexander Leidinger static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
114d056fa04SAlexander Leidinger 
115d056fa04SAlexander Leidinger static uint32_t
116d056fa04SAlexander Leidinger emu_midi_card_intr(void *p, uint32_t intr_status)
117d056fa04SAlexander Leidinger {
118d056fa04SAlexander Leidinger 	struct emu_midi_softc *sc = (struct emu_midi_softc *)p;
119d056fa04SAlexander Leidinger 	if (sc->mpu_intr)
120d056fa04SAlexander Leidinger 		(sc->mpu_intr) (sc->mpu);
121d056fa04SAlexander Leidinger 	if (sc->mpu_intr == NULL) {
122d056fa04SAlexander Leidinger 		/* We should read MIDI event to unlock card after
123d056fa04SAlexander Leidinger 		 * interrupt. XXX - check, why this happens.  */
124b28624fdSAriff Abdullah 		if (bootverbose)
125d056fa04SAlexander Leidinger 			device_printf(sc->dev, "midi interrupt %08x without interrupt handler, force mread!\n", intr_status);
126d056fa04SAlexander Leidinger 		(void)emu_mread((void *)(NULL), sc, 0);
127d056fa04SAlexander Leidinger 	}
128d056fa04SAlexander Leidinger 	return (intr_status); /* Acknowledge everything */
129d056fa04SAlexander Leidinger }
130d056fa04SAlexander Leidinger 
131d056fa04SAlexander Leidinger static void
132d056fa04SAlexander Leidinger emu_midi_intr(void *p)
133d056fa04SAlexander Leidinger {
134d056fa04SAlexander Leidinger 	(void)emu_midi_card_intr(p, 0);
135d056fa04SAlexander Leidinger }
136d056fa04SAlexander Leidinger 
137d056fa04SAlexander Leidinger static int
138d056fa04SAlexander Leidinger emu_midi_probe(device_t dev)
139d056fa04SAlexander Leidinger {
140d056fa04SAlexander Leidinger 	struct emu_midi_softc *scp;
1417387abd3SWarner Losh 	uintptr_t func, is_emu10k1;
142d056fa04SAlexander Leidinger 
1437387abd3SWarner Losh 	BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
144d056fa04SAlexander Leidinger 	if (func != SCF_MIDI)
145d056fa04SAlexander Leidinger 		return (ENXIO);
146d056fa04SAlexander Leidinger 
147d056fa04SAlexander Leidinger 	scp = device_get_softc(dev);
148d056fa04SAlexander Leidinger 	bzero(scp, sizeof(*scp));
1497387abd3SWarner Losh 	BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &is_emu10k1);
150ca23ff19SAlexander Leidinger 	scp->is_emu10k1 = is_emu10k1 ? 1 : 0;
151d056fa04SAlexander Leidinger 
152d056fa04SAlexander Leidinger 	device_set_desc(dev, "EMU10Kx MIDI Interface");
153d056fa04SAlexander Leidinger 	return (0);
154d056fa04SAlexander Leidinger }
155d056fa04SAlexander Leidinger 
156d056fa04SAlexander Leidinger static int
157d056fa04SAlexander Leidinger emu_midi_attach(device_t dev)
158d056fa04SAlexander Leidinger {
159d056fa04SAlexander Leidinger 	struct emu_midi_softc * scp;
160d056fa04SAlexander Leidinger 	struct sndcard_func *func;
161d056fa04SAlexander Leidinger 	struct emu_midiinfo *midiinfo;
162d056fa04SAlexander Leidinger 	uint32_t inte_val, ipr_val;
163d056fa04SAlexander Leidinger 
164d056fa04SAlexander Leidinger 	scp = device_get_softc(dev);
165d056fa04SAlexander Leidinger 	func = device_get_ivars(dev);
166d056fa04SAlexander Leidinger 
167d056fa04SAlexander Leidinger 	scp->dev = dev;
168d056fa04SAlexander Leidinger 	midiinfo = (struct emu_midiinfo *)func->varinfo;
169d056fa04SAlexander Leidinger 	scp->port = midiinfo->port;
170d056fa04SAlexander Leidinger 	scp->card = midiinfo->card;
171d056fa04SAlexander Leidinger 
172b28624fdSAriff Abdullah 	mtx_init(&scp->mtx, device_get_nameunit(dev), "midi softc", MTX_DEF);
173d056fa04SAlexander Leidinger 
174d056fa04SAlexander Leidinger 	if (scp->is_emu10k1) {
175d056fa04SAlexander Leidinger 		/* SB Live! - only one MIDI device here */
176d056fa04SAlexander Leidinger 		inte_val = 0;
17770e0bbedSPedro F. Giffuni 		/* inte_val |= EMU_INTE_MIDITXENABLE;*/
17870e0bbedSPedro F. Giffuni 		inte_val |= EMU_INTE_MIDIRXENABLE;
17970e0bbedSPedro F. Giffuni 		ipr_val = EMU_IPR_MIDITRANSBUFE;
18070e0bbedSPedro F. Giffuni 		ipr_val |= EMU_IPR_MIDIRECVBUFE;
181d056fa04SAlexander Leidinger 	} else {
18270e0bbedSPedro F. Giffuni 		if (scp->port == EMU_A_MUDATA1) {
183d056fa04SAlexander Leidinger 			/* EXTERNAL MIDI (AudigyDrive) */
184d056fa04SAlexander Leidinger 			inte_val = 0;
18570e0bbedSPedro F. Giffuni 			/* inte_val |= A_EMU_INTE_MIDITXENABLE1;*/
18670e0bbedSPedro F. Giffuni 			inte_val |= EMU_INTE_MIDIRXENABLE;
18770e0bbedSPedro F. Giffuni 			ipr_val = EMU_IPR_MIDITRANSBUFE;
18870e0bbedSPedro F. Giffuni 			ipr_val |= EMU_IPR_MIDIRECVBUFE;
189d056fa04SAlexander Leidinger 		} else {
190d056fa04SAlexander Leidinger 			/* MIDI hw config port 2 */
191d056fa04SAlexander Leidinger 			inte_val = 0;
19270e0bbedSPedro F. Giffuni 			/* inte_val |= A_EMU_INTE_MIDITXENABLE2;*/
19370e0bbedSPedro F. Giffuni 			inte_val |= EMU_INTE_A_MIDIRXENABLE2;
19470e0bbedSPedro F. Giffuni 			ipr_val = EMU_IPR_A_MIDITRANSBUFE2;
19570e0bbedSPedro F. Giffuni 			ipr_val |= EMU_IPR_A_MIDIRECBUFE2;
196d056fa04SAlexander Leidinger 		}
197d056fa04SAlexander Leidinger 	}
198d056fa04SAlexander Leidinger 
199d056fa04SAlexander Leidinger 	scp->ihandle = emu_intr_register(scp->card, inte_val, ipr_val, &emu_midi_card_intr, scp);
200d056fa04SAlexander Leidinger 	/* Init the interface. */
201d056fa04SAlexander Leidinger 	scp->mpu = mpu401_init(&emu_mpu_class, scp, emu_midi_intr, &scp->mpu_intr);
202d056fa04SAlexander Leidinger 	if (scp->mpu == NULL) {
203d056fa04SAlexander Leidinger 		emu_intr_unregister(scp->card, scp->ihandle);
204d056fa04SAlexander Leidinger 		mtx_destroy(&scp->mtx);
205d056fa04SAlexander Leidinger 		return (ENOMEM);
206d056fa04SAlexander Leidinger 	}
207d056fa04SAlexander Leidinger 	/*
208d056fa04SAlexander Leidinger 	 * XXX I don't know how to check for Live!Drive / AudigyDrive
209d056fa04SAlexander Leidinger 	 * presence. Let's hope that IR enabling code will not harm if
210d056fa04SAlexander Leidinger 	 * it is not present.
211d056fa04SAlexander Leidinger 	 */
212d056fa04SAlexander Leidinger 	if (scp->is_emu10k1)
213d056fa04SAlexander Leidinger 		emu_enable_ir(scp->card);
214d056fa04SAlexander Leidinger 	else {
21570e0bbedSPedro F. Giffuni 		if (scp->port == EMU_A_MUDATA1)
216d056fa04SAlexander Leidinger 			emu_enable_ir(scp->card);
217d056fa04SAlexander Leidinger 	}
218d056fa04SAlexander Leidinger 
219d056fa04SAlexander Leidinger 	return (0);
220d056fa04SAlexander Leidinger }
221d056fa04SAlexander Leidinger 
222d056fa04SAlexander Leidinger static int
223d056fa04SAlexander Leidinger emu_midi_detach(device_t dev)
224d056fa04SAlexander Leidinger {
225d056fa04SAlexander Leidinger 	struct emu_midi_softc *scp;
226d056fa04SAlexander Leidinger 
227d056fa04SAlexander Leidinger 	scp = device_get_softc(dev);
228d056fa04SAlexander Leidinger 	mpu401_uninit(scp->mpu);
229d056fa04SAlexander Leidinger 	emu_intr_unregister(scp->card, scp->ihandle);
230d056fa04SAlexander Leidinger 	mtx_destroy(&scp->mtx);
231d056fa04SAlexander Leidinger 	return (0);
232d056fa04SAlexander Leidinger }
233d056fa04SAlexander Leidinger 
234d056fa04SAlexander Leidinger static device_method_t emu_midi_methods[] = {
235d056fa04SAlexander Leidinger 	DEVMETHOD(device_probe, emu_midi_probe),
236d056fa04SAlexander Leidinger 	DEVMETHOD(device_attach, emu_midi_attach),
237d056fa04SAlexander Leidinger 	DEVMETHOD(device_detach, emu_midi_detach),
238d056fa04SAlexander Leidinger 
23961bfd867SSofian Brabez 	DEVMETHOD_END
240d056fa04SAlexander Leidinger };
241d056fa04SAlexander Leidinger 
242d056fa04SAlexander Leidinger static driver_t emu_midi_driver = {
243d056fa04SAlexander Leidinger 	"midi",
244d056fa04SAlexander Leidinger 	emu_midi_methods,
245d056fa04SAlexander Leidinger 	sizeof(struct emu_midi_softc),
246d056fa04SAlexander Leidinger };
2473390adfeSJohn Baldwin DRIVER_MODULE(snd_emu10kx_midi, emu10kx, emu_midi_driver, 0, 0);
248d056fa04SAlexander Leidinger MODULE_DEPEND(snd_emu10kx_midi, snd_emu10kx, SND_EMU10KX_MINVER, SND_EMU10KX_PREFVER, SND_EMU10KX_MAXVER);
249d056fa04SAlexander Leidinger MODULE_DEPEND(snd_emu10kx_midi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
250d056fa04SAlexander Leidinger MODULE_VERSION(snd_emu10kx_midi, SND_EMU10KX_PREFVER);
251