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
47struct audio_soc_aux_node {
48	SLIST_ENTRY(audio_soc_aux_node)	link;
49	device_t			dev;
50};
51
52struct 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
59struct 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
82static struct ofw_compat_data compat_data[] = {
83	{"simple-audio-card",	1},
84	{NULL,			0},
85};
86
87static 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
100static int	audio_soc_probe(device_t dev);
101static int	audio_soc_attach(device_t dev);
102static int	audio_soc_detach(device_t dev);
103
104/*
105 * Invert master/slave roles for CODEC side of the node
106 */
107static uint32_t
108audio_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
134static uint32_t
135audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz)
136{
137
138	return (blocksz);
139}
140
141static int
142audio_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
154static uint32_t
155audio_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
192static uint32_t
193audio_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
204static void *
205audio_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
226static int
227audio_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
243static int
244audio_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 = sndbuf_getbuf(ausoc_chan->buf);
253	if (buffer)
254		free(buffer, M_DEVBUF);
255
256	return (0);
257}
258
259static struct pcmchan_caps *
260audio_soc_chan_getcaps(kobj_t obj, void *data)
261{
262	struct audio_soc_softc *sc;
263	struct audio_soc_channel *ausoc_chan;
264
265	ausoc_chan = data;
266	sc = ausoc_chan->sc;
267
268	return AUDIO_DAI_GET_CAPS(sc->cpu_dev);
269}
270
271static kobj_method_t audio_soc_chan_methods[] = {
272	KOBJMETHOD(channel_init, 	audio_soc_chan_init),
273	KOBJMETHOD(channel_free, 	audio_soc_chan_free),
274	KOBJMETHOD(channel_setformat, 	audio_soc_chan_setformat),
275	KOBJMETHOD(channel_setspeed, 	audio_soc_chan_setspeed),
276	KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize),
277	KOBJMETHOD(channel_trigger,	audio_soc_chan_trigger),
278	KOBJMETHOD(channel_getptr,	audio_soc_chan_getptr),
279	KOBJMETHOD(channel_getcaps,	audio_soc_chan_getcaps),
280	KOBJMETHOD_END
281};
282CHANNEL_DECLARE(audio_soc_chan);
283
284static void
285audio_soc_intr(void *arg)
286{
287	struct audio_soc_softc *sc;
288	int channel_intr_required;
289
290	sc = (struct audio_soc_softc *)arg;
291	channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf);
292	if (channel_intr_required & AUDIO_DAI_PLAY_INTR)
293		chn_intr(sc->play_channel.pcm);
294	if (channel_intr_required & AUDIO_DAI_REC_INTR)
295		chn_intr(sc->rec_channel.pcm);
296}
297
298static int
299audio_soc_probe(device_t dev)
300{
301
302	if (!ofw_bus_status_okay(dev))
303		return (ENXIO);
304
305	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
306		device_set_desc(dev, "simple-audio-card");
307		return (BUS_PROBE_DEFAULT);
308	}
309
310	return (ENXIO);
311}
312
313static void
314audio_soc_init(void *arg)
315{
316	struct audio_soc_softc *sc;
317	phandle_t node, child;
318	device_t daidev, auxdev;
319	uint32_t xref;
320	uint32_t *aux_devs;
321	int ncells, i;
322	struct audio_soc_aux_node *aux_node;
323
324	sc = (struct audio_soc_softc *)arg;
325	config_intrhook_disestablish(&sc->init_hook);
326
327	node = ofw_bus_get_node(sc->dev);
328	/* TODO: handle multi-link nodes */
329	child = ofw_bus_find_child(node, "simple-audio-card,cpu");
330	if (child == 0) {
331		device_printf(sc->dev, "cpu node is missing\n");
332		return;
333	}
334	if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
335		device_printf(sc->dev, "missing sound-dai property in cpu node\n");
336		return;
337	}
338	daidev = OF_device_from_xref(xref);
339	if (daidev == NULL) {
340		device_printf(sc->dev, "no driver attached to cpu node\n");
341		return;
342	}
343	sc->cpu_dev = daidev;
344
345	child = ofw_bus_find_child(node, "simple-audio-card,codec");
346	if (child == 0) {
347		device_printf(sc->dev, "codec node is missing\n");
348		return;
349	}
350	if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
351		device_printf(sc->dev, "missing sound-dai property in codec node\n");
352		return;
353	}
354	daidev = OF_device_from_xref(xref);
355	if (daidev == NULL) {
356		device_printf(sc->dev, "no driver attached to codec node\n");
357		return;
358	}
359	sc->codec_dev = daidev;
360
361	/* Add AUX devices */
362	aux_devs = NULL;
363	ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs),
364	    (void **)&aux_devs);
365
366	for (i = 0; i < ncells; i++) {
367		auxdev = OF_device_from_xref(aux_devs[i]);
368		if (auxdev == NULL)
369			device_printf(sc->dev, "warning: no driver attached to aux node\n");
370		aux_node = (struct audio_soc_aux_node *)malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
371		if (aux_node == NULL) {
372			device_printf(sc->dev, "failed to allocate aux node struct\n");
373			return;
374		}
375		aux_node->dev = auxdev;
376		SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link);
377	}
378
379	if (aux_devs)
380		OF_prop_free(aux_devs);
381
382	if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) {
383		device_printf(sc->dev, "failed to initialize cpu node\n");
384		return;
385	}
386
387	/* Reverse clock roles for CODEC */
388	if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) {
389		device_printf(sc->dev, "failed to initialize codec node\n");
390		return;
391	}
392
393	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
394		if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) {
395			device_printf(sc->dev, "failed to initialize aux node\n");
396			return;
397		}
398	}
399
400	if (pcm_register(sc->dev, sc, 1, 1)) {
401		device_printf(sc->dev, "failed to register PCM\n");
402		return;
403	}
404
405	sc->play_channel.sc = sc;
406	sc->rec_channel.sc = sc;
407
408	pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
409	pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
410
411	pcm_setstatus(sc->dev, "at simplebus");
412
413	AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
414	AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
415	SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
416		AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev);
417	}
418}
419
420static int
421audio_soc_attach(device_t dev)
422{
423	struct audio_soc_softc *sc;
424	char *name;
425	phandle_t node, cpu_child;
426	uint32_t xref;
427	int i, ret;
428	char tmp[32];
429	unsigned int fmt, pol, clk;
430	bool frame_master, bitclock_master;
431
432	sc = device_get_softc(dev);
433	sc->dev = dev;
434	node = ofw_bus_get_node(dev);
435
436	ret = OF_getprop_alloc(node, "name", (void **)&name);
437	if (ret == -1)
438		name = "SoC audio";
439
440	sc->name = strdup(name, M_DEVBUF);
441	device_set_desc(dev, sc->name);
442
443	if (ret != -1)
444		OF_prop_free(name);
445
446	SLIST_INIT(&sc->aux_devs);
447
448	ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp));
449	if (ret == 0) {
450		for (i = 0; i < nitems(ausoc_dai_formats); i++) {
451			if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) {
452				fmt = ausoc_dai_formats[i].fmt;
453				break;
454			}
455		}
456		if (i == nitems(ausoc_dai_formats))
457			return (EINVAL);
458	} else
459		fmt = AUDIO_DAI_FORMAT_I2S;
460
461	if (OF_getencprop(node, "simple-audio-card,mclk-fs",
462	    &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0)
463		sc->link_mclk_fs = 0;
464
465	/* Unless specified otherwise, CPU node is the master */
466	frame_master = bitclock_master = true;
467
468	cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu");
469
470	if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0)
471		frame_master = cpu_child == OF_node_from_xref(xref);
472
473	if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0)
474		bitclock_master = cpu_child == OF_node_from_xref(xref);
475
476	if (frame_master) {
477		clk = bitclock_master ?
478		    AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
479	} else {
480		clk = bitclock_master ?
481		    AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
482	}
483
484	bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion");
485	bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion");
486	if (bitclock_inversion) {
487		pol = frame_inversion ?
488		    AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
489	} else {
490		pol = frame_inversion ?
491		    AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
492	}
493
494	sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk);
495
496	sc->init_hook.ich_func = audio_soc_init;
497	sc->init_hook.ich_arg = sc;
498	if (config_intrhook_establish(&sc->init_hook) != 0)
499		return (ENOMEM);
500
501	return (0);
502}
503
504static int
505audio_soc_detach(device_t dev)
506{
507	struct audio_soc_softc *sc;
508	struct audio_soc_aux_node *aux;
509
510	sc = device_get_softc(dev);
511	if (sc->name)
512		free(sc->name, M_DEVBUF);
513
514	while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) {
515		SLIST_REMOVE_HEAD(&sc->aux_devs, link);
516		free(aux, M_DEVBUF);
517	}
518
519	return (0);
520}
521
522static device_method_t audio_soc_methods[] = {
523        /* device_if methods */
524	DEVMETHOD(device_probe,		audio_soc_probe),
525	DEVMETHOD(device_attach,	audio_soc_attach),
526	DEVMETHOD(device_detach,	audio_soc_detach),
527
528	DEVMETHOD_END,
529};
530
531static driver_t audio_soc_driver = {
532	"pcm",
533	audio_soc_methods,
534	sizeof(struct audio_soc_softc),
535};
536
537DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, NULL, NULL);
538MODULE_VERSION(audio_soc, 1);
539