1 /* $NetBSD: repulse.c,v 1.24 2022/02/12 23:30:30 andvar Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Ignatios Souvatzis.
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: repulse.c,v 1.24 2022/02/12 23:30:30 andvar Exp $");
34
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/device.h>
40 #include <sys/fcntl.h> /* FREAD */
41 #include <sys/bus.h>
42
43 #include <sys/audioio.h>
44 #include <dev/audio/audio_if.h>
45
46 #include <dev/ic/ac97reg.h>
47 #include <dev/ic/ac97var.h>
48
49 #include <amiga/dev/zbusvar.h>
50 #include <amiga/amiga/isr.h>
51
52 #include <amiga/dev/repulse_firmware.h>
53
54 #ifndef vu_int8_t
55 #define vu_int8_t volatile uint8_t
56 #endif
57 #ifndef vu_int16_t
58 #define vu_int16_t volatile uint16_t
59 #endif
60 #ifndef vu_int32_t
61 #define vu_int32_t volatile uint32_t
62 #endif
63
64 /* ac97 attachment functions */
65
66 int repac_attach(void *, struct ac97_codec_if *);
67 int repac_read(void *, uint8_t, uint16_t *);
68 int repac_write(void *, uint8_t, uint16_t);
69 int repac_reset(void *);
70 enum ac97_host_flag repac_flags(void *);
71
72 /* audio attachment functions */
73
74 int rep_getdev(void *, struct audio_device *);
75 int rep_get_props(void *);
76 int rep_halt_output(void *);
77 int rep_halt_input(void *);
78 int rep_query_format(void *, audio_format_query_t *);
79 int rep_set_format(void *, int, const audio_params_t *, const audio_params_t *,
80 audio_filter_reg_t *, audio_filter_reg_t *);
81 int rep_round_blocksize(void *, int, int, const audio_params_t *);
82 int rep_set_port(void *, mixer_ctrl_t *);
83 int rep_get_port(void *, mixer_ctrl_t *);
84 int rep_query_devinfo(void *, mixer_devinfo_t *);
85 void rep_get_locks(void *, kmutex_t **, kmutex_t **);
86
87 int rep_start_input(void *, void *, int, void (*)(void *), void *);
88 int rep_start_output(void *, void *, int, void (*)(void *), void *);
89
90 int rep_intr(void *);
91
92
93 /* audio attachment */
94
95 const struct audio_hw_if rep_hw_if = {
96 .query_format = rep_query_format,
97 .set_format = rep_set_format,
98 .round_blocksize = rep_round_blocksize,
99 .start_output = rep_start_output,
100 .start_input = rep_start_input,
101 .halt_output = rep_halt_output,
102 .halt_input = rep_halt_input,
103 .getdev = rep_getdev,
104 .set_port = rep_set_port,
105 .get_port = rep_get_port,
106 .query_devinfo = rep_query_devinfo,
107 .get_props = rep_get_props,
108 .get_locks = rep_get_locks,
109 };
110
111 /* hardware registers */
112
113 struct repulse_hw {
114 vu_int16_t rhw_status;
115 vu_int16_t rhw_fifostatus; /* 0xrrrrpppp0000flag */
116 vu_int16_t rhw_reg_address;
117 vu_int16_t rhw_reg_data;
118 /* 0x08 */
119 vu_int16_t rhw_fifo_lh;
120 vu_int16_t rhw_fifo_ll;
121 vu_int16_t rhw_fifo_rh;
122 vu_int16_t rhw_fifo_rl;
123 /* 0x10 */
124 vu_int16_t rhw_fifo_pack;
125 vu_int16_t rhw_play_fifosz;
126 vu_int32_t rhw_spdifin_stat;
127 #define rhw_spdifout_stat rhw_spdifin_stat;
128
129 /* 0x18 */
130 vu_int16_t rhw_capt_fifosz;
131 vu_int16_t rhw_version;
132 vu_int16_t rhw_dummy1;
133 vu_int8_t rhw_firmwareload;
134 /* 0x1F */
135 vu_int8_t rhw_dummy2[66 - 31];
136 /* 0x42 */
137 vu_int16_t rhw_reset;
138 } /* __attribute__((packed)) */;
139
140 #define REPSTATUS_PLAY 0x0001
141 #define REPSTATUS_RECORD 0x0002
142 #define REPSTATUS_PLAYFIFORST 0x0004
143 #define REPSTATUS_RECFIFORST 0x0008
144
145 #define REPSTATUS_REGSENDBUSY 0x0010
146 #define REPSTATUS_LOOPBACK 0x0020
147 #define REPSTATUS_ENSPDIFIN 0x0040
148 #define REPSTATUS_ENSPDIFOUT 0x0080
149
150 #define REPSTATUS_CODECRESET 0x0200
151 #define REPSTATUS_SPDIFOUT24 0x0400
152 #define REPSTATUS_SPDIFIN24 0x0800
153
154 #define REPSTATUS_RECIRQENABLE 0x1000
155 #define REPSTATUS_RECIRQACK 0x2000
156 #define REPSTATUS_PLAYIRQENABLE 0x4000
157 #define REPSTATUS_PLAYIRQACK 0x8000
158
159 #define REPFIFO_PLAYFIFOFULL 0x0001
160 #define REPFIFO_PLAYFIFOEMPTY 0x0002
161 #define REPFIFO_RECFIFOFULL 0x0004
162 #define REPFIFO_RECFIFOEMPTY 0x0008
163 #define REPFIFO_PLAYFIFOGAUGE(x) ((x << 4) & 0xf000)
164 #define REPFIFO_RECFIFOGAUGE(x) (x & 0xf000)
165
166 /* AmigaDOS Delay() ticks */
167
168 #define USECPERTICK (1000000/50)
169
170 /* NetBSD device attachment */
171
172 struct repulse_softc {
173 device_t sc_dev;
174 struct isr sc_isr;
175 struct ac97_host_if sc_achost;
176 struct ac97_codec_if *sc_codec_if;
177
178 struct repulse_hw *sc_boardp;
179
180 void (*sc_captmore)(void *);
181 void *sc_captarg;
182
183 void *sc_captbuf;
184 int sc_captscale;
185 int sc_captbufsz;
186 unsigned sc_captflags;
187
188
189 void (*sc_playmore)(void *);
190 void *sc_playarg;
191 int sc_playscale;
192 unsigned sc_playflags;
193
194 kmutex_t sc_lock;
195 kmutex_t sc_intr_lock;
196 };
197
198 const struct audio_format repulse_format = {
199 .mode = AUMODE_PLAY | AUMODE_RECORD,
200 .encoding = AUDIO_ENCODING_SLINEAR_BE,
201 .validbits = 16,
202 .precision = 16,
203 .channels = 2,
204 .channel_mask = AUFMT_STEREO,
205 .frequency_type = 6,
206 .frequency = { 8000, 16000, 22050, 32000, 44100, 48000 },
207 };
208
209 int repulse_match (device_t, cfdata_t, void *);
210 void repulse_attach (device_t, device_t, void *);
211
212 CFATTACH_DECL_NEW(repulse, sizeof(struct repulse_softc),
213 repulse_match, repulse_attach, NULL, NULL);
214
215 int
repulse_match(device_t parent,cfdata_t cf,void * aux)216 repulse_match(device_t parent, cfdata_t cf, void *aux)
217 {
218 struct zbus_args *zap;
219
220 zap = aux;
221
222 if (zap->manid != 0x4144)
223 return (0);
224
225 if (zap->prodid != 0)
226 return (0);
227
228 return (1);
229 }
230
231 void
repulse_attach(device_t parent,device_t self,void * aux)232 repulse_attach(device_t parent, device_t self, void *aux)
233 {
234 struct repulse_softc *sc;
235 struct zbus_args *zap;
236 struct repulse_hw *bp;
237 const uint8_t *fwp;
238 int needs_firmware;
239 uint16_t a;
240
241 sc = device_private(self);
242 sc->sc_dev = self;
243 zap = aux;
244 bp = (struct repulse_hw *)zap->va;
245 sc->sc_boardp = bp;
246 sc->sc_playscale = 4;
247 sc->sc_captscale = 4;
248
249 needs_firmware = 0;
250 if (bp->rhw_fifostatus & 0x00f0)
251 needs_firmware = 1;
252 else {
253 bp->rhw_status = 0x000c;
254 if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a)
255 needs_firmware = 1;
256 }
257
258 printf(": ");
259 if (needs_firmware) {
260 printf("loading ");
261 bp->rhw_reset = 0;
262
263 delay(1 * USECPERTICK);
264
265 for (fwp = (const uint8_t *)repulse_firmware;
266 fwp < (repulse_firmware_size +
267 (const uint8_t *)repulse_firmware); fwp++)
268 bp->rhw_firmwareload = *fwp;
269
270 delay(1 * USECPERTICK);
271
272 if (bp->rhw_fifostatus & 0x00f0)
273 goto Initerr;
274
275 a = /* bp->rhw_status;
276 a |= */ REPSTATUS_CODECRESET;
277 bp->rhw_status = a;
278
279 a = bp->rhw_status;
280 if ((a & REPSTATUS_CODECRESET) == 0)
281 goto Initerr;
282
283 (void)bp->rhw_status;
284 (void)bp->rhw_status;
285 (void)bp->rhw_status;
286 a = bp->rhw_status;
287 a &= ~REPSTATUS_CODECRESET;
288 bp->rhw_status = a;
289 }
290
291 printf("firmware version 0x%x\n", bp->rhw_version);
292
293 sc->sc_achost.arg = sc;
294
295 sc->sc_achost.reset = repac_reset;
296 sc->sc_achost.read = repac_read;
297 sc->sc_achost.write = repac_write;
298 sc->sc_achost.attach = repac_attach;
299 sc->sc_achost.flags = 0;
300
301 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
302 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
303
304 if (ac97_attach(&sc->sc_achost, self, &sc->sc_lock)) {
305 printf("%s: error attaching codec\n", device_xname(self));
306 return;
307 }
308
309 #ifdef DIAGNOSTIC
310 /*
311 * Print a warning if the codec doesn't support hardware variable
312 * rate audio. As the initial incarnations of the Repulse board
313 * are AC'97 2.1, it is expected that we'll always have VRA.
314 */
315 /*
316 * XXX this should be a panic(). OTOH, audio codec speed is not
317 * important enough to do this.
318 */
319 a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if);
320 if (!(a & AC97_EXT_AUDIO_VRA)) {
321 printf("%s: warning: codec doesn't support "
322 "hardware AC'97 2.0 Variable Rate Audio\n",
323 device_xname(self));
324 }
325 #endif
326
327 sc->sc_isr.isr_ipl = 2;
328 sc->sc_isr.isr_arg = sc;
329 sc->sc_isr.isr_intr = rep_intr;
330 add_isr(&sc->sc_isr);
331
332 audio_attach_mi(&rep_hw_if, sc, self);
333
334 return;
335
336 Initerr:
337 printf("\n%s: firmware not successfully loaded\n", device_xname(self));
338 return;
339
340 }
341
342 int
repac_reset(void * arg)343 repac_reset(void *arg)
344 {
345 struct repulse_softc *sc;
346 struct repulse_hw *bp;
347 uint16_t a;
348
349 sc = arg;
350 bp = sc->sc_boardp;
351 a = bp->rhw_status;
352 a |= REPSTATUS_CODECRESET;
353 bp->rhw_status = a;
354
355 a = bp->rhw_status;
356 #ifdef DIAGNOSTIC
357 if ((a & REPSTATUS_CODECRESET) == 0)
358 panic("%s: cannot set reset bit", device_xname(sc->sc_dev));
359 #endif
360
361 a = bp->rhw_status;
362 a = bp->rhw_status;
363 a = bp->rhw_status;
364 a = bp->rhw_status;
365 a &= ~REPSTATUS_CODECRESET;
366 bp->rhw_status = a;
367 return 0;
368 }
369
370 int
repac_read(void * arg,u_int8_t reg,u_int16_t * valuep)371 repac_read(void *arg, u_int8_t reg, u_int16_t *valuep)
372 {
373 struct repulse_softc *sc;
374 struct repulse_hw *bp;
375
376 sc = arg;
377 bp = sc->sc_boardp;
378 while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
379 continue;
380 bp->rhw_reg_address = (reg & 0x7F) | 0x80;
381
382 while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
383 continue;
384
385 *valuep = bp->rhw_reg_data;
386
387 return 0;
388 }
389
390 int
repac_write(void * arg,uint8_t reg,uint16_t value)391 repac_write(void *arg, uint8_t reg, uint16_t value)
392 {
393 struct repulse_softc *sc;
394 struct repulse_hw *bp;
395
396 sc = arg;
397 bp = sc->sc_boardp;
398 bp->rhw_reg_data = value;
399 bp->rhw_reg_address = reg & 0x7F;
400
401 while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
402 continue;
403
404 return 0;
405 }
406
407 int
repac_attach(void * arg,struct ac97_codec_if * acip)408 repac_attach(void *arg, struct ac97_codec_if *acip)
409 {
410 struct repulse_softc *sc;
411
412 sc = arg;
413 sc->sc_codec_if = acip;
414
415 return 0;
416 }
417
418 /* audio(9) support stuff which is not ac97-constant */
419
420 int
rep_getdev(void * arg,struct audio_device * retp)421 rep_getdev(void *arg, struct audio_device *retp)
422 {
423 struct repulse_softc *sc;
424 struct repulse_hw *bp;
425
426 if (retp != NULL) {
427 sc = arg;
428 bp = sc->sc_boardp;
429 strncpy(retp->name, "Repulse", sizeof(retp->name));
430 snprintf(retp->version, sizeof(retp->version), "0x%x",
431 bp->rhw_version);
432 strncpy(retp->config, "", sizeof(retp->config));
433 }
434
435 return 0;
436 }
437
438 int
rep_get_props(void * v)439 rep_get_props(void *v)
440 {
441
442 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
443 AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
444 }
445
446 int
rep_halt_output(void * arg)447 rep_halt_output(void *arg)
448 {
449 struct repulse_softc *sc;
450 struct repulse_hw *bp;
451
452 sc = arg;
453 bp = sc->sc_boardp;
454 bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY);
455
456
457 return 0;
458 }
459
460 int
rep_halt_input(void * arg)461 rep_halt_input(void *arg)
462 {
463 struct repulse_softc *sc;
464 struct repulse_hw *bp;
465
466 sc = arg;
467 bp = sc->sc_boardp;
468 bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD);
469
470 return 0;
471 }
472
473 int
rep_query_format(void * arg,audio_format_query_t * afp)474 rep_query_format(void *arg, audio_format_query_t *afp)
475 {
476
477 return audio_query_format(&repulse_format, 1, afp);
478 }
479
480 /*
481 * XXX the following three functions need to be enhanced for the FPGA s/pdif
482 * mode. Generic ac97 versions for now.
483 */
484
485 int
rep_get_port(void * arg,mixer_ctrl_t * cp)486 rep_get_port(void *arg, mixer_ctrl_t *cp)
487 {
488 struct repulse_softc *sc;
489
490 sc = arg;
491 return sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp);
492 }
493
494 int
rep_set_port(void * arg,mixer_ctrl_t * cp)495 rep_set_port(void *arg, mixer_ctrl_t *cp)
496 {
497 struct repulse_softc *sc;
498
499 sc = arg;
500 return sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp);
501 }
502
503 int
rep_query_devinfo(void * arg,mixer_devinfo_t * dip)504 rep_query_devinfo(void *arg, mixer_devinfo_t *dip)
505 {
506 struct repulse_softc *sc;
507
508 sc = arg;
509 return sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip);
510 }
511
512 int
rep_round_blocksize(void * arg,int blk,int mode,const audio_params_t * param)513 rep_round_blocksize(void *arg, int blk, int mode, const audio_params_t *param)
514 {
515 int b1;
516
517 b1 = (blk & -32);
518
519 if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */)
520 b1 = 65536 / 2 / 2 / 4;
521 return b1;
522 }
523
524 void
rep_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)525 rep_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
526 {
527 struct repulse_softc *sc = opaque;
528
529 *intr = &sc->sc_intr_lock;
530 *thread = &sc->sc_lock;
531 }
532
533
534 int
rep_set_format(void * addr,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)535 rep_set_format(void *addr, int setmode,
536 const audio_params_t *play, const audio_params_t *rec,
537 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
538 {
539 struct repulse_softc *sc;
540
541 sc = addr;
542 /* XXX 96kHz */
543 if ((setmode & AUMODE_PLAY)) {
544 repac_write(sc, AC97_REG_PCM_FRONT_DAC_RATE, play->sample_rate);
545 }
546 if ((setmode & AUMODE_RECORD)) {
547 repac_write(sc, AC97_REG_PCM_LR_ADC_RATE, rec->sample_rate);
548 }
549
550 return 0;
551 }
552
553
554 int
rep_start_output(void * addr,void * block,int blksize,void (* intr)(void *),void * intrarg)555 rep_start_output(void *addr, void *block, int blksize,
556 void (*intr)(void*), void *intrarg)
557 {
558 struct repulse_softc *sc;
559 struct repulse_hw *bp;
560 uint16_t status;
561
562
563 sc = addr;
564 bp = sc->sc_boardp;
565
566 /* TODO: prepare hw if necessary */
567 status = bp->rhw_status;
568 if (!(status & REPSTATUS_PLAY))
569 bp->rhw_status = status |
570 REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST;
571
572 /* copy data */
573 uint16_t *q = block;
574 int length = blksize;
575 while (length > 0) {
576 bp->rhw_fifo_lh = *q++;
577 bp->rhw_fifo_rh = *q++;
578 length -= 4;
579 }
580
581 /* TODO: set hw if necessary */
582 if (intr) {
583 bp->rhw_status |= REPSTATUS_PLAYIRQENABLE;
584 bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2;
585 /* /2: give us time to return on the first call */
586 }
587
588 /* save callback function */
589 sc->sc_playarg = intrarg;
590 sc->sc_playmore = intr;
591
592 return 0;
593 }
594
595 int
rep_start_input(void * addr,void * block,int blksize,void (* intr)(void *),void * intrarg)596 rep_start_input(void *addr, void *block, int blksize,
597 void (*intr)(void*), void *intrarg)
598 {
599 struct repulse_softc *sc;
600 struct repulse_hw *bp;
601 uint16_t status;
602
603 sc = addr;
604 bp = sc->sc_boardp;
605
606 sc->sc_captbuf = block;
607 sc->sc_captbufsz = blksize;
608 sc->sc_captarg = intrarg;
609 sc->sc_captmore = intr;
610
611 status = bp->rhw_status;
612 if (!(status & REPSTATUS_RECORD))
613 bp->rhw_status = status | REPSTATUS_RECORD
614 | REPSTATUS_RECFIFORST;
615
616 bp->rhw_status |= REPSTATUS_RECIRQENABLE;
617 bp->rhw_capt_fifosz = blksize / sc->sc_captscale;
618
619 return 0;
620 }
621
622 /* irq handler */
623
624 int
rep_intr(void * tag)625 rep_intr(void *tag)
626 {
627 struct repulse_softc *sc;
628 struct repulse_hw *bp;
629 int foundone;
630 uint16_t status;
631
632 foundone = 0;
633
634 sc = tag;
635
636 mutex_spin_enter(&sc->sc_intr_lock);
637
638 bp = sc->sc_boardp;
639 status = bp->rhw_status;
640
641 if (status & REPSTATUS_PLAYIRQACK) {
642 foundone = 1;
643 status &= ~REPSTATUS_PLAYIRQENABLE;
644 bp->rhw_status = status;
645 (*sc->sc_playmore)(sc->sc_playarg);
646 }
647
648 if (status & REPSTATUS_RECIRQACK) {
649 foundone = 1;
650 status &= ~REPSTATUS_RECIRQENABLE;
651 bp->rhw_status = status;
652 uint16_t *q = sc->sc_captbuf;
653 int length = sc->sc_captbufsz;
654 while (length > 0) {
655 *q++ = bp->rhw_fifo_lh;
656 *q++ = bp->rhw_fifo_rh;
657 length -= 4;
658 }
659 (*sc->sc_captmore)(sc->sc_captarg);
660 }
661
662 mutex_spin_exit(&sc->sc_intr_lock);
663
664 return foundone;
665 }
666