415 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2// linux/sound/bcm/bcm63xx-pcm-whistler.c3// BCM63xx whistler pcm interface4// Copyright (c) 2020 Broadcom Corporation5// 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 18struct i2s_dma_desc {19 unsigned char *dma_area;20 dma_addr_t dma_addr;21 unsigned int dma_len;22};23 24struct bcm63xx_runtime_data {25 int dma_len;26 dma_addr_t dma_addr;27 dma_addr_t dma_addr_next;28};29 30static 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 44static 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 60static 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 72static 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 141static 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 173static snd_pcm_uframes_t174bcm63xx_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 189static 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;219out:220 return ret;221}222 223static 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 233static 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 348static 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 375static 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 386int 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 407int bcm63xx_soc_platform_remove(struct platform_device *pdev)408{409 return 0;410}411 412MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");413MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");414MODULE_LICENSE("GPL v2");415