1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 4 * Copyright (c) 2015-2018 Tai-hwa Liang <avatar@FreeBSD.org> 5 * All rights reserved 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/types.h> 31 #include <sys/bus.h> 32 #include <machine/bus.h> 33 #include <sys/rman.h> 34 #include <sys/systm.h> 35 #include <sys/kobj.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 39 #ifdef HAVE_KERNEL_OPTION_HEADERS 40 #include "opt_snd.h" 41 #endif 42 43 #include <dev/pci/pcireg.h> 44 #include <dev/pci/pcivar.h> 45 46 #include <dev/sound/chip.h> 47 #include <dev/sound/pcm/sound.h> 48 49 #include <dev/sound/midi/midi.h> 50 #include <dev/sound/midi/mpu401.h> 51 52 #include <dev/sound/pci/csareg.h> 53 #include <dev/sound/pci/csavar.h> 54 55 #include "mpufoi_if.h" 56 57 SND_DECLARE_FILE("$FreeBSD$"); 58 59 /* pulled from mpu401.c */ 60 #define MPU_DATAPORT 0 61 #define MPU_CMDPORT 1 62 #define MPU_STATPORT 1 63 #define MPU_RESET 0xff 64 #define MPU_UART 0x3f 65 #define MPU_ACK 0xfe 66 #define MPU_STATMASK 0xc0 67 #define MPU_OUTPUTBUSY 0x40 68 #define MPU_INPUTBUSY 0x80 69 70 /* device private data */ 71 struct csa_midi_softc { 72 /* hardware resources */ 73 int io_rid; /* io rid */ 74 struct resource *io; /* io */ 75 76 struct mtx mtx; 77 device_t dev; 78 struct mpu401 *mpu; 79 mpu401_intr_t *mpu_intr; 80 int mflags; /* MIDI flags */ 81 }; 82 83 static struct kobj_class csamidi_mpu_class; 84 static devclass_t midicsa_devclass; 85 86 static u_int32_t 87 csamidi_readio(struct csa_midi_softc *scp, u_long offset) 88 { 89 if (offset < BA0_AC97_RESET) 90 return bus_space_read_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset) & 0xffffffff; 91 else 92 return (0); 93 } 94 95 static void 96 csamidi_writeio(struct csa_midi_softc *scp, u_long offset, u_int32_t data) 97 { 98 if (offset < BA0_AC97_RESET) 99 bus_space_write_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset, data); 100 } 101 102 static void 103 csamidi_midi_intr(void *arg) 104 { 105 struct csa_midi_softc *scp = (struct csa_midi_softc *)arg; 106 107 if (scp->mpu_intr) 108 (scp->mpu_intr)(scp->mpu); 109 } 110 111 static unsigned char 112 csamidi_mread(struct mpu401 *arg __unused, void *cookie, int reg) 113 { 114 struct csa_midi_softc *scp = cookie; 115 unsigned int rc; 116 unsigned int uart_stat; 117 118 rc = 0; 119 /* hacks to convert hardware status to MPU compatible ones */ 120 switch (reg) { 121 case MPU_STATPORT: 122 uart_stat = csamidi_readio(scp, BA0_MIDSR); 123 if (uart_stat & MIDSR_TBF) 124 rc |= MPU_OUTPUTBUSY; /* Tx buffer full */ 125 if (uart_stat & MIDSR_RBE) 126 rc |= MPU_INPUTBUSY; 127 break; 128 case MPU_DATAPORT: 129 rc = csamidi_readio(scp, BA0_MIDRP); 130 break; 131 default: 132 printf("csamidi_mread: unknown register %d\n", reg); 133 break; 134 } 135 return (rc); 136 } 137 138 static void 139 csamidi_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b) 140 { 141 struct csa_midi_softc *scp = cookie; 142 unsigned int val; 143 144 switch (reg) { 145 case MPU_CMDPORT: 146 switch (b) 147 { 148 case MPU_RESET: 149 /* preserve current operation mode */ 150 val = csamidi_readio(scp, BA0_MIDCR); 151 /* reset the MIDI port */ 152 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MRST); 153 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MLB); 154 csamidi_writeio(scp, BA0_MIDCR, 0x00); 155 /* restore previous operation mode */ 156 csamidi_writeio(scp, BA0_MIDCR, val); 157 break; 158 case MPU_UART: 159 /* switch to UART mode, no-op */ 160 default: 161 break; 162 } 163 break; 164 case MPU_DATAPORT: 165 /* put the MIDI databyte in the write port */ 166 csamidi_writeio(scp, BA0_MIDWP, b); 167 break; 168 default: 169 printf("csamidi_mwrite: unknown register %d\n", reg); 170 break; 171 } 172 } 173 174 static int 175 csamidi_muninit(struct mpu401 *arg __unused, void *cookie) 176 { 177 struct csa_midi_softc *scp = cookie; 178 179 mtx_lock(&scp->mtx); 180 scp->mpu_intr = NULL; 181 mtx_unlock(&scp->mtx); 182 183 return (0); 184 } 185 186 static int 187 midicsa_probe(device_t dev) 188 { 189 struct sndcard_func *func; 190 191 /* The parent device has already been probed. */ 192 193 func = device_get_ivars(dev); 194 if (func == NULL || func->func != SCF_MIDI) 195 return (ENXIO); 196 197 device_set_desc(dev, "CS461x MIDI"); 198 return (0); 199 } 200 201 static int 202 midicsa_attach(device_t dev) 203 { 204 struct csa_midi_softc *scp; 205 int rc = ENXIO; 206 207 scp = device_get_softc(dev); 208 209 bzero(scp, sizeof(struct csa_midi_softc)); 210 scp->dev = dev; 211 212 /* allocate the required resources */ 213 scp->io_rid = PCIR_BAR(0); 214 scp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 215 &scp->io_rid, RF_ACTIVE); 216 if (scp->io == NULL) 217 goto err0; 218 219 /* init the fake MPU401 interface. */ 220 scp->mpu = mpu401_init(&csamidi_mpu_class, scp, csamidi_midi_intr, 221 &scp->mpu_intr); 222 if (scp->mpu == NULL) { 223 rc = ENOMEM; 224 goto err1; 225 } 226 227 mtx_init(&scp->mtx, device_get_nameunit(dev), "csamidi softc", 228 MTX_DEF); 229 230 /* reset the MIDI port */ 231 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MRST); 232 /* MIDI transmit enable, no interrupt */ 233 csamidi_writeio(scp, BA0_MIDCR, MIDCR_TXE | MIDCR_RXE); 234 csamidi_writeio(scp, BA0_HICR, HICR_IEV | HICR_CHGM); 235 236 return (0); 237 err1: 238 bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, scp->io); 239 scp->io = NULL; 240 err0: 241 return (rc); 242 } 243 244 static int 245 midicsa_detach(device_t dev) 246 { 247 struct csa_midi_softc *scp; 248 int rc = 0; 249 250 scp = device_get_softc(dev); 251 rc = mpu401_uninit(scp->mpu); 252 if (rc) 253 return (rc); 254 if (scp->io != NULL) { 255 bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, 256 scp->io); 257 scp->io = NULL; 258 } 259 mtx_destroy(&scp->mtx); 260 return (rc); 261 } 262 263 static kobj_method_t csamidi_mpu_methods[] = { 264 KOBJMETHOD(mpufoi_read, csamidi_mread), 265 KOBJMETHOD(mpufoi_write, csamidi_mwrite), 266 KOBJMETHOD(mpufoi_uninit, csamidi_muninit), 267 KOBJMETHOD_END 268 }; 269 270 static DEFINE_CLASS(csamidi_mpu, csamidi_mpu_methods, 0); 271 272 static device_method_t midicsa_methods[] = { 273 DEVMETHOD(device_probe, midicsa_probe), 274 DEVMETHOD(device_attach, midicsa_attach), 275 DEVMETHOD(device_detach, midicsa_detach), 276 277 DEVMETHOD_END 278 }; 279 280 static driver_t midicsa_driver = { 281 "midi", 282 midicsa_methods, 283 sizeof(struct csa_midi_softc), 284 }; 285 DRIVER_MODULE(snd_csa_midi, csa, midicsa_driver, midicsa_devclass, 0, 0); 286 MODULE_DEPEND(snd_csa_midi, snd_csa, 1, 1, 1); 287 MODULE_DEPEND(snd_csa_midi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 288 MODULE_VERSION(snd_csa_midi, 1); 289