Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 // linux/sound/bcm/bcm63xx-pcm-whistler.c
0003 // BCM63xx whistler pcm interface
0004 // Copyright (c) 2020 Broadcom Corporation
0005 // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
0006 
0007 #include <linux/dma-mapping.h>
0008 #include <linux/io.h>
0009 #include <linux/irq.h>
0010 #include <linux/module.h>
0011 #include <sound/pcm_params.h>
0012 #include <linux/regmap.h>
0013 #include <linux/of_device.h>
0014 #include <sound/soc.h>
0015 #include "bcm63xx-i2s.h"
0016 
0017 
0018 struct i2s_dma_desc {
0019     unsigned char *dma_area;
0020     dma_addr_t dma_addr;
0021     unsigned int dma_len;
0022 };
0023 
0024 struct bcm63xx_runtime_data {
0025     int dma_len;
0026     dma_addr_t dma_addr;
0027     dma_addr_t dma_addr_next;
0028 };
0029 
0030 static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
0031     .info = SNDRV_PCM_INFO_MMAP |
0032         SNDRV_PCM_INFO_MMAP_VALID |
0033         SNDRV_PCM_INFO_INTERLEAVED |
0034         SNDRV_PCM_INFO_PAUSE |
0035         SNDRV_PCM_INFO_RESUME,
0036     .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
0037     .period_bytes_max = 8192 - 32,
0038     .periods_min = 1,
0039     .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
0040     .buffer_bytes_max = 128 * 1024,
0041     .fifo_size = 32,
0042 };
0043 
0044 static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
0045                  struct snd_pcm_substream *substream,
0046                  struct snd_pcm_hw_params *params)
0047 {
0048     struct i2s_dma_desc *dma_desc;
0049     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0050 
0051     dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
0052     if (!dma_desc)
0053         return -ENOMEM;
0054 
0055     snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
0056 
0057     return 0;
0058 }
0059 
0060 static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
0061             struct snd_pcm_substream *substream)
0062 {
0063     struct i2s_dma_desc *dma_desc;
0064     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0065 
0066     dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
0067     kfree(dma_desc);
0068 
0069     return 0;
0070 }
0071 
0072 static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
0073                    struct snd_pcm_substream *substream, int cmd)
0074 {
0075     int ret = 0;
0076     struct snd_soc_pcm_runtime *rtd;
0077     struct bcm_i2s_priv *i2s_priv;
0078     struct regmap   *regmap_i2s;
0079 
0080     rtd = asoc_substream_to_rtd(substream);
0081     i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
0082     regmap_i2s = i2s_priv->regmap_i2s;
0083 
0084     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0085         switch (cmd) {
0086         case SNDRV_PCM_TRIGGER_START:
0087             regmap_update_bits(regmap_i2s,
0088                        I2S_TX_IRQ_EN,
0089                        I2S_TX_DESC_OFF_INTR_EN,
0090                        I2S_TX_DESC_OFF_INTR_EN);
0091             regmap_update_bits(regmap_i2s,
0092                        I2S_TX_CFG,
0093                        I2S_TX_ENABLE_MASK,
0094                        I2S_TX_ENABLE);
0095             break;
0096         case SNDRV_PCM_TRIGGER_STOP:
0097         case SNDRV_PCM_TRIGGER_SUSPEND:
0098         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0099             regmap_write(regmap_i2s,
0100                      I2S_TX_IRQ_EN,
0101                      0);
0102             regmap_update_bits(regmap_i2s,
0103                        I2S_TX_CFG,
0104                        I2S_TX_ENABLE_MASK,
0105                        0);
0106             break;
0107         default:
0108             ret = -EINVAL;
0109         }
0110     } else {
0111         switch (cmd) {
0112         case SNDRV_PCM_TRIGGER_START:
0113             regmap_update_bits(regmap_i2s,
0114                        I2S_RX_IRQ_EN,
0115                        I2S_RX_DESC_OFF_INTR_EN_MSK,
0116                        I2S_RX_DESC_OFF_INTR_EN);
0117             regmap_update_bits(regmap_i2s,
0118                        I2S_RX_CFG,
0119                        I2S_RX_ENABLE_MASK,
0120                        I2S_RX_ENABLE);
0121             break;
0122         case SNDRV_PCM_TRIGGER_STOP:
0123         case SNDRV_PCM_TRIGGER_SUSPEND:
0124         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0125             regmap_update_bits(regmap_i2s,
0126                        I2S_RX_IRQ_EN,
0127                        I2S_RX_DESC_OFF_INTR_EN_MSK,
0128                        0);
0129             regmap_update_bits(regmap_i2s,
0130                        I2S_RX_CFG,
0131                        I2S_RX_ENABLE_MASK,
0132                        0);
0133             break;
0134         default:
0135             ret = -EINVAL;
0136         }
0137     }
0138     return ret;
0139 }
0140 
0141 static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
0142             struct snd_pcm_substream *substream)
0143 {
0144     struct i2s_dma_desc *dma_desc;
0145     struct regmap       *regmap_i2s;
0146     struct bcm_i2s_priv *i2s_priv;
0147     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0148     struct snd_pcm_runtime *runtime = substream->runtime;
0149     uint32_t regaddr_desclen, regaddr_descaddr;
0150 
0151     dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
0152     dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
0153     dma_desc->dma_addr = runtime->dma_addr;
0154     dma_desc->dma_area = runtime->dma_area;
0155 
0156     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0157         regaddr_desclen = I2S_TX_DESC_IFF_LEN;
0158         regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
0159     } else {
0160         regaddr_desclen = I2S_RX_DESC_IFF_LEN;
0161         regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
0162     }
0163 
0164     i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
0165     regmap_i2s = i2s_priv->regmap_i2s;
0166 
0167     regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
0168     regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
0169 
0170     return 0;
0171 }
0172 
0173 static snd_pcm_uframes_t
0174 bcm63xx_pcm_pointer(struct snd_soc_component *component,
0175         struct snd_pcm_substream *substream)
0176 {
0177     snd_pcm_uframes_t x;
0178     struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
0179 
0180     if (!prtd->dma_addr_next)
0181         prtd->dma_addr_next = substream->runtime->dma_addr;
0182 
0183     x = bytes_to_frames(substream->runtime,
0184         prtd->dma_addr_next - substream->runtime->dma_addr);
0185 
0186     return x == substream->runtime->buffer_size ? 0 : x;
0187 }
0188 
0189 static int bcm63xx_pcm_open(struct snd_soc_component *component,
0190             struct snd_pcm_substream *substream)
0191 {
0192     int ret = 0;
0193     struct snd_pcm_runtime *runtime = substream->runtime;
0194     struct bcm63xx_runtime_data *prtd;
0195 
0196     runtime->hw = bcm63xx_pcm_hardware;
0197     ret = snd_pcm_hw_constraint_step(runtime, 0,
0198                      SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
0199     if (ret)
0200         goto out;
0201 
0202     ret = snd_pcm_hw_constraint_step(runtime, 0,
0203                      SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
0204     if (ret)
0205         goto out;
0206 
0207     ret = snd_pcm_hw_constraint_integer(runtime,
0208                         SNDRV_PCM_HW_PARAM_PERIODS);
0209     if (ret < 0)
0210         goto out;
0211 
0212     ret = -ENOMEM;
0213     prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
0214     if (!prtd)
0215         goto out;
0216 
0217     runtime->private_data = prtd;
0218     return 0;
0219 out:
0220     return ret;
0221 }
0222 
0223 static int bcm63xx_pcm_close(struct snd_soc_component *component,
0224             struct snd_pcm_substream *substream)
0225 {
0226     struct snd_pcm_runtime *runtime = substream->runtime;
0227     struct bcm63xx_runtime_data *prtd = runtime->private_data;
0228 
0229     kfree(prtd);
0230     return 0;
0231 }
0232 
0233 static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
0234 {
0235     unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
0236     struct bcm63xx_runtime_data *prtd;
0237     struct snd_pcm_substream *substream;
0238     struct snd_pcm_runtime *runtime;
0239     struct regmap *regmap_i2s;
0240     struct i2s_dma_desc *dma_desc;
0241     struct snd_soc_pcm_runtime *rtd;
0242     struct bcm_i2s_priv *i2s_priv;
0243 
0244     i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
0245     regmap_i2s = i2s_priv->regmap_i2s;
0246 
0247     /* rx */
0248     regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
0249 
0250     if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
0251         substream = i2s_priv->capture_substream;
0252         runtime = substream->runtime;
0253         rtd = asoc_substream_to_rtd(substream);
0254         prtd = runtime->private_data;
0255         dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
0256 
0257         offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
0258                I2S_RX_DESC_OFF_LEVEL_SHIFT;
0259         while (offlevel) {
0260             regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
0261             regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
0262             offlevel--;
0263         }
0264         prtd->dma_addr_next = val_1 + val_2;
0265         ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
0266                I2S_RX_DESC_IFF_LEVEL_SHIFT;
0267 
0268         availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
0269         while (availdepth) {
0270             dma_desc->dma_addr +=
0271                     snd_pcm_lib_period_bytes(substream);
0272             dma_desc->dma_area +=
0273                     snd_pcm_lib_period_bytes(substream);
0274             if (dma_desc->dma_addr - runtime->dma_addr >=
0275                         runtime->dma_bytes) {
0276                 dma_desc->dma_addr = runtime->dma_addr;
0277                 dma_desc->dma_area = runtime->dma_area;
0278             }
0279 
0280             prtd->dma_addr = dma_desc->dma_addr;
0281             regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
0282                      snd_pcm_lib_period_bytes(substream));
0283             regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
0284                      dma_desc->dma_addr);
0285             availdepth--;
0286         }
0287 
0288         snd_pcm_period_elapsed(substream);
0289 
0290         /* Clear interrupt by writing 0 */
0291         regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
0292                    I2S_RX_INTR_MASK, 0);
0293     }
0294 
0295     /* tx */
0296     regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
0297 
0298     if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
0299         substream = i2s_priv->play_substream;
0300         runtime = substream->runtime;
0301         rtd = asoc_substream_to_rtd(substream);
0302         prtd = runtime->private_data;
0303         dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
0304 
0305         offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
0306                I2S_TX_DESC_OFF_LEVEL_SHIFT;
0307         while (offlevel) {
0308             regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
0309             regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
0310             prtd->dma_addr_next = val_1 + val_2;
0311             offlevel--;
0312         }
0313 
0314         ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
0315             I2S_TX_DESC_IFF_LEVEL_SHIFT;
0316         availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
0317 
0318         while (availdepth) {
0319             dma_desc->dma_addr +=
0320                     snd_pcm_lib_period_bytes(substream);
0321             dma_desc->dma_area +=
0322                     snd_pcm_lib_period_bytes(substream);
0323 
0324             if (dma_desc->dma_addr - runtime->dma_addr >=
0325                             runtime->dma_bytes) {
0326                 dma_desc->dma_addr = runtime->dma_addr;
0327                 dma_desc->dma_area = runtime->dma_area;
0328             }
0329 
0330             prtd->dma_addr = dma_desc->dma_addr;
0331             regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
0332                 snd_pcm_lib_period_bytes(substream));
0333             regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
0334                     dma_desc->dma_addr);
0335             availdepth--;
0336         }
0337 
0338         snd_pcm_period_elapsed(substream);
0339 
0340         /* Clear interrupt by writing 0 */
0341         regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
0342                    I2S_TX_INTR_MASK, 0);
0343     }
0344 
0345     return IRQ_HANDLED;
0346 }
0347 
0348 static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
0349         struct snd_soc_pcm_runtime *rtd)
0350 {
0351     struct snd_pcm *pcm = rtd->pcm;
0352     struct bcm_i2s_priv *i2s_priv;
0353     int ret;
0354 
0355     i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
0356 
0357     of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
0358 
0359     ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
0360     if (ret)
0361         return ret;
0362 
0363     if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
0364         i2s_priv->play_substream =
0365             pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
0366     if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
0367         i2s_priv->capture_substream =
0368             pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
0369 
0370     return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
0371                         pcm->card->dev,
0372                         bcm63xx_pcm_hardware.buffer_bytes_max);
0373 }
0374 
0375 static const struct snd_soc_component_driver bcm63xx_soc_platform = {
0376     .open = bcm63xx_pcm_open,
0377     .close = bcm63xx_pcm_close,
0378     .hw_params = bcm63xx_pcm_hw_params,
0379     .hw_free = bcm63xx_pcm_hw_free,
0380     .prepare = bcm63xx_pcm_prepare,
0381     .trigger = bcm63xx_pcm_trigger,
0382     .pointer = bcm63xx_pcm_pointer,
0383     .pcm_construct = bcm63xx_soc_pcm_new,
0384 };
0385 
0386 int bcm63xx_soc_platform_probe(struct platform_device *pdev,
0387                    struct bcm_i2s_priv *i2s_priv)
0388 {
0389     int ret;
0390 
0391     ret = platform_get_irq(pdev, 0);
0392     if (ret < 0)
0393         return ret;
0394 
0395     ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
0396                    irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
0397     if (ret) {
0398         dev_err(&pdev->dev,
0399             "i2s_init: failed to request interrupt.ret=%d\n", ret);
0400         return ret;
0401     }
0402 
0403     return devm_snd_soc_register_component(&pdev->dev,
0404                     &bcm63xx_soc_platform, NULL, 0);
0405 }
0406 
0407 int bcm63xx_soc_platform_remove(struct platform_device *pdev)
0408 {
0409     return 0;
0410 }
0411 
0412 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
0413 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
0414 MODULE_LICENSE("GPL v2");