1*6e54367aSthorpej /* $NetBSD: rk_i2s.c,v 1.10 2021/01/27 03:10:19 thorpej Exp $ */
25a4a43f8Sjmcneill
35a4a43f8Sjmcneill /*-
45a4a43f8Sjmcneill * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
55a4a43f8Sjmcneill * All rights reserved.
65a4a43f8Sjmcneill *
75a4a43f8Sjmcneill * Redistribution and use in source and binary forms, with or without
85a4a43f8Sjmcneill * modification, are permitted provided that the following conditions
95a4a43f8Sjmcneill * are met:
105a4a43f8Sjmcneill * 1. Redistributions of source code must retain the above copyright
115a4a43f8Sjmcneill * notice, this list of conditions and the following disclaimer.
125a4a43f8Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
135a4a43f8Sjmcneill * notice, this list of conditions and the following disclaimer in the
145a4a43f8Sjmcneill * documentation and/or other materials provided with the distribution.
155a4a43f8Sjmcneill *
165a4a43f8Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
175a4a43f8Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
185a4a43f8Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
195a4a43f8Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
205a4a43f8Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
215a4a43f8Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
225a4a43f8Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
235a4a43f8Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
245a4a43f8Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255a4a43f8Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265a4a43f8Sjmcneill * SUCH DAMAGE.
275a4a43f8Sjmcneill */
285a4a43f8Sjmcneill
295a4a43f8Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: rk_i2s.c,v 1.10 2021/01/27 03:10:19 thorpej Exp $");
315a4a43f8Sjmcneill
325a4a43f8Sjmcneill #include <sys/param.h>
335a4a43f8Sjmcneill #include <sys/bus.h>
345a4a43f8Sjmcneill #include <sys/cpu.h>
355a4a43f8Sjmcneill #include <sys/device.h>
365a4a43f8Sjmcneill #include <sys/kmem.h>
375a4a43f8Sjmcneill
385a4a43f8Sjmcneill #include <sys/audioio.h>
395a4a43f8Sjmcneill #include <dev/audio/audio_if.h>
405a4a43f8Sjmcneill #include <dev/audio/linear.h>
415a4a43f8Sjmcneill
425a4a43f8Sjmcneill #include <dev/fdt/fdtvar.h>
435a4a43f8Sjmcneill #include <dev/fdt/syscon.h>
445a4a43f8Sjmcneill
455a4a43f8Sjmcneill #define RK_I2S_FIFO_DEPTH 32
465a4a43f8Sjmcneill #define RK_I2S_SAMPLE_RATE 48000
475a4a43f8Sjmcneill
485a4a43f8Sjmcneill #define I2S_TXCR 0x00
495a4a43f8Sjmcneill #define TXCR_RCNT __BITS(22,17)
505a4a43f8Sjmcneill #define TXCR_TCSR __BITS(16,15)
515a4a43f8Sjmcneill #define TXCR_HWT __BIT(14)
525a4a43f8Sjmcneill #define TXCR_SJM __BIT(12)
535a4a43f8Sjmcneill #define TXCR_FBM __BIT(11)
545a4a43f8Sjmcneill #define TXCR_IBM __BITS(10,9)
555a4a43f8Sjmcneill #define TXCR_PBM __BITS(8,7)
565a4a43f8Sjmcneill #define TXCR_TFS __BIT(5)
575a4a43f8Sjmcneill #define TXCR_VDW __BITS(4,0)
585a4a43f8Sjmcneill #define I2S_RXCR 0x04
595a4a43f8Sjmcneill #define RXCR_RCSR __BITS(16,15)
605a4a43f8Sjmcneill #define RXCR_HWT __BIT(14)
615a4a43f8Sjmcneill #define RXCR_SJM __BIT(12)
625a4a43f8Sjmcneill #define RXCR_FBM __BIT(11)
635a4a43f8Sjmcneill #define RXCR_IBM __BITS(10,9)
645a4a43f8Sjmcneill #define RXCR_PBM __BITS(8,7)
655a4a43f8Sjmcneill #define RXCR_TFS __BIT(5)
665a4a43f8Sjmcneill #define RXCR_VDW __BITS(4,0)
675a4a43f8Sjmcneill #define I2S_CKR 0x08
685a4a43f8Sjmcneill #define CKR_TRCM __BITS(29,28)
695a4a43f8Sjmcneill #define CKR_MSS __BIT(27)
705a4a43f8Sjmcneill #define CKR_CKP __BIT(26)
715a4a43f8Sjmcneill #define CKR_RLP __BIT(25)
725a4a43f8Sjmcneill #define CKR_TLP __BIT(24)
735a4a43f8Sjmcneill #define CKR_MDIV __BITS(23,16)
745a4a43f8Sjmcneill #define CKR_RSD __BITS(15,8)
755a4a43f8Sjmcneill #define CKR_TSD __BITS(7,0)
765a4a43f8Sjmcneill #define I2S_TXFIFOLR 0x0c
775a4a43f8Sjmcneill #define TXFIFOLR_TFL(n) __BITS((n) * 6 + 5, (n) * 6)
785a4a43f8Sjmcneill #define I2S_DMACR 0x10
795a4a43f8Sjmcneill #define DMACR_RDE __BIT(24)
805a4a43f8Sjmcneill #define DMACR_RDL __BITS(20,16)
815a4a43f8Sjmcneill #define DMACR_TDE __BIT(8)
825a4a43f8Sjmcneill #define DMACR_TDL __BITS(4,0)
835a4a43f8Sjmcneill #define I2S_INTCR 0x14
845a4a43f8Sjmcneill #define INTCR_RFT __BITS(24,20)
855a4a43f8Sjmcneill #define INTCR_RXOIC __BIT(18)
865a4a43f8Sjmcneill #define INTCR_RXOIE __BIT(17)
875a4a43f8Sjmcneill #define INTCR_RXFIE __BIT(16)
885a4a43f8Sjmcneill #define INTCR_TFT __BITS(8,4)
895a4a43f8Sjmcneill #define INTCR_TXUIC __BIT(2)
905a4a43f8Sjmcneill #define INTCR_TXUIE __BIT(1)
915a4a43f8Sjmcneill #define INTCR_TXEIE __BIT(0)
925a4a43f8Sjmcneill #define I2S_INTSR 0x18
935a4a43f8Sjmcneill #define INTSR_RXOI __BIT(17)
945a4a43f8Sjmcneill #define INTSR_RXFI __BIT(16)
955a4a43f8Sjmcneill #define INTSR_TXUI __BIT(1)
965a4a43f8Sjmcneill #define INTSR_TXEI __BIT(0)
975a4a43f8Sjmcneill #define I2S_XFER 0x1c
985a4a43f8Sjmcneill #define XFER_RXS __BIT(1)
995a4a43f8Sjmcneill #define XFER_TXS __BIT(0)
1005a4a43f8Sjmcneill #define I2S_CLR 0x20
1015a4a43f8Sjmcneill #define CLR_RXC __BIT(1)
1025a4a43f8Sjmcneill #define CLR_TXC __BIT(0)
1035a4a43f8Sjmcneill #define I2S_TXDR 0x24
1045a4a43f8Sjmcneill #define I2S_RXDR 0x28
1055a4a43f8Sjmcneill #define I2S_RXFIFOLR 0x2c
1065a4a43f8Sjmcneill #define RXFIFOLR_RFL(n) __BITS((n) * 6 + 5, (n) * 6)
1075a4a43f8Sjmcneill
1085a4a43f8Sjmcneill struct rk_i2s_config {
1095a4a43f8Sjmcneill bus_size_t oe_reg;
1105a4a43f8Sjmcneill u_int oe_mask;
1115a4a43f8Sjmcneill u_int oe_val;
1125a4a43f8Sjmcneill };
1135a4a43f8Sjmcneill
1145a4a43f8Sjmcneill static const struct rk_i2s_config rk3399_i2s_config = {
1155a4a43f8Sjmcneill .oe_reg = 0x0e220,
1165a4a43f8Sjmcneill .oe_mask = __BITS(13,11),
1175a4a43f8Sjmcneill .oe_val = 0x7,
1185a4a43f8Sjmcneill };
1195a4a43f8Sjmcneill
120646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
121646c0f59Sthorpej { .compat = "rockchip,rk3066-i2s", },
122646c0f59Sthorpej { .compat = "rockchip,rk3188-i2s", },
123646c0f59Sthorpej { .compat = "rockchip,rk3288-i2s", },
124646c0f59Sthorpej { .compat = "rockchip,rk3399-i2s", .data = &rk3399_i2s_config },
125*6e54367aSthorpej DEVICE_COMPAT_EOL
1265a4a43f8Sjmcneill };
1275a4a43f8Sjmcneill
1285a4a43f8Sjmcneill struct rk_i2s_softc;
1295a4a43f8Sjmcneill
1305a4a43f8Sjmcneill struct rk_i2s_chan {
1315a4a43f8Sjmcneill uint32_t *ch_start;
1325a4a43f8Sjmcneill uint32_t *ch_end;
1335a4a43f8Sjmcneill uint32_t *ch_cur;
1345a4a43f8Sjmcneill
1355a4a43f8Sjmcneill int ch_blksize;
1365a4a43f8Sjmcneill int ch_resid;
1375a4a43f8Sjmcneill
1385a4a43f8Sjmcneill void (*ch_intr)(void *);
1395a4a43f8Sjmcneill void *ch_intrarg;
1405a4a43f8Sjmcneill };
1415a4a43f8Sjmcneill
1425a4a43f8Sjmcneill struct rk_i2s_softc {
1435a4a43f8Sjmcneill device_t sc_dev;
1445a4a43f8Sjmcneill bus_space_tag_t sc_bst;
1455a4a43f8Sjmcneill bus_space_handle_t sc_bsh;
1465a4a43f8Sjmcneill int sc_phandle;
1475a4a43f8Sjmcneill struct clk *sc_clk;
1485a4a43f8Sjmcneill struct syscon *sc_grf;
1495a4a43f8Sjmcneill const struct rk_i2s_config *sc_conf;
1505a4a43f8Sjmcneill
1515a4a43f8Sjmcneill kmutex_t sc_lock;
1525a4a43f8Sjmcneill kmutex_t sc_intr_lock;
1535a4a43f8Sjmcneill
1545a4a43f8Sjmcneill struct audio_format sc_format;
1555a4a43f8Sjmcneill
1565a4a43f8Sjmcneill struct rk_i2s_chan sc_pchan;
1575a4a43f8Sjmcneill struct rk_i2s_chan sc_rchan;
1585a4a43f8Sjmcneill
1595a4a43f8Sjmcneill u_int sc_active;
1605a4a43f8Sjmcneill
1615a4a43f8Sjmcneill struct audio_dai_device sc_dai;
1625a4a43f8Sjmcneill };
1635a4a43f8Sjmcneill
1645a4a43f8Sjmcneill #define RD4(sc, reg) \
1655a4a43f8Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1665a4a43f8Sjmcneill #define WR4(sc, reg, val) \
1675a4a43f8Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1685a4a43f8Sjmcneill
1695a4a43f8Sjmcneill static int
rk_i2s_query_format(void * priv,audio_format_query_t * afp)1705a4a43f8Sjmcneill rk_i2s_query_format(void *priv, audio_format_query_t *afp)
1715a4a43f8Sjmcneill {
1725a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
1735a4a43f8Sjmcneill
1745a4a43f8Sjmcneill return audio_query_format(&sc->sc_format, 1, afp);
1755a4a43f8Sjmcneill }
1765a4a43f8Sjmcneill
1775a4a43f8Sjmcneill static int
rk_i2s_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)1785a4a43f8Sjmcneill rk_i2s_set_format(void *priv, int setmode,
1795a4a43f8Sjmcneill const audio_params_t *play, const audio_params_t *rec,
1805a4a43f8Sjmcneill audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
1815a4a43f8Sjmcneill {
1825a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
1835a4a43f8Sjmcneill uint32_t ckr, txcr, rxcr;
1845a4a43f8Sjmcneill
1855a4a43f8Sjmcneill ckr = RD4(sc, I2S_CKR);
1865a4a43f8Sjmcneill if ((ckr & CKR_MSS) == 0) {
1875a4a43f8Sjmcneill const u_int mclk_rate = clk_get_rate(sc->sc_clk);
1885a4a43f8Sjmcneill const u_int bclk_rate = 2 * 32 * RK_I2S_SAMPLE_RATE;
1895a4a43f8Sjmcneill const u_int bclk_div = mclk_rate / bclk_rate;
1905a4a43f8Sjmcneill const u_int lrck_div = bclk_rate / RK_I2S_SAMPLE_RATE;
1915a4a43f8Sjmcneill
1925a4a43f8Sjmcneill ckr &= ~CKR_MDIV;
1935a4a43f8Sjmcneill ckr |= __SHIFTIN(bclk_div - 1, CKR_MDIV);
1945a4a43f8Sjmcneill ckr &= ~CKR_TSD;
1955a4a43f8Sjmcneill ckr |= __SHIFTIN(lrck_div - 1, CKR_TSD);
1965a4a43f8Sjmcneill ckr &= ~CKR_RSD;
1975a4a43f8Sjmcneill ckr |= __SHIFTIN(lrck_div - 1, CKR_RSD);
1985a4a43f8Sjmcneill }
1995a4a43f8Sjmcneill
2005a4a43f8Sjmcneill ckr &= ~CKR_TRCM;
2015a4a43f8Sjmcneill ckr |= __SHIFTIN(0, CKR_TRCM);
2025a4a43f8Sjmcneill WR4(sc, I2S_CKR, ckr);
2035a4a43f8Sjmcneill
2045a4a43f8Sjmcneill if (play && (setmode & AUMODE_PLAY) != 0) {
2055a4a43f8Sjmcneill if (play->channels & 1)
2065a4a43f8Sjmcneill return EINVAL;
2075a4a43f8Sjmcneill txcr = RD4(sc, I2S_TXCR);
2085a4a43f8Sjmcneill txcr &= ~TXCR_VDW;
2095a4a43f8Sjmcneill txcr |= __SHIFTIN(play->validbits - 1, TXCR_VDW);
2105a4a43f8Sjmcneill txcr &= ~TXCR_TCSR;
2115a4a43f8Sjmcneill txcr |= __SHIFTIN(play->channels / 2 - 1, TXCR_TCSR);
2125a4a43f8Sjmcneill WR4(sc, I2S_TXCR, txcr);
2135a4a43f8Sjmcneill }
2145a4a43f8Sjmcneill
2155a4a43f8Sjmcneill if (rec && (setmode & AUMODE_RECORD) != 0) {
2165a4a43f8Sjmcneill if (rec->channels & 1)
2175a4a43f8Sjmcneill return EINVAL;
2185a4a43f8Sjmcneill rxcr = RD4(sc, I2S_RXCR);
2195a4a43f8Sjmcneill rxcr &= ~RXCR_VDW;
2205a4a43f8Sjmcneill rxcr |= __SHIFTIN(rec->validbits - 1, RXCR_VDW);
2215a4a43f8Sjmcneill rxcr &= ~RXCR_RCSR;
2225a4a43f8Sjmcneill rxcr |= __SHIFTIN(rec->channels / 2 - 1, RXCR_RCSR);
2235a4a43f8Sjmcneill WR4(sc, I2S_RXCR, rxcr);
2245a4a43f8Sjmcneill }
2255a4a43f8Sjmcneill
2265a4a43f8Sjmcneill return 0;
2275a4a43f8Sjmcneill }
2285a4a43f8Sjmcneill
2295a4a43f8Sjmcneill static int
rk_i2s_get_props(void * priv)2305a4a43f8Sjmcneill rk_i2s_get_props(void *priv)
2315a4a43f8Sjmcneill {
2325a4a43f8Sjmcneill
2335a4a43f8Sjmcneill return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
2345a4a43f8Sjmcneill AUDIO_PROP_FULLDUPLEX;
2355a4a43f8Sjmcneill }
2365a4a43f8Sjmcneill
2375a4a43f8Sjmcneill static void *
rk_i2s_allocm(void * priv,int dir,size_t size)2385a4a43f8Sjmcneill rk_i2s_allocm(void *priv, int dir, size_t size)
2395a4a43f8Sjmcneill {
2405a4a43f8Sjmcneill return kmem_zalloc(size, KM_SLEEP);
2415a4a43f8Sjmcneill }
2425a4a43f8Sjmcneill
2435a4a43f8Sjmcneill static void
rk_i2s_freem(void * priv,void * addr,size_t size)2445a4a43f8Sjmcneill rk_i2s_freem(void *priv, void *addr, size_t size)
2455a4a43f8Sjmcneill {
2465a4a43f8Sjmcneill kmem_free(addr, size);
2475a4a43f8Sjmcneill }
2485a4a43f8Sjmcneill
2495a4a43f8Sjmcneill static int
rk_i2s_trigger_output(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)2505a4a43f8Sjmcneill rk_i2s_trigger_output(void *priv, void *start, void *end, int blksize,
2515a4a43f8Sjmcneill void (*intr)(void *), void *intrarg, const audio_params_t *params)
2525a4a43f8Sjmcneill {
2535a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
2545a4a43f8Sjmcneill struct rk_i2s_chan *ch = &sc->sc_pchan;
2555a4a43f8Sjmcneill uint32_t val;
2565a4a43f8Sjmcneill
2575a4a43f8Sjmcneill if (sc->sc_active == 0) {
2585a4a43f8Sjmcneill val = RD4(sc, I2S_XFER);
2595a4a43f8Sjmcneill val |= (XFER_TXS | XFER_RXS);
2605a4a43f8Sjmcneill WR4(sc, I2S_XFER, val);
2615a4a43f8Sjmcneill }
2625a4a43f8Sjmcneill
2635a4a43f8Sjmcneill sc->sc_active |= XFER_TXS;
2645a4a43f8Sjmcneill
2655a4a43f8Sjmcneill val = RD4(sc, I2S_INTCR);
2665a4a43f8Sjmcneill val |= INTCR_TXEIE;
2675a4a43f8Sjmcneill val &= ~INTCR_TFT;
2685a4a43f8Sjmcneill val |= __SHIFTIN(RK_I2S_FIFO_DEPTH / 2, INTCR_TFT);
2695a4a43f8Sjmcneill WR4(sc, I2S_INTCR, val);
2705a4a43f8Sjmcneill
2715a4a43f8Sjmcneill ch->ch_intr = intr;
2725a4a43f8Sjmcneill ch->ch_intrarg = intrarg;
2735a4a43f8Sjmcneill ch->ch_start = ch->ch_cur = start;
2745a4a43f8Sjmcneill ch->ch_end = end;
2755a4a43f8Sjmcneill ch->ch_blksize = blksize;
2765a4a43f8Sjmcneill ch->ch_resid = blksize;
2775a4a43f8Sjmcneill
2785a4a43f8Sjmcneill return 0;
2795a4a43f8Sjmcneill }
2805a4a43f8Sjmcneill
2815a4a43f8Sjmcneill static int
rk_i2s_trigger_input(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)2825a4a43f8Sjmcneill rk_i2s_trigger_input(void *priv, void *start, void *end, int blksize,
2835a4a43f8Sjmcneill void (*intr)(void *), void *intrarg, const audio_params_t *params)
2845a4a43f8Sjmcneill {
2855a4a43f8Sjmcneill return EIO;
2865a4a43f8Sjmcneill }
2875a4a43f8Sjmcneill
2885a4a43f8Sjmcneill static int
rk_i2s_halt_output(void * priv)2895a4a43f8Sjmcneill rk_i2s_halt_output(void *priv)
2905a4a43f8Sjmcneill {
2915a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
2925a4a43f8Sjmcneill struct rk_i2s_chan *ch = &sc->sc_pchan;
2935a4a43f8Sjmcneill uint32_t val;
2945a4a43f8Sjmcneill
2955a4a43f8Sjmcneill sc->sc_active &= ~XFER_TXS;
2965a4a43f8Sjmcneill if (sc->sc_active == 0) {
2975a4a43f8Sjmcneill val = RD4(sc, I2S_XFER);
2985a4a43f8Sjmcneill val &= ~(XFER_TXS|XFER_RXS);
2995a4a43f8Sjmcneill WR4(sc, I2S_XFER, val);
3005a4a43f8Sjmcneill }
3015a4a43f8Sjmcneill
3025a4a43f8Sjmcneill val = RD4(sc, I2S_INTCR);
3035a4a43f8Sjmcneill val &= ~INTCR_TXEIE;
3045a4a43f8Sjmcneill WR4(sc, I2S_INTCR, val);
3055a4a43f8Sjmcneill
3065a4a43f8Sjmcneill val = RD4(sc, I2S_CLR);
3075a4a43f8Sjmcneill val |= CLR_TXC;
3085a4a43f8Sjmcneill WR4(sc, I2S_CLR, val);
3095a4a43f8Sjmcneill
3105a4a43f8Sjmcneill while ((RD4(sc, I2S_CLR) & CLR_TXC) != 0)
3115a4a43f8Sjmcneill delay(1);
3125a4a43f8Sjmcneill
3135a4a43f8Sjmcneill ch->ch_intr = NULL;
3145a4a43f8Sjmcneill ch->ch_intrarg = NULL;
3155a4a43f8Sjmcneill
3165a4a43f8Sjmcneill return 0;
3175a4a43f8Sjmcneill }
3185a4a43f8Sjmcneill
3195a4a43f8Sjmcneill static int
rk_i2s_halt_input(void * priv)3205a4a43f8Sjmcneill rk_i2s_halt_input(void *priv)
3215a4a43f8Sjmcneill {
3225a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
3235a4a43f8Sjmcneill struct rk_i2s_chan *ch = &sc->sc_rchan;
3245a4a43f8Sjmcneill uint32_t val;
3255a4a43f8Sjmcneill
3265a4a43f8Sjmcneill sc->sc_active &= ~XFER_RXS;
3275a4a43f8Sjmcneill if (sc->sc_active == 0) {
3285a4a43f8Sjmcneill val = RD4(sc, I2S_XFER);
3295a4a43f8Sjmcneill val &= ~(XFER_TXS|XFER_RXS);
3305a4a43f8Sjmcneill WR4(sc, I2S_XFER, val);
3315a4a43f8Sjmcneill }
3325a4a43f8Sjmcneill
3335a4a43f8Sjmcneill val = RD4(sc, I2S_INTCR);
3345a4a43f8Sjmcneill val &= ~INTCR_RXFIE;
3355a4a43f8Sjmcneill WR4(sc, I2S_INTCR, val);
3365a4a43f8Sjmcneill
3375a4a43f8Sjmcneill ch->ch_intr = NULL;
3385a4a43f8Sjmcneill ch->ch_intrarg = NULL;
3395a4a43f8Sjmcneill
3405a4a43f8Sjmcneill return 0;
3415a4a43f8Sjmcneill }
3425a4a43f8Sjmcneill
3435a4a43f8Sjmcneill static void
rk_i2s_get_locks(void * priv,kmutex_t ** intr,kmutex_t ** thread)3445a4a43f8Sjmcneill rk_i2s_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
3455a4a43f8Sjmcneill {
3465a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
3475a4a43f8Sjmcneill
3485a4a43f8Sjmcneill *intr = &sc->sc_intr_lock;
3495a4a43f8Sjmcneill *thread = &sc->sc_lock;
3505a4a43f8Sjmcneill }
3515a4a43f8Sjmcneill
3525a4a43f8Sjmcneill static const struct audio_hw_if rk_i2s_hw_if = {
3535a4a43f8Sjmcneill .query_format = rk_i2s_query_format,
3545a4a43f8Sjmcneill .set_format = rk_i2s_set_format,
3555a4a43f8Sjmcneill .get_props = rk_i2s_get_props,
3565a4a43f8Sjmcneill .allocm = rk_i2s_allocm,
3575a4a43f8Sjmcneill .freem = rk_i2s_freem,
3585a4a43f8Sjmcneill .trigger_output = rk_i2s_trigger_output,
3595a4a43f8Sjmcneill .trigger_input = rk_i2s_trigger_input,
3605a4a43f8Sjmcneill .halt_output = rk_i2s_halt_output,
3615a4a43f8Sjmcneill .halt_input = rk_i2s_halt_input,
3625a4a43f8Sjmcneill .get_locks = rk_i2s_get_locks,
3635a4a43f8Sjmcneill };
3645a4a43f8Sjmcneill
3655a4a43f8Sjmcneill static int
rk_i2s_intr(void * priv)3665a4a43f8Sjmcneill rk_i2s_intr(void *priv)
3675a4a43f8Sjmcneill {
3685a4a43f8Sjmcneill struct rk_i2s_softc * const sc = priv;
3695a4a43f8Sjmcneill struct rk_i2s_chan * const pch = &sc->sc_pchan;
3705a4a43f8Sjmcneill #if notyet
3715a4a43f8Sjmcneill struct rk_i2s_chan * const rch = &sc->sc_rchan;
3725a4a43f8Sjmcneill #endif
3735a4a43f8Sjmcneill uint32_t sr, val;
3745a4a43f8Sjmcneill int fifolr;
3755a4a43f8Sjmcneill
3765a4a43f8Sjmcneill mutex_enter(&sc->sc_intr_lock);
3775a4a43f8Sjmcneill
3785a4a43f8Sjmcneill sr = RD4(sc, I2S_INTSR);
3795a4a43f8Sjmcneill
3805a4a43f8Sjmcneill if ((sr & INTSR_RXFI) != 0) {
3815a4a43f8Sjmcneill #if notyet
3825a4a43f8Sjmcneill val = RD4(sc, I2S_RXFIFOLR);
3835a4a43f8Sjmcneill fifolr = __SHIFTOUT(val, RXFIFOLR_RFL(0));
3845a4a43f8Sjmcneill while (fifolr > 0) {
3855a4a43f8Sjmcneill *rch->ch_data = RD4(sc, I2S_RXDR);
3865a4a43f8Sjmcneill rch->ch_data++;
3875a4a43f8Sjmcneill rch->ch_resid -= 4;
3885a4a43f8Sjmcneill if (rch->ch_resid == 0)
3895a4a43f8Sjmcneill rch->ch_intr(rch->ch_intrarg);
3905a4a43f8Sjmcneill --fifolr;
3915a4a43f8Sjmcneill }
3925a4a43f8Sjmcneill #endif
3935a4a43f8Sjmcneill }
3945a4a43f8Sjmcneill
3955a4a43f8Sjmcneill if ((sr & INTSR_TXEI) != 0) {
3965a4a43f8Sjmcneill val = RD4(sc, I2S_TXFIFOLR);
3975a4a43f8Sjmcneill fifolr = __SHIFTOUT(val, TXFIFOLR_TFL(0));
3985a4a43f8Sjmcneill fifolr = uimin(fifolr, RK_I2S_FIFO_DEPTH);
3995a4a43f8Sjmcneill while (fifolr < RK_I2S_FIFO_DEPTH - 1) {
4005a4a43f8Sjmcneill WR4(sc, I2S_TXDR, *pch->ch_cur);
4015a4a43f8Sjmcneill pch->ch_cur++;
4025a4a43f8Sjmcneill if (pch->ch_cur == pch->ch_end)
4035a4a43f8Sjmcneill pch->ch_cur = pch->ch_start;
4045a4a43f8Sjmcneill pch->ch_resid -= 4;
4055a4a43f8Sjmcneill if (pch->ch_resid == 0) {
4065a4a43f8Sjmcneill pch->ch_intr(pch->ch_intrarg);
4075a4a43f8Sjmcneill pch->ch_resid = pch->ch_blksize;
4085a4a43f8Sjmcneill }
4095a4a43f8Sjmcneill ++fifolr;
4105a4a43f8Sjmcneill }
4115a4a43f8Sjmcneill }
4125a4a43f8Sjmcneill
4135a4a43f8Sjmcneill mutex_exit(&sc->sc_intr_lock);
4145a4a43f8Sjmcneill
4155a4a43f8Sjmcneill return 0;
4165a4a43f8Sjmcneill }
4175a4a43f8Sjmcneill
4185a4a43f8Sjmcneill static int
rk_i2s_dai_set_sysclk(audio_dai_tag_t dai,u_int rate,int dir)4195a4a43f8Sjmcneill rk_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir)
4205a4a43f8Sjmcneill {
4215a4a43f8Sjmcneill struct rk_i2s_softc * const sc = audio_dai_private(dai);
4225a4a43f8Sjmcneill int error;
4235a4a43f8Sjmcneill
4245a4a43f8Sjmcneill error = clk_set_rate(sc->sc_clk, rate);
4255a4a43f8Sjmcneill if (error != 0) {
4265a4a43f8Sjmcneill device_printf(sc->sc_dev, "failed to set sysclk to %u Hz: %d\n",
4275a4a43f8Sjmcneill rate, error);
4285a4a43f8Sjmcneill return error;
4295a4a43f8Sjmcneill }
4305a4a43f8Sjmcneill
4315a4a43f8Sjmcneill return 0;
4325a4a43f8Sjmcneill }
4335a4a43f8Sjmcneill
4345a4a43f8Sjmcneill static int
rk_i2s_dai_set_format(audio_dai_tag_t dai,u_int format)4355a4a43f8Sjmcneill rk_i2s_dai_set_format(audio_dai_tag_t dai, u_int format)
4365a4a43f8Sjmcneill {
4375a4a43f8Sjmcneill struct rk_i2s_softc * const sc = audio_dai_private(dai);
4385a4a43f8Sjmcneill uint32_t txcr, rxcr, ckr;
4395a4a43f8Sjmcneill
4405a4a43f8Sjmcneill const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK);
4415a4a43f8Sjmcneill const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK);
4425a4a43f8Sjmcneill const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK);
4435a4a43f8Sjmcneill
4445a4a43f8Sjmcneill txcr = RD4(sc, I2S_TXCR);
4455a4a43f8Sjmcneill rxcr = RD4(sc, I2S_RXCR);
4465a4a43f8Sjmcneill ckr = RD4(sc, I2S_CKR);
4475a4a43f8Sjmcneill
4485a4a43f8Sjmcneill txcr &= ~(TXCR_IBM|TXCR_PBM|TXCR_TFS);
4495a4a43f8Sjmcneill rxcr &= ~(RXCR_IBM|RXCR_PBM|RXCR_TFS);
4505a4a43f8Sjmcneill switch (fmt) {
4515a4a43f8Sjmcneill case AUDIO_DAI_FORMAT_I2S:
4525a4a43f8Sjmcneill txcr |= __SHIFTIN(0, TXCR_IBM);
4535a4a43f8Sjmcneill rxcr |= __SHIFTIN(0, RXCR_IBM);
4545a4a43f8Sjmcneill break;
4555a4a43f8Sjmcneill case AUDIO_DAI_FORMAT_LJ:
4565a4a43f8Sjmcneill txcr |= __SHIFTIN(1, TXCR_IBM);
4575a4a43f8Sjmcneill rxcr |= __SHIFTIN(1, RXCR_IBM);
4585a4a43f8Sjmcneill break;
4595a4a43f8Sjmcneill case AUDIO_DAI_FORMAT_RJ:
4605a4a43f8Sjmcneill txcr |= __SHIFTIN(2, TXCR_IBM);
4615a4a43f8Sjmcneill rxcr |= __SHIFTIN(2, RXCR_IBM);
4625a4a43f8Sjmcneill break;
4635a4a43f8Sjmcneill case AUDIO_DAI_FORMAT_DSPA:
4645a4a43f8Sjmcneill txcr |= __SHIFTIN(0, TXCR_PBM);
4655a4a43f8Sjmcneill txcr |= TXCR_TFS;
4665a4a43f8Sjmcneill rxcr |= __SHIFTIN(0, RXCR_PBM);
4675a4a43f8Sjmcneill txcr |= RXCR_TFS;
4685a4a43f8Sjmcneill break;
4695a4a43f8Sjmcneill case AUDIO_DAI_FORMAT_DSPB:
4705a4a43f8Sjmcneill txcr |= __SHIFTIN(1, TXCR_PBM);
4715a4a43f8Sjmcneill txcr |= TXCR_TFS;
4725a4a43f8Sjmcneill rxcr |= __SHIFTIN(1, RXCR_PBM);
4735a4a43f8Sjmcneill txcr |= RXCR_TFS;
4745a4a43f8Sjmcneill break;
4755a4a43f8Sjmcneill default:
4765a4a43f8Sjmcneill return EINVAL;
4775a4a43f8Sjmcneill }
4785a4a43f8Sjmcneill
4795a4a43f8Sjmcneill WR4(sc, I2S_TXCR, txcr);
4805a4a43f8Sjmcneill WR4(sc, I2S_RXCR, rxcr);
4815a4a43f8Sjmcneill
4825a4a43f8Sjmcneill switch (pol) {
4835a4a43f8Sjmcneill case AUDIO_DAI_POLARITY_IB_NF:
4845a4a43f8Sjmcneill ckr |= CKR_CKP;
4855a4a43f8Sjmcneill break;
4865a4a43f8Sjmcneill case AUDIO_DAI_POLARITY_NB_NF:
4875a4a43f8Sjmcneill ckr &= ~CKR_CKP;
4885a4a43f8Sjmcneill break;
4895a4a43f8Sjmcneill default:
4905a4a43f8Sjmcneill return EINVAL;
4915a4a43f8Sjmcneill }
4925a4a43f8Sjmcneill
4935a4a43f8Sjmcneill switch (clk) {
4945a4a43f8Sjmcneill case AUDIO_DAI_CLOCK_CBM_CFM:
4955a4a43f8Sjmcneill ckr |= CKR_MSS; /* sclk input */
4965a4a43f8Sjmcneill break;
4975a4a43f8Sjmcneill case AUDIO_DAI_CLOCK_CBS_CFS:
4985a4a43f8Sjmcneill ckr &= ~CKR_MSS; /* sclk output */
4995a4a43f8Sjmcneill break;
5005a4a43f8Sjmcneill default:
5015a4a43f8Sjmcneill return EINVAL;
5025a4a43f8Sjmcneill }
5035a4a43f8Sjmcneill
5045a4a43f8Sjmcneill WR4(sc, I2S_CKR, ckr);
5055a4a43f8Sjmcneill
5065a4a43f8Sjmcneill return 0;
5075a4a43f8Sjmcneill }
5085a4a43f8Sjmcneill
5095a4a43f8Sjmcneill static audio_dai_tag_t
rk_i2s_dai_get_tag(device_t dev,const void * data,size_t len)5105a4a43f8Sjmcneill rk_i2s_dai_get_tag(device_t dev, const void *data, size_t len)
5115a4a43f8Sjmcneill {
5125a4a43f8Sjmcneill struct rk_i2s_softc * const sc = device_private(dev);
5135a4a43f8Sjmcneill
5145a4a43f8Sjmcneill if (len != 4)
5155a4a43f8Sjmcneill return NULL;
5165a4a43f8Sjmcneill
5175a4a43f8Sjmcneill return &sc->sc_dai;
5185a4a43f8Sjmcneill }
5195a4a43f8Sjmcneill
5205a4a43f8Sjmcneill static struct fdtbus_dai_controller_func rk_i2s_dai_funcs = {
5215a4a43f8Sjmcneill .get_tag = rk_i2s_dai_get_tag
5225a4a43f8Sjmcneill };
5235a4a43f8Sjmcneill
5245a4a43f8Sjmcneill static int
rk_i2s_clock_init(struct rk_i2s_softc * sc)5255a4a43f8Sjmcneill rk_i2s_clock_init(struct rk_i2s_softc *sc)
5265a4a43f8Sjmcneill {
5275a4a43f8Sjmcneill const int phandle = sc->sc_phandle;
5285a4a43f8Sjmcneill int error;
5295a4a43f8Sjmcneill
5305a4a43f8Sjmcneill sc->sc_clk = fdtbus_clock_get(phandle, "i2s_clk");
5315a4a43f8Sjmcneill if (sc->sc_clk == NULL) {
5325a4a43f8Sjmcneill aprint_error(": couldn't find i2s_clk clock\n");
5335a4a43f8Sjmcneill return ENXIO;
5345a4a43f8Sjmcneill }
5355a4a43f8Sjmcneill error = clk_enable(sc->sc_clk);
5365a4a43f8Sjmcneill if (error != 0) {
5375a4a43f8Sjmcneill aprint_error(": couldn't enable i2s_clk clock: %d\n", error);
5385a4a43f8Sjmcneill return error;
5395a4a43f8Sjmcneill }
5405a4a43f8Sjmcneill
5415a4a43f8Sjmcneill /* Enable bus clock */
5422cdfae77Smrg error = fdtbus_clock_enable(phandle, "i2s_hclk", true);
5432cdfae77Smrg if (error != 0) {
5445a4a43f8Sjmcneill aprint_error(": couldn't enable i2s_hclk clock: %d\n", error);
5455a4a43f8Sjmcneill return error;
5465a4a43f8Sjmcneill }
5475a4a43f8Sjmcneill
5485a4a43f8Sjmcneill return 0;
5495a4a43f8Sjmcneill }
5505a4a43f8Sjmcneill
5515a4a43f8Sjmcneill static int
rk_i2s_match(device_t parent,cfdata_t cf,void * aux)5525a4a43f8Sjmcneill rk_i2s_match(device_t parent, cfdata_t cf, void *aux)
5535a4a43f8Sjmcneill {
5545a4a43f8Sjmcneill struct fdt_attach_args * const faa = aux;
5555a4a43f8Sjmcneill
556*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
5575a4a43f8Sjmcneill }
5585a4a43f8Sjmcneill
5595a4a43f8Sjmcneill static void
rk_i2s_attach(device_t parent,device_t self,void * aux)5605a4a43f8Sjmcneill rk_i2s_attach(device_t parent, device_t self, void *aux)
5615a4a43f8Sjmcneill {
5625a4a43f8Sjmcneill struct rk_i2s_softc * const sc = device_private(self);
5635a4a43f8Sjmcneill struct fdt_attach_args * const faa = aux;
5645a4a43f8Sjmcneill const int phandle = faa->faa_phandle;
5655a4a43f8Sjmcneill char intrstr[128];
5665a4a43f8Sjmcneill bus_addr_t addr;
5675a4a43f8Sjmcneill bus_size_t size;
5685a4a43f8Sjmcneill uint32_t val;
5695a4a43f8Sjmcneill
5705a4a43f8Sjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
5715a4a43f8Sjmcneill aprint_error(": couldn't get registers\n");
5725a4a43f8Sjmcneill return;
5735a4a43f8Sjmcneill }
5745a4a43f8Sjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
5755a4a43f8Sjmcneill aprint_error(": couldn't decode interrupt\n");
5765a4a43f8Sjmcneill return;
5775a4a43f8Sjmcneill }
5785a4a43f8Sjmcneill
5795a4a43f8Sjmcneill sc->sc_dev = self;
5805a4a43f8Sjmcneill sc->sc_phandle = phandle;
5815a4a43f8Sjmcneill sc->sc_bst = faa->faa_bst;
5825a4a43f8Sjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
5835a4a43f8Sjmcneill aprint_error(": couldn't map registers\n");
5845a4a43f8Sjmcneill return;
5855a4a43f8Sjmcneill }
5865a4a43f8Sjmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
5875a4a43f8Sjmcneill mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
5885a4a43f8Sjmcneill
589*6e54367aSthorpej sc->sc_conf = of_compatible_lookup(phandle, compat_data)->data;
59033a7d25eSjmcneill if (sc->sc_conf != NULL && sc->sc_conf->oe_mask != 0) {
5915a4a43f8Sjmcneill sc->sc_grf = fdtbus_syscon_acquire(phandle, "rockchip,grf");
59233a7d25eSjmcneill if (sc->sc_grf == NULL) {
59333a7d25eSjmcneill aprint_error(": couldn't find grf\n");
59433a7d25eSjmcneill return;
59533a7d25eSjmcneill }
5965a4a43f8Sjmcneill syscon_lock(sc->sc_grf);
5975a4a43f8Sjmcneill val = __SHIFTIN(sc->sc_conf->oe_val, sc->sc_conf->oe_mask);
5985a4a43f8Sjmcneill val |= (sc->sc_conf->oe_mask << 16);
5995a4a43f8Sjmcneill syscon_write_4(sc->sc_grf, sc->sc_conf->oe_reg, val);
6005a4a43f8Sjmcneill syscon_unlock(sc->sc_grf);
6015a4a43f8Sjmcneill }
6025a4a43f8Sjmcneill
6035a4a43f8Sjmcneill if (rk_i2s_clock_init(sc) != 0)
6045a4a43f8Sjmcneill return;
6055a4a43f8Sjmcneill
6065a4a43f8Sjmcneill aprint_naive("\n");
6075a4a43f8Sjmcneill aprint_normal(": I2S/PCM controller\n");
6085a4a43f8Sjmcneill
60964e248edSryo if (fdtbus_intr_establish_xname(phandle, 0, IPL_AUDIO, FDT_INTR_MPSAFE,
61064e248edSryo rk_i2s_intr, sc, device_xname(self)) == NULL) {
6115a4a43f8Sjmcneill aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr);
6125a4a43f8Sjmcneill return;
6135a4a43f8Sjmcneill }
6145a4a43f8Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
6155a4a43f8Sjmcneill
6165a4a43f8Sjmcneill sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
6175a4a43f8Sjmcneill sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
6185a4a43f8Sjmcneill sc->sc_format.validbits = 16;
6195a4a43f8Sjmcneill sc->sc_format.precision = 16;
6205a4a43f8Sjmcneill sc->sc_format.channels = 2;
6215a4a43f8Sjmcneill sc->sc_format.channel_mask = AUFMT_STEREO;
6225a4a43f8Sjmcneill sc->sc_format.frequency_type = 1;
6235a4a43f8Sjmcneill sc->sc_format.frequency[0] = RK_I2S_SAMPLE_RATE;
6245a4a43f8Sjmcneill
6255a4a43f8Sjmcneill sc->sc_dai.dai_set_sysclk = rk_i2s_dai_set_sysclk;
6265a4a43f8Sjmcneill sc->sc_dai.dai_set_format = rk_i2s_dai_set_format;
6275a4a43f8Sjmcneill sc->sc_dai.dai_hw_if = &rk_i2s_hw_if;
6285a4a43f8Sjmcneill sc->sc_dai.dai_dev = self;
6295a4a43f8Sjmcneill sc->sc_dai.dai_priv = sc;
6305a4a43f8Sjmcneill fdtbus_register_dai_controller(self, phandle, &rk_i2s_dai_funcs);
6315a4a43f8Sjmcneill }
6325a4a43f8Sjmcneill
6335a4a43f8Sjmcneill CFATTACH_DECL_NEW(rk_i2s, sizeof(struct rk_i2s_softc),
6345a4a43f8Sjmcneill rk_i2s_match, rk_i2s_attach, NULL, NULL);
635