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