1/*	$OpenBSD: graphaudio.c,v 1.5 2022/10/28 15:09:45 kn Exp $	*/
2/*
3 * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
4 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
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/fdt.h>
25
26#include <dev/ofw/openfirm.h>
27#include <dev/ofw/ofw_misc.h>
28
29#include <sys/audioio.h>
30#include <dev/audio_if.h>
31#include <dev/midi_if.h>
32
33struct graphaudio_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};
42
43int	graphaudio_match(struct device *, void *, void *);
44void	graphaudio_attach(struct device *, struct device *, void *);
45void	graphaudio_attach_deferred(struct device *);
46void	graphaudio_set_format(struct graphaudio_softc *, uint32_t,
47	    uint32_t, uint32_t);
48
49int	graphaudio_open(void *, int);
50void	graphaudio_close(void *);
51int	graphaudio_set_params(void *, int, int,
52	    struct audio_params *, struct audio_params *);
53void	*graphaudio_allocm(void *, int, size_t, int, int);
54void	graphaudio_freem(void *, void *, int);
55int	graphaudio_set_port(void *, mixer_ctrl_t *);
56int	graphaudio_get_port(void *, mixer_ctrl_t *);
57int	graphaudio_query_devinfo(void *, mixer_devinfo_t *);
58int	graphaudio_round_blocksize(void *, int);
59size_t	graphaudio_round_buffersize(void *, int, size_t);
60int	graphaudio_trigger_output(void *, void *, void *, int,
61	    void (*)(void *), void *, struct audio_params *);
62int	graphaudio_trigger_input(void *, void *, void *, int,
63	    void (*)(void *), void *, struct audio_params *);
64int	graphaudio_halt_output(void *);
65int	graphaudio_halt_input(void *);
66
67const struct audio_hw_if graphaudio_hw_if = {
68	.open = graphaudio_open,
69	.close = graphaudio_close,
70	.set_params = graphaudio_set_params,
71	.allocm = graphaudio_allocm,
72	.freem = graphaudio_freem,
73	.set_port = graphaudio_set_port,
74	.get_port = graphaudio_get_port,
75	.query_devinfo = graphaudio_query_devinfo,
76	.round_blocksize = graphaudio_round_blocksize,
77	.round_buffersize = graphaudio_round_buffersize,
78	.trigger_output = graphaudio_trigger_output,
79	.trigger_input = graphaudio_trigger_input,
80	.halt_output = graphaudio_halt_output,
81	.halt_input = graphaudio_halt_input,
82};
83
84const struct cfattach graphaudio_ca = {
85	sizeof(struct graphaudio_softc), graphaudio_match, graphaudio_attach
86};
87
88struct cfdriver graphaudio_cd = {
89	NULL, "graphaudio", DV_DULL
90};
91
92int
93graphaudio_match(struct device *parent, void *match, void *aux)
94{
95	struct fdt_attach_args *faa = aux;
96
97	return OF_is_compatible(faa->fa_node, "audio-graph-card");
98}
99
100void
101graphaudio_attach(struct device *parent, struct device *self, void *aux)
102{
103	struct graphaudio_softc *sc = (struct graphaudio_softc *)self;
104	struct fdt_attach_args *faa = aux;
105
106	printf("\n");
107
108	sc->sc_node = faa->fa_node;
109	config_defer(self, graphaudio_attach_deferred);
110}
111
112void
113graphaudio_attach_deferred(struct device *self)
114{
115	struct graphaudio_softc *sc = (struct graphaudio_softc *)self;
116	char format[16] = { 0 };
117	uint32_t fmt, pol, clk;
118	uint32_t dais;
119	struct device_ports *dp;
120	struct endpoint *ep, *rep;
121
122	dais = OF_getpropint(sc->sc_node, "dais", 0);
123	dp = device_ports_byphandle(dais);
124	if (dp == NULL)
125		return;
126
127	ep = endpoint_byreg(dp, -1, -1);
128	if (ep == NULL)
129		return;
130
131	rep = endpoint_remote(ep);
132	if (rep == NULL)
133		return;
134
135	sc->sc_mclk_fs = OF_getpropint(ep->ep_node, "mclk-fs", 0);
136
137	sc->sc_dai_cpu = endpoint_get_cookie(ep);
138	sc->sc_dai_codec = endpoint_get_cookie(rep);
139
140	if (sc->sc_dai_cpu == NULL || sc->sc_dai_codec == NULL)
141		return;
142
143	OF_getprop(ep->ep_node, "dai-format", format, sizeof(format));
144	if (!strcmp(format, "i2s"))
145		fmt = DAI_FORMAT_I2S;
146	else if (!strcmp(format, "right_j"))
147		fmt = DAI_FORMAT_RJ;
148	else if (!strcmp(format, "left_j"))
149		fmt = DAI_FORMAT_LJ;
150	else if (!strcmp(format, "dsp_a"))
151		fmt = DAI_FORMAT_DSPA;
152	else if (!strcmp(format, "dsp_b"))
153		fmt = DAI_FORMAT_DSPB;
154	else if (!strcmp(format, "ac97"))
155		fmt = DAI_FORMAT_AC97;
156	else if (!strcmp(format, "pdm"))
157		fmt = DAI_FORMAT_PDM;
158	else if (!strcmp(format, "msb"))
159		fmt = DAI_FORMAT_MSB;
160	else if (!strcmp(format, "lsb"))
161		fmt = DAI_FORMAT_LSB;
162	else
163		return;
164
165	pol = 0;
166	if (OF_getproplen(ep->ep_node, "frame-inversion") == 0)
167		pol |= DAI_POLARITY_IF;
168	else
169		pol |= DAI_POLARITY_NF;
170	if (OF_getproplen(ep->ep_node, "bitclock-inversion") == 0)
171		pol |= DAI_POLARITY_IB;
172	else
173		pol |= DAI_POLARITY_NB;
174
175	clk = 0;
176	if (OF_getproplen(ep->ep_node, "frame-master") == 0)
177		clk |= DAI_CLOCK_CFM;
178	else
179		clk |= DAI_CLOCK_CFS;
180	if (OF_getproplen(ep->ep_node, "bitclock-master") == 0)
181		clk |= DAI_CLOCK_CBM;
182	else
183		clk |= DAI_CLOCK_CBS;
184
185	graphaudio_set_format(sc, fmt, pol, clk);
186
187	audio_attach_mi(&graphaudio_hw_if, sc, NULL, &sc->sc_dev);
188}
189
190void
191graphaudio_set_format(struct graphaudio_softc *sc, uint32_t fmt, uint32_t pol,
192    uint32_t clk)
193{
194	if (sc->sc_dai_cpu->dd_set_format)
195		sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie,
196		    fmt, pol, clk);
197	if (sc->sc_dai_codec->dd_set_format)
198		sc->sc_dai_codec->dd_set_format(sc->sc_dai_codec->dd_cookie,
199		    fmt, pol, clk);
200}
201
202int
203graphaudio_open(void *cookie, int flags)
204{
205	struct graphaudio_softc *sc = cookie;
206	struct dai_device *dai;
207	const struct audio_hw_if *hwif;
208	int error;
209
210	dai = sc->sc_dai_cpu;
211	hwif = dai->dd_hw_if;
212	if (hwif->open) {
213		error = hwif->open(dai->dd_cookie, flags);
214		if (error) {
215			graphaudio_close(cookie);
216			return error;
217		}
218	}
219
220	dai = sc->sc_dai_codec;
221	hwif = dai->dd_hw_if;
222	if (hwif->open) {
223		error = hwif->open(dai->dd_cookie, flags);
224		if (error) {
225			graphaudio_close(cookie);
226			return error;
227		}
228	}
229
230	return 0;
231}
232
233void
234graphaudio_close(void *cookie)
235{
236	struct graphaudio_softc *sc = cookie;
237	struct dai_device *dai;
238	const struct audio_hw_if *hwif;
239
240	dai = sc->sc_dai_codec;
241	hwif = dai->dd_hw_if;
242	if (hwif->close)
243		hwif->close(dai->dd_cookie);
244
245	dai = sc->sc_dai_cpu;
246	hwif = dai->dd_hw_if;
247	if (hwif->close)
248		hwif->close(dai->dd_cookie);
249}
250
251int
252graphaudio_set_params(void *cookie, int setmode, int usemode,
253    struct audio_params *play, struct audio_params *rec)
254{
255	struct graphaudio_softc *sc = cookie;
256	struct dai_device *dai;
257	const struct audio_hw_if *hwif;
258	uint32_t rate;
259	int error;
260
261	if (sc->sc_mclk_fs) {
262		if (setmode & AUMODE_PLAY)
263			rate = play->sample_rate * sc->sc_mclk_fs;
264		else
265			rate = rec->sample_rate * sc->sc_mclk_fs;
266
267		dai = sc->sc_dai_codec;
268		if (dai->dd_set_sysclk) {
269			error = dai->dd_set_sysclk(dai->dd_cookie, rate);
270			if (error)
271				return error;
272		}
273
274		dai = sc->sc_dai_cpu;
275		if (dai->dd_set_sysclk) {
276			error = dai->dd_set_sysclk(dai->dd_cookie, rate);
277			if (error)
278				return error;
279		}
280	}
281
282	dai = sc->sc_dai_cpu;
283	hwif = dai->dd_hw_if;
284	if (hwif->set_params) {
285		error = hwif->set_params(dai->dd_cookie,
286		    setmode, usemode, play, rec);
287		if (error)
288			return error;
289	}
290
291	dai = sc->sc_dai_codec;
292	hwif = dai->dd_hw_if;
293	if (hwif->set_params) {
294		error = hwif->set_params(dai->dd_cookie,
295		    setmode, usemode, play, rec);
296		if (error)
297			return error;
298	}
299
300	return 0;
301}
302
303void *
304graphaudio_allocm(void *cookie, int direction, size_t size, int type,
305    int flags)
306{
307	struct graphaudio_softc *sc = cookie;
308	struct dai_device *dai = sc->sc_dai_cpu;
309	const struct audio_hw_if *hwif = dai->dd_hw_if;
310
311	if (hwif->allocm)
312		return hwif->allocm(dai->dd_cookie,
313		    direction, size, type, flags);
314
315	return NULL;
316}
317
318void
319graphaudio_freem(void *cookie, void *addr, int type)
320{
321	struct graphaudio_softc *sc = cookie;
322	struct dai_device *dai = sc->sc_dai_cpu;
323	const struct audio_hw_if *hwif = dai->dd_hw_if;
324
325	if (hwif->freem)
326		hwif->freem(dai->dd_cookie, addr, type);
327}
328
329int
330graphaudio_set_port(void *cookie, mixer_ctrl_t *cp)
331{
332	struct graphaudio_softc *sc = cookie;
333	struct dai_device *dai = sc->sc_dai_codec;
334	const struct audio_hw_if *hwif = dai->dd_hw_if;
335
336	if (hwif->set_port)
337		return hwif->set_port(dai->dd_cookie, cp);
338
339	return ENXIO;
340}
341
342int
343graphaudio_get_port(void *cookie, mixer_ctrl_t *cp)
344{
345	struct graphaudio_softc *sc = cookie;
346	struct dai_device *dai = sc->sc_dai_codec;
347	const struct audio_hw_if *hwif = dai->dd_hw_if;
348
349	if (hwif->get_port)
350		return hwif->get_port(dai->dd_cookie, cp);
351
352	return ENXIO;
353}
354
355int
356graphaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip)
357{
358	struct graphaudio_softc *sc = cookie;
359	struct dai_device *dai = sc->sc_dai_codec;
360	const struct audio_hw_if *hwif = dai->dd_hw_if;
361
362	if (hwif->query_devinfo)
363		return hwif->query_devinfo(dai->dd_cookie, dip);
364
365	return ENXIO;
366}
367
368int
369graphaudio_round_blocksize(void *cookie, int block)
370{
371	struct graphaudio_softc *sc = cookie;
372	struct dai_device *dai = sc->sc_dai_cpu;
373	const struct audio_hw_if *hwif = dai->dd_hw_if;
374
375	if (hwif->round_blocksize)
376		return hwif->round_blocksize(dai->dd_cookie, block);
377
378	return block;
379}
380
381size_t
382graphaudio_round_buffersize(void *cookie, int direction, size_t bufsize)
383{
384	struct graphaudio_softc *sc = cookie;
385	struct dai_device *dai = sc->sc_dai_cpu;
386	const struct audio_hw_if *hwif = dai->dd_hw_if;
387
388	if (hwif->round_buffersize)
389		return hwif->round_buffersize(dai->dd_cookie,
390		    direction, bufsize);
391
392	return bufsize;
393}
394
395int
396graphaudio_trigger_output(void *cookie, void *start, void *end, int blksize,
397    void (*intr)(void *), void *arg, struct audio_params *param)
398{
399	struct graphaudio_softc *sc = cookie;
400	struct dai_device *dai;
401	const struct audio_hw_if *hwif;
402	int error;
403
404	dai = sc->sc_dai_codec;
405	hwif = dai->dd_hw_if;
406	if (hwif->trigger_output) {
407		error = hwif->trigger_output(dai->dd_cookie,
408		    start, end, blksize, intr, arg, param);
409		if (error) {
410			graphaudio_halt_output(cookie);
411			return error;
412		}
413	}
414
415	dai = sc->sc_dai_cpu;
416	hwif = dai->dd_hw_if;
417	if (hwif->trigger_output) {
418		error = hwif->trigger_output(dai->dd_cookie,
419		    start, end, blksize, intr, arg, param);
420		if (error) {
421			graphaudio_halt_output(cookie);
422			return error;
423		}
424	}
425
426	return 0;
427}
428
429int
430graphaudio_trigger_input(void *cookie, void *start, void *end, int blksize,
431    void (*intr)(void *), void *arg, struct audio_params *param)
432{
433	struct graphaudio_softc *sc = cookie;
434	struct dai_device *dai;
435	const struct audio_hw_if *hwif;
436	int error;
437
438	dai = sc->sc_dai_codec;
439	hwif = dai->dd_hw_if;
440	if (hwif->trigger_input) {
441		error = hwif->trigger_input(dai->dd_cookie,
442		    start, end, blksize, intr, arg, param);
443		if (error) {
444			graphaudio_halt_input(cookie);
445			return error;
446		}
447	}
448
449	dai = sc->sc_dai_cpu;
450	hwif = dai->dd_hw_if;
451	if (hwif->trigger_input) {
452		error = hwif->trigger_input(dai->dd_cookie,
453		    start, end, blksize, intr, arg, param);
454		if (error) {
455			graphaudio_halt_input(cookie);
456			return error;
457		}
458	}
459
460	return 0;
461}
462
463int
464graphaudio_halt_output(void *cookie)
465{
466	struct graphaudio_softc *sc = cookie;
467	struct dai_device *dai;
468	const struct audio_hw_if *hwif;
469
470	dai = sc->sc_dai_codec;
471	hwif = dai->dd_hw_if;
472	if (hwif->halt_output)
473		hwif->halt_output(dai->dd_cookie);
474
475	dai = sc->sc_dai_cpu;
476	hwif = dai->dd_hw_if;
477	if (hwif->halt_output)
478		hwif->halt_output(dai->dd_cookie);
479
480	return 0;
481}
482
483int
484graphaudio_halt_input(void *cookie)
485{
486	struct graphaudio_softc *sc = cookie;
487	struct dai_device *dai;
488	const struct audio_hw_if *hwif;
489
490	dai = sc->sc_dai_codec;
491	hwif = dai->dd_hw_if;
492	if (hwif->halt_input)
493		hwif->halt_input(dai->dd_cookie);
494
495	dai = sc->sc_dai_cpu;
496	hwif = dai->dd_hw_if;
497	if (hwif->halt_input)
498		hwif->halt_input(dai->dd_cookie);
499
500	return 0;
501}
502