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, ®);
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