Simon Glass | 63a65b4 | 2018-12-27 20:15:21 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright 2018 Google LLC |
| 4 | * Copyright 2014 Rockchip Electronics Co., Ltd. |
| 5 | * Taken from dc i2s/rockchip.c |
| 6 | */ |
| 7 | |
| 8 | #define LOG_CATEGORY UCLASS_I2S |
| 9 | |
Simon Glass | 63a65b4 | 2018-12-27 20:15:21 -0700 | [diff] [blame] | 10 | #include <dm.h> |
| 11 | #include <i2s.h> |
Simon Glass | 0f2af88 | 2020-05-10 11:40:05 -0600 | [diff] [blame] | 12 | #include <log.h> |
Simon Glass | 63a65b4 | 2018-12-27 20:15:21 -0700 | [diff] [blame] | 13 | #include <sound.h> |
| 14 | #include <asm/io.h> |
Simon Glass | 4dcacfc | 2020-05-10 11:40:13 -0600 | [diff] [blame] | 15 | #include <linux/bitops.h> |
Simon Glass | 63a65b4 | 2018-12-27 20:15:21 -0700 | [diff] [blame] | 16 | |
| 17 | struct rk_i2s_regs { |
| 18 | u32 txcr; /* I2S_TXCR, 0x00 */ |
| 19 | u32 rxcr; /* I2S_RXCR, 0x04 */ |
| 20 | u32 ckr; /* I2S_CKR, 0x08 */ |
| 21 | u32 fifolr; /* I2S_FIFOLR, 0x0C */ |
| 22 | u32 dmacr; /* I2S_DMACR, 0x10 */ |
| 23 | u32 intcr; /* I2S_INTCR, 0x14 */ |
| 24 | u32 intsr; /* I2S_INTSR, 0x18 */ |
| 25 | u32 xfer; /* I2S_XFER, 0x1C */ |
| 26 | u32 clr; /* I2S_CLR, 0x20 */ |
| 27 | u32 txdr; /* I2S_TXDR, 0x24 */ |
| 28 | u32 rxdr; /* I2S_RXDR, 0x28 */ |
| 29 | }; |
| 30 | |
| 31 | enum { |
| 32 | /* I2S_XFER */ |
| 33 | I2S_RX_TRAN_BIT = BIT(1), |
| 34 | I2S_TX_TRAN_BIT = BIT(0), |
| 35 | I2S_TRAN_MASK = 3 << 0, |
| 36 | |
| 37 | /* I2S_TXCKR */ |
| 38 | I2S_MCLK_DIV_SHIFT = 16, |
| 39 | I2S_MCLK_DIV_MASK = (0xff << I2S_MCLK_DIV_SHIFT), |
| 40 | |
| 41 | I2S_RX_SCLK_DIV_SHIFT = 8, |
| 42 | I2S_RX_SCLK_DIV_MASK = 0xff << I2S_RX_SCLK_DIV_SHIFT, |
| 43 | I2S_TX_SCLK_DIV_SHIFT = 0, |
| 44 | I2S_TX_SCLK_DIV_MASK = 0xff << I2S_TX_SCLK_DIV_SHIFT, |
| 45 | |
| 46 | I2S_DATA_WIDTH_SHIFT = 0, |
| 47 | I2S_DATA_WIDTH_MASK = 0x1f << I2S_DATA_WIDTH_SHIFT, |
| 48 | }; |
| 49 | |
| 50 | static int rockchip_i2s_init(struct i2s_uc_priv *priv) |
| 51 | { |
| 52 | struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; |
| 53 | u32 bps = priv->bitspersample; |
| 54 | u32 lrf = priv->rfs; |
| 55 | u32 chn = priv->channels; |
| 56 | u32 mode = 0; |
| 57 | |
| 58 | clrbits_le32(®s->xfer, I2S_TX_TRAN_BIT); |
| 59 | mode = readl(®s->txcr) & ~0x1f; |
| 60 | switch (priv->bitspersample) { |
| 61 | case 16: |
| 62 | case 24: |
| 63 | mode |= (priv->bitspersample - 1) << I2S_DATA_WIDTH_SHIFT; |
| 64 | break; |
| 65 | default: |
| 66 | log_err("Invalid sample size input %d\n", priv->bitspersample); |
| 67 | return -EINVAL; |
| 68 | } |
| 69 | writel(mode, ®s->txcr); |
| 70 | |
| 71 | mode = readl(®s->ckr) & ~I2S_MCLK_DIV_MASK; |
| 72 | mode |= (lrf / (bps * chn) - 1) << I2S_MCLK_DIV_SHIFT; |
| 73 | |
| 74 | mode &= ~I2S_TX_SCLK_DIV_MASK; |
| 75 | mode |= (priv->bitspersample * priv->channels - 1) << |
| 76 | I2S_TX_SCLK_DIV_SHIFT; |
| 77 | writel(mode, ®s->ckr); |
| 78 | |
| 79 | return 0; |
| 80 | } |
| 81 | |
| 82 | static int i2s_send_data(struct rk_i2s_regs *regs, u32 *data, uint length) |
| 83 | { |
| 84 | for (int i = 0; i < min(32u, length); i++) |
| 85 | writel(*data++, ®s->txdr); |
| 86 | |
| 87 | length -= min(32u, length); |
| 88 | |
| 89 | /* enable both tx and rx */ |
| 90 | setbits_le32(®s->xfer, I2S_TRAN_MASK); |
| 91 | while (length) { |
| 92 | if ((readl(®s->fifolr) & 0x3f) < 0x20) { |
| 93 | writel(*data++, ®s->txdr); |
| 94 | length--; |
| 95 | } |
| 96 | } |
| 97 | while (readl(®s->fifolr) & 0x3f) |
| 98 | /* wait until FIFO empty */; |
| 99 | clrbits_le32(®s->xfer, I2S_TRAN_MASK); |
| 100 | writel(0, ®s->clr); |
| 101 | |
| 102 | return 0; |
| 103 | } |
| 104 | |
| 105 | static int rockchip_i2s_tx_data(struct udevice *dev, void *data, uint data_size) |
| 106 | { |
| 107 | struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); |
| 108 | struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; |
| 109 | |
| 110 | return i2s_send_data(regs, data, data_size / sizeof(u32)); |
| 111 | } |
| 112 | |
| 113 | static int rockchip_i2s_probe(struct udevice *dev) |
| 114 | { |
| 115 | struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); |
| 116 | ulong base; |
| 117 | |
| 118 | base = dev_read_addr(dev); |
| 119 | if (base == FDT_ADDR_T_NONE) { |
| 120 | log_debug("Missing i2s base\n"); |
| 121 | return -EINVAL; |
| 122 | } |
| 123 | priv->base_address = base; |
| 124 | priv->id = 1; |
| 125 | priv->audio_pll_clk = 4800000; |
| 126 | priv->samplingrate = 48000; |
| 127 | priv->bitspersample = 16; |
| 128 | priv->channels = 2; |
| 129 | priv->rfs = 256; |
| 130 | priv->bfs = 32; |
| 131 | |
| 132 | return rockchip_i2s_init(priv); |
| 133 | } |
| 134 | |
| 135 | static const struct i2s_ops rockchip_i2s_ops = { |
| 136 | .tx_data = rockchip_i2s_tx_data, |
| 137 | }; |
| 138 | |
| 139 | static const struct udevice_id rockchip_i2s_ids[] = { |
| 140 | { .compatible = "rockchip,rk3288-i2s" }, |
| 141 | { } |
| 142 | }; |
| 143 | |
| 144 | U_BOOT_DRIVER(rockchip_i2s) = { |
| 145 | .name = "rockchip_i2s", |
| 146 | .id = UCLASS_I2S, |
| 147 | .of_match = rockchip_i2s_ids, |
| 148 | .probe = rockchip_i2s_probe, |
| 149 | .ops = &rockchip_i2s_ops, |
| 150 | }; |