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