#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/fdt.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <dev/midi_if.h>
#define RK_I2S_FIFO_DEPTH 32
#define RK_I2S_SAMPLE_RATE 48000
#define I2S_TXCR 0x00
#define TXCR_RCNT_MASK (0x3f << 17)
#define TXCR_RCNT_SHIFT 17
#define TXCR_TCSR_MASK (0x3 << 15)
#define TXCR_TCSR_SHIFT 15
#define TXCR_HWT (1 << 14)
#define TXCR_SJM (1 << 12)
#define TXCR_FBM (1 << 11)
#define TXCR_IBM_MASK (0x3 << 9)
#define TXCR_IBM_SHIFT 9
#define TXCR_PBM_MASK (0x3 << 7)
#define TXCR_PBM_SHIFT 7
#define TXCR_TFS (1 << 5)
#define TXCR_VDW_MASK (0x1f << 0)
#define TXCR_VDW_SHIFT 0
#define I2S_RXCR 0x04
#define RXCR_RCSR_MASK (0x3 << 15)
#define RXCR_RCSR_SHIFT 15
#define RXCR_HWT (1 << 14)
#define RXCR_SJM (1 << 12)
#define RXCR_FBM (1 << 11)
#define RXCR_IBM_MASK (0x3 << 9)
#define RXCR_IBM_SHIFT 9
#define RXCR_PBM_MASK (0x3 << 7)
#define RXCR_PBM_SHIFT 7
#define RXCR_TFS (1 << 5)
#define RXCR_VDW_MASK (0x1f << 0)
#define RXCR_VDW_SHIFT 0
#define I2S_CKR 0x08
#define CKR_TRCM_MASK (0x3 << 28)
#define CKR_TRCM_SHIFT 28
#define CKR_MSS (1 << 27)
#define CKR_CKP (1 << 26)
#define CKR_RLP (1 << 25)
#define CKR_TLP (1 << 24)
#define CKR_MDIV_MASK (0xff << 16)
#define CKR_MDIV_SHIFT 16
#define CKR_RSD_MASK (0xff << 8)
#define CKR_RSD_SHIFT 8
#define CKR_TSD_MASK (0xff << 0)
#define CKR_TSD_SHIFT 0
#define I2S_TXFIFOLR 0x0c
#define TXFIFOLR_TFL_MASK(n) (0x3f << ((n) * 6))
#define TXFIFOLR_TFL_SHIFT(n) ((n) * 6)
#define I2S_DMACR 0x10
#define DMACR_RDE (1 << 24)
#define DMACR_RDL_MASK (0x1f << 16)
#define DMACR_RDL_SHIFT 16
#define DMACR_TDE (1 << 8)
#define DMACR_TDL_MASK (0x1f << 0)
#define DMACR_TDL_SHIFT 0
#define I2S_INTCR 0x14
#define INTCR_RFT_MASK (0x1f << 20)
#define INTCR_RFT_SHIFT 20
#define INTCR_RXOIC (1 << 18)
#define INTCR_RXOIE (1 << 17)
#define INTCR_RXFIE (1 << 16)
#define INTCR_TFT_MASK (0x1f << 4)
#define INTCR_TFT_SHIFT 4
#define INTCR_TXUIC (1 << 2)
#define INTCR_TXUIE (1 << 1)
#define INTCR_TXEIE (1 << 0)
#define I2S_INTSR 0x18
#define INTSR_RXOI (1 << 17)
#define INTSR_RXFI (1 << 16)
#define INTSR_TXUI (1 << 1)
#define INTSR_TXEI (1 << 0)
#define I2S_XFER 0x1c
#define XFER_RXS (1 << 1)
#define XFER_TXS (1 << 0)
#define I2S_CLR 0x20
#define CLR_RXC (1 << 1)
#define CLR_TXC (1 << 0)
#define I2S_TXDR 0x24
#define I2S_RXDR 0x28
#define I2S_RXFIFOLR 0x2c
#define RXFIFOLR_RFL_MASK(n) (0x3f << ((n) * 6))
#define RXFIFOLR_RFL_SHIFT(n) ((n) * 6)
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
int rkiis_match(struct device *, void *, void *);
void rkiis_attach(struct device *, struct device *, void *);
int rkiis_intr(void *);
int rkiis_set_format(void *, uint32_t, uint32_t, uint32_t);
int rkiis_set_sysclk(void *, uint32_t);
int rkiis_open(void *, int);
int rkiis_set_params(void *, int, int,
struct audio_params *, struct audio_params *);
void *rkiis_allocm(void *, int, size_t, int, int);
void rkiis_freem(void *, void *, int);
int rkiis_trigger_output(void *, void *, void *, int,
void (*)(void *), void *, struct audio_params *);
int rkiis_trigger_input(void *, void *, void *, int,
void (*)(void *), void *, struct audio_params *);
int rkiis_halt_output(void *);
int rkiis_halt_input(void *);
struct rkiis_config {
bus_size_t oe_reg;
uint32_t oe_mask;
uint32_t oe_shift;
uint32_t oe_val;
};
struct rkiis_config rk3399_i2s_config = {
.oe_reg = 0xe220,
.oe_mask = 0x7,
.oe_shift = 11,
.oe_val = 0x7,
};
struct rkiis_chan {
uint32_t *ch_start;
uint32_t *ch_end;
uint32_t *ch_cur;
int ch_blksize;
int ch_resid;
void (*ch_intr)(void *);
void *ch_intrarg;
};
struct rkiis_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
void *sc_ih;
int sc_node;
struct rkiis_config *sc_conf;
struct rkiis_chan sc_pchan;
struct rkiis_chan sc_rchan;
uint32_t sc_active;
struct dai_device sc_dai;
};
const struct audio_hw_if rkiis_hw_if = {
.open = rkiis_open,
.set_params = rkiis_set_params,
.allocm = rkiis_allocm,
.freem = rkiis_freem,
.trigger_output = rkiis_trigger_output,
.trigger_input = rkiis_trigger_input,
.halt_output = rkiis_halt_output,
.halt_input = rkiis_halt_input,
};
const struct cfattach rkiis_ca = {
sizeof (struct rkiis_softc), rkiis_match, rkiis_attach
};
struct cfdriver rkiis_cd = {
NULL, "rkiis", DV_DULL
};
int
rkiis_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "rockchip,rk3399-i2s");
}
void
rkiis_attach(struct device *parent, struct device *self, void *aux)
{
struct rkiis_softc *sc = (struct rkiis_softc *)self;
struct fdt_attach_args *faa = aux;
struct regmap *rm;
uint32_t grf, val;
if (faa->fa_nreg < 1) {
printf(": no registers\n");
return;
}
sc->sc_iot = faa->fa_iot;
sc->sc_node = faa->fa_node;
sc->sc_conf = &rk3399_i2s_config;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
printf(": can't map registers\n");
return;
}
pinctrl_byname(sc->sc_node, "default");
clock_enable_all(sc->sc_node);
grf = OF_getpropint(sc->sc_node, "rockchip,grf", 0);
rm = regmap_byphandle(grf);
if (rm && sc->sc_conf->oe_mask) {
val = sc->sc_conf->oe_val << sc->sc_conf->oe_shift;
val |= (sc->sc_conf->oe_mask << sc->sc_conf->oe_shift) << 16;
regmap_write_4(rm, sc->sc_conf->oe_reg, val);
}
sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_AUDIO | IPL_MPSAFE,
rkiis_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL) {
printf(": can't establish interrupt\n");
goto unmap;
}
printf("\n");
sc->sc_dai.dd_node = faa->fa_node;
sc->sc_dai.dd_cookie = sc;
sc->sc_dai.dd_hw_if = &rkiis_hw_if;
sc->sc_dai.dd_set_format = rkiis_set_format;
sc->sc_dai.dd_set_sysclk = rkiis_set_sysclk;
dai_register(&sc->sc_dai);
return;
unmap:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
}
int
rkiis_intr(void *cookie)
{
struct rkiis_softc *sc = cookie;
struct rkiis_chan *pch = &sc->sc_pchan;
#if notyet
struct rkiis_chan *rch = &sc->sc_rchan;
#endif
uint32_t sr, val;
int fifolr;
mtx_enter(&audio_lock);
sr = HREAD4(sc, I2S_INTSR);
if ((sr & INTSR_RXFI) != 0) {
#if notyet
val = HREAD4(sc, I2S_RXFIFOLR);
fifolr = val & RXFIFOLR_RFL_MASK(0);
fifolr >>= RXFIFOLR_RFL_SHIFT(0);
while (fifolr > 0) {
*rch->ch_data = HREAD4(sc, I2S_RXDR);
rch->ch_data++;
rch->ch_resid -= 4;
if (rch->ch_resid == 0)
rch->ch_intr(rch->ch_intrarg);
--fifolr;
}
#endif
}
if ((sr & INTSR_TXEI) != 0) {
val = HREAD4(sc, I2S_TXFIFOLR);
fifolr = val & TXFIFOLR_TFL_MASK(0);
fifolr >>= TXFIFOLR_TFL_SHIFT(0);
fifolr = min(fifolr, RK_I2S_FIFO_DEPTH);
while (fifolr < RK_I2S_FIFO_DEPTH - 1) {
HWRITE4(sc, I2S_TXDR, *pch->ch_cur);
pch->ch_cur++;
if (pch->ch_cur == pch->ch_end)
pch->ch_cur = pch->ch_start;
pch->ch_resid -= 4;
if (pch->ch_resid == 0) {
pch->ch_intr(pch->ch_intrarg);
pch->ch_resid = pch->ch_blksize;
}
++fifolr;
}
}
mtx_leave(&audio_lock);
return 1;
}
int
rkiis_set_format(void *cookie, uint32_t fmt, uint32_t pol,
uint32_t clk)
{
struct rkiis_softc *sc = cookie;
uint32_t txcr, rxcr, ckr;
txcr = HREAD4(sc, I2S_TXCR);
rxcr = HREAD4(sc, I2S_RXCR);
ckr = HREAD4(sc, I2S_CKR);
txcr &= ~(TXCR_IBM_MASK|TXCR_PBM_MASK|TXCR_TFS);
rxcr &= ~(RXCR_IBM_MASK|RXCR_PBM_MASK|RXCR_TFS);
switch (fmt) {
case DAI_FORMAT_I2S:
txcr |= 0 << TXCR_IBM_SHIFT;
rxcr |= 0 << RXCR_IBM_SHIFT;
break;
case DAI_FORMAT_LJ:
txcr |= 1 << TXCR_IBM_SHIFT;
rxcr |= 1 << RXCR_IBM_SHIFT;
break;
case DAI_FORMAT_RJ:
txcr |= 2 << TXCR_IBM_SHIFT;
rxcr |= 2 << RXCR_IBM_SHIFT;
break;
case DAI_FORMAT_DSPA:
txcr |= 0 << TXCR_PBM_SHIFT;
txcr |= TXCR_TFS;
rxcr |= 0 << RXCR_PBM_SHIFT;
txcr |= RXCR_TFS;
break;
case DAI_FORMAT_DSPB:
txcr |= 1 << TXCR_PBM_SHIFT;
txcr |= TXCR_TFS;
rxcr |= 1 << RXCR_PBM_SHIFT;
txcr |= RXCR_TFS;
break;
default:
return EINVAL;
}
HWRITE4(sc, I2S_TXCR, txcr);
HWRITE4(sc, I2S_RXCR, rxcr);
switch (pol) {
case DAI_POLARITY_IB|DAI_POLARITY_NF:
ckr |= CKR_CKP;
break;
case DAI_POLARITY_NB|DAI_POLARITY_NF:
ckr &= ~CKR_CKP;
break;
default:
return EINVAL;
}
switch (clk) {
case DAI_CLOCK_CBM|DAI_CLOCK_CFM:
ckr |= CKR_MSS;
break;
case DAI_CLOCK_CBS|DAI_CLOCK_CFS:
ckr &= ~CKR_MSS;
break;
default:
return EINVAL;
}
HWRITE4(sc, I2S_CKR, ckr);
return 0;
}
int
rkiis_set_sysclk(void *cookie, uint32_t rate)
{
struct rkiis_softc *sc = cookie;
int error;
error = clock_set_frequency(sc->sc_node, "i2s_clk", rate);
if (error != 0) {
printf("%s: can't set sysclk to %u Hz\n",
sc->sc_dev.dv_xname, rate);
return error;
}
return 0;
}
int
rkiis_open(void *cookie, int flags)
{
if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
return ENXIO;
return 0;
}
int
rkiis_set_params(void *cookie, int setmode, int usemode,
struct audio_params *play, struct audio_params *rec)
{
struct rkiis_softc *sc = cookie;
uint32_t mclk_rate, bclk_rate;
uint32_t bclk_div, lrck_div;
uint32_t ckr, txcr, rxcr;
int i;
ckr = HREAD4(sc, I2S_CKR);
if ((ckr & CKR_MSS) == 0) {
mclk_rate = clock_get_frequency(sc->sc_node, "i2s_clk");
bclk_rate = 2 * 32 * RK_I2S_SAMPLE_RATE;
bclk_div = mclk_rate / bclk_rate;
lrck_div = bclk_rate / RK_I2S_SAMPLE_RATE;
ckr &= ~CKR_MDIV_MASK;
ckr |= (bclk_div - 1) << CKR_MDIV_SHIFT;
ckr &= ~CKR_TSD_MASK;
ckr |= (lrck_div - 1) << CKR_TSD_SHIFT;
ckr &= ~CKR_RSD_MASK;
ckr |= (lrck_div - 1) << CKR_RSD_SHIFT;
}
ckr &= ~CKR_TRCM_MASK;
HWRITE4(sc, I2S_CKR, ckr);
for (i = 0; i < 2; i++) {
struct audio_params *p;
int mode;
switch (i) {
case 0:
mode = AUMODE_PLAY;
p = play;
break;
case 1:
mode = AUMODE_RECORD;
p = rec;
break;
default:
return EINVAL;
}
if (!(setmode & mode))
continue;
if (p->channels & 1)
return EINVAL;
if (setmode & AUMODE_PLAY) {
txcr = HREAD4(sc, I2S_TXCR);
txcr &= ~TXCR_VDW_MASK;
txcr |= (16 - 1) << TXCR_VDW_SHIFT;
txcr &= ~TXCR_TCSR_MASK;
txcr |= (p->channels / 2 - 1) << TXCR_TCSR_SHIFT;
HWRITE4(sc, I2S_TXCR, txcr);
} else {
rxcr = HREAD4(sc, I2S_RXCR);
rxcr &= ~RXCR_VDW_MASK;
rxcr |= (16 - 1) << RXCR_VDW_SHIFT;
rxcr &= ~RXCR_RCSR_MASK;
rxcr |= (p->channels / 2 - 1) << RXCR_RCSR_SHIFT;
HWRITE4(sc, I2S_RXCR, rxcr);
}
p->encoding = AUDIO_ENCODING_SLINEAR_LE;
p->precision = 16;
p->bps = AUDIO_BPS(p->precision);
p->msb = 1;
p->sample_rate = RK_I2S_SAMPLE_RATE;
}
return 0;
}
void *
rkiis_allocm(void *cookie, int direction, size_t size, int type,
int flags)
{
return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
}
void
rkiis_freem(void *cookie, void *addr, int size)
{
free(addr, M_DEVBUF, size);
}
int
rkiis_trigger_output(void *cookie, void *start, void *end, int blksize,
void (*intr)(void *), void *intrarg, struct audio_params *params)
{
struct rkiis_softc *sc = cookie;
struct rkiis_chan *ch = &sc->sc_pchan;
uint32_t val;
if (sc->sc_active == 0) {
val = HREAD4(sc, I2S_XFER);
val |= (XFER_TXS | XFER_RXS);
HWRITE4(sc, I2S_XFER, val);
}
sc->sc_active |= XFER_TXS;
val = HREAD4(sc, I2S_INTCR);
val |= INTCR_TXEIE;
val &= ~INTCR_TFT_MASK;
val |= (RK_I2S_FIFO_DEPTH / 2) << INTCR_TFT_SHIFT;
HWRITE4(sc, I2S_INTCR, val);
ch->ch_intr = intr;
ch->ch_intrarg = intrarg;
ch->ch_start = ch->ch_cur = start;
ch->ch_end = end;
ch->ch_blksize = blksize;
ch->ch_resid = blksize;
return 0;
}
int
rkiis_trigger_input(void *cookie, void *start, void *end, int blksize,
void (*intr)(void *), void *intrarg, struct audio_params *params)
{
return EIO;
}
int
rkiis_halt_output(void *cookie)
{
struct rkiis_softc *sc = cookie;
struct rkiis_chan *ch = &sc->sc_pchan;
uint32_t val;
sc->sc_active &= ~XFER_TXS;
if (sc->sc_active == 0) {
val = HREAD4(sc, I2S_XFER);
val &= ~(XFER_TXS|XFER_RXS);
HWRITE4(sc, I2S_XFER, val);
}
val = HREAD4(sc, I2S_INTCR);
val &= ~INTCR_TXEIE;
HWRITE4(sc, I2S_INTCR, val);
val = HREAD4(sc, I2S_CLR);
val |= CLR_TXC;
HWRITE4(sc, I2S_CLR, val);
while ((HREAD4(sc, I2S_CLR) & CLR_TXC) != 0)
delay(1);
ch->ch_intr = NULL;
ch->ch_intrarg = NULL;
return 0;
}
int
rkiis_halt_input(void *cookie)
{
struct rkiis_softc *sc = cookie;
struct rkiis_chan *ch = &sc->sc_rchan;
uint32_t val;
sc->sc_active &= ~XFER_RXS;
if (sc->sc_active == 0) {
val = HREAD4(sc, I2S_XFER);
val &= ~(XFER_TXS|XFER_RXS);
HWRITE4(sc, I2S_XFER, val);
}
val = HREAD4(sc, I2S_INTCR);
val &= ~INTCR_RXFIE;
HWRITE4(sc, I2S_INTCR, val);
ch->ch_intr = NULL;
ch->ch_intrarg = NULL;
return 0;
}