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