xref: /openbsd/sys/dev/fdt/simpleaudio.c (revision d415bd75)
1 /*	$OpenBSD: simpleaudio.c,v 1.7 2022/10/28 15:09:45 kn Exp $	*/
2 /*
3  * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
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/device.h>
21 #include <sys/malloc.h>
22 
23 #include <machine/fdt.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/ofw_misc.h>
27 #include <dev/ofw/ofw_pinctrl.h>
28 
29 #include <sys/audioio.h>
30 #include <dev/audio_if.h>
31 #include <dev/midi_if.h>
32 
33 struct simpleaudio_softc {
34 	struct device		sc_dev;
35 	int			sc_node;
36 
37 	uint32_t		sc_mclk_fs;
38 
39 	struct dai_device	*sc_dai_cpu;
40 	struct dai_device	*sc_dai_codec;
41 	struct dai_device	**sc_dai_aux;
42 	int			sc_dai_naux;
43 };
44 
45 int simpleaudio_match(struct device *, void *, void *);
46 void simpleaudio_attach(struct device *, struct device *, void *);
47 void simpleaudio_attach_deferred(struct device *);
48 void simpleaudio_set_format(struct simpleaudio_softc *, uint32_t,
49     uint32_t, uint32_t);
50 
51 int simpleaudio_open(void *, int);
52 void simpleaudio_close(void *);
53 int simpleaudio_set_params(void *, int, int,
54     struct audio_params *, struct audio_params *);
55 void *simpleaudio_allocm(void *, int, size_t, int, int);
56 void simpleaudio_freem(void *, void *, int);
57 int simpleaudio_set_port(void *, mixer_ctrl_t *);
58 int simpleaudio_get_port(void *, mixer_ctrl_t *);
59 int simpleaudio_query_devinfo(void *, mixer_devinfo_t *);
60 int simpleaudio_round_blocksize(void *, int);
61 size_t simpleaudio_round_buffersize(void *, int, size_t);
62 int simpleaudio_trigger_output(void *, void *, void *, int,
63     void (*)(void *), void *, struct audio_params *);
64 int simpleaudio_trigger_input(void *, void *, void *, int,
65     void (*)(void *), void *, struct audio_params *);
66 int simpleaudio_halt_output(void *);
67 int simpleaudio_halt_input(void *);
68 
69 const struct audio_hw_if simpleaudio_hw_if = {
70 	.open = simpleaudio_open,
71 	.close = simpleaudio_close,
72 	.set_params = simpleaudio_set_params,
73 	.allocm = simpleaudio_allocm,
74 	.freem = simpleaudio_freem,
75 	.set_port = simpleaudio_set_port,
76 	.get_port = simpleaudio_get_port,
77 	.query_devinfo = simpleaudio_query_devinfo,
78 	.round_blocksize = simpleaudio_round_blocksize,
79 	.round_buffersize = simpleaudio_round_buffersize,
80 	.trigger_output = simpleaudio_trigger_output,
81 	.trigger_input = simpleaudio_trigger_input,
82 	.halt_output = simpleaudio_halt_output,
83 	.halt_input = simpleaudio_halt_input,
84 };
85 
86 const struct cfattach simpleaudio_ca = {
87 	sizeof(struct simpleaudio_softc), simpleaudio_match, simpleaudio_attach
88 };
89 
90 struct cfdriver simpleaudio_cd = {
91 	NULL, "simpleaudio", DV_DULL
92 };
93 
94 int
95 simpleaudio_match(struct device *parent, void *match, void *aux)
96 {
97 	struct fdt_attach_args *faa = aux;
98 
99 	return OF_is_compatible(faa->fa_node, "simple-audio-card");
100 }
101 
102 void
103 simpleaudio_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct simpleaudio_softc *sc = (struct simpleaudio_softc *)self;
106 	struct fdt_attach_args *faa = aux;
107 
108 	printf("\n");
109 
110 	pinctrl_byname(faa->fa_node, "default");
111 
112 	sc->sc_node = faa->fa_node;
113 	config_defer(self, simpleaudio_attach_deferred);
114 }
115 
116 void
117 simpleaudio_attach_deferred(struct device *self)
118 {
119 	struct simpleaudio_softc *sc = (struct simpleaudio_softc *)self;
120 	char format[16] = { 0 };
121 	uint32_t fmt, pol, clk;
122 	uint32_t *auxdevs;
123 	ssize_t len;
124 	int i, node;
125 
126 	/* TODO: implement simple-audio-card,dai-link */
127 	if (OF_getnodebyname(sc->sc_node, "simple-audio-card,cpu") == 0 ||
128 	    OF_getnodebyname(sc->sc_node, "simple-audio-card,codec") == 0)
129 		return;
130 
131 	sc->sc_mclk_fs = OF_getpropint(sc->sc_node,
132 	    "simple-audio-card,mclk-fs", 0);
133 
134 	node = OF_getnodebyname(sc->sc_node, "simple-audio-card,cpu");
135 	sc->sc_dai_cpu = dai_byphandle(OF_getpropint(node, "sound-dai", 0));
136 
137 	node = OF_getnodebyname(sc->sc_node, "simple-audio-card,codec");
138 	sc->sc_dai_codec = dai_byphandle(OF_getpropint(node, "sound-dai", 0));
139 
140 	if (sc->sc_dai_cpu == NULL || sc->sc_dai_codec == NULL)
141 		return;
142 
143 	OF_getprop(sc->sc_node, "simple-audio-card,format",
144 	    format, sizeof(format));
145 	if (!strcmp(format, "i2s"))
146 		fmt = DAI_FORMAT_I2S;
147 	else if (!strcmp(format, "right_j"))
148 		fmt = DAI_FORMAT_RJ;
149 	else if (!strcmp(format, "left_j"))
150 		fmt = DAI_FORMAT_LJ;
151 	else if (!strcmp(format, "dsp_a"))
152 		fmt = DAI_FORMAT_DSPA;
153 	else if (!strcmp(format, "dsp_b"))
154 		fmt = DAI_FORMAT_DSPB;
155 	else if (!strcmp(format, "ac97"))
156 		fmt = DAI_FORMAT_AC97;
157 	else if (!strcmp(format, "pdm"))
158 		fmt = DAI_FORMAT_PDM;
159 	else if (!strcmp(format, "msb"))
160 		fmt = DAI_FORMAT_MSB;
161 	else if (!strcmp(format, "lsb"))
162 		fmt = DAI_FORMAT_LSB;
163 	else
164 		return;
165 
166 	pol = 0;
167 	if (OF_getproplen(sc->sc_node, "simple-audio-card,frame-inversion") == 0)
168 		pol |= DAI_POLARITY_IF;
169 	else
170 		pol |= DAI_POLARITY_NF;
171 	if (OF_getproplen(sc->sc_node, "simple-audio-card,bitclock-inversion") == 0)
172 		pol |= DAI_POLARITY_IB;
173 	else
174 		pol |= DAI_POLARITY_NB;
175 
176 	clk = 0;
177 	if (OF_getproplen(sc->sc_node, "simple-audio-card,frame-master") == 0)
178 		clk |= DAI_CLOCK_CFM;
179 	else
180 		clk |= DAI_CLOCK_CFS;
181 	if (OF_getproplen(sc->sc_node, "simple-audio-card,bitclock-master") == 0)
182 		clk |= DAI_CLOCK_CBM;
183 	else
184 		clk |= DAI_CLOCK_CBS;
185 
186 	len = OF_getproplen(sc->sc_node, "simple-audio-card,aux-devs");
187 	if (len > 0) {
188 		if (len % sizeof(uint32_t) != 0)
189 			return;
190 
191 		auxdevs = malloc(len, M_TEMP, M_WAITOK);
192 		OF_getpropintarray(sc->sc_node, "simple-audio-card,aux-devs",
193 		    auxdevs, len);
194 
195 		sc->sc_dai_naux = len / sizeof(uint32_t);
196 		sc->sc_dai_aux = mallocarray(sc->sc_dai_naux,
197 		    sizeof(struct dai_device *), M_DEVBUF, M_WAITOK | M_ZERO);
198 
199 		for (i = 0; i < sc->sc_dai_naux; i++)
200 			sc->sc_dai_aux[i] = dai_byphandle(auxdevs[i]);
201 
202 		free(auxdevs, M_TEMP, len);
203 	}
204 
205 	simpleaudio_set_format(sc, fmt, pol, clk);
206 
207 	audio_attach_mi(&simpleaudio_hw_if, sc, NULL, &sc->sc_dev);
208 }
209 
210 void
211 simpleaudio_set_format(struct simpleaudio_softc *sc, uint32_t fmt, uint32_t pol,
212     uint32_t clk)
213 {
214 	if (sc->sc_dai_cpu->dd_set_format)
215 		sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie,
216 		    fmt, pol, clk);
217 	if (sc->sc_dai_codec->dd_set_format)
218 		sc->sc_dai_codec->dd_set_format(sc->sc_dai_codec->dd_cookie,
219 		    fmt, pol, clk);
220 }
221 
222 int
223 simpleaudio_open(void *cookie, int flags)
224 {
225 	struct simpleaudio_softc *sc = cookie;
226 	struct dai_device *dai;
227 	const struct audio_hw_if *hwif;
228 	int error, i;
229 
230 	dai = sc->sc_dai_cpu;
231 	hwif = dai->dd_hw_if;
232 	if (hwif->open) {
233 		error = hwif->open(dai->dd_cookie, flags);
234 		if (error) {
235 			simpleaudio_close(cookie);
236 			return error;
237 		}
238 	}
239 
240 	dai = sc->sc_dai_codec;
241 	hwif = dai->dd_hw_if;
242 	if (hwif->open) {
243 		error = hwif->open(dai->dd_cookie, flags);
244 		if (error) {
245 			simpleaudio_close(cookie);
246 			return error;
247 		}
248 	}
249 
250 	for (i = 0; i < sc->sc_dai_naux; i++) {
251 		dai = sc->sc_dai_aux[i];
252 		hwif = dai->dd_hw_if;
253 		if (hwif->open) {
254 			error = hwif->open(dai->dd_cookie, flags);
255 			if (error) {
256 				simpleaudio_close(cookie);
257 				return error;
258 			}
259 		}
260 	}
261 
262 	return 0;
263 }
264 
265 void
266 simpleaudio_close(void *cookie)
267 {
268 	struct simpleaudio_softc *sc = cookie;
269 	struct dai_device *dai;
270 	const struct audio_hw_if *hwif;
271 	int i;
272 
273 	for (i = 0; i < sc->sc_dai_naux; i++) {
274 		dai = sc->sc_dai_aux[i];
275 		hwif = dai->dd_hw_if;
276 		if (hwif->close)
277 			hwif->close(dai->dd_cookie);
278 	}
279 
280 	dai = sc->sc_dai_codec;
281 	hwif = dai->dd_hw_if;
282 	if (hwif->close)
283 		hwif->close(dai->dd_cookie);
284 
285 	dai = sc->sc_dai_cpu;
286 	hwif = dai->dd_hw_if;
287 	if (hwif->close)
288 		hwif->close(dai->dd_cookie);
289 }
290 
291 int
292 simpleaudio_set_params(void *cookie, int setmode, int usemode,
293     struct audio_params *play, struct audio_params *rec)
294 {
295 	struct simpleaudio_softc *sc = cookie;
296 	struct dai_device *dai;
297 	const struct audio_hw_if *hwif;
298 	uint32_t rate;
299 	int error;
300 
301 	if (sc->sc_mclk_fs) {
302 		if (setmode & AUMODE_PLAY)
303 			rate = play->sample_rate * sc->sc_mclk_fs;
304 		else
305 			rate = rec->sample_rate * sc->sc_mclk_fs;
306 
307 		dai = sc->sc_dai_codec;
308 		if (dai->dd_set_sysclk) {
309 			error = dai->dd_set_sysclk(dai->dd_cookie, rate);
310 			if (error)
311 				return error;
312 		}
313 
314 		dai = sc->sc_dai_cpu;
315 		if (dai->dd_set_sysclk) {
316 			error = dai->dd_set_sysclk(dai->dd_cookie, rate);
317 			if (error)
318 				return error;
319 		}
320 	}
321 
322 	dai = sc->sc_dai_cpu;
323 	hwif = dai->dd_hw_if;
324 	if (hwif->set_params) {
325 		error = hwif->set_params(dai->dd_cookie,
326 		    setmode, usemode, play, rec);
327 		if (error)
328 			return error;
329 	}
330 
331 	dai = sc->sc_dai_codec;
332 	hwif = dai->dd_hw_if;
333 	if (hwif->set_params) {
334 		error = hwif->set_params(dai->dd_cookie,
335 		    setmode, usemode, play, rec);
336 		if (error)
337 			return error;
338 	}
339 
340 	return 0;
341 }
342 
343 void *
344 simpleaudio_allocm(void *cookie, int direction, size_t size, int type,
345     int flags)
346 {
347 	struct simpleaudio_softc *sc = cookie;
348 	struct dai_device *dai = sc->sc_dai_cpu;
349 	const struct audio_hw_if *hwif = dai->dd_hw_if;
350 
351 	if (hwif->allocm)
352 		return hwif->allocm(dai->dd_cookie,
353 		    direction, size, type, flags);
354 
355 	return NULL;
356 }
357 
358 void
359 simpleaudio_freem(void *cookie, void *addr, int type)
360 {
361 	struct simpleaudio_softc *sc = cookie;
362 	struct dai_device *dai = sc->sc_dai_cpu;
363 	const struct audio_hw_if *hwif = dai->dd_hw_if;
364 
365 	if (hwif->freem)
366 		hwif->freem(dai->dd_cookie, addr, type);
367 }
368 
369 int
370 simpleaudio_set_port(void *cookie, mixer_ctrl_t *cp)
371 {
372 	struct simpleaudio_softc *sc = cookie;
373 	struct dai_device *dai = sc->sc_dai_codec;
374 	const struct audio_hw_if *hwif = dai->dd_hw_if;
375 
376 	if (hwif->set_port)
377 		return hwif->set_port(dai->dd_cookie, cp);
378 
379 	return ENXIO;
380 }
381 
382 int
383 simpleaudio_get_port(void *cookie, mixer_ctrl_t *cp)
384 {
385 	struct simpleaudio_softc *sc = cookie;
386 	struct dai_device *dai = sc->sc_dai_codec;
387 	const struct audio_hw_if *hwif = dai->dd_hw_if;
388 
389 	if (hwif->get_port)
390 		return hwif->get_port(dai->dd_cookie, cp);
391 
392 	return ENXIO;
393 }
394 
395 int
396 simpleaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip)
397 {
398 	struct simpleaudio_softc *sc = cookie;
399 	struct dai_device *dai = sc->sc_dai_codec;
400 	const struct audio_hw_if *hwif = dai->dd_hw_if;
401 
402 	if (hwif->query_devinfo)
403 		return hwif->query_devinfo(dai->dd_cookie, dip);
404 
405 	return ENXIO;
406 }
407 
408 int
409 simpleaudio_round_blocksize(void *cookie, int block)
410 {
411 	struct simpleaudio_softc *sc = cookie;
412 	struct dai_device *dai = sc->sc_dai_cpu;
413 	const struct audio_hw_if *hwif = dai->dd_hw_if;
414 
415 	if (hwif->round_blocksize)
416 		return hwif->round_blocksize(dai->dd_cookie, block);
417 
418 	return block;
419 }
420 
421 size_t
422 simpleaudio_round_buffersize(void *cookie, int direction, size_t bufsize)
423 {
424 	struct simpleaudio_softc *sc = cookie;
425 	struct dai_device *dai = sc->sc_dai_cpu;
426 	const struct audio_hw_if *hwif = dai->dd_hw_if;
427 
428 	if (hwif->round_buffersize)
429 		return hwif->round_buffersize(dai->dd_cookie,
430 		    direction, bufsize);
431 
432 	return bufsize;
433 }
434 
435 int
436 simpleaudio_trigger_output(void *cookie, void *start, void *end, int blksize,
437     void (*intr)(void *), void *arg, struct audio_params *param)
438 {
439 	struct simpleaudio_softc *sc = cookie;
440 	struct dai_device *dai;
441 	const struct audio_hw_if *hwif;
442 	int error, i;
443 
444 	for (i = 0; i < sc->sc_dai_naux; i++) {
445 		dai = sc->sc_dai_aux[i];
446 		hwif = dai->dd_hw_if;
447 		if (hwif->trigger_output) {
448 			error = hwif->trigger_output(dai->dd_cookie,
449 			    start, end, blksize, intr, arg, param);
450 			if (error) {
451 				simpleaudio_halt_output(cookie);
452 				return error;
453 			}
454 		}
455 	}
456 
457 	dai = sc->sc_dai_codec;
458 	hwif = dai->dd_hw_if;
459 	if (hwif->trigger_output) {
460 		error = hwif->trigger_output(dai->dd_cookie,
461 		    start, end, blksize, intr, arg, param);
462 		if (error) {
463 			simpleaudio_halt_output(cookie);
464 			return error;
465 		}
466 	}
467 
468 	dai = sc->sc_dai_cpu;
469 	hwif = dai->dd_hw_if;
470 	if (hwif->trigger_output) {
471 		error = hwif->trigger_output(dai->dd_cookie,
472 		    start, end, blksize, intr, arg, param);
473 		if (error) {
474 			simpleaudio_halt_output(cookie);
475 			return error;
476 		}
477 	}
478 
479 	return 0;
480 }
481 
482 int
483 simpleaudio_trigger_input(void *cookie, void *start, void *end, int blksize,
484     void (*intr)(void *), void *arg, struct audio_params *param)
485 {
486 	struct simpleaudio_softc *sc = cookie;
487 	struct dai_device *dai;
488 	const struct audio_hw_if *hwif;
489 	int error, i;
490 
491 	for (i = 0; i < sc->sc_dai_naux; i++) {
492 		dai = sc->sc_dai_aux[i];
493 		hwif = dai->dd_hw_if;
494 		if (hwif->trigger_input) {
495 			error = hwif->trigger_input(dai->dd_cookie,
496 			    start, end, blksize, intr, arg, param);
497 			if (error) {
498 				simpleaudio_halt_input(cookie);
499 				return error;
500 			}
501 		}
502 	}
503 
504 	dai = sc->sc_dai_codec;
505 	hwif = dai->dd_hw_if;
506 	if (hwif->trigger_input) {
507 		error = hwif->trigger_input(dai->dd_cookie,
508 		    start, end, blksize, intr, arg, param);
509 		if (error) {
510 			simpleaudio_halt_input(cookie);
511 			return error;
512 		}
513 	}
514 
515 	dai = sc->sc_dai_cpu;
516 	hwif = dai->dd_hw_if;
517 	if (hwif->trigger_input) {
518 		error = hwif->trigger_input(dai->dd_cookie,
519 		    start, end, blksize, intr, arg, param);
520 		if (error) {
521 			simpleaudio_halt_input(cookie);
522 			return error;
523 		}
524 	}
525 
526 	return 0;
527 }
528 
529 int
530 simpleaudio_halt_output(void *cookie)
531 {
532 	struct simpleaudio_softc *sc = cookie;
533 	struct dai_device *dai;
534 	const struct audio_hw_if *hwif;
535 	int i;
536 
537 	for (i = 0; i < sc->sc_dai_naux; i++) {
538 		dai = sc->sc_dai_aux[i];
539 		hwif = dai->dd_hw_if;
540 		if (hwif->halt_output)
541 			hwif->halt_output(dai->dd_cookie);
542 	}
543 
544 	dai = sc->sc_dai_codec;
545 	hwif = dai->dd_hw_if;
546 	if (hwif->halt_output)
547 		hwif->halt_output(dai->dd_cookie);
548 
549 	dai = sc->sc_dai_cpu;
550 	hwif = dai->dd_hw_if;
551 	if (hwif->halt_output)
552 		hwif->halt_output(dai->dd_cookie);
553 
554 	return 0;
555 }
556 
557 int
558 simpleaudio_halt_input(void *cookie)
559 {
560 	struct simpleaudio_softc *sc = cookie;
561 	struct dai_device *dai;
562 	const struct audio_hw_if *hwif;
563 	int i;
564 
565 	for (i = 0; i < sc->sc_dai_naux; i++) {
566 		dai = sc->sc_dai_aux[i];
567 		hwif = dai->dd_hw_if;
568 		if (hwif->halt_input)
569 			hwif->halt_input(dai->dd_cookie);
570 	}
571 
572 	dai = sc->sc_dai_codec;
573 	hwif = dai->dd_hw_if;
574 	if (hwif->halt_input)
575 		hwif->halt_input(dai->dd_cookie);
576 
577 	dai = sc->sc_dai_cpu;
578 	hwif = dai->dd_hw_if;
579 	if (hwif->halt_input)
580 		hwif->halt_input(dai->dd_cookie);
581 
582 	return 0;
583 }
584