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 85 static u_int32_t 86 csamidi_readio(struct csa_midi_softc *scp, u_long offset) 87 { 88 if (offset < BA0_AC97_RESET) 89 return bus_space_read_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset) & 0xffffffff; 90 else 91 return (0); 92 } 93 94 static void 95 csamidi_writeio(struct csa_midi_softc *scp, u_long offset, u_int32_t data) 96 { 97 if (offset < BA0_AC97_RESET) 98 bus_space_write_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset, data); 99 } 100 101 static void 102 csamidi_midi_intr(void *arg) 103 { 104 struct csa_midi_softc *scp = (struct csa_midi_softc *)arg; 105 106 if (scp->mpu_intr) 107 (scp->mpu_intr)(scp->mpu); 108 } 109 110 static unsigned char 111 csamidi_mread(struct mpu401 *arg __unused, void *cookie, int reg) 112 { 113 struct csa_midi_softc *scp = cookie; 114 unsigned int rc; 115 unsigned int uart_stat; 116 117 rc = 0; 118 /* hacks to convert hardware status to MPU compatible ones */ 119 switch (reg) { 120 case MPU_STATPORT: 121 uart_stat = csamidi_readio(scp, BA0_MIDSR); 122 if (uart_stat & MIDSR_TBF) 123 rc |= MPU_OUTPUTBUSY; /* Tx buffer full */ 124 if (uart_stat & MIDSR_RBE) 125 rc |= MPU_INPUTBUSY; 126 break; 127 case MPU_DATAPORT: 128 rc = csamidi_readio(scp, BA0_MIDRP); 129 break; 130 default: 131 printf("csamidi_mread: unknown register %d\n", reg); 132 break; 133 } 134 return (rc); 135 } 136 137 static void 138 csamidi_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b) 139 { 140 struct csa_midi_softc *scp = cookie; 141 unsigned int val; 142 143 switch (reg) { 144 case MPU_CMDPORT: 145 switch (b) 146 { 147 case MPU_RESET: 148 /* preserve current operation mode */ 149 val = csamidi_readio(scp, BA0_MIDCR); 150 /* reset the MIDI port */ 151 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MRST); 152 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MLB); 153 csamidi_writeio(scp, BA0_MIDCR, 0x00); 154 /* restore previous operation mode */ 155 csamidi_writeio(scp, BA0_MIDCR, val); 156 break; 157 case MPU_UART: 158 /* switch to UART mode, no-op */ 159 default: 160 break; 161 } 162 break; 163 case MPU_DATAPORT: 164 /* put the MIDI databyte in the write port */ 165 csamidi_writeio(scp, BA0_MIDWP, b); 166 break; 167 default: 168 printf("csamidi_mwrite: unknown register %d\n", reg); 169 break; 170 } 171 } 172 173 static int 174 csamidi_muninit(struct mpu401 *arg __unused, void *cookie) 175 { 176 struct csa_midi_softc *scp = cookie; 177 178 mtx_lock(&scp->mtx); 179 scp->mpu_intr = NULL; 180 mtx_unlock(&scp->mtx); 181 182 return (0); 183 } 184 185 static int 186 midicsa_probe(device_t dev) 187 { 188 struct sndcard_func *func; 189 190 /* The parent device has already been probed. */ 191 192 func = device_get_ivars(dev); 193 if (func == NULL || func->func != SCF_MIDI) 194 return (ENXIO); 195 196 device_set_desc(dev, "CS461x MIDI"); 197 return (0); 198 } 199 200 static int 201 midicsa_attach(device_t dev) 202 { 203 struct csa_midi_softc *scp; 204 int rc = ENXIO; 205 206 scp = device_get_softc(dev); 207 208 bzero(scp, sizeof(struct csa_midi_softc)); 209 scp->dev = dev; 210 211 /* allocate the required resources */ 212 scp->io_rid = PCIR_BAR(0); 213 scp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 214 &scp->io_rid, RF_ACTIVE); 215 if (scp->io == NULL) 216 goto err0; 217 218 /* init the fake MPU401 interface. */ 219 scp->mpu = mpu401_init(&csamidi_mpu_class, scp, csamidi_midi_intr, 220 &scp->mpu_intr); 221 if (scp->mpu == NULL) { 222 rc = ENOMEM; 223 goto err1; 224 } 225 226 mtx_init(&scp->mtx, device_get_nameunit(dev), "csamidi softc", 227 MTX_DEF); 228 229 /* reset the MIDI port */ 230 csamidi_writeio(scp, BA0_MIDCR, MIDCR_MRST); 231 /* MIDI transmit enable, no interrupt */ 232 csamidi_writeio(scp, BA0_MIDCR, MIDCR_TXE | MIDCR_RXE); 233 csamidi_writeio(scp, BA0_HICR, HICR_IEV | HICR_CHGM); 234 235 return (0); 236 err1: 237 bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, scp->io); 238 scp->io = NULL; 239 err0: 240 return (rc); 241 } 242 243 static int 244 midicsa_detach(device_t dev) 245 { 246 struct csa_midi_softc *scp; 247 int rc = 0; 248 249 scp = device_get_softc(dev); 250 rc = mpu401_uninit(scp->mpu); 251 if (rc) 252 return (rc); 253 if (scp->io != NULL) { 254 bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, 255 scp->io); 256 scp->io = NULL; 257 } 258 mtx_destroy(&scp->mtx); 259 return (rc); 260 } 261 262 static kobj_method_t csamidi_mpu_methods[] = { 263 KOBJMETHOD(mpufoi_read, csamidi_mread), 264 KOBJMETHOD(mpufoi_write, csamidi_mwrite), 265 KOBJMETHOD(mpufoi_uninit, csamidi_muninit), 266 KOBJMETHOD_END 267 }; 268 269 static DEFINE_CLASS(csamidi_mpu, csamidi_mpu_methods, 0); 270 271 static device_method_t midicsa_methods[] = { 272 DEVMETHOD(device_probe, midicsa_probe), 273 DEVMETHOD(device_attach, midicsa_attach), 274 DEVMETHOD(device_detach, midicsa_detach), 275 276 DEVMETHOD_END 277 }; 278 279 static driver_t midicsa_driver = { 280 "midi", 281 midicsa_methods, 282 sizeof(struct csa_midi_softc), 283 }; 284 DRIVER_MODULE(snd_csa_midi, csa, midicsa_driver, 0, 0); 285 MODULE_DEPEND(snd_csa_midi, snd_csa, 1, 1, 1); 286 MODULE_DEPEND(snd_csa_midi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 287 MODULE_VERSION(snd_csa_midi, 1); 288