1 1.23 isaki /* $NetBSD: cms.c,v 1.23 2019/05/08 13:40:18 isaki Exp $ */ 2 1.1 augustss 3 1.1 augustss /* 4 1.1 augustss * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 1.1 augustss * All rights reserved. 6 1.1 augustss * 7 1.1 augustss * Redistribution and use in source and binary forms, with or without 8 1.1 augustss * modification, are permitted provided that the following conditions 9 1.1 augustss * are met: 10 1.1 augustss * 1. Redistributions of source code must retain the above copyright 11 1.1 augustss * notice, this list of conditions and the following disclaimer. 12 1.1 augustss * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 augustss * notice, this list of conditions and the following disclaimer in the 14 1.1 augustss * documentation and/or other materials provided with the distribution. 15 1.1 augustss * 16 1.1 augustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 augustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 augustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 augustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 augustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 augustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 augustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 augustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 augustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 augustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 augustss * POSSIBILITY OF SUCH DAMAGE. 27 1.1 augustss */ 28 1.3 lukem 29 1.3 lukem #include <sys/cdefs.h> 30 1.23 isaki __KERNEL_RCSID(0, "$NetBSD: cms.c,v 1.23 2019/05/08 13:40:18 isaki Exp $"); 31 1.1 augustss 32 1.1 augustss #include <sys/param.h> 33 1.1 augustss #include <sys/systm.h> 34 1.1 augustss #include <sys/kernel.h> 35 1.1 augustss #include <sys/device.h> 36 1.1 augustss #include <sys/select.h> 37 1.1 augustss 38 1.15 ad #include <sys/bus.h> 39 1.1 augustss 40 1.1 augustss #include <sys/audioio.h> 41 1.23 isaki #include <dev/audio/audio_if.h> 42 1.1 augustss 43 1.1 augustss #include <sys/midiio.h> 44 1.1 augustss #include <dev/midi_if.h> 45 1.1 augustss #include <dev/midivar.h> 46 1.1 augustss #include <dev/midisynvar.h> 47 1.1 augustss 48 1.1 augustss #include <dev/isa/isareg.h> 49 1.1 augustss #include <dev/isa/isavar.h> 50 1.1 augustss #include <dev/isa/cmsreg.h> 51 1.1 augustss 52 1.1 augustss #ifdef AUDIO_DEBUG 53 1.1 augustss #define DPRINTF(x) if (cmsdebug) printf x 54 1.1 augustss int cmsdebug = 0; 55 1.1 augustss #else 56 1.1 augustss #define DPRINTF(x) 57 1.1 augustss #endif 58 1.1 augustss 59 1.1 augustss struct cms_softc { 60 1.20 jakllsch kmutex_t sc_lock; 61 1.20 jakllsch 62 1.1 augustss bus_space_tag_t sc_iot; 63 1.1 augustss bus_space_handle_t sc_ioh; 64 1.1 augustss 65 1.1 augustss /* shadow registers for each chip */ 66 1.1 augustss u_int8_t sc_shadowregs[32*2]; 67 1.1 augustss midisyn sc_midisyn; 68 1.1 augustss }; 69 1.1 augustss 70 1.16 cube int cms_probe(device_t, cfdata_t, void *); 71 1.16 cube void cms_attach(device_t, device_t, void *); 72 1.1 augustss 73 1.16 cube CFATTACH_DECL_NEW(cms, sizeof(struct cms_softc), 74 1.7 thorpej cms_probe, cms_attach, NULL, NULL); 75 1.1 augustss 76 1.9 perry int cms_open(midisyn *, int); 77 1.9 perry void cms_close(midisyn *); 78 1.12 chap void cms_on(midisyn *, uint_fast16_t, midipitch_t, int16_t); 79 1.12 chap void cms_off(midisyn *, uint_fast16_t, uint_fast8_t); 80 1.1 augustss 81 1.1 augustss struct midisyn_methods midi_cms_hw = { 82 1.12 chap .open = cms_open, 83 1.12 chap .close = cms_close, 84 1.12 chap .attackv = cms_on, 85 1.12 chap .releasev = cms_off, 86 1.1 augustss }; 87 1.1 augustss 88 1.9 perry static void cms_reset(struct cms_softc *); 89 1.1 augustss 90 1.1 augustss static char cms_note_table[] = { 91 1.1 augustss /* A */ 3, 92 1.1 augustss /* A# */ 31, 93 1.1 augustss /* B */ 58, 94 1.1 augustss /* C */ 83, 95 1.1 augustss /* C# */ 107, 96 1.1 augustss /* D */ 130, 97 1.1 augustss /* D# */ 151, 98 1.1 augustss /* E */ 172, 99 1.1 augustss /* F */ 191, 100 1.1 augustss /* F# */ 209, 101 1.1 augustss /* G */ 226, 102 1.1 augustss /* G# */ 242, 103 1.1 augustss }; 104 1.1 augustss 105 1.1 augustss #define NOTE_TO_OCTAVE(note) (((note)-CMS_FIRST_NOTE)/12) 106 1.1 augustss #define NOTE_TO_COUNT(note) cms_note_table[(((note)-CMS_FIRST_NOTE)%12)] 107 1.1 augustss 108 1.1 augustss int 109 1.16 cube cms_probe(device_t parent, cfdata_t match, void *aux) 110 1.1 augustss { 111 1.1 augustss struct isa_attach_args *ia = aux; 112 1.1 augustss bus_space_tag_t iot; 113 1.1 augustss bus_space_handle_t ioh; 114 1.1 augustss int found = 0; 115 1.1 augustss int i; 116 1.1 augustss 117 1.1 augustss DPRINTF(("cms_probe():\n")); 118 1.1 augustss 119 1.1 augustss iot = ia->ia_iot; 120 1.1 augustss 121 1.4 thorpej if (ia->ia_nio < 1) 122 1.1 augustss return 0; 123 1.1 augustss 124 1.4 thorpej if (ISA_DIRECT_CONFIG(ia)) 125 1.4 thorpej return 0; 126 1.4 thorpej 127 1.8 drochner if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 128 1.4 thorpej return 0; 129 1.4 thorpej 130 1.4 thorpej if (bus_space_map(iot, ia->ia_io[0].ir_addr, CMS_IOSIZE, 0, &ioh)) 131 1.1 augustss return 0; 132 1.1 augustss 133 1.1 augustss bus_space_write_1(iot, ioh, CMS_WREG, 0xaa); 134 1.1 augustss if (bus_space_read_1(iot, ioh, CMS_RREG) != 0xaa) 135 1.1 augustss goto out; 136 1.1 augustss 137 1.1 augustss for (i = 0; i < 8; i++) { 138 1.1 augustss if (bus_space_read_1(iot, ioh, CMS_MREG) != 0x7f) 139 1.1 augustss goto out; 140 1.1 augustss } 141 1.1 augustss found = 1; 142 1.1 augustss 143 1.4 thorpej ia->ia_nio = 1; 144 1.4 thorpej ia->ia_io[0].ir_size = CMS_IOSIZE; 145 1.4 thorpej 146 1.4 thorpej ia->ia_niomem = 0; 147 1.4 thorpej ia->ia_nirq = 0; 148 1.4 thorpej ia->ia_ndrq = 0; 149 1.1 augustss 150 1.1 augustss out: 151 1.1 augustss bus_space_unmap(iot, ioh, CMS_IOSIZE); 152 1.1 augustss 153 1.1 augustss return found; 154 1.1 augustss } 155 1.1 augustss 156 1.1 augustss 157 1.1 augustss void 158 1.16 cube cms_attach(device_t parent, device_t self, void *aux) 159 1.1 augustss { 160 1.16 cube struct cms_softc *sc = device_private(self); 161 1.1 augustss struct isa_attach_args *ia = aux; 162 1.1 augustss bus_space_tag_t iot; 163 1.1 augustss bus_space_handle_t ioh; 164 1.1 augustss midisyn *ms; 165 1.1 augustss 166 1.20 jakllsch mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_AUDIO); 167 1.16 cube 168 1.16 cube aprint_normal("\n"); 169 1.1 augustss 170 1.1 augustss DPRINTF(("cms_attach():\n")); 171 1.1 augustss 172 1.1 augustss iot = ia->ia_iot; 173 1.1 augustss 174 1.4 thorpej if (bus_space_map(iot, ia->ia_io[0].ir_addr, CMS_IOSIZE, 0, &ioh)) { 175 1.16 cube aprint_error_dev(self, "can't map i/o space\n"); 176 1.1 augustss return; 177 1.1 augustss } 178 1.1 augustss 179 1.1 augustss sc->sc_iot = iot; 180 1.1 augustss sc->sc_ioh = ioh; 181 1.1 augustss 182 1.1 augustss /* now let's reset the chips */ 183 1.1 augustss cms_reset(sc); 184 1.1 augustss 185 1.21 plunky /* init the synthesiser */ 186 1.1 augustss ms = &sc->sc_midisyn; 187 1.1 augustss ms->mets = &midi_cms_hw; 188 1.1 augustss strcpy(ms->name, "Creative Music System"); 189 1.1 augustss ms->nvoice = CMS_NVOICES; 190 1.1 augustss ms->data = sc; 191 1.19 jmcneill ms->lock = &sc->sc_lock; 192 1.21 plunky midisyn_init(ms); 193 1.1 augustss 194 1.21 plunky /* and attach the midi device with the synthesiser */ 195 1.21 plunky midi_attach_mi(&midisyn_hw_if, ms, self); 196 1.1 augustss } 197 1.1 augustss 198 1.1 augustss 199 1.1 augustss int 200 1.14 christos cms_open(midisyn *ms, int flag) 201 1.1 augustss { 202 1.1 augustss struct cms_softc *sc = (struct cms_softc *)ms->data; 203 1.1 augustss 204 1.1 augustss cms_reset(sc); 205 1.1 augustss 206 1.1 augustss return 0; 207 1.1 augustss } 208 1.1 augustss 209 1.1 augustss void 210 1.16 cube cms_close(midisyn *ms) 211 1.1 augustss { 212 1.1 augustss struct cms_softc *sc = (struct cms_softc *)ms->data; 213 1.1 augustss 214 1.1 augustss cms_reset(sc); 215 1.1 augustss } 216 1.1 augustss 217 1.1 augustss void 218 1.12 chap cms_on(midisyn *ms, uint_fast16_t vidx, midipitch_t mp, int16_t level_cB) 219 1.1 augustss { 220 1.1 augustss struct cms_softc *sc = (struct cms_softc *)ms->data; 221 1.12 chap int chip = CHAN_TO_CHIP(vidx); 222 1.12 chap int voice = CHAN_TO_VOICE(vidx); 223 1.12 chap uint32_t note; 224 1.1 augustss u_int8_t octave; 225 1.1 augustss u_int8_t count; 226 1.1 augustss u_int8_t reg; 227 1.1 augustss u_int8_t vol; 228 1.12 chap 229 1.12 chap /* 230 1.12 chap * The next line is a regrettable hack, because it drops all pitch 231 1.12 chap * adjustment midisyn has supplied in miditune, so this synth will 232 1.12 chap * not respond to tuning, pitchbend, etc. It seems it ought to be 233 1.12 chap * possible to DTRT if the formula that generated the cms_note_table 234 1.12 chap * can be found, but I've had no luck, and a plot of the numbers in 235 1.12 chap * the table clearly curves the wrong way to be derived any obvious 236 1.12 chap * way from the equal tempered scale. (Or maybe the table's wrong? 237 1.12 chap * Has this device been playing flat up the scale? I don't have 238 1.12 chap * access to one to try.) 239 1.12 chap */ 240 1.12 chap note = MIDIPITCH_TO_KEY(mp); 241 1.1 augustss 242 1.1 augustss if (note < CMS_FIRST_NOTE) 243 1.1 augustss return; 244 1.1 augustss 245 1.1 augustss octave = NOTE_TO_OCTAVE(note); 246 1.1 augustss count = NOTE_TO_COUNT(note); 247 1.1 augustss 248 1.1 augustss DPRINTF(("chip=%d voice=%d octave=%d count=%d offset=%d shift=%d\n", 249 1.1 augustss chip, voice, octave, count, OCTAVE_OFFSET(voice), 250 1.1 augustss OCTAVE_SHIFT(voice))); 251 1.1 augustss 252 1.1 augustss /* write the count */ 253 1.1 augustss CMS_WRITE(sc, chip, CMS_IREG_FREQ0 + voice, count); 254 1.1 augustss 255 1.1 augustss /* select the octave */ 256 1.1 augustss reg = CMS_READ(sc, chip, CMS_IREG_OCTAVE_1_0 + OCTAVE_OFFSET(voice)); 257 1.1 augustss reg &= ~(0x0f<<OCTAVE_SHIFT(voice)); 258 1.1 augustss reg |= ((octave&0x7)<<OCTAVE_SHIFT(voice)); 259 1.1 augustss CMS_WRITE(sc, chip, CMS_IREG_OCTAVE_1_0 + OCTAVE_OFFSET(voice), reg); 260 1.1 augustss 261 1.1 augustss /* set the volume */ 262 1.12 chap /* this may be the wrong curve but will do something. no docs! */ 263 1.22 maya vol = 15 + ((level_cB > -75) ? level_cB/5 : -15); 264 1.1 augustss CMS_WRITE(sc, chip, CMS_IREG_VOL0 + voice, ((vol<<4)|vol)); 265 1.1 augustss 266 1.1 augustss /* enable the voice */ 267 1.1 augustss reg = CMS_READ(sc, chip, CMS_IREG_FREQ_CTL); 268 1.1 augustss reg |= (1<<voice); 269 1.10 perry CMS_WRITE(sc, chip, CMS_IREG_FREQ_CTL, reg); 270 1.1 augustss } 271 1.1 augustss 272 1.1 augustss void 273 1.14 christos cms_off(midisyn *ms, uint_fast16_t vidx, uint_fast8_t vel) 274 1.1 augustss { 275 1.1 augustss struct cms_softc *sc = (struct cms_softc *)ms->data; 276 1.12 chap int chip = CHAN_TO_CHIP(vidx); 277 1.12 chap int voice = CHAN_TO_VOICE(vidx); 278 1.1 augustss u_int8_t reg; 279 1.1 augustss 280 1.1 augustss /* disable the channel */ 281 1.1 augustss reg = CMS_READ(sc, chip, CMS_IREG_FREQ_CTL); 282 1.1 augustss reg &= ~(1<<voice); 283 1.10 perry CMS_WRITE(sc, chip, CMS_IREG_FREQ_CTL, reg); 284 1.1 augustss } 285 1.1 augustss 286 1.1 augustss static void 287 1.16 cube cms_reset(struct cms_softc *sc) 288 1.1 augustss { 289 1.1 augustss int i; 290 1.1 augustss 291 1.1 augustss DPRINTF(("cms_reset():\n")); 292 1.1 augustss 293 1.1 augustss for (i = 0; i < 6; i++) { 294 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_VOL0+i, 0x00); 295 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_VOL0+i, 0x00); 296 1.1 augustss 297 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_FREQ0+i, 0x00); 298 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_FREQ0+i, 0x00); 299 1.1 augustss } 300 1.1 augustss 301 1.1 augustss for (i = 0; i < 3; i++) { 302 1.10 perry CMS_WRITE(sc, 0, CMS_IREG_OCTAVE_1_0+i, 0x00); 303 1.10 perry CMS_WRITE(sc, 1, CMS_IREG_OCTAVE_1_0+i, 0x00); 304 1.1 augustss } 305 1.1 augustss 306 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_FREQ_CTL, 0x00); 307 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_FREQ_CTL, 0x00); 308 1.1 augustss 309 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_NOISE_CTL, 0x00); 310 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_NOISE_CTL, 0x00); 311 1.1 augustss 312 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_NOISE_BW, 0x00); 313 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_NOISE_BW, 0x00); 314 1.1 augustss 315 1.1 augustss /* 316 1.1 augustss * These registers don't appear to be useful, but must be 317 1.1 augustss * cleared otherwise certain voices don't work properly 318 1.1 augustss */ 319 1.1 augustss CMS_WRITE(sc, 0, 0x18, 0x00); 320 1.1 augustss CMS_WRITE(sc, 1, 0x18, 0x00); 321 1.1 augustss CMS_WRITE(sc, 0, 0x19, 0x00); 322 1.1 augustss CMS_WRITE(sc, 1, 0x19, 0x00); 323 1.1 augustss 324 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_SYS_CTL, CMS_IREG_SYS_RESET); 325 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_SYS_CTL, CMS_IREG_SYS_RESET); 326 1.1 augustss 327 1.1 augustss CMS_WRITE(sc, 0, CMS_IREG_SYS_CTL, CMS_IREG_SYS_ENBL); 328 1.1 augustss CMS_WRITE(sc, 1, CMS_IREG_SYS_CTL, CMS_IREG_SYS_ENBL); 329 1.1 augustss } 330