linux/sound/soc/bcm/bcm63xx-pcm-whistler.c
<<
>>
Prefs
   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
  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_t
 174bcm63xx_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                bool val_read = false;
 260                while (offlevel) {
 261                        regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
 262                        regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
 263                        val_read = true;
 264                        offlevel--;
 265                }
 266                if (val_read)
 267                        prtd->dma_addr_next = val_1 + val_2;
 268
 269                ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
 270                           I2S_RX_DESC_IFF_LEVEL_SHIFT;
 271
 272                availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
 273                while (availdepth) {
 274                        dma_desc->dma_addr +=
 275                                        snd_pcm_lib_period_bytes(substream);
 276                        dma_desc->dma_area +=
 277                                        snd_pcm_lib_period_bytes(substream);
 278                        if (dma_desc->dma_addr - runtime->dma_addr >=
 279                                                runtime->dma_bytes) {
 280                                dma_desc->dma_addr = runtime->dma_addr;
 281                                dma_desc->dma_area = runtime->dma_area;
 282                        }
 283
 284                        prtd->dma_addr = dma_desc->dma_addr;
 285                        regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
 286                                     snd_pcm_lib_period_bytes(substream));
 287                        regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
 288                                     dma_desc->dma_addr);
 289                        availdepth--;
 290                }
 291
 292                snd_pcm_period_elapsed(substream);
 293
 294                /* Clear interrupt by writing 0 */
 295                regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
 296                                   I2S_RX_INTR_MASK, 0);
 297        }
 298
 299        /* tx */
 300        regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
 301
 302        if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
 303                substream = i2s_priv->play_substream;
 304                runtime = substream->runtime;
 305                rtd = snd_soc_substream_to_rtd(substream);
 306                prtd = runtime->private_data;
 307                dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
 308
 309                offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
 310                           I2S_TX_DESC_OFF_LEVEL_SHIFT;
 311                while (offlevel) {
 312                        regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
 313                        regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
 314                        prtd->dma_addr_next = val_1 + val_2;
 315                        offlevel--;
 316                }
 317
 318                ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
 319                        I2S_TX_DESC_IFF_LEVEL_SHIFT;
 320                availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
 321
 322                while (availdepth) {
 323                        dma_desc->dma_addr +=
 324                                        snd_pcm_lib_period_bytes(substream);
 325                        dma_desc->dma_area +=
 326                                        snd_pcm_lib_period_bytes(substream);
 327
 328                        if (dma_desc->dma_addr - runtime->dma_addr >=
 329                                                        runtime->dma_bytes) {
 330                                dma_desc->dma_addr = runtime->dma_addr;
 331                                dma_desc->dma_area = runtime->dma_area;
 332                        }
 333
 334                        prtd->dma_addr = dma_desc->dma_addr;
 335                        regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
 336                                snd_pcm_lib_period_bytes(substream));
 337                        regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
 338                                        dma_desc->dma_addr);
 339                        availdepth--;
 340                }
 341
 342                snd_pcm_period_elapsed(substream);
 343
 344                /* Clear interrupt by writing 0 */
 345                regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
 346                                   I2S_TX_INTR_MASK, 0);
 347        }
 348
 349        return IRQ_HANDLED;
 350}
 351
 352static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
 353                struct snd_soc_pcm_runtime *rtd)
 354{
 355        struct snd_pcm *pcm = rtd->pcm;
 356        struct bcm_i2s_priv *i2s_priv;
 357        int ret;
 358
 359        i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
 360
 361        of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
 362
 363        ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
 364        if (ret)
 365                return ret;
 366
 367        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
 368                i2s_priv->play_substream =
 369                        pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 370        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
 371                i2s_priv->capture_substream =
 372                        pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 373
 374        return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
 375                                            pcm->card->dev,
 376                                            bcm63xx_pcm_hardware.buffer_bytes_max);
 377}
 378
 379static const struct snd_soc_component_driver bcm63xx_soc_platform = {
 380        .open = bcm63xx_pcm_open,
 381        .close = bcm63xx_pcm_close,
 382        .hw_params = bcm63xx_pcm_hw_params,
 383        .hw_free = bcm63xx_pcm_hw_free,
 384        .prepare = bcm63xx_pcm_prepare,
 385        .trigger = bcm63xx_pcm_trigger,
 386        .pointer = bcm63xx_pcm_pointer,
 387        .pcm_construct = bcm63xx_soc_pcm_new,
 388};
 389
 390int bcm63xx_soc_platform_probe(struct platform_device *pdev,
 391                               struct bcm_i2s_priv *i2s_priv)
 392{
 393        int ret;
 394
 395        ret = platform_get_irq(pdev, 0);
 396        if (ret < 0)
 397                return ret;
 398
 399        ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
 400                               irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
 401        if (ret) {
 402                dev_err(&pdev->dev,
 403                        "i2s_init: failed to request interrupt.ret=%d\n", ret);
 404                return ret;
 405        }
 406
 407        return devm_snd_soc_register_component(&pdev->dev,
 408                                        &bcm63xx_soc_platform, NULL, 0);
 409}
 410
 411int bcm63xx_soc_platform_remove(struct platform_device *pdev)
 412{
 413        return 0;
 414}
 415
 416MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
 417MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
 418MODULE_LICENSE("GPL v2");
 419