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