xref: /openbsd-src/sys/arch/arm64/dev/aplmca.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: aplmca.c,v 1.5 2022/10/28 15:09:45 kn Exp $	*/
2 /*
3  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/audioio.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/fcntl.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_clock.h>
30 #include <dev/ofw/ofw_misc.h>
31 #include <dev/ofw/ofw_power.h>
32 #include <dev/ofw/fdt.h>
33 
34 #include <dev/audio_if.h>
35 
36 #include <arm64/dev/apldma.h>
37 
38 /*
39  * This driver is based on preliminary device tree bindings and will
40  * almost certainly need changes once the official bindings land in
41  * mainline Linux.  Support for these preliminary bindings will be
42  * dropped as soon as official bindings are available.
43  */
44 
45 #define MCA_CL_STRIDE		0x4000
46 #define MCA_SW_STRIDE		0x8000
47 #define MCA_SERDES_TXA		0x0300
48 
49 #define MCA_STATUS(idx)			((idx) * MCA_CL_STRIDE + 0x0000)
50 #define  MCA_STATUS_MCLK_EN		(1 << 0)
51 
52 #define MCA_SYNCGEN_STATUS(idx)		((idx) * MCA_CL_STRIDE + 0x0100)
53 #define  MCA_SYNCGEN_STATUS_EN		(1 << 0)
54 #define MCA_SYNCGEN_MCLK_SEL(idx)	((idx) * MCA_CL_STRIDE + 0x0104)
55 #define MCA_SYNCGEN_HI_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x0108)
56 #define MCA_SYNCGEN_LO_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x010c)
57 
58 #define MCA_SERDES_BASE(idx, off)	((idx) * MCA_CL_STRIDE + (off))
59 #define MCA_SERDES_STATUS(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0000)
60 #define  MCA_SERDES_STATUS_EN		(1 << 0)
61 #define  MCA_SERDES_STATUS_RST		(1 << 1)
62 #define MCA_SERDES_CONF(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0004)
63 #define MCA_SERDES_CONF_NSLOTS_MASK	(0xf << 0)
64 #define MCA_SERDES_CONF_NSLOTS_SHIFT	0
65 #define MCA_SERDES_CONF_WIDTH_MASK	(0x1f << 4)
66 #define MCA_SERDES_CONF_WIDTH_32BIT	(0x10 << 4)
67 #define MCA_SERDES_CONF_BCLK_POL	(1 << 10)
68 #define MCA_SERDES_CONF_MAGIC		(0x7 << 12)
69 #define MCA_SERDES_CONF_SYNC_SEL_MASK	(0x7 << 16)
70 #define MCA_SERDES_CONF_SYNC_SEL_SHIFT	16
71 #define MCA_SERDES_BITSTART(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0008)
72 #define MCA_SERDES_CHANMASK0(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x000c)
73 #define MCA_SERDES_CHANMASK1(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0010)
74 #define MCA_SERDES_CHANMASK2(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0014)
75 #define MCA_SERDES_CHANMASK3(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0018)
76 
77 #define MCA_PORT_ENABLE(idx)		((idx) * MCA_CL_STRIDE + 0x0600)
78 #define  MCA_PORT_ENABLE_CLOCKS		(0x3 << 1)
79 #define  MCA_PORT_ENABLE_TX_DATA	(1 << 3)
80 #define MCA_PORT_CLOCK_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0604)
81 #define  MCA_PORT_CLOCK_SEL_SHIFT	8
82 #define MCA_PORT_DATA_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0608)
83 #define  MCA_PORT_DATA_SEL_TXA(idx)	(1 << ((idx) * 2))
84 #define  MCA_PORT_DATA_SEL_TXB(idx)	(2 << ((idx) * 2))
85 
86 #define MCA_DMA_ADAPTER_A(idx)		((idx) * MCA_SW_STRIDE + 0x0000)
87 #define MCA_DMA_ADAPTER_B(idx)		((idx) * MCA_SW_STRIDE + 0x4000)
88 #define  MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT	0
89 #define  MCA_DMA_ADAPTER_TX_NCHANS_SHIFT	5
90 #define  MCA_DMA_ADAPTER_NCHANS_SHIFT		20
91 
92 
93 #define HREAD4(sc, reg)							\
94 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
95 #define HWRITE4(sc, reg, val)						\
96 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
97 #define HSET4(sc, reg, bits)						\
98 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
99 #define HCLR4(sc, reg, bits)						\
100 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
101 
102 struct aplmca_dai {
103 	struct aplmca_softc	*ad_sc;
104 	struct dai_device	ad_dai;
105 	int			ad_cluster;
106 
107 	struct apldma_channel	*ad_ac;
108 	void			*ad_pbuf;
109 };
110 
111 struct aplmca_softc {
112 	struct device		sc_dev;
113 	bus_space_tag_t		sc_iot;
114 	bus_space_handle_t	sc_ioh;
115 	bus_space_handle_t	sc_sw_ioh;
116 
117 	int			sc_node;
118 	uint32_t		sc_phandle;
119 
120 	int			sc_nclusters;
121 	struct aplmca_dai	*sc_ad;
122 };
123 
124 int	aplmca_set_format(void *, uint32_t, uint32_t, uint32_t);
125 int	aplmca_set_sysclk(void *, uint32_t);
126 
127 int	aplmca_open(void *, int);
128 int	aplmca_set_params(void *, int, int,
129 	    struct audio_params *, struct audio_params *);
130 void	*aplmca_allocm(void *, int, size_t, int, int);
131 void	aplmca_freem(void *, void *, int);
132 int	aplmca_trigger_output(void *, void *, void *, int,
133 	    void (*)(void *), void *, struct audio_params *);
134 int	aplmca_trigger_input(void *, void *, void *, int,
135 	    void (*)(void *), void *, struct audio_params *);
136 int	aplmca_halt_output(void *);
137 int	aplmca_halt_input(void *);
138 
139 const struct audio_hw_if aplmca_hw_if = {
140 	.open = aplmca_open,
141 	.set_params = aplmca_set_params,
142 	.allocm = aplmca_allocm,
143 	.freem = aplmca_freem,
144 	.trigger_output = aplmca_trigger_output,
145 	.trigger_input = aplmca_trigger_input,
146 	.halt_output = aplmca_halt_output,
147 	.halt_input = aplmca_halt_input,
148 };
149 
150 int	aplmca_match(struct device *, void *, void *);
151 void	aplmca_attach(struct device *, struct device *, void *);
152 
153 const struct cfattach aplmca_ca = {
154 	sizeof (struct aplmca_softc), aplmca_match, aplmca_attach
155 };
156 
157 struct cfdriver aplmca_cd = {
158 	NULL, "aplmca", DV_DULL
159 };
160 
161 int
162 aplmca_match(struct device *parent, void *match, void *aux)
163 {
164 	struct fdt_attach_args *faa = aux;
165 
166 	return OF_is_compatible(faa->fa_node, "apple,mca");
167 }
168 
169 void
170 aplmca_attach(struct device *parent, struct device *self, void *aux)
171 {
172 	struct aplmca_softc *sc = (struct aplmca_softc *)self;
173 	struct fdt_attach_args *faa = aux;
174 	int i;
175 
176 	if (faa->fa_nreg < 2) {
177 		printf(": no registers\n");
178 		return;
179 	}
180 
181 	sc->sc_iot = faa->fa_iot;
182 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
183 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
184 		printf(": can't map registers\n");
185 		return;
186 	}
187 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
188 	    faa->fa_reg[1].size, 0, &sc->sc_sw_ioh)) {
189 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
190 		printf(": can't map registers\n");
191 		return;
192 	}
193 
194 	sc->sc_node = faa->fa_node;
195 	sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0);
196 
197 	sc->sc_nclusters = OF_getpropint(faa->fa_node, "apple,nclusters", 6);
198 	sc->sc_ad = mallocarray(sc->sc_nclusters, sizeof(*sc->sc_ad),
199 	    M_DEVBUF, M_WAITOK | M_ZERO);
200 
201 	for (i = 0; i < sc->sc_nclusters; i++) {
202 		sc->sc_ad[i].ad_cluster = i;
203 		sc->sc_ad[i].ad_sc = sc;
204 		sc->sc_ad[i].ad_dai.dd_node = sc->sc_node;
205 		sc->sc_ad[i].ad_dai.dd_cookie = &sc->sc_ad[i];
206 		sc->sc_ad[i].ad_dai.dd_hw_if = &aplmca_hw_if;
207 		sc->sc_ad[i].ad_dai.dd_set_format = aplmca_set_format;
208 		sc->sc_ad[i].ad_dai.dd_set_sysclk = aplmca_set_sysclk;
209 	}
210 
211 	printf("\n");
212 
213 	power_domain_enable_idx(sc->sc_node, 0);
214 
215 	for (i = 0; i < sc->sc_nclusters; i++) {
216 		HCLR4(sc, MCA_SERDES_STATUS(i, MCA_SERDES_TXA),
217 		    MCA_SERDES_STATUS_EN);
218 		HCLR4(sc, MCA_SYNCGEN_STATUS(i), MCA_SYNCGEN_STATUS_EN);
219 		HCLR4(sc, MCA_STATUS(i), MCA_STATUS_MCLK_EN);
220 	}
221 }
222 
223 int
224 aplmca_dai_init(struct aplmca_softc *sc, int port)
225 {
226 	struct aplmca_dai *ad = &sc->sc_ad[port];
227 	uint32_t conf;
228 	char name[5];
229 	int idx;
230 
231 	/* Allocate DMA channel. */
232 	snprintf(name, sizeof(name), "tx%da", ad->ad_cluster);
233 	idx = OF_getindex(sc->sc_node, name, "dma-names");
234 	if (idx == -1)
235 		return ENOENT;
236 	ad->ad_ac = apldma_alloc_channel(idx);
237 	if (ad->ad_ac == NULL)
238 		return ENOENT;
239 
240 	power_domain_enable_idx(sc->sc_node, port + 1);
241 
242 	/* Basic SERDES configuration. */
243 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
244 	conf &= ~MCA_SERDES_CONF_SYNC_SEL_MASK;
245 	conf |= (ad->ad_cluster + 1) << MCA_SERDES_CONF_SYNC_SEL_SHIFT;
246 	conf |= MCA_SERDES_CONF_MAGIC;
247 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
248 
249 	/* Output port configuration. */
250 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
251 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
252 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
253 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
254 	HWRITE4(sc, MCA_PORT_ENABLE(port),
255 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
256 
257 	return 0;
258 }
259 
260 void
261 aplmca_dai_link(struct aplmca_softc *sc, int master, int port)
262 {
263 	struct aplmca_dai *ad = &sc->sc_ad[master];
264 
265 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
266 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
267 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
268 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
269 	HWRITE4(sc, MCA_PORT_ENABLE(port),
270 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
271 }
272 
273 uint32_t *
274 aplmca_dai_next_dai(uint32_t *cells)
275 {
276 	uint32_t phandle = cells[0];
277 	int node, ncells;
278 
279 	node = OF_getnodebyphandle(phandle);
280 	if (node == 0)
281 		return NULL;
282 
283 	ncells = OF_getpropint(node, "#sound-dai-cells", 0);
284 	return cells + ncells + 1;
285 }
286 
287 struct dai_device *
288 aplmca_alloc_cluster(int node)
289 {
290 	struct aplmca_softc *sc = aplmca_cd.cd_devs[0];
291 	uint32_t *dais;
292 	uint32_t *dai;
293 	uint32_t ports[2];
294 	int nports = 0;
295 	int len, i;
296 
297 	len = OF_getproplen(node, "sound-dai");
298 	if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t))
299 		return NULL;
300 
301 	dais = malloc(len, M_TEMP, M_WAITOK);
302 	OF_getpropintarray(node, "sound-dai", dais, len);
303 
304 	dai = dais;
305 	while (dai && dai < dais + (len / sizeof(uint32_t))) {
306 		if (dai[0] == sc->sc_phandle && nports < nitems(ports))
307 			ports[nports++] = dai[1];
308 		dai = aplmca_dai_next_dai(dai);
309 	}
310 
311 	free(dais, M_TEMP, len);
312 
313 	if (nports == 0)
314 		return NULL;
315 	for (i = 0; i < nports; i++) {
316 		if (ports[i] >= sc->sc_nclusters)
317 			return NULL;
318 	}
319 
320 	if (sc->sc_ad[ports[0]].ad_ac != NULL)
321 		return NULL;
322 
323 	/* Setup the primary cluster. */
324 	if (aplmca_dai_init(sc, ports[0]))
325 		return NULL;
326 
327 	/*
328 	 * Additional interfaces receive the same output as the
329 	 * primary interface by linking the output port to the primary
330 	 * cluster.
331 	 */
332 	for (i = 1; i < nports; i++)
333 		aplmca_dai_link(sc, ports[0], ports[i]);
334 
335 	return &sc->sc_ad[ports[0]].ad_dai;
336 }
337 
338 int
339 aplmca_set_format(void *cookie, uint32_t fmt, uint32_t pol,
340     uint32_t clk)
341 {
342 	struct aplmca_dai *ad = cookie;
343 	struct aplmca_softc *sc = ad->ad_sc;
344 	uint32_t conf;
345 
346 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
347 	conf &= ~MCA_SERDES_CONF_WIDTH_MASK;
348 	conf |= MCA_SERDES_CONF_WIDTH_32BIT;
349 
350 	switch (fmt) {
351 	case DAI_FORMAT_I2S:
352 		conf &= ~MCA_SERDES_CONF_BCLK_POL;
353 		break;
354 	case DAI_FORMAT_RJ:
355 	case DAI_FORMAT_LJ:
356 		conf |= MCA_SERDES_CONF_BCLK_POL;
357 		break;
358 	default:
359 		return EINVAL;
360 	}
361 
362 	if (pol & DAI_POLARITY_IB)
363 		conf ^= MCA_SERDES_CONF_BCLK_POL;
364 	if (pol & DAI_POLARITY_IF)
365 		return EINVAL;
366 
367 	if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM))
368 		return EINVAL;
369 
370 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
371 
372 	return 0;
373 }
374 
375 int
376 aplmca_set_sysclk(void *cookie, uint32_t rate)
377 {
378 	struct aplmca_dai *ad = cookie;
379 	struct aplmca_softc *sc = ad->ad_sc;
380 
381 	return clock_set_frequency_idx(sc->sc_node, ad->ad_cluster, rate);
382 }
383 
384 int
385 aplmca_open(void *cookie, int flags)
386 {
387 	if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
388 		return ENXIO;
389 
390 	return 0;
391 }
392 
393 int
394 aplmca_set_params(void *cookie, int setmode, int usemode,
395     struct audio_params *play, struct audio_params *rec)
396 {
397 	if (setmode & AUMODE_PLAY) {
398 		play->sample_rate = 48000;
399 		play->encoding = AUDIO_ENCODING_SLINEAR_LE;
400 		play->precision = 24;
401 		play->bps = 4;
402 		play->msb = 0;
403 		play->channels = 2;
404 	}
405 
406 	return 0;
407 }
408 
409 void *
410 aplmca_allocm(void *cookie, int direction, size_t size, int type,
411     int flags)
412 {
413 	struct aplmca_dai *ad = cookie;
414 
415 	if (direction == AUMODE_PLAY) {
416 		ad->ad_pbuf = apldma_allocm(ad->ad_ac, size, flags);
417 		return ad->ad_pbuf;
418 	}
419 
420 	return malloc(size, type, flags | M_ZERO);
421 }
422 
423 void
424 aplmca_freem(void *cookie, void *addr, int type)
425 {
426 	struct aplmca_dai *ad = cookie;
427 
428 	if (addr == ad->ad_pbuf) {
429 		apldma_freem(ad->ad_ac);
430 		return;
431 	}
432 
433 	free(addr, type, 0);
434 }
435 
436 int
437 aplmca_trigger_output(void *cookie, void *start, void *end, int blksize,
438     void (*intr)(void *), void *intrarg, struct audio_params *params)
439 {
440 	struct aplmca_dai *ad = cookie;
441 	struct aplmca_softc *sc = ad->ad_sc;
442 	uint32_t conf, period;
443 	int pad;
444 
445 	if (params->channels > 16)
446 		return EINVAL;
447 
448 	/* Finalize SERDES configuration. */
449 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
450 	conf &= ~MCA_SERDES_CONF_NSLOTS_MASK;
451 	conf |= ((params->channels - 1) << MCA_SERDES_CONF_NSLOTS_SHIFT);
452 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
453 	HWRITE4(sc, MCA_SERDES_CHANMASK0(ad->ad_cluster, MCA_SERDES_TXA),
454 	    0xffffffff);
455 	HWRITE4(sc, MCA_SERDES_CHANMASK1(ad->ad_cluster, MCA_SERDES_TXA),
456 	    0xffffffff << params->channels);
457 	HWRITE4(sc, MCA_SERDES_CHANMASK2(ad->ad_cluster, MCA_SERDES_TXA),
458 	    0xffffffff);
459 	HWRITE4(sc, MCA_SERDES_CHANMASK3(ad->ad_cluster, MCA_SERDES_TXA),
460 	    0xffffffff << params->channels);
461 
462 	period = params->channels * 32;
463 	HWRITE4(sc, MCA_SYNCGEN_HI_PERIOD(ad->ad_cluster), period - 2);
464 	HWRITE4(sc, MCA_SYNCGEN_LO_PERIOD(ad->ad_cluster), 0);
465 
466 	clock_enable_idx(sc->sc_node, ad->ad_cluster);
467 
468 	HWRITE4(sc, MCA_SYNCGEN_MCLK_SEL(ad->ad_cluster),
469 	    ad->ad_cluster + 1);
470 
471 	HSET4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
472 	HSET4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
473 	    MCA_SYNCGEN_STATUS_EN);
474 	HSET4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
475 	    MCA_SERDES_STATUS_EN);
476 
477 	pad = params->bps * 8 - params->precision;
478 	bus_space_write_4(sc->sc_iot, sc->sc_sw_ioh,
479 	    MCA_DMA_ADAPTER_A(ad->ad_cluster),
480 	    pad << MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT |
481 	    2 << MCA_DMA_ADAPTER_TX_NCHANS_SHIFT |
482 	    2 << MCA_DMA_ADAPTER_NCHANS_SHIFT);
483 
484 	return apldma_trigger_output(ad->ad_ac, start, end, blksize,
485 	    intr, intrarg, params);
486 }
487 
488 int
489 aplmca_trigger_input(void *cookie, void *start, void *end, int blksize,
490     void (*intr)(void *), void *intrarg, struct audio_params *params)
491 {
492 	printf("%s\n", __func__);
493 	return EIO;
494 }
495 
496 int
497 aplmca_halt_output(void *cookie)
498 {
499 	struct aplmca_dai *ad = cookie;
500 	struct aplmca_softc *sc = ad->ad_sc;
501 	int error;
502 
503 	error = apldma_halt_output(ad->ad_ac);
504 
505 	HCLR4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
506 	    MCA_SERDES_STATUS_EN);
507 	HCLR4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
508 	    MCA_SYNCGEN_STATUS_EN);
509 	HCLR4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
510 
511 	clock_disable_idx(sc->sc_node, ad->ad_cluster);
512 
513 	return error;
514 }
515 
516 int
517 aplmca_halt_input(void *cookie)
518 {
519 	printf("%s\n", __func__);
520 	return 0;
521 }
522