1 /*        $NetBSD: vraiu.c,v 1.20 2021/01/13 06:39:46 skrll Exp $     */
2 
3 /*
4  * Copyright (c) 2001 HAMAJIMA Katsuomi. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: vraiu.c,v 1.20 2021/01/13 06:39:46 skrll Exp $");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/bswap.h>
35 
36 #include <machine/cpu.h>
37 #include <machine/intr.h>
38 #include <machine/bus.h>
39 #include <machine/platid.h>
40 #include <machine/platid_mask.h>
41 #include <machine/config_hook.h>
42 
43 #include <sys/audioio.h>
44 #include <dev/audio/audio_if.h>
45 
46 #include <hpcmips/vr/vr.h>
47 #include <hpcmips/vr/vripif.h>
48 #include <hpcmips/vr/icureg.h>
49 #include <hpcmips/vr/cmureg.h>
50 #include <hpcmips/vr/vraiureg.h>
51 
52 #ifdef VRAIU_DEBUG
53 int vraiu_debug = VRAIU_DEBUG;
54 #define DPRINTFN(n,x) if (vraiu_debug>(n)) printf x;
55 #else
56 #define DPRINTFN(n,x)
57 #endif
58 
59 #define AUDIO_BUF_SIZE 2048
60 
61 struct vraiu_softc {
62           device_t            sc_dev;
63           kmutex_t            sc_lock;
64           kmutex_t            sc_intr_lock;
65           bus_space_tag_t               sc_iot;
66           bus_space_handle_t  sc_ioh;
67           bus_dma_tag_t                 sc_dmat;
68           bus_dmamap_t                  sc_dmap;
69           vrip_chipset_tag_t  sc_vrip;
70           vrdcu_chipset_tag_t sc_dc;
71           vrdmaau_chipset_tag_t         sc_ac;
72           vrcmu_chipset_tag_t sc_cc;
73           void                          *sc_handler;
74           u_short   *sc_buf;  /* DMA buffer pointer */
75           u_int     sc_rate;  /* sampling rate */
76           u_char    sc_volume;          /* volume */
77           void      (*sc_intr)(void *); /* interrupt routine */
78           void      *sc_intrdata;                 /* interrupt data */
79 };
80 
81 int vraiu_match(device_t, cfdata_t, void *);
82 void vraiu_attach(device_t, device_t, void *);
83 int vraiu_intr(void *);
84 
85 CFATTACH_DECL_NEW(vraiu, sizeof(struct vraiu_softc),
86     vraiu_match, vraiu_attach, NULL, NULL);
87 
88 struct audio_device aiu_device = {
89           "VR4121 AIU",
90           "0.1",
91           "aiu"
92 };
93 
94 const struct audio_format vraiu_formats = {
95           .mode               = AUMODE_PLAY,
96           .encoding = AUDIO_ENCODING_SLINEAR_NE,
97           .validbits          = 10,
98           .precision          = 16,
99           .channels = 1,
100           .channel_mask       = AUFMT_MONAURAL,
101           .frequency_type     = 4,
102           .frequency          = { 8000, 11025, 22050, 44100 },
103 };
104 
105 /*
106  * Define our interface to the higher level audio driver.
107  */
108 int vraiu_query_format(void *, audio_format_query_t *);
109 int vraiu_round_blocksize(void *, int, int, const audio_params_t *);
110 int vraiu_commit_settings(void *);
111 int vraiu_init_output(void *, void*, int);
112 int vraiu_start_output(void *, void *, int, void (*)(void *), void *);
113 int vraiu_halt_output(void *);
114 int vraiu_getdev(void *, struct audio_device *);
115 int vraiu_set_port(void *, mixer_ctrl_t *);
116 int vraiu_get_port(void *, mixer_ctrl_t *);
117 int vraiu_query_devinfo(void *, mixer_devinfo_t *);
118 int vraiu_set_format(void *, int,
119     const audio_params_t *, const audio_params_t *,
120     audio_filter_reg_t *, audio_filter_reg_t *);
121 int vraiu_get_props(void *);
122 void vraiu_get_locks(void *, kmutex_t **, kmutex_t **);
123 
124 const struct audio_hw_if vraiu_hw_if = {
125           .query_format                 = vraiu_query_format,
126           .set_format                   = vraiu_set_format,
127           .round_blocksize    = vraiu_round_blocksize,
128           .commit_settings    = vraiu_commit_settings,
129           .init_output                  = vraiu_init_output,
130           .start_output                 = vraiu_start_output,
131           .halt_output                  = vraiu_halt_output,
132           .getdev                       = vraiu_getdev,
133           .set_port           = vraiu_set_port,
134           .get_port           = vraiu_get_port,
135           .query_devinfo                = vraiu_query_devinfo,
136           .get_props                    = vraiu_get_props,
137           .get_locks                    = vraiu_get_locks,
138 };
139 
140 /*
141  * convert to 1ch 10bit unsigned PCM data.
142  */
143 static void vraiu_slinear16_1(struct vraiu_softc *, u_short *, void *, int);
144 
145 int
vraiu_match(device_t parent,cfdata_t cf,void * aux)146 vraiu_match(device_t parent, cfdata_t cf, void *aux)
147 {
148           return 1;
149 }
150 
151 void
vraiu_attach(device_t parent,device_t self,void * aux)152 vraiu_attach(device_t parent, device_t self, void *aux)
153 {
154           struct vrip_attach_args *va;
155           struct vraiu_softc *sc;
156           bus_dma_segment_t segs;
157           int rsegs;
158 
159           va = aux;
160           sc = device_private(self);
161           sc->sc_dev = self;
162           sc->sc_intr = NULL;
163           sc->sc_iot = va->va_iot;
164           sc->sc_vrip = va->va_vc;
165           sc->sc_cc = va->va_cc;
166           sc->sc_dc = va->va_dc;
167           sc->sc_ac = va->va_ac;
168           sc->sc_dmat = &vrdcu_bus_dma_tag;
169           sc->sc_volume = 127;
170           mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
171           mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
172 
173           if (!sc->sc_cc) {
174                     printf(" not configured: cmu not found\n");
175                     return;
176           }
177           if (!sc->sc_dc) {
178                     printf(" not configured: dcu not found\n");
179                     return;
180           }
181           if (!sc->sc_ac) {
182                     printf(" not configured: dmaau not found\n");
183                     return;
184           }
185           if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size,
186                                 0 /* no flags */, &sc->sc_ioh)) {
187                     printf(": can't map i/o space\n");
188                     return;
189           }
190 
191           /* install interrupt handler and enable interrupt */
192           if (!(sc->sc_handler = vrip_intr_establish(va->va_vc, va->va_unit,
193               0, IPL_AUDIO, vraiu_intr, sc))) {
194                     printf(": can't map interrupt line.\n");
195                     return;
196           }
197           vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, (AIUINT_INTMEND | \
198                                                                        AIUINT_INTM | \
199                                                                        AIUINT_INTMIDLE | \
200                                                                        AIUINT_INTMST | \
201                                                                        AIUINT_INTSEND | \
202                                                                        AIUINT_INTS | \
203                                                                        AIUINT_INTSIDLE), 0);
204 
205           if (bus_dmamem_alloc(sc->sc_dmat, AUDIO_BUF_SIZE, 0, 0, &segs, 1,
206                                    &rsegs, BUS_DMA_WAITOK)) {
207                     printf(": can't allocate memory.\n");
208                     return;
209           }
210           if (bus_dmamem_map(sc->sc_dmat, &segs, rsegs, AUDIO_BUF_SIZE,
211                                  (void **)&sc->sc_buf,
212                                  BUS_DMA_WAITOK | BUS_DMA_COHERENT)) {
213                     printf(": can't map memory.\n");
214                     bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
215                     return;
216           }
217           if (bus_dmamap_create(sc->sc_dmat, AUDIO_BUF_SIZE, 1, AUDIO_BUF_SIZE,
218                                     0, BUS_DMA_WAITOK, &sc->sc_dmap)) {
219                     printf(": can't create DMA map.\n");
220                     bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
221                                          AUDIO_BUF_SIZE);
222                     bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
223                     return;
224           }
225           if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, sc->sc_buf,
226                                            AUDIO_BUF_SIZE, NULL, BUS_DMA_WAITOK)) {
227                     printf(": can't load DMA map.\n");
228                     bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
229                     bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
230                                          AUDIO_BUF_SIZE);
231                     bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
232                     return;
233           }
234           if (sc->sc_ac->ac_set_aiuout(sc->sc_ac, sc->sc_buf)) {
235                     printf(": can't set DMA address.\n");
236                     bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
237                     bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
238                     bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
239                                          AUDIO_BUF_SIZE);
240                     bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
241                     return;
242           }
243           printf("\n");
244 
245           sc->sc_rate = SPS8000;
246           DPRINTFN(1, ("vraiu_attach: reset AIU\n"))
247           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIURST);
248           /* attach audio subsystem */
249           audio_attach_mi(&vraiu_hw_if, sc, self);
250 }
251 
252 int
vraiu_query_format(void * self,audio_format_query_t * afp)253 vraiu_query_format(void *self, audio_format_query_t *afp)
254 {
255 
256           return audio_query_format(&vraiu_formats, 1, afp);
257 }
258 
259 int
vraiu_set_format(void * self,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)260 vraiu_set_format(void *self, int setmode,
261                      const audio_params_t *play, const audio_params_t *rec,
262                      audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
263 {
264           struct vraiu_softc *sc;
265 
266           DPRINTFN(1, ("%s: %ubit, %uch, %uHz, encoding %u\n", __func__,
267                          play->precision, play->channels, play->sample_rate,
268                          play->encoding));
269           sc = self;
270 
271           switch (play->sample_rate) {
272           case 8000:
273                     sc->sc_rate = SPS8000;
274                     break;
275           case 11025:
276                     sc->sc_rate = SPS11025;
277                     break;
278           case 22050:
279                     sc->sc_rate = SPS22050;
280                     break;
281           case 44100:
282                     sc->sc_rate = SPS44100;
283                     break;
284           default:
285                     /* NOTREACHED */
286                     panic("%s: rate error (%d)\n", __func__, play->sample_rate);
287           }
288 
289           return 0;
290 }
291 
292 int
vraiu_round_blocksize(void * self,int bs,int mode,const audio_params_t * param)293 vraiu_round_blocksize(void *self, int bs, int mode, const audio_params_t *param)
294 {
295           return AUDIO_BUF_SIZE;
296 }
297 
298 int
vraiu_commit_settings(void * self)299 vraiu_commit_settings(void *self)
300 {
301           struct vraiu_softc *sc;
302           int err;
303 
304           DPRINTFN(1, ("vraiu_commit_settings\n"));
305           sc = self;
306 
307           DPRINTFN(1, ("vraiu_commit_settings: set conversion rate %d\n",
308                          sc->sc_rate))
309           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNVR_REG_W, sc->sc_rate);
310           DPRINTFN(1, ("vraiu_commit_settings: clock supply start\n"))
311           if ((err = sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 1))) {
312                     DPRINTFN(0, ("vraiu_commit_settings: clock supply error\n"));
313                     return err;
314           }
315           DPRINTFN(1, ("vraiu_commit_settings: enable DMA\n"))
316           if ((err = sc->sc_dc->dc_enable_aiuout(sc->sc_dc))) {
317                     sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
318                     DPRINTFN(0, ("vraiu_commit_settings: enable DMA error\n"));
319                     return err;
320           }
321           DPRINTFN(1, ("vraiu_commit_settings: Vref on\n"))
322           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, DAENAIU);
323           return 0;
324 }
325 
326 int
vraiu_init_output(void * self,void * buffer,int size)327 vraiu_init_output(void *self, void *buffer, int size)
328 {
329           struct vraiu_softc *sc;
330 
331           DPRINTFN(1, ("vraiu_init_output: buffer %p, size %d\n", buffer, size));
332           sc = self;
333           sc->sc_intr = NULL;
334           DPRINTFN(1, ("vraiu_init_output: speaker power on\n"))
335           config_hook_call(CONFIG_HOOK_POWERCONTROL,
336                                CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)1);
337           DPRINTFN(1, ("vraiu_init_output: start output\n"))
338           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIUSEN);
339           return 0;
340 }
341 
342 int
vraiu_start_output(void * self,void * block,int bsize,void (* intr)(void *),void * intrarg)343 vraiu_start_output(void *self, void *block, int bsize,
344                        void (*intr)(void *), void *intrarg)
345 {
346           struct vraiu_softc *sc;
347 
348           DPRINTFN(2, ("vraiu_start_output: block %p, bsize %d\n",
349                          block, bsize));
350           sc = self;
351           vraiu_slinear16_1(sc, sc->sc_buf, block, bsize);
352           bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, AUDIO_BUF_SIZE,
353                               BUS_DMASYNC_PREWRITE);
354           sc->sc_intr = intr;
355           sc->sc_intrdata = intrarg;
356           /* clear interrupt status */
357           bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W,
358                                 SENDINTR | SINTR | SIDLEINTR);
359           /* enable interrupt */
360           vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 1);
361           return 0;
362 }
363 
364 int
vraiu_intr(void * self)365 vraiu_intr(void* self)
366 {
367           struct vraiu_softc *sc;
368           uint32_t reg;
369 
370           DPRINTFN(2, ("vraiu_intr"));
371           sc = self;
372 
373           mutex_spin_enter(&sc->sc_intr_lock);
374 
375           vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
376           vrip_intr_getstatus2(sc->sc_vrip, sc->sc_handler, &reg);
377           if (reg & AIUINT_INTSEND) {
378                     DPRINTFN(2, (": AIUINT_INTSEND"));
379                     if (sc->sc_intr) {
380                               void (*intr)(void *);
381                               intr = sc->sc_intr;
382                               sc->sc_intr = NULL;
383                               (*(intr))(sc->sc_intrdata);
384                     }
385                     bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W, SENDINTR);
386           }
387           DPRINTFN(2, ("\n"));
388 
389           mutex_spin_exit(&sc->sc_intr_lock);
390 
391           return 0;
392 }
393 
394 int
vraiu_halt_output(void * self)395 vraiu_halt_output(void *self)
396 {
397           struct vraiu_softc *sc;
398 
399           DPRINTFN(1, ("vraiu_halt_output\n"));
400           sc =self;
401           DPRINTFN(1, ("vraiu_halt_output: disable interrupt\n"))
402           vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
403           DPRINTFN(1, ("vraiu_halt_output: stop output\n"))
404           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, 0);
405           DPRINTFN(1, ("vraiu_halt_output: speaker power off\n"))
406           config_hook_call(CONFIG_HOOK_POWERCONTROL,
407                                CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)0);
408           DPRINTFN(1, ("vraiu_halt_output: Vref off\n"))
409           bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, 0);
410           DPRINTFN(1, ("vraiu_halt_output: disable DMA\n"))
411           sc->sc_dc->dc_disable(sc->sc_dc);
412           DPRINTFN(1, ("vraiu_halt_output: clock supply stop\n"))
413           sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
414           sc->sc_intr = NULL;
415           return 0;
416 }
417 
418 int
vraiu_getdev(void * self,struct audio_device * ret)419 vraiu_getdev(void *self, struct audio_device *ret)
420 {
421 
422           DPRINTFN(3, ("vraiu_getdev\n"));
423           *ret = aiu_device;
424           return 0;
425 }
426 
427 int
vraiu_set_port(void * self,mixer_ctrl_t * mc)428 vraiu_set_port(void *self, mixer_ctrl_t *mc)
429 {
430           struct vraiu_softc *sc;
431 
432           DPRINTFN(3, ("vraiu_set_port\n"));
433           sc = self;
434           /* software mixer, 1ch */
435           if (mc->dev == 0) {
436                     if (mc->type != AUDIO_MIXER_VALUE)
437                               return EINVAL;
438                     if (mc->un.value.num_channels != 1)
439                               return EINVAL;
440                     sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
441                     return 0;
442           }
443 
444           return EINVAL;
445 }
446 
447 int
vraiu_get_port(void * self,mixer_ctrl_t * mc)448 vraiu_get_port(void *self, mixer_ctrl_t *mc)
449 {
450           struct vraiu_softc *sc;
451 
452           DPRINTFN(3, ("vraiu_get_port\n"));
453           sc = self;
454           /* software mixer, 1ch */
455           if (mc->dev == 0) {
456                     if (mc->un.value.num_channels != 1)
457                               return EINVAL;
458                     mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_volume;
459                     return 0;
460           }
461 
462           return EINVAL;
463 }
464 
465 int
vraiu_query_devinfo(void * self,mixer_devinfo_t * di)466 vraiu_query_devinfo(void *self, mixer_devinfo_t *di)
467 {
468 
469           DPRINTFN(3, ("vraiu_query_devinfo\n"));
470           /* software mixer, 1ch */
471           switch (di->index) {
472           case 0: /* inputs.dac mixer value */
473                     di->mixer_class = 1;
474                     di->next = di->prev = AUDIO_MIXER_LAST;
475                     strcpy(di->label.name, AudioNdac);
476                     di->type = AUDIO_MIXER_VALUE;
477                     di->un.v.num_channels = 1;
478                     strcpy(di->un.v.units.name, AudioNvolume);
479                     return 0;
480           case 1: /* outputs class */
481                     di->mixer_class = 1;
482                     di->next = di->prev = AUDIO_MIXER_LAST;
483                     strcpy(di->label.name, AudioCinputs);
484                     di->type = AUDIO_MIXER_CLASS;
485                     return 0;
486           }
487 
488           return ENXIO;
489 }
490 
491 int
vraiu_get_props(void * self)492 vraiu_get_props(void *self)
493 {
494           DPRINTFN(3, ("vraiu_get_props\n"));
495 
496           return AUDIO_PROP_PLAYBACK;
497 }
498 
499 void
vraiu_get_locks(void * self,kmutex_t ** intr,kmutex_t ** thread)500 vraiu_get_locks(void *self, kmutex_t **intr, kmutex_t **thread)
501 {
502           struct vraiu_softc *sc;
503 
504           DPRINTFN(3, ("vraiu_get_locks\n"));
505           sc = self;
506 
507           *intr = &sc->sc_intr_lock;
508           *thread = &sc->sc_lock;
509 }
510 
511 /* slinear16/mono -> ulinear10/mono with volume */
512 static void
vraiu_slinear16_1(struct vraiu_softc * sc,u_short * dmap,void * p,int n)513 vraiu_slinear16_1(struct vraiu_softc *sc, u_short *dmap, void *p, int n)
514 {
515           short *q;
516 
517           DPRINTFN(3, ("vraiu_slinear16_1\n"));
518           q = p;
519 #ifdef DIAGNOSTIC
520           if (n > AUDIO_BUF_SIZE) {
521                     printf("%s: output data too large (%d > %d)\n",
522                            device_xname(sc->sc_dev), n, AUDIO_BUF_SIZE);
523                     n = AUDIO_BUF_SIZE;
524           }
525 #endif
526           n /= 2;
527           while (n--) {
528                     int i = *q++;
529                     i = i * sc->sc_volume / 255;
530                     *dmap++ = (i >> 6) + 0x200;
531           }
532 }
533