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