1 /*        $NetBSD: wss.c,v 1.77 2021/08/07 16:19:12 thorpej Exp $     */
2 
3 /*
4  * Copyright (c) 1994 John Brezak
5  * Copyright (c) 1991-1993 Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *        This product includes software developed by the Computer Systems
19  *        Engineering Group at Lawrence Berkeley Laboratory.
20  * 4. Neither the name of the University nor of the Laboratory may be used
21  *    to endorse or promote products derived from this software without
22  *    specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: wss.c,v 1.77 2021/08/07 16:19:12 thorpej Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/errno.h>
45 #include <sys/cpu.h>
46 #include <sys/intr.h>
47 #include <sys/bus.h>
48 #include <sys/audioio.h>
49 
50 #include <dev/audio/audio_if.h>
51 
52 #include <dev/isa/isavar.h>
53 #include <dev/isa/isadmavar.h>
54 
55 #include <dev/ic/ad1848reg.h>
56 #include <dev/ic/cs4231reg.h>
57 #include <dev/isa/ad1848var.h>
58 #include <dev/isa/cs4231var.h>
59 #include <dev/isa/wssreg.h>
60 #include <dev/isa/wssvar.h>
61 #include <dev/isa/madreg.h>
62 
63 #ifdef AUDIO_DEBUG
64 #define DPRINTF(x)  if (wssdebug) printf x
65 int       wssdebug = 0;
66 #else
67 #define DPRINTF(x)
68 #endif
69 
70 struct audio_device wss_device = {
71           "wss,ad1848",
72           "",
73           "WSS"
74 };
75 
76 int       wss_intr(void *);
77 int       wss_getdev(void *, struct audio_device *);
78 
79 int       wss_mixer_set_port(void *, mixer_ctrl_t *);
80 int       wss_mixer_get_port(void *, mixer_ctrl_t *);
81 int       wss_query_devinfo(void *, mixer_devinfo_t *);
82 
83 /*
84  * Define our interface to the higher level audio driver.
85  */
86 
87 const struct audio_hw_if wss_hw_if = {
88           .open                         = ad1848_isa_open,
89           .close                        = ad1848_isa_close,
90           .query_format                 = ad1848_query_format,
91           .set_format                   = ad1848_set_format,
92           .commit_settings    = ad1848_commit_settings,
93           .halt_output                  = ad1848_isa_halt_output,
94           .halt_input                   = ad1848_isa_halt_input,
95           .getdev                       = wss_getdev,
96           .set_port           = wss_mixer_set_port,
97           .get_port           = wss_mixer_get_port,
98           .query_devinfo                = wss_query_devinfo,
99           .allocm                       = ad1848_isa_malloc,
100           .freem                        = ad1848_isa_free,
101           .round_buffersize   = ad1848_isa_round_buffersize,
102           .get_props                    = ad1848_isa_get_props,
103           .trigger_output               = ad1848_isa_trigger_output,
104           .trigger_input                = ad1848_isa_trigger_input,
105           .get_locks                    = ad1848_get_locks,
106 };
107 
108 /*
109  * Attach hardware to driver, attach hardware driver to audio
110  * pseudo-device driver .
111  */
112 void
wssattach(struct wss_softc * sc)113 wssattach(struct wss_softc *sc)
114 {
115           struct ad1848_softc *ac;
116 #if 0 /* loses on CS423X chips */
117           int version;
118 #endif
119 
120           ac = &sc->sc_ad1848.sc_ad1848;
121 
122           ad1848_init_locks(ac, IPL_AUDIO);
123 
124           madattach(sc);
125 
126           sc->sc_ad1848.sc_ih = isa_intr_establish(sc->wss_ic, sc->wss_irq,
127               IST_EDGE, IPL_AUDIO, wss_intr, &sc->sc_ad1848);
128 
129           ad1848_isa_attach(&sc->sc_ad1848);
130 
131 #if 0 /* loses on CS423X chips */
132           version = bus_space_read_1(sc->sc_iot, sc->sc_ioh, WSS_STATUS)
133               & WSS_VERSMASK;
134           printf(" (vers %d)", version);
135 #endif
136 
137           switch(sc->mad_chip_type) {
138           case MAD_82C928:
139                     printf(", 82C928");
140                     break;
141           case MAD_OTI601D:
142                     printf(", OTI-601D");
143                     break;
144           case MAD_82C929:
145                     printf(", 82C929");
146                     break;
147           case MAD_82C931:
148                     printf(", 82C931");
149                     break;
150           default:
151                     break;
152           }
153           printf("\n");
154 
155           ac->parent = sc;
156 
157           audio_attach_mi(&wss_hw_if, &sc->sc_ad1848, ac->sc_dev);
158 
159           if (sc->mad_chip_type != MAD_NONE) {
160                     struct audio_attach_args arg;
161                     arg.type = AUDIODEV_TYPE_OPL;
162                     arg.hwif = 0;
163                     arg.hdl = 0;
164                     (void)config_found(ac->sc_dev, &arg, audioprint,
165                         CFARGS(.iattr = "wss"));
166           }
167 }
168 
169 int
wss_intr(void * addr)170 wss_intr(void *addr)
171 {
172           struct ad1848_isa_softc *sc;
173           int handled;
174 
175           sc = addr;
176 
177           mutex_spin_enter(&sc->sc_ad1848.sc_intr_lock);
178 
179           handled = ad1848_isa_intr(sc);
180 
181           mutex_spin_exit(&sc->sc_ad1848.sc_intr_lock);
182 
183           return handled;
184 }
185 
186 int
wss_getdev(void * addr,struct audio_device * retp)187 wss_getdev(void *addr, struct audio_device *retp)
188 {
189 
190           *retp = wss_device;
191           return 0;
192 }
193 
194 static ad1848_devmap_t mappings[] = {
195           { WSS_MIC_IN_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
196           { WSS_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
197           { WSS_DAC_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
198           { WSS_MONITOR_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL },
199           { WSS_MIC_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
200           { WSS_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
201           { WSS_DAC_MUTE, AD1848_KIND_MUTE, AD1848_DAC_CHANNEL },
202           { WSS_MONITOR_MUTE, AD1848_KIND_MUTE, AD1848_MONO_CHANNEL },
203           { WSS_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
204           { WSS_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1}
205 };
206 
207 static int nummap = sizeof(mappings) / sizeof(mappings[0]);
208 
209 int
wss_mixer_set_port(void * addr,mixer_ctrl_t * cp)210 wss_mixer_set_port(void *addr, mixer_ctrl_t *cp)
211 {
212           struct ad1848_softc *ac;
213 
214           ac = addr;
215           return ad1848_mixer_set_port(ac, mappings, nummap, cp);
216 }
217 
218 int
wss_mixer_get_port(void * addr,mixer_ctrl_t * cp)219 wss_mixer_get_port(void *addr, mixer_ctrl_t *cp)
220 {
221           struct ad1848_softc *ac;
222 
223           ac = addr;
224           return ad1848_mixer_get_port(ac, mappings, nummap, cp);
225 }
226 
227 int
wss_query_devinfo(void * addr,mixer_devinfo_t * dip)228 wss_query_devinfo(void *addr, mixer_devinfo_t *dip)
229 {
230 
231           DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
232           switch(dip->index) {
233           case WSS_MIC_IN_LVL:          /* Microphone */
234                     dip->type = AUDIO_MIXER_VALUE;
235                     dip->mixer_class = WSS_INPUT_CLASS;
236                     dip->prev = AUDIO_MIXER_LAST;
237                     dip->next = WSS_MIC_IN_MUTE;
238                     strcpy(dip->label.name, AudioNmicrophone);
239                     dip->un.v.num_channels = 2;
240                     strcpy(dip->un.v.units.name, AudioNvolume);
241                     break;
242 
243           case WSS_LINE_IN_LVL:         /* line/CD */
244                     dip->type = AUDIO_MIXER_VALUE;
245                     dip->mixer_class = WSS_INPUT_CLASS;
246                     dip->prev = AUDIO_MIXER_LAST;
247                     dip->next = WSS_LINE_IN_MUTE;
248                     strcpy(dip->label.name, AudioNcd);
249                     dip->un.v.num_channels = 2;
250                     strcpy(dip->un.v.units.name, AudioNvolume);
251                     break;
252 
253           case WSS_DAC_LVL:             /*  dacout */
254                     dip->type = AUDIO_MIXER_VALUE;
255                     dip->mixer_class = WSS_INPUT_CLASS;
256                     dip->prev = AUDIO_MIXER_LAST;
257                     dip->next = WSS_DAC_MUTE;
258                     strcpy(dip->label.name, AudioNdac);
259                     dip->un.v.num_channels = 2;
260                     strcpy(dip->un.v.units.name, AudioNvolume);
261                     break;
262 
263           case WSS_REC_LVL:   /* record level */
264                     dip->type = AUDIO_MIXER_VALUE;
265                     dip->mixer_class = WSS_RECORD_CLASS;
266                     dip->prev = AUDIO_MIXER_LAST;
267                     dip->next = WSS_RECORD_SOURCE;
268                     strcpy(dip->label.name, AudioNrecord);
269                     dip->un.v.num_channels = 2;
270                     strcpy(dip->un.v.units.name, AudioNvolume);
271                     break;
272 
273           case WSS_MONITOR_LVL:         /* monitor level */
274                     dip->type = AUDIO_MIXER_VALUE;
275                     dip->mixer_class = WSS_MONITOR_CLASS;
276                     dip->prev = AUDIO_MIXER_LAST;
277                     dip->next = WSS_MONITOR_MUTE;
278                     strcpy(dip->label.name, AudioNmonitor);
279                     dip->un.v.num_channels = 1;
280                     strcpy(dip->un.v.units.name, AudioNvolume);
281                     break;
282 
283           case WSS_INPUT_CLASS:                             /* input class descriptor */
284                     dip->type = AUDIO_MIXER_CLASS;
285                     dip->mixer_class = WSS_INPUT_CLASS;
286                     dip->next = dip->prev = AUDIO_MIXER_LAST;
287                     strcpy(dip->label.name, AudioCinputs);
288                     break;
289 
290           case WSS_MONITOR_CLASS:                           /* monitor class descriptor */
291                     dip->type = AUDIO_MIXER_CLASS;
292                     dip->mixer_class = WSS_MONITOR_CLASS;
293                     dip->next = dip->prev = AUDIO_MIXER_LAST;
294                     strcpy(dip->label.name, AudioCmonitor);
295                     break;
296 
297           case WSS_RECORD_CLASS:                            /* record source class */
298                     dip->type = AUDIO_MIXER_CLASS;
299                     dip->mixer_class = WSS_RECORD_CLASS;
300                     dip->next = dip->prev = AUDIO_MIXER_LAST;
301                     strcpy(dip->label.name, AudioCrecord);
302                     break;
303 
304           case WSS_MIC_IN_MUTE:
305                     dip->mixer_class = WSS_INPUT_CLASS;
306                     dip->type = AUDIO_MIXER_ENUM;
307                     dip->prev = WSS_MIC_IN_LVL;
308                     dip->next = AUDIO_MIXER_LAST;
309                     goto mute;
310 
311           case WSS_LINE_IN_MUTE:
312                     dip->mixer_class = WSS_INPUT_CLASS;
313                     dip->type = AUDIO_MIXER_ENUM;
314                     dip->prev = WSS_LINE_IN_LVL;
315                     dip->next = AUDIO_MIXER_LAST;
316                     goto mute;
317 
318           case WSS_DAC_MUTE:
319                     dip->mixer_class = WSS_INPUT_CLASS;
320                     dip->type = AUDIO_MIXER_ENUM;
321                     dip->prev = WSS_DAC_LVL;
322                     dip->next = AUDIO_MIXER_LAST;
323                     goto mute;
324 
325           case WSS_MONITOR_MUTE:
326                     dip->mixer_class = WSS_MONITOR_CLASS;
327                     dip->type = AUDIO_MIXER_ENUM;
328                     dip->prev = WSS_MONITOR_LVL;
329                     dip->next = AUDIO_MIXER_LAST;
330           mute:
331                     strcpy(dip->label.name, AudioNmute);
332                     dip->un.e.num_mem = 2;
333                     strcpy(dip->un.e.member[0].label.name, AudioNoff);
334                     dip->un.e.member[0].ord = 0;
335                     strcpy(dip->un.e.member[1].label.name, AudioNon);
336                     dip->un.e.member[1].ord = 1;
337                     break;
338 
339           case WSS_RECORD_SOURCE:
340                     dip->mixer_class = WSS_RECORD_CLASS;
341                     dip->type = AUDIO_MIXER_ENUM;
342                     dip->prev = WSS_REC_LVL;
343                     dip->next = AUDIO_MIXER_LAST;
344                     strcpy(dip->label.name, AudioNsource);
345                     dip->un.e.num_mem = 3;
346                     strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
347                     dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
348                     strcpy(dip->un.e.member[1].label.name, AudioNcd);
349                     dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
350                     strcpy(dip->un.e.member[2].label.name, AudioNdac);
351                     dip->un.e.member[2].ord = WSS_DAC_LVL;
352                     break;
353 
354           default:
355                     return ENXIO;
356                     /*NOTREACHED*/
357           }
358           DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
359 
360           return 0;
361 }
362 
363 
364 /*
365  * Copyright by Hannu Savolainen 1994
366  *
367  * Redistribution and use in source and binary forms, with or without
368  * modification, are permitted provided that the following conditions are
369  * met: 1. Redistributions of source code must retain the above copyright
370  * notice, this list of conditions and the following disclaimer. 2.
371  * Redistributions in binary form must reproduce the above copyright notice,
372  * this list of conditions and the following disclaimer in the documentation
373  * and/or other materials provided with the distribution.
374  *
375  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
376  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
377  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
378  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
379  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
380  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
381  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
382  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
383  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
384  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
385  * SUCH DAMAGE.
386  *
387  */
388 /*
389  * Initialization code for OPTi MAD16 compatible audio chips. Including
390  *
391  *      OPTi 82C928     MAD16           (replaced by C929)
392  *      OAK OTI-601D    Mozart
393  *      OPTi 82C929     MAD16 Pro
394  *
395  */
396 
397 u_int
mad_read(struct wss_softc * sc,int port)398 mad_read(struct wss_softc *sc, int port)
399 {
400           u_int tmp;
401           int pwd;
402 
403           switch (sc->mad_chip_type) {  /* Output password */
404           case MAD_82C928:
405           case MAD_OTI601D:
406                     pwd = M_PASSWD_928;
407                     break;
408           case MAD_82C929:
409                     pwd = M_PASSWD_929;
410                     break;
411           case MAD_82C931:
412                     pwd = M_PASSWD_931;
413                     break;
414           default:
415                     panic("mad_read: Bad chip type=%d", sc->mad_chip_type);
416           }
417           bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
418           tmp = bus_space_read_1(sc->sc_iot, sc->mad_ioh, port);
419           return tmp;
420 }
421 
422 void
mad_write(struct wss_softc * sc,int port,int value)423 mad_write(struct wss_softc *sc, int port, int value)
424 {
425           int pwd;
426 
427           switch (sc->mad_chip_type) {  /* Output password */
428           case MAD_82C928:
429           case MAD_OTI601D:
430                     pwd = M_PASSWD_928;
431                     break;
432           case MAD_82C929:
433                     pwd = M_PASSWD_929;
434                     break;
435           case MAD_82C931:
436                     pwd = M_PASSWD_931;
437                     break;
438           default:
439                     panic("mad_write: Bad chip type=%d", sc->mad_chip_type);
440           }
441           bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
442           bus_space_write_1(sc->sc_iot, sc->mad_ioh, port, value & 0xff);
443 }
444 
445 void
madattach(struct wss_softc * sc)446 madattach(struct wss_softc *sc)
447 {
448           struct ad1848_softc *ac;
449           unsigned char cs4231_mode;
450           int joy;
451 
452           ac = (struct ad1848_softc *)&sc->sc_ad1848;
453           if (sc->mad_chip_type == MAD_NONE)
454                     return;
455 
456           /* Do we want the joystick disabled? */
457           joy = device_cfdata(ac->sc_dev)->cf_flags & 2 ? MC1_JOYDISABLE : 0;
458 
459           /* enable WSS emulation at the I/O port */
460           mad_write(sc, MC1_PORT, M_WSS_PORT_SELECT(sc->mad_ioindex) | joy);
461           mad_write(sc, MC2_PORT, MC2_NO_CD_DRQ); /* disable CD */
462           mad_write(sc, MC3_PORT, 0xf0); /* Disable SB */
463 
464           cs4231_mode =
465                     strncmp(ac->chip_name, "CS4248", 6) == 0 ||
466                     strncmp(ac->chip_name, "CS4231", 6) == 0 ? 0x02 : 0;
467 
468           if (sc->mad_chip_type == MAD_82C929) {
469                     mad_write(sc, MC4_PORT, 0x92);
470                     mad_write(sc, MC5_PORT, 0xA5 | cs4231_mode);
471                     mad_write(sc, MC6_PORT, 0x03);          /* Disable MPU401 */
472           } else {
473                     mad_write(sc, MC4_PORT, 0x02);
474                     mad_write(sc, MC5_PORT, 0x30 | cs4231_mode);
475           }
476 
477 #ifdef AUDIO_DEBUG
478           if (wssdebug) {
479                     int i;
480                     for (i = MC1_PORT; i <= MC7_PORT; i++)
481                               DPRINTF(("port %03x after init = %02x\n",
482                                          i, mad_read(sc, i)));
483           }
484 #endif
485 }
486