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/irq.h> 10 #include <linux/module.h> 11 #include <sound/pcm_params.h> 12 #include <linux/regmap.h> 13 #include <linux/of_device.h> 14 #include <sound/soc.h> 15 #include "bcm63xx-i2s.h" 16 17 18 struct i2s_dma_desc { 19 unsigned char *dma_area; 20 dma_addr_t dma_addr; 21 unsigned int dma_len; 22 }; 23 24 struct bcm63xx_runtime_data { 25 int dma_len; 26 dma_addr_t dma_addr; 27 dma_addr_t dma_addr_next; 28 }; 29 30 static const struct snd_pcm_hardware bcm63xx_pcm_hardware = { 31 .info = SNDRV_PCM_INFO_MMAP | 32 SNDRV_PCM_INFO_MMAP_VALID | 33 SNDRV_PCM_INFO_INTERLEAVED | 34 SNDRV_PCM_INFO_PAUSE | 35 SNDRV_PCM_INFO_RESUME, 36 .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */ 37 .period_bytes_max = 8192 - 32, 38 .periods_min = 1, 39 .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc), 40 .buffer_bytes_max = 128 * 1024, 41 .fifo_size = 32, 42 }; 43 44 static int bcm63xx_pcm_hw_params(struct snd_soc_component *component, 45 struct snd_pcm_substream *substream, 46 struct snd_pcm_hw_params *params) 47 { 48 struct i2s_dma_desc *dma_desc; 49 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 50 51 dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT); 52 if (!dma_desc) 53 return -ENOMEM; 54 55 snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_desc); 56 57 return 0; 58 } 59 60 static int bcm63xx_pcm_hw_free(struct snd_soc_component *component, 61 struct snd_pcm_substream *substream) 62 { 63 struct i2s_dma_desc *dma_desc; 64 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 65 66 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 67 kfree(dma_desc); 68 69 return 0; 70 } 71 72 static int bcm63xx_pcm_trigger(struct snd_soc_component *component, 73 struct snd_pcm_substream *substream, int cmd) 74 { 75 int ret = 0; 76 struct snd_soc_pcm_runtime *rtd; 77 struct bcm_i2s_priv *i2s_priv; 78 struct regmap *regmap_i2s; 79 80 rtd = snd_soc_substream_to_rtd(substream); 81 i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev); 82 regmap_i2s = i2s_priv->regmap_i2s; 83 84 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 85 switch (cmd) { 86 case SNDRV_PCM_TRIGGER_START: 87 regmap_update_bits(regmap_i2s, 88 I2S_TX_IRQ_EN, 89 I2S_TX_DESC_OFF_INTR_EN, 90 I2S_TX_DESC_OFF_INTR_EN); 91 regmap_update_bits(regmap_i2s, 92 I2S_TX_CFG, 93 I2S_TX_ENABLE_MASK, 94 I2S_TX_ENABLE); 95 break; 96 case SNDRV_PCM_TRIGGER_STOP: 97 case SNDRV_PCM_TRIGGER_SUSPEND: 98 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 99 regmap_write(regmap_i2s, 100 I2S_TX_IRQ_EN, 101 0); 102 regmap_update_bits(regmap_i2s, 103 I2S_TX_CFG, 104 I2S_TX_ENABLE_MASK, 105 0); 106 break; 107 default: 108 ret = -EINVAL; 109 } 110 } else { 111 switch (cmd) { 112 case SNDRV_PCM_TRIGGER_START: 113 regmap_update_bits(regmap_i2s, 114 I2S_RX_IRQ_EN, 115 I2S_RX_DESC_OFF_INTR_EN_MSK, 116 I2S_RX_DESC_OFF_INTR_EN); 117 regmap_update_bits(regmap_i2s, 118 I2S_RX_CFG, 119 I2S_RX_ENABLE_MASK, 120 I2S_RX_ENABLE); 121 break; 122 case SNDRV_PCM_TRIGGER_STOP: 123 case SNDRV_PCM_TRIGGER_SUSPEND: 124 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 125 regmap_update_bits(regmap_i2s, 126 I2S_RX_IRQ_EN, 127 I2S_RX_DESC_OFF_INTR_EN_MSK, 128 0); 129 regmap_update_bits(regmap_i2s, 130 I2S_RX_CFG, 131 I2S_RX_ENABLE_MASK, 132 0); 133 break; 134 default: 135 ret = -EINVAL; 136 } 137 } 138 return ret; 139 } 140 141 static int bcm63xx_pcm_prepare(struct snd_soc_component *component, 142 struct snd_pcm_substream *substream) 143 { 144 struct i2s_dma_desc *dma_desc; 145 struct regmap *regmap_i2s; 146 struct bcm_i2s_priv *i2s_priv; 147 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 148 struct snd_pcm_runtime *runtime = substream->runtime; 149 uint32_t regaddr_desclen, regaddr_descaddr; 150 151 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 152 dma_desc->dma_len = snd_pcm_lib_period_bytes(substream); 153 dma_desc->dma_addr = runtime->dma_addr; 154 dma_desc->dma_area = runtime->dma_area; 155 156 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 157 regaddr_desclen = I2S_TX_DESC_IFF_LEN; 158 regaddr_descaddr = I2S_TX_DESC_IFF_ADDR; 159 } else { 160 regaddr_desclen = I2S_RX_DESC_IFF_LEN; 161 regaddr_descaddr = I2S_RX_DESC_IFF_ADDR; 162 } 163 164 i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev); 165 regmap_i2s = i2s_priv->regmap_i2s; 166 167 regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len); 168 regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr); 169 170 return 0; 171 } 172 173 static snd_pcm_uframes_t 174 bcm63xx_pcm_pointer(struct snd_soc_component *component, 175 struct snd_pcm_substream *substream) 176 { 177 snd_pcm_uframes_t x; 178 struct bcm63xx_runtime_data *prtd = substream->runtime->private_data; 179 180 if (!prtd->dma_addr_next) 181 prtd->dma_addr_next = substream->runtime->dma_addr; 182 183 x = bytes_to_frames(substream->runtime, 184 prtd->dma_addr_next - substream->runtime->dma_addr); 185 186 return x == substream->runtime->buffer_size ? 0 : x; 187 } 188 189 static int bcm63xx_pcm_open(struct snd_soc_component *component, 190 struct snd_pcm_substream *substream) 191 { 192 int ret = 0; 193 struct snd_pcm_runtime *runtime = substream->runtime; 194 struct bcm63xx_runtime_data *prtd; 195 196 runtime->hw = bcm63xx_pcm_hardware; 197 ret = snd_pcm_hw_constraint_step(runtime, 0, 198 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 199 if (ret) 200 goto out; 201 202 ret = snd_pcm_hw_constraint_step(runtime, 0, 203 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 204 if (ret) 205 goto out; 206 207 ret = snd_pcm_hw_constraint_integer(runtime, 208 SNDRV_PCM_HW_PARAM_PERIODS); 209 if (ret < 0) 210 goto out; 211 212 ret = -ENOMEM; 213 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 214 if (!prtd) 215 goto out; 216 217 runtime->private_data = prtd; 218 return 0; 219 out: 220 return ret; 221 } 222 223 static int bcm63xx_pcm_close(struct snd_soc_component *component, 224 struct snd_pcm_substream *substream) 225 { 226 struct snd_pcm_runtime *runtime = substream->runtime; 227 struct bcm63xx_runtime_data *prtd = runtime->private_data; 228 229 kfree(prtd); 230 return 0; 231 } 232 233 static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv) 234 { 235 unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2; 236 struct bcm63xx_runtime_data *prtd; 237 struct snd_pcm_substream *substream; 238 struct snd_pcm_runtime *runtime; 239 struct regmap *regmap_i2s; 240 struct i2s_dma_desc *dma_desc; 241 struct snd_soc_pcm_runtime *rtd; 242 struct bcm_i2s_priv *i2s_priv; 243 244 i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv; 245 regmap_i2s = i2s_priv->regmap_i2s; 246 247 /* rx */ 248 regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status); 249 250 if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) { 251 substream = i2s_priv->capture_substream; 252 runtime = substream->runtime; 253 rtd = snd_soc_substream_to_rtd(substream); 254 prtd = runtime->private_data; 255 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 256 257 offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >> 258 I2S_RX_DESC_OFF_LEVEL_SHIFT; 259 while (offlevel) { 260 regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1); 261 regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2); 262 offlevel--; 263 } 264 prtd->dma_addr_next = val_1 + val_2; 265 ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >> 266 I2S_RX_DESC_IFF_LEVEL_SHIFT; 267 268 availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 269 while (availdepth) { 270 dma_desc->dma_addr += 271 snd_pcm_lib_period_bytes(substream); 272 dma_desc->dma_area += 273 snd_pcm_lib_period_bytes(substream); 274 if (dma_desc->dma_addr - runtime->dma_addr >= 275 runtime->dma_bytes) { 276 dma_desc->dma_addr = runtime->dma_addr; 277 dma_desc->dma_area = runtime->dma_area; 278 } 279 280 prtd->dma_addr = dma_desc->dma_addr; 281 regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN, 282 snd_pcm_lib_period_bytes(substream)); 283 regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR, 284 dma_desc->dma_addr); 285 availdepth--; 286 } 287 288 snd_pcm_period_elapsed(substream); 289 290 /* Clear interrupt by writing 0 */ 291 regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL, 292 I2S_RX_INTR_MASK, 0); 293 } 294 295 /* tx */ 296 regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status); 297 298 if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) { 299 substream = i2s_priv->play_substream; 300 runtime = substream->runtime; 301 rtd = snd_soc_substream_to_rtd(substream); 302 prtd = runtime->private_data; 303 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream); 304 305 offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >> 306 I2S_TX_DESC_OFF_LEVEL_SHIFT; 307 while (offlevel) { 308 regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1); 309 regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2); 310 prtd->dma_addr_next = val_1 + val_2; 311 offlevel--; 312 } 313 314 ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >> 315 I2S_TX_DESC_IFF_LEVEL_SHIFT; 316 availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 317 318 while (availdepth) { 319 dma_desc->dma_addr += 320 snd_pcm_lib_period_bytes(substream); 321 dma_desc->dma_area += 322 snd_pcm_lib_period_bytes(substream); 323 324 if (dma_desc->dma_addr - runtime->dma_addr >= 325 runtime->dma_bytes) { 326 dma_desc->dma_addr = runtime->dma_addr; 327 dma_desc->dma_area = runtime->dma_area; 328 } 329 330 prtd->dma_addr = dma_desc->dma_addr; 331 regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN, 332 snd_pcm_lib_period_bytes(substream)); 333 regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR, 334 dma_desc->dma_addr); 335 availdepth--; 336 } 337 338 snd_pcm_period_elapsed(substream); 339 340 /* Clear interrupt by writing 0 */ 341 regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL, 342 I2S_TX_INTR_MASK, 0); 343 } 344 345 return IRQ_HANDLED; 346 } 347 348 static int bcm63xx_soc_pcm_new(struct snd_soc_component *component, 349 struct snd_soc_pcm_runtime *rtd) 350 { 351 struct snd_pcm *pcm = rtd->pcm; 352 struct bcm_i2s_priv *i2s_priv; 353 int ret; 354 355 i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev); 356 357 of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); 358 359 ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32)); 360 if (ret) 361 return ret; 362 363 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 364 i2s_priv->play_substream = 365 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 366 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 367 i2s_priv->capture_substream = 368 pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 369 370 return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, 371 pcm->card->dev, 372 bcm63xx_pcm_hardware.buffer_bytes_max); 373 } 374 375 static const struct snd_soc_component_driver bcm63xx_soc_platform = { 376 .open = bcm63xx_pcm_open, 377 .close = bcm63xx_pcm_close, 378 .hw_params = bcm63xx_pcm_hw_params, 379 .hw_free = bcm63xx_pcm_hw_free, 380 .prepare = bcm63xx_pcm_prepare, 381 .trigger = bcm63xx_pcm_trigger, 382 .pointer = bcm63xx_pcm_pointer, 383 .pcm_construct = bcm63xx_soc_pcm_new, 384 }; 385 386 int bcm63xx_soc_platform_probe(struct platform_device *pdev, 387 struct bcm_i2s_priv *i2s_priv) 388 { 389 int ret; 390 391 ret = platform_get_irq(pdev, 0); 392 if (ret < 0) 393 return ret; 394 395 ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr, 396 irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv); 397 if (ret) { 398 dev_err(&pdev->dev, 399 "i2s_init: failed to request interrupt.ret=%d\n", ret); 400 return ret; 401 } 402 403 return devm_snd_soc_register_component(&pdev->dev, 404 &bcm63xx_soc_platform, NULL, 0); 405 } 406 407 int bcm63xx_soc_platform_remove(struct platform_device *pdev) 408 { 409 return 0; 410 } 411 412 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 413 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface"); 414 MODULE_LICENSE("GPL v2"); 415
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.