1*2aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2*2aec85b2SThomas Gleixner // Copyright (C) 2014-2015 Broadcom Corporation
31200a7d9SSimran Rai #include <linux/debugfs.h>
41200a7d9SSimran Rai #include <linux/dma-mapping.h>
51200a7d9SSimran Rai #include <linux/init.h>
61200a7d9SSimran Rai #include <linux/io.h>
71200a7d9SSimran Rai #include <linux/module.h>
81200a7d9SSimran Rai #include <linux/slab.h>
91200a7d9SSimran Rai #include <linux/timer.h>
101200a7d9SSimran Rai #include <sound/core.h>
111200a7d9SSimran Rai #include <sound/pcm.h>
121200a7d9SSimran Rai #include <sound/pcm_params.h>
131200a7d9SSimran Rai #include <sound/soc.h>
141200a7d9SSimran Rai #include <sound/soc-dai.h>
151200a7d9SSimran Rai
161200a7d9SSimran Rai #include "cygnus-ssp.h"
171200a7d9SSimran Rai
181200a7d9SSimran Rai /* Register offset needed for ASoC PCM module */
191200a7d9SSimran Rai
201200a7d9SSimran Rai #define INTH_R5F_STATUS_OFFSET 0x040
211200a7d9SSimran Rai #define INTH_R5F_CLEAR_OFFSET 0x048
221200a7d9SSimran Rai #define INTH_R5F_MASK_SET_OFFSET 0x050
231200a7d9SSimran Rai #define INTH_R5F_MASK_CLEAR_OFFSET 0x054
241200a7d9SSimran Rai
251200a7d9SSimran Rai #define BF_REARM_FREE_MARK_OFFSET 0x344
261200a7d9SSimran Rai #define BF_REARM_FULL_MARK_OFFSET 0x348
271200a7d9SSimran Rai
281200a7d9SSimran Rai /* Ring Buffer Ctrl Regs --- Start */
291200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
301200a7d9SSimran Rai #define SRC_RBUF_0_RDADDR_OFFSET 0x500
311200a7d9SSimran Rai #define SRC_RBUF_1_RDADDR_OFFSET 0x518
321200a7d9SSimran Rai #define SRC_RBUF_2_RDADDR_OFFSET 0x530
331200a7d9SSimran Rai #define SRC_RBUF_3_RDADDR_OFFSET 0x548
341200a7d9SSimran Rai #define SRC_RBUF_4_RDADDR_OFFSET 0x560
351200a7d9SSimran Rai #define SRC_RBUF_5_RDADDR_OFFSET 0x578
361200a7d9SSimran Rai #define SRC_RBUF_6_RDADDR_OFFSET 0x590
371200a7d9SSimran Rai
381200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
391200a7d9SSimran Rai #define SRC_RBUF_0_WRADDR_OFFSET 0x504
401200a7d9SSimran Rai #define SRC_RBUF_1_WRADDR_OFFSET 0x51c
411200a7d9SSimran Rai #define SRC_RBUF_2_WRADDR_OFFSET 0x534
421200a7d9SSimran Rai #define SRC_RBUF_3_WRADDR_OFFSET 0x54c
431200a7d9SSimran Rai #define SRC_RBUF_4_WRADDR_OFFSET 0x564
441200a7d9SSimran Rai #define SRC_RBUF_5_WRADDR_OFFSET 0x57c
451200a7d9SSimran Rai #define SRC_RBUF_6_WRADDR_OFFSET 0x594
461200a7d9SSimran Rai
471200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
481200a7d9SSimran Rai #define SRC_RBUF_0_BASEADDR_OFFSET 0x508
491200a7d9SSimran Rai #define SRC_RBUF_1_BASEADDR_OFFSET 0x520
501200a7d9SSimran Rai #define SRC_RBUF_2_BASEADDR_OFFSET 0x538
511200a7d9SSimran Rai #define SRC_RBUF_3_BASEADDR_OFFSET 0x550
521200a7d9SSimran Rai #define SRC_RBUF_4_BASEADDR_OFFSET 0x568
531200a7d9SSimran Rai #define SRC_RBUF_5_BASEADDR_OFFSET 0x580
541200a7d9SSimran Rai #define SRC_RBUF_6_BASEADDR_OFFSET 0x598
551200a7d9SSimran Rai
561200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
571200a7d9SSimran Rai #define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
581200a7d9SSimran Rai #define SRC_RBUF_1_ENDADDR_OFFSET 0x524
591200a7d9SSimran Rai #define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
601200a7d9SSimran Rai #define SRC_RBUF_3_ENDADDR_OFFSET 0x554
611200a7d9SSimran Rai #define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
621200a7d9SSimran Rai #define SRC_RBUF_5_ENDADDR_OFFSET 0x584
631200a7d9SSimran Rai #define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
641200a7d9SSimran Rai
651200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
661200a7d9SSimran Rai #define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
671200a7d9SSimran Rai #define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
681200a7d9SSimran Rai #define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
691200a7d9SSimran Rai #define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
701200a7d9SSimran Rai #define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
711200a7d9SSimran Rai #define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
721200a7d9SSimran Rai #define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
731200a7d9SSimran Rai
741200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
751200a7d9SSimran Rai #define DST_RBUF_0_RDADDR_OFFSET 0x5c0
761200a7d9SSimran Rai #define DST_RBUF_1_RDADDR_OFFSET 0x5d8
771200a7d9SSimran Rai #define DST_RBUF_2_RDADDR_OFFSET 0x5f0
781200a7d9SSimran Rai #define DST_RBUF_3_RDADDR_OFFSET 0x608
791200a7d9SSimran Rai #define DST_RBUF_4_RDADDR_OFFSET 0x620
801200a7d9SSimran Rai #define DST_RBUF_5_RDADDR_OFFSET 0x638
811200a7d9SSimran Rai
821200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
831200a7d9SSimran Rai #define DST_RBUF_0_WRADDR_OFFSET 0x5c4
841200a7d9SSimran Rai #define DST_RBUF_1_WRADDR_OFFSET 0x5dc
851200a7d9SSimran Rai #define DST_RBUF_2_WRADDR_OFFSET 0x5f4
861200a7d9SSimran Rai #define DST_RBUF_3_WRADDR_OFFSET 0x60c
871200a7d9SSimran Rai #define DST_RBUF_4_WRADDR_OFFSET 0x624
881200a7d9SSimran Rai #define DST_RBUF_5_WRADDR_OFFSET 0x63c
891200a7d9SSimran Rai
901200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
911200a7d9SSimran Rai #define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
921200a7d9SSimran Rai #define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
931200a7d9SSimran Rai #define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
941200a7d9SSimran Rai #define DST_RBUF_3_BASEADDR_OFFSET 0x610
951200a7d9SSimran Rai #define DST_RBUF_4_BASEADDR_OFFSET 0x628
961200a7d9SSimran Rai #define DST_RBUF_5_BASEADDR_OFFSET 0x640
971200a7d9SSimran Rai
981200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
991200a7d9SSimran Rai #define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
1001200a7d9SSimran Rai #define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
1011200a7d9SSimran Rai #define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
1021200a7d9SSimran Rai #define DST_RBUF_3_ENDADDR_OFFSET 0x614
1031200a7d9SSimran Rai #define DST_RBUF_4_ENDADDR_OFFSET 0x62c
1041200a7d9SSimran Rai #define DST_RBUF_5_ENDADDR_OFFSET 0x644
1051200a7d9SSimran Rai
1061200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
1071200a7d9SSimran Rai #define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
1081200a7d9SSimran Rai #define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
1091200a7d9SSimran Rai #define DST_RBUF_2_FULL_MARK_OFFSET 0x600
1101200a7d9SSimran Rai #define DST_RBUF_3_FULL_MARK_OFFSET 0x618
1111200a7d9SSimran Rai #define DST_RBUF_4_FULL_MARK_OFFSET 0x630
1121200a7d9SSimran Rai #define DST_RBUF_5_FULL_MARK_OFFSET 0x648
1131200a7d9SSimran Rai /* Ring Buffer Ctrl Regs --- End */
1141200a7d9SSimran Rai
1151200a7d9SSimran Rai /* Error Status Regs --- Start */
1161200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
1171200a7d9SSimran Rai #define ESR0_STATUS_OFFSET 0x900
1181200a7d9SSimran Rai #define ESR1_STATUS_OFFSET 0x918
1191200a7d9SSimran Rai #define ESR2_STATUS_OFFSET 0x930
1201200a7d9SSimran Rai #define ESR3_STATUS_OFFSET 0x948
1211200a7d9SSimran Rai #define ESR4_STATUS_OFFSET 0x960
1221200a7d9SSimran Rai
1231200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
1241200a7d9SSimran Rai #define ESR0_STATUS_CLR_OFFSET 0x908
1251200a7d9SSimran Rai #define ESR1_STATUS_CLR_OFFSET 0x920
1261200a7d9SSimran Rai #define ESR2_STATUS_CLR_OFFSET 0x938
1271200a7d9SSimran Rai #define ESR3_STATUS_CLR_OFFSET 0x950
1281200a7d9SSimran Rai #define ESR4_STATUS_CLR_OFFSET 0x968
1291200a7d9SSimran Rai
1301200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
1311200a7d9SSimran Rai #define ESR0_MASK_STATUS_OFFSET 0x90c
1321200a7d9SSimran Rai #define ESR1_MASK_STATUS_OFFSET 0x924
1331200a7d9SSimran Rai #define ESR2_MASK_STATUS_OFFSET 0x93c
1341200a7d9SSimran Rai #define ESR3_MASK_STATUS_OFFSET 0x954
1351200a7d9SSimran Rai #define ESR4_MASK_STATUS_OFFSET 0x96c
1361200a7d9SSimran Rai
1371200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
1381200a7d9SSimran Rai #define ESR0_MASK_SET_OFFSET 0x910
1391200a7d9SSimran Rai #define ESR1_MASK_SET_OFFSET 0x928
1401200a7d9SSimran Rai #define ESR2_MASK_SET_OFFSET 0x940
1411200a7d9SSimran Rai #define ESR3_MASK_SET_OFFSET 0x958
1421200a7d9SSimran Rai #define ESR4_MASK_SET_OFFSET 0x970
1431200a7d9SSimran Rai
1441200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
1451200a7d9SSimran Rai #define ESR0_MASK_CLR_OFFSET 0x914
1461200a7d9SSimran Rai #define ESR1_MASK_CLR_OFFSET 0x92c
1471200a7d9SSimran Rai #define ESR2_MASK_CLR_OFFSET 0x944
1481200a7d9SSimran Rai #define ESR3_MASK_CLR_OFFSET 0x95c
1491200a7d9SSimran Rai #define ESR4_MASK_CLR_OFFSET 0x974
1501200a7d9SSimran Rai /* Error Status Regs --- End */
1511200a7d9SSimran Rai
1521200a7d9SSimran Rai #define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */
1531200a7d9SSimran Rai #define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */
1541200a7d9SSimran Rai #define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */
1551200a7d9SSimran Rai #define R5F_ESR3_SHIFT 3 /* esr3 = freemark */
1561200a7d9SSimran Rai #define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */
1571200a7d9SSimran Rai
1581200a7d9SSimran Rai
1591200a7d9SSimran Rai /* Mask for R5F register. Set all relevant interrupt for playback handler */
1601200a7d9SSimran Rai #define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
1611200a7d9SSimran Rai BIT(R5F_ESR1_SHIFT) | \
1621200a7d9SSimran Rai BIT(R5F_ESR3_SHIFT))
1631200a7d9SSimran Rai
1641200a7d9SSimran Rai /* Mask for R5F register. Set all relevant interrupt for capture handler */
1651200a7d9SSimran Rai #define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
1661200a7d9SSimran Rai
1671200a7d9SSimran Rai /*
1681200a7d9SSimran Rai * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
1691200a7d9SSimran Rai * This number should be a multiple of 256. Minimum value is 256
1701200a7d9SSimran Rai */
1711200a7d9SSimran Rai #define PERIOD_BYTES_MIN 0x100
1721200a7d9SSimran Rai
1731200a7d9SSimran Rai static const struct snd_pcm_hardware cygnus_pcm_hw = {
1741200a7d9SSimran Rai .info = SNDRV_PCM_INFO_MMAP |
1751200a7d9SSimran Rai SNDRV_PCM_INFO_MMAP_VALID |
1761200a7d9SSimran Rai SNDRV_PCM_INFO_INTERLEAVED,
1771200a7d9SSimran Rai .formats = SNDRV_PCM_FMTBIT_S16_LE |
1781200a7d9SSimran Rai SNDRV_PCM_FMTBIT_S32_LE,
1791200a7d9SSimran Rai
1801200a7d9SSimran Rai /* A period is basically an interrupt */
1811200a7d9SSimran Rai .period_bytes_min = PERIOD_BYTES_MIN,
1821200a7d9SSimran Rai .period_bytes_max = 0x10000,
1831200a7d9SSimran Rai
1841200a7d9SSimran Rai /* period_min/max gives range of approx interrupts per buffer */
1851200a7d9SSimran Rai .periods_min = 2,
1861200a7d9SSimran Rai .periods_max = 8,
1871200a7d9SSimran Rai
1881200a7d9SSimran Rai /*
1891200a7d9SSimran Rai * maximum buffer size in bytes = period_bytes_max * periods_max
1901200a7d9SSimran Rai * We allocate this amount of data for each enabled channel
1911200a7d9SSimran Rai */
1921200a7d9SSimran Rai .buffer_bytes_max = 4 * 0x8000,
1931200a7d9SSimran Rai };
1941200a7d9SSimran Rai
1951200a7d9SSimran Rai static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
1961200a7d9SSimran Rai
cygnus_dai_get_dma_data(struct snd_pcm_substream * substream)1971200a7d9SSimran Rai static struct cygnus_aio_port *cygnus_dai_get_dma_data(
1981200a7d9SSimran Rai struct snd_pcm_substream *substream)
1991200a7d9SSimran Rai {
2000359c834SKuninori Morimoto struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
2011200a7d9SSimran Rai
202fc392364SKuninori Morimoto return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream);
2031200a7d9SSimran Rai }
2041200a7d9SSimran Rai
ringbuf_set_initial(void __iomem * audio_io,struct ringbuf_regs * p_rbuf,bool is_playback,u32 start,u32 periodsize,u32 bufsize)2051200a7d9SSimran Rai static void ringbuf_set_initial(void __iomem *audio_io,
2061200a7d9SSimran Rai struct ringbuf_regs *p_rbuf,
2071200a7d9SSimran Rai bool is_playback,
2081200a7d9SSimran Rai u32 start,
2091200a7d9SSimran Rai u32 periodsize,
2101200a7d9SSimran Rai u32 bufsize)
2111200a7d9SSimran Rai {
2121200a7d9SSimran Rai u32 initial_rd;
2131200a7d9SSimran Rai u32 initial_wr;
2141200a7d9SSimran Rai u32 end;
2151200a7d9SSimran Rai u32 fmark_val; /* free or full mark */
2161200a7d9SSimran Rai
2171200a7d9SSimran Rai p_rbuf->period_bytes = periodsize;
2181200a7d9SSimran Rai p_rbuf->buf_size = bufsize;
2191200a7d9SSimran Rai
2201200a7d9SSimran Rai if (is_playback) {
2211200a7d9SSimran Rai /* Set the pointers to indicate full (flip uppermost bit) */
2221200a7d9SSimran Rai initial_rd = start;
2231200a7d9SSimran Rai initial_wr = initial_rd ^ BIT(31);
2241200a7d9SSimran Rai } else {
2251200a7d9SSimran Rai /* Set the pointers to indicate empty */
2261200a7d9SSimran Rai initial_wr = start;
2271200a7d9SSimran Rai initial_rd = initial_wr;
2281200a7d9SSimran Rai }
2291200a7d9SSimran Rai
2301200a7d9SSimran Rai end = start + bufsize - 1;
2311200a7d9SSimran Rai
2321200a7d9SSimran Rai /*
2331200a7d9SSimran Rai * The interrupt will fire when free/full mark is *exceeded*
2341200a7d9SSimran Rai * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
2351200a7d9SSimran Rai * to be PERIOD_BYTES_MIN less than the period size.
2361200a7d9SSimran Rai */
2371200a7d9SSimran Rai fmark_val = periodsize - PERIOD_BYTES_MIN;
2381200a7d9SSimran Rai
2391200a7d9SSimran Rai writel(start, audio_io + p_rbuf->baseaddr);
2401200a7d9SSimran Rai writel(end, audio_io + p_rbuf->endaddr);
2411200a7d9SSimran Rai writel(fmark_val, audio_io + p_rbuf->fmark);
2421200a7d9SSimran Rai writel(initial_rd, audio_io + p_rbuf->rdaddr);
2431200a7d9SSimran Rai writel(initial_wr, audio_io + p_rbuf->wraddr);
2441200a7d9SSimran Rai }
2451200a7d9SSimran Rai
configure_ringbuf_regs(struct snd_pcm_substream * substream)2461200a7d9SSimran Rai static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
2471200a7d9SSimran Rai {
2481200a7d9SSimran Rai struct cygnus_aio_port *aio;
2491200a7d9SSimran Rai struct ringbuf_regs *p_rbuf;
2501200a7d9SSimran Rai int status = 0;
2511200a7d9SSimran Rai
2521200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
2531200a7d9SSimran Rai
2541200a7d9SSimran Rai /* Map the ssp portnum to a set of ring buffers. */
2551200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2561200a7d9SSimran Rai p_rbuf = &aio->play_rb_regs;
2571200a7d9SSimran Rai
2581200a7d9SSimran Rai switch (aio->portnum) {
2591200a7d9SSimran Rai case 0:
2601200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_PLAYBACK(0);
2611200a7d9SSimran Rai break;
2621200a7d9SSimran Rai case 1:
2631200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_PLAYBACK(2);
2641200a7d9SSimran Rai break;
2651200a7d9SSimran Rai case 2:
2661200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_PLAYBACK(4);
2671200a7d9SSimran Rai break;
2681200a7d9SSimran Rai case 3: /* SPDIF */
2691200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_PLAYBACK(6);
2701200a7d9SSimran Rai break;
2711200a7d9SSimran Rai default:
2721200a7d9SSimran Rai status = -EINVAL;
2731200a7d9SSimran Rai }
2741200a7d9SSimran Rai } else {
2751200a7d9SSimran Rai p_rbuf = &aio->capture_rb_regs;
2761200a7d9SSimran Rai
2771200a7d9SSimran Rai switch (aio->portnum) {
2781200a7d9SSimran Rai case 0:
2791200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_CAPTURE(0);
2801200a7d9SSimran Rai break;
2811200a7d9SSimran Rai case 1:
2821200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_CAPTURE(2);
2831200a7d9SSimran Rai break;
2841200a7d9SSimran Rai case 2:
2851200a7d9SSimran Rai *p_rbuf = RINGBUF_REG_CAPTURE(4);
2861200a7d9SSimran Rai break;
2871200a7d9SSimran Rai default:
2881200a7d9SSimran Rai status = -EINVAL;
2891200a7d9SSimran Rai }
2901200a7d9SSimran Rai }
2911200a7d9SSimran Rai
2921200a7d9SSimran Rai return status;
2931200a7d9SSimran Rai }
2941200a7d9SSimran Rai
get_ringbuf(struct snd_pcm_substream * substream)2951200a7d9SSimran Rai static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
2961200a7d9SSimran Rai {
2971200a7d9SSimran Rai struct cygnus_aio_port *aio;
2981200a7d9SSimran Rai struct ringbuf_regs *p_rbuf = NULL;
2991200a7d9SSimran Rai
3001200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
3011200a7d9SSimran Rai
3021200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3031200a7d9SSimran Rai p_rbuf = &aio->play_rb_regs;
3041200a7d9SSimran Rai else
3051200a7d9SSimran Rai p_rbuf = &aio->capture_rb_regs;
3061200a7d9SSimran Rai
3071200a7d9SSimran Rai return p_rbuf;
3081200a7d9SSimran Rai }
3091200a7d9SSimran Rai
enable_intr(struct snd_pcm_substream * substream)3101200a7d9SSimran Rai static void enable_intr(struct snd_pcm_substream *substream)
3111200a7d9SSimran Rai {
3121200a7d9SSimran Rai struct cygnus_aio_port *aio;
3131200a7d9SSimran Rai u32 clear_mask;
3141200a7d9SSimran Rai
3151200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
3161200a7d9SSimran Rai
3171200a7d9SSimran Rai /* The port number maps to the bit position to be cleared */
3181200a7d9SSimran Rai clear_mask = BIT(aio->portnum);
3191200a7d9SSimran Rai
3201200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3211200a7d9SSimran Rai /* Clear interrupt status before enabling them */
3221200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
3231200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
3241200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
3251200a7d9SSimran Rai /* Unmask the interrupts of the given port*/
3261200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
3271200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
3281200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
3291200a7d9SSimran Rai
3301200a7d9SSimran Rai writel(ANY_PLAYBACK_IRQ,
3311200a7d9SSimran Rai aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
3321200a7d9SSimran Rai } else {
3331200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
3341200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
3351200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
3361200a7d9SSimran Rai writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
3371200a7d9SSimran Rai
3381200a7d9SSimran Rai writel(ANY_CAPTURE_IRQ,
3391200a7d9SSimran Rai aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
3401200a7d9SSimran Rai }
3411200a7d9SSimran Rai
3421200a7d9SSimran Rai }
3431200a7d9SSimran Rai
disable_intr(struct snd_pcm_substream * substream)3441200a7d9SSimran Rai static void disable_intr(struct snd_pcm_substream *substream)
3451200a7d9SSimran Rai {
3460359c834SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3471200a7d9SSimran Rai struct cygnus_aio_port *aio;
3481200a7d9SSimran Rai u32 set_mask;
3491200a7d9SSimran Rai
3501200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
3511200a7d9SSimran Rai
352fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
3531200a7d9SSimran Rai
3541200a7d9SSimran Rai /* The port number maps to the bit position to be set */
3551200a7d9SSimran Rai set_mask = BIT(aio->portnum);
3561200a7d9SSimran Rai
3571200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3581200a7d9SSimran Rai /* Mask the interrupts of the given port*/
3591200a7d9SSimran Rai writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
3601200a7d9SSimran Rai writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
3611200a7d9SSimran Rai writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
3621200a7d9SSimran Rai } else {
3631200a7d9SSimran Rai writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
3641200a7d9SSimran Rai writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
3651200a7d9SSimran Rai }
3661200a7d9SSimran Rai
3671200a7d9SSimran Rai }
3681200a7d9SSimran Rai
cygnus_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)369dc06bd0fSKuninori Morimoto static int cygnus_pcm_trigger(struct snd_soc_component *component,
370dc06bd0fSKuninori Morimoto struct snd_pcm_substream *substream, int cmd)
3711200a7d9SSimran Rai {
3721200a7d9SSimran Rai int ret = 0;
3731200a7d9SSimran Rai
3741200a7d9SSimran Rai switch (cmd) {
3751200a7d9SSimran Rai case SNDRV_PCM_TRIGGER_START:
3761200a7d9SSimran Rai case SNDRV_PCM_TRIGGER_RESUME:
3771200a7d9SSimran Rai enable_intr(substream);
3781200a7d9SSimran Rai break;
3791200a7d9SSimran Rai
3801200a7d9SSimran Rai case SNDRV_PCM_TRIGGER_STOP:
3811200a7d9SSimran Rai case SNDRV_PCM_TRIGGER_SUSPEND:
3821200a7d9SSimran Rai disable_intr(substream);
3831200a7d9SSimran Rai break;
3841200a7d9SSimran Rai default:
3851200a7d9SSimran Rai ret = -EINVAL;
3861200a7d9SSimran Rai }
3871200a7d9SSimran Rai
3881200a7d9SSimran Rai return ret;
3891200a7d9SSimran Rai }
3901200a7d9SSimran Rai
cygnus_pcm_period_elapsed(struct snd_pcm_substream * substream)3911200a7d9SSimran Rai static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
3921200a7d9SSimran Rai {
3931200a7d9SSimran Rai struct cygnus_aio_port *aio;
3941200a7d9SSimran Rai struct ringbuf_regs *p_rbuf = NULL;
3951200a7d9SSimran Rai u32 regval;
3961200a7d9SSimran Rai
3971200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
3981200a7d9SSimran Rai
3991200a7d9SSimran Rai p_rbuf = get_ringbuf(substream);
4001200a7d9SSimran Rai
4011200a7d9SSimran Rai /*
4021200a7d9SSimran Rai * If free/full mark interrupt occurs, provide timestamp
4031200a7d9SSimran Rai * to ALSA and update appropriate idx by period_bytes
4041200a7d9SSimran Rai */
4051200a7d9SSimran Rai snd_pcm_period_elapsed(substream);
4061200a7d9SSimran Rai
4071200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4081200a7d9SSimran Rai /* Set the ring buffer to full */
4091200a7d9SSimran Rai regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
4101200a7d9SSimran Rai regval = regval ^ BIT(31);
4111200a7d9SSimran Rai writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
4121200a7d9SSimran Rai } else {
4131200a7d9SSimran Rai /* Set the ring buffer to empty */
4141200a7d9SSimran Rai regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
4151200a7d9SSimran Rai writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
4161200a7d9SSimran Rai }
4171200a7d9SSimran Rai }
4181200a7d9SSimran Rai
4191200a7d9SSimran Rai /*
4201200a7d9SSimran Rai * ESR0/1/3 status Description
4211200a7d9SSimran Rai * 0x1 I2S0_out port caused interrupt
4221200a7d9SSimran Rai * 0x2 I2S1_out port caused interrupt
4231200a7d9SSimran Rai * 0x4 I2S2_out port caused interrupt
4241200a7d9SSimran Rai * 0x8 SPDIF_out port caused interrupt
4251200a7d9SSimran Rai */
handle_playback_irq(struct cygnus_audio * cygaud)4261200a7d9SSimran Rai static void handle_playback_irq(struct cygnus_audio *cygaud)
4271200a7d9SSimran Rai {
4281200a7d9SSimran Rai void __iomem *audio_io;
4291200a7d9SSimran Rai u32 port;
4301200a7d9SSimran Rai u32 esr_status0, esr_status1, esr_status3;
4311200a7d9SSimran Rai
4321200a7d9SSimran Rai audio_io = cygaud->audio;
4331200a7d9SSimran Rai
4341200a7d9SSimran Rai /*
4351200a7d9SSimran Rai * ESR status gets updates with/without interrupts enabled.
4361200a7d9SSimran Rai * So, check the ESR mask, which provides interrupt enable/
4371200a7d9SSimran Rai * disable status and use it to determine which ESR status
4381200a7d9SSimran Rai * should be serviced.
4391200a7d9SSimran Rai */
4401200a7d9SSimran Rai esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
4411200a7d9SSimran Rai esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
4421200a7d9SSimran Rai esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
4431200a7d9SSimran Rai esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
4441200a7d9SSimran Rai esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
4451200a7d9SSimran Rai esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
4461200a7d9SSimran Rai
4471200a7d9SSimran Rai for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
4481200a7d9SSimran Rai u32 esrmask = BIT(port);
4491200a7d9SSimran Rai
4501200a7d9SSimran Rai /*
4511200a7d9SSimran Rai * Ringbuffer or FIFO underflow
4521200a7d9SSimran Rai * If we get this interrupt then, it is also true that we have
4531200a7d9SSimran Rai * not yet responded to the freemark interrupt.
4541200a7d9SSimran Rai * Log a debug message. The freemark handler below will
4551200a7d9SSimran Rai * handle getting everything going again.
4561200a7d9SSimran Rai */
4571200a7d9SSimran Rai if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
4581200a7d9SSimran Rai dev_dbg(cygaud->dev,
4591200a7d9SSimran Rai "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
4601200a7d9SSimran Rai esr_status0, esr_status1, esr_status3);
4611200a7d9SSimran Rai }
4621200a7d9SSimran Rai
4631200a7d9SSimran Rai /*
4641200a7d9SSimran Rai * Freemark is hit. This is the normal interrupt.
4651200a7d9SSimran Rai * In typical operation the read and write regs will be equal
4661200a7d9SSimran Rai */
4671200a7d9SSimran Rai if (esrmask & esr_status3) {
4681200a7d9SSimran Rai struct snd_pcm_substream *playstr;
4691200a7d9SSimran Rai
4701200a7d9SSimran Rai playstr = cygaud->portinfo[port].play_stream;
4711200a7d9SSimran Rai cygnus_pcm_period_elapsed(playstr);
4721200a7d9SSimran Rai }
4731200a7d9SSimran Rai }
4741200a7d9SSimran Rai
4751200a7d9SSimran Rai /* Clear ESR interrupt */
4761200a7d9SSimran Rai writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
4771200a7d9SSimran Rai writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
4781200a7d9SSimran Rai writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
4791200a7d9SSimran Rai /* Rearm freemark logic by writing 1 to the correct bit */
4801200a7d9SSimran Rai writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
4811200a7d9SSimran Rai }
4821200a7d9SSimran Rai
4831200a7d9SSimran Rai /*
4841200a7d9SSimran Rai * ESR2/4 status Description
4851200a7d9SSimran Rai * 0x1 I2S0_in port caused interrupt
4861200a7d9SSimran Rai * 0x2 I2S1_in port caused interrupt
4871200a7d9SSimran Rai * 0x4 I2S2_in port caused interrupt
4881200a7d9SSimran Rai */
handle_capture_irq(struct cygnus_audio * cygaud)4891200a7d9SSimran Rai static void handle_capture_irq(struct cygnus_audio *cygaud)
4901200a7d9SSimran Rai {
4911200a7d9SSimran Rai void __iomem *audio_io;
4921200a7d9SSimran Rai u32 port;
4931200a7d9SSimran Rai u32 esr_status2, esr_status4;
4941200a7d9SSimran Rai
4951200a7d9SSimran Rai audio_io = cygaud->audio;
4961200a7d9SSimran Rai
4971200a7d9SSimran Rai /*
4981200a7d9SSimran Rai * ESR status gets updates with/without interrupts enabled.
4991200a7d9SSimran Rai * So, check the ESR mask, which provides interrupt enable/
5001200a7d9SSimran Rai * disable status and use it to determine which ESR status
5011200a7d9SSimran Rai * should be serviced.
5021200a7d9SSimran Rai */
5031200a7d9SSimran Rai esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
5041200a7d9SSimran Rai esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
5051200a7d9SSimran Rai esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
5061200a7d9SSimran Rai esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
5071200a7d9SSimran Rai
5081200a7d9SSimran Rai for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
5091200a7d9SSimran Rai u32 esrmask = BIT(port);
5101200a7d9SSimran Rai
5111200a7d9SSimran Rai /*
5121200a7d9SSimran Rai * Ringbuffer or FIFO overflow
5131200a7d9SSimran Rai * If we get this interrupt then, it is also true that we have
5141200a7d9SSimran Rai * not yet responded to the fullmark interrupt.
5151200a7d9SSimran Rai * Log a debug message. The fullmark handler below will
5161200a7d9SSimran Rai * handle getting everything going again.
5171200a7d9SSimran Rai */
5181200a7d9SSimran Rai if (esrmask & esr_status2)
5191200a7d9SSimran Rai dev_dbg(cygaud->dev,
5201200a7d9SSimran Rai "Overflow: esr2=0x%x\n", esr_status2);
5211200a7d9SSimran Rai
5221200a7d9SSimran Rai if (esrmask & esr_status4) {
5231200a7d9SSimran Rai struct snd_pcm_substream *capstr;
5241200a7d9SSimran Rai
5251200a7d9SSimran Rai capstr = cygaud->portinfo[port].capture_stream;
5261200a7d9SSimran Rai cygnus_pcm_period_elapsed(capstr);
5271200a7d9SSimran Rai }
5281200a7d9SSimran Rai }
5291200a7d9SSimran Rai
5301200a7d9SSimran Rai writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
5311200a7d9SSimran Rai writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
5321200a7d9SSimran Rai /* Rearm fullmark logic by writing 1 to the correct bit */
5331200a7d9SSimran Rai writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
5341200a7d9SSimran Rai }
5351200a7d9SSimran Rai
cygnus_dma_irq(int irq,void * data)5361200a7d9SSimran Rai static irqreturn_t cygnus_dma_irq(int irq, void *data)
5371200a7d9SSimran Rai {
5381200a7d9SSimran Rai u32 r5_status;
5391200a7d9SSimran Rai struct cygnus_audio *cygaud = data;
5401200a7d9SSimran Rai
5411200a7d9SSimran Rai /*
5421200a7d9SSimran Rai * R5 status bits Description
5431200a7d9SSimran Rai * 0 ESR0 (playback FIFO interrupt)
5441200a7d9SSimran Rai * 1 ESR1 (playback rbuf interrupt)
5451200a7d9SSimran Rai * 2 ESR2 (capture rbuf interrupt)
5461200a7d9SSimran Rai * 3 ESR3 (Freemark play. interrupt)
5471200a7d9SSimran Rai * 4 ESR4 (Fullmark capt. interrupt)
5481200a7d9SSimran Rai */
5491200a7d9SSimran Rai r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
5501200a7d9SSimran Rai
5511200a7d9SSimran Rai if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
5521200a7d9SSimran Rai return IRQ_NONE;
5531200a7d9SSimran Rai
5541200a7d9SSimran Rai /* If playback interrupt happened */
5551200a7d9SSimran Rai if (ANY_PLAYBACK_IRQ & r5_status) {
5561200a7d9SSimran Rai handle_playback_irq(cygaud);
5571200a7d9SSimran Rai writel(ANY_PLAYBACK_IRQ & r5_status,
5581200a7d9SSimran Rai cygaud->audio + INTH_R5F_CLEAR_OFFSET);
5591200a7d9SSimran Rai }
5601200a7d9SSimran Rai
5611200a7d9SSimran Rai /* If capture interrupt happened */
5621200a7d9SSimran Rai if (ANY_CAPTURE_IRQ & r5_status) {
5631200a7d9SSimran Rai handle_capture_irq(cygaud);
5641200a7d9SSimran Rai writel(ANY_CAPTURE_IRQ & r5_status,
5651200a7d9SSimran Rai cygaud->audio + INTH_R5F_CLEAR_OFFSET);
5661200a7d9SSimran Rai }
5671200a7d9SSimran Rai
5681200a7d9SSimran Rai return IRQ_HANDLED;
5691200a7d9SSimran Rai }
5701200a7d9SSimran Rai
cygnus_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)571dc06bd0fSKuninori Morimoto static int cygnus_pcm_open(struct snd_soc_component *component,
572dc06bd0fSKuninori Morimoto struct snd_pcm_substream *substream)
5731200a7d9SSimran Rai {
5740359c834SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5751200a7d9SSimran Rai struct snd_pcm_runtime *runtime = substream->runtime;
5761200a7d9SSimran Rai struct cygnus_aio_port *aio;
5771200a7d9SSimran Rai int ret;
5781200a7d9SSimran Rai
5791200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
5801200a7d9SSimran Rai if (!aio)
5811200a7d9SSimran Rai return -ENODEV;
5821200a7d9SSimran Rai
583fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
5841200a7d9SSimran Rai
5851200a7d9SSimran Rai snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
5861200a7d9SSimran Rai
5871200a7d9SSimran Rai ret = snd_pcm_hw_constraint_step(runtime, 0,
5881200a7d9SSimran Rai SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
5891200a7d9SSimran Rai if (ret < 0)
5901200a7d9SSimran Rai return ret;
5911200a7d9SSimran Rai
5921200a7d9SSimran Rai ret = snd_pcm_hw_constraint_step(runtime, 0,
5931200a7d9SSimran Rai SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
5941200a7d9SSimran Rai if (ret < 0)
5951200a7d9SSimran Rai return ret;
5961200a7d9SSimran Rai /*
5971200a7d9SSimran Rai * Keep track of which substream belongs to which port.
5981200a7d9SSimran Rai * This info is needed by snd_pcm_period_elapsed() in irq_handler
5991200a7d9SSimran Rai */
6001200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6011200a7d9SSimran Rai aio->play_stream = substream;
6021200a7d9SSimran Rai else
6031200a7d9SSimran Rai aio->capture_stream = substream;
6041200a7d9SSimran Rai
6051200a7d9SSimran Rai return 0;
6061200a7d9SSimran Rai }
6071200a7d9SSimran Rai
cygnus_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)608dc06bd0fSKuninori Morimoto static int cygnus_pcm_close(struct snd_soc_component *component,
609dc06bd0fSKuninori Morimoto struct snd_pcm_substream *substream)
6101200a7d9SSimran Rai {
6110359c834SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6121200a7d9SSimran Rai struct cygnus_aio_port *aio;
6131200a7d9SSimran Rai
6141200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
6151200a7d9SSimran Rai
616fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
6171200a7d9SSimran Rai
6181200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6191200a7d9SSimran Rai aio->play_stream = NULL;
6201200a7d9SSimran Rai else
6211200a7d9SSimran Rai aio->capture_stream = NULL;
6221200a7d9SSimran Rai
6231200a7d9SSimran Rai if (!aio->play_stream && !aio->capture_stream)
624fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum);
6251200a7d9SSimran Rai
6261200a7d9SSimran Rai return 0;
6271200a7d9SSimran Rai }
6281200a7d9SSimran Rai
cygnus_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)629dc06bd0fSKuninori Morimoto static int cygnus_pcm_prepare(struct snd_soc_component *component,
630dc06bd0fSKuninori Morimoto struct snd_pcm_substream *substream)
6311200a7d9SSimran Rai {
6320359c834SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6331200a7d9SSimran Rai struct snd_pcm_runtime *runtime = substream->runtime;
6341200a7d9SSimran Rai struct cygnus_aio_port *aio;
6351200a7d9SSimran Rai unsigned long bufsize, periodsize;
6361200a7d9SSimran Rai bool is_play;
6371200a7d9SSimran Rai u32 start;
6381200a7d9SSimran Rai struct ringbuf_regs *p_rbuf = NULL;
6391200a7d9SSimran Rai
6401200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
641fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
6421200a7d9SSimran Rai
6431200a7d9SSimran Rai bufsize = snd_pcm_lib_buffer_bytes(substream);
6441200a7d9SSimran Rai periodsize = snd_pcm_lib_period_bytes(substream);
6451200a7d9SSimran Rai
646fc392364SKuninori Morimoto dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
6471200a7d9SSimran Rai __func__, bufsize, periodsize);
6481200a7d9SSimran Rai
6491200a7d9SSimran Rai configure_ringbuf_regs(substream);
6501200a7d9SSimran Rai
6511200a7d9SSimran Rai p_rbuf = get_ringbuf(substream);
6521200a7d9SSimran Rai
6531200a7d9SSimran Rai start = runtime->dma_addr;
6541200a7d9SSimran Rai
6551200a7d9SSimran Rai is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
6561200a7d9SSimran Rai
6571200a7d9SSimran Rai ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
6581200a7d9SSimran Rai periodsize, bufsize);
6591200a7d9SSimran Rai
660e1fb714bSHariprasad Kelam return 0;
6611200a7d9SSimran Rai }
6621200a7d9SSimran Rai
cygnus_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)663dc06bd0fSKuninori Morimoto static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
664dc06bd0fSKuninori Morimoto struct snd_pcm_substream *substream)
6651200a7d9SSimran Rai {
6661200a7d9SSimran Rai struct cygnus_aio_port *aio;
6671200a7d9SSimran Rai unsigned int res = 0, cur = 0, base = 0;
6681200a7d9SSimran Rai struct ringbuf_regs *p_rbuf = NULL;
6691200a7d9SSimran Rai
6701200a7d9SSimran Rai aio = cygnus_dai_get_dma_data(substream);
6711200a7d9SSimran Rai
6721200a7d9SSimran Rai /*
6731200a7d9SSimran Rai * Get the offset of the current read (for playack) or write
6741200a7d9SSimran Rai * index (for capture). Report this value back to the asoc framework.
6751200a7d9SSimran Rai */
6761200a7d9SSimran Rai p_rbuf = get_ringbuf(substream);
6771200a7d9SSimran Rai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6781200a7d9SSimran Rai cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
6791200a7d9SSimran Rai else
6801200a7d9SSimran Rai cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
6811200a7d9SSimran Rai
6821200a7d9SSimran Rai base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
6831200a7d9SSimran Rai
6841200a7d9SSimran Rai /*
6851200a7d9SSimran Rai * Mask off the MSB of the rdaddr,wraddr and baseaddr
6861200a7d9SSimran Rai * since MSB is not part of the address
6871200a7d9SSimran Rai */
6881200a7d9SSimran Rai res = (cur & 0x7fffffff) - (base & 0x7fffffff);
6891200a7d9SSimran Rai
6901200a7d9SSimran Rai return bytes_to_frames(substream->runtime, res);
6911200a7d9SSimran Rai }
6921200a7d9SSimran Rai
cygnus_dma_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)693dc06bd0fSKuninori Morimoto static int cygnus_dma_new(struct snd_soc_component *component,
694dc06bd0fSKuninori Morimoto struct snd_soc_pcm_runtime *rtd)
6951200a7d9SSimran Rai {
6965ac813c8SLars-Peter Clausen size_t size = cygnus_pcm_hw.buffer_bytes_max;
6971200a7d9SSimran Rai struct snd_card *card = rtd->card->snd_card;
6981200a7d9SSimran Rai
6991200a7d9SSimran Rai if (!card->dev->dma_mask)
7001200a7d9SSimran Rai card->dev->dma_mask = &cygnus_dma_dmamask;
7011200a7d9SSimran Rai if (!card->dev->coherent_dma_mask)
7021200a7d9SSimran Rai card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
7031200a7d9SSimran Rai
7045ac813c8SLars-Peter Clausen snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
7055ac813c8SLars-Peter Clausen card->dev, size, size);
7061200a7d9SSimran Rai
7071200a7d9SSimran Rai return 0;
7081200a7d9SSimran Rai }
7091200a7d9SSimran Rai
710540b925aSKuninori Morimoto static struct snd_soc_component_driver cygnus_soc_platform = {
711dc06bd0fSKuninori Morimoto .open = cygnus_pcm_open,
712dc06bd0fSKuninori Morimoto .close = cygnus_pcm_close,
713dc06bd0fSKuninori Morimoto .prepare = cygnus_pcm_prepare,
714dc06bd0fSKuninori Morimoto .trigger = cygnus_pcm_trigger,
715dc06bd0fSKuninori Morimoto .pointer = cygnus_pcm_pointer,
716dc06bd0fSKuninori Morimoto .pcm_construct = cygnus_dma_new,
7171200a7d9SSimran Rai };
7181200a7d9SSimran Rai
cygnus_soc_platform_register(struct device * dev,struct cygnus_audio * cygaud)7191200a7d9SSimran Rai int cygnus_soc_platform_register(struct device *dev,
7201200a7d9SSimran Rai struct cygnus_audio *cygaud)
7211200a7d9SSimran Rai {
72214db5499STang Bin int rc;
7231200a7d9SSimran Rai
7241200a7d9SSimran Rai dev_dbg(dev, "%s Enter\n", __func__);
7251200a7d9SSimran Rai
7261200a7d9SSimran Rai rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
7271200a7d9SSimran Rai IRQF_SHARED, "cygnus-audio", cygaud);
7281200a7d9SSimran Rai if (rc) {
7291200a7d9SSimran Rai dev_err(dev, "%s request_irq error %d\n", __func__, rc);
7301200a7d9SSimran Rai return rc;
7311200a7d9SSimran Rai }
7321200a7d9SSimran Rai
733540b925aSKuninori Morimoto rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
734540b925aSKuninori Morimoto NULL, 0);
7351200a7d9SSimran Rai if (rc) {
7361200a7d9SSimran Rai dev_err(dev, "%s failed\n", __func__);
7371200a7d9SSimran Rai return rc;
7381200a7d9SSimran Rai }
7391200a7d9SSimran Rai
7401200a7d9SSimran Rai return 0;
7411200a7d9SSimran Rai }
7421200a7d9SSimran Rai
cygnus_soc_platform_unregister(struct device * dev)7431200a7d9SSimran Rai int cygnus_soc_platform_unregister(struct device *dev)
7441200a7d9SSimran Rai {
7451200a7d9SSimran Rai return 0;
7461200a7d9SSimran Rai }
7471200a7d9SSimran Rai
7481200a7d9SSimran Rai MODULE_LICENSE("GPL v2");
7491200a7d9SSimran Rai MODULE_AUTHOR("Broadcom");
7501200a7d9SSimran Rai MODULE_DESCRIPTION("Cygnus ASoC PCM module");
751