xref: /openbmc/u-boot/drivers/sound/rockchip_i2s.c (revision e5fd39c8)
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 
10 #include <common.h>
11 #include <dm.h>
12 #include <i2s.h>
13 #include <sound.h>
14 #include <asm/io.h>
15 
16 struct 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 
30 enum {
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 
rockchip_i2s_init(struct i2s_uc_priv * priv)49 static 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 
i2s_send_data(struct rk_i2s_regs * regs,u32 * data,uint length)81 static 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 
rockchip_i2s_tx_data(struct udevice * dev,void * data,uint data_size)104 static 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 
rockchip_i2s_probe(struct udevice * dev)112 static 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 
134 static const struct i2s_ops rockchip_i2s_ops = {
135 	.tx_data	= rockchip_i2s_tx_data,
136 };
137 
138 static const struct udevice_id rockchip_i2s_ids[] = {
139 	{ .compatible = "rockchip,rk3288-i2s" },
140 	{ }
141 };
142 
143 U_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 };
150