Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge series "ASoC: brcm: add dsl and pon chip audio driver" from Kevin Li <kevin-ke.li@broadcom.com>:

Changes in v2:
* Make the comment a C++ one for license header
* Remove all empty functions
* Change all variable to use kernel coding style
* Comment chip TX RX block independently generate I2S bus signals

Kevin Li (2):
ASoC: brcm: Add DSL/PON SoC audio driver
ASoC: brcm: DSL/PON SoC device tree bindings of audio driver

.../bindings/sound/brcm,bcm63xx-audio.txt | 29 ++
sound/soc/bcm/Kconfig | 9 +
sound/soc/bcm/Makefile | 4 +
sound/soc/bcm/bcm63xx-i2s-whistler.c | 317 ++++++++++++
sound/soc/bcm/bcm63xx-i2s.h | 90 ++++
sound/soc/bcm/bcm63xx-pcm-whistler.c | 485 ++++++++++++++++++
6 files changed, 934 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/brcm,bcm63xx-audio.txt
create mode 100644 sound/soc/bcm/bcm63xx-i2s-whistler.c
create mode 100644 sound/soc/bcm/bcm63xx-i2s.h
create mode 100644 sound/soc/bcm/bcm63xx-pcm-whistler.c

--
2.25.1

+934
+29
Documentation/devicetree/bindings/sound/brcm,bcm63xx-audio.txt
··· 1 + Broadcom DSL/PON BCM63xx Audio I2S controller 2 + 3 + Required properties: 4 + - compatible: Should be "brcm,bcm63xx-i2s". 5 + - #address-cells: 32bit valued, 1 cell. 6 + - #size-cells: 32bit valued, 0 cell. 7 + - reg: Should contain audio registers location and length 8 + - interrupts: Should contain the interrupt for the controller. 9 + - clocks: Must contain an entry for each entry in clock-names. 10 + Please refer to clock-bindings.txt. 11 + - clock-names: One of each entry matching the clocks phandles list: 12 + - "i2sclk" (generated clock) Required. 13 + - "i2sosc" (fixed 200MHz clock) Required. 14 + 15 + (1) : The generated clock is required only when any of TX and RX 16 + works on Master Mode. 17 + (2) : The fixed 200MHz clock is from internal chip and always on 18 + 19 + Example: 20 + 21 + i2s: bcm63xx-i2s { 22 + #address-cells = <1>; 23 + #size-cells = <0>; 24 + compatible = "brcm,bcm63xx-i2s"; 25 + reg = <0xFF802080 0xFF>; 26 + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; 27 + clocks = <&i2sclk>, <&osc>; 28 + clock-names = "i2sclk","i2sosc"; 29 + };
+9
sound/soc/bcm/Kconfig
··· 17 17 Cygnus chips (bcm958300, bcm958305, bcm911360) 18 18 19 19 If you don't know what to do here, say N. 20 + 21 + config SND_BCM63XX_I2S_WHISTLER 22 + tristate "SoC Audio support for the Broadcom BCM63XX I2S module" 23 + select REGMAP_MMIO 24 + help 25 + Say Y if you want to add support for ASoC audio on Broadcom 26 + DSL/PON chips (bcm63158, bcm63178) 27 + 28 + If you don't know what to do here, say N
+4
sound/soc/bcm/Makefile
··· 9 9 10 10 obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o 11 11 12 + # BCM63XX Platform Support 13 + snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o 14 + 15 + obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
+317
sound/soc/bcm/bcm63xx-i2s-whistler.c
··· 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_rates = 1, 216 + .symmetric_channels = 1, 217 + }; 218 + 219 + static const struct snd_soc_component_driver bcm63xx_i2s_component = { 220 + .name = "bcm63xx", 221 + }; 222 + 223 + static 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 + 291 + static 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 298 + static const struct of_device_id snd_soc_bcm_audio_match[] = { 299 + {.compatible = "brcm,bcm63xx-i2s"}, 300 + { } 301 + }; 302 + #endif 303 + 304 + static 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 + 313 + module_platform_driver(bcm63xx_i2s_driver); 314 + 315 + MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 316 + MODULE_DESCRIPTION("Broadcom DSL XPON ASOC I2S Interface"); 317 + MODULE_LICENSE("GPL v2");
+90
sound/soc/bcm/bcm63xx-i2s.h
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + // linux/sound/soc/bcm/bcm63xx-i2s.h 3 + // Copyright (c) 2020 Broadcom Corporation 4 + // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> 5 + 6 + #ifndef __BCM63XX_I2S_H 7 + #define __BCM63XX_I2S_H 8 + 9 + #define I2S_DESC_FIFO_DEPTH 8 10 + #define I2S_MISC_CFG (0x003C) 11 + #define I2S_PAD_LVL_LOOP_DIS_MASK (1 << 2) 12 + #define I2S_PAD_LVL_LOOP_DIS_ENABLE I2S_PAD_LVL_LOOP_DIS_MASK 13 + 14 + #define I2S_TX_ENABLE_MASK (1 << 31) 15 + #define I2S_TX_ENABLE I2S_TX_ENABLE_MASK 16 + #define I2S_TX_OUT_R (1 << 19) 17 + #define I2S_TX_DATA_ALIGNMENT (1 << 2) 18 + #define I2S_TX_DATA_ENABLE (1 << 1) 19 + #define I2S_TX_CLOCK_ENABLE (1 << 0) 20 + 21 + #define I2S_TX_DESC_OFF_LEVEL_SHIFT 12 22 + #define I2S_TX_DESC_OFF_LEVEL_MASK (0x0F << I2S_TX_DESC_OFF_LEVEL_SHIFT) 23 + #define I2S_TX_DESC_IFF_LEVEL_SHIFT 8 24 + #define I2S_TX_DESC_IFF_LEVEL_MASK (0x0F << I2S_TX_DESC_IFF_LEVEL_SHIFT) 25 + #define I2S_TX_DESC_OFF_INTR_EN_MSK (1 << 1) 26 + #define I2S_TX_DESC_OFF_INTR_EN I2S_TX_DESC_OFF_INTR_EN_MSK 27 + 28 + #define I2S_TX_CFG (0x0000) 29 + #define I2S_TX_IRQ_CTL (0x0004) 30 + #define I2S_TX_IRQ_EN (0x0008) 31 + #define I2S_TX_IRQ_IFF_THLD (0x000c) 32 + #define I2S_TX_IRQ_OFF_THLD (0x0010) 33 + #define I2S_TX_DESC_IFF_ADDR (0x0014) 34 + #define I2S_TX_DESC_IFF_LEN (0x0018) 35 + #define I2S_TX_DESC_OFF_ADDR (0x001C) 36 + #define I2S_TX_DESC_OFF_LEN (0x0020) 37 + #define I2S_TX_CFG_2 (0x0024) 38 + #define I2S_TX_SLAVE_MODE_SHIFT 13 39 + #define I2S_TX_SLAVE_MODE_MASK (1 << I2S_TX_SLAVE_MODE_SHIFT) 40 + #define I2S_TX_SLAVE_MODE I2S_TX_SLAVE_MODE_MASK 41 + #define I2S_TX_MASTER_MODE 0 42 + #define I2S_TX_INTR_MASK 0x0F 43 + 44 + #define I2S_RX_ENABLE_MASK (1 << 31) 45 + #define I2S_RX_ENABLE I2S_RX_ENABLE_MASK 46 + #define I2S_RX_IN_R (1 << 19) 47 + #define I2S_RX_DATA_ALIGNMENT (1 << 2) 48 + #define I2S_RX_CLOCK_ENABLE (1 << 0) 49 + 50 + #define I2S_RX_DESC_OFF_LEVEL_SHIFT 12 51 + #define I2S_RX_DESC_OFF_LEVEL_MASK (0x0F << I2S_RX_DESC_OFF_LEVEL_SHIFT) 52 + #define I2S_RX_DESC_IFF_LEVEL_SHIFT 8 53 + #define I2S_RX_DESC_IFF_LEVEL_MASK (0x0F << I2S_RX_DESC_IFF_LEVEL_SHIFT) 54 + #define I2S_RX_DESC_OFF_INTR_EN_MSK (1 << 1) 55 + #define I2S_RX_DESC_OFF_INTR_EN I2S_RX_DESC_OFF_INTR_EN_MSK 56 + 57 + #define I2S_RX_CFG (0x0040) /* 20c0 */ 58 + #define I2S_RX_IRQ_CTL (0x0044) 59 + #define I2S_RX_IRQ_EN (0x0048) 60 + #define I2S_RX_IRQ_IFF_THLD (0x004C) 61 + #define I2S_RX_IRQ_OFF_THLD (0x0050) 62 + #define I2S_RX_DESC_IFF_ADDR (0x0054) 63 + #define I2S_RX_DESC_IFF_LEN (0x0058) 64 + #define I2S_RX_DESC_OFF_ADDR (0x005C) 65 + #define I2S_RX_DESC_OFF_LEN (0x0060) 66 + #define I2S_RX_CFG_2 (0x0064) 67 + #define I2S_RX_SLAVE_MODE_SHIFT 13 68 + #define I2S_RX_SLAVE_MODE_MASK (1 << I2S_RX_SLAVE_MODE_SHIFT) 69 + #define I2S_RX_SLAVE_MODE I2S_RX_SLAVE_MODE_MASK 70 + #define I2S_RX_MASTER_MODE 0 71 + #define I2S_RX_INTR_MASK 0x0F 72 + 73 + #define I2S_REG_MAX 0x007C 74 + 75 + struct bcm_i2s_priv { 76 + struct device *dev; 77 + struct resource *r_irq; 78 + struct regmap *regmap_i2s; 79 + struct clk *i2s_clk; 80 + struct snd_pcm_substream *play_substream; 81 + struct snd_pcm_substream *capture_substream; 82 + struct i2s_dma_desc *play_dma_desc; 83 + struct i2s_dma_desc *capture_dma_desc; 84 + }; 85 + 86 + extern int bcm63xx_soc_platform_probe(struct platform_device *pdev, 87 + struct bcm_i2s_priv *i2s_priv); 88 + extern int bcm63xx_soc_platform_remove(struct platform_device *pdev); 89 + 90 + #endif
+485
sound/soc/bcm/bcm63xx-pcm-whistler.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + // linux/sound/bcm/bcm63xx-pcm-whistler.c 3 + // BCM63xx whistler pcm interface 4 + // Copyright (c) 2020 Broadcom Corporation 5 + // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> 6 + 7 + #include <linux/dma-mapping.h> 8 + #include <linux/io.h> 9 + #include <linux/module.h> 10 + #include <sound/pcm_params.h> 11 + #include <linux/regmap.h> 12 + #include <linux/of_device.h> 13 + #include <sound/soc.h> 14 + #include "bcm63xx-i2s.h" 15 + 16 + 17 + struct i2s_dma_desc { 18 + unsigned char *dma_area; 19 + dma_addr_t dma_addr; 20 + unsigned int dma_len; 21 + }; 22 + 23 + struct bcm63xx_runtime_data { 24 + int dma_len; 25 + dma_addr_t dma_addr; 26 + dma_addr_t dma_addr_next; 27 + }; 28 + 29 + static const struct snd_pcm_hardware bcm63xx_pcm_hardware = { 30 + .info = SNDRV_PCM_INFO_MMAP | 31 + SNDRV_PCM_INFO_MMAP_VALID | 32 + SNDRV_PCM_INFO_INTERLEAVED | 33 + SNDRV_PCM_INFO_PAUSE | 34 + SNDRV_PCM_INFO_RESUME, 35 + .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */ 36 + .period_bytes_max = 8192 - 32, 37 + .periods_min = 1, 38 + .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc), 39 + .buffer_bytes_max = 128 * 1024, 40 + .fifo_size = 32, 41 + }; 42 + 43 + static int bcm63xx_pcm_hw_params(struct snd_soc_component *component, 44 + struct snd_pcm_substream *substream, 45 + struct snd_pcm_hw_params *params) 46 + { 47 + struct i2s_dma_desc *dma_desc; 48 + struct snd_soc_pcm_runtime *rtd = substream->private_data; 49 + struct snd_pcm_runtime *runtime = substream->runtime; 50 + 51 + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 52 + runtime->dma_bytes = params_buffer_bytes(params); 53 + 54 + dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT); 55 + if (!dma_desc) 56 + return -ENOMEM; 57 + 58 + snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_desc); 59 + 60 + return 0; 61 + } 62 + 63 + static int bcm63xx_pcm_hw_free(struct snd_soc_component *component, 64 + struct snd_pcm_substream *substream) 65 + { 66 + struct i2s_dma_desc *dma_desc; 67 + struct snd_soc_pcm_runtime *rtd = substream->private_data; 68 + 69 + dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 70 + kfree(dma_desc); 71 + snd_pcm_set_runtime_buffer(substream, NULL); 72 + 73 + return 0; 74 + } 75 + 76 + static int bcm63xx_pcm_trigger(struct snd_soc_component *component, 77 + struct snd_pcm_substream *substream, int cmd) 78 + { 79 + int ret = 0; 80 + struct snd_soc_pcm_runtime *rtd; 81 + struct bcm_i2s_priv *i2s_priv; 82 + struct regmap *regmap_i2s; 83 + 84 + rtd = substream->private_data; 85 + i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 86 + regmap_i2s = i2s_priv->regmap_i2s; 87 + 88 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 89 + switch (cmd) { 90 + case SNDRV_PCM_TRIGGER_START: 91 + regmap_update_bits(regmap_i2s, 92 + I2S_TX_IRQ_EN, 93 + I2S_TX_DESC_OFF_INTR_EN, 94 + I2S_TX_DESC_OFF_INTR_EN); 95 + regmap_update_bits(regmap_i2s, 96 + I2S_TX_CFG, 97 + I2S_TX_ENABLE_MASK, 98 + I2S_TX_ENABLE); 99 + break; 100 + case SNDRV_PCM_TRIGGER_STOP: 101 + case SNDRV_PCM_TRIGGER_SUSPEND: 102 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 103 + regmap_write(regmap_i2s, 104 + I2S_TX_IRQ_EN, 105 + 0); 106 + regmap_update_bits(regmap_i2s, 107 + I2S_TX_CFG, 108 + I2S_TX_ENABLE_MASK, 109 + 0); 110 + break; 111 + default: 112 + ret = -EINVAL; 113 + } 114 + } else { 115 + switch (cmd) { 116 + case SNDRV_PCM_TRIGGER_START: 117 + regmap_update_bits(regmap_i2s, 118 + I2S_RX_IRQ_EN, 119 + I2S_RX_DESC_OFF_INTR_EN_MSK, 120 + I2S_RX_DESC_OFF_INTR_EN); 121 + regmap_update_bits(regmap_i2s, 122 + I2S_RX_CFG, 123 + I2S_RX_ENABLE_MASK, 124 + I2S_RX_ENABLE); 125 + break; 126 + case SNDRV_PCM_TRIGGER_STOP: 127 + case SNDRV_PCM_TRIGGER_SUSPEND: 128 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 129 + regmap_update_bits(regmap_i2s, 130 + I2S_RX_IRQ_EN, 131 + I2S_RX_DESC_OFF_INTR_EN_MSK, 132 + 0); 133 + regmap_update_bits(regmap_i2s, 134 + I2S_RX_CFG, 135 + I2S_RX_ENABLE_MASK, 136 + 0); 137 + break; 138 + default: 139 + ret = -EINVAL; 140 + } 141 + } 142 + return ret; 143 + } 144 + 145 + static int bcm63xx_pcm_prepare(struct snd_soc_component *component, 146 + struct snd_pcm_substream *substream) 147 + { 148 + struct i2s_dma_desc *dma_desc; 149 + struct regmap *regmap_i2s; 150 + struct bcm_i2s_priv *i2s_priv; 151 + struct snd_soc_pcm_runtime *rtd = substream->private_data; 152 + struct snd_pcm_runtime *runtime = substream->runtime; 153 + uint32_t regaddr_desclen, regaddr_descaddr; 154 + 155 + dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 156 + dma_desc->dma_len = snd_pcm_lib_period_bytes(substream); 157 + dma_desc->dma_addr = runtime->dma_addr; 158 + dma_desc->dma_area = runtime->dma_area; 159 + 160 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 161 + regaddr_desclen = I2S_TX_DESC_IFF_LEN; 162 + regaddr_descaddr = I2S_TX_DESC_IFF_ADDR; 163 + } else { 164 + regaddr_desclen = I2S_RX_DESC_IFF_LEN; 165 + regaddr_descaddr = I2S_RX_DESC_IFF_ADDR; 166 + } 167 + 168 + i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 169 + regmap_i2s = i2s_priv->regmap_i2s; 170 + 171 + regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len); 172 + regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr); 173 + 174 + return 0; 175 + } 176 + 177 + static snd_pcm_uframes_t 178 + bcm63xx_pcm_pointer(struct snd_soc_component *component, 179 + struct snd_pcm_substream *substream) 180 + { 181 + snd_pcm_uframes_t x; 182 + struct bcm63xx_runtime_data *prtd = substream->runtime->private_data; 183 + 184 + if ((void *)prtd->dma_addr_next == NULL) 185 + prtd->dma_addr_next = substream->runtime->dma_addr; 186 + 187 + x = bytes_to_frames(substream->runtime, 188 + prtd->dma_addr_next - substream->runtime->dma_addr); 189 + 190 + return x == substream->runtime->buffer_size ? 0 : x; 191 + } 192 + 193 + static int bcm63xx_pcm_mmap(struct snd_soc_component *component, 194 + struct snd_pcm_substream *substream, 195 + struct vm_area_struct *vma) 196 + { 197 + struct snd_pcm_runtime *runtime = substream->runtime; 198 + 199 + return dma_mmap_wc(substream->pcm->card->dev, vma, 200 + runtime->dma_area, 201 + runtime->dma_addr, 202 + runtime->dma_bytes); 203 + 204 + } 205 + 206 + static int bcm63xx_pcm_open(struct snd_soc_component *component, 207 + struct snd_pcm_substream *substream) 208 + { 209 + int ret = 0; 210 + struct snd_pcm_runtime *runtime = substream->runtime; 211 + struct bcm63xx_runtime_data *prtd; 212 + 213 + runtime->hw = bcm63xx_pcm_hardware; 214 + ret = snd_pcm_hw_constraint_step(runtime, 0, 215 + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 216 + if (ret) 217 + goto out; 218 + 219 + ret = snd_pcm_hw_constraint_step(runtime, 0, 220 + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 221 + if (ret) 222 + goto out; 223 + 224 + ret = snd_pcm_hw_constraint_integer(runtime, 225 + SNDRV_PCM_HW_PARAM_PERIODS); 226 + if (ret < 0) 227 + goto out; 228 + 229 + ret = -ENOMEM; 230 + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 231 + if (!prtd) 232 + goto out; 233 + 234 + runtime->private_data = prtd; 235 + return 0; 236 + out: 237 + return ret; 238 + } 239 + 240 + static int bcm63xx_pcm_close(struct snd_soc_component *component, 241 + struct snd_pcm_substream *substream) 242 + { 243 + struct snd_pcm_runtime *runtime = substream->runtime; 244 + struct bcm63xx_runtime_data *prtd = runtime->private_data; 245 + 246 + kfree(prtd); 247 + return 0; 248 + } 249 + 250 + static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv) 251 + { 252 + unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2; 253 + struct bcm63xx_runtime_data *prtd; 254 + struct snd_pcm_substream *substream; 255 + struct snd_pcm_runtime *runtime; 256 + struct regmap *regmap_i2s; 257 + struct i2s_dma_desc *dma_desc; 258 + struct snd_soc_pcm_runtime *rtd; 259 + struct bcm_i2s_priv *i2s_priv; 260 + 261 + i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv; 262 + regmap_i2s = i2s_priv->regmap_i2s; 263 + 264 + /* rx */ 265 + regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status); 266 + 267 + if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) { 268 + substream = i2s_priv->capture_substream; 269 + runtime = substream->runtime; 270 + rtd = substream->private_data; 271 + prtd = runtime->private_data; 272 + dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 273 + 274 + offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >> 275 + I2S_RX_DESC_OFF_LEVEL_SHIFT; 276 + while (offlevel) { 277 + regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1); 278 + regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2); 279 + offlevel--; 280 + } 281 + prtd->dma_addr_next = val_1 + val_2; 282 + ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >> 283 + I2S_RX_DESC_IFF_LEVEL_SHIFT; 284 + 285 + availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 286 + while (availdepth) { 287 + dma_desc->dma_addr += 288 + snd_pcm_lib_period_bytes(substream); 289 + dma_desc->dma_area += 290 + snd_pcm_lib_period_bytes(substream); 291 + if (dma_desc->dma_addr - runtime->dma_addr >= 292 + runtime->dma_bytes) { 293 + dma_desc->dma_addr = runtime->dma_addr; 294 + dma_desc->dma_area = runtime->dma_area; 295 + } 296 + 297 + prtd->dma_addr = dma_desc->dma_addr; 298 + regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN, 299 + snd_pcm_lib_period_bytes(substream)); 300 + regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR, 301 + dma_desc->dma_addr); 302 + availdepth--; 303 + } 304 + 305 + snd_pcm_period_elapsed(substream); 306 + 307 + /* Clear interrupt by writing 0 */ 308 + regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL, 309 + I2S_RX_INTR_MASK, 0); 310 + } 311 + 312 + /* tx */ 313 + regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status); 314 + 315 + if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) { 316 + substream = i2s_priv->play_substream; 317 + runtime = substream->runtime; 318 + rtd = substream->private_data; 319 + prtd = runtime->private_data; 320 + dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 321 + 322 + offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >> 323 + I2S_TX_DESC_OFF_LEVEL_SHIFT; 324 + while (offlevel) { 325 + regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1); 326 + regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2); 327 + prtd->dma_addr_next = val_1 + val_2; 328 + offlevel--; 329 + } 330 + 331 + ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >> 332 + I2S_TX_DESC_IFF_LEVEL_SHIFT; 333 + availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 334 + 335 + while (availdepth) { 336 + dma_desc->dma_addr += 337 + snd_pcm_lib_period_bytes(substream); 338 + dma_desc->dma_area += 339 + snd_pcm_lib_period_bytes(substream); 340 + 341 + if (dma_desc->dma_addr - runtime->dma_addr >= 342 + runtime->dma_bytes) { 343 + dma_desc->dma_addr = runtime->dma_addr; 344 + dma_desc->dma_area = runtime->dma_area; 345 + } 346 + 347 + prtd->dma_addr = dma_desc->dma_addr; 348 + regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN, 349 + snd_pcm_lib_period_bytes(substream)); 350 + regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR, 351 + dma_desc->dma_addr); 352 + availdepth--; 353 + } 354 + 355 + snd_pcm_period_elapsed(substream); 356 + 357 + /* Clear interrupt by writing 0 */ 358 + regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL, 359 + I2S_TX_INTR_MASK, 0); 360 + } 361 + 362 + return IRQ_HANDLED; 363 + } 364 + 365 + static int bcm63xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 366 + { 367 + struct snd_pcm_substream *substream = pcm->streams[stream].substream; 368 + struct snd_dma_buffer *buf = &substream->dma_buffer; 369 + size_t size = bcm63xx_pcm_hardware.buffer_bytes_max; 370 + 371 + buf->dev.type = SNDRV_DMA_TYPE_DEV; 372 + buf->dev.dev = pcm->card->dev; 373 + buf->private_data = NULL; 374 + 375 + buf->area = dma_alloc_wc(pcm->card->dev, 376 + size, &buf->addr, 377 + GFP_KERNEL); 378 + if (!buf->area) 379 + return -ENOMEM; 380 + buf->bytes = size; 381 + return 0; 382 + } 383 + 384 + static int bcm63xx_soc_pcm_new(struct snd_soc_component *component, 385 + struct snd_soc_pcm_runtime *rtd) 386 + { 387 + struct snd_pcm *pcm = rtd->pcm; 388 + struct bcm_i2s_priv *i2s_priv; 389 + int ret; 390 + 391 + i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 392 + 393 + of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); 394 + 395 + ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32)); 396 + if (ret) 397 + goto out; 398 + 399 + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 400 + ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, 401 + SNDRV_PCM_STREAM_PLAYBACK); 402 + if (ret) 403 + goto out; 404 + 405 + i2s_priv->play_substream = 406 + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 407 + } 408 + 409 + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 410 + ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, 411 + SNDRV_PCM_STREAM_CAPTURE); 412 + if (ret) 413 + goto out; 414 + i2s_priv->capture_substream = 415 + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 416 + } 417 + 418 + out: 419 + return ret; 420 + } 421 + 422 + static void bcm63xx_pcm_free_dma_buffers(struct snd_soc_component *component, 423 + struct snd_pcm *pcm) 424 + { 425 + int stream; 426 + struct snd_dma_buffer *buf; 427 + struct snd_pcm_substream *substream; 428 + 429 + for (stream = 0; stream < 2; stream++) { 430 + substream = pcm->streams[stream].substream; 431 + if (!substream) 432 + continue; 433 + buf = &substream->dma_buffer; 434 + if (!buf->area) 435 + continue; 436 + dma_free_wc(pcm->card->dev, buf->bytes, 437 + buf->area, buf->addr); 438 + buf->area = NULL; 439 + } 440 + } 441 + 442 + static const struct snd_soc_component_driver bcm63xx_soc_platform = { 443 + .open = bcm63xx_pcm_open, 444 + .close = bcm63xx_pcm_close, 445 + .hw_params = bcm63xx_pcm_hw_params, 446 + .hw_free = bcm63xx_pcm_hw_free, 447 + .prepare = bcm63xx_pcm_prepare, 448 + .trigger = bcm63xx_pcm_trigger, 449 + .pointer = bcm63xx_pcm_pointer, 450 + .mmap = bcm63xx_pcm_mmap, 451 + .pcm_construct = bcm63xx_soc_pcm_new, 452 + .pcm_destruct = bcm63xx_pcm_free_dma_buffers, 453 + }; 454 + 455 + int bcm63xx_soc_platform_probe(struct platform_device *pdev, 456 + struct bcm_i2s_priv *i2s_priv) 457 + { 458 + int ret; 459 + 460 + i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 461 + if (!i2s_priv->r_irq) { 462 + dev_err(&pdev->dev, "Unable to get register irq resource.\n"); 463 + return -ENODEV; 464 + } 465 + 466 + ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, 467 + i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); 468 + if (ret) { 469 + dev_err(&pdev->dev, 470 + "i2s_init: failed to request interrupt.ret=%d\n", ret); 471 + return ret; 472 + } 473 + 474 + return devm_snd_soc_register_component(&pdev->dev, 475 + &bcm63xx_soc_platform, NULL, 0); 476 + } 477 + 478 + int bcm63xx_soc_platform_remove(struct platform_device *pdev) 479 + { 480 + return 0; 481 + } 482 + 483 + MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 484 + MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface"); 485 + MODULE_LICENSE("GPL v2");