xref: /src/sys/dev/sound/fdt/audio_soc.c (revision 792251295cdf6c3e1cbb1aa6291434539632912f)
1 /*-
2  * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 #include <sys/cdefs.h>
27 #include "opt_platform.h"
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/clock.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/module.h>
36 #include <sys/endian.h>
37 
38 #include <dev/ofw/ofw_bus.h>
39 #include <dev/ofw/ofw_bus_subr.h>
40 
41 #include <dev/sound/fdt/audio_dai.h>
42 #include <dev/sound/pcm/sound.h>
43 #include "audio_dai_if.h"
44 
45 #define	AUDIO_BUFFER_SIZE	48000 * 4
46 
47 struct audio_soc_aux_node {
48 	SLIST_ENTRY(audio_soc_aux_node)	link;
49 	device_t			dev;
50 };
51 
52 struct audio_soc_channel {
53 	struct audio_soc_softc	*sc;	/* parent device's softc */
54 	struct pcm_channel 	*pcm;	/* PCM channel */
55 	struct snd_dbuf		*buf;	/* PCM buffer */
56 	int			dir;	/* direction */
57 };
58 
59 struct audio_soc_softc {
60 	/*
61 	 * pcm_register assumes that sc is snddev_info,
62 	 * so this has to be first structure member for "compatibility"
63 	 */
64 	struct snddev_info	info;
65 	device_t		dev;
66 	char			*name;
67 	struct intr_config_hook init_hook;
68 	device_t		cpu_dev;
69 	device_t		codec_dev;
70 	SLIST_HEAD(, audio_soc_aux_node)	aux_devs;
71 	unsigned int		mclk_fs;
72 	struct audio_soc_channel 	play_channel;
73 	struct audio_soc_channel 	rec_channel;
74 	/*
75 	 * The format is from the CPU node, for CODEC node clock roles
76 	 * need to be reversed.
77 	 */
78 	uint32_t		format;
79 	uint32_t		link_mclk_fs;
80 };
81 
82 static struct ofw_compat_data compat_data[] = {
83 	{"simple-audio-card",	1},
84 	{NULL,			0},
85 };
86 
87 static struct {
88 	const char *name;
89 	unsigned int fmt;
90 } ausoc_dai_formats[] = {
91 	{ "i2s",	AUDIO_DAI_FORMAT_I2S },
92 	{ "right_j",	AUDIO_DAI_FORMAT_RJ },
93 	{ "left_j",	AUDIO_DAI_FORMAT_LJ },
94 	{ "dsp_a",	AUDIO_DAI_FORMAT_DSPA },
95 	{ "dsp_b",	AUDIO_DAI_FORMAT_DSPB },
96 	{ "ac97",	AUDIO_DAI_FORMAT_AC97 },
97 	{ "pdm",	AUDIO_DAI_FORMAT_PDM },
98 };
99 
100 static int	audio_soc_probe(device_t dev);
101 static int	audio_soc_attach(device_t dev);
102 static int	audio_soc_detach(device_t dev);
103 
104 /*
105  * Invert master/slave roles for CODEC side of the node
106  */
107 static uint32_t
audio_soc_reverse_clocks(uint32_t format)108 audio_soc_reverse_clocks(uint32_t format)
109 {
110 	int fmt, pol, clk;
111 
112 	fmt = AUDIO_DAI_FORMAT_FORMAT(format);
113 	pol = AUDIO_DAI_FORMAT_POLARITY(format);
114 	clk = AUDIO_DAI_FORMAT_CLOCK(format);
115 
116 	switch (clk) {
117 	case AUDIO_DAI_CLOCK_CBM_CFM:
118 		clk = AUDIO_DAI_CLOCK_CBS_CFS;
119 		break;
120 	case AUDIO_DAI_CLOCK_CBS_CFM:
121 		clk = AUDIO_DAI_CLOCK_CBM_CFS;
122 		break;
123 	case AUDIO_DAI_CLOCK_CBM_CFS:
124 		clk = AUDIO_DAI_CLOCK_CBS_CFM;
125 		break;
126 	case AUDIO_DAI_CLOCK_CBS_CFS:
127 		clk = AUDIO_DAI_CLOCK_CBM_CFM;
128 		break;
129 	}
130 
131 	return AUDIO_DAI_FORMAT(fmt, pol, clk);
132 }
133 
134 static uint32_t
audio_soc_chan_setblocksize(kobj_t obj,void * data,uint32_t blocksz)135 audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz)
136 {
137 
138 	return (blocksz);
139 }
140 
141 static int
audio_soc_chan_setformat(kobj_t obj,void * data,uint32_t format)142 audio_soc_chan_setformat(kobj_t obj, void *data, uint32_t format)
143 {
144 
145 	struct audio_soc_softc *sc;
146 	struct audio_soc_channel *ausoc_chan;
147 
148 	ausoc_chan = data;
149 	sc = ausoc_chan->sc;
150 
151 	return AUDIO_DAI_SET_CHANFORMAT(sc->cpu_dev, format);
152 }
153 
154 static uint32_t
audio_soc_chan_setspeed(kobj_t obj,void * data,uint32_t speed)155 audio_soc_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
156 {
157 
158 	struct audio_soc_softc *sc;
159 	struct audio_soc_channel *ausoc_chan;
160 	uint32_t rate;
161 	struct audio_soc_aux_node *aux_node;
162 
163 	ausoc_chan = data;
164 	sc = ausoc_chan->sc;
165 
166 	if (sc->link_mclk_fs) {
167 		rate = speed * sc->link_mclk_fs;
168 		if (AUDIO_DAI_SET_SYSCLK(sc->cpu_dev, rate, AUDIO_DAI_CLOCK_IN))
169 			device_printf(sc->dev, "failed to set sysclk for CPU node\n");
170 
171 		if (AUDIO_DAI_SET_SYSCLK(sc->codec_dev, rate, AUDIO_DAI_CLOCK_OUT))
172 			device_printf(sc->dev, "failed to set sysclk for codec node\n");
173 
174 		SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
175 			if (AUDIO_DAI_SET_SYSCLK(aux_node->dev, rate, AUDIO_DAI_CLOCK_OUT))
176 				device_printf(sc->dev, "failed to set sysclk for aux node\n");
177 		}
178 	}
179 
180 	/*
181 	 * Let CPU node determine speed
182 	 */
183 	speed = AUDIO_DAI_SET_CHANSPEED(sc->cpu_dev, speed);
184 	AUDIO_DAI_SET_CHANSPEED(sc->codec_dev, speed);
185 	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
186 		AUDIO_DAI_SET_CHANSPEED(aux_node->dev, speed);
187 	}
188 
189 	return (speed);
190 }
191 
192 static uint32_t
audio_soc_chan_getptr(kobj_t obj,void * data)193 audio_soc_chan_getptr(kobj_t obj, void *data)
194 {
195 	struct audio_soc_softc *sc;
196 	struct audio_soc_channel *ausoc_chan;
197 
198 	ausoc_chan = data;
199 	sc = ausoc_chan->sc;
200 
201 	return AUDIO_DAI_GET_PTR(sc->cpu_dev, ausoc_chan->dir);
202 }
203 
204 static void *
audio_soc_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)205 audio_soc_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
206 	struct pcm_channel *c, int dir)
207 {
208 	struct audio_soc_channel *ausoc_chan;
209 	void *buffer;
210 
211 	ausoc_chan = devinfo;
212 	buffer = malloc(AUDIO_BUFFER_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
213 
214 	if (sndbuf_setup(b, buffer, AUDIO_BUFFER_SIZE) != 0) {
215 		free(buffer, M_DEVBUF);
216 		return NULL;
217 	}
218 
219 	ausoc_chan->dir = dir;
220 	ausoc_chan->buf = b;
221 	ausoc_chan->pcm = c;
222 
223 	return (devinfo);
224 }
225 
226 static int
audio_soc_chan_trigger(kobj_t obj,void * data,int go)227 audio_soc_chan_trigger(kobj_t obj, void *data, int go)
228 {
229 	struct audio_soc_softc *sc;
230 	struct audio_soc_channel *ausoc_chan;
231 	struct audio_soc_aux_node *aux_node;
232 
233 	ausoc_chan = (struct audio_soc_channel *)data;
234 	sc = ausoc_chan->sc;
235 	AUDIO_DAI_TRIGGER(sc->codec_dev, go, ausoc_chan->dir);
236 	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
237 		AUDIO_DAI_TRIGGER(aux_node->dev, go, ausoc_chan->dir);
238 	}
239 
240 	return AUDIO_DAI_TRIGGER(sc->cpu_dev, go, ausoc_chan->dir);
241 }
242 
243 static int
audio_soc_chan_free(kobj_t obj,void * data)244 audio_soc_chan_free(kobj_t obj, void *data)
245 {
246 
247 	struct audio_soc_channel *ausoc_chan;
248 	void *buffer;
249 
250 	ausoc_chan = (struct audio_soc_channel *)data;
251 
252 	buffer = ausoc_chan->buf->buf;
253 	free(buffer, M_DEVBUF);
254 
255 	return (0);
256 }
257 
258 static struct pcmchan_caps *
audio_soc_chan_getcaps(kobj_t obj,void * data)259 audio_soc_chan_getcaps(kobj_t obj, void *data)
260 {
261 	struct audio_soc_softc *sc;
262 	struct audio_soc_channel *ausoc_chan;
263 
264 	ausoc_chan = data;
265 	sc = ausoc_chan->sc;
266 
267 	return AUDIO_DAI_GET_CAPS(sc->cpu_dev);
268 }
269 
270 static kobj_method_t audio_soc_chan_methods[] = {
271 	KOBJMETHOD(channel_init, 	audio_soc_chan_init),
272 	KOBJMETHOD(channel_free, 	audio_soc_chan_free),
273 	KOBJMETHOD(channel_setformat, 	audio_soc_chan_setformat),
274 	KOBJMETHOD(channel_setspeed, 	audio_soc_chan_setspeed),
275 	KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize),
276 	KOBJMETHOD(channel_trigger,	audio_soc_chan_trigger),
277 	KOBJMETHOD(channel_getptr,	audio_soc_chan_getptr),
278 	KOBJMETHOD(channel_getcaps,	audio_soc_chan_getcaps),
279 	KOBJMETHOD_END
280 };
281 CHANNEL_DECLARE(audio_soc_chan);
282 
283 static void
audio_soc_intr(void * arg)284 audio_soc_intr(void *arg)
285 {
286 	struct audio_soc_softc *sc;
287 	int channel_intr_required;
288 
289 	sc = (struct audio_soc_softc *)arg;
290 	channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf);
291 	if (channel_intr_required & AUDIO_DAI_PLAY_INTR)
292 		chn_intr(sc->play_channel.pcm);
293 	if (channel_intr_required & AUDIO_DAI_REC_INTR)
294 		chn_intr(sc->rec_channel.pcm);
295 }
296 
297 static int
audio_soc_probe(device_t dev)298 audio_soc_probe(device_t dev)
299 {
300 
301 	if (!ofw_bus_status_okay(dev))
302 		return (ENXIO);
303 
304 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
305 		device_set_desc(dev, "simple-audio-card");
306 		return (BUS_PROBE_DEFAULT);
307 	}
308 
309 	return (ENXIO);
310 }
311 
312 static void
audio_soc_init(void * arg)313 audio_soc_init(void *arg)
314 {
315 	struct audio_soc_softc *sc;
316 	phandle_t node, child;
317 	device_t daidev, auxdev;
318 	uint32_t xref;
319 	uint32_t *aux_devs;
320 	int ncells, i;
321 	struct audio_soc_aux_node *aux_node;
322 
323 	sc = (struct audio_soc_softc *)arg;
324 	config_intrhook_disestablish(&sc->init_hook);
325 
326 	node = ofw_bus_get_node(sc->dev);
327 	/* TODO: handle multi-link nodes */
328 	child = ofw_bus_find_child(node, "simple-audio-card,cpu");
329 	if (child == 0) {
330 		device_printf(sc->dev, "cpu node is missing\n");
331 		return;
332 	}
333 	if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
334 		device_printf(sc->dev, "missing sound-dai property in cpu node\n");
335 		return;
336 	}
337 	daidev = OF_device_from_xref(xref);
338 	if (daidev == NULL) {
339 		device_printf(sc->dev, "no driver attached to cpu node\n");
340 		return;
341 	}
342 	sc->cpu_dev = daidev;
343 
344 	child = ofw_bus_find_child(node, "simple-audio-card,codec");
345 	if (child == 0) {
346 		device_printf(sc->dev, "codec node is missing\n");
347 		return;
348 	}
349 	if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
350 		device_printf(sc->dev, "missing sound-dai property in codec node\n");
351 		return;
352 	}
353 	daidev = OF_device_from_xref(xref);
354 	if (daidev == NULL) {
355 		device_printf(sc->dev, "no driver attached to codec node\n");
356 		return;
357 	}
358 	sc->codec_dev = daidev;
359 
360 	/* Add AUX devices */
361 	aux_devs = NULL;
362 	ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs),
363 	    (void **)&aux_devs);
364 
365 	for (i = 0; i < ncells; i++) {
366 		auxdev = OF_device_from_xref(aux_devs[i]);
367 		if (auxdev == NULL)
368 			device_printf(sc->dev, "warning: no driver attached to aux node\n");
369 		aux_node = malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
370 		if (aux_node == NULL) {
371 			device_printf(sc->dev, "failed to allocate aux node struct\n");
372 			return;
373 		}
374 		aux_node->dev = auxdev;
375 		SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link);
376 	}
377 
378 	if (aux_devs)
379 		OF_prop_free(aux_devs);
380 
381 	if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) {
382 		device_printf(sc->dev, "failed to initialize cpu node\n");
383 		return;
384 	}
385 
386 	/* Reverse clock roles for CODEC */
387 	if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) {
388 		device_printf(sc->dev, "failed to initialize codec node\n");
389 		return;
390 	}
391 
392 	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
393 		if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) {
394 			device_printf(sc->dev, "failed to initialize aux node\n");
395 			return;
396 		}
397 	}
398 
399 	pcm_init(sc->dev, sc);
400 
401 	sc->play_channel.sc = sc;
402 	sc->rec_channel.sc = sc;
403 
404 	pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
405 	pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
406 
407 	if (pcm_register(sc->dev, "at simplebus")) {
408 		device_printf(sc->dev, "failed to register PCM\n");
409 		return;
410 	}
411 
412 	AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
413 	AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
414 	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
415 		AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev);
416 	}
417 }
418 
419 static int
audio_soc_attach(device_t dev)420 audio_soc_attach(device_t dev)
421 {
422 	struct audio_soc_softc *sc;
423 	char *name;
424 	phandle_t node, cpu_child;
425 	uint32_t xref;
426 	int i, ret;
427 	char tmp[32];
428 	unsigned int fmt, pol, clk;
429 	bool frame_master, bitclock_master;
430 
431 	sc = device_get_softc(dev);
432 	sc->dev = dev;
433 	node = ofw_bus_get_node(dev);
434 
435 	ret = OF_getprop_alloc(node, "name", (void **)&name);
436 	if (ret == -1)
437 		name = "SoC audio";
438 
439 	sc->name = strdup(name, M_DEVBUF);
440 	device_set_desc(dev, sc->name);
441 
442 	if (ret != -1)
443 		OF_prop_free(name);
444 
445 	SLIST_INIT(&sc->aux_devs);
446 
447 	ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp));
448 	if (ret == 0) {
449 		for (i = 0; i < nitems(ausoc_dai_formats); i++) {
450 			if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) {
451 				fmt = ausoc_dai_formats[i].fmt;
452 				break;
453 			}
454 		}
455 		if (i == nitems(ausoc_dai_formats))
456 			return (EINVAL);
457 	} else
458 		fmt = AUDIO_DAI_FORMAT_I2S;
459 
460 	if (OF_getencprop(node, "simple-audio-card,mclk-fs",
461 	    &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0)
462 		sc->link_mclk_fs = 0;
463 
464 	/* Unless specified otherwise, CPU node is the master */
465 	frame_master = bitclock_master = true;
466 
467 	cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu");
468 
469 	if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0)
470 		frame_master = cpu_child == OF_node_from_xref(xref);
471 
472 	if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0)
473 		bitclock_master = cpu_child == OF_node_from_xref(xref);
474 
475 	if (frame_master) {
476 		clk = bitclock_master ?
477 		    AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
478 	} else {
479 		clk = bitclock_master ?
480 		    AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
481 	}
482 
483 	bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion");
484 	bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion");
485 	if (bitclock_inversion) {
486 		pol = frame_inversion ?
487 		    AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
488 	} else {
489 		pol = frame_inversion ?
490 		    AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
491 	}
492 
493 	sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk);
494 
495 	sc->init_hook.ich_func = audio_soc_init;
496 	sc->init_hook.ich_arg = sc;
497 	if (config_intrhook_establish(&sc->init_hook) != 0)
498 		return (ENOMEM);
499 
500 	return (0);
501 }
502 
503 static int
audio_soc_detach(device_t dev)504 audio_soc_detach(device_t dev)
505 {
506 	struct audio_soc_softc *sc;
507 	struct audio_soc_aux_node *aux;
508 
509 	sc = device_get_softc(dev);
510 	free(sc->name, M_DEVBUF);
511 
512 	while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) {
513 		SLIST_REMOVE_HEAD(&sc->aux_devs, link);
514 		free(aux, M_DEVBUF);
515 	}
516 
517 	return (0);
518 }
519 
520 static device_method_t audio_soc_methods[] = {
521         /* device_if methods */
522 	DEVMETHOD(device_probe,		audio_soc_probe),
523 	DEVMETHOD(device_attach,	audio_soc_attach),
524 	DEVMETHOD(device_detach,	audio_soc_detach),
525 
526 	DEVMETHOD_END,
527 };
528 
529 static driver_t audio_soc_driver = {
530 	"pcm",
531 	audio_soc_methods,
532 	sizeof(struct audio_soc_softc),
533 };
534 
535 DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, NULL, NULL);
536 MODULE_VERSION(audio_soc, 1);
537