xref: /openbsd/sys/dev/fdt/graphaudio.c (revision 7eff0e4e)
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 
33 struct 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 
43 int	graphaudio_match(struct device *, void *, void *);
44 void	graphaudio_attach(struct device *, struct device *, void *);
45 void	graphaudio_attach_deferred(struct device *);
46 void	graphaudio_set_format(struct graphaudio_softc *, uint32_t,
47 	    uint32_t, uint32_t);
48 
49 int	graphaudio_open(void *, int);
50 void	graphaudio_close(void *);
51 int	graphaudio_set_params(void *, int, int,
52 	    struct audio_params *, struct audio_params *);
53 void	*graphaudio_allocm(void *, int, size_t, int, int);
54 void	graphaudio_freem(void *, void *, int);
55 int	graphaudio_set_port(void *, mixer_ctrl_t *);
56 int	graphaudio_get_port(void *, mixer_ctrl_t *);
57 int	graphaudio_query_devinfo(void *, mixer_devinfo_t *);
58 int	graphaudio_round_blocksize(void *, int);
59 size_t	graphaudio_round_buffersize(void *, int, size_t);
60 int	graphaudio_trigger_output(void *, void *, void *, int,
61 	    void (*)(void *), void *, struct audio_params *);
62 int	graphaudio_trigger_input(void *, void *, void *, int,
63 	    void (*)(void *), void *, struct audio_params *);
64 int	graphaudio_halt_output(void *);
65 int	graphaudio_halt_input(void *);
66 
67 const 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 
84 const struct cfattach graphaudio_ca = {
85 	sizeof(struct graphaudio_softc), graphaudio_match, graphaudio_attach
86 };
87 
88 struct cfdriver graphaudio_cd = {
89 	NULL, "graphaudio", DV_DULL
90 };
91 
92 int
graphaudio_match(struct device * parent,void * match,void * aux)93 graphaudio_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 
100 void
graphaudio_attach(struct device * parent,struct device * self,void * aux)101 graphaudio_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 
112 void
graphaudio_attach_deferred(struct device * self)113 graphaudio_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 
190 void
graphaudio_set_format(struct graphaudio_softc * sc,uint32_t fmt,uint32_t pol,uint32_t clk)191 graphaudio_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 
202 int
graphaudio_open(void * cookie,int flags)203 graphaudio_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 
233 void
graphaudio_close(void * cookie)234 graphaudio_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 
251 int
graphaudio_set_params(void * cookie,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)252 graphaudio_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 
303 void *
graphaudio_allocm(void * cookie,int direction,size_t size,int type,int flags)304 graphaudio_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 
318 void
graphaudio_freem(void * cookie,void * addr,int type)319 graphaudio_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 
329 int
graphaudio_set_port(void * cookie,mixer_ctrl_t * cp)330 graphaudio_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 
342 int
graphaudio_get_port(void * cookie,mixer_ctrl_t * cp)343 graphaudio_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 
355 int
graphaudio_query_devinfo(void * cookie,mixer_devinfo_t * dip)356 graphaudio_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 
368 int
graphaudio_round_blocksize(void * cookie,int block)369 graphaudio_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 
381 size_t
graphaudio_round_buffersize(void * cookie,int direction,size_t bufsize)382 graphaudio_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 
395 int
graphaudio_trigger_output(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)396 graphaudio_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 
429 int
graphaudio_trigger_input(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)430 graphaudio_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 
463 int
graphaudio_halt_output(void * cookie)464 graphaudio_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 
483 int
graphaudio_halt_input(void * cookie)484 graphaudio_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