xref: /openbsd-src/sys/arch/arm64/dev/aplmca.c (revision 952ee722ff3e76fd82a065b2cc272b46ed9c22f9)
1*952ee722Skettenis /*	$OpenBSD: aplmca.c,v 1.7 2023/07/26 11:09:24 kettenis Exp $	*/
27400b107Skettenis /*
37400b107Skettenis  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
47400b107Skettenis  *
57400b107Skettenis  * Permission to use, copy, modify, and distribute this software for any
67400b107Skettenis  * purpose with or without fee is hereby granted, provided that the above
77400b107Skettenis  * copyright notice and this permission notice appear in all copies.
87400b107Skettenis  *
97400b107Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107400b107Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117400b107Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127400b107Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137400b107Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147400b107Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157400b107Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167400b107Skettenis  */
177400b107Skettenis 
187400b107Skettenis #include <sys/param.h>
197400b107Skettenis #include <sys/systm.h>
207400b107Skettenis #include <sys/audioio.h>
217400b107Skettenis #include <sys/device.h>
227400b107Skettenis #include <sys/malloc.h>
237eff0e4eSkn #include <sys/fcntl.h>
247400b107Skettenis 
257400b107Skettenis #include <machine/bus.h>
267400b107Skettenis #include <machine/fdt.h>
277400b107Skettenis 
287400b107Skettenis #include <dev/ofw/openfirm.h>
297400b107Skettenis #include <dev/ofw/ofw_clock.h>
307400b107Skettenis #include <dev/ofw/ofw_misc.h>
317400b107Skettenis #include <dev/ofw/ofw_power.h>
327400b107Skettenis #include <dev/ofw/fdt.h>
337400b107Skettenis 
347400b107Skettenis #include <dev/audio_if.h>
357400b107Skettenis 
367400b107Skettenis #include <arm64/dev/apldma.h>
377400b107Skettenis 
387400b107Skettenis /*
397400b107Skettenis  * This driver is based on preliminary device tree bindings and will
407400b107Skettenis  * almost certainly need changes once the official bindings land in
417400b107Skettenis  * mainline Linux.  Support for these preliminary bindings will be
427400b107Skettenis  * dropped as soon as official bindings are available.
437400b107Skettenis  */
447400b107Skettenis 
457400b107Skettenis #define MCA_CL_STRIDE		0x4000
467400b107Skettenis #define MCA_SW_STRIDE		0x8000
477400b107Skettenis #define MCA_SERDES_TXA		0x0300
487400b107Skettenis 
497400b107Skettenis #define MCA_STATUS(idx)			((idx) * MCA_CL_STRIDE + 0x0000)
507400b107Skettenis #define  MCA_STATUS_MCLK_EN		(1 << 0)
51407288f4Skettenis #define MCA_MCLK_CONF(idx)		((idx) * MCA_CL_STRIDE + 0x0004)
52407288f4Skettenis #define  MCA_MCLK_CONF_DIV_MASK		(0xf << 8)
53407288f4Skettenis #define  MCA_MCLK_CONF_DIV_SHIFT	8
547400b107Skettenis 
557400b107Skettenis #define MCA_SYNCGEN_STATUS(idx)		((idx) * MCA_CL_STRIDE + 0x0100)
567400b107Skettenis #define  MCA_SYNCGEN_STATUS_EN		(1 << 0)
577400b107Skettenis #define MCA_SYNCGEN_MCLK_SEL(idx)	((idx) * MCA_CL_STRIDE + 0x0104)
587400b107Skettenis #define MCA_SYNCGEN_HI_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x0108)
597400b107Skettenis #define MCA_SYNCGEN_LO_PERIOD(idx)	((idx) * MCA_CL_STRIDE + 0x010c)
607400b107Skettenis 
617400b107Skettenis #define MCA_SERDES_BASE(idx, off)	((idx) * MCA_CL_STRIDE + (off))
627400b107Skettenis #define MCA_SERDES_STATUS(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0000)
637400b107Skettenis #define  MCA_SERDES_STATUS_EN		(1 << 0)
647400b107Skettenis #define  MCA_SERDES_STATUS_RST		(1 << 1)
657400b107Skettenis #define MCA_SERDES_CONF(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0004)
667400b107Skettenis #define MCA_SERDES_CONF_NSLOTS_MASK	(0xf << 0)
677400b107Skettenis #define MCA_SERDES_CONF_NSLOTS_SHIFT	0
687400b107Skettenis #define MCA_SERDES_CONF_WIDTH_MASK	(0x1f << 4)
697400b107Skettenis #define MCA_SERDES_CONF_WIDTH_32BIT	(0x10 << 4)
707400b107Skettenis #define MCA_SERDES_CONF_BCLK_POL	(1 << 10)
717400b107Skettenis #define MCA_SERDES_CONF_MAGIC		(0x7 << 12)
727400b107Skettenis #define MCA_SERDES_CONF_SYNC_SEL_MASK	(0x7 << 16)
737400b107Skettenis #define MCA_SERDES_CONF_SYNC_SEL_SHIFT	16
747400b107Skettenis #define MCA_SERDES_BITSTART(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0008)
757400b107Skettenis #define MCA_SERDES_CHANMASK0(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x000c)
767400b107Skettenis #define MCA_SERDES_CHANMASK1(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0010)
777400b107Skettenis #define MCA_SERDES_CHANMASK2(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0014)
787400b107Skettenis #define MCA_SERDES_CHANMASK3(idx, off)	(MCA_SERDES_BASE(idx, off) + 0x0018)
797400b107Skettenis 
807400b107Skettenis #define MCA_PORT_ENABLE(idx)		((idx) * MCA_CL_STRIDE + 0x0600)
817400b107Skettenis #define  MCA_PORT_ENABLE_CLOCKS		(0x3 << 1)
827400b107Skettenis #define  MCA_PORT_ENABLE_TX_DATA	(1 << 3)
837400b107Skettenis #define MCA_PORT_CLOCK_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0604)
847400b107Skettenis #define  MCA_PORT_CLOCK_SEL_SHIFT	8
857400b107Skettenis #define MCA_PORT_DATA_SEL(idx)		((idx) * MCA_CL_STRIDE + 0x0608)
867400b107Skettenis #define  MCA_PORT_DATA_SEL_TXA(idx)	(1 << ((idx) * 2))
877400b107Skettenis #define  MCA_PORT_DATA_SEL_TXB(idx)	(2 << ((idx) * 2))
887400b107Skettenis 
897400b107Skettenis #define MCA_DMA_ADAPTER_A(idx)		((idx) * MCA_SW_STRIDE + 0x0000)
907400b107Skettenis #define MCA_DMA_ADAPTER_B(idx)		((idx) * MCA_SW_STRIDE + 0x4000)
917400b107Skettenis #define  MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT	0
927400b107Skettenis #define  MCA_DMA_ADAPTER_TX_NCHANS_SHIFT	5
937400b107Skettenis #define  MCA_DMA_ADAPTER_NCHANS_SHIFT		20
947400b107Skettenis 
957400b107Skettenis 
967400b107Skettenis #define HREAD4(sc, reg)							\
977400b107Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
987400b107Skettenis #define HWRITE4(sc, reg, val)						\
997400b107Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
1007400b107Skettenis #define HSET4(sc, reg, bits)						\
1017400b107Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
1027400b107Skettenis #define HCLR4(sc, reg, bits)						\
1037400b107Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
1047400b107Skettenis 
1057400b107Skettenis struct aplmca_dai {
1067400b107Skettenis 	struct aplmca_softc	*ad_sc;
1077400b107Skettenis 	struct dai_device	ad_dai;
1087400b107Skettenis 	int			ad_cluster;
1097400b107Skettenis 
1107400b107Skettenis 	struct apldma_channel	*ad_ac;
1117400b107Skettenis 	void			*ad_pbuf;
1127400b107Skettenis };
1137400b107Skettenis 
1147400b107Skettenis struct aplmca_softc {
1157400b107Skettenis 	struct device		sc_dev;
1167400b107Skettenis 	bus_space_tag_t		sc_iot;
1177400b107Skettenis 	bus_space_handle_t	sc_ioh;
1187400b107Skettenis 	bus_space_handle_t	sc_sw_ioh;
1197400b107Skettenis 
1207400b107Skettenis 	int			sc_node;
1217400b107Skettenis 	uint32_t		sc_phandle;
1227400b107Skettenis 
1237400b107Skettenis 	int			sc_nclusters;
1247400b107Skettenis 	struct aplmca_dai	*sc_ad;
1257400b107Skettenis };
1267400b107Skettenis 
1277400b107Skettenis int	aplmca_set_format(void *, uint32_t, uint32_t, uint32_t);
1287400b107Skettenis int	aplmca_set_sysclk(void *, uint32_t);
1297400b107Skettenis 
1307eff0e4eSkn int	aplmca_open(void *, int);
1317400b107Skettenis int	aplmca_set_params(void *, int, int,
1327400b107Skettenis 	    struct audio_params *, struct audio_params *);
1337400b107Skettenis void	*aplmca_allocm(void *, int, size_t, int, int);
1347400b107Skettenis void	aplmca_freem(void *, void *, int);
1357400b107Skettenis int	aplmca_trigger_output(void *, void *, void *, int,
1367400b107Skettenis 	    void (*)(void *), void *, struct audio_params *);
1377400b107Skettenis int	aplmca_trigger_input(void *, void *, void *, int,
1387400b107Skettenis 	    void (*)(void *), void *, struct audio_params *);
1397400b107Skettenis int	aplmca_halt_output(void *);
1407400b107Skettenis int	aplmca_halt_input(void *);
1417400b107Skettenis 
14236a50a7eSkn const struct audio_hw_if aplmca_hw_if = {
1437eff0e4eSkn 	.open = aplmca_open,
1447400b107Skettenis 	.set_params = aplmca_set_params,
1457400b107Skettenis 	.allocm = aplmca_allocm,
1467400b107Skettenis 	.freem = aplmca_freem,
1477400b107Skettenis 	.trigger_output = aplmca_trigger_output,
1487400b107Skettenis 	.trigger_input = aplmca_trigger_input,
1497400b107Skettenis 	.halt_output = aplmca_halt_output,
1507400b107Skettenis 	.halt_input = aplmca_halt_input,
1517400b107Skettenis };
1527400b107Skettenis 
1537400b107Skettenis int	aplmca_match(struct device *, void *, void *);
1547400b107Skettenis void	aplmca_attach(struct device *, struct device *, void *);
155*952ee722Skettenis int	aplmca_activate(struct device *, int);
1567400b107Skettenis 
1577400b107Skettenis const struct cfattach aplmca_ca = {
158*952ee722Skettenis 	sizeof (struct aplmca_softc), aplmca_match, aplmca_attach, NULL,
159*952ee722Skettenis 	aplmca_activate
1607400b107Skettenis };
1617400b107Skettenis 
1627400b107Skettenis struct cfdriver aplmca_cd = {
1637400b107Skettenis 	NULL, "aplmca", DV_DULL
1647400b107Skettenis };
1657400b107Skettenis 
1667400b107Skettenis int
aplmca_match(struct device * parent,void * match,void * aux)1677400b107Skettenis aplmca_match(struct device *parent, void *match, void *aux)
1687400b107Skettenis {
1697400b107Skettenis 	struct fdt_attach_args *faa = aux;
1707400b107Skettenis 
1717400b107Skettenis 	return OF_is_compatible(faa->fa_node, "apple,mca");
1727400b107Skettenis }
1737400b107Skettenis 
1747400b107Skettenis void
aplmca_attach(struct device * parent,struct device * self,void * aux)1757400b107Skettenis aplmca_attach(struct device *parent, struct device *self, void *aux)
1767400b107Skettenis {
1777400b107Skettenis 	struct aplmca_softc *sc = (struct aplmca_softc *)self;
1787400b107Skettenis 	struct fdt_attach_args *faa = aux;
1797400b107Skettenis 	int i;
1807400b107Skettenis 
1817400b107Skettenis 	if (faa->fa_nreg < 2) {
1827400b107Skettenis 		printf(": no registers\n");
1837400b107Skettenis 		return;
1847400b107Skettenis 	}
1857400b107Skettenis 
1867400b107Skettenis 	sc->sc_iot = faa->fa_iot;
1877400b107Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1887400b107Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1897400b107Skettenis 		printf(": can't map registers\n");
1907400b107Skettenis 		return;
1917400b107Skettenis 	}
1927400b107Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
1937400b107Skettenis 	    faa->fa_reg[1].size, 0, &sc->sc_sw_ioh)) {
1947400b107Skettenis 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
1957400b107Skettenis 		printf(": can't map registers\n");
1967400b107Skettenis 		return;
1977400b107Skettenis 	}
1987400b107Skettenis 
1997400b107Skettenis 	sc->sc_node = faa->fa_node;
2007400b107Skettenis 	sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0);
2017400b107Skettenis 
2027400b107Skettenis 	sc->sc_nclusters = OF_getpropint(faa->fa_node, "apple,nclusters", 6);
2037400b107Skettenis 	sc->sc_ad = mallocarray(sc->sc_nclusters, sizeof(*sc->sc_ad),
2047400b107Skettenis 	    M_DEVBUF, M_WAITOK | M_ZERO);
2057400b107Skettenis 
2067400b107Skettenis 	for (i = 0; i < sc->sc_nclusters; i++) {
2077400b107Skettenis 		sc->sc_ad[i].ad_cluster = i;
2087400b107Skettenis 		sc->sc_ad[i].ad_sc = sc;
2097400b107Skettenis 		sc->sc_ad[i].ad_dai.dd_node = sc->sc_node;
210f2359a08Skettenis 		sc->sc_ad[i].ad_dai.dd_cookie = &sc->sc_ad[i];
2117400b107Skettenis 		sc->sc_ad[i].ad_dai.dd_hw_if = &aplmca_hw_if;
2127400b107Skettenis 		sc->sc_ad[i].ad_dai.dd_set_format = aplmca_set_format;
2137400b107Skettenis 		sc->sc_ad[i].ad_dai.dd_set_sysclk = aplmca_set_sysclk;
2147400b107Skettenis 	}
2157400b107Skettenis 
2167400b107Skettenis 	printf("\n");
2177400b107Skettenis 
2187400b107Skettenis 	power_domain_enable_idx(sc->sc_node, 0);
2197400b107Skettenis 
2207400b107Skettenis 	for (i = 0; i < sc->sc_nclusters; i++) {
2217400b107Skettenis 		HCLR4(sc, MCA_SERDES_STATUS(i, MCA_SERDES_TXA),
2227400b107Skettenis 		    MCA_SERDES_STATUS_EN);
2237400b107Skettenis 		HCLR4(sc, MCA_SYNCGEN_STATUS(i), MCA_SYNCGEN_STATUS_EN);
2247400b107Skettenis 		HCLR4(sc, MCA_STATUS(i), MCA_STATUS_MCLK_EN);
2257400b107Skettenis 	}
2267400b107Skettenis }
2277400b107Skettenis 
2287400b107Skettenis int
aplmca_activate(struct device * self,int act)229*952ee722Skettenis aplmca_activate(struct device *self, int act)
230*952ee722Skettenis {
231*952ee722Skettenis 	struct aplmca_softc *sc = (struct aplmca_softc *)self;
232*952ee722Skettenis 	int i;
233*952ee722Skettenis 
234*952ee722Skettenis 	switch (act) {
235*952ee722Skettenis 	case DVACT_SUSPEND:
236*952ee722Skettenis 		for (i = 0; i < sc->sc_nclusters; i++) {
237*952ee722Skettenis 			if (sc->sc_ad[i].ad_ac)
238*952ee722Skettenis 				power_domain_disable_idx(sc->sc_node, i + 1);
239*952ee722Skettenis 		}
240*952ee722Skettenis 		power_domain_disable_idx(sc->sc_node, 0);
241*952ee722Skettenis 		break;
242*952ee722Skettenis 	case DVACT_RESUME:
243*952ee722Skettenis 		power_domain_enable_idx(sc->sc_node, 0);
244*952ee722Skettenis 		for (i = 0; i < sc->sc_nclusters; i++) {
245*952ee722Skettenis 			if (sc->sc_ad[i].ad_ac)
246*952ee722Skettenis 				power_domain_enable_idx(sc->sc_node, i + 1);
247*952ee722Skettenis 		}
248*952ee722Skettenis 		break;
249*952ee722Skettenis 	}
250*952ee722Skettenis 
251*952ee722Skettenis 	return 0;
252*952ee722Skettenis }
253*952ee722Skettenis 
254*952ee722Skettenis int
aplmca_dai_init(struct aplmca_softc * sc,int port)2557400b107Skettenis aplmca_dai_init(struct aplmca_softc *sc, int port)
2567400b107Skettenis {
2577400b107Skettenis 	struct aplmca_dai *ad = &sc->sc_ad[port];
2587400b107Skettenis 	uint32_t conf;
2597400b107Skettenis 	char name[5];
2607400b107Skettenis 	int idx;
2617400b107Skettenis 
2627400b107Skettenis 	/* Allocate DMA channel. */
2637400b107Skettenis 	snprintf(name, sizeof(name), "tx%da", ad->ad_cluster);
2647400b107Skettenis 	idx = OF_getindex(sc->sc_node, name, "dma-names");
2657400b107Skettenis 	if (idx == -1)
2667400b107Skettenis 		return ENOENT;
2677400b107Skettenis 	ad->ad_ac = apldma_alloc_channel(idx);
2687400b107Skettenis 	if (ad->ad_ac == NULL)
2697400b107Skettenis 		return ENOENT;
2707400b107Skettenis 
2717400b107Skettenis 	power_domain_enable_idx(sc->sc_node, port + 1);
2727400b107Skettenis 
2737400b107Skettenis 	/* Basic SERDES configuration. */
2747400b107Skettenis 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
2757400b107Skettenis 	conf &= ~MCA_SERDES_CONF_SYNC_SEL_MASK;
2767400b107Skettenis 	conf |= (ad->ad_cluster + 1) << MCA_SERDES_CONF_SYNC_SEL_SHIFT;
2777400b107Skettenis 	conf |= MCA_SERDES_CONF_MAGIC;
2787400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
2797400b107Skettenis 
2807400b107Skettenis 	/* Output port configuration. */
2817400b107Skettenis 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
2827400b107Skettenis 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
2837400b107Skettenis 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
2847400b107Skettenis 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
2857400b107Skettenis 	HWRITE4(sc, MCA_PORT_ENABLE(port),
2867400b107Skettenis 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
2877400b107Skettenis 
2887400b107Skettenis 	return 0;
2897400b107Skettenis }
2907400b107Skettenis 
29112e50ba5Skettenis void
aplmca_dai_link(struct aplmca_softc * sc,int master,int port)29212e50ba5Skettenis aplmca_dai_link(struct aplmca_softc *sc, int master, int port)
29312e50ba5Skettenis {
29412e50ba5Skettenis 	struct aplmca_dai *ad = &sc->sc_ad[master];
29512e50ba5Skettenis 
29612e50ba5Skettenis 	HWRITE4(sc, MCA_PORT_CLOCK_SEL(port),
29712e50ba5Skettenis 	    (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT);
29812e50ba5Skettenis 	HWRITE4(sc, MCA_PORT_DATA_SEL(port),
29912e50ba5Skettenis 	    MCA_PORT_DATA_SEL_TXA(ad->ad_cluster));
30012e50ba5Skettenis 	HWRITE4(sc, MCA_PORT_ENABLE(port),
30112e50ba5Skettenis 	    MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA);
30212e50ba5Skettenis }
30312e50ba5Skettenis 
3047400b107Skettenis uint32_t *
aplmca_dai_next_dai(uint32_t * cells)3057400b107Skettenis aplmca_dai_next_dai(uint32_t *cells)
3067400b107Skettenis {
3077400b107Skettenis 	uint32_t phandle = cells[0];
3087400b107Skettenis 	int node, ncells;
3097400b107Skettenis 
3107400b107Skettenis 	node = OF_getnodebyphandle(phandle);
3117400b107Skettenis 	if (node == 0)
3127400b107Skettenis 		return NULL;
3137400b107Skettenis 
3147400b107Skettenis 	ncells = OF_getpropint(node, "#sound-dai-cells", 0);
3157400b107Skettenis 	return cells + ncells + 1;
3167400b107Skettenis }
3177400b107Skettenis 
3187400b107Skettenis struct dai_device *
aplmca_alloc_cluster(int node)3197400b107Skettenis aplmca_alloc_cluster(int node)
3207400b107Skettenis {
3217400b107Skettenis 	struct aplmca_softc *sc = aplmca_cd.cd_devs[0];
3227400b107Skettenis 	uint32_t *dais;
3237400b107Skettenis 	uint32_t *dai;
32412e50ba5Skettenis 	uint32_t ports[2];
3257400b107Skettenis 	int nports = 0;
32612e50ba5Skettenis 	int len, i;
3277400b107Skettenis 
3287400b107Skettenis 	len = OF_getproplen(node, "sound-dai");
3297400b107Skettenis 	if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t))
3307400b107Skettenis 		return NULL;
3317400b107Skettenis 
3327400b107Skettenis 	dais = malloc(len, M_TEMP, M_WAITOK);
3337400b107Skettenis 	OF_getpropintarray(node, "sound-dai", dais, len);
3347400b107Skettenis 
3357400b107Skettenis 	dai = dais;
3367400b107Skettenis 	while (dai && dai < dais + (len / sizeof(uint32_t))) {
33712e50ba5Skettenis 		if (dai[0] == sc->sc_phandle && nports < nitems(ports))
33812e50ba5Skettenis 			ports[nports++] = dai[1];
3397400b107Skettenis 		dai = aplmca_dai_next_dai(dai);
3407400b107Skettenis 	}
3417400b107Skettenis 
3427400b107Skettenis 	free(dais, M_TEMP, len);
3437400b107Skettenis 
3447400b107Skettenis 	if (nports == 0)
3457400b107Skettenis 		return NULL;
34612e50ba5Skettenis 	for (i = 0; i < nports; i++) {
34712e50ba5Skettenis 		if (ports[i] >= sc->sc_nclusters)
34812e50ba5Skettenis 			return NULL;
34912e50ba5Skettenis 	}
3507400b107Skettenis 
35112e50ba5Skettenis 	if (sc->sc_ad[ports[0]].ad_ac != NULL)
3527400b107Skettenis 		return NULL;
3537400b107Skettenis 
35412e50ba5Skettenis 	/* Setup the primary cluster. */
35512e50ba5Skettenis 	if (aplmca_dai_init(sc, ports[0]))
3567400b107Skettenis 		return NULL;
3577400b107Skettenis 
35812e50ba5Skettenis 	/*
35912e50ba5Skettenis 	 * Additional interfaces receive the same output as the
36012e50ba5Skettenis 	 * primary interface by linking the output port to the primary
36112e50ba5Skettenis 	 * cluster.
36212e50ba5Skettenis 	 */
36312e50ba5Skettenis 	for (i = 1; i < nports; i++)
36412e50ba5Skettenis 		aplmca_dai_link(sc, ports[0], ports[i]);
36512e50ba5Skettenis 
36612e50ba5Skettenis 	return &sc->sc_ad[ports[0]].ad_dai;
3677400b107Skettenis }
3687400b107Skettenis 
3697400b107Skettenis int
aplmca_set_format(void * cookie,uint32_t fmt,uint32_t pol,uint32_t clk)3707400b107Skettenis aplmca_set_format(void *cookie, uint32_t fmt, uint32_t pol,
3717400b107Skettenis     uint32_t clk)
3727400b107Skettenis {
3737400b107Skettenis 	struct aplmca_dai *ad = cookie;
3747400b107Skettenis 	struct aplmca_softc *sc = ad->ad_sc;
3757400b107Skettenis 	uint32_t conf;
3767400b107Skettenis 
3777400b107Skettenis 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
3787400b107Skettenis 	conf &= ~MCA_SERDES_CONF_WIDTH_MASK;
3797400b107Skettenis 	conf |= MCA_SERDES_CONF_WIDTH_32BIT;
3807400b107Skettenis 
3817400b107Skettenis 	switch (fmt) {
3827400b107Skettenis 	case DAI_FORMAT_I2S:
3837400b107Skettenis 		conf &= ~MCA_SERDES_CONF_BCLK_POL;
3847400b107Skettenis 		break;
3857400b107Skettenis 	case DAI_FORMAT_RJ:
3867400b107Skettenis 	case DAI_FORMAT_LJ:
3877400b107Skettenis 		conf |= MCA_SERDES_CONF_BCLK_POL;
3887400b107Skettenis 		break;
3897400b107Skettenis 	default:
3907400b107Skettenis 		return EINVAL;
3917400b107Skettenis 	}
3927400b107Skettenis 
3937400b107Skettenis 	if (pol & DAI_POLARITY_IB)
3947400b107Skettenis 		conf ^= MCA_SERDES_CONF_BCLK_POL;
3957400b107Skettenis 	if (pol & DAI_POLARITY_IF)
3967400b107Skettenis 		return EINVAL;
3977400b107Skettenis 
3987400b107Skettenis 	if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM))
3997400b107Skettenis 		return EINVAL;
4007400b107Skettenis 
4017400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
4027400b107Skettenis 
4037400b107Skettenis 	return 0;
4047400b107Skettenis }
4057400b107Skettenis 
4067400b107Skettenis int
aplmca_set_sysclk(void * cookie,uint32_t rate)4077400b107Skettenis aplmca_set_sysclk(void *cookie, uint32_t rate)
4087400b107Skettenis {
4097400b107Skettenis 	struct aplmca_dai *ad = cookie;
4107400b107Skettenis 	struct aplmca_softc *sc = ad->ad_sc;
4117400b107Skettenis 
4127400b107Skettenis 	return clock_set_frequency_idx(sc->sc_node, ad->ad_cluster, rate);
4137400b107Skettenis }
4147400b107Skettenis 
4157400b107Skettenis int
aplmca_open(void * cookie,int flags)4167eff0e4eSkn aplmca_open(void *cookie, int flags)
4177eff0e4eSkn {
4187eff0e4eSkn 	if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
4197eff0e4eSkn 		return ENXIO;
4207eff0e4eSkn 
4217eff0e4eSkn 	return 0;
4227eff0e4eSkn }
4237eff0e4eSkn 
4247eff0e4eSkn int
aplmca_set_params(void * cookie,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)4257400b107Skettenis aplmca_set_params(void *cookie, int setmode, int usemode,
4267400b107Skettenis     struct audio_params *play, struct audio_params *rec)
4277400b107Skettenis {
4287400b107Skettenis 	if (setmode & AUMODE_PLAY) {
4297400b107Skettenis 		play->sample_rate = 48000;
4307400b107Skettenis 		play->encoding = AUDIO_ENCODING_SLINEAR_LE;
4317400b107Skettenis 		play->precision = 24;
4327400b107Skettenis 		play->bps = 4;
4337400b107Skettenis 		play->msb = 0;
4347400b107Skettenis 		play->channels = 2;
4357400b107Skettenis 	}
4367400b107Skettenis 
4377400b107Skettenis 	return 0;
4387400b107Skettenis }
4397400b107Skettenis 
4407400b107Skettenis void *
aplmca_allocm(void * cookie,int direction,size_t size,int type,int flags)4417400b107Skettenis aplmca_allocm(void *cookie, int direction, size_t size, int type,
4427400b107Skettenis     int flags)
4437400b107Skettenis {
4447400b107Skettenis 	struct aplmca_dai *ad = cookie;
4457400b107Skettenis 
4467400b107Skettenis 	if (direction == AUMODE_PLAY) {
4477400b107Skettenis 		ad->ad_pbuf = apldma_allocm(ad->ad_ac, size, flags);
4487400b107Skettenis 		return ad->ad_pbuf;
4497400b107Skettenis 	}
4507400b107Skettenis 
4517400b107Skettenis 	return malloc(size, type, flags | M_ZERO);
4527400b107Skettenis }
4537400b107Skettenis 
4547400b107Skettenis void
aplmca_freem(void * cookie,void * addr,int type)4557400b107Skettenis aplmca_freem(void *cookie, void *addr, int type)
4567400b107Skettenis {
4577400b107Skettenis 	struct aplmca_dai *ad = cookie;
4587400b107Skettenis 
4597400b107Skettenis 	if (addr == ad->ad_pbuf) {
4607400b107Skettenis 		apldma_freem(ad->ad_ac);
4617400b107Skettenis 		return;
4627400b107Skettenis 	}
4637400b107Skettenis 
4647400b107Skettenis 	free(addr, type, 0);
4657400b107Skettenis }
4667400b107Skettenis 
4677400b107Skettenis int
aplmca_trigger_output(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * params)4687400b107Skettenis aplmca_trigger_output(void *cookie, void *start, void *end, int blksize,
4697400b107Skettenis     void (*intr)(void *), void *intrarg, struct audio_params *params)
4707400b107Skettenis {
4717400b107Skettenis 	struct aplmca_dai *ad = cookie;
4727400b107Skettenis 	struct aplmca_softc *sc = ad->ad_sc;
4737400b107Skettenis 	uint32_t conf, period;
4747400b107Skettenis 	int pad;
4757400b107Skettenis 
4767400b107Skettenis 	if (params->channels > 16)
4777400b107Skettenis 		return EINVAL;
4787400b107Skettenis 
4797400b107Skettenis 	/* Finalize SERDES configuration. */
4807400b107Skettenis 	conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA));
4817400b107Skettenis 	conf &= ~MCA_SERDES_CONF_NSLOTS_MASK;
4827400b107Skettenis 	conf |= ((params->channels - 1) << MCA_SERDES_CONF_NSLOTS_SHIFT);
4837400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf);
4847400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CHANMASK0(ad->ad_cluster, MCA_SERDES_TXA),
4857400b107Skettenis 	    0xffffffff);
4867400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CHANMASK1(ad->ad_cluster, MCA_SERDES_TXA),
4877400b107Skettenis 	    0xffffffff << params->channels);
4887400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CHANMASK2(ad->ad_cluster, MCA_SERDES_TXA),
4897400b107Skettenis 	    0xffffffff);
4907400b107Skettenis 	HWRITE4(sc, MCA_SERDES_CHANMASK3(ad->ad_cluster, MCA_SERDES_TXA),
4917400b107Skettenis 	    0xffffffff << params->channels);
4927400b107Skettenis 
4937400b107Skettenis 	period = params->channels * 32;
4947400b107Skettenis 	HWRITE4(sc, MCA_SYNCGEN_HI_PERIOD(ad->ad_cluster), period - 2);
4957400b107Skettenis 	HWRITE4(sc, MCA_SYNCGEN_LO_PERIOD(ad->ad_cluster), 0);
496407288f4Skettenis 	HWRITE4(sc, MCA_MCLK_CONF(ad->ad_cluster),
497407288f4Skettenis 	    1 << MCA_MCLK_CONF_DIV_SHIFT);
4987400b107Skettenis 
4997400b107Skettenis 	clock_enable_idx(sc->sc_node, ad->ad_cluster);
5007400b107Skettenis 
5017400b107Skettenis 	HWRITE4(sc, MCA_SYNCGEN_MCLK_SEL(ad->ad_cluster),
5027400b107Skettenis 	    ad->ad_cluster + 1);
5037400b107Skettenis 
5047400b107Skettenis 	HSET4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
5057400b107Skettenis 	HSET4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
5067400b107Skettenis 	    MCA_SYNCGEN_STATUS_EN);
5077400b107Skettenis 	HSET4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
5087400b107Skettenis 	    MCA_SERDES_STATUS_EN);
5097400b107Skettenis 
5107400b107Skettenis 	pad = params->bps * 8 - params->precision;
5117400b107Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_sw_ioh,
5127400b107Skettenis 	    MCA_DMA_ADAPTER_A(ad->ad_cluster),
5137400b107Skettenis 	    pad << MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT |
5147400b107Skettenis 	    2 << MCA_DMA_ADAPTER_TX_NCHANS_SHIFT |
5157400b107Skettenis 	    2 << MCA_DMA_ADAPTER_NCHANS_SHIFT);
5167400b107Skettenis 
5177400b107Skettenis 	return apldma_trigger_output(ad->ad_ac, start, end, blksize,
5187400b107Skettenis 	    intr, intrarg, params);
5197400b107Skettenis }
5207400b107Skettenis 
5217400b107Skettenis int
aplmca_trigger_input(void * cookie,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * params)5227400b107Skettenis aplmca_trigger_input(void *cookie, void *start, void *end, int blksize,
5237400b107Skettenis     void (*intr)(void *), void *intrarg, struct audio_params *params)
5247400b107Skettenis {
5257400b107Skettenis 	printf("%s\n", __func__);
5267400b107Skettenis 	return EIO;
5277400b107Skettenis }
5287400b107Skettenis 
5297400b107Skettenis int
aplmca_halt_output(void * cookie)5307400b107Skettenis aplmca_halt_output(void *cookie)
5317400b107Skettenis {
5327400b107Skettenis 	struct aplmca_dai *ad = cookie;
5337400b107Skettenis 	struct aplmca_softc *sc = ad->ad_sc;
5347400b107Skettenis 	int error;
5357400b107Skettenis 
5367400b107Skettenis 	error = apldma_halt_output(ad->ad_ac);
5377400b107Skettenis 
5387400b107Skettenis 	HCLR4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA),
5397400b107Skettenis 	    MCA_SERDES_STATUS_EN);
5407400b107Skettenis 	HCLR4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster),
5417400b107Skettenis 	    MCA_SYNCGEN_STATUS_EN);
5427400b107Skettenis 	HCLR4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN);
5437400b107Skettenis 
5447400b107Skettenis 	clock_disable_idx(sc->sc_node, ad->ad_cluster);
5457400b107Skettenis 
5467400b107Skettenis 	return error;
5477400b107Skettenis }
5487400b107Skettenis 
5497400b107Skettenis int
aplmca_halt_input(void * cookie)5507400b107Skettenis aplmca_halt_input(void *cookie)
5517400b107Skettenis {
5527400b107Skettenis 	printf("%s\n", __func__);
5537400b107Skettenis 	return 0;
5547400b107Skettenis }
555