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