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