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