linux/sound/soc/bcm/cygnus-pcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2// Copyright (C) 2014-2015 Broadcom Corporation
   3#include <linux/debugfs.h>
   4#include <linux/dma-mapping.h>
   5#include <linux/init.h>
   6#include <linux/io.h>
   7#include <linux/module.h>
   8#include <linux/slab.h>
   9#include <linux/timer.h>
  10#include <sound/core.h>
  11#include <sound/pcm.h>
  12#include <sound/pcm_params.h>
  13#include <sound/soc.h>
  14#include <sound/soc-dai.h>
  15
  16#include "cygnus-ssp.h"
  17
  18/* Register offset needed for ASoC PCM module */
  19
  20#define INTH_R5F_STATUS_OFFSET     0x040
  21#define INTH_R5F_CLEAR_OFFSET      0x048
  22#define INTH_R5F_MASK_SET_OFFSET   0x050
  23#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
  24
  25#define BF_REARM_FREE_MARK_OFFSET 0x344
  26#define BF_REARM_FULL_MARK_OFFSET 0x348
  27
  28/* Ring Buffer Ctrl Regs --- Start */
  29/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
  30#define SRC_RBUF_0_RDADDR_OFFSET 0x500
  31#define SRC_RBUF_1_RDADDR_OFFSET 0x518
  32#define SRC_RBUF_2_RDADDR_OFFSET 0x530
  33#define SRC_RBUF_3_RDADDR_OFFSET 0x548
  34#define SRC_RBUF_4_RDADDR_OFFSET 0x560
  35#define SRC_RBUF_5_RDADDR_OFFSET 0x578
  36#define SRC_RBUF_6_RDADDR_OFFSET 0x590
  37
  38/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
  39#define SRC_RBUF_0_WRADDR_OFFSET 0x504
  40#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
  41#define SRC_RBUF_2_WRADDR_OFFSET 0x534
  42#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
  43#define SRC_RBUF_4_WRADDR_OFFSET 0x564
  44#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
  45#define SRC_RBUF_6_WRADDR_OFFSET 0x594
  46
  47/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
  48#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
  49#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
  50#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
  51#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
  52#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
  53#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
  54#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
  55
  56/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
  57#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
  58#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
  59#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
  60#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
  61#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
  62#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
  63#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
  64
  65/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
  66#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
  67#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
  68#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
  69#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
  70#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
  71#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
  72#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
  73
  74/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
  75#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
  76#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
  77#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
  78#define DST_RBUF_3_RDADDR_OFFSET 0x608
  79#define DST_RBUF_4_RDADDR_OFFSET 0x620
  80#define DST_RBUF_5_RDADDR_OFFSET 0x638
  81
  82/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
  83#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
  84#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
  85#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
  86#define DST_RBUF_3_WRADDR_OFFSET 0x60c
  87#define DST_RBUF_4_WRADDR_OFFSET 0x624
  88#define DST_RBUF_5_WRADDR_OFFSET 0x63c
  89
  90/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
  91#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
  92#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
  93#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
  94#define DST_RBUF_3_BASEADDR_OFFSET 0x610
  95#define DST_RBUF_4_BASEADDR_OFFSET 0x628
  96#define DST_RBUF_5_BASEADDR_OFFSET 0x640
  97
  98/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
  99#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
 100#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
 101#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
 102#define DST_RBUF_3_ENDADDR_OFFSET 0x614
 103#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
 104#define DST_RBUF_5_ENDADDR_OFFSET 0x644
 105
 106/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
 107#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
 108#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
 109#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
 110#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
 111#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
 112#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
 113/* Ring Buffer Ctrl Regs --- End */
 114
 115/* Error Status Regs --- Start */
 116/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
 117#define ESR0_STATUS_OFFSET 0x900
 118#define ESR1_STATUS_OFFSET 0x918
 119#define ESR2_STATUS_OFFSET 0x930
 120#define ESR3_STATUS_OFFSET 0x948
 121#define ESR4_STATUS_OFFSET 0x960
 122
 123/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
 124#define ESR0_STATUS_CLR_OFFSET 0x908
 125#define ESR1_STATUS_CLR_OFFSET 0x920
 126#define ESR2_STATUS_CLR_OFFSET 0x938
 127#define ESR3_STATUS_CLR_OFFSET 0x950
 128#define ESR4_STATUS_CLR_OFFSET 0x968
 129
 130/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
 131#define ESR0_MASK_STATUS_OFFSET 0x90c
 132#define ESR1_MASK_STATUS_OFFSET 0x924
 133#define ESR2_MASK_STATUS_OFFSET 0x93c
 134#define ESR3_MASK_STATUS_OFFSET 0x954
 135#define ESR4_MASK_STATUS_OFFSET 0x96c
 136
 137/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
 138#define ESR0_MASK_SET_OFFSET 0x910
 139#define ESR1_MASK_SET_OFFSET 0x928
 140#define ESR2_MASK_SET_OFFSET 0x940
 141#define ESR3_MASK_SET_OFFSET 0x958
 142#define ESR4_MASK_SET_OFFSET 0x970
 143
 144/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
 145#define ESR0_MASK_CLR_OFFSET 0x914
 146#define ESR1_MASK_CLR_OFFSET 0x92c
 147#define ESR2_MASK_CLR_OFFSET 0x944
 148#define ESR3_MASK_CLR_OFFSET 0x95c
 149#define ESR4_MASK_CLR_OFFSET 0x974
 150/* Error Status Regs --- End */
 151
 152#define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
 153#define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
 154#define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
 155#define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
 156#define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
 157
 158
 159/* Mask for R5F register.  Set all relevant interrupt for playback handler */
 160#define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
 161                           BIT(R5F_ESR1_SHIFT) | \
 162                           BIT(R5F_ESR3_SHIFT))
 163
 164/* Mask for R5F register.  Set all relevant interrupt for capture handler */
 165#define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
 166
 167/*
 168 * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
 169 * This number should be a multiple of 256. Minimum value is 256
 170 */
 171#define PERIOD_BYTES_MIN 0x100
 172
 173static const struct snd_pcm_hardware cygnus_pcm_hw = {
 174        .info = SNDRV_PCM_INFO_MMAP |
 175                        SNDRV_PCM_INFO_MMAP_VALID |
 176                        SNDRV_PCM_INFO_INTERLEAVED,
 177        .formats = SNDRV_PCM_FMTBIT_S16_LE |
 178                        SNDRV_PCM_FMTBIT_S32_LE,
 179
 180        /* A period is basically an interrupt */
 181        .period_bytes_min = PERIOD_BYTES_MIN,
 182        .period_bytes_max = 0x10000,
 183
 184        /* period_min/max gives range of approx interrupts per buffer */
 185        .periods_min = 2,
 186        .periods_max = 8,
 187
 188        /*
 189         * maximum buffer size in bytes = period_bytes_max * periods_max
 190         * We allocate this amount of data for each enabled channel
 191         */
 192        .buffer_bytes_max = 4 * 0x8000,
 193};
 194
 195static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
 196
 197static struct cygnus_aio_port *cygnus_dai_get_dma_data(
 198                                struct snd_pcm_substream *substream)
 199{
 200        struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
 201
 202        return snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(soc_runtime, 0), substream);
 203}
 204
 205static void ringbuf_set_initial(void __iomem *audio_io,
 206                struct ringbuf_regs *p_rbuf,
 207                bool is_playback,
 208                u32 start,
 209                u32 periodsize,
 210                u32 bufsize)
 211{
 212        u32 initial_rd;
 213        u32 initial_wr;
 214        u32 end;
 215        u32 fmark_val; /* free or full mark */
 216
 217        p_rbuf->period_bytes = periodsize;
 218        p_rbuf->buf_size = bufsize;
 219
 220        if (is_playback) {
 221                /* Set the pointers to indicate full (flip uppermost bit) */
 222                initial_rd = start;
 223                initial_wr = initial_rd ^ BIT(31);
 224        } else {
 225                /* Set the pointers to indicate empty */
 226                initial_wr = start;
 227                initial_rd = initial_wr;
 228        }
 229
 230        end = start + bufsize - 1;
 231
 232        /*
 233         * The interrupt will fire when free/full mark is *exceeded*
 234         * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
 235         * to be PERIOD_BYTES_MIN less than the period size.
 236         */
 237        fmark_val = periodsize - PERIOD_BYTES_MIN;
 238
 239        writel(start, audio_io + p_rbuf->baseaddr);
 240        writel(end, audio_io + p_rbuf->endaddr);
 241        writel(fmark_val, audio_io + p_rbuf->fmark);
 242        writel(initial_rd, audio_io + p_rbuf->rdaddr);
 243        writel(initial_wr, audio_io + p_rbuf->wraddr);
 244}
 245
 246static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
 247{
 248        struct cygnus_aio_port *aio;
 249        struct ringbuf_regs *p_rbuf;
 250        int status = 0;
 251
 252        aio = cygnus_dai_get_dma_data(substream);
 253
 254        /* Map the ssp portnum to a set of ring buffers. */
 255        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 256                p_rbuf = &aio->play_rb_regs;
 257
 258                switch (aio->portnum) {
 259                case 0:
 260                        *p_rbuf = RINGBUF_REG_PLAYBACK(0);
 261                        break;
 262                case 1:
 263                        *p_rbuf = RINGBUF_REG_PLAYBACK(2);
 264                        break;
 265                case 2:
 266                        *p_rbuf = RINGBUF_REG_PLAYBACK(4);
 267                        break;
 268                case 3: /* SPDIF */
 269                        *p_rbuf = RINGBUF_REG_PLAYBACK(6);
 270                        break;
 271                default:
 272                        status = -EINVAL;
 273                }
 274        } else {
 275                p_rbuf = &aio->capture_rb_regs;
 276
 277                switch (aio->portnum) {
 278                case 0:
 279                        *p_rbuf = RINGBUF_REG_CAPTURE(0);
 280                        break;
 281                case 1:
 282                        *p_rbuf = RINGBUF_REG_CAPTURE(2);
 283                        break;
 284                case 2:
 285                        *p_rbuf = RINGBUF_REG_CAPTURE(4);
 286                        break;
 287                default:
 288                        status = -EINVAL;
 289                }
 290        }
 291
 292        return status;
 293}
 294
 295static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
 296{
 297        struct cygnus_aio_port *aio;
 298        struct ringbuf_regs *p_rbuf = NULL;
 299
 300        aio = cygnus_dai_get_dma_data(substream);
 301
 302        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 303                p_rbuf = &aio->play_rb_regs;
 304        else
 305                p_rbuf = &aio->capture_rb_regs;
 306
 307        return p_rbuf;
 308}
 309
 310static void enable_intr(struct snd_pcm_substream *substream)
 311{
 312        struct cygnus_aio_port *aio;
 313        u32 clear_mask;
 314
 315        aio = cygnus_dai_get_dma_data(substream);
 316
 317        /* The port number maps to the bit position to be cleared */
 318        clear_mask = BIT(aio->portnum);
 319
 320        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 321                /* Clear interrupt status before enabling them */
 322                writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
 323                writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
 324                writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
 325                /* Unmask the interrupts of the given port*/
 326                writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
 327                writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
 328                writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
 329
 330                writel(ANY_PLAYBACK_IRQ,
 331                        aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
 332        } else {
 333                writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
 334                writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
 335                writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
 336                writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
 337
 338                writel(ANY_CAPTURE_IRQ,
 339                        aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
 340        }
 341
 342}
 343
 344static void disable_intr(struct snd_pcm_substream *substream)
 345{
 346        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 347        struct cygnus_aio_port *aio;
 348        u32 set_mask;
 349
 350        aio = cygnus_dai_get_dma_data(substream);
 351
 352        dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
 353
 354        /* The port number maps to the bit position to be set */
 355        set_mask = BIT(aio->portnum);
 356
 357        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 358                /* Mask the interrupts of the given port*/
 359                writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
 360                writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
 361                writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
 362        } else {
 363                writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
 364                writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
 365        }
 366
 367}
 368
 369static int cygnus_pcm_trigger(struct snd_soc_component *component,
 370                              struct snd_pcm_substream *substream, int cmd)
 371{
 372        int ret = 0;
 373
 374        switch (cmd) {
 375        case SNDRV_PCM_TRIGGER_START:
 376        case SNDRV_PCM_TRIGGER_RESUME:
 377                enable_intr(substream);
 378                break;
 379
 380        case SNDRV_PCM_TRIGGER_STOP:
 381        case SNDRV_PCM_TRIGGER_SUSPEND:
 382                disable_intr(substream);
 383                break;
 384        default:
 385                ret = -EINVAL;
 386        }
 387
 388        return ret;
 389}
 390
 391static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
 392{
 393        struct cygnus_aio_port *aio;
 394        struct ringbuf_regs *p_rbuf = NULL;
 395        u32 regval;
 396
 397        aio = cygnus_dai_get_dma_data(substream);
 398
 399        p_rbuf = get_ringbuf(substream);
 400
 401        /*
 402         * If free/full mark interrupt occurs, provide timestamp
 403         * to ALSA and update appropriate idx by period_bytes
 404         */
 405        snd_pcm_period_elapsed(substream);
 406
 407        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 408                /* Set the ring buffer to full */
 409                regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
 410                regval = regval ^ BIT(31);
 411                writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
 412        } else {
 413                /* Set the ring buffer to empty */
 414                regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
 415                writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
 416        }
 417}
 418
 419/*
 420 * ESR0/1/3 status  Description
 421 *  0x1 I2S0_out port caused interrupt
 422 *  0x2 I2S1_out port caused interrupt
 423 *  0x4 I2S2_out port caused interrupt
 424 *  0x8 SPDIF_out port caused interrupt
 425 */
 426static void handle_playback_irq(struct cygnus_audio *cygaud)
 427{
 428        void __iomem *audio_io;
 429        u32 port;
 430        u32 esr_status0, esr_status1, esr_status3;
 431
 432        audio_io = cygaud->audio;
 433
 434        /*
 435         * ESR status gets updates with/without interrupts enabled.
 436         * So, check the ESR mask, which provides interrupt enable/
 437         * disable status and use it to determine which ESR status
 438         * should be serviced.
 439         */
 440        esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
 441        esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
 442        esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
 443        esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
 444        esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
 445        esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
 446
 447        for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
 448                u32 esrmask = BIT(port);
 449
 450                /*
 451                 * Ringbuffer or FIFO underflow
 452                 * If we get this interrupt then, it is also true that we have
 453                 * not yet responded to the freemark interrupt.
 454                 * Log a debug message.  The freemark handler below will
 455                 * handle getting everything going again.
 456                 */
 457                if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
 458                        dev_dbg(cygaud->dev,
 459                                "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
 460                                esr_status0, esr_status1, esr_status3);
 461                }
 462
 463                /*
 464                 * Freemark is hit. This is the normal interrupt.
 465                 * In typical operation the read and write regs will be equal
 466                 */
 467                if (esrmask & esr_status3) {
 468                        struct snd_pcm_substream *playstr;
 469
 470                        playstr = cygaud->portinfo[port].play_stream;
 471                        cygnus_pcm_period_elapsed(playstr);
 472                }
 473        }
 474
 475        /* Clear ESR interrupt */
 476        writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
 477        writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
 478        writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
 479        /* Rearm freemark logic by writing 1 to the correct bit */
 480        writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
 481}
 482
 483/*
 484 * ESR2/4 status  Description
 485 *  0x1 I2S0_in port caused interrupt
 486 *  0x2 I2S1_in port caused interrupt
 487 *  0x4 I2S2_in port caused interrupt
 488 */
 489static void handle_capture_irq(struct cygnus_audio *cygaud)
 490{
 491        void __iomem *audio_io;
 492        u32 port;
 493        u32 esr_status2, esr_status4;
 494
 495        audio_io = cygaud->audio;
 496
 497        /*
 498         * ESR status gets updates with/without interrupts enabled.
 499         * So, check the ESR mask, which provides interrupt enable/
 500         * disable status and use it to determine which ESR status
 501         * should be serviced.
 502         */
 503        esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
 504        esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
 505        esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
 506        esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
 507
 508        for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
 509                u32 esrmask = BIT(port);
 510
 511                /*
 512                 * Ringbuffer or FIFO overflow
 513                 * If we get this interrupt then, it is also true that we have
 514                 * not yet responded to the fullmark interrupt.
 515                 * Log a debug message.  The fullmark handler below will
 516                 * handle getting everything going again.
 517                 */
 518                if (esrmask & esr_status2)
 519                        dev_dbg(cygaud->dev,
 520                                "Overflow: esr2=0x%x\n", esr_status2);
 521
 522                if (esrmask & esr_status4) {
 523                        struct snd_pcm_substream *capstr;
 524
 525                        capstr = cygaud->portinfo[port].capture_stream;
 526                        cygnus_pcm_period_elapsed(capstr);
 527                }
 528        }
 529
 530        writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
 531        writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
 532        /* Rearm fullmark logic by writing 1 to the correct bit */
 533        writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
 534}
 535
 536static irqreturn_t cygnus_dma_irq(int irq, void *data)
 537{
 538        u32 r5_status;
 539        struct cygnus_audio *cygaud = data;
 540
 541        /*
 542         * R5 status bits       Description
 543         *  0           ESR0 (playback FIFO interrupt)
 544         *  1           ESR1 (playback rbuf interrupt)
 545         *  2           ESR2 (capture rbuf interrupt)
 546         *  3           ESR3 (Freemark play. interrupt)
 547         *  4           ESR4 (Fullmark capt. interrupt)
 548         */
 549        r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
 550
 551        if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
 552                return IRQ_NONE;
 553
 554        /* If playback interrupt happened */
 555        if (ANY_PLAYBACK_IRQ & r5_status) {
 556                handle_playback_irq(cygaud);
 557                writel(ANY_PLAYBACK_IRQ & r5_status,
 558                        cygaud->audio + INTH_R5F_CLEAR_OFFSET);
 559        }
 560
 561        /* If  capture interrupt happened */
 562        if (ANY_CAPTURE_IRQ & r5_status) {
 563                handle_capture_irq(cygaud);
 564                writel(ANY_CAPTURE_IRQ & r5_status,
 565                        cygaud->audio + INTH_R5F_CLEAR_OFFSET);
 566        }
 567
 568        return IRQ_HANDLED;
 569}
 570
 571static int cygnus_pcm_open(struct snd_soc_component *component,
 572                           struct snd_pcm_substream *substream)
 573{
 574        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 575        struct snd_pcm_runtime *runtime = substream->runtime;
 576        struct cygnus_aio_port *aio;
 577        int ret;
 578
 579        aio = cygnus_dai_get_dma_data(substream);
 580        if (!aio)
 581                return -ENODEV;
 582
 583        dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
 584
 585        snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
 586
 587        ret = snd_pcm_hw_constraint_step(runtime, 0,
 588                SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
 589        if (ret < 0)
 590                return ret;
 591
 592        ret = snd_pcm_hw_constraint_step(runtime, 0,
 593                SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
 594        if (ret < 0)
 595                return ret;
 596        /*
 597         * Keep track of which substream belongs to which port.
 598         * This info is needed by snd_pcm_period_elapsed() in irq_handler
 599         */
 600        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 601                aio->play_stream = substream;
 602        else
 603                aio->capture_stream = substream;
 604
 605        return 0;
 606}
 607
 608static int cygnus_pcm_close(struct snd_soc_component *component,
 609                            struct snd_pcm_substream *substream)
 610{
 611        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 612        struct cygnus_aio_port *aio;
 613
 614        aio = cygnus_dai_get_dma_data(substream);
 615
 616        dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
 617
 618        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 619                aio->play_stream = NULL;
 620        else
 621                aio->capture_stream = NULL;
 622
 623        if (!aio->play_stream && !aio->capture_stream)
 624                dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "freed  port %d\n", aio->portnum);
 625
 626        return 0;
 627}
 628
 629static int cygnus_pcm_prepare(struct snd_soc_component *component,
 630                              struct snd_pcm_substream *substream)
 631{
 632        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 633        struct snd_pcm_runtime *runtime = substream->runtime;
 634        struct cygnus_aio_port *aio;
 635        unsigned long bufsize, periodsize;
 636        bool is_play;
 637        u32 start;
 638        struct ringbuf_regs *p_rbuf = NULL;
 639
 640        aio = cygnus_dai_get_dma_data(substream);
 641        dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
 642
 643        bufsize = snd_pcm_lib_buffer_bytes(substream);
 644        periodsize = snd_pcm_lib_period_bytes(substream);
 645
 646        dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
 647                        __func__, bufsize, periodsize);
 648
 649        configure_ringbuf_regs(substream);
 650
 651        p_rbuf = get_ringbuf(substream);
 652
 653        start = runtime->dma_addr;
 654
 655        is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
 656
 657        ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
 658                                periodsize, bufsize);
 659
 660        return 0;
 661}
 662
 663static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
 664                                            struct snd_pcm_substream *substream)
 665{
 666        struct cygnus_aio_port *aio;
 667        unsigned int res = 0, cur = 0, base = 0;
 668        struct ringbuf_regs *p_rbuf = NULL;
 669
 670        aio = cygnus_dai_get_dma_data(substream);
 671
 672        /*
 673         * Get the offset of the current read (for playack) or write
 674         * index (for capture).  Report this value back to the asoc framework.
 675         */
 676        p_rbuf = get_ringbuf(substream);
 677        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 678                cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
 679        else
 680                cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
 681
 682        base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
 683
 684        /*
 685         * Mask off the MSB of the rdaddr,wraddr and baseaddr
 686         * since MSB is not part of the address
 687         */
 688        res = (cur & 0x7fffffff) - (base & 0x7fffffff);
 689
 690        return bytes_to_frames(substream->runtime, res);
 691}
 692
 693static int cygnus_dma_new(struct snd_soc_component *component,
 694                          struct snd_soc_pcm_runtime *rtd)
 695{
 696        size_t size = cygnus_pcm_hw.buffer_bytes_max;
 697        struct snd_card *card = rtd->card->snd_card;
 698
 699        if (!card->dev->dma_mask)
 700                card->dev->dma_mask = &cygnus_dma_dmamask;
 701        if (!card->dev->coherent_dma_mask)
 702                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 703
 704        snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
 705                                       card->dev, size, size);
 706
 707        return 0;
 708}
 709
 710static const struct snd_soc_component_driver cygnus_soc_platform = {
 711        .open           = cygnus_pcm_open,
 712        .close          = cygnus_pcm_close,
 713        .prepare        = cygnus_pcm_prepare,
 714        .trigger        = cygnus_pcm_trigger,
 715        .pointer        = cygnus_pcm_pointer,
 716        .pcm_construct  = cygnus_dma_new,
 717};
 718
 719int cygnus_soc_platform_register(struct device *dev,
 720                                 struct cygnus_audio *cygaud)
 721{
 722        int rc;
 723
 724        dev_dbg(dev, "%s Enter\n", __func__);
 725
 726        rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
 727                                IRQF_SHARED, "cygnus-audio", cygaud);
 728        if (rc) {
 729                dev_err(dev, "%s request_irq error %d\n", __func__, rc);
 730                return rc;
 731        }
 732
 733        rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
 734                                             NULL, 0);
 735        if (rc) {
 736                dev_err(dev, "%s failed\n", __func__);
 737                return rc;
 738        }
 739
 740        return 0;
 741}
 742
 743int cygnus_soc_platform_unregister(struct device *dev)
 744{
 745        return 0;
 746}
 747
 748MODULE_LICENSE("GPL v2");
 749MODULE_AUTHOR("Broadcom");
 750MODULE_DESCRIPTION("Cygnus ASoC PCM module");
 751