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