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/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
  17struct i2s_dma_desc {
  18        unsigned char *dma_area;
  19        dma_addr_t dma_addr;
  20        unsigned int dma_len;
  21};
  22
  23struct bcm63xx_runtime_data {
  24        int dma_len;
  25        dma_addr_t dma_addr;
  26        dma_addr_t dma_addr_next;
  27};
  28
  29static 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
  43static 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 = asoc_substream_to_rtd(substream);
  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(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
  59
  60        return 0;
  61}
  62
  63static 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 = asoc_substream_to_rtd(substream);
  68
  69        dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
  70        kfree(dma_desc);
  71        snd_pcm_set_runtime_buffer(substream, NULL);
  72
  73        return 0;
  74}
  75
  76static 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 = asoc_substream_to_rtd(substream);
  85        i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->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
 145static 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 = asoc_substream_to_rtd(substream);
 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(asoc_rtd_to_cpu(rtd, 0), 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(asoc_rtd_to_cpu(rtd, 0)->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
 177static snd_pcm_uframes_t
 178bcm63xx_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 (!prtd->dma_addr_next)
 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
 193static 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
 206static 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;
 236out:
 237        return ret;
 238}
 239
 240static 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
 250static 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 = asoc_substream_to_rtd(substream);
 271                prtd = runtime->private_data;
 272                dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), 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 = asoc_substream_to_rtd(substream);
 319                prtd = runtime->private_data;
 320                dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), 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
 365static 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
 384static 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(asoc_rtd_to_cpu(rtd, 0)->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
 418out:
 419        return ret;
 420}
 421
 422static 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
 442static 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
 455int 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
 478int bcm63xx_soc_platform_remove(struct platform_device *pdev)
 479{
 480        return 0;
 481}
 482
 483MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
 484MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
 485MODULE_LICENSE("GPL v2");
 486