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
simpleaudio_match(struct device * parent,void * match,void * aux)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
simpleaudio_attach(struct device * parent,struct device * self,void * aux)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
simpleaudio_attach_deferred(struct device * self)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
simpleaudio_set_format(struct simpleaudio_softc * sc,uint32_t fmt,uint32_t pol,uint32_t clk)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
simpleaudio_open(void * cookie,int flags)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
simpleaudio_close(void * cookie)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
simpleaudio_set_params(void * cookie,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)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 *
simpleaudio_allocm(void * cookie,int direction,size_t size,int type,int flags)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
simpleaudio_freem(void * cookie,void * addr,int type)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
simpleaudio_set_port(void * cookie,mixer_ctrl_t * cp)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
simpleaudio_get_port(void * cookie,mixer_ctrl_t * cp)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
simpleaudio_query_devinfo(void * cookie,mixer_devinfo_t * dip)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
simpleaudio_round_blocksize(void * cookie,int block)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
simpleaudio_round_buffersize(void * cookie,int direction,size_t bufsize)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
simpleaudio_trigger_output(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)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
simpleaudio_trigger_input(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)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
simpleaudio_halt_output(void * cookie)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
simpleaudio_halt_input(void * cookie)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