mpc5200_ac97.c revision 1.2 1 /* $NetBSD: mpc5200_ac97.c,v 1.2 2026/07/01 00:03:18 rkujawa Exp $ */
2
3 /*-
4 * Copyright (c) 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Radoslaw Kujawa.
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 /*
33 * Driver for the Freescale MPC5200B on-chip AC97 controller (a PSC in AC97
34 * mode), written with the assumption that it's connected to a codec that
35 * can be driver through the ac97(4) interface.
36 */
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: mpc5200_ac97.c,v 1.2 2026/07/01 00:03:18 rkujawa Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/bus.h>
45 #include <sys/mutex.h>
46 #include <sys/audioio.h>
47
48 #include <dev/audio/audio_if.h>
49
50 #include <dev/ic/ac97var.h>
51
52 #include <dev/ofw/openfirm.h>
53
54 #include <machine/intr.h>
55
56 #include <powerpc/mpc5200/obiovar.h>
57 #include <powerpc/mpc5200/mpc5200reg.h>
58 #include <powerpc/mpc5200/pscreg.h>
59 #include <powerpc/mpc5200/mpc5200_ac97reg.h>
60 #include <powerpc/mpc5200/mpc5200_ac97var.h>
61 #include <powerpc/mpc5200/bestcommvar.h>
62
63 /*
64 * BestComm wiring for the PSC2 transmit path.
65 */
66 #define MPCAC97_TX_TASK 12
67 #define MPCAC97_TX_INITIATOR 12
68 #define MPCAC97_TX_PRIO 5 /* SDMA initiator priority (0-7) */
69 #define AC97_FIFO_TX PSC_TB /* DMA target: Tx Buffer (0x0c) */
70
71 #define MPCAC97_RATE 48000 /* AC97 fixed sample rate */
72 #define MPCAC97_FRAME_SIZE 8 /* 2 ch * 32-bit slot word */
73 #define MPCAC97_MAX_NBLK 256 /* SDMA descriptor-ring sanity cap */
74
75 /*
76 * Tx FIFO "almost empty" threshold
77 */
78 #define MPCAC97_TFALARM 0x100
79 #define MPCAC97_TFGRAN 0x04
80
81 /* Codec register-access poll limits. */
82 #define AC97_CMD_TIMEOUT 1000 /* x10us = 10ms for a command frame */
83 #define AC97_READY_TIMEOUT 1000 /* x1ms = 1s for codec power-up */
84
85 #define AC97_REG_POWERDOWN 0x26 /* powerdown ctrl/status (AC97_REG_POWER) */
86 #define AC97_POWER_READY 0x000f /* ADC+DAC+ANL+REF ready bits */
87
88 static int mpcac97_match(device_t, cfdata_t, void *);
89 static void mpcac97_attach(device_t, device_t, void *);
90
91 static void mpcac97_init_psc(struct mpcac97_softc *);
92 static int mpcac97_intr(void *);
93
94 /* ac97(4) host interface. */
95 static int mpcac97_ac97_attach(void *, struct ac97_codec_if *);
96 static int mpcac97_ac97_read(void *, uint8_t, uint16_t *);
97 static int mpcac97_ac97_write(void *, uint8_t, uint16_t);
98 static int mpcac97_ac97_reset(void *);
99 static enum ac97_host_flags mpcac97_ac97_flags(void *);
100
101 /* audio(4) hardware interface. */
102 static int mpcac97_open(void *, int);
103 static void mpcac97_close(void *);
104 static int mpcac97_query_format(void *, audio_format_query_t *);
105 static int mpcac97_set_format(void *, int, const audio_params_t *,
106 const audio_params_t *, audio_filter_reg_t *,
107 audio_filter_reg_t *);
108 static int mpcac97_round_blocksize(void *, int, int,
109 const audio_params_t *);
110 static size_t mpcac97_round_buffersize(void *, int, size_t);
111 static void *mpcac97_allocm(void *, int, size_t);
112 static void mpcac97_freem(void *, void *, size_t);
113 static int mpcac97_get_props(void *);
114 static int mpcac97_trigger_output(void *, void *, void *, int,
115 void (*)(void *), void *, const audio_params_t *);
116 static int mpcac97_halt_output(void *);
117 static int mpcac97_getdev(void *, struct audio_device *);
118 static int mpcac97_set_port(void *, mixer_ctrl_t *);
119 static int mpcac97_get_port(void *, mixer_ctrl_t *);
120 static int mpcac97_query_devinfo(void *, mixer_devinfo_t *);
121 static void mpcac97_get_locks(void *, kmutex_t **, kmutex_t **);
122
123 CFATTACH_DECL_NEW(mpcaudio, sizeof(struct mpcac97_softc),
124 mpcac97_match, mpcac97_attach, NULL, NULL);
125
126 static const struct bestcomm_bd_layout mpcac97_tx_layout =
127 BESTCOMM_LAYOUT_GEN_TX;
128
129 /*
130 * Data-routing-descriptor offsets
131 */
132 static const uint16_t mpcac97_tx_drd[] = {
133 0x04, 0x0c, 0x10, 0x14, 0x20, 0x28, 0x30, 0x34
134 };
135
136 static const struct audio_format mpcac97_formats[] = {
137 {
138 .mode = AUMODE_PLAY,
139 .encoding = AUDIO_ENCODING_SLINEAR_BE,
140 .validbits = 16,
141 .precision = 32,
142 .channels = 2,
143 .channel_mask = AUFMT_STEREO,
144 .frequency_type = 1,
145 .frequency = { MPCAC97_RATE },
146 },
147 };
148
149 static const struct audio_hw_if mpcac97_hw_if = {
150 .open = mpcac97_open,
151 .close = mpcac97_close,
152 .query_format = mpcac97_query_format,
153 .set_format = mpcac97_set_format,
154 .round_blocksize = mpcac97_round_blocksize,
155 .round_buffersize = mpcac97_round_buffersize,
156 .allocm = mpcac97_allocm,
157 .freem = mpcac97_freem,
158 .get_props = mpcac97_get_props,
159 .trigger_output = mpcac97_trigger_output,
160 .halt_output = mpcac97_halt_output,
161 .getdev = mpcac97_getdev,
162 .set_port = mpcac97_set_port,
163 .get_port = mpcac97_get_port,
164 .query_devinfo = mpcac97_query_devinfo,
165 .get_locks = mpcac97_get_locks,
166 };
167
168 /* Register accessors over the big-endian SoC bus_space tag. */
169 static inline uint8_t
170 RD1(struct mpcac97_softc *sc, bus_size_t o)
171 {
172 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, o);
173 }
174 static inline uint16_t
175 RD2(struct mpcac97_softc *sc, bus_size_t o)
176 {
177 return bus_space_read_2(sc->sc_iot, sc->sc_ioh, o);
178 }
179 static inline uint32_t
180 RD4(struct mpcac97_softc *sc, bus_size_t o)
181 {
182 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, o);
183 }
184 static inline void
185 WR1(struct mpcac97_softc *sc, bus_size_t o, uint8_t v)
186 {
187 bus_space_write_1(sc->sc_iot, sc->sc_ioh, o, v);
188 }
189 static inline void
190 WR2(struct mpcac97_softc *sc, bus_size_t o, uint16_t v)
191 {
192 bus_space_write_2(sc->sc_iot, sc->sc_ioh, o, v);
193 }
194 static inline void
195 WR4(struct mpcac97_softc *sc, bus_size_t o, uint32_t v)
196 {
197 bus_space_write_4(sc->sc_iot, sc->sc_ioh, o, v);
198 }
199
200 static int
201 mpcac97_match(device_t parent, cfdata_t cf, void *aux)
202 {
203 struct obio_attach_args *oba = aux;
204 char compat[40];
205 int len;
206
207 if (strcmp(oba->obio_name, "sound") == 0)
208 return 1;
209
210 len = OF_getprop(oba->obio_node, "compatible", compat, sizeof(compat));
211 if (len > 0 &&
212 (strcmp(compat, "mpc5200-psc-ac97") == 0 ||
213 strcmp(compat, "mpc5200b-psc-ac97") == 0))
214 return 1;
215
216 return 0;
217 }
218
219 static void
220 mpcac97_attach(device_t parent, device_t self, void *aux)
221 {
222 struct mpcac97_softc *sc = device_private(self);
223 struct obio_attach_args *oba = aux;
224 bus_addr_t addr;
225 bus_size_t size;
226
227 sc->sc_dev = self;
228 sc->sc_iot = oba->obio_bst;
229 sc->sc_dmat = oba->obio_dmat;
230
231 aprint_naive("\n");
232 aprint_normal(": MPC5200 AC97 controller\n");
233
234 if (!bestcomm_available()) {
235 aprint_error_dev(self, "BestComm SDMA not available\n");
236 return;
237 }
238
239 /*
240 * XXX: we should not depend on the firmware here?
241 */
242 addr = oba->obio_addr != 0 ? oba->obio_addr :
243 (MPC5200_MBAR_DEFAULT + MPC5200_REG_PSC2);
244 size = oba->obio_size != 0 ? oba->obio_size : MPC5200_PSC_SIZE;
245 sc->sc_pa = addr;
246 if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
247 aprint_error_dev(self, "can't map registers\n");
248 return;
249 }
250
251 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
252 /*
253 * XXX: The hardware interrupt lock is at IPL_NET, not usual IPL_AUDIO!
254 * The completion interrupt is a leaf on the shared BestComm SDMA cascade
255 * (along with FEC), and every leaf on that cascade MUST run at the
256 * cascade's own IPL!!! Otherwise, enjoy hard lockups.
257 */
258 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NET);
259
260 /* Put PSC2 into normal AC97 mode and enable the transceiver. */
261 mpcac97_init_psc(sc);
262
263 /*
264 * Attach the codec-independent AC97 layer.
265 */
266 sc->sc_host_if.arg = sc;
267 sc->sc_host_if.attach = mpcac97_ac97_attach;
268 sc->sc_host_if.read = mpcac97_ac97_read;
269 sc->sc_host_if.write = mpcac97_ac97_write;
270 sc->sc_host_if.reset = mpcac97_ac97_reset;
271 sc->sc_host_if.flags = mpcac97_ac97_flags;
272 aprint_debug_dev(self, "calling ac97_attach\n");
273 if (ac97_attach(&sc->sc_host_if, self, &sc->sc_lock) != 0) {
274 aprint_error_dev(self, "no AC97 codec found\n");
275 goto fail;
276 }
277 aprint_debug_dev(self, "ac97_attach returned ok\n");
278
279 /*
280 * The codec's SPDIF control is lock-gated
281 */
282 mutex_enter(&sc->sc_lock);
283 sc->sc_codec->vtbl->unlock(sc->sc_codec);
284 mutex_exit(&sc->sc_lock);
285 aprint_debug_dev(self, "spdif unlocked\n");
286
287 /*
288 * Route the BestComm transmit-task completion event to our block
289 * handler.
290 */
291 sc->sc_txih = intr_establish(bestcomm_task_irq(MPCAC97_TX_TASK),
292 IST_LEVEL, IPL_NET, mpcac97_intr, sc);
293 if (sc->sc_txih == NULL) {
294 aprint_error_dev(self, "can't establish SDMA interrupt\n");
295 goto fail;
296 }
297 aprint_debug_dev(self, "SDMA intr established\n");
298
299 sc->sc_audiodev = audio_attach_mi(&mpcac97_hw_if, sc, self);
300 aprint_debug_dev(self, "audio_attach_mi done\n");
301 return;
302
303 fail:
304 if (sc->sc_txih != NULL)
305 intr_disestablish(sc->sc_txih);
306 mutex_destroy(&sc->sc_lock);
307 mutex_destroy(&sc->sc_intr_lock);
308 bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
309 }
310
311 /*
312 * Configure PSC2 for "normal" AC97 mode.
313 */
314 static void
315 mpcac97_init_psc(struct mpcac97_softc *sc)
316 {
317
318 WR1(sc, PSC_CR, CMD_RESET_RX);
319 WR1(sc, PSC_CR, CMD_RESET_TX);
320 WR1(sc, PSC_CR, CMD_RESET_ERR);
321 WR1(sc, PSC_CR, CMD_RESET_MR);
322
323 WR4(sc, PSC_SICR, SICR_NORMAL_AC97);
324 WR4(sc, PSC_AC97SLOTS, AC97SLOTS_TX_STEREO);
325
326 WR2(sc, PSC_TFALARM, MPCAC97_TFALARM);
327 WR1(sc, PSC_TFCNTL, MPCAC97_TFGRAN);
328
329 WR2(sc, PSC_IMR, 0); /* no PSC interrupts; BestComm drives playback */
330 }
331
332 /*
333 * BestComm transmit-task completion handler.
334 */
335 static int
336 mpcac97_intr(void *arg)
337 {
338 struct mpcac97_softc *sc = arg;
339 int handled = 0;
340
341 mutex_enter(&sc->sc_intr_lock);
342 if (!sc->sc_txon) {
343 mutex_exit(&sc->sc_intr_lock);
344 return 0;
345 }
346
347 while ((bestcomm_bd_status(&sc->sc_txring, sc->sc_curblk) &
348 BESTCOMM_BD_READY) == 0) {
349 u_int blk = sc->sc_curblk;
350
351 bestcomm_bd_post(&sc->sc_txring, blk,
352 sc->sc_blkbase + blk * sc->sc_blksize, sc->sc_blksize, 0);
353 sc->sc_curblk = (blk + 1) % sc->sc_nblk;
354 if (sc->sc_pintr != NULL)
355 (*sc->sc_pintr)(sc->sc_pintrarg);
356 handled = 1;
357 }
358
359 /* Restart the task if it ran the ring dry between completions. */
360 if (handled)
361 bestcomm_task_start(MPCAC97_TX_TASK);
362
363 mutex_exit(&sc->sc_intr_lock);
364 return handled;
365 }
366
367 /*
368 * ac97(4) host interface.
369 */
370 static int
371 mpcac97_ac97_attach(void *arg, struct ac97_codec_if *codec)
372 {
373 struct mpcac97_softc *sc = arg;
374
375 sc->sc_codec = codec;
376 return 0;
377 }
378
379 static int
380 mpcac97_ac97_read(void *arg, uint8_t reg, uint16_t *valp)
381 {
382 struct mpcac97_softc *sc = arg;
383 int i;
384
385 /* Drop any stale response, then issue the read command. */
386 (void)RD4(sc, PSC_AC97DATA);
387 WR4(sc, PSC_AC97CMD, AC97CMD_READ | AC97CMD_INDEX(reg));
388
389 for (i = 0; i < AC97_CMD_TIMEOUT; i++) {
390 if (RD2(sc, PSC_SR) & AC97_SR_DATA_VALID) {
391 *valp = AC97DATA_VALUE(RD4(sc, PSC_AC97DATA));
392 return 0;
393 }
394 DELAY(10);
395 }
396
397 *valp = 0xffff;
398 return ETIMEDOUT;
399 }
400
401 static int
402 mpcac97_ac97_write(void *arg, uint8_t reg, uint16_t val)
403 {
404 struct mpcac97_softc *sc = arg;
405 int i;
406
407 WR4(sc, PSC_AC97CMD, AC97CMD_WRITE(reg, val));
408
409 /* CMD_SEND is set by the write and cleared once the frame is sent. */
410 for (i = 0; i < AC97_CMD_TIMEOUT; i++) {
411 if ((RD2(sc, PSC_SR) & AC97_SR_CMD_SEND) == 0)
412 return 0;
413 DELAY(10);
414 }
415
416 return ETIMEDOUT;
417 }
418
419 static int
420 mpcac97_ac97_reset(void *arg)
421 {
422 struct mpcac97_softc *sc = arg;
423 uint16_t val = 0xffff;
424 int i, rderr = ETIMEDOUT;
425
426 /*
427 * Cold-reset the external codec by pulsing RES low then high
428 */
429 WR1(sc, PSC_CR, CMD_TX_DISABLE | CMD_RX_DISABLE);
430 WR1(sc, PSC_OP1, AC97_OP_RES); /* RES -> low */
431 DELAY(1000);
432 WR1(sc, PSC_OP0, AC97_OP_RES); /* RES -> high */
433 DELAY(1000);
434 WR1(sc, PSC_CR, CMD_TX_ENABLE | CMD_RX_ENABLE);
435
436 /* Wait for the codec's analog/digital subsections to power up. */
437 for (i = 0; i < AC97_READY_TIMEOUT; i++) {
438 rderr = mpcac97_ac97_read(sc, AC97_REG_POWERDOWN, &val);
439 if (rderr == 0 &&
440 (val & AC97_POWER_READY) == AC97_POWER_READY) {
441 aprint_debug_dev(sc->sc_dev,
442 "codec ready after ~%dms (powerdown=0x%04x)\n",
443 i, val);
444 return 0;
445 }
446 DELAY(1000);
447 }
448
449 /*
450 * Rare, not-reliably-reproducible cold-boot race: the STAC9766 is
451 * occasionally not ready within AC97_READY_TIMEOUT. Try to hunt this
452 * down.
453 */
454 aprint_error_dev(sc->sc_dev,
455 "codec not ready after reset: last read %s, powerdown=0x%04x, "
456 "sr=0x%04x\n",
457 rderr ? "TIMED OUT (AC-link silent)" : "ok-but-not-ready",
458 val, RD2(sc, PSC_SR));
459 return 0; /* best effort: let ac97_attach probe the codec ID */
460 }
461
462 static enum ac97_host_flags
463 mpcac97_ac97_flags(void *arg)
464 {
465
466 return 0;
467 }
468
469 /*
470 * audio(4) hardware interface.
471 */
472 static int
473 mpcac97_open(void *v, int flags)
474 {
475 struct mpcac97_softc *sc = v;
476
477 /*
478 * Lock the codec's SPDIF control for the duration the device is open
479 */
480 sc->sc_codec->vtbl->lock(sc->sc_codec);
481 return 0;
482 }
483
484 static void
485 mpcac97_close(void *v)
486 {
487 struct mpcac97_softc *sc = v;
488
489 sc->sc_codec->vtbl->unlock(sc->sc_codec);
490 }
491
492 static int
493 mpcac97_query_format(void *v, audio_format_query_t *afp)
494 {
495
496 return audio_query_format(mpcac97_formats,
497 __arraycount(mpcac97_formats), afp);
498 }
499
500 static int
501 mpcac97_set_format(void *v, int setmode,
502 const audio_params_t *play, const audio_params_t *rec,
503 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
504 {
505
506 /*
507 * Only the single advertised hardware format is offered
508 */
509 return 0;
510 }
511
512 static int
513 mpcac97_round_blocksize(void *v, int bs, int mode, const audio_params_t *p)
514 {
515
516 /* Keep a block a whole number of 8-byte stereo DMA frames. */
517 bs &= ~(MPCAC97_FRAME_SIZE - 1);
518 if (bs < MPCAC97_FRAME_SIZE)
519 bs = MPCAC97_FRAME_SIZE;
520 return bs;
521 }
522
523 static size_t
524 mpcac97_round_buffersize(void *v, int mode, size_t size)
525 {
526
527 /*
528 * The audio layer sizes the hardware buffer as a whole number of
529 * blocks; keep it intact.
530 */
531 return size;
532 }
533
534 static void *
535 mpcac97_allocm(void *v, int direction, size_t size)
536 {
537 struct mpcac97_softc *sc = v;
538 int rseg;
539
540 if (direction != AUMODE_PLAY) /* playback only */
541 return NULL;
542 if (sc->sc_dmakva != NULL) /* one play buffer */
543 return NULL;
544
545 if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_dmaseg,
546 1, &rseg, BUS_DMA_WAITOK) != 0)
547 return NULL;
548 sc->sc_dmanseg = rseg;
549 if (bus_dmamem_map(sc->sc_dmat, &sc->sc_dmaseg, rseg, size,
550 &sc->sc_dmakva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
551 goto free;
552 if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_WAITOK,
553 &sc->sc_dmamap) != 0)
554 goto unmap;
555 if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmakva, size,
556 NULL, BUS_DMA_WAITOK) != 0)
557 goto destroy;
558
559 sc->sc_dmaphys = sc->sc_dmamap->dm_segs[0].ds_addr;
560 sc->sc_dmasize = size;
561 return sc->sc_dmakva;
562
563 destroy:
564 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
565 sc->sc_dmamap = NULL;
566 unmap:
567 bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmakva, size);
568 sc->sc_dmakva = NULL;
569 free:
570 bus_dmamem_free(sc->sc_dmat, &sc->sc_dmaseg, rseg);
571 return NULL;
572 }
573
574 static void
575 mpcac97_freem(void *v, void *ptr, size_t size)
576 {
577 struct mpcac97_softc *sc = v;
578
579 if (ptr != sc->sc_dmakva)
580 return;
581
582 bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
583 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
584 bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmakva, size);
585 bus_dmamem_free(sc->sc_dmat, &sc->sc_dmaseg, sc->sc_dmanseg);
586 sc->sc_dmamap = NULL;
587 sc->sc_dmakva = NULL;
588 sc->sc_dmasize = 0;
589 }
590
591 static int
592 mpcac97_get_props(void *v)
593 {
594
595 return AUDIO_PROP_PLAYBACK;
596 }
597
598 static int
599 mpcac97_trigger_output(void *v, void *start, void *end, int blksize,
600 void (*intr)(void *), void *arg, const audio_params_t *param)
601 {
602 struct mpcac97_softc *sc = v;
603 bus_addr_t base;
604 u_int i, nblk;
605
606 nblk = ((char *)end - (char *)start) / blksize;
607 if (nblk == 0 || nblk > MPCAC97_MAX_NBLK)
608 return EINVAL;
609 if ((char *)start < (char *)sc->sc_dmakva ||
610 (char *)start + nblk * blksize >
611 (char *)sc->sc_dmakva + sc->sc_dmasize)
612 return EINVAL;
613
614 base = sc->sc_dmaphys + ((char *)start - (char *)sc->sc_dmakva);
615
616 /* (Re)build the BD ring: one descriptor per audio block. */
617 bestcomm_bd_teardown(&sc->sc_txring);
618 if (bestcomm_bd_setup(&sc->sc_txring, MPCAC97_TX_TASK,
619 &mpcac97_tx_layout, sc->sc_pa + AC97_FIFO_TX, nblk, blksize,
620 sizeof(uint32_t), MPCAC97_TX_INITIATOR, MPCAC97_TX_PRIO) != 0)
621 return ENOMEM;
622
623 /*
624 * GEN_TX_BD is a runtime-configurable task
625 */
626 bestcomm_task_set_initiator(MPCAC97_TX_TASK, MPCAC97_TX_INITIATOR,
627 mpcac97_tx_drd, __arraycount(mpcac97_tx_drd));
628
629 sc->sc_blkbase = base;
630 sc->sc_blksize = blksize;
631 sc->sc_nblk = nblk;
632 sc->sc_curblk = 0;
633 sc->sc_pintr = intr;
634 sc->sc_pintrarg = arg;
635 sc->sc_txon = true;
636
637 for (i = 0; i < nblk; i++)
638 bestcomm_bd_post(&sc->sc_txring, i, base + i * blksize,
639 blksize, 0);
640
641 bestcomm_task_start(MPCAC97_TX_TASK);
642 return 0;
643 }
644
645 static int
646 mpcac97_halt_output(void *v)
647 {
648 struct mpcac97_softc *sc = v;
649
650 sc->sc_txon = false;
651 sc->sc_pintr = NULL;
652 sc->sc_pintrarg = NULL;
653 bestcomm_bd_teardown(&sc->sc_txring);
654 return 0;
655 }
656
657 static int
658 mpcac97_getdev(void *v, struct audio_device *adp)
659 {
660
661 strlcpy(adp->name, "MPC5200 AC97", sizeof(adp->name));
662 strlcpy(adp->version, "", sizeof(adp->version));
663 strlcpy(adp->config, "mpcac97", sizeof(adp->config));
664 return 0;
665 }
666
667 static int
668 mpcac97_set_port(void *v, mixer_ctrl_t *mc)
669 {
670 struct mpcac97_softc *sc = v;
671
672 return sc->sc_codec->vtbl->mixer_set_port(sc->sc_codec, mc);
673 }
674
675 static int
676 mpcac97_get_port(void *v, mixer_ctrl_t *mc)
677 {
678 struct mpcac97_softc *sc = v;
679
680 return sc->sc_codec->vtbl->mixer_get_port(sc->sc_codec, mc);
681 }
682
683 static int
684 mpcac97_query_devinfo(void *v, mixer_devinfo_t *di)
685 {
686 struct mpcac97_softc *sc = v;
687
688 return sc->sc_codec->vtbl->query_devinfo(sc->sc_codec, di);
689 }
690
691 static void
692 mpcac97_get_locks(void *v, kmutex_t **intr, kmutex_t **thread)
693 {
694 struct mpcac97_softc *sc = v;
695
696 *intr = &sc->sc_intr_lock;
697 *thread = &sc->sc_lock;
698 }
699