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 = substream->private_data;
 211
 212        return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, 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 = substream->private_data;
 357        struct cygnus_aio_port *aio;
 358        u32 set_mask;
 359
 360        aio = cygnus_dai_get_dma_data(substream);
 361
 362        dev_dbg(rtd->cpu_dai->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_pcm_substream *substream, int cmd)
 380{
 381        int ret = 0;
 382
 383        switch (cmd) {
 384        case SNDRV_PCM_TRIGGER_START:
 385        case SNDRV_PCM_TRIGGER_RESUME:
 386                enable_intr(substream);
 387                break;
 388
 389        case SNDRV_PCM_TRIGGER_STOP:
 390        case SNDRV_PCM_TRIGGER_SUSPEND:
 391                disable_intr(substream);
 392                break;
 393        default:
 394                ret = -EINVAL;
 395        }
 396
 397        return ret;
 398}
 399
 400static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
 401{
 402        struct cygnus_aio_port *aio;
 403        struct ringbuf_regs *p_rbuf = NULL;
 404        u32 regval;
 405
 406        aio = cygnus_dai_get_dma_data(substream);
 407
 408        p_rbuf = get_ringbuf(substream);
 409
 410        /*
 411         * If free/full mark interrupt occurs, provide timestamp
 412         * to ALSA and update appropriate idx by period_bytes
 413         */
 414        snd_pcm_period_elapsed(substream);
 415
 416        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 417                /* Set the ring buffer to full */
 418                regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
 419                regval = regval ^ BIT(31);
 420                writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
 421        } else {
 422                /* Set the ring buffer to empty */
 423                regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
 424                writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
 425        }
 426}
 427
 428/*
 429 * ESR0/1/3 status  Description
 430 *  0x1 I2S0_out port caused interrupt
 431 *  0x2 I2S1_out port caused interrupt
 432 *  0x4 I2S2_out port caused interrupt
 433 *  0x8 SPDIF_out port caused interrupt
 434 */
 435static void handle_playback_irq(struct cygnus_audio *cygaud)
 436{
 437        void __iomem *audio_io;
 438        u32 port;
 439        u32 esr_status0, esr_status1, esr_status3;
 440
 441        audio_io = cygaud->audio;
 442
 443        /*
 444         * ESR status gets updates with/without interrupts enabled.
 445         * So, check the ESR mask, which provides interrupt enable/
 446         * disable status and use it to determine which ESR status
 447         * should be serviced.
 448         */
 449        esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
 450        esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
 451        esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
 452        esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
 453        esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
 454        esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
 455
 456        for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
 457                u32 esrmask = BIT(port);
 458
 459                /*
 460                 * Ringbuffer or FIFO underflow
 461                 * If we get this interrupt then, it is also true that we have
 462                 * not yet responded to the freemark interrupt.
 463                 * Log a debug message.  The freemark handler below will
 464                 * handle getting everything going again.
 465                 */
 466                if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
 467                        dev_dbg(cygaud->dev,
 468                                "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
 469                                esr_status0, esr_status1, esr_status3);
 470                }
 471
 472                /*
 473                 * Freemark is hit. This is the normal interrupt.
 474                 * In typical operation the read and write regs will be equal
 475                 */
 476                if (esrmask & esr_status3) {
 477                        struct snd_pcm_substream *playstr;
 478
 479                        playstr = cygaud->portinfo[port].play_stream;
 480                        cygnus_pcm_period_elapsed(playstr);
 481                }
 482        }
 483
 484        /* Clear ESR interrupt */
 485        writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
 486        writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
 487        writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
 488        /* Rearm freemark logic by writing 1 to the correct bit */
 489        writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
 490}
 491
 492/*
 493 * ESR2/4 status  Description
 494 *  0x1 I2S0_in port caused interrupt
 495 *  0x2 I2S1_in port caused interrupt
 496 *  0x4 I2S2_in port caused interrupt
 497 */
 498static void handle_capture_irq(struct cygnus_audio *cygaud)
 499{
 500        void __iomem *audio_io;
 501        u32 port;
 502        u32 esr_status2, esr_status4;
 503
 504        audio_io = cygaud->audio;
 505
 506        /*
 507         * ESR status gets updates with/without interrupts enabled.
 508         * So, check the ESR mask, which provides interrupt enable/
 509         * disable status and use it to determine which ESR status
 510         * should be serviced.
 511         */
 512        esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
 513        esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
 514        esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
 515        esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
 516
 517        for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
 518                u32 esrmask = BIT(port);
 519
 520                /*
 521                 * Ringbuffer or FIFO overflow
 522                 * If we get this interrupt then, it is also true that we have
 523                 * not yet responded to the fullmark interrupt.
 524                 * Log a debug message.  The fullmark handler below will
 525                 * handle getting everything going again.
 526                 */
 527                if (esrmask & esr_status2)
 528                        dev_dbg(cygaud->dev,
 529                                "Overflow: esr2=0x%x\n", esr_status2);
 530
 531                if (esrmask & esr_status4) {
 532                        struct snd_pcm_substream *capstr;
 533
 534                        capstr = cygaud->portinfo[port].capture_stream;
 535                        cygnus_pcm_period_elapsed(capstr);
 536                }
 537        }
 538
 539        writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
 540        writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
 541        /* Rearm fullmark logic by writing 1 to the correct bit */
 542        writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
 543}
 544
 545static irqreturn_t cygnus_dma_irq(int irq, void *data)
 546{
 547        u32 r5_status;
 548        struct cygnus_audio *cygaud = data;
 549
 550        /*
 551         * R5 status bits       Description
 552         *  0           ESR0 (playback FIFO interrupt)
 553         *  1           ESR1 (playback rbuf interrupt)
 554         *  2           ESR2 (capture rbuf interrupt)
 555         *  3           ESR3 (Freemark play. interrupt)
 556         *  4           ESR4 (Fullmark capt. interrupt)
 557         */
 558        r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
 559
 560        if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
 561                return IRQ_NONE;
 562
 563        /* If playback interrupt happened */
 564        if (ANY_PLAYBACK_IRQ & r5_status) {
 565                handle_playback_irq(cygaud);
 566                writel(ANY_PLAYBACK_IRQ & r5_status,
 567                        cygaud->audio + INTH_R5F_CLEAR_OFFSET);
 568        }
 569
 570        /* If  capture interrupt happened */
 571        if (ANY_CAPTURE_IRQ & r5_status) {
 572                handle_capture_irq(cygaud);
 573                writel(ANY_CAPTURE_IRQ & r5_status,
 574                        cygaud->audio + INTH_R5F_CLEAR_OFFSET);
 575        }
 576
 577        return IRQ_HANDLED;
 578}
 579
 580static int cygnus_pcm_open(struct snd_pcm_substream *substream)
 581{
 582        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 583        struct snd_pcm_runtime *runtime = substream->runtime;
 584        struct cygnus_aio_port *aio;
 585        int ret;
 586
 587        aio = cygnus_dai_get_dma_data(substream);
 588        if (!aio)
 589                return -ENODEV;
 590
 591        dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
 592
 593        snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
 594
 595        ret = snd_pcm_hw_constraint_step(runtime, 0,
 596                SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
 597        if (ret < 0)
 598                return ret;
 599
 600        ret = snd_pcm_hw_constraint_step(runtime, 0,
 601                SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
 602        if (ret < 0)
 603                return ret;
 604        /*
 605         * Keep track of which substream belongs to which port.
 606         * This info is needed by snd_pcm_period_elapsed() in irq_handler
 607         */
 608        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 609                aio->play_stream = substream;
 610        else
 611                aio->capture_stream = substream;
 612
 613        return 0;
 614}
 615
 616static int cygnus_pcm_close(struct snd_pcm_substream *substream)
 617{
 618        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 619        struct cygnus_aio_port *aio;
 620
 621        aio = cygnus_dai_get_dma_data(substream);
 622
 623        dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
 624
 625        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 626                aio->play_stream = NULL;
 627        else
 628                aio->capture_stream = NULL;
 629
 630        if (!aio->play_stream && !aio->capture_stream)
 631                dev_dbg(rtd->cpu_dai->dev, "freed  port %d\n", aio->portnum);
 632
 633        return 0;
 634}
 635
 636static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
 637        struct snd_pcm_hw_params *params)
 638{
 639        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 640        struct snd_pcm_runtime *runtime = substream->runtime;
 641        struct cygnus_aio_port *aio;
 642        int ret = 0;
 643
 644        aio = cygnus_dai_get_dma_data(substream);
 645        dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
 646
 647        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 648        runtime->dma_bytes = params_buffer_bytes(params);
 649
 650        return ret;
 651}
 652
 653static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
 654{
 655        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 656        struct cygnus_aio_port *aio;
 657
 658        aio = cygnus_dai_get_dma_data(substream);
 659        dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
 660
 661        snd_pcm_set_runtime_buffer(substream, NULL);
 662        return 0;
 663}
 664
 665static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
 666{
 667        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 668        struct snd_pcm_runtime *runtime = substream->runtime;
 669        struct cygnus_aio_port *aio;
 670        unsigned long bufsize, periodsize;
 671        int ret = 0;
 672        bool is_play;
 673        u32 start;
 674        struct ringbuf_regs *p_rbuf = NULL;
 675
 676        aio = cygnus_dai_get_dma_data(substream);
 677        dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
 678
 679        bufsize = snd_pcm_lib_buffer_bytes(substream);
 680        periodsize = snd_pcm_lib_period_bytes(substream);
 681
 682        dev_dbg(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n",
 683                        __func__, bufsize, periodsize);
 684
 685        configure_ringbuf_regs(substream);
 686
 687        p_rbuf = get_ringbuf(substream);
 688
 689        start = runtime->dma_addr;
 690
 691        is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
 692
 693        ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
 694                                periodsize, bufsize);
 695
 696        return ret;
 697}
 698
 699static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
 700{
 701        struct cygnus_aio_port *aio;
 702        unsigned int res = 0, cur = 0, base = 0;
 703        struct ringbuf_regs *p_rbuf = NULL;
 704
 705        aio = cygnus_dai_get_dma_data(substream);
 706
 707        /*
 708         * Get the offset of the current read (for playack) or write
 709         * index (for capture).  Report this value back to the asoc framework.
 710         */
 711        p_rbuf = get_ringbuf(substream);
 712        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 713                cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
 714        else
 715                cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
 716
 717        base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
 718
 719        /*
 720         * Mask off the MSB of the rdaddr,wraddr and baseaddr
 721         * since MSB is not part of the address
 722         */
 723        res = (cur & 0x7fffffff) - (base & 0x7fffffff);
 724
 725        return bytes_to_frames(substream->runtime, res);
 726}
 727
 728static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 729{
 730        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 731        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 732        struct snd_dma_buffer *buf = &substream->dma_buffer;
 733        size_t size;
 734
 735        size = cygnus_pcm_hw.buffer_bytes_max;
 736
 737        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 738        buf->dev.dev = pcm->card->dev;
 739        buf->private_data = NULL;
 740        buf->area = dma_alloc_coherent(pcm->card->dev, size,
 741                        &buf->addr, GFP_KERNEL);
 742
 743        dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
 744                                __func__, size, buf->area);
 745
 746        if (!buf->area) {
 747                dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
 748                return -ENOMEM;
 749        }
 750        buf->bytes = size;
 751
 752        return 0;
 753}
 754
 755
 756static const struct snd_pcm_ops cygnus_pcm_ops = {
 757        .open           = cygnus_pcm_open,
 758        .close          = cygnus_pcm_close,
 759        .ioctl          = snd_pcm_lib_ioctl,
 760        .hw_params      = cygnus_pcm_hw_params,
 761        .hw_free        = cygnus_pcm_hw_free,
 762        .prepare        = cygnus_pcm_prepare,
 763        .trigger        = cygnus_pcm_trigger,
 764        .pointer        = cygnus_pcm_pointer,
 765};
 766
 767static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
 768{
 769        struct snd_pcm_substream *substream;
 770        struct snd_dma_buffer *buf;
 771
 772        substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 773        if (substream) {
 774                buf = &substream->dma_buffer;
 775                if (buf->area) {
 776                        dma_free_coherent(pcm->card->dev, buf->bytes,
 777                                buf->area, buf->addr);
 778                        buf->area = NULL;
 779                }
 780        }
 781
 782        substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 783        if (substream) {
 784                buf = &substream->dma_buffer;
 785                if (buf->area) {
 786                        dma_free_coherent(pcm->card->dev, buf->bytes,
 787                                buf->area, buf->addr);
 788                        buf->area = NULL;
 789                }
 790        }
 791}
 792
 793static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
 794{
 795        struct snd_card *card = rtd->card->snd_card;
 796        struct snd_pcm *pcm = rtd->pcm;
 797        int ret;
 798
 799        if (!card->dev->dma_mask)
 800                card->dev->dma_mask = &cygnus_dma_dmamask;
 801        if (!card->dev->coherent_dma_mask)
 802                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 803
 804        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 805                ret = cygnus_pcm_preallocate_dma_buffer(pcm,
 806                                SNDRV_PCM_STREAM_PLAYBACK);
 807                if (ret)
 808                        return ret;
 809        }
 810
 811        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 812                ret = cygnus_pcm_preallocate_dma_buffer(pcm,
 813                                SNDRV_PCM_STREAM_CAPTURE);
 814                if (ret) {
 815                        cygnus_dma_free_dma_buffers(pcm);
 816                        return ret;
 817                }
 818        }
 819
 820        return 0;
 821}
 822
 823static struct snd_soc_component_driver cygnus_soc_platform = {
 824        .ops            = &cygnus_pcm_ops,
 825        .pcm_new        = cygnus_dma_new,
 826        .pcm_free       = cygnus_dma_free_dma_buffers,
 827};
 828
 829int cygnus_soc_platform_register(struct device *dev,
 830                                 struct cygnus_audio *cygaud)
 831{
 832        int rc = 0;
 833
 834        dev_dbg(dev, "%s Enter\n", __func__);
 835
 836        rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
 837                                IRQF_SHARED, "cygnus-audio", cygaud);
 838        if (rc) {
 839                dev_err(dev, "%s request_irq error %d\n", __func__, rc);
 840                return rc;
 841        }
 842
 843        rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
 844                                             NULL, 0);
 845        if (rc) {
 846                dev_err(dev, "%s failed\n", __func__);
 847                return rc;
 848        }
 849
 850        return 0;
 851}
 852
 853int cygnus_soc_platform_unregister(struct device *dev)
 854{
 855        return 0;
 856}
 857
 858MODULE_LICENSE("GPL v2");
 859MODULE_AUTHOR("Broadcom");
 860MODULE_DESCRIPTION("Cygnus ASoC PCM module");
 861