1 /* $OpenBSD: aplmca.c,v 1.7 2023/07/26 11:09:24 kettenis Exp $ */
2 /*
3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/audioio.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/fcntl.h>
24
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_clock.h>
30 #include <dev/ofw/ofw_misc.h>
31 #include <dev/ofw/ofw_power.h>
32 #include <dev/ofw/fdt.h>
33
34 #include <dev/audio_if.h>
35
36 #include <arm64/dev/apldma.h>
37
38 /*
39 * This driver is based on preliminary device tree bindings and will
40 * almost certainly need changes once the official bindings land in
41 * mainline Linux. Support for these preliminary bindings will be
42 * dropped as soon as official bindings are available.
43 */
44
45 #define MCA_CL_STRIDE 0x4000
46 #define MCA_SW_STRIDE 0x8000
47 #define MCA_SERDES_TXA 0x0300
48
49 #define MCA_STATUS(idx) ((idx) * MCA_CL_STRIDE + 0x0000)
50 #define MCA_STATUS_MCLK_EN (1 << 0)
51 #define MCA_MCLK_CONF(idx) ((idx) * MCA_CL_STRIDE + 0x0004)
52 #define MCA_MCLK_CONF_DIV_MASK (0xf << 8)
53 #define MCA_MCLK_CONF_DIV_SHIFT 8
54
55 #define MCA_SYNCGEN_STATUS(idx) ((idx) * MCA_CL_STRIDE + 0x0100)
56 #define MCA_SYNCGEN_STATUS_EN (1 << 0)
57 #define MCA_SYNCGEN_MCLK_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0104)
58 #define MCA_SYNCGEN_HI_PERIOD(idx) ((idx) * MCA_CL_STRIDE + 0x0108)
59 #define MCA_SYNCGEN_LO_PERIOD(idx) ((idx) * MCA_CL_STRIDE + 0x010c)
60
61 #define MCA_SERDES_BASE(idx, off) ((idx) * MCA_CL_STRIDE + (off))
62 #define MCA_SERDES_STATUS(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0000)
63 #define MCA_SERDES_STATUS_EN (1 << 0)
64 #define MCA_SERDES_STATUS_RST (1 << 1)
65 #define MCA_SERDES_CONF(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0004)
66 #define MCA_SERDES_CONF_NSLOTS_MASK (0xf << 0)
67 #define MCA_SERDES_CONF_NSLOTS_SHIFT 0
68 #define MCA_SERDES_CONF_WIDTH_MASK (0x1f << 4)
69 #define MCA_SERDES_CONF_WIDTH_32BIT (0x10 << 4)
70 #define MCA_SERDES_CONF_BCLK_POL (1 << 10)
71 #define MCA_SERDES_CONF_MAGIC (0x7 << 12)
72 #define MCA_SERDES_CONF_SYNC_SEL_MASK (0x7 << 16)
73 #define MCA_SERDES_CONF_SYNC_SEL_SHIFT 16
74 #define MCA_SERDES_BITSTART(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0008)
75 #define MCA_SERDES_CHANMASK0(idx, off) (MCA_SERDES_BASE(idx, off) + 0x000c)
76 #define MCA_SERDES_CHANMASK1(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0010)
77 #define MCA_SERDES_CHANMASK2(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0014)
78 #define MCA_SERDES_CHANMASK3(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0018)
79
80 #define MCA_PORT_ENABLE(idx) ((idx) * MCA_CL_STRIDE + 0x0600)
81 #define MCA_PORT_ENABLE_CLOCKS (0x3 << 1)
82 #define MCA_PORT_ENABLE_TX_DATA (1 << 3)
83 #define MCA_PORT_CLOCK_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0604)
84 #define MCA_PORT_CLOCK_SEL_SHIFT 8
85 #define MCA_PORT_DATA_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0608)
86 #define MCA_PORT_DATA_SEL_TXA(idx) (1 << ((idx) * 2))
87 #define MCA_PORT_DATA_SEL_TXB(idx) (2 << ((idx) * 2))
88
89 #define MCA_DMA_ADAPTER_A(idx) ((idx) * MCA_SW_STRIDE + 0x0000)
90 #define MCA_DMA_ADAPTER_B(idx) ((idx) * MCA_SW_STRIDE + 0x4000)
91 #define MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT 0
92 #define MCA_DMA_ADAPTER_TX_NCHANS_SHIFT 5
93 #define MCA_DMA_ADAPTER_NCHANS_SHIFT 20
94
95
96 #define HREAD4(sc, reg) \
97 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
98 #define HWRITE4(sc, reg, val) \
99 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
100 #define HSET4(sc, reg, bits) \
101 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
102 #define HCLR4(sc, reg, bits) \
103 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
104
105 struct aplmca_dai {
106 struct aplmca_softc *ad_sc;
107 struct dai_device ad_dai;
108 int ad_cluster;
109
110 struct apldma_channel *ad_ac;
111 void *ad_pbuf;
112 };
113
114 struct aplmca_softc {
115 struct device sc_dev;
116 bus_space_tag_t sc_iot;
117 bus_space_handle_t sc_ioh;
118 bus_space_handle_t sc_sw_ioh;
119
120 int sc_node;
121 uint32_t sc_phandle;
122
123 int sc_nclusters;
124 struct aplmca_dai *sc_ad;
125 };
126
127 int aplmca_set_format(void *, uint32_t, uint32_t, uint32_t);
128 int aplmca_set_sysclk(void *, uint32_t);
129
130 int aplmca_open(void *, int);
131 int aplmca_set_params(void *, int, int,
132 struct audio_params *, struct audio_params *);
133 void *aplmca_allocm(void *, int, size_t, int, int);
134 void aplmca_freem(void *, void *, int);
135 int aplmca_trigger_output(void *, void *, void *, int,
136 void (*)(void *), void *, struct audio_params *);
137 int aplmca_trigger_input(void *, void *, void *, int,
138 void (*)(void *), void *, struct audio_params *);
139 int aplmca_halt_output(void *);
140 int aplmca_halt_input(void *);
141
142 const struct audio_hw_if aplmca_hw_if = {
143 .open = aplmca_open,
144 .set_params = aplmca_set_params,
145 .allocm = aplmca_allocm,
146 .freem = aplmca_freem,
147 .trigger_output = aplmca_trigger_output,
148 .trigger_input = aplmca_trigger_input,
149 .halt_output = aplmca_halt_output,
150 .halt_input = aplmca_halt_input,
151 };
152
153 int aplmca_match(struct device *, void *, void *);
154 void aplmca_attach(struct device *, struct device *, void *);
155 int aplmca_activate(struct device *, int);
156
157 const struct cfattach aplmca_ca = {
158 sizeof (struct aplmca_softc), aplmca_match, aplmca_attach, NULL,
159 aplmca_activate
160 };
161
162 struct cfdriver aplmca_cd = {
163 NULL, "aplmca", DV_DULL
164 };
165
166 int
aplmca_match(struct device * parent,void * match,void * aux)167 aplmca_match(struct device *parent, void *match, void *aux)
168 {
169 struct fdt_attach_args *faa = aux;
170
171 return OF_is_compatible(faa->fa_node, "apple,mca");
172 }
173
174 void
aplmca_attach(struct device * parent,struct device * self,void * aux)175 aplmca_attach(struct device *parent, struct device *self, void *aux)
176 {
177 struct aplmca_softc *sc = (struct aplmca_softc *)self;
178 struct fdt_attach_args *faa = aux;
179 int i;
180
181 if (faa->fa_nreg < 2) {
182 printf(": no registers\n");
183 return;
184 }
185
186 sc->sc_iot = faa->fa_iot;
187 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
188 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
189 printf(": can't map registers\n");
190 return;
191 }
192 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
193 faa->fa_reg[1].size, 0, &sc->sc_sw_ioh)) {
194 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
195 printf(": can't map registers\n");
196 return;
197 }
198
199 sc->sc_node = faa->fa_node;
200 sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0);
201
202 sc->sc_nclusters = OF_getpropint(faa->fa_node, "apple,nclusters", 6);
203 sc->sc_ad = mallocarray(sc->sc_nclusters, sizeof(*sc->sc_ad),
204 M_DEVBUF, M_WAITOK | M_ZERO);
205
206 for (i = 0; i < sc->sc_nclusters; i++) {
207 sc->sc_ad[i].ad_cluster = i;
208 sc->sc_ad[i].ad_sc = sc;
209 sc->sc_ad[i].ad_dai.dd_node = sc->sc_node;
210 sc->sc_ad[i].ad_dai.dd_cookie = &sc->sc_ad[i];
211 sc->sc_ad[i].ad_dai.dd_hw_if = &aplmca_hw_if;
212 sc->sc_ad[i].ad_dai.dd_set_format = aplmca_set_format;
213 sc->sc_ad[i].ad_dai.dd_set_sysclk = aplmca_set_sysclk;
214 }
215
216 printf("\n");
217
218 power_domain_enable_idx(sc->sc_node, 0);
219
220 for (i = 0; i < sc->sc_nclusters; i++) {
221 HCLR4(sc, MCA_SERDES_STATUS(i, MCA_SERDES_TXA),
222 MCA_SERDES_STATUS_EN);
223 HCLR4(sc, MCA_SYNCGEN_STATUS(i), MCA_SYNCGEN_STATUS_EN);
224 HCLR4(sc, MCA_STATUS(i), MCA_STATUS_MCLK_EN);
225 }
226 }
227
228 int
aplmca_activate(struct device * self,int act)229 aplmca_activate(struct device *self, int act)
230 {
231 struct aplmca_softc *sc = (struct aplmca_softc *)self;
232 int i;
233
234 switch (act) {
235 case DVACT_SUSPEND:
236 for (i = 0; i < sc->sc_nclusters; i++) {
237 if (sc->sc_ad[i].ad_ac)
238 power_domain_disable_idx(sc->sc_node, i + 1);
239 }
240 power_domain_disable_idx(sc->sc_node, 0);
241 break;
242 case DVACT_RESUME:
243 power_domain_enable_idx(sc->sc_node, 0);
244 for (i = 0; i < sc->sc_nclusters; i++) {
245 if (sc->sc_ad[i].ad_ac)
246 power_domain_enable_idx(sc->sc_node, i + 1);
247 }
248 break;
249 }
250
251 return 0;
252 }
253
254 int
aplmca_dai_init(struct aplmca_softc * sc,int port)255 aplmca_dai_init(struct aplmca_softc *sc, int port)
256 {
257 struct aplmca_dai *ad = &sc->sc_ad[port];
258 uint32_t conf;
259 char name[5];
260 int idx;
261
262 /* Allocate DMA channel. */
263 snprintf(name, sizeof(name), "tx%da", ad->ad_cluster);
264 idx = OF_getindex(sc->sc_node, name, "dma-names");
265 if (idx == -1)
266 return ENOENT;
267 ad->ad_ac = apldma_alloc_channel(idx);
268 if (ad->ad_ac == NULL)
269 return ENOENT;
270
271 power_domain_enable_idx(sc->sc_node, port + 1);
272
273 /* Basic SERDES configuration. */
274 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
275 conf &= ~MCA_SERDES_CONF_SYNC_SEL_MASK;
276 conf |= (ad->ad_cluster + 1) << MCA_SERDES_CONF_SYNC_SEL_SHIFT;
277 conf |= MCA_SERDES_CONF_MAGIC;
278 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
279
280 /* Output port configuration. */
281 HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
282 (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
283 HWRITE4(sc, MCA_PORT_DATA_SEL(port),
284 MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
285 HWRITE4(sc, MCA_PORT_ENABLE(port),
286 MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
287
288 return 0;
289 }
290
291 void
aplmca_dai_link(struct aplmca_softc * sc,int master,int port)292 aplmca_dai_link(struct aplmca_softc *sc, int master, int port)
293 {
294 struct aplmca_dai *ad = &sc->sc_ad[master];
295
296 HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
297 (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
298 HWRITE4(sc, MCA_PORT_DATA_SEL(port),
299 MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
300 HWRITE4(sc, MCA_PORT_ENABLE(port),
301 MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
302 }
303
304 uint32_t *
aplmca_dai_next_dai(uint32_t * cells)305 aplmca_dai_next_dai(uint32_t *cells)
306 {
307 uint32_t phandle = cells[0];
308 int node, ncells;
309
310 node = OF_getnodebyphandle(phandle);
311 if (node == 0)
312 return NULL;
313
314 ncells = OF_getpropint(node, "#sound-dai-cells", 0);
315 return cells + ncells + 1;
316 }
317
318 struct dai_device *
aplmca_alloc_cluster(int node)319 aplmca_alloc_cluster(int node)
320 {
321 struct aplmca_softc *sc = aplmca_cd.cd_devs[0];
322 uint32_t *dais;
323 uint32_t *dai;
324 uint32_t ports[2];
325 int nports = 0;
326 int len, i;
327
328 len = OF_getproplen(node, "sound-dai");
329 if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t))
330 return NULL;
331
332 dais = malloc(len, M_TEMP, M_WAITOK);
333 OF_getpropintarray(node, "sound-dai", dais, len);
334
335 dai = dais;
336 while (dai && dai < dais + (len / sizeof(uint32_t))) {
337 if (dai[0] == sc->sc_phandle && nports < nitems(ports))
338 ports[nports++] = dai[1];
339 dai = aplmca_dai_next_dai(dai);
340 }
341
342 free(dais, M_TEMP, len);
343
344 if (nports == 0)
345 return NULL;
346 for (i = 0; i < nports; i++) {
347 if (ports[i] >= sc->sc_nclusters)
348 return NULL;
349 }
350
351 if (sc->sc_ad[ports[0]].ad_ac != NULL)
352 return NULL;
353
354 /* Setup the primary cluster. */
355 if (aplmca_dai_init(sc, ports[0]))
356 return NULL;
357
358 /*
359 * Additional interfaces receive the same output as the
360 * primary interface by linking the output port to the primary
361 * cluster.
362 */
363 for (i = 1; i < nports; i++)
364 aplmca_dai_link(sc, ports[0], ports[i]);
365
366 return &sc->sc_ad[ports[0]].ad_dai;
367 }
368
369 int
aplmca_set_format(void * cookie,uint32_t fmt,uint32_t pol,uint32_t clk)370 aplmca_set_format(void *cookie, uint32_t fmt, uint32_t pol,
371 uint32_t clk)
372 {
373 struct aplmca_dai *ad = cookie;
374 struct aplmca_softc *sc = ad->ad_sc;
375 uint32_t conf;
376
377 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
378 conf &= ~MCA_SERDES_CONF_WIDTH_MASK;
379 conf |= MCA_SERDES_CONF_WIDTH_32BIT;
380
381 switch (fmt) {
382 case DAI_FORMAT_I2S:
383 conf &= ~MCA_SERDES_CONF_BCLK_POL;
384 break;
385 case DAI_FORMAT_RJ:
386 case DAI_FORMAT_LJ:
387 conf |= MCA_SERDES_CONF_BCLK_POL;
388 break;
389 default:
390 return EINVAL;
391 }
392
393 if (pol & DAI_POLARITY_IB)
394 conf ^= MCA_SERDES_CONF_BCLK_POL;
395 if (pol & DAI_POLARITY_IF)
396 return EINVAL;
397
398 if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM))
399 return EINVAL;
400
401 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
402
403 return 0;
404 }
405
406 int
aplmca_set_sysclk(void * cookie,uint32_t rate)407 aplmca_set_sysclk(void *cookie, uint32_t rate)
408 {
409 struct aplmca_dai *ad = cookie;
410 struct aplmca_softc *sc = ad->ad_sc;
411
412 return clock_set_frequency_idx(sc->sc_node, ad->ad_cluster, rate);
413 }
414
415 int
aplmca_open(void * cookie,int flags)416 aplmca_open(void *cookie, int flags)
417 {
418 if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
419 return ENXIO;
420
421 return 0;
422 }
423
424 int
aplmca_set_params(void * cookie,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)425 aplmca_set_params(void *cookie, int setmode, int usemode,
426 struct audio_params *play, struct audio_params *rec)
427 {
428 if (setmode & AUMODE_PLAY) {
429 play->sample_rate = 48000;
430 play->encoding = AUDIO_ENCODING_SLINEAR_LE;
431 play->precision = 24;
432 play->bps = 4;
433 play->msb = 0;
434 play->channels = 2;
435 }
436
437 return 0;
438 }
439
440 void *
aplmca_allocm(void * cookie,int direction,size_t size,int type,int flags)441 aplmca_allocm(void *cookie, int direction, size_t size, int type,
442 int flags)
443 {
444 struct aplmca_dai *ad = cookie;
445
446 if (direction == AUMODE_PLAY) {
447 ad->ad_pbuf = apldma_allocm(ad->ad_ac, size, flags);
448 return ad->ad_pbuf;
449 }
450
451 return malloc(size, type, flags | M_ZERO);
452 }
453
454 void
aplmca_freem(void * cookie,void * addr,int type)455 aplmca_freem(void *cookie, void *addr, int type)
456 {
457 struct aplmca_dai *ad = cookie;
458
459 if (addr == ad->ad_pbuf) {
460 apldma_freem(ad->ad_ac);
461 return;
462 }
463
464 free(addr, type, 0);
465 }
466
467 int
aplmca_trigger_output(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * params)468 aplmca_trigger_output(void *cookie, void *start, void *end, int blksize,
469 void (*intr)(void *), void *intrarg, struct audio_params *params)
470 {
471 struct aplmca_dai *ad = cookie;
472 struct aplmca_softc *sc = ad->ad_sc;
473 uint32_t conf, period;
474 int pad;
475
476 if (params->channels > 16)
477 return EINVAL;
478
479 /* Finalize SERDES configuration. */
480 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
481 conf &= ~MCA_SERDES_CONF_NSLOTS_MASK;
482 conf |= ((params->channels - 1) << MCA_SERDES_CONF_NSLOTS_SHIFT);
483 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
484 HWRITE4(sc, MCA_SERDES_CHANMASK0(ad->ad_cluster, MCA_SERDES_TXA),
485 0xffffffff);
486 HWRITE4(sc, MCA_SERDES_CHANMASK1(ad->ad_cluster, MCA_SERDES_TXA),
487 0xffffffff << params->channels);
488 HWRITE4(sc, MCA_SERDES_CHANMASK2(ad->ad_cluster, MCA_SERDES_TXA),
489 0xffffffff);
490 HWRITE4(sc, MCA_SERDES_CHANMASK3(ad->ad_cluster, MCA_SERDES_TXA),
491 0xffffffff << params->channels);
492
493 period = params->channels * 32;
494 HWRITE4(sc, MCA_SYNCGEN_HI_PERIOD(ad->ad_cluster), period - 2);
495 HWRITE4(sc, MCA_SYNCGEN_LO_PERIOD(ad->ad_cluster), 0);
496 HWRITE4(sc, MCA_MCLK_CONF(ad->ad_cluster),
497 1 << MCA_MCLK_CONF_DIV_SHIFT);
498
499 clock_enable_idx(sc->sc_node, ad->ad_cluster);
500
501 HWRITE4(sc, MCA_SYNCGEN_MCLK_SEL(ad->ad_cluster),
502 ad->ad_cluster + 1);
503
504 HSET4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
505 HSET4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
506 MCA_SYNCGEN_STATUS_EN);
507 HSET4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
508 MCA_SERDES_STATUS_EN);
509
510 pad = params->bps * 8 - params->precision;
511 bus_space_write_4(sc->sc_iot, sc->sc_sw_ioh,
512 MCA_DMA_ADAPTER_A(ad->ad_cluster),
513 pad << MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT |
514 2 << MCA_DMA_ADAPTER_TX_NCHANS_SHIFT |
515 2 << MCA_DMA_ADAPTER_NCHANS_SHIFT);
516
517 return apldma_trigger_output(ad->ad_ac, start, end, blksize,
518 intr, intrarg, params);
519 }
520
521 int
aplmca_trigger_input(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * params)522 aplmca_trigger_input(void *cookie, void *start, void *end, int blksize,
523 void (*intr)(void *), void *intrarg, struct audio_params *params)
524 {
525 printf("%s\n", __func__);
526 return EIO;
527 }
528
529 int
aplmca_halt_output(void * cookie)530 aplmca_halt_output(void *cookie)
531 {
532 struct aplmca_dai *ad = cookie;
533 struct aplmca_softc *sc = ad->ad_sc;
534 int error;
535
536 error = apldma_halt_output(ad->ad_ac);
537
538 HCLR4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
539 MCA_SERDES_STATUS_EN);
540 HCLR4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
541 MCA_SYNCGEN_STATUS_EN);
542 HCLR4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
543
544 clock_disable_idx(sc->sc_node, ad->ad_cluster);
545
546 return error;
547 }
548
549 int
aplmca_halt_input(void * cookie)550 aplmca_halt_input(void *cookie)
551 {
552 printf("%s\n", __func__);
553 return 0;
554 }
555