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
33struct 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
45int simpleaudio_match(struct device *, void *, void *);
46void simpleaudio_attach(struct device *, struct device *, void *);
47void simpleaudio_attach_deferred(struct device *);
48void simpleaudio_set_format(struct simpleaudio_softc *, uint32_t,
49    uint32_t, uint32_t);
50
51int simpleaudio_open(void *, int);
52void simpleaudio_close(void *);
53int simpleaudio_set_params(void *, int, int,
54    struct audio_params *, struct audio_params *);
55void *simpleaudio_allocm(void *, int, size_t, int, int);
56void simpleaudio_freem(void *, void *, int);
57int simpleaudio_set_port(void *, mixer_ctrl_t *);
58int simpleaudio_get_port(void *, mixer_ctrl_t *);
59int simpleaudio_query_devinfo(void *, mixer_devinfo_t *);
60int simpleaudio_round_blocksize(void *, int);
61size_t simpleaudio_round_buffersize(void *, int, size_t);
62int simpleaudio_trigger_output(void *, void *, void *, int,
63    void (*)(void *), void *, struct audio_params *);
64int simpleaudio_trigger_input(void *, void *, void *, int,
65    void (*)(void *), void *, struct audio_params *);
66int simpleaudio_halt_output(void *);
67int simpleaudio_halt_input(void *);
68
69const 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
86const struct cfattach simpleaudio_ca = {
87	sizeof(struct simpleaudio_softc), simpleaudio_match, simpleaudio_attach
88};
89
90struct cfdriver simpleaudio_cd = {
91	NULL, "simpleaudio", DV_DULL
92};
93
94int
95simpleaudio_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
102void
103simpleaudio_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
116void
117simpleaudio_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
210void
211simpleaudio_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
222int
223simpleaudio_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
265void
266simpleaudio_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
291int
292simpleaudio_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
343void *
344simpleaudio_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
358void
359simpleaudio_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
369int
370simpleaudio_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
382int
383simpleaudio_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
395int
396simpleaudio_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
408int
409simpleaudio_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
421size_t
422simpleaudio_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
435int
436simpleaudio_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
482int
483simpleaudio_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
529int
530simpleaudio_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
557int
558simpleaudio_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