xref: /netbsd/sys/dev/fdt/ausoc.c (revision 8e90f9ed)
1*8e90f9edSthorpej /* $NetBSD: ausoc.c,v 1.6 2021/01/27 03:10:21 thorpej Exp $ */
2aacbd9a6Sjmcneill 
3aacbd9a6Sjmcneill /*-
4aacbd9a6Sjmcneill  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
5aacbd9a6Sjmcneill  * All rights reserved.
6aacbd9a6Sjmcneill  *
7aacbd9a6Sjmcneill  * Redistribution and use in source and binary forms, with or without
8aacbd9a6Sjmcneill  * modification, are permitted provided that the following conditions
9aacbd9a6Sjmcneill  * are met:
10aacbd9a6Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11aacbd9a6Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12aacbd9a6Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13aacbd9a6Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14aacbd9a6Sjmcneill  *    documentation and/or other materials provided with the distribution.
15aacbd9a6Sjmcneill  *
16aacbd9a6Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17aacbd9a6Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18aacbd9a6Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19aacbd9a6Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20aacbd9a6Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21aacbd9a6Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22aacbd9a6Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23aacbd9a6Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24aacbd9a6Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25aacbd9a6Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26aacbd9a6Sjmcneill  * SUCH DAMAGE.
27aacbd9a6Sjmcneill  */
28aacbd9a6Sjmcneill 
29aacbd9a6Sjmcneill #include <sys/cdefs.h>
30*8e90f9edSthorpej __KERNEL_RCSID(0, "$NetBSD: ausoc.c,v 1.6 2021/01/27 03:10:21 thorpej Exp $");
31aacbd9a6Sjmcneill 
32aacbd9a6Sjmcneill #include <sys/param.h>
33aacbd9a6Sjmcneill #include <sys/bus.h>
34aacbd9a6Sjmcneill #include <sys/cpu.h>
35aacbd9a6Sjmcneill #include <sys/device.h>
36aacbd9a6Sjmcneill #include <sys/kmem.h>
37aacbd9a6Sjmcneill #include <sys/gpio.h>
38aacbd9a6Sjmcneill 
39aacbd9a6Sjmcneill #include <sys/audioio.h>
40b950503fSisaki #include <dev/audio/audio_if.h>
41b950503fSisaki #include <dev/audio/audio_dai.h>
42aacbd9a6Sjmcneill 
43aacbd9a6Sjmcneill #include <dev/fdt/fdtvar.h>
44aacbd9a6Sjmcneill 
45*8e90f9edSthorpej static const struct device_compatible_entry compat_data[] = {
46*8e90f9edSthorpej 	{ .compat = "simple-audio-card" },
47*8e90f9edSthorpej 	DEVICE_COMPAT_EOL
48*8e90f9edSthorpej };
49aacbd9a6Sjmcneill 
50aacbd9a6Sjmcneill struct ausoc_link {
51aacbd9a6Sjmcneill 	const char		*link_name;
52aacbd9a6Sjmcneill 
53aacbd9a6Sjmcneill 	audio_dai_tag_t		link_cpu;
54aacbd9a6Sjmcneill 	audio_dai_tag_t		link_codec;
55aacbd9a6Sjmcneill 	audio_dai_tag_t		*link_aux;
56aacbd9a6Sjmcneill 	u_int			link_naux;
57aacbd9a6Sjmcneill 
58aacbd9a6Sjmcneill 	u_int			link_mclk_fs;
59aacbd9a6Sjmcneill 
60aacbd9a6Sjmcneill 	kmutex_t		link_lock;
61aacbd9a6Sjmcneill 	kmutex_t		link_intr_lock;
62aacbd9a6Sjmcneill };
63aacbd9a6Sjmcneill 
64aacbd9a6Sjmcneill struct ausoc_softc {
65aacbd9a6Sjmcneill 	device_t		sc_dev;
66aacbd9a6Sjmcneill 	int			sc_phandle;
67aacbd9a6Sjmcneill 	const char		*sc_name;
68aacbd9a6Sjmcneill 
69aacbd9a6Sjmcneill 	struct ausoc_link	*sc_link;
70aacbd9a6Sjmcneill 	u_int			sc_nlink;
71aacbd9a6Sjmcneill };
72aacbd9a6Sjmcneill 
73aacbd9a6Sjmcneill static void
ausoc_close(void * priv)74aacbd9a6Sjmcneill ausoc_close(void *priv)
75aacbd9a6Sjmcneill {
76aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
77aacbd9a6Sjmcneill 	u_int aux;
78aacbd9a6Sjmcneill 
79aacbd9a6Sjmcneill 	for (aux = 0; aux < link->link_naux; aux++)
80aacbd9a6Sjmcneill 		audio_dai_close(link->link_aux[aux]);
81aacbd9a6Sjmcneill 	audio_dai_close(link->link_codec);
82aacbd9a6Sjmcneill 	audio_dai_close(link->link_cpu);
83aacbd9a6Sjmcneill }
84aacbd9a6Sjmcneill 
85aacbd9a6Sjmcneill static int
ausoc_open(void * priv,int flags)86aacbd9a6Sjmcneill ausoc_open(void *priv, int flags)
87aacbd9a6Sjmcneill {
88aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
89aacbd9a6Sjmcneill 	u_int aux;
90aacbd9a6Sjmcneill 	int error;
91aacbd9a6Sjmcneill 
92aacbd9a6Sjmcneill 	error = audio_dai_open(link->link_cpu, flags);
93aacbd9a6Sjmcneill 	if (error)
94aacbd9a6Sjmcneill 		goto failed;
95aacbd9a6Sjmcneill 
96aacbd9a6Sjmcneill 	error = audio_dai_open(link->link_codec, flags);
97aacbd9a6Sjmcneill 	if (error)
98aacbd9a6Sjmcneill 		goto failed;
99aacbd9a6Sjmcneill 
100aacbd9a6Sjmcneill 	for (aux = 0; aux < link->link_naux; aux++) {
101aacbd9a6Sjmcneill 		error = audio_dai_open(link->link_aux[aux], flags);
102aacbd9a6Sjmcneill 		if (error)
103aacbd9a6Sjmcneill 			goto failed;
104aacbd9a6Sjmcneill 	}
105aacbd9a6Sjmcneill 
106aacbd9a6Sjmcneill 	return 0;
107aacbd9a6Sjmcneill 
108aacbd9a6Sjmcneill failed:
109aacbd9a6Sjmcneill 	ausoc_close(priv);
110aacbd9a6Sjmcneill 	return error;
111aacbd9a6Sjmcneill }
112aacbd9a6Sjmcneill 
113aacbd9a6Sjmcneill static int
ausoc_query_format(void * priv,audio_format_query_t * afp)114b950503fSisaki ausoc_query_format(void *priv, audio_format_query_t *afp)
115aacbd9a6Sjmcneill {
116aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
117aacbd9a6Sjmcneill 
118b950503fSisaki 	return audio_dai_query_format(link->link_cpu, afp);
119aacbd9a6Sjmcneill }
120aacbd9a6Sjmcneill 
121aacbd9a6Sjmcneill static int
ausoc_set_format(void * priv,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)122b950503fSisaki ausoc_set_format(void *priv, int setmode,
123b950503fSisaki     const audio_params_t *play, const audio_params_t *rec,
124b950503fSisaki     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
125aacbd9a6Sjmcneill {
126aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
127b26779d1Sjmcneill 	const audio_params_t *params = (setmode & AUMODE_PLAY) != 0 ?
128b26779d1Sjmcneill 	    play : rec;
129aacbd9a6Sjmcneill 	int error;
130aacbd9a6Sjmcneill 
131b26779d1Sjmcneill 	if (link->link_mclk_fs) {
132b26779d1Sjmcneill 		const u_int rate = params->sample_rate * link->link_mclk_fs;
133b26779d1Sjmcneill 		error = audio_dai_set_sysclk(link->link_codec, rate,
134b26779d1Sjmcneill 		    AUDIO_DAI_CLOCK_IN);
135b26779d1Sjmcneill 		if (error)
136b26779d1Sjmcneill 			return error;
137b26779d1Sjmcneill 		error = audio_dai_set_sysclk(link->link_cpu, rate,
138b26779d1Sjmcneill 		    AUDIO_DAI_CLOCK_OUT);
139b26779d1Sjmcneill 		if (error)
140b26779d1Sjmcneill 			return error;
141b26779d1Sjmcneill 	}
142b26779d1Sjmcneill 
143b950503fSisaki 	error = audio_dai_mi_set_format(link->link_cpu, setmode,
144b950503fSisaki 	    play, rec, pfil, rfil);
145aacbd9a6Sjmcneill 	if (error)
146aacbd9a6Sjmcneill 		return error;
147aacbd9a6Sjmcneill 
148b950503fSisaki 	return audio_dai_mi_set_format(link->link_codec, setmode,
149b950503fSisaki 	    play, rec, pfil, rfil);
150aacbd9a6Sjmcneill }
151aacbd9a6Sjmcneill 
152aacbd9a6Sjmcneill static int
ausoc_set_port(void * priv,mixer_ctrl_t * mc)153aacbd9a6Sjmcneill ausoc_set_port(void *priv, mixer_ctrl_t *mc)
154aacbd9a6Sjmcneill {
155aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
156aacbd9a6Sjmcneill 
157498de7edSjmcneill 	return audio_dai_set_port(link->link_codec, mc);
158aacbd9a6Sjmcneill }
159aacbd9a6Sjmcneill 
160aacbd9a6Sjmcneill static int
ausoc_get_port(void * priv,mixer_ctrl_t * mc)161aacbd9a6Sjmcneill ausoc_get_port(void *priv, mixer_ctrl_t *mc)
162aacbd9a6Sjmcneill {
163aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
164aacbd9a6Sjmcneill 
165498de7edSjmcneill 	return audio_dai_get_port(link->link_codec, mc);
166aacbd9a6Sjmcneill }
167aacbd9a6Sjmcneill 
168aacbd9a6Sjmcneill static int
ausoc_query_devinfo(void * priv,mixer_devinfo_t * di)169aacbd9a6Sjmcneill ausoc_query_devinfo(void *priv, mixer_devinfo_t *di)
170aacbd9a6Sjmcneill {
171aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
172aacbd9a6Sjmcneill 
173498de7edSjmcneill 	return audio_dai_query_devinfo(link->link_codec, di);
174aacbd9a6Sjmcneill }
175aacbd9a6Sjmcneill 
176aacbd9a6Sjmcneill static void *
ausoc_allocm(void * priv,int dir,size_t size)177aacbd9a6Sjmcneill ausoc_allocm(void *priv, int dir, size_t size)
178aacbd9a6Sjmcneill {
179aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
180aacbd9a6Sjmcneill 
181aacbd9a6Sjmcneill 	return audio_dai_allocm(link->link_cpu, dir, size);
182aacbd9a6Sjmcneill }
183aacbd9a6Sjmcneill 
184aacbd9a6Sjmcneill static void
ausoc_freem(void * priv,void * addr,size_t size)185aacbd9a6Sjmcneill ausoc_freem(void *priv, void *addr, size_t size)
186aacbd9a6Sjmcneill {
187aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
188aacbd9a6Sjmcneill 
189aacbd9a6Sjmcneill 	return audio_dai_freem(link->link_cpu, addr, size);
190aacbd9a6Sjmcneill }
191aacbd9a6Sjmcneill 
192aacbd9a6Sjmcneill static int
ausoc_getdev(void * priv,struct audio_device * adev)193aacbd9a6Sjmcneill ausoc_getdev(void *priv, struct audio_device *adev)
194aacbd9a6Sjmcneill {
195aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
196aacbd9a6Sjmcneill 
197aacbd9a6Sjmcneill 	/* Defaults */
198aacbd9a6Sjmcneill 	snprintf(adev->name, sizeof(adev->name), "%s", link->link_name);
199aacbd9a6Sjmcneill 	snprintf(adev->version, sizeof(adev->version), "");
200aacbd9a6Sjmcneill 	snprintf(adev->config, sizeof(adev->config), "ausoc");
201aacbd9a6Sjmcneill 
202aacbd9a6Sjmcneill 	/* Codec can override */
203aacbd9a6Sjmcneill 	(void)audio_dai_getdev(link->link_codec, adev);
204aacbd9a6Sjmcneill 
205aacbd9a6Sjmcneill 	return 0;
206aacbd9a6Sjmcneill }
207aacbd9a6Sjmcneill 
208aacbd9a6Sjmcneill static int
ausoc_get_props(void * priv)209aacbd9a6Sjmcneill ausoc_get_props(void *priv)
210aacbd9a6Sjmcneill {
211aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
212aacbd9a6Sjmcneill 
213aacbd9a6Sjmcneill 	return audio_dai_get_props(link->link_cpu);
214aacbd9a6Sjmcneill }
215aacbd9a6Sjmcneill 
216aacbd9a6Sjmcneill static int
ausoc_round_blocksize(void * priv,int bs,int mode,const audio_params_t * params)217aacbd9a6Sjmcneill ausoc_round_blocksize(void *priv, int bs, int mode,
218aacbd9a6Sjmcneill     const audio_params_t *params)
219aacbd9a6Sjmcneill {
220aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
221aacbd9a6Sjmcneill 
222aacbd9a6Sjmcneill 	return audio_dai_round_blocksize(link->link_cpu, bs, mode, params);
223aacbd9a6Sjmcneill }
224aacbd9a6Sjmcneill 
225aacbd9a6Sjmcneill static size_t
ausoc_round_buffersize(void * priv,int dir,size_t bufsize)226aacbd9a6Sjmcneill ausoc_round_buffersize(void *priv, int dir, size_t bufsize)
227aacbd9a6Sjmcneill {
228aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
229aacbd9a6Sjmcneill 
230aacbd9a6Sjmcneill 	return audio_dai_round_buffersize(link->link_cpu, dir, bufsize);
231aacbd9a6Sjmcneill }
232aacbd9a6Sjmcneill 
233aacbd9a6Sjmcneill static int
ausoc_halt_output(void * priv)234aacbd9a6Sjmcneill ausoc_halt_output(void *priv)
235aacbd9a6Sjmcneill {
236aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
237aacbd9a6Sjmcneill 	u_int n;
238aacbd9a6Sjmcneill 
239aacbd9a6Sjmcneill 	for (n = 0; n < link->link_naux; n++)
240aacbd9a6Sjmcneill 		audio_dai_halt(link->link_aux[n], AUMODE_PLAY);
241aacbd9a6Sjmcneill 
242aacbd9a6Sjmcneill 	audio_dai_halt(link->link_codec, AUMODE_PLAY);
243aacbd9a6Sjmcneill 
244aacbd9a6Sjmcneill 	return audio_dai_halt(link->link_cpu, AUMODE_PLAY);
245aacbd9a6Sjmcneill }
246aacbd9a6Sjmcneill 
247aacbd9a6Sjmcneill static int
ausoc_halt_input(void * priv)248aacbd9a6Sjmcneill ausoc_halt_input(void *priv)
249aacbd9a6Sjmcneill {
250aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
251aacbd9a6Sjmcneill 	u_int n;
252aacbd9a6Sjmcneill 
253aacbd9a6Sjmcneill 	for (n = 0; n < link->link_naux; n++)
254aacbd9a6Sjmcneill 		audio_dai_halt(link->link_aux[n], AUMODE_RECORD);
255aacbd9a6Sjmcneill 
256aacbd9a6Sjmcneill 	audio_dai_halt(link->link_codec, AUMODE_RECORD);
257aacbd9a6Sjmcneill 
258aacbd9a6Sjmcneill 	return audio_dai_halt(link->link_cpu, AUMODE_RECORD);
259aacbd9a6Sjmcneill }
260aacbd9a6Sjmcneill 
261aacbd9a6Sjmcneill static int
ausoc_trigger_output(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)262aacbd9a6Sjmcneill ausoc_trigger_output(void *priv, void *start, void *end, int blksize,
263aacbd9a6Sjmcneill     void (*intr)(void *), void *intrarg, const audio_params_t *params)
264aacbd9a6Sjmcneill {
265aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
266aacbd9a6Sjmcneill 	int error;
267b26779d1Sjmcneill 	u_int n;
268aacbd9a6Sjmcneill 
269aacbd9a6Sjmcneill 	for (n = 0; n < link->link_naux; n++) {
270aacbd9a6Sjmcneill 		error = audio_dai_trigger(link->link_aux[n], start, end,
271aacbd9a6Sjmcneill 		    blksize, intr, intrarg, params, AUMODE_PLAY);
272aacbd9a6Sjmcneill 		if (error)
273aacbd9a6Sjmcneill 			goto failed;
274aacbd9a6Sjmcneill 	}
275aacbd9a6Sjmcneill 	error = audio_dai_trigger(link->link_codec, start, end, blksize,
276aacbd9a6Sjmcneill 	    intr, intrarg, params, AUMODE_PLAY);
277aacbd9a6Sjmcneill 	if (error)
278aacbd9a6Sjmcneill 		goto failed;
279aacbd9a6Sjmcneill 
280aacbd9a6Sjmcneill 	return audio_dai_trigger(link->link_cpu, start, end, blksize,
281aacbd9a6Sjmcneill 	    intr, intrarg, params, AUMODE_PLAY);
282aacbd9a6Sjmcneill 
283aacbd9a6Sjmcneill failed:
284aacbd9a6Sjmcneill 	ausoc_halt_output(priv);
285aacbd9a6Sjmcneill 	return error;
286aacbd9a6Sjmcneill }
287aacbd9a6Sjmcneill 
288aacbd9a6Sjmcneill static int
ausoc_trigger_input(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)289aacbd9a6Sjmcneill ausoc_trigger_input(void *priv, void *start, void *end, int blksize,
290aacbd9a6Sjmcneill     void (*intr)(void *), void *intrarg, const audio_params_t *params)
291aacbd9a6Sjmcneill {
292aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
293aacbd9a6Sjmcneill 	int error;
294b26779d1Sjmcneill 	u_int n;
295aacbd9a6Sjmcneill 
296aacbd9a6Sjmcneill 	for (n = 0; n < link->link_naux; n++) {
297aacbd9a6Sjmcneill 		error = audio_dai_trigger(link->link_aux[n], start, end,
298aacbd9a6Sjmcneill 		    blksize, intr, intrarg, params, AUMODE_RECORD);
299aacbd9a6Sjmcneill 		if (error)
300aacbd9a6Sjmcneill 			goto failed;
301aacbd9a6Sjmcneill 	}
302aacbd9a6Sjmcneill 	error = audio_dai_trigger(link->link_codec, start, end, blksize,
303aacbd9a6Sjmcneill 	    intr, intrarg, params, AUMODE_RECORD);
304aacbd9a6Sjmcneill 	if (error)
305aacbd9a6Sjmcneill 		goto failed;
306aacbd9a6Sjmcneill 
307aacbd9a6Sjmcneill 	return audio_dai_trigger(link->link_cpu, start, end, blksize,
308aacbd9a6Sjmcneill 	    intr, intrarg, params, AUMODE_RECORD);
309aacbd9a6Sjmcneill 
310aacbd9a6Sjmcneill failed:
311aacbd9a6Sjmcneill 	ausoc_halt_input(priv);
312aacbd9a6Sjmcneill 	return error;
313aacbd9a6Sjmcneill }
314aacbd9a6Sjmcneill 
315aacbd9a6Sjmcneill static void
ausoc_get_locks(void * priv,kmutex_t ** intr,kmutex_t ** thread)316aacbd9a6Sjmcneill ausoc_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
317aacbd9a6Sjmcneill {
318aacbd9a6Sjmcneill 	struct ausoc_link * const link = priv;
319aacbd9a6Sjmcneill 
320aacbd9a6Sjmcneill 	return audio_dai_get_locks(link->link_cpu, intr, thread);
321aacbd9a6Sjmcneill }
322aacbd9a6Sjmcneill 
323aacbd9a6Sjmcneill static const struct audio_hw_if ausoc_hw_if = {
324aacbd9a6Sjmcneill 	.open = ausoc_open,
325aacbd9a6Sjmcneill 	.close = ausoc_close,
326b950503fSisaki 	.query_format = ausoc_query_format,
327b950503fSisaki 	.set_format = ausoc_set_format,
328aacbd9a6Sjmcneill 	.allocm = ausoc_allocm,
329aacbd9a6Sjmcneill 	.freem = ausoc_freem,
330aacbd9a6Sjmcneill 	.getdev = ausoc_getdev,
331aacbd9a6Sjmcneill 	.set_port = ausoc_set_port,
332aacbd9a6Sjmcneill 	.get_port = ausoc_get_port,
333aacbd9a6Sjmcneill 	.query_devinfo = ausoc_query_devinfo,
334aacbd9a6Sjmcneill 	.get_props = ausoc_get_props,
335aacbd9a6Sjmcneill 	.round_blocksize = ausoc_round_blocksize,
336aacbd9a6Sjmcneill 	.round_buffersize = ausoc_round_buffersize,
337aacbd9a6Sjmcneill 	.trigger_output = ausoc_trigger_output,
338aacbd9a6Sjmcneill 	.trigger_input = ausoc_trigger_input,
339aacbd9a6Sjmcneill 	.halt_output = ausoc_halt_output,
340aacbd9a6Sjmcneill 	.halt_input = ausoc_halt_input,
341aacbd9a6Sjmcneill 	.get_locks = ausoc_get_locks,
342aacbd9a6Sjmcneill };
343aacbd9a6Sjmcneill 
344aacbd9a6Sjmcneill static int
ausoc_match(device_t parent,cfdata_t cf,void * aux)345aacbd9a6Sjmcneill ausoc_match(device_t parent, cfdata_t cf, void *aux)
346aacbd9a6Sjmcneill {
347aacbd9a6Sjmcneill 	struct fdt_attach_args * const faa = aux;
348aacbd9a6Sjmcneill 
349*8e90f9edSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
350aacbd9a6Sjmcneill }
351aacbd9a6Sjmcneill 
352aacbd9a6Sjmcneill static struct {
353aacbd9a6Sjmcneill 	const char *name;
354aacbd9a6Sjmcneill 	u_int fmt;
355aacbd9a6Sjmcneill } ausoc_dai_formats[] = {
356aacbd9a6Sjmcneill 	{ "i2s",	AUDIO_DAI_FORMAT_I2S },
357aacbd9a6Sjmcneill 	{ "right_j",	AUDIO_DAI_FORMAT_RJ },
358aacbd9a6Sjmcneill 	{ "left_j",	AUDIO_DAI_FORMAT_LJ },
359aacbd9a6Sjmcneill 	{ "dsp_a",	AUDIO_DAI_FORMAT_DSPA },
360aacbd9a6Sjmcneill 	{ "dsp_b",	AUDIO_DAI_FORMAT_DSPB },
361aacbd9a6Sjmcneill 	{ "ac97",	AUDIO_DAI_FORMAT_AC97 },
362aacbd9a6Sjmcneill 	{ "pdm",	AUDIO_DAI_FORMAT_PDM },
363aacbd9a6Sjmcneill };
364aacbd9a6Sjmcneill 
365aacbd9a6Sjmcneill static int
ausoc_link_format(struct ausoc_softc * sc,struct ausoc_link * link,int phandle,int dai_phandle,bool single_link,u_int * format)366aacbd9a6Sjmcneill ausoc_link_format(struct ausoc_softc *sc, struct ausoc_link *link, int phandle,
367aacbd9a6Sjmcneill     int dai_phandle, bool single_link, u_int *format)
368aacbd9a6Sjmcneill {
369aacbd9a6Sjmcneill 	const char *format_prop = single_link ?
370aacbd9a6Sjmcneill 	    "simple-audio-card,format" : "format";
371aacbd9a6Sjmcneill 	const char *frame_master_prop = single_link ?
372aacbd9a6Sjmcneill 	    "simple-audio-card,frame-master" : "frame-master";
373aacbd9a6Sjmcneill 	const char *bitclock_master_prop = single_link ?
374aacbd9a6Sjmcneill 	    "simple-audio-card,bitclock-master" : "bitclock-master";
375aacbd9a6Sjmcneill 	const char *bitclock_inversion_prop = single_link ?
376aacbd9a6Sjmcneill 	    "simple-audio-card,bitclock-inversion" : "bitclock-inversion";
377aacbd9a6Sjmcneill 	const char *frame_inversion_prop = single_link ?
378aacbd9a6Sjmcneill 	    "simple-audio-card,frame-inversion" : "frame-inversion";
379aacbd9a6Sjmcneill 
380aacbd9a6Sjmcneill 	u_int fmt, pol, clk;
381aacbd9a6Sjmcneill 	const char *s;
382aacbd9a6Sjmcneill 	u_int n;
383aacbd9a6Sjmcneill 
384aacbd9a6Sjmcneill 	s = fdtbus_get_string(phandle, format_prop);
385aacbd9a6Sjmcneill 	if (s) {
386aacbd9a6Sjmcneill 		for (n = 0; n < __arraycount(ausoc_dai_formats); n++) {
387aacbd9a6Sjmcneill 			if (strcmp(s, ausoc_dai_formats[n].name) == 0) {
388aacbd9a6Sjmcneill 				fmt = ausoc_dai_formats[n].fmt;
389aacbd9a6Sjmcneill 				break;
390aacbd9a6Sjmcneill 			}
391aacbd9a6Sjmcneill 		}
392aacbd9a6Sjmcneill 		if (n == __arraycount(ausoc_dai_formats))
393aacbd9a6Sjmcneill 			return EINVAL;
394aacbd9a6Sjmcneill 	} else {
395aacbd9a6Sjmcneill 		fmt = AUDIO_DAI_FORMAT_I2S;
396aacbd9a6Sjmcneill 	}
397aacbd9a6Sjmcneill 
398aacbd9a6Sjmcneill 	const bool frame_master =
399aacbd9a6Sjmcneill 	    dai_phandle == fdtbus_get_phandle(phandle, frame_master_prop);
400aacbd9a6Sjmcneill 	const bool bitclock_master =
401aacbd9a6Sjmcneill 	    dai_phandle == fdtbus_get_phandle(phandle, bitclock_master_prop);
402aacbd9a6Sjmcneill 	if (frame_master) {
403aacbd9a6Sjmcneill 		clk = bitclock_master ?
404aacbd9a6Sjmcneill 		    AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
405aacbd9a6Sjmcneill 	} else {
406aacbd9a6Sjmcneill 		clk = bitclock_master ?
407aacbd9a6Sjmcneill 		    AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
408aacbd9a6Sjmcneill 	}
409aacbd9a6Sjmcneill 
410aacbd9a6Sjmcneill 	const bool bitclock_inversion = of_hasprop(phandle, bitclock_inversion_prop);
411aacbd9a6Sjmcneill 	const bool frame_inversion = of_hasprop(phandle, frame_inversion_prop);
412aacbd9a6Sjmcneill 	if (bitclock_inversion) {
413aacbd9a6Sjmcneill 		pol = frame_inversion ?
414aacbd9a6Sjmcneill 		    AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
415aacbd9a6Sjmcneill 	} else {
416aacbd9a6Sjmcneill 		pol = frame_inversion ?
417aacbd9a6Sjmcneill 		    AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
418aacbd9a6Sjmcneill 	}
419aacbd9a6Sjmcneill 
420aacbd9a6Sjmcneill 	*format = __SHIFTIN(fmt, AUDIO_DAI_FORMAT_MASK) |
421aacbd9a6Sjmcneill 		  __SHIFTIN(pol, AUDIO_DAI_POLARITY_MASK) |
422aacbd9a6Sjmcneill 		  __SHIFTIN(clk, AUDIO_DAI_CLOCK_MASK);
423aacbd9a6Sjmcneill 
424aacbd9a6Sjmcneill 	return 0;
425aacbd9a6Sjmcneill }
426aacbd9a6Sjmcneill 
427aacbd9a6Sjmcneill static void
ausoc_attach_link(struct ausoc_softc * sc,struct ausoc_link * link,int card_phandle,int link_phandle)428aacbd9a6Sjmcneill ausoc_attach_link(struct ausoc_softc *sc, struct ausoc_link *link,
429aacbd9a6Sjmcneill     int card_phandle, int link_phandle)
430aacbd9a6Sjmcneill {
431aacbd9a6Sjmcneill 	const bool single_link = card_phandle == link_phandle;
432aacbd9a6Sjmcneill 	const char *cpu_prop = single_link ?
433aacbd9a6Sjmcneill 	    "simple-audio-card,cpu" : "cpu";
434aacbd9a6Sjmcneill 	const char *codec_prop = single_link ?
435aacbd9a6Sjmcneill 	    "simple-audio-card,codec" : "codec";
436aacbd9a6Sjmcneill 	const char *mclk_fs_prop = single_link ?
437aacbd9a6Sjmcneill 	    "simple-audio-card,mclk-fs" : "mclk-fs";
438aacbd9a6Sjmcneill 	const char *node_name = fdtbus_get_string(link_phandle, "name");
439aacbd9a6Sjmcneill 	u_int n, format;
440aacbd9a6Sjmcneill 
441aacbd9a6Sjmcneill 	const int cpu_phandle = of_find_firstchild_byname(link_phandle, cpu_prop);
442aacbd9a6Sjmcneill 	if (cpu_phandle <= 0) {
443aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev, "missing %s prop on %s node\n",
444aacbd9a6Sjmcneill 		    cpu_prop, node_name);
445aacbd9a6Sjmcneill 		return;
446aacbd9a6Sjmcneill 	}
447aacbd9a6Sjmcneill 
448aacbd9a6Sjmcneill 	link->link_cpu = fdtbus_dai_acquire(cpu_phandle, "sound-dai");
449aacbd9a6Sjmcneill 	if (!link->link_cpu) {
450aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev,
451aacbd9a6Sjmcneill 		    "couldn't acquire cpu dai on %s node\n", node_name);
452aacbd9a6Sjmcneill 		return;
453aacbd9a6Sjmcneill 	}
454aacbd9a6Sjmcneill 
455aacbd9a6Sjmcneill 	const int codec_phandle = of_find_firstchild_byname(link_phandle, codec_prop);
456aacbd9a6Sjmcneill 	if (codec_phandle <= 0) {
457aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev, "missing %s prop on %s node\n",
458aacbd9a6Sjmcneill 		    codec_prop, node_name);
459aacbd9a6Sjmcneill 		return;
460aacbd9a6Sjmcneill 	}
461aacbd9a6Sjmcneill 
462aacbd9a6Sjmcneill 	link->link_codec = fdtbus_dai_acquire(codec_phandle, "sound-dai");
463aacbd9a6Sjmcneill 	if (!link->link_codec) {
464aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev,
465aacbd9a6Sjmcneill 		    "couldn't acquire codec dai on %s node\n", node_name);
466aacbd9a6Sjmcneill 		return;
467aacbd9a6Sjmcneill 	}
468aacbd9a6Sjmcneill 
469aacbd9a6Sjmcneill 	for (;;) {
470aacbd9a6Sjmcneill 		if (fdtbus_dai_acquire_index(card_phandle,
471aacbd9a6Sjmcneill 		    "simple-audio-card,aux-devs", link->link_naux) == NULL)
472aacbd9a6Sjmcneill 			break;
473aacbd9a6Sjmcneill 		link->link_naux++;
474aacbd9a6Sjmcneill 	}
475aacbd9a6Sjmcneill 	if (link->link_naux) {
476aacbd9a6Sjmcneill 		link->link_aux = kmem_zalloc(sizeof(audio_dai_tag_t) * link->link_naux, KM_SLEEP);
477aacbd9a6Sjmcneill 		for (n = 0; n < link->link_naux; n++) {
478aacbd9a6Sjmcneill 			link->link_aux[n] = fdtbus_dai_acquire_index(card_phandle,
479aacbd9a6Sjmcneill 			    "simple-audio-card,aux-devs", n);
480bfc29e26Sjmcneill 			KASSERT(link->link_aux[n] != NULL);
481bfc29e26Sjmcneill 
482bfc29e26Sjmcneill 			/* Attach aux devices to codec */
483bfc29e26Sjmcneill 			audio_dai_add_device(link->link_codec, link->link_aux[n]);
484aacbd9a6Sjmcneill 		}
485aacbd9a6Sjmcneill 	}
486aacbd9a6Sjmcneill 
487aacbd9a6Sjmcneill 	of_getprop_uint32(link_phandle, mclk_fs_prop, &link->link_mclk_fs);
488aacbd9a6Sjmcneill 	if (ausoc_link_format(sc, link, link_phandle, codec_phandle, single_link, &format) != 0) {
489aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't parse format properties\n");
490aacbd9a6Sjmcneill 		return;
491aacbd9a6Sjmcneill 	}
492aacbd9a6Sjmcneill 	if (audio_dai_set_format(link->link_cpu, format) != 0) {
493aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't set cpu format\n");
494aacbd9a6Sjmcneill 		return;
495aacbd9a6Sjmcneill 	}
496aacbd9a6Sjmcneill 	if (audio_dai_set_format(link->link_codec, format) != 0) {
497aacbd9a6Sjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't set codec format\n");
498aacbd9a6Sjmcneill 		return;
499aacbd9a6Sjmcneill 	}
500aacbd9a6Sjmcneill 
501aacbd9a6Sjmcneill 	aprint_normal_dev(sc->sc_dev, "codec: %s, cpu: %s",
502aacbd9a6Sjmcneill 	    device_xname(audio_dai_device(link->link_codec)),
503aacbd9a6Sjmcneill 	    device_xname(audio_dai_device(link->link_cpu)));
504aacbd9a6Sjmcneill 	for (n = 0; n < link->link_naux; n++) {
505aacbd9a6Sjmcneill 		if (n == 0)
506aacbd9a6Sjmcneill 			aprint_normal(", aux:");
507aacbd9a6Sjmcneill 		aprint_normal(" %s",
508aacbd9a6Sjmcneill 		    device_xname(audio_dai_device(link->link_aux[n])));
509aacbd9a6Sjmcneill 	}
510aacbd9a6Sjmcneill 	aprint_normal("\n");
511aacbd9a6Sjmcneill 
512aacbd9a6Sjmcneill 	audio_attach_mi(&ausoc_hw_if, link, sc->sc_dev);
513aacbd9a6Sjmcneill }
514aacbd9a6Sjmcneill 
515aacbd9a6Sjmcneill static void
ausoc_attach_cb(device_t self)516aacbd9a6Sjmcneill ausoc_attach_cb(device_t self)
517aacbd9a6Sjmcneill {
518aacbd9a6Sjmcneill 	struct ausoc_softc * const sc = device_private(self);
519aacbd9a6Sjmcneill 	const int phandle = sc->sc_phandle;
520aacbd9a6Sjmcneill 	const char *name;
521aacbd9a6Sjmcneill 	int child, n;
522aacbd9a6Sjmcneill 	size_t len;
523aacbd9a6Sjmcneill 
524aacbd9a6Sjmcneill 	/*
525aacbd9a6Sjmcneill 	 * If the root node defines a cpu and codec, there is only one link. For
526aacbd9a6Sjmcneill 	 * cards with multiple links, there will be simple-audio-card,dai-link
527aacbd9a6Sjmcneill 	 * child nodes for each one.
528aacbd9a6Sjmcneill 	 */
529aacbd9a6Sjmcneill 	if (of_find_firstchild_byname(phandle, "simple-audio-card,cpu") > 0 &&
530aacbd9a6Sjmcneill 	    of_find_firstchild_byname(phandle, "simple-audio-card,codec") > 0) {
531aacbd9a6Sjmcneill 		sc->sc_nlink = 1;
532aacbd9a6Sjmcneill 		sc->sc_link = kmem_zalloc(sizeof(*sc->sc_link), KM_SLEEP);
533aacbd9a6Sjmcneill 		sc->sc_link[0].link_name = sc->sc_name;
534aacbd9a6Sjmcneill 		ausoc_attach_link(sc, &sc->sc_link[0], phandle, phandle);
535aacbd9a6Sjmcneill 	} else {
536aacbd9a6Sjmcneill 		for (child = OF_child(phandle); child; child = OF_peer(child)) {
537aacbd9a6Sjmcneill 			name = fdtbus_get_string(child, "name");
538aacbd9a6Sjmcneill 			len = strlen("simple-audio-card,dai-link");
539aacbd9a6Sjmcneill 			if (strncmp(name, "simple-audio-card,dai-link", len) != 0)
540aacbd9a6Sjmcneill 				continue;
541aacbd9a6Sjmcneill 			sc->sc_nlink++;
542aacbd9a6Sjmcneill 		}
543aacbd9a6Sjmcneill 		if (sc->sc_nlink == 0)
544aacbd9a6Sjmcneill 			return;
545aacbd9a6Sjmcneill 		sc->sc_link = kmem_zalloc(sizeof(*sc->sc_link) * sc->sc_nlink,
546aacbd9a6Sjmcneill 		    KM_SLEEP);
547aacbd9a6Sjmcneill 		for (child = OF_child(phandle), n = 0; child; child = OF_peer(child)) {
548aacbd9a6Sjmcneill 			name = fdtbus_get_string(child, "name");
549aacbd9a6Sjmcneill 			len = strlen("simple-audio-card,dai-link");
550aacbd9a6Sjmcneill 			if (strncmp(name, "simple-audio-card,dai-link", len) != 0)
551aacbd9a6Sjmcneill 				continue;
552aacbd9a6Sjmcneill 			sc->sc_link[n].link_name = sc->sc_name;
553aacbd9a6Sjmcneill 			ausoc_attach_link(sc, &sc->sc_link[n], phandle, child);
554aacbd9a6Sjmcneill 			n++;
555aacbd9a6Sjmcneill 		}
556aacbd9a6Sjmcneill 	}
557aacbd9a6Sjmcneill }
558aacbd9a6Sjmcneill 
559aacbd9a6Sjmcneill static void
ausoc_attach(device_t parent,device_t self,void * aux)560aacbd9a6Sjmcneill ausoc_attach(device_t parent, device_t self, void *aux)
561aacbd9a6Sjmcneill {
562aacbd9a6Sjmcneill 	struct ausoc_softc * const sc = device_private(self);
563aacbd9a6Sjmcneill 	struct fdt_attach_args * const faa = aux;
564aacbd9a6Sjmcneill 	const int phandle = faa->faa_phandle;
565aacbd9a6Sjmcneill 
566aacbd9a6Sjmcneill 	sc->sc_dev = self;
567aacbd9a6Sjmcneill 	sc->sc_phandle = phandle;
568aacbd9a6Sjmcneill 	sc->sc_name = fdtbus_get_string(phandle, "simple-audio-card,name");
569aacbd9a6Sjmcneill 	if (!sc->sc_name)
570aacbd9a6Sjmcneill 		sc->sc_name = "SoC Audio";
571aacbd9a6Sjmcneill 
572aacbd9a6Sjmcneill 	aprint_naive("\n");
573aacbd9a6Sjmcneill 	aprint_normal(": %s\n", sc->sc_name);
574aacbd9a6Sjmcneill 
575aacbd9a6Sjmcneill 	/*
576aacbd9a6Sjmcneill 	 * Defer attachment until all other drivers are ready.
577aacbd9a6Sjmcneill 	 */
578aacbd9a6Sjmcneill 	config_defer(self, ausoc_attach_cb);
579aacbd9a6Sjmcneill }
580aacbd9a6Sjmcneill 
581aacbd9a6Sjmcneill CFATTACH_DECL_NEW(ausoc, sizeof(struct ausoc_softc),
582aacbd9a6Sjmcneill     ausoc_match, ausoc_attach, NULL, NULL);
583