bcm63xx-i2s-whistler.c (8739B)
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 18static 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 30static 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 40static 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 63static 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 74static 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 89static 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 142static 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 194static 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 200static 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 219static const struct snd_soc_component_driver bcm63xx_i2s_component = { 220 .name = "bcm63xx", 221}; 222 223static int bcm63xx_i2s_dev_probe(struct platform_device *pdev) 224{ 225 int ret = 0; 226 void __iomem *regs; 227 struct resource *r_mem, *region; 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 r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 244 if (!r_mem) { 245 dev_err(&pdev->dev, "Unable to get register resource.\n"); 246 return -ENODEV; 247 } 248 249 region = devm_request_mem_region(&pdev->dev, r_mem->start, 250 resource_size(r_mem), DRV_NAME); 251 if (!region) { 252 dev_err(&pdev->dev, "Memory region already claimed\n"); 253 return -EBUSY; 254 } 255 256 regs = devm_ioremap_resource(&pdev->dev, r_mem); 257 if (IS_ERR(regs)) { 258 ret = PTR_ERR(regs); 259 return ret; 260 } 261 262 regmap_i2s = devm_regmap_init_mmio(&pdev->dev, 263 regs, &brcm_i2s_regmap_config); 264 if (IS_ERR(regmap_i2s)) 265 return PTR_ERR(regmap_i2s); 266 267 regmap_update_bits(regmap_i2s, I2S_MISC_CFG, 268 I2S_PAD_LVL_LOOP_DIS_MASK, 269 I2S_PAD_LVL_LOOP_DIS_ENABLE); 270 271 ret = devm_snd_soc_register_component(&pdev->dev, 272 &bcm63xx_i2s_component, 273 &bcm63xx_i2s_dai, 1); 274 if (ret) { 275 dev_err(&pdev->dev, "failed to register the dai\n"); 276 return ret; 277 } 278 279 i2s_priv->dev = &pdev->dev; 280 i2s_priv->i2s_clk = i2s_clk; 281 i2s_priv->regmap_i2s = regmap_i2s; 282 dev_set_drvdata(&pdev->dev, i2s_priv); 283 284 ret = bcm63xx_soc_platform_probe(pdev, i2s_priv); 285 if (ret) 286 dev_err(&pdev->dev, "failed to register the pcm\n"); 287 288 return ret; 289} 290 291static int bcm63xx_i2s_dev_remove(struct platform_device *pdev) 292{ 293 bcm63xx_soc_platform_remove(pdev); 294 return 0; 295} 296 297#ifdef CONFIG_OF 298static const struct of_device_id snd_soc_bcm_audio_match[] = { 299 {.compatible = "brcm,bcm63xx-i2s"}, 300 { } 301}; 302#endif 303 304static struct platform_driver bcm63xx_i2s_driver = { 305 .driver = { 306 .name = DRV_NAME, 307 .of_match_table = of_match_ptr(snd_soc_bcm_audio_match), 308 }, 309 .probe = bcm63xx_i2s_dev_probe, 310 .remove = bcm63xx_i2s_dev_remove, 311}; 312 313module_platform_driver(bcm63xx_i2s_driver); 314 315MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 316MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface"); 317MODULE_LICENSE("GPL v2");