xref: /openbsd-src/sys/arch/arm64/dev/aplaudio.c (revision ee8269ddb4b2451f6b04c27d348abc5a38574ca8)
1 /*	$OpenBSD: aplaudio.c,v 1.6 2023/01/14 23:35:09 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4  * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30 
31 #include <sys/audioio.h>
32 #include <dev/audio_if.h>
33 
34 #include <arm64/dev/aplmca.h>
35 
36 struct aplaudio_softc {
37 	struct device		sc_dev;
38 
39 	struct dai_device	*sc_dai_cpu;
40 	struct dai_device	*sc_dai_codec[6];
41 };
42 
43 void	aplaudio_set_format(struct aplaudio_softc *, uint32_t,
44 	    uint32_t, uint32_t);
45 void	aplaudio_set_tdm_slots(struct aplaudio_softc *);
46 
47 int	aplaudio_open(void *, int);
48 void	aplaudio_close(void *);
49 int	aplaudio_set_params(void *, int, int,
50 	    struct audio_params *, struct audio_params *);
51 void	*aplaudio_allocm(void *, int, size_t, int, int);
52 void	aplaudio_freem(void *, void *, int);
53 int	aplaudio_set_port(void *, mixer_ctrl_t *);
54 int	aplaudio_get_port(void *, mixer_ctrl_t *);
55 int	aplaudio_query_devinfo(void *, mixer_devinfo_t *);
56 int	aplaudio_round_blocksize(void *, int);
57 size_t	aplaudio_round_buffersize(void *, int, size_t);
58 int	aplaudio_trigger_output(void *, void *, void *, int,
59 	    void (*)(void *), void *, struct audio_params *);
60 int	aplaudio_trigger_input(void *, void *, void *, int,
61 	    void (*)(void *), void *, struct audio_params *);
62 int	aplaudio_halt_output(void *);
63 int	aplaudio_halt_input(void *);
64 
65 const struct audio_hw_if aplaudio_hw_if = {
66 	.open = aplaudio_open,
67 	.close = aplaudio_close,
68 	.set_params = aplaudio_set_params,
69 	.allocm = aplaudio_allocm,
70 	.freem = aplaudio_freem,
71 	.set_port = aplaudio_set_port,
72 	.get_port = aplaudio_get_port,
73 	.query_devinfo = aplaudio_query_devinfo,
74 	.round_blocksize = aplaudio_round_blocksize,
75 	.round_buffersize = aplaudio_round_buffersize,
76 	.trigger_output = aplaudio_trigger_output,
77 	.trigger_input = aplaudio_trigger_input,
78 	.halt_output = aplaudio_halt_output,
79 	.halt_input = aplaudio_halt_input,
80 };
81 
82 int	aplaudio_match(struct device *, void *, void *);
83 void	aplaudio_attach(struct device *, struct device *, void *);
84 
85 const struct cfattach aplaudio_ca = {
86 	sizeof (struct aplaudio_softc), aplaudio_match, aplaudio_attach
87 };
88 
89 struct cfdriver aplaudio_cd = {
90 	NULL, "aplaudio", DV_DULL
91 };
92 
93 int
aplaudio_match(struct device * parent,void * match,void * aux)94 aplaudio_match(struct device *parent, void *match, void *aux)
95 {
96 	struct fdt_attach_args *faa = aux;
97 
98 	return OF_is_compatible(faa->fa_node, "apple,macaudio");
99 }
100 
101 void
aplaudio_attach(struct device * parent,struct device * self,void * aux)102 aplaudio_attach(struct device *parent, struct device *self, void *aux)
103 {
104 	struct aplaudio_softc *sc = (struct aplaudio_softc *)self;
105 	struct fdt_attach_args *faa = aux;
106 	uint32_t fmt, pol, clk;
107 	uint32_t node, cpu, codec;
108 	uint32_t *dais;
109 	char status[32];
110 	int count = 0;
111 	int i, ncells;
112 	int len;
113 
114 	printf("\n");
115 
116 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
117 		if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
118 		    strcmp(status, "disabled") == 0)
119 			continue;
120 
121 		cpu = OF_getnodebyname(node, "cpu");
122 		if (cpu == 0)
123 			continue;
124 
125 		sc->sc_dai_cpu = aplmca_alloc_cluster(cpu);
126 		if (sc->sc_dai_cpu == NULL)
127 			continue;
128 
129 		codec = OF_getnodebyname(node, "codec");
130 		if (codec == 0)
131 			continue;
132 
133 		len = OF_getproplen(codec, "sound-dai");
134 		if (len < 0)
135 			continue;
136 
137 		dais = malloc(len, M_TEMP, M_WAITOK);
138 		OF_getpropintarray(codec, "sound-dai", dais, len);
139 
140 		ncells = len / sizeof(uint32_t);
141 		ncells = MIN(ncells, nitems(sc->sc_dai_codec));
142 		for (i = 0; i < ncells; i++) {
143 			sc->sc_dai_codec[i] = dai_byphandle(dais[i]);
144 			if (sc->sc_dai_codec[i] == NULL)
145 				continue;
146 			count++;
147 		}
148 
149 		free(dais, M_TEMP, len);
150 
151 		if (count == 0)
152 			continue;
153 		if (count > 1)
154 			aplaudio_set_tdm_slots(sc);
155 
156 		/* XXX Parameters are missing from the device tree? */
157 		fmt = DAI_FORMAT_LJ;
158 		pol = 0;
159 		clk = DAI_CLOCK_CFM | DAI_CLOCK_CBM;
160 		aplaudio_set_format(sc, fmt, pol, clk);
161 
162 		audio_attach_mi(&aplaudio_hw_if, sc, NULL, self);
163 
164 		/* XXX Only attach the first set of interfaces for now. */
165 		return;
166 	}
167 }
168 
169 void
aplaudio_set_format(struct aplaudio_softc * sc,uint32_t fmt,uint32_t pol,uint32_t clk)170 aplaudio_set_format(struct aplaudio_softc *sc, uint32_t fmt, uint32_t pol,
171     uint32_t clk)
172 {
173 	struct dai_device *dai;
174 	int i;
175 
176 	if (sc->sc_dai_cpu->dd_set_format)
177 		sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie,
178 		    fmt, pol, clk);
179 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
180 		dai = sc->sc_dai_codec[i];
181 		if (dai == NULL)
182 			continue;
183 		if (dai->dd_set_format)
184 			dai->dd_set_format(dai->dd_cookie, fmt, pol, clk);
185 	}
186 }
187 
188 void
aplaudio_set_tdm_slots(struct aplaudio_softc * sc)189 aplaudio_set_tdm_slots(struct aplaudio_softc *sc)
190 {
191 	struct dai_device *dai;
192 	int i;
193 
194 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
195 		dai = sc->sc_dai_codec[i];
196 		if (dai == NULL)
197 			continue;
198 		if (dai->dd_set_tdm_slot) {
199 			char prefix[8];
200 			int slot = 0;
201 
202 			if (OF_getprop(dai->dd_node, "sound-name-prefix",
203 			    prefix, sizeof(prefix)) > 0) {
204 				if (strncmp(prefix, "Right", 5) == 0)
205 					slot = 1;
206 			}
207 
208 			dai->dd_set_tdm_slot(dai->dd_cookie, slot);
209 		}
210 	}
211 }
212 
213 int
aplaudio_open(void * cookie,int flags)214 aplaudio_open(void *cookie, int flags)
215 {
216 	struct aplaudio_softc *sc = cookie;
217 	struct dai_device *dai;
218 	const struct audio_hw_if *hwif;
219 	int error;
220 	int i;
221 
222 	dai = sc->sc_dai_cpu;
223 	hwif = dai->dd_hw_if;
224 	if (hwif->open) {
225 		error = hwif->open(dai->dd_cookie, flags);
226 		if (error) {
227 			aplaudio_close(cookie);
228 			return error;
229 		}
230 	}
231 
232 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
233 		dai = sc->sc_dai_codec[i];
234 		if (dai == NULL)
235 			continue;
236 		hwif = dai->dd_hw_if;
237 		if (hwif->open) {
238 			error = hwif->open(dai->dd_cookie, flags);
239 			if (error) {
240 				aplaudio_close(cookie);
241 				return error;
242 			}
243 		}
244 	}
245 
246 	return 0;
247 }
248 
249 void
aplaudio_close(void * cookie)250 aplaudio_close(void *cookie)
251 {
252 	struct aplaudio_softc *sc = cookie;
253 	struct dai_device *dai;
254 	const struct audio_hw_if *hwif;
255 	int i;
256 
257 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
258 		dai = sc->sc_dai_codec[i];
259 		if (dai == NULL)
260 			continue;
261 		hwif = dai->dd_hw_if;
262 		if (hwif->close)
263 			hwif->close(dai->dd_cookie);
264 	}
265 
266 	dai = sc->sc_dai_cpu;
267 	hwif = dai->dd_hw_if;
268 	if (hwif->close)
269 		hwif->close(dai->dd_cookie);
270 }
271 
272 int
aplaudio_set_params(void * cookie,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)273 aplaudio_set_params(void *cookie, int setmode, int usemode,
274     struct audio_params *play, struct audio_params *rec)
275 {
276 	struct aplaudio_softc *sc = cookie;
277 	struct dai_device *dai;
278 	const struct audio_hw_if *hwif;
279 	uint32_t rate;
280 	int error;
281 	int i;
282 
283 	dai = sc->sc_dai_cpu;
284 	hwif = dai->dd_hw_if;
285 	if (hwif->set_params) {
286 		error = hwif->set_params(dai->dd_cookie,
287 		    setmode, usemode, play, rec);
288 		if (error)
289 			return error;
290 	}
291 
292 	if (setmode & AUMODE_PLAY)
293 		rate = play->sample_rate * play->channels * play->bps * 8;
294 	else
295 		rate = rec->sample_rate * rec->channels * rec->bps * 8;
296 
297 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
298 		dai = sc->sc_dai_codec[i];
299 		if (dai == NULL)
300 			continue;
301 		if (dai->dd_set_sysclk) {
302 			error = dai->dd_set_sysclk(dai->dd_cookie, rate);
303 			if (error)
304 				return error;
305 		}
306 	}
307 
308 	dai = sc->sc_dai_cpu;
309 	if (dai->dd_set_sysclk) {
310 		error = dai->dd_set_sysclk(dai->dd_cookie, rate);
311 		if (error)
312 			return error;
313 	}
314 
315 	return 0;
316 }
317 
318 void *
aplaudio_allocm(void * cookie,int direction,size_t size,int type,int flags)319 aplaudio_allocm(void *cookie, int direction, size_t size, int type,
320     int flags)
321 {
322 	struct aplaudio_softc *sc = cookie;
323 	struct dai_device *dai = sc->sc_dai_cpu;
324 	const struct audio_hw_if *hwif = dai->dd_hw_if;
325 
326 	if (hwif->allocm)
327 		return hwif->allocm(dai->dd_cookie,
328 		    direction, size, type, flags);
329 
330 	return NULL;
331 }
332 
333 void
aplaudio_freem(void * cookie,void * addr,int type)334 aplaudio_freem(void *cookie, void *addr, int type)
335 {
336 	struct aplaudio_softc *sc = cookie;
337 	struct dai_device *dai = sc->sc_dai_cpu;
338 	const struct audio_hw_if *hwif = dai->dd_hw_if;
339 
340 	if (hwif->freem)
341 		hwif->freem(dai->dd_cookie, addr, type);
342 }
343 
344 int
aplaudio_set_port(void * cookie,mixer_ctrl_t * cp)345 aplaudio_set_port(void *cookie, mixer_ctrl_t *cp)
346 {
347 	struct aplaudio_softc *sc = cookie;
348 	struct dai_device *dai;
349 	const struct audio_hw_if *hwif;
350 	int error = ENXIO;
351 	int i;
352 
353 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
354 		dai = sc->sc_dai_codec[i];
355 		if (dai == NULL)
356 			continue;
357 		hwif = dai->dd_hw_if;
358 		if (hwif->set_port)
359 			error = hwif->set_port(dai->dd_cookie, cp);
360 	}
361 
362 	return error;
363 }
364 
365 int
aplaudio_get_port(void * cookie,mixer_ctrl_t * cp)366 aplaudio_get_port(void *cookie, mixer_ctrl_t *cp)
367 {
368 	struct aplaudio_softc *sc = cookie;
369 	struct dai_device *dai;
370 	const struct audio_hw_if *hwif;
371 	int error = ENXIO;
372 	int i;
373 
374 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
375 		dai = sc->sc_dai_codec[i];
376 		if (dai == NULL)
377 			continue;
378 		hwif = dai->dd_hw_if;
379 		if (hwif->get_port)
380 			error = hwif->get_port(dai->dd_cookie, cp);
381 	}
382 
383 	return error;
384 }
385 
386 int
aplaudio_query_devinfo(void * cookie,mixer_devinfo_t * dip)387 aplaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip)
388 {
389 	struct aplaudio_softc *sc = cookie;
390 	struct dai_device *dai;
391 	const struct audio_hw_if *hwif;
392 	int i;
393 
394 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
395 		dai = sc->sc_dai_codec[i];
396 		if (dai == NULL)
397 			continue;
398 		hwif = dai->dd_hw_if;
399 		if (hwif->query_devinfo)
400 			return hwif->query_devinfo(dai->dd_cookie, dip);
401 	}
402 
403 	return ENXIO;
404 }
405 
406 int
aplaudio_round_blocksize(void * cookie,int block)407 aplaudio_round_blocksize(void *cookie, int block)
408 {
409 	struct aplaudio_softc *sc = cookie;
410 	struct dai_device *dai = sc->sc_dai_cpu;
411 	const struct audio_hw_if *hwif = dai->dd_hw_if;
412 
413 	if (hwif->round_blocksize)
414 		return hwif->round_blocksize(dai->dd_cookie, block);
415 
416 	return block;
417 }
418 
419 size_t
aplaudio_round_buffersize(void * cookie,int direction,size_t bufsize)420 aplaudio_round_buffersize(void *cookie, int direction, size_t bufsize)
421 {
422 	struct aplaudio_softc *sc = cookie;
423 	struct dai_device *dai = sc->sc_dai_cpu;
424 	const struct audio_hw_if *hwif = dai->dd_hw_if;
425 
426 	if (hwif->round_buffersize)
427 		return hwif->round_buffersize(dai->dd_cookie,
428 		    direction, bufsize);
429 
430 	return bufsize;
431 }
432 
433 int
aplaudio_trigger_output(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)434 aplaudio_trigger_output(void *cookie, void *start, void *end, int blksize,
435     void (*intr)(void *), void *arg, struct audio_params *param)
436 {
437 	struct aplaudio_softc *sc = cookie;
438 	struct dai_device *dai;
439 	const struct audio_hw_if *hwif;
440 	int error;
441 	int i;
442 
443 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
444 		dai = sc->sc_dai_codec[i];
445 		if (dai == NULL)
446 			continue;
447 		hwif = dai->dd_hw_if;
448 		if (hwif->trigger_output) {
449 			error = hwif->trigger_output(dai->dd_cookie,
450 			    start, end, blksize, intr, arg, param);
451 			if (error) {
452 				aplaudio_halt_output(cookie);
453 				return error;
454 			}
455 		}
456 	}
457 
458 	dai = sc->sc_dai_cpu;
459 	hwif = dai->dd_hw_if;
460 	if (hwif->trigger_output) {
461 		error = hwif->trigger_output(dai->dd_cookie,
462 		    start, end, blksize, intr, arg, param);
463 		if (error) {
464 			aplaudio_halt_output(cookie);
465 			return error;
466 		}
467 	}
468 
469 	return 0;
470 }
471 
472 int
aplaudio_trigger_input(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)473 aplaudio_trigger_input(void *cookie, void *start, void *end, int blksize,
474     void (*intr)(void *), void *arg, struct audio_params *param)
475 {
476 	struct aplaudio_softc *sc = cookie;
477 	struct dai_device *dai;
478 	const struct audio_hw_if *hwif;
479 	int error;
480 	int i;
481 
482 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
483 		dai = sc->sc_dai_codec[i];
484 		if (dai == NULL)
485 			continue;
486 		hwif = dai->dd_hw_if;
487 		if (hwif->trigger_input) {
488 			error = hwif->trigger_input(dai->dd_cookie,
489 			    start, end, blksize, intr, arg, param);
490 			if (error) {
491 				aplaudio_halt_input(cookie);
492 				return error;
493 			}
494 		}
495 	}
496 
497 	dai = sc->sc_dai_cpu;
498 	hwif = dai->dd_hw_if;
499 	if (hwif->trigger_input) {
500 		error = hwif->trigger_input(dai->dd_cookie,
501 		    start, end, blksize, intr, arg, param);
502 		if (error) {
503 			aplaudio_halt_input(cookie);
504 			return error;
505 		}
506 	}
507 
508 	return 0;
509 }
510 
511 int
aplaudio_halt_output(void * cookie)512 aplaudio_halt_output(void *cookie)
513 {
514 	struct aplaudio_softc *sc = cookie;
515 	struct dai_device *dai;
516 	const struct audio_hw_if *hwif;
517 	int i;
518 
519 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
520 		dai = sc->sc_dai_codec[i];
521 		if (dai == NULL)
522 			continue;
523 		hwif = dai->dd_hw_if;
524 		if (hwif->halt_output)
525 			hwif->halt_output(dai->dd_cookie);
526 	}
527 
528 	dai = sc->sc_dai_cpu;
529 	hwif = dai->dd_hw_if;
530 	if (hwif->halt_output)
531 		hwif->halt_output(dai->dd_cookie);
532 
533 	return 0;
534 }
535 
536 int
aplaudio_halt_input(void * cookie)537 aplaudio_halt_input(void *cookie)
538 {
539 	struct aplaudio_softc *sc = cookie;
540 	struct dai_device *dai;
541 	const struct audio_hw_if *hwif;
542 	int i;
543 
544 	for (i = 0; i < nitems(sc->sc_dai_codec); i++) {
545 		dai = sc->sc_dai_codec[i];
546 		if (dai == NULL)
547 			continue;
548 		hwif = dai->dd_hw_if;
549 		if (hwif->halt_input)
550 			hwif->halt_input(dai->dd_cookie);
551 	}
552 
553 	dai = sc->sc_dai_cpu;
554 	hwif = dai->dd_hw_if;
555 	if (hwif->halt_input)
556 		hwif->halt_input(dai->dd_cookie);
557 
558 	return 0;
559 }
560