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 <kevin-ke.li@broadcom.com>
8 #include <linux/dma-mapping.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"
16 #define DRV_NAME "brcm-i2s"
18 static bool brcm_i2s_wr_reg(struct device
*dev
, unsigned int 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
:
30 static bool brcm_i2s_rd_reg(struct device
*dev
, unsigned int reg
)
33 case I2S_TX_CFG
... I2S_REG_MAX
:
40 static bool brcm_i2s_volatile_reg(struct device
*dev
, unsigned int reg
)
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
:
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
:
63 static const struct regmap_config brcm_i2s_regmap_config
= {
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
,
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
)
79 struct bcm_i2s_priv
*i2s_priv
= snd_soc_dai_get_drvdata(dai
);
81 ret
= clk_set_rate(i2s_priv
->i2s_clk
, params_rate(params
));
83 dev_err(i2s_priv
->dev
,
84 "Can't set sample rate, err: %d\n", ret
);
89 static int bcm63xx_i2s_startup(struct snd_pcm_substream
*substream
,
90 struct snd_soc_dai
*dai
)
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
;
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);
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,
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
,
117 regmap_update_bits(regmap_i2s
, I2S_TX_CFG_2
,
118 I2S_TX_SLAVE_MODE_MASK
,
121 regmap_update_bits(regmap_i2s
, I2S_RX_CFG
,
122 I2S_RX_IN_R
| I2S_RX_DATA_ALIGNMENT
|
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);
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);
135 regmap_update_bits(regmap_i2s
, I2S_RX_CFG_2
,
136 I2S_RX_SLAVE_MODE_MASK
,
142 static void bcm63xx_i2s_shutdown(struct snd_pcm_substream
*substream
,
143 struct snd_soc_dai
*dai
)
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
;
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);
157 regmap_read(regmap_i2s
, I2S_TX_CFG_2
, &slavemode
);
158 slavemode
= slavemode
& I2S_TX_SLAVE_MODE_MASK
;
160 regmap_read(regmap_i2s
, I2S_RX_CFG
, &enabled
);
161 enabled
= enabled
& I2S_RX_ENABLE_MASK
;
163 regmap_update_bits(regmap_i2s
, I2S_RX_CFG_2
,
164 I2S_RX_SLAVE_MODE_MASK
,
167 regmap_update_bits(regmap_i2s
, I2S_TX_CFG_2
,
168 I2S_TX_SLAVE_MODE_MASK
,
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);
178 regmap_read(regmap_i2s
, I2S_RX_CFG_2
, &slavemode
);
179 slavemode
= slavemode
& I2S_RX_SLAVE_MODE_MASK
;
181 regmap_read(regmap_i2s
, I2S_TX_CFG
, &enabled
);
182 enabled
= enabled
& I2S_TX_ENABLE_MASK
;
184 regmap_update_bits(regmap_i2s
, I2S_TX_CFG_2
,
185 I2S_TX_SLAVE_MODE_MASK
,
189 regmap_update_bits(regmap_i2s
, I2S_RX_CFG_2
,
190 I2S_RX_SLAVE_MODE_MASK
, I2S_RX_SLAVE_MODE
);
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
,
200 static struct snd_soc_dai_driver bcm63xx_i2s_dai
= {
205 .rates
= SNDRV_PCM_RATE_8000_192000
,
206 .formats
= SNDRV_PCM_FMTBIT_S32_LE
,
211 .rates
= SNDRV_PCM_RATE_8000_192000
,
212 .formats
= SNDRV_PCM_FMTBIT_S32_LE
,
214 .ops
= &bcm63xx_i2s_dai_ops
,
216 .symmetric_channels
= 1,
219 static const struct snd_soc_component_driver bcm63xx_i2s_component
= {
221 .legacy_dai_naming
= 1,
224 static int bcm63xx_i2s_dev_probe(struct platform_device
*pdev
)
228 struct bcm_i2s_priv
*i2s_priv
;
229 struct regmap
*regmap_i2s
;
232 i2s_priv
= devm_kzalloc(&pdev
->dev
, sizeof(*i2s_priv
), GFP_KERNEL
);
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
);
243 regs
= devm_platform_ioremap_resource(pdev
, 0);
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
);
254 regmap_update_bits(regmap_i2s
, I2S_MISC_CFG
,
255 I2S_PAD_LVL_LOOP_DIS_MASK
,
256 I2S_PAD_LVL_LOOP_DIS_ENABLE
);
258 ret
= devm_snd_soc_register_component(&pdev
->dev
,
259 &bcm63xx_i2s_component
,
260 &bcm63xx_i2s_dai
, 1);
262 dev_err(&pdev
->dev
, "failed to register the dai\n");
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
);
271 ret
= bcm63xx_soc_platform_probe(pdev
, i2s_priv
);
273 dev_err(&pdev
->dev
, "failed to register the pcm\n");
278 static void bcm63xx_i2s_dev_remove(struct platform_device
*pdev
)
280 bcm63xx_soc_platform_remove(pdev
);
284 static const struct of_device_id snd_soc_bcm_audio_match
[] = {
285 {.compatible
= "brcm,bcm63xx-i2s"},
290 static struct platform_driver bcm63xx_i2s_driver
= {
293 .of_match_table
= of_match_ptr(snd_soc_bcm_audio_match
),
295 .probe
= bcm63xx_i2s_dev_probe
,
296 .remove
= bcm63xx_i2s_dev_remove
,
299 module_platform_driver(bcm63xx_i2s_driver
);
301 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
302 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface");
303 MODULE_LICENSE("GPL v2");