162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2014-2015 Broadcom Corporation 362306a36Sopenharmony_ci#include <linux/debugfs.h> 462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/timer.h> 1062306a36Sopenharmony_ci#include <sound/core.h> 1162306a36Sopenharmony_ci#include <sound/pcm.h> 1262306a36Sopenharmony_ci#include <sound/pcm_params.h> 1362306a36Sopenharmony_ci#include <sound/soc.h> 1462306a36Sopenharmony_ci#include <sound/soc-dai.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cygnus-ssp.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Register offset needed for ASoC PCM module */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define INTH_R5F_STATUS_OFFSET 0x040 2162306a36Sopenharmony_ci#define INTH_R5F_CLEAR_OFFSET 0x048 2262306a36Sopenharmony_ci#define INTH_R5F_MASK_SET_OFFSET 0x050 2362306a36Sopenharmony_ci#define INTH_R5F_MASK_CLEAR_OFFSET 0x054 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define BF_REARM_FREE_MARK_OFFSET 0x344 2662306a36Sopenharmony_ci#define BF_REARM_FULL_MARK_OFFSET 0x348 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Ring Buffer Ctrl Regs --- Start */ 2962306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */ 3062306a36Sopenharmony_ci#define SRC_RBUF_0_RDADDR_OFFSET 0x500 3162306a36Sopenharmony_ci#define SRC_RBUF_1_RDADDR_OFFSET 0x518 3262306a36Sopenharmony_ci#define SRC_RBUF_2_RDADDR_OFFSET 0x530 3362306a36Sopenharmony_ci#define SRC_RBUF_3_RDADDR_OFFSET 0x548 3462306a36Sopenharmony_ci#define SRC_RBUF_4_RDADDR_OFFSET 0x560 3562306a36Sopenharmony_ci#define SRC_RBUF_5_RDADDR_OFFSET 0x578 3662306a36Sopenharmony_ci#define SRC_RBUF_6_RDADDR_OFFSET 0x590 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */ 3962306a36Sopenharmony_ci#define SRC_RBUF_0_WRADDR_OFFSET 0x504 4062306a36Sopenharmony_ci#define SRC_RBUF_1_WRADDR_OFFSET 0x51c 4162306a36Sopenharmony_ci#define SRC_RBUF_2_WRADDR_OFFSET 0x534 4262306a36Sopenharmony_ci#define SRC_RBUF_3_WRADDR_OFFSET 0x54c 4362306a36Sopenharmony_ci#define SRC_RBUF_4_WRADDR_OFFSET 0x564 4462306a36Sopenharmony_ci#define SRC_RBUF_5_WRADDR_OFFSET 0x57c 4562306a36Sopenharmony_ci#define SRC_RBUF_6_WRADDR_OFFSET 0x594 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */ 4862306a36Sopenharmony_ci#define SRC_RBUF_0_BASEADDR_OFFSET 0x508 4962306a36Sopenharmony_ci#define SRC_RBUF_1_BASEADDR_OFFSET 0x520 5062306a36Sopenharmony_ci#define SRC_RBUF_2_BASEADDR_OFFSET 0x538 5162306a36Sopenharmony_ci#define SRC_RBUF_3_BASEADDR_OFFSET 0x550 5262306a36Sopenharmony_ci#define SRC_RBUF_4_BASEADDR_OFFSET 0x568 5362306a36Sopenharmony_ci#define SRC_RBUF_5_BASEADDR_OFFSET 0x580 5462306a36Sopenharmony_ci#define SRC_RBUF_6_BASEADDR_OFFSET 0x598 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */ 5762306a36Sopenharmony_ci#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c 5862306a36Sopenharmony_ci#define SRC_RBUF_1_ENDADDR_OFFSET 0x524 5962306a36Sopenharmony_ci#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c 6062306a36Sopenharmony_ci#define SRC_RBUF_3_ENDADDR_OFFSET 0x554 6162306a36Sopenharmony_ci#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c 6262306a36Sopenharmony_ci#define SRC_RBUF_5_ENDADDR_OFFSET 0x584 6362306a36Sopenharmony_ci#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */ 6662306a36Sopenharmony_ci#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510 6762306a36Sopenharmony_ci#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528 6862306a36Sopenharmony_ci#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540 6962306a36Sopenharmony_ci#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558 7062306a36Sopenharmony_ci#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570 7162306a36Sopenharmony_ci#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588 7262306a36Sopenharmony_ci#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */ 7562306a36Sopenharmony_ci#define DST_RBUF_0_RDADDR_OFFSET 0x5c0 7662306a36Sopenharmony_ci#define DST_RBUF_1_RDADDR_OFFSET 0x5d8 7762306a36Sopenharmony_ci#define DST_RBUF_2_RDADDR_OFFSET 0x5f0 7862306a36Sopenharmony_ci#define DST_RBUF_3_RDADDR_OFFSET 0x608 7962306a36Sopenharmony_ci#define DST_RBUF_4_RDADDR_OFFSET 0x620 8062306a36Sopenharmony_ci#define DST_RBUF_5_RDADDR_OFFSET 0x638 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */ 8362306a36Sopenharmony_ci#define DST_RBUF_0_WRADDR_OFFSET 0x5c4 8462306a36Sopenharmony_ci#define DST_RBUF_1_WRADDR_OFFSET 0x5dc 8562306a36Sopenharmony_ci#define DST_RBUF_2_WRADDR_OFFSET 0x5f4 8662306a36Sopenharmony_ci#define DST_RBUF_3_WRADDR_OFFSET 0x60c 8762306a36Sopenharmony_ci#define DST_RBUF_4_WRADDR_OFFSET 0x624 8862306a36Sopenharmony_ci#define DST_RBUF_5_WRADDR_OFFSET 0x63c 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */ 9162306a36Sopenharmony_ci#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8 9262306a36Sopenharmony_ci#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0 9362306a36Sopenharmony_ci#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8 9462306a36Sopenharmony_ci#define DST_RBUF_3_BASEADDR_OFFSET 0x610 9562306a36Sopenharmony_ci#define DST_RBUF_4_BASEADDR_OFFSET 0x628 9662306a36Sopenharmony_ci#define DST_RBUF_5_BASEADDR_OFFSET 0x640 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */ 9962306a36Sopenharmony_ci#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc 10062306a36Sopenharmony_ci#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4 10162306a36Sopenharmony_ci#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc 10262306a36Sopenharmony_ci#define DST_RBUF_3_ENDADDR_OFFSET 0x614 10362306a36Sopenharmony_ci#define DST_RBUF_4_ENDADDR_OFFSET 0x62c 10462306a36Sopenharmony_ci#define DST_RBUF_5_ENDADDR_OFFSET 0x644 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */ 10762306a36Sopenharmony_ci#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0 10862306a36Sopenharmony_ci#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8 10962306a36Sopenharmony_ci#define DST_RBUF_2_FULL_MARK_OFFSET 0x600 11062306a36Sopenharmony_ci#define DST_RBUF_3_FULL_MARK_OFFSET 0x618 11162306a36Sopenharmony_ci#define DST_RBUF_4_FULL_MARK_OFFSET 0x630 11262306a36Sopenharmony_ci#define DST_RBUF_5_FULL_MARK_OFFSET 0x648 11362306a36Sopenharmony_ci/* Ring Buffer Ctrl Regs --- End */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Error Status Regs --- Start */ 11662306a36Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */ 11762306a36Sopenharmony_ci#define ESR0_STATUS_OFFSET 0x900 11862306a36Sopenharmony_ci#define ESR1_STATUS_OFFSET 0x918 11962306a36Sopenharmony_ci#define ESR2_STATUS_OFFSET 0x930 12062306a36Sopenharmony_ci#define ESR3_STATUS_OFFSET 0x948 12162306a36Sopenharmony_ci#define ESR4_STATUS_OFFSET 0x960 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */ 12462306a36Sopenharmony_ci#define ESR0_STATUS_CLR_OFFSET 0x908 12562306a36Sopenharmony_ci#define ESR1_STATUS_CLR_OFFSET 0x920 12662306a36Sopenharmony_ci#define ESR2_STATUS_CLR_OFFSET 0x938 12762306a36Sopenharmony_ci#define ESR3_STATUS_CLR_OFFSET 0x950 12862306a36Sopenharmony_ci#define ESR4_STATUS_CLR_OFFSET 0x968 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */ 13162306a36Sopenharmony_ci#define ESR0_MASK_STATUS_OFFSET 0x90c 13262306a36Sopenharmony_ci#define ESR1_MASK_STATUS_OFFSET 0x924 13362306a36Sopenharmony_ci#define ESR2_MASK_STATUS_OFFSET 0x93c 13462306a36Sopenharmony_ci#define ESR3_MASK_STATUS_OFFSET 0x954 13562306a36Sopenharmony_ci#define ESR4_MASK_STATUS_OFFSET 0x96c 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */ 13862306a36Sopenharmony_ci#define ESR0_MASK_SET_OFFSET 0x910 13962306a36Sopenharmony_ci#define ESR1_MASK_SET_OFFSET 0x928 14062306a36Sopenharmony_ci#define ESR2_MASK_SET_OFFSET 0x940 14162306a36Sopenharmony_ci#define ESR3_MASK_SET_OFFSET 0x958 14262306a36Sopenharmony_ci#define ESR4_MASK_SET_OFFSET 0x970 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */ 14562306a36Sopenharmony_ci#define ESR0_MASK_CLR_OFFSET 0x914 14662306a36Sopenharmony_ci#define ESR1_MASK_CLR_OFFSET 0x92c 14762306a36Sopenharmony_ci#define ESR2_MASK_CLR_OFFSET 0x944 14862306a36Sopenharmony_ci#define ESR3_MASK_CLR_OFFSET 0x95c 14962306a36Sopenharmony_ci#define ESR4_MASK_CLR_OFFSET 0x974 15062306a36Sopenharmony_ci/* Error Status Regs --- End */ 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */ 15362306a36Sopenharmony_ci#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */ 15462306a36Sopenharmony_ci#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */ 15562306a36Sopenharmony_ci#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */ 15662306a36Sopenharmony_ci#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Mask for R5F register. Set all relevant interrupt for playback handler */ 16062306a36Sopenharmony_ci#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \ 16162306a36Sopenharmony_ci BIT(R5F_ESR1_SHIFT) | \ 16262306a36Sopenharmony_ci BIT(R5F_ESR3_SHIFT)) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Mask for R5F register. Set all relevant interrupt for capture handler */ 16562306a36Sopenharmony_ci#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT)) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* 16862306a36Sopenharmony_ci * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick. 16962306a36Sopenharmony_ci * This number should be a multiple of 256. Minimum value is 256 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci#define PERIOD_BYTES_MIN 0x100 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct snd_pcm_hardware cygnus_pcm_hw = { 17462306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 17562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 17662306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED, 17762306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 17862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* A period is basically an interrupt */ 18162306a36Sopenharmony_ci .period_bytes_min = PERIOD_BYTES_MIN, 18262306a36Sopenharmony_ci .period_bytes_max = 0x10000, 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* period_min/max gives range of approx interrupts per buffer */ 18562306a36Sopenharmony_ci .periods_min = 2, 18662306a36Sopenharmony_ci .periods_max = 8, 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * maximum buffer size in bytes = period_bytes_max * periods_max 19062306a36Sopenharmony_ci * We allocate this amount of data for each enabled channel 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci .buffer_bytes_max = 4 * 0x8000, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic u64 cygnus_dma_dmamask = DMA_BIT_MASK(32); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct cygnus_aio_port *cygnus_dai_get_dma_data( 19862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void ringbuf_set_initial(void __iomem *audio_io, 20662306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf, 20762306a36Sopenharmony_ci bool is_playback, 20862306a36Sopenharmony_ci u32 start, 20962306a36Sopenharmony_ci u32 periodsize, 21062306a36Sopenharmony_ci u32 bufsize) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 initial_rd; 21362306a36Sopenharmony_ci u32 initial_wr; 21462306a36Sopenharmony_ci u32 end; 21562306a36Sopenharmony_ci u32 fmark_val; /* free or full mark */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci p_rbuf->period_bytes = periodsize; 21862306a36Sopenharmony_ci p_rbuf->buf_size = bufsize; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (is_playback) { 22162306a36Sopenharmony_ci /* Set the pointers to indicate full (flip uppermost bit) */ 22262306a36Sopenharmony_ci initial_rd = start; 22362306a36Sopenharmony_ci initial_wr = initial_rd ^ BIT(31); 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci /* Set the pointers to indicate empty */ 22662306a36Sopenharmony_ci initial_wr = start; 22762306a36Sopenharmony_ci initial_rd = initial_wr; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci end = start + bufsize - 1; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * The interrupt will fire when free/full mark is *exceeded* 23462306a36Sopenharmony_ci * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark 23562306a36Sopenharmony_ci * to be PERIOD_BYTES_MIN less than the period size. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci fmark_val = periodsize - PERIOD_BYTES_MIN; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci writel(start, audio_io + p_rbuf->baseaddr); 24062306a36Sopenharmony_ci writel(end, audio_io + p_rbuf->endaddr); 24162306a36Sopenharmony_ci writel(fmark_val, audio_io + p_rbuf->fmark); 24262306a36Sopenharmony_ci writel(initial_rd, audio_io + p_rbuf->rdaddr); 24362306a36Sopenharmony_ci writel(initial_wr, audio_io + p_rbuf->wraddr); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int configure_ringbuf_regs(struct snd_pcm_substream *substream) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct cygnus_aio_port *aio; 24962306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf; 25062306a36Sopenharmony_ci int status = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Map the ssp portnum to a set of ring buffers. */ 25562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 25662306a36Sopenharmony_ci p_rbuf = &aio->play_rb_regs; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (aio->portnum) { 25962306a36Sopenharmony_ci case 0: 26062306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(0); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case 1: 26362306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(2); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case 2: 26662306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(4); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case 3: /* SPDIF */ 26962306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(6); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci default: 27262306a36Sopenharmony_ci status = -EINVAL; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci p_rbuf = &aio->capture_rb_regs; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci switch (aio->portnum) { 27862306a36Sopenharmony_ci case 0: 27962306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(0); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case 1: 28262306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(2); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case 2: 28562306a36Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(4); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci status = -EINVAL; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return status; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct cygnus_aio_port *aio; 29862306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 30362306a36Sopenharmony_ci p_rbuf = &aio->play_rb_regs; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci p_rbuf = &aio->capture_rb_regs; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return p_rbuf; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void enable_intr(struct snd_pcm_substream *substream) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct cygnus_aio_port *aio; 31362306a36Sopenharmony_ci u32 clear_mask; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* The port number maps to the bit position to be cleared */ 31862306a36Sopenharmony_ci clear_mask = BIT(aio->portnum); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 32162306a36Sopenharmony_ci /* Clear interrupt status before enabling them */ 32262306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET); 32362306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET); 32462306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET); 32562306a36Sopenharmony_ci /* Unmask the interrupts of the given port*/ 32662306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET); 32762306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET); 32862306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci writel(ANY_PLAYBACK_IRQ, 33162306a36Sopenharmony_ci aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET); 33462306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET); 33562306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET); 33662306a36Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci writel(ANY_CAPTURE_IRQ, 33962306a36Sopenharmony_ci aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void disable_intr(struct snd_pcm_substream *substream) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 34762306a36Sopenharmony_ci struct cygnus_aio_port *aio; 34862306a36Sopenharmony_ci u32 set_mask; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* The port number maps to the bit position to be set */ 35562306a36Sopenharmony_ci set_mask = BIT(aio->portnum); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 35862306a36Sopenharmony_ci /* Mask the interrupts of the given port*/ 35962306a36Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET); 36062306a36Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET); 36162306a36Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET); 36462306a36Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int cygnus_pcm_trigger(struct snd_soc_component *component, 37062306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci int ret = 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci switch (cmd) { 37562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 37662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 37762306a36Sopenharmony_ci enable_intr(substream); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 38162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 38262306a36Sopenharmony_ci disable_intr(substream); 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci default: 38562306a36Sopenharmony_ci ret = -EINVAL; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct cygnus_aio_port *aio; 39462306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 39562306a36Sopenharmony_ci u32 regval; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci p_rbuf = get_ringbuf(substream); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * If free/full mark interrupt occurs, provide timestamp 40362306a36Sopenharmony_ci * to ALSA and update appropriate idx by period_bytes 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40862306a36Sopenharmony_ci /* Set the ring buffer to full */ 40962306a36Sopenharmony_ci regval = readl(aio->cygaud->audio + p_rbuf->rdaddr); 41062306a36Sopenharmony_ci regval = regval ^ BIT(31); 41162306a36Sopenharmony_ci writel(regval, aio->cygaud->audio + p_rbuf->wraddr); 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci /* Set the ring buffer to empty */ 41462306a36Sopenharmony_ci regval = readl(aio->cygaud->audio + p_rbuf->wraddr); 41562306a36Sopenharmony_ci writel(regval, aio->cygaud->audio + p_rbuf->rdaddr); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* 42062306a36Sopenharmony_ci * ESR0/1/3 status Description 42162306a36Sopenharmony_ci * 0x1 I2S0_out port caused interrupt 42262306a36Sopenharmony_ci * 0x2 I2S1_out port caused interrupt 42362306a36Sopenharmony_ci * 0x4 I2S2_out port caused interrupt 42462306a36Sopenharmony_ci * 0x8 SPDIF_out port caused interrupt 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic void handle_playback_irq(struct cygnus_audio *cygaud) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci void __iomem *audio_io; 42962306a36Sopenharmony_ci u32 port; 43062306a36Sopenharmony_ci u32 esr_status0, esr_status1, esr_status3; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci audio_io = cygaud->audio; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * ESR status gets updates with/without interrupts enabled. 43662306a36Sopenharmony_ci * So, check the ESR mask, which provides interrupt enable/ 43762306a36Sopenharmony_ci * disable status and use it to determine which ESR status 43862306a36Sopenharmony_ci * should be serviced. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET); 44162306a36Sopenharmony_ci esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET); 44262306a36Sopenharmony_ci esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET); 44362306a36Sopenharmony_ci esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET); 44462306a36Sopenharmony_ci esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET); 44562306a36Sopenharmony_ci esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) { 44862306a36Sopenharmony_ci u32 esrmask = BIT(port); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Ringbuffer or FIFO underflow 45262306a36Sopenharmony_ci * If we get this interrupt then, it is also true that we have 45362306a36Sopenharmony_ci * not yet responded to the freemark interrupt. 45462306a36Sopenharmony_ci * Log a debug message. The freemark handler below will 45562306a36Sopenharmony_ci * handle getting everything going again. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci if ((esrmask & esr_status1) || (esrmask & esr_status0)) { 45862306a36Sopenharmony_ci dev_dbg(cygaud->dev, 45962306a36Sopenharmony_ci "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n", 46062306a36Sopenharmony_ci esr_status0, esr_status1, esr_status3); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * Freemark is hit. This is the normal interrupt. 46562306a36Sopenharmony_ci * In typical operation the read and write regs will be equal 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci if (esrmask & esr_status3) { 46862306a36Sopenharmony_ci struct snd_pcm_substream *playstr; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci playstr = cygaud->portinfo[port].play_stream; 47162306a36Sopenharmony_ci cygnus_pcm_period_elapsed(playstr); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Clear ESR interrupt */ 47662306a36Sopenharmony_ci writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET); 47762306a36Sopenharmony_ci writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET); 47862306a36Sopenharmony_ci writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET); 47962306a36Sopenharmony_ci /* Rearm freemark logic by writing 1 to the correct bit */ 48062306a36Sopenharmony_ci writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/* 48462306a36Sopenharmony_ci * ESR2/4 status Description 48562306a36Sopenharmony_ci * 0x1 I2S0_in port caused interrupt 48662306a36Sopenharmony_ci * 0x2 I2S1_in port caused interrupt 48762306a36Sopenharmony_ci * 0x4 I2S2_in port caused interrupt 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistatic void handle_capture_irq(struct cygnus_audio *cygaud) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci void __iomem *audio_io; 49262306a36Sopenharmony_ci u32 port; 49362306a36Sopenharmony_ci u32 esr_status2, esr_status4; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci audio_io = cygaud->audio; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * ESR status gets updates with/without interrupts enabled. 49962306a36Sopenharmony_ci * So, check the ESR mask, which provides interrupt enable/ 50062306a36Sopenharmony_ci * disable status and use it to determine which ESR status 50162306a36Sopenharmony_ci * should be serviced. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET); 50462306a36Sopenharmony_ci esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET); 50562306a36Sopenharmony_ci esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET); 50662306a36Sopenharmony_ci esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) { 50962306a36Sopenharmony_ci u32 esrmask = BIT(port); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * Ringbuffer or FIFO overflow 51362306a36Sopenharmony_ci * If we get this interrupt then, it is also true that we have 51462306a36Sopenharmony_ci * not yet responded to the fullmark interrupt. 51562306a36Sopenharmony_ci * Log a debug message. The fullmark handler below will 51662306a36Sopenharmony_ci * handle getting everything going again. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci if (esrmask & esr_status2) 51962306a36Sopenharmony_ci dev_dbg(cygaud->dev, 52062306a36Sopenharmony_ci "Overflow: esr2=0x%x\n", esr_status2); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (esrmask & esr_status4) { 52362306a36Sopenharmony_ci struct snd_pcm_substream *capstr; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci capstr = cygaud->portinfo[port].capture_stream; 52662306a36Sopenharmony_ci cygnus_pcm_period_elapsed(capstr); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET); 53162306a36Sopenharmony_ci writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET); 53262306a36Sopenharmony_ci /* Rearm fullmark logic by writing 1 to the correct bit */ 53362306a36Sopenharmony_ci writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic irqreturn_t cygnus_dma_irq(int irq, void *data) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci u32 r5_status; 53962306a36Sopenharmony_ci struct cygnus_audio *cygaud = data; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * R5 status bits Description 54362306a36Sopenharmony_ci * 0 ESR0 (playback FIFO interrupt) 54462306a36Sopenharmony_ci * 1 ESR1 (playback rbuf interrupt) 54562306a36Sopenharmony_ci * 2 ESR2 (capture rbuf interrupt) 54662306a36Sopenharmony_ci * 3 ESR3 (Freemark play. interrupt) 54762306a36Sopenharmony_ci * 4 ESR4 (Fullmark capt. interrupt) 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ))) 55262306a36Sopenharmony_ci return IRQ_NONE; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* If playback interrupt happened */ 55562306a36Sopenharmony_ci if (ANY_PLAYBACK_IRQ & r5_status) { 55662306a36Sopenharmony_ci handle_playback_irq(cygaud); 55762306a36Sopenharmony_ci writel(ANY_PLAYBACK_IRQ & r5_status, 55862306a36Sopenharmony_ci cygaud->audio + INTH_R5F_CLEAR_OFFSET); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* If capture interrupt happened */ 56262306a36Sopenharmony_ci if (ANY_CAPTURE_IRQ & r5_status) { 56362306a36Sopenharmony_ci handle_capture_irq(cygaud); 56462306a36Sopenharmony_ci writel(ANY_CAPTURE_IRQ & r5_status, 56562306a36Sopenharmony_ci cygaud->audio + INTH_R5F_CLEAR_OFFSET); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return IRQ_HANDLED; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int cygnus_pcm_open(struct snd_soc_component *component, 57262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 57562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 57662306a36Sopenharmony_ci struct cygnus_aio_port *aio; 57762306a36Sopenharmony_ci int ret; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 58062306a36Sopenharmony_ci if (!aio) 58162306a36Sopenharmony_ci return -ENODEV; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 58862306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN); 58962306a36Sopenharmony_ci if (ret < 0) 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 59362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN); 59462306a36Sopenharmony_ci if (ret < 0) 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci /* 59762306a36Sopenharmony_ci * Keep track of which substream belongs to which port. 59862306a36Sopenharmony_ci * This info is needed by snd_pcm_period_elapsed() in irq_handler 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 60162306a36Sopenharmony_ci aio->play_stream = substream; 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci aio->capture_stream = substream; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int cygnus_pcm_close(struct snd_soc_component *component, 60962306a36Sopenharmony_ci struct snd_pcm_substream *substream) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 61262306a36Sopenharmony_ci struct cygnus_aio_port *aio; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 61962306a36Sopenharmony_ci aio->play_stream = NULL; 62062306a36Sopenharmony_ci else 62162306a36Sopenharmony_ci aio->capture_stream = NULL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!aio->play_stream && !aio->capture_stream) 62462306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int cygnus_pcm_prepare(struct snd_soc_component *component, 63062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 63362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 63462306a36Sopenharmony_ci struct cygnus_aio_port *aio; 63562306a36Sopenharmony_ci unsigned long bufsize, periodsize; 63662306a36Sopenharmony_ci bool is_play; 63762306a36Sopenharmony_ci u32 start; 63862306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 64162306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci bufsize = snd_pcm_lib_buffer_bytes(substream); 64462306a36Sopenharmony_ci periodsize = snd_pcm_lib_period_bytes(substream); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n", 64762306a36Sopenharmony_ci __func__, bufsize, periodsize); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci configure_ringbuf_regs(substream); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci p_rbuf = get_ringbuf(substream); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci start = runtime->dma_addr; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start, 65862306a36Sopenharmony_ci periodsize, bufsize); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component, 66462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct cygnus_aio_port *aio; 66762306a36Sopenharmony_ci unsigned int res = 0, cur = 0, base = 0; 66862306a36Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* 67362306a36Sopenharmony_ci * Get the offset of the current read (for playack) or write 67462306a36Sopenharmony_ci * index (for capture). Report this value back to the asoc framework. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci p_rbuf = get_ringbuf(substream); 67762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 67862306a36Sopenharmony_ci cur = readl(aio->cygaud->audio + p_rbuf->rdaddr); 67962306a36Sopenharmony_ci else 68062306a36Sopenharmony_ci cur = readl(aio->cygaud->audio + p_rbuf->wraddr); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci base = readl(aio->cygaud->audio + p_rbuf->baseaddr); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * Mask off the MSB of the rdaddr,wraddr and baseaddr 68662306a36Sopenharmony_ci * since MSB is not part of the address 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci res = (cur & 0x7fffffff) - (base & 0x7fffffff); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, res); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int cygnus_dma_new(struct snd_soc_component *component, 69462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci size_t size = cygnus_pcm_hw.buffer_bytes_max; 69762306a36Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (!card->dev->dma_mask) 70062306a36Sopenharmony_ci card->dev->dma_mask = &cygnus_dma_dmamask; 70162306a36Sopenharmony_ci if (!card->dev->coherent_dma_mask) 70262306a36Sopenharmony_ci card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 70562306a36Sopenharmony_ci card->dev, size, size); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic struct snd_soc_component_driver cygnus_soc_platform = { 71162306a36Sopenharmony_ci .open = cygnus_pcm_open, 71262306a36Sopenharmony_ci .close = cygnus_pcm_close, 71362306a36Sopenharmony_ci .prepare = cygnus_pcm_prepare, 71462306a36Sopenharmony_ci .trigger = cygnus_pcm_trigger, 71562306a36Sopenharmony_ci .pointer = cygnus_pcm_pointer, 71662306a36Sopenharmony_ci .pcm_construct = cygnus_dma_new, 71762306a36Sopenharmony_ci}; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciint cygnus_soc_platform_register(struct device *dev, 72062306a36Sopenharmony_ci struct cygnus_audio *cygaud) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci int rc; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci dev_dbg(dev, "%s Enter\n", __func__); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq, 72762306a36Sopenharmony_ci IRQF_SHARED, "cygnus-audio", cygaud); 72862306a36Sopenharmony_ci if (rc) { 72962306a36Sopenharmony_ci dev_err(dev, "%s request_irq error %d\n", __func__, rc); 73062306a36Sopenharmony_ci return rc; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform, 73462306a36Sopenharmony_ci NULL, 0); 73562306a36Sopenharmony_ci if (rc) { 73662306a36Sopenharmony_ci dev_err(dev, "%s failed\n", __func__); 73762306a36Sopenharmony_ci return rc; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return 0; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciint cygnus_soc_platform_unregister(struct device *dev) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 74962306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 75062306a36Sopenharmony_ciMODULE_DESCRIPTION("Cygnus ASoC PCM module"); 751