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> 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_new = bcm63xx_i2s_dev_remove, 297 }; 298 299 module_platform_driver(bcm63xx_i2s_driver); 300 301 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 302 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface"); 303 MODULE_LICENSE("GPL v2"); 304