Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/bcm/bcm63xx-i2s-whistler.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
// linux/sound/bcm/bcm63xx-i2s-whistler.c
3
// BCM63xx whistler i2s driver
4
// Copyright (c) 2020 Broadcom Corporation
5
// Author: Kevin-Ke Li <[email protected]>
6
7
#include <linux/clk.h>
8
#include <linux/dma-mapping.h>
9
#include <linux/io.h>
10
#include <linux/module.h>
11
#include <linux/regmap.h>
12
#include <sound/pcm_params.h>
13
#include <sound/soc.h>
14
#include "bcm63xx-i2s.h"
15
16
#define DRV_NAME "brcm-i2s"
17
18
static bool brcm_i2s_wr_reg(struct device *dev, unsigned int reg)
19
{
20
switch (reg) {
21
case I2S_TX_CFG ... I2S_TX_DESC_IFF_LEN:
22
case I2S_TX_CFG_2 ... I2S_RX_DESC_IFF_LEN:
23
case I2S_RX_CFG_2 ... I2S_REG_MAX:
24
return true;
25
default:
26
return false;
27
}
28
}
29
30
static bool brcm_i2s_rd_reg(struct device *dev, unsigned int reg)
31
{
32
switch (reg) {
33
case I2S_TX_CFG ... I2S_REG_MAX:
34
return true;
35
default:
36
return false;
37
}
38
}
39
40
static bool brcm_i2s_volatile_reg(struct device *dev, unsigned int reg)
41
{
42
switch (reg) {
43
case I2S_TX_CFG:
44
case I2S_TX_IRQ_CTL:
45
case I2S_TX_DESC_IFF_ADDR:
46
case I2S_TX_DESC_IFF_LEN:
47
case I2S_TX_DESC_OFF_ADDR:
48
case I2S_TX_DESC_OFF_LEN:
49
case I2S_TX_CFG_2:
50
case I2S_RX_CFG:
51
case I2S_RX_IRQ_CTL:
52
case I2S_RX_DESC_OFF_ADDR:
53
case I2S_RX_DESC_OFF_LEN:
54
case I2S_RX_DESC_IFF_LEN:
55
case I2S_RX_DESC_IFF_ADDR:
56
case I2S_RX_CFG_2:
57
return true;
58
default:
59
return false;
60
}
61
}
62
63
static const struct regmap_config brcm_i2s_regmap_config = {
64
.reg_bits = 32,
65
.reg_stride = 4,
66
.val_bits = 32,
67
.max_register = I2S_REG_MAX,
68
.writeable_reg = brcm_i2s_wr_reg,
69
.readable_reg = brcm_i2s_rd_reg,
70
.volatile_reg = brcm_i2s_volatile_reg,
71
.cache_type = REGCACHE_FLAT,
72
};
73
74
static int bcm63xx_i2s_hw_params(struct snd_pcm_substream *substream,
75
struct snd_pcm_hw_params *params,
76
struct snd_soc_dai *dai)
77
{
78
int ret = 0;
79
struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
80
81
ret = clk_set_rate(i2s_priv->i2s_clk, params_rate(params));
82
if (ret < 0)
83
dev_err(i2s_priv->dev,
84
"Can't set sample rate, err: %d\n", ret);
85
86
return ret;
87
}
88
89
static int bcm63xx_i2s_startup(struct snd_pcm_substream *substream,
90
struct snd_soc_dai *dai)
91
{
92
unsigned int slavemode;
93
struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
94
struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
95
96
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
97
regmap_update_bits(regmap_i2s, I2S_TX_CFG,
98
I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
99
I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE,
100
I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
101
I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE);
102
regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 0);
103
regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 0);
104
regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 1);
105
106
/* TX and RX block each have an independent bit to indicate
107
* if it is generating the clock for the I2S bus. The bus
108
* clocks need to be generated from either the TX or RX block,
109
* but not both
110
*/
111
regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
112
if (slavemode & I2S_RX_SLAVE_MODE_MASK)
113
regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
114
I2S_TX_SLAVE_MODE_MASK,
115
I2S_TX_MASTER_MODE);
116
else
117
regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
118
I2S_TX_SLAVE_MODE_MASK,
119
I2S_TX_SLAVE_MODE);
120
} else {
121
regmap_update_bits(regmap_i2s, I2S_RX_CFG,
122
I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
123
I2S_RX_CLOCK_ENABLE,
124
I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
125
I2S_RX_CLOCK_ENABLE);
126
regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 0);
127
regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 0);
128
regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 1);
129
130
regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
131
if (slavemode & I2S_TX_SLAVE_MODE_MASK)
132
regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
133
I2S_RX_SLAVE_MODE_MASK, 0);
134
else
135
regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
136
I2S_RX_SLAVE_MODE_MASK,
137
I2S_RX_SLAVE_MODE);
138
}
139
return 0;
140
}
141
142
static void bcm63xx_i2s_shutdown(struct snd_pcm_substream *substream,
143
struct snd_soc_dai *dai)
144
{
145
unsigned int enabled, slavemode;
146
struct bcm_i2s_priv *i2s_priv = snd_soc_dai_get_drvdata(dai);
147
struct regmap *regmap_i2s = i2s_priv->regmap_i2s;
148
149
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
150
regmap_update_bits(regmap_i2s, I2S_TX_CFG,
151
I2S_TX_OUT_R | I2S_TX_DATA_ALIGNMENT |
152
I2S_TX_DATA_ENABLE | I2S_TX_CLOCK_ENABLE, 0);
153
regmap_write(regmap_i2s, I2S_TX_IRQ_CTL, 1);
154
regmap_write(regmap_i2s, I2S_TX_IRQ_IFF_THLD, 4);
155
regmap_write(regmap_i2s, I2S_TX_IRQ_OFF_THLD, 4);
156
157
regmap_read(regmap_i2s, I2S_TX_CFG_2, &slavemode);
158
slavemode = slavemode & I2S_TX_SLAVE_MODE_MASK;
159
if (!slavemode) {
160
regmap_read(regmap_i2s, I2S_RX_CFG, &enabled);
161
enabled = enabled & I2S_RX_ENABLE_MASK;
162
if (enabled)
163
regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
164
I2S_RX_SLAVE_MODE_MASK,
165
I2S_RX_MASTER_MODE);
166
}
167
regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
168
I2S_TX_SLAVE_MODE_MASK,
169
I2S_TX_SLAVE_MODE);
170
} else {
171
regmap_update_bits(regmap_i2s, I2S_RX_CFG,
172
I2S_RX_IN_R | I2S_RX_DATA_ALIGNMENT |
173
I2S_RX_CLOCK_ENABLE, 0);
174
regmap_write(regmap_i2s, I2S_RX_IRQ_CTL, 1);
175
regmap_write(regmap_i2s, I2S_RX_IRQ_IFF_THLD, 4);
176
regmap_write(regmap_i2s, I2S_RX_IRQ_OFF_THLD, 4);
177
178
regmap_read(regmap_i2s, I2S_RX_CFG_2, &slavemode);
179
slavemode = slavemode & I2S_RX_SLAVE_MODE_MASK;
180
if (!slavemode) {
181
regmap_read(regmap_i2s, I2S_TX_CFG, &enabled);
182
enabled = enabled & I2S_TX_ENABLE_MASK;
183
if (enabled)
184
regmap_update_bits(regmap_i2s, I2S_TX_CFG_2,
185
I2S_TX_SLAVE_MODE_MASK,
186
I2S_TX_MASTER_MODE);
187
}
188
189
regmap_update_bits(regmap_i2s, I2S_RX_CFG_2,
190
I2S_RX_SLAVE_MODE_MASK, I2S_RX_SLAVE_MODE);
191
}
192
}
193
194
static const struct snd_soc_dai_ops bcm63xx_i2s_dai_ops = {
195
.startup = bcm63xx_i2s_startup,
196
.shutdown = bcm63xx_i2s_shutdown,
197
.hw_params = bcm63xx_i2s_hw_params,
198
};
199
200
static struct snd_soc_dai_driver bcm63xx_i2s_dai = {
201
.name = DRV_NAME,
202
.playback = {
203
.channels_min = 2,
204
.channels_max = 2,
205
.rates = SNDRV_PCM_RATE_8000_192000,
206
.formats = SNDRV_PCM_FMTBIT_S32_LE,
207
},
208
.capture = {
209
.channels_min = 2,
210
.channels_max = 2,
211
.rates = SNDRV_PCM_RATE_8000_192000,
212
.formats = SNDRV_PCM_FMTBIT_S32_LE,
213
},
214
.ops = &bcm63xx_i2s_dai_ops,
215
.symmetric_rate = 1,
216
.symmetric_channels = 1,
217
};
218
219
static const struct snd_soc_component_driver bcm63xx_i2s_component = {
220
.name = "bcm63xx",
221
.legacy_dai_naming = 1,
222
};
223
224
static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
225
{
226
int ret = 0;
227
void __iomem *regs;
228
struct bcm_i2s_priv *i2s_priv;
229
struct regmap *regmap_i2s;
230
struct clk *i2s_clk;
231
232
i2s_priv = devm_kzalloc(&pdev->dev, sizeof(*i2s_priv), GFP_KERNEL);
233
if (!i2s_priv)
234
return -ENOMEM;
235
236
i2s_clk = devm_clk_get(&pdev->dev, "i2sclk");
237
if (IS_ERR(i2s_clk)) {
238
dev_err(&pdev->dev, "%s: cannot get a brcm clock: %ld\n",
239
__func__, PTR_ERR(i2s_clk));
240
return PTR_ERR(i2s_clk);
241
}
242
243
regs = devm_platform_ioremap_resource(pdev, 0);
244
if (IS_ERR(regs)) {
245
ret = PTR_ERR(regs);
246
return ret;
247
}
248
249
regmap_i2s = devm_regmap_init_mmio(&pdev->dev,
250
regs, &brcm_i2s_regmap_config);
251
if (IS_ERR(regmap_i2s))
252
return PTR_ERR(regmap_i2s);
253
254
regmap_update_bits(regmap_i2s, I2S_MISC_CFG,
255
I2S_PAD_LVL_LOOP_DIS_MASK,
256
I2S_PAD_LVL_LOOP_DIS_ENABLE);
257
258
ret = devm_snd_soc_register_component(&pdev->dev,
259
&bcm63xx_i2s_component,
260
&bcm63xx_i2s_dai, 1);
261
if (ret) {
262
dev_err(&pdev->dev, "failed to register the dai\n");
263
return ret;
264
}
265
266
i2s_priv->dev = &pdev->dev;
267
i2s_priv->i2s_clk = i2s_clk;
268
i2s_priv->regmap_i2s = regmap_i2s;
269
dev_set_drvdata(&pdev->dev, i2s_priv);
270
271
ret = bcm63xx_soc_platform_probe(pdev, i2s_priv);
272
if (ret)
273
dev_err(&pdev->dev, "failed to register the pcm\n");
274
275
return ret;
276
}
277
278
static void bcm63xx_i2s_dev_remove(struct platform_device *pdev)
279
{
280
bcm63xx_soc_platform_remove(pdev);
281
}
282
283
#ifdef CONFIG_OF
284
static const struct of_device_id snd_soc_bcm_audio_match[] = {
285
{.compatible = "brcm,bcm63xx-i2s"},
286
{ }
287
};
288
#endif
289
290
static struct platform_driver bcm63xx_i2s_driver = {
291
.driver = {
292
.name = DRV_NAME,
293
.of_match_table = of_match_ptr(snd_soc_bcm_audio_match),
294
},
295
.probe = bcm63xx_i2s_dev_probe,
296
.remove = bcm63xx_i2s_dev_remove,
297
};
298
299
module_platform_driver(bcm63xx_i2s_driver);
300
301
MODULE_AUTHOR("Kevin,Li <[email protected]>");
302
MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface");
303
MODULE_LICENSE("GPL v2");
304
305