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