vraiu.c revision 1.17 1 /* $NetBSD: vraiu.c,v 1.17 2019/05/08 13:40:15 isaki 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.17 2019/05/08 13:40:15 isaki 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_start_input(void *, void *, int, void (*)(void *), void *);
114 int vraiu_halt_output(void *);
115 int vraiu_halt_input(void *);
116 int vraiu_getdev(void *, struct audio_device *);
117 int vraiu_set_port(void *, mixer_ctrl_t *);
118 int vraiu_get_port(void *, mixer_ctrl_t *);
119 int vraiu_query_devinfo(void *, mixer_devinfo_t *);
120 int vraiu_set_format(void *, int,
121 const audio_params_t *, const audio_params_t *,
122 audio_filter_reg_t *, audio_filter_reg_t *);
123 int vraiu_get_props(void *);
124 void vraiu_get_locks(void *, kmutex_t **, kmutex_t **);
125
126 const struct audio_hw_if vraiu_hw_if = {
127 .query_format = vraiu_query_format,
128 .set_format = vraiu_set_format,
129 .round_blocksize = vraiu_round_blocksize,
130 .commit_settings = vraiu_commit_settings,
131 .init_output = vraiu_init_output,
132 .init_input = NULL,
133 .start_output = vraiu_start_output,
134 .start_input = vraiu_start_input,
135 .halt_output = vraiu_halt_output,
136 .halt_input = vraiu_halt_input,
137 .getdev = vraiu_getdev,
138 .set_port = vraiu_set_port,
139 .get_port = vraiu_get_port,
140 .query_devinfo = vraiu_query_devinfo,
141 .get_props = vraiu_get_props,
142 .get_locks = vraiu_get_locks,
143 };
144
145 /*
146 * convert to 1ch 10bit unsigned PCM data.
147 */
148 static void vraiu_slinear16_1(struct vraiu_softc *, u_short *, void *, int);
149
150 int
151 vraiu_match(device_t parent, cfdata_t cf, void *aux)
152 {
153 return 1;
154 }
155
156 void
157 vraiu_attach(device_t parent, device_t self, void *aux)
158 {
159 struct vrip_attach_args *va;
160 struct vraiu_softc *sc;
161 bus_dma_segment_t segs;
162 int rsegs;
163
164 va = aux;
165 sc = device_private(self);
166 sc->sc_dev = self;
167 sc->sc_intr = NULL;
168 sc->sc_iot = va->va_iot;
169 sc->sc_vrip = va->va_vc;
170 sc->sc_cc = va->va_cc;
171 sc->sc_dc = va->va_dc;
172 sc->sc_ac = va->va_ac;
173 sc->sc_dmat = &vrdcu_bus_dma_tag;
174 sc->sc_volume = 127;
175 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
176 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
177
178 if (!sc->sc_cc) {
179 printf(" not configured: cmu not found\n");
180 return;
181 }
182 if (!sc->sc_dc) {
183 printf(" not configured: dcu not found\n");
184 return;
185 }
186 if (!sc->sc_ac) {
187 printf(" not configured: dmaau not found\n");
188 return;
189 }
190 if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size,
191 0 /* no flags */, &sc->sc_ioh)) {
192 printf(": can't map i/o space\n");
193 return;
194 }
195
196 /* install interrupt handler and enable interrupt */
197 if (!(sc->sc_handler = vrip_intr_establish(va->va_vc, va->va_unit,
198 0, IPL_AUDIO, vraiu_intr, sc))) {
199 printf(": can't map interrupt line.\n");
200 return;
201 }
202 vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, (AIUINT_INTMEND | \
203 AIUINT_INTM | \
204 AIUINT_INTMIDLE | \
205 AIUINT_INTMST | \
206 AIUINT_INTSEND | \
207 AIUINT_INTS | \
208 AIUINT_INTSIDLE), 0);
209
210 if (bus_dmamem_alloc(sc->sc_dmat, AUDIO_BUF_SIZE, 0, 0, &segs, 1,
211 &rsegs, BUS_DMA_WAITOK)) {
212 printf(": can't allocate memory.\n");
213 return;
214 }
215 if (bus_dmamem_map(sc->sc_dmat, &segs, rsegs, AUDIO_BUF_SIZE,
216 (void **)&sc->sc_buf,
217 BUS_DMA_WAITOK | BUS_DMA_COHERENT)) {
218 printf(": can't map memory.\n");
219 bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
220 return;
221 }
222 if (bus_dmamap_create(sc->sc_dmat, AUDIO_BUF_SIZE, 1, AUDIO_BUF_SIZE,
223 0, BUS_DMA_WAITOK, &sc->sc_dmap)) {
224 printf(": can't create DMA map.\n");
225 bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
226 AUDIO_BUF_SIZE);
227 bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
228 return;
229 }
230 if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, sc->sc_buf,
231 AUDIO_BUF_SIZE, NULL, BUS_DMA_WAITOK)) {
232 printf(": can't load DMA map.\n");
233 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
234 bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
235 AUDIO_BUF_SIZE);
236 bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
237 return;
238 }
239 if (sc->sc_ac->ac_set_aiuout(sc->sc_ac, sc->sc_buf)) {
240 printf(": can't set DMA address.\n");
241 bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
242 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
243 bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
244 AUDIO_BUF_SIZE);
245 bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
246 return;
247 }
248 printf("\n");
249
250 sc->sc_rate = SPS8000;
251 DPRINTFN(1, ("vraiu_attach: reset AIU\n"))
252 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIURST);
253 /* attach audio subsystem */
254 audio_attach_mi(&vraiu_hw_if, sc, self);
255 }
256
257 int
258 vraiu_query_format(void *self, audio_format_query_t *afp)
259 {
260
261 return audio_query_format(&vraiu_formats, 1, afp);
262 }
263
264 int
265 vraiu_set_format(void *self, int setmode,
266 const audio_params_t *play, const audio_params_t *rec,
267 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
268 {
269 struct vraiu_softc *sc;
270
271 DPRINTFN(1, ("%s: %ubit, %uch, %uHz, encoding %u\n", __func__,
272 play->precision, play->channels, play->sample_rate,
273 play->encoding));
274 sc = self;
275
276 switch (play->sample_rate) {
277 case 8000:
278 sc->sc_rate = SPS8000;
279 break;
280 case 11025:
281 sc->sc_rate = SPS11025;
282 break;
283 case 22050:
284 sc->sc_rate = SPS22050;
285 break;
286 case 44100:
287 sc->sc_rate = SPS44100;
288 break;
289 default:
290 /* NOTREACHED */
291 panic("%s: rate error (%d)\n", __func__, play->sample_rate);
292 }
293
294 return 0;
295 }
296
297 int
298 vraiu_round_blocksize(void *self, int bs, int mode, const audio_params_t *param)
299 {
300 return AUDIO_BUF_SIZE;
301 }
302
303 int
304 vraiu_commit_settings(void *self)
305 {
306 struct vraiu_softc *sc;
307 int err;
308
309 DPRINTFN(1, ("vraiu_commit_settings\n"));
310 sc = self;
311
312 DPRINTFN(1, ("vraiu_commit_settings: set conversion rate %d\n",
313 sc->sc_rate))
314 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNVR_REG_W, sc->sc_rate);
315 DPRINTFN(1, ("vraiu_commit_settings: clock supply start\n"))
316 if ((err = sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 1))) {
317 DPRINTFN(0, ("vraiu_commit_settings: clock supply error\n"));
318 return err;
319 }
320 DPRINTFN(1, ("vraiu_commit_settings: enable DMA\n"))
321 if ((err = sc->sc_dc->dc_enable_aiuout(sc->sc_dc))) {
322 sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
323 DPRINTFN(0, ("vraiu_commit_settings: enable DMA error\n"));
324 return err;
325 }
326 DPRINTFN(1, ("vraiu_commit_settings: Vref on\n"))
327 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, DAENAIU);
328 return 0;
329 }
330
331 int
332 vraiu_init_output(void *self, void *buffer, int size)
333 {
334 struct vraiu_softc *sc;
335
336 DPRINTFN(1, ("vraiu_init_output: buffer %p, size %d\n", buffer, size));
337 sc = self;
338 sc->sc_intr = NULL;
339 DPRINTFN(1, ("vraiu_init_output: speaker power on\n"))
340 config_hook_call(CONFIG_HOOK_POWERCONTROL,
341 CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)1);
342 DPRINTFN(1, ("vraiu_init_output: start output\n"))
343 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIUSEN);
344 return 0;
345 }
346
347 int
348 vraiu_start_output(void *self, void *block, int bsize,
349 void (*intr)(void *), void *intrarg)
350 {
351 struct vraiu_softc *sc;
352
353 DPRINTFN(2, ("vraiu_start_output: block %p, bsize %d\n",
354 block, bsize));
355 sc = self;
356 vraiu_slinear16_1(sc, sc->sc_buf, block, bsize);
357 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, AUDIO_BUF_SIZE,
358 BUS_DMASYNC_PREWRITE);
359 sc->sc_intr = intr;
360 sc->sc_intrdata = intrarg;
361 /* clear interrupt status */
362 bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W,
363 SENDINTR | SINTR | SIDLEINTR);
364 /* enable interrupt */
365 vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 1);
366 return 0;
367 }
368
369 int
370 vraiu_start_input(void *self, void *block, int bsize,
371 void (*intr)(void *), void *intrarg)
372 {
373
374 DPRINTFN(3, ("vraiu_start_input\n"));
375 /* no input */
376 return ENXIO;
377 }
378
379 int
380 vraiu_intr(void* self)
381 {
382 struct vraiu_softc *sc;
383 uint32_t reg;
384
385 DPRINTFN(2, ("vraiu_intr"));
386 sc = self;
387
388 mutex_spin_enter(&sc->sc_intr_lock);
389
390 vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
391 vrip_intr_getstatus2(sc->sc_vrip, sc->sc_handler, ®);
392 if (reg & AIUINT_INTSEND) {
393 DPRINTFN(2, (": AIUINT_INTSEND"));
394 if (sc->sc_intr) {
395 void (*intr)(void *);
396 intr = sc->sc_intr;
397 sc->sc_intr = NULL;
398 (*(intr))(sc->sc_intrdata);
399 }
400 bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W, SENDINTR);
401 }
402 DPRINTFN(2, ("\n"));
403
404 mutex_spin_exit(&sc->sc_intr_lock);
405
406 return 0;
407 }
408
409 int
410 vraiu_halt_output(void *self)
411 {
412 struct vraiu_softc *sc;
413
414 DPRINTFN(1, ("vraiu_halt_output\n"));
415 sc =self;
416 DPRINTFN(1, ("vraiu_halt_output: disable interrupt\n"))
417 vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
418 DPRINTFN(1, ("vraiu_halt_output: stop output\n"))
419 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, 0);
420 DPRINTFN(1, ("vraiu_halt_output: speaker power off\n"))
421 config_hook_call(CONFIG_HOOK_POWERCONTROL,
422 CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)0);
423 DPRINTFN(1, ("vraiu_halt_output: Vref off\n"))
424 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, 0);
425 DPRINTFN(1, ("vraiu_halt_output: disable DMA\n"))
426 sc->sc_dc->dc_disable(sc->sc_dc);
427 DPRINTFN(1, ("vraiu_halt_output: clock supply stop\n"))
428 sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
429 sc->sc_intr = NULL;
430 return 0;
431 }
432
433 int
434 vraiu_halt_input(void *self)
435 {
436
437 DPRINTFN(3, ("vraiu_halt_input\n"));
438 /* no input */
439 return ENXIO;
440 }
441
442
443 int
444 vraiu_getdev(void *self, struct audio_device *ret)
445 {
446
447 DPRINTFN(3, ("vraiu_getdev\n"));
448 *ret = aiu_device;
449 return 0;
450 }
451
452 int
453 vraiu_set_port(void *self, mixer_ctrl_t *mc)
454 {
455 struct vraiu_softc *sc;
456
457 DPRINTFN(3, ("vraiu_set_port\n"));
458 sc = self;
459 /* software mixer, 1ch */
460 if (mc->dev == 0) {
461 if (mc->type != AUDIO_MIXER_VALUE)
462 return EINVAL;
463 if (mc->un.value.num_channels != 1)
464 return EINVAL;
465 sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
466 return 0;
467 }
468
469 return EINVAL;
470 }
471
472 int
473 vraiu_get_port(void *self, mixer_ctrl_t *mc)
474 {
475 struct vraiu_softc *sc;
476
477 DPRINTFN(3, ("vraiu_get_port\n"));
478 sc = self;
479 /* software mixer, 1ch */
480 if (mc->dev == 0) {
481 if (mc->un.value.num_channels != 1)
482 return EINVAL;
483 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_volume;
484 return 0;
485 }
486
487 return EINVAL;
488 }
489
490 int
491 vraiu_query_devinfo(void *self, mixer_devinfo_t *di)
492 {
493
494 DPRINTFN(3, ("vraiu_query_devinfo\n"));
495 /* software mixer, 1ch */
496 switch (di->index) {
497 case 0: /* inputs.dac mixer value */
498 di->mixer_class = 1;
499 di->next = di->prev = AUDIO_MIXER_LAST;
500 strcpy(di->label.name, AudioNdac);
501 di->type = AUDIO_MIXER_VALUE;
502 di->un.v.num_channels = 1;
503 strcpy(di->un.v.units.name, AudioNvolume);
504 return 0;
505 case 1: /* outputs class */
506 di->mixer_class = 1;
507 di->next = di->prev = AUDIO_MIXER_LAST;
508 strcpy(di->label.name, AudioCinputs);
509 di->type = AUDIO_MIXER_CLASS;
510 return 0;
511 }
512
513 return ENXIO;
514 }
515
516 int
517 vraiu_get_props(void *self)
518 {
519 DPRINTFN(3, ("vraiu_get_props\n"));
520
521 return 0;
522 }
523
524 void
525 vraiu_get_locks(void *self, kmutex_t **intr, kmutex_t **thread)
526 {
527 struct vraiu_softc *sc;
528
529 DPRINTFN(3, ("vraiu_get_locks\n"));
530 sc = self;
531
532 *intr = &sc->sc_intr_lock;
533 *thread = &sc->sc_lock;
534 }
535
536 /* slinear16/mono -> ulinear10/mono with volume */
537 static void
538 vraiu_slinear16_1(struct vraiu_softc *sc, u_short *dmap, void *p, int n)
539 {
540 short *q;
541
542 DPRINTFN(3, ("vraiu_slinear16_1\n"));
543 q = p;
544 #ifdef DIAGNOSTIC
545 if (n > AUDIO_BUF_SIZE) {
546 printf("%s: output data too large (%d > %d)\n",
547 device_xname(sc->sc_dev), n, AUDIO_BUF_SIZE);
548 n = AUDIO_BUF_SIZE;
549 }
550 #endif
551 n /= 2;
552 while (n--) {
553 int i = *q++;
554 i = i * sc->sc_volume / 255;
555 *dmap++ = (i >> 6) + 0x200;
556 }
557 }
558