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