1 /*        $NetBSD: paud_isa.c,v 1.19 2020/02/29 05:51:11 isaki Exp $  */
2 
3 /*-
4  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Paul Kranenburg.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: paud_isa.c,v 1.19 2020/02/29 05:51:11 isaki Exp $");
34 
35 #include "audio.h"
36 #if NAUDIO > 0
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/device.h>
42 #include <sys/kmem.h>
43 
44 #include <sys/bus.h>
45 #include <machine/intr.h>
46 
47 #include <sys/audioio.h>
48 #include <dev/audio/audio_if.h>
49 
50 #include <dev/isa/isavar.h>
51 #include <dev/isa/isadmavar.h>
52 
53 #include <dev/ic/ad1848reg.h>
54 #include <dev/ic/cs4231reg.h>
55 
56 #include <dev/isa/ad1848var.h>
57 #include <dev/isa/cs4231var.h>
58 
59 #define   PAUD_MIC_IN_LVL               0
60 #define   PAUD_LINE_IN_LVL    1
61 #define   PAUD_DAC_LVL                  2
62 #define   PAUD_REC_LVL                  3
63 #define   PAUD_MONITOR_LVL    4
64 #define   PAUD_MIC_IN_MUTE    5
65 #define   PAUD_LINE_IN_MUTE   6
66 #define   PAUD_DAC_MUTE                 7
67 #define   PAUD_MONITOR_MUTE   8
68 #define   PAUD_RECORD_SOURCE  9
69 #define   PAUD_INPUT_CLASS    10
70 #define   PAUD_RECORD_CLASS   11
71 #define   PAUD_MONITOR_CLASS  12
72 
73 
74 /* autoconfiguration driver */
75 static void paud_attach_isa(device_t, device_t, void *);
76 static int  paud_match_isa(device_t, cfdata_t, void *);
77 
78 CFATTACH_DECL_NEW(paud_isa, sizeof(struct ad1848_isa_softc),
79     paud_match_isa, paud_attach_isa, NULL, NULL);
80 
81 /*
82  * Define our interface to the higher level audio driver.
83  */
84 static struct audio_device paud_device = {
85           "paud,cs4232",
86           "",
87           ""
88 };
89 
90 static int paud_intr(void *);
91 static int paud_getdev(void *, struct audio_device *);
92 static int paud_mixer_set_port(void *, mixer_ctrl_t *);
93 static int paud_mixer_get_port(void *, mixer_ctrl_t *);
94 static int paud_query_devinfo(void *, mixer_devinfo_t *);
95 
96 static const struct audio_hw_if paud_hw_if = {
97           .open                         = ad1848_isa_open,
98           .close                        = ad1848_isa_close,
99           .query_format                 = ad1848_query_format,
100           .set_format                   = ad1848_set_format,
101           .commit_settings    = ad1848_commit_settings,
102           .halt_output                  = ad1848_isa_halt_output,
103           .halt_input                   = ad1848_isa_halt_input,
104           .getdev                       = paud_getdev,
105           .set_port           = paud_mixer_set_port,
106           .get_port           = paud_mixer_get_port,
107           .query_devinfo                = paud_query_devinfo,
108           .allocm                       = ad1848_isa_malloc,
109           .freem                        = ad1848_isa_free,
110           .round_buffersize   = ad1848_isa_round_buffersize,
111           .get_props                    = ad1848_isa_get_props,
112           .trigger_output               = ad1848_isa_trigger_output,
113           .trigger_input                = ad1848_isa_trigger_input,
114           .get_locks                    = ad1848_get_locks,
115 };
116 
117 /* autoconfig routines */
118 
119 static int
paud_match_isa(device_t parent,cfdata_t cf,void * aux)120 paud_match_isa(device_t parent, cfdata_t cf, void *aux)
121 {
122           struct ad1848_isa_softc probesc, *sc = &probesc;
123           struct isa_attach_args *ia = aux;
124 
125           if (ia->ia_io[0].ir_addr != 0x830)
126                     return 0;
127 
128           sc->sc_ad1848.sc_iot = ia->ia_iot;
129           sc->sc_ic = ia->ia_ic;
130           if (ad1848_isa_mapprobe(sc, ia->ia_io[0].ir_addr)) {
131                     ia->ia_io[0].ir_size = AD1848_NPORT;
132                     ad1848_isa_unmap(sc);
133                     return 1;
134           }
135           return 0;
136 }
137 
138 /*
139  * Audio chip found.
140  */
141 static void
paud_attach_isa(device_t parent,device_t self,void * aux)142 paud_attach_isa(device_t parent, device_t self, void *aux)
143 {
144           struct ad1848_isa_softc *sc;
145           struct isa_attach_args *ia;
146 
147           sc = device_private(self);
148           sc->sc_ad1848.sc_dev = self;
149           ia = aux;
150           sc->sc_ad1848.sc_iot = ia->ia_iot;
151           sc->sc_ic = ia->ia_ic;
152 
153           mutex_init(&sc->sc_ad1848.sc_lock, MUTEX_DEFAULT, IPL_NONE);
154           mutex_init(&sc->sc_ad1848.sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
155 
156           if (ad1848_isa_mapprobe(sc, ia->ia_io[0].ir_addr) == 0) {
157                     aprint_error(": attach failed\n");
158                     return;
159           }
160           sc->sc_playdrq = ia->ia_drq[0].ir_drq;
161           sc->sc_recdrq = ia->ia_drq[1].ir_drq;
162           sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq[0].ir_irq,
163               IST_EDGE, IPL_AUDIO, paud_intr, sc);
164           ad1848_isa_attach(sc);
165           aprint_normal("\n");
166           audio_attach_mi(&paud_hw_if, &sc->sc_ad1848, self);
167 
168 }
169 
170 static int
paud_intr(void * addr)171 paud_intr(void *addr)
172 {
173           struct ad1848_isa_softc *sc = addr;
174           int ret;
175 
176           mutex_spin_enter(&sc->sc_ad1848.sc_intr_lock);
177           ret = ad1848_isa_intr(sc);
178           mutex_spin_exit(&sc->sc_ad1848.sc_intr_lock);
179 
180           return ret;
181 }
182 
183 static int
paud_getdev(void * addr,struct audio_device * retp)184 paud_getdev(void *addr, struct audio_device *retp)
185 {
186 
187           *retp = paud_device;
188           return 0;
189 }
190 
191 static ad1848_devmap_t mappings[] = {
192           { PAUD_MIC_IN_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
193           { PAUD_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
194           { PAUD_DAC_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
195           { PAUD_MONITOR_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL },
196           { PAUD_MIC_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
197           { PAUD_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
198           { PAUD_DAC_MUTE, AD1848_KIND_MUTE, AD1848_DAC_CHANNEL },
199           { PAUD_MONITOR_MUTE, AD1848_KIND_MUTE, AD1848_MONO_CHANNEL },
200           { PAUD_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
201           { PAUD_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1}
202 };
203 
204 static const int nummap = sizeof(mappings) / sizeof(mappings[0]);
205 
206 static int
paud_mixer_set_port(void * addr,mixer_ctrl_t * cp)207 paud_mixer_set_port(void *addr, mixer_ctrl_t *cp)
208 {
209           struct ad1848_softc *ac;
210 
211           ac = addr;
212           return ad1848_mixer_set_port(ac, mappings, nummap, cp);
213 }
214 
215 static int
paud_mixer_get_port(void * addr,mixer_ctrl_t * cp)216 paud_mixer_get_port(void *addr, mixer_ctrl_t *cp)
217 {
218           struct ad1848_softc *ac;
219 
220           ac = addr;
221           return ad1848_mixer_get_port(ac, mappings, nummap, cp);
222 }
223 
224 static int
paud_query_devinfo(void * addr,mixer_devinfo_t * dip)225 paud_query_devinfo(void *addr, mixer_devinfo_t *dip)
226 {
227 
228           switch(dip->index) {
229           case PAUD_MIC_IN_LVL:         /* Microphone */
230                     dip->type = AUDIO_MIXER_VALUE;
231                     dip->mixer_class = PAUD_INPUT_CLASS;
232                     dip->prev = AUDIO_MIXER_LAST;
233                     dip->next = PAUD_MIC_IN_MUTE;
234                     strcpy(dip->label.name, AudioNmicrophone);
235                     dip->un.v.num_channels = 2;
236                     strcpy(dip->un.v.units.name, AudioNvolume);
237                     break;
238 
239           case PAUD_LINE_IN_LVL:        /* line/CD */
240                     dip->type = AUDIO_MIXER_VALUE;
241                     dip->mixer_class = PAUD_INPUT_CLASS;
242                     dip->prev = AUDIO_MIXER_LAST;
243                     dip->next = PAUD_LINE_IN_MUTE;
244                     strcpy(dip->label.name, AudioNcd);
245                     dip->un.v.num_channels = 2;
246                     strcpy(dip->un.v.units.name, AudioNvolume);
247                     break;
248 
249           case PAUD_DAC_LVL:            /*  dacout */
250                     dip->type = AUDIO_MIXER_VALUE;
251                     dip->mixer_class = PAUD_INPUT_CLASS;
252                     dip->prev = AUDIO_MIXER_LAST;
253                     dip->next = PAUD_DAC_MUTE;
254                     strcpy(dip->label.name, AudioNdac);
255                     dip->un.v.num_channels = 2;
256                     strcpy(dip->un.v.units.name, AudioNvolume);
257                     break;
258 
259           case PAUD_REC_LVL:  /* record level */
260                     dip->type = AUDIO_MIXER_VALUE;
261                     dip->mixer_class = PAUD_RECORD_CLASS;
262                     dip->prev = AUDIO_MIXER_LAST;
263                     dip->next = PAUD_RECORD_SOURCE;
264                     strcpy(dip->label.name, AudioNrecord);
265                     dip->un.v.num_channels = 2;
266                     strcpy(dip->un.v.units.name, AudioNvolume);
267                     break;
268 
269           case PAUD_MONITOR_LVL:        /* monitor level */
270                     dip->type = AUDIO_MIXER_VALUE;
271                     dip->mixer_class = PAUD_MONITOR_CLASS;
272                     dip->prev = AUDIO_MIXER_LAST;
273                     dip->next = PAUD_MONITOR_MUTE;
274                     strcpy(dip->label.name, AudioNmonitor);
275                     dip->un.v.num_channels = 1;
276                     strcpy(dip->un.v.units.name, AudioNvolume);
277                     break;
278 
279           case PAUD_INPUT_CLASS:                            /* input class descriptor */
280                     dip->type = AUDIO_MIXER_CLASS;
281                     dip->mixer_class = PAUD_INPUT_CLASS;
282                     dip->next = dip->prev = AUDIO_MIXER_LAST;
283                     strcpy(dip->label.name, AudioCinputs);
284                     break;
285 
286           case PAUD_MONITOR_CLASS:                          /* monitor class descriptor */
287                     dip->type = AUDIO_MIXER_CLASS;
288                     dip->mixer_class = PAUD_MONITOR_CLASS;
289                     dip->next = dip->prev = AUDIO_MIXER_LAST;
290                     strcpy(dip->label.name, AudioCmonitor);
291                     break;
292 
293           case PAUD_RECORD_CLASS:                           /* record source class */
294                     dip->type = AUDIO_MIXER_CLASS;
295                     dip->mixer_class = PAUD_RECORD_CLASS;
296                     dip->next = dip->prev = AUDIO_MIXER_LAST;
297                     strcpy(dip->label.name, AudioCrecord);
298                     break;
299 
300           case PAUD_MIC_IN_MUTE:
301                     dip->mixer_class = PAUD_INPUT_CLASS;
302                     dip->type = AUDIO_MIXER_ENUM;
303                     dip->prev = PAUD_MIC_IN_LVL;
304                     dip->next = AUDIO_MIXER_LAST;
305                     goto mute;
306 
307           case PAUD_LINE_IN_MUTE:
308                     dip->mixer_class = PAUD_INPUT_CLASS;
309                     dip->type = AUDIO_MIXER_ENUM;
310                     dip->prev = PAUD_LINE_IN_LVL;
311                     dip->next = AUDIO_MIXER_LAST;
312                     goto mute;
313 
314           case PAUD_DAC_MUTE:
315                     dip->mixer_class = PAUD_INPUT_CLASS;
316                     dip->type = AUDIO_MIXER_ENUM;
317                     dip->prev = PAUD_DAC_LVL;
318                     dip->next = AUDIO_MIXER_LAST;
319                     goto mute;
320 
321           case PAUD_MONITOR_MUTE:
322                     dip->mixer_class = PAUD_MONITOR_CLASS;
323                     dip->type = AUDIO_MIXER_ENUM;
324                     dip->prev = PAUD_MONITOR_LVL;
325                     dip->next = AUDIO_MIXER_LAST;
326           mute:
327                     strcpy(dip->label.name, AudioNmute);
328                     dip->un.e.num_mem = 2;
329                     strcpy(dip->un.e.member[0].label.name, AudioNoff);
330                     dip->un.e.member[0].ord = 0;
331                     strcpy(dip->un.e.member[1].label.name, AudioNon);
332                     dip->un.e.member[1].ord = 1;
333                     break;
334 
335           case PAUD_RECORD_SOURCE:
336                     dip->mixer_class = PAUD_RECORD_CLASS;
337                     dip->type = AUDIO_MIXER_ENUM;
338                     dip->prev = PAUD_REC_LVL;
339                     dip->next = AUDIO_MIXER_LAST;
340                     strcpy(dip->label.name, AudioNsource);
341                     dip->un.e.num_mem = 3;
342                     strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
343                     dip->un.e.member[0].ord = PAUD_MIC_IN_LVL;
344                     strcpy(dip->un.e.member[1].label.name, AudioNcd);
345                     dip->un.e.member[1].ord = PAUD_LINE_IN_LVL;
346                     strcpy(dip->un.e.member[2].label.name, AudioNdac);
347                     dip->un.e.member[2].ord = PAUD_DAC_LVL;
348                     break;
349 
350           default:
351                     return ENXIO;
352                     /*NOTREACHED*/
353           }
354 
355           return 0;
356 }
357 
358 #endif
359