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