// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2018 Google LLC * Copyright 2014 Rockchip Electronics Co., Ltd. * Taken from dc i2s/rockchip.c */ #define LOG_CATEGORY UCLASS_I2S #include #include #include #include #include #include #include struct rk_i2s_regs { u32 txcr; /* I2S_TXCR, 0x00 */ u32 rxcr; /* I2S_RXCR, 0x04 */ u32 ckr; /* I2S_CKR, 0x08 */ u32 fifolr; /* I2S_FIFOLR, 0x0C */ u32 dmacr; /* I2S_DMACR, 0x10 */ u32 intcr; /* I2S_INTCR, 0x14 */ u32 intsr; /* I2S_INTSR, 0x18 */ u32 xfer; /* I2S_XFER, 0x1C */ u32 clr; /* I2S_CLR, 0x20 */ u32 txdr; /* I2S_TXDR, 0x24 */ u32 rxdr; /* I2S_RXDR, 0x28 */ }; enum { /* I2S_XFER */ I2S_RX_TRAN_BIT = BIT(1), I2S_TX_TRAN_BIT = BIT(0), I2S_TRAN_MASK = 3 << 0, /* I2S_TXCKR */ I2S_MCLK_DIV_SHIFT = 16, I2S_MCLK_DIV_MASK = (0xff << I2S_MCLK_DIV_SHIFT), I2S_RX_SCLK_DIV_SHIFT = 8, I2S_RX_SCLK_DIV_MASK = 0xff << I2S_RX_SCLK_DIV_SHIFT, I2S_TX_SCLK_DIV_SHIFT = 0, I2S_TX_SCLK_DIV_MASK = 0xff << I2S_TX_SCLK_DIV_SHIFT, I2S_DATA_WIDTH_SHIFT = 0, I2S_DATA_WIDTH_MASK = 0x1f << I2S_DATA_WIDTH_SHIFT, }; static int rockchip_i2s_init(struct i2s_uc_priv *priv) { struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; u32 bps = priv->bitspersample; u32 lrf = priv->rfs; u32 chn = priv->channels; u32 mode = 0; clrbits_le32(®s->xfer, I2S_TX_TRAN_BIT); mode = readl(®s->txcr) & ~0x1f; switch (priv->bitspersample) { case 16: case 24: mode |= (priv->bitspersample - 1) << I2S_DATA_WIDTH_SHIFT; break; default: log_err("Invalid sample size input %d\n", priv->bitspersample); return -EINVAL; } writel(mode, ®s->txcr); mode = readl(®s->ckr) & ~I2S_MCLK_DIV_MASK; mode |= (lrf / (bps * chn) - 1) << I2S_MCLK_DIV_SHIFT; mode &= ~I2S_TX_SCLK_DIV_MASK; mode |= (priv->bitspersample * priv->channels - 1) << I2S_TX_SCLK_DIV_SHIFT; writel(mode, ®s->ckr); return 0; } static int i2s_send_data(struct rk_i2s_regs *regs, u32 *data, uint length) { for (int i = 0; i < min(32u, length); i++) writel(*data++, ®s->txdr); length -= min(32u, length); /* enable both tx and rx */ setbits_le32(®s->xfer, I2S_TRAN_MASK); while (length) { if ((readl(®s->fifolr) & 0x3f) < 0x20) { writel(*data++, ®s->txdr); length--; } } while (readl(®s->fifolr) & 0x3f) /* wait until FIFO empty */; clrbits_le32(®s->xfer, I2S_TRAN_MASK); writel(0, ®s->clr); return 0; } static int rockchip_i2s_tx_data(struct udevice *dev, void *data, uint data_size) { struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; return i2s_send_data(regs, data, data_size / sizeof(u32)); } static int rockchip_i2s_probe(struct udevice *dev) { struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); ulong base; base = dev_read_addr(dev); if (base == FDT_ADDR_T_NONE) { log_debug("Missing i2s base\n"); return -EINVAL; } priv->base_address = base; priv->id = 1; priv->audio_pll_clk = 4800000; priv->samplingrate = 48000; priv->bitspersample = 16; priv->channels = 2; priv->rfs = 256; priv->bfs = 32; return rockchip_i2s_init(priv); } static const struct i2s_ops rockchip_i2s_ops = { .tx_data = rockchip_i2s_tx_data, }; static const struct udevice_id rockchip_i2s_ids[] = { { .compatible = "rockchip,rk3288-i2s" }, { } }; U_BOOT_DRIVER(rockchip_i2s) = { .name = "rockchip_i2s", .id = UCLASS_I2S, .of_match = rockchip_i2s_ids, .probe = rockchip_i2s_probe, .ops = &rockchip_i2s_ops, };