18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/timer.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 238c2ecf20Sopenharmony_ci#include <sound/soc.h> 248c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "cygnus-ssp.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Register offset needed for ASoC PCM module */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define INTH_R5F_STATUS_OFFSET 0x040 318c2ecf20Sopenharmony_ci#define INTH_R5F_CLEAR_OFFSET 0x048 328c2ecf20Sopenharmony_ci#define INTH_R5F_MASK_SET_OFFSET 0x050 338c2ecf20Sopenharmony_ci#define INTH_R5F_MASK_CLEAR_OFFSET 0x054 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define BF_REARM_FREE_MARK_OFFSET 0x344 368c2ecf20Sopenharmony_ci#define BF_REARM_FULL_MARK_OFFSET 0x348 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Ring Buffer Ctrl Regs --- Start */ 398c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */ 408c2ecf20Sopenharmony_ci#define SRC_RBUF_0_RDADDR_OFFSET 0x500 418c2ecf20Sopenharmony_ci#define SRC_RBUF_1_RDADDR_OFFSET 0x518 428c2ecf20Sopenharmony_ci#define SRC_RBUF_2_RDADDR_OFFSET 0x530 438c2ecf20Sopenharmony_ci#define SRC_RBUF_3_RDADDR_OFFSET 0x548 448c2ecf20Sopenharmony_ci#define SRC_RBUF_4_RDADDR_OFFSET 0x560 458c2ecf20Sopenharmony_ci#define SRC_RBUF_5_RDADDR_OFFSET 0x578 468c2ecf20Sopenharmony_ci#define SRC_RBUF_6_RDADDR_OFFSET 0x590 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */ 498c2ecf20Sopenharmony_ci#define SRC_RBUF_0_WRADDR_OFFSET 0x504 508c2ecf20Sopenharmony_ci#define SRC_RBUF_1_WRADDR_OFFSET 0x51c 518c2ecf20Sopenharmony_ci#define SRC_RBUF_2_WRADDR_OFFSET 0x534 528c2ecf20Sopenharmony_ci#define SRC_RBUF_3_WRADDR_OFFSET 0x54c 538c2ecf20Sopenharmony_ci#define SRC_RBUF_4_WRADDR_OFFSET 0x564 548c2ecf20Sopenharmony_ci#define SRC_RBUF_5_WRADDR_OFFSET 0x57c 558c2ecf20Sopenharmony_ci#define SRC_RBUF_6_WRADDR_OFFSET 0x594 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */ 588c2ecf20Sopenharmony_ci#define SRC_RBUF_0_BASEADDR_OFFSET 0x508 598c2ecf20Sopenharmony_ci#define SRC_RBUF_1_BASEADDR_OFFSET 0x520 608c2ecf20Sopenharmony_ci#define SRC_RBUF_2_BASEADDR_OFFSET 0x538 618c2ecf20Sopenharmony_ci#define SRC_RBUF_3_BASEADDR_OFFSET 0x550 628c2ecf20Sopenharmony_ci#define SRC_RBUF_4_BASEADDR_OFFSET 0x568 638c2ecf20Sopenharmony_ci#define SRC_RBUF_5_BASEADDR_OFFSET 0x580 648c2ecf20Sopenharmony_ci#define SRC_RBUF_6_BASEADDR_OFFSET 0x598 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */ 678c2ecf20Sopenharmony_ci#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c 688c2ecf20Sopenharmony_ci#define SRC_RBUF_1_ENDADDR_OFFSET 0x524 698c2ecf20Sopenharmony_ci#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c 708c2ecf20Sopenharmony_ci#define SRC_RBUF_3_ENDADDR_OFFSET 0x554 718c2ecf20Sopenharmony_ci#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c 728c2ecf20Sopenharmony_ci#define SRC_RBUF_5_ENDADDR_OFFSET 0x584 738c2ecf20Sopenharmony_ci#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */ 768c2ecf20Sopenharmony_ci#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510 778c2ecf20Sopenharmony_ci#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528 788c2ecf20Sopenharmony_ci#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540 798c2ecf20Sopenharmony_ci#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558 808c2ecf20Sopenharmony_ci#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570 818c2ecf20Sopenharmony_ci#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588 828c2ecf20Sopenharmony_ci#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */ 858c2ecf20Sopenharmony_ci#define DST_RBUF_0_RDADDR_OFFSET 0x5c0 868c2ecf20Sopenharmony_ci#define DST_RBUF_1_RDADDR_OFFSET 0x5d8 878c2ecf20Sopenharmony_ci#define DST_RBUF_2_RDADDR_OFFSET 0x5f0 888c2ecf20Sopenharmony_ci#define DST_RBUF_3_RDADDR_OFFSET 0x608 898c2ecf20Sopenharmony_ci#define DST_RBUF_4_RDADDR_OFFSET 0x620 908c2ecf20Sopenharmony_ci#define DST_RBUF_5_RDADDR_OFFSET 0x638 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */ 938c2ecf20Sopenharmony_ci#define DST_RBUF_0_WRADDR_OFFSET 0x5c4 948c2ecf20Sopenharmony_ci#define DST_RBUF_1_WRADDR_OFFSET 0x5dc 958c2ecf20Sopenharmony_ci#define DST_RBUF_2_WRADDR_OFFSET 0x5f4 968c2ecf20Sopenharmony_ci#define DST_RBUF_3_WRADDR_OFFSET 0x60c 978c2ecf20Sopenharmony_ci#define DST_RBUF_4_WRADDR_OFFSET 0x624 988c2ecf20Sopenharmony_ci#define DST_RBUF_5_WRADDR_OFFSET 0x63c 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */ 1018c2ecf20Sopenharmony_ci#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8 1028c2ecf20Sopenharmony_ci#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0 1038c2ecf20Sopenharmony_ci#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8 1048c2ecf20Sopenharmony_ci#define DST_RBUF_3_BASEADDR_OFFSET 0x610 1058c2ecf20Sopenharmony_ci#define DST_RBUF_4_BASEADDR_OFFSET 0x628 1068c2ecf20Sopenharmony_ci#define DST_RBUF_5_BASEADDR_OFFSET 0x640 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */ 1098c2ecf20Sopenharmony_ci#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc 1108c2ecf20Sopenharmony_ci#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4 1118c2ecf20Sopenharmony_ci#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc 1128c2ecf20Sopenharmony_ci#define DST_RBUF_3_ENDADDR_OFFSET 0x614 1138c2ecf20Sopenharmony_ci#define DST_RBUF_4_ENDADDR_OFFSET 0x62c 1148c2ecf20Sopenharmony_ci#define DST_RBUF_5_ENDADDR_OFFSET 0x644 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */ 1178c2ecf20Sopenharmony_ci#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0 1188c2ecf20Sopenharmony_ci#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8 1198c2ecf20Sopenharmony_ci#define DST_RBUF_2_FULL_MARK_OFFSET 0x600 1208c2ecf20Sopenharmony_ci#define DST_RBUF_3_FULL_MARK_OFFSET 0x618 1218c2ecf20Sopenharmony_ci#define DST_RBUF_4_FULL_MARK_OFFSET 0x630 1228c2ecf20Sopenharmony_ci#define DST_RBUF_5_FULL_MARK_OFFSET 0x648 1238c2ecf20Sopenharmony_ci/* Ring Buffer Ctrl Regs --- End */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Error Status Regs --- Start */ 1268c2ecf20Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */ 1278c2ecf20Sopenharmony_ci#define ESR0_STATUS_OFFSET 0x900 1288c2ecf20Sopenharmony_ci#define ESR1_STATUS_OFFSET 0x918 1298c2ecf20Sopenharmony_ci#define ESR2_STATUS_OFFSET 0x930 1308c2ecf20Sopenharmony_ci#define ESR3_STATUS_OFFSET 0x948 1318c2ecf20Sopenharmony_ci#define ESR4_STATUS_OFFSET 0x960 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */ 1348c2ecf20Sopenharmony_ci#define ESR0_STATUS_CLR_OFFSET 0x908 1358c2ecf20Sopenharmony_ci#define ESR1_STATUS_CLR_OFFSET 0x920 1368c2ecf20Sopenharmony_ci#define ESR2_STATUS_CLR_OFFSET 0x938 1378c2ecf20Sopenharmony_ci#define ESR3_STATUS_CLR_OFFSET 0x950 1388c2ecf20Sopenharmony_ci#define ESR4_STATUS_CLR_OFFSET 0x968 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */ 1418c2ecf20Sopenharmony_ci#define ESR0_MASK_STATUS_OFFSET 0x90c 1428c2ecf20Sopenharmony_ci#define ESR1_MASK_STATUS_OFFSET 0x924 1438c2ecf20Sopenharmony_ci#define ESR2_MASK_STATUS_OFFSET 0x93c 1448c2ecf20Sopenharmony_ci#define ESR3_MASK_STATUS_OFFSET 0x954 1458c2ecf20Sopenharmony_ci#define ESR4_MASK_STATUS_OFFSET 0x96c 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */ 1488c2ecf20Sopenharmony_ci#define ESR0_MASK_SET_OFFSET 0x910 1498c2ecf20Sopenharmony_ci#define ESR1_MASK_SET_OFFSET 0x928 1508c2ecf20Sopenharmony_ci#define ESR2_MASK_SET_OFFSET 0x940 1518c2ecf20Sopenharmony_ci#define ESR3_MASK_SET_OFFSET 0x958 1528c2ecf20Sopenharmony_ci#define ESR4_MASK_SET_OFFSET 0x970 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */ 1558c2ecf20Sopenharmony_ci#define ESR0_MASK_CLR_OFFSET 0x914 1568c2ecf20Sopenharmony_ci#define ESR1_MASK_CLR_OFFSET 0x92c 1578c2ecf20Sopenharmony_ci#define ESR2_MASK_CLR_OFFSET 0x944 1588c2ecf20Sopenharmony_ci#define ESR3_MASK_CLR_OFFSET 0x95c 1598c2ecf20Sopenharmony_ci#define ESR4_MASK_CLR_OFFSET 0x974 1608c2ecf20Sopenharmony_ci/* Error Status Regs --- End */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */ 1638c2ecf20Sopenharmony_ci#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */ 1648c2ecf20Sopenharmony_ci#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */ 1658c2ecf20Sopenharmony_ci#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */ 1668c2ecf20Sopenharmony_ci#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */ 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Mask for R5F register. Set all relevant interrupt for playback handler */ 1708c2ecf20Sopenharmony_ci#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \ 1718c2ecf20Sopenharmony_ci BIT(R5F_ESR1_SHIFT) | \ 1728c2ecf20Sopenharmony_ci BIT(R5F_ESR3_SHIFT)) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Mask for R5F register. Set all relevant interrupt for capture handler */ 1758c2ecf20Sopenharmony_ci#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT)) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick. 1798c2ecf20Sopenharmony_ci * This number should be a multiple of 256. Minimum value is 256 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci#define PERIOD_BYTES_MIN 0x100 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware cygnus_pcm_hw = { 1848c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 1858c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 1868c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED, 1878c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 1888c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* A period is basically an interrupt */ 1918c2ecf20Sopenharmony_ci .period_bytes_min = PERIOD_BYTES_MIN, 1928c2ecf20Sopenharmony_ci .period_bytes_max = 0x10000, 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* period_min/max gives range of approx interrupts per buffer */ 1958c2ecf20Sopenharmony_ci .periods_min = 2, 1968c2ecf20Sopenharmony_ci .periods_max = 8, 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * maximum buffer size in bytes = period_bytes_max * periods_max 2008c2ecf20Sopenharmony_ci * We allocate this amount of data for each enabled channel 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci .buffer_bytes_max = 4 * 0x8000, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic u64 cygnus_dma_dmamask = DMA_BIT_MASK(32); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct cygnus_aio_port *cygnus_dai_get_dma_data( 2088c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void ringbuf_set_initial(void __iomem *audio_io, 2168c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf, 2178c2ecf20Sopenharmony_ci bool is_playback, 2188c2ecf20Sopenharmony_ci u32 start, 2198c2ecf20Sopenharmony_ci u32 periodsize, 2208c2ecf20Sopenharmony_ci u32 bufsize) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u32 initial_rd; 2238c2ecf20Sopenharmony_ci u32 initial_wr; 2248c2ecf20Sopenharmony_ci u32 end; 2258c2ecf20Sopenharmony_ci u32 fmark_val; /* free or full mark */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci p_rbuf->period_bytes = periodsize; 2288c2ecf20Sopenharmony_ci p_rbuf->buf_size = bufsize; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (is_playback) { 2318c2ecf20Sopenharmony_ci /* Set the pointers to indicate full (flip uppermost bit) */ 2328c2ecf20Sopenharmony_ci initial_rd = start; 2338c2ecf20Sopenharmony_ci initial_wr = initial_rd ^ BIT(31); 2348c2ecf20Sopenharmony_ci } else { 2358c2ecf20Sopenharmony_ci /* Set the pointers to indicate empty */ 2368c2ecf20Sopenharmony_ci initial_wr = start; 2378c2ecf20Sopenharmony_ci initial_rd = initial_wr; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci end = start + bufsize - 1; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * The interrupt will fire when free/full mark is *exceeded* 2448c2ecf20Sopenharmony_ci * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark 2458c2ecf20Sopenharmony_ci * to be PERIOD_BYTES_MIN less than the period size. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci fmark_val = periodsize - PERIOD_BYTES_MIN; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci writel(start, audio_io + p_rbuf->baseaddr); 2508c2ecf20Sopenharmony_ci writel(end, audio_io + p_rbuf->endaddr); 2518c2ecf20Sopenharmony_ci writel(fmark_val, audio_io + p_rbuf->fmark); 2528c2ecf20Sopenharmony_ci writel(initial_rd, audio_io + p_rbuf->rdaddr); 2538c2ecf20Sopenharmony_ci writel(initial_wr, audio_io + p_rbuf->wraddr); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int configure_ringbuf_regs(struct snd_pcm_substream *substream) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 2598c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf; 2608c2ecf20Sopenharmony_ci int status = 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Map the ssp portnum to a set of ring buffers. */ 2658c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2668c2ecf20Sopenharmony_ci p_rbuf = &aio->play_rb_regs; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci switch (aio->portnum) { 2698c2ecf20Sopenharmony_ci case 0: 2708c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(0); 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case 1: 2738c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(2); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case 2: 2768c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(4); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case 3: /* SPDIF */ 2798c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_PLAYBACK(6); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci status = -EINVAL; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci p_rbuf = &aio->capture_rb_regs; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci switch (aio->portnum) { 2888c2ecf20Sopenharmony_ci case 0: 2898c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(0); 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case 1: 2928c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(2); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case 2: 2958c2ecf20Sopenharmony_ci *p_rbuf = RINGBUF_REG_CAPTURE(4); 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci default: 2988c2ecf20Sopenharmony_ci status = -EINVAL; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return status; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 3088c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3138c2ecf20Sopenharmony_ci p_rbuf = &aio->play_rb_regs; 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci p_rbuf = &aio->capture_rb_regs; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return p_rbuf; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void enable_intr(struct snd_pcm_substream *substream) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 3238c2ecf20Sopenharmony_ci u32 clear_mask; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* The port number maps to the bit position to be cleared */ 3288c2ecf20Sopenharmony_ci clear_mask = BIT(aio->portnum); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3318c2ecf20Sopenharmony_ci /* Clear interrupt status before enabling them */ 3328c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET); 3338c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET); 3348c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET); 3358c2ecf20Sopenharmony_ci /* Unmask the interrupts of the given port*/ 3368c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET); 3378c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET); 3388c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci writel(ANY_PLAYBACK_IRQ, 3418c2ecf20Sopenharmony_ci aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 3428c2ecf20Sopenharmony_ci } else { 3438c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET); 3448c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET); 3458c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET); 3468c2ecf20Sopenharmony_ci writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci writel(ANY_CAPTURE_IRQ, 3498c2ecf20Sopenharmony_ci aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void disable_intr(struct snd_pcm_substream *substream) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3578c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 3588c2ecf20Sopenharmony_ci u32 set_mask; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* The port number maps to the bit position to be set */ 3658c2ecf20Sopenharmony_ci set_mask = BIT(aio->portnum); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3688c2ecf20Sopenharmony_ci /* Mask the interrupts of the given port*/ 3698c2ecf20Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET); 3708c2ecf20Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET); 3718c2ecf20Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET); 3728c2ecf20Sopenharmony_ci } else { 3738c2ecf20Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET); 3748c2ecf20Sopenharmony_ci writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int cygnus_pcm_trigger(struct snd_soc_component *component, 3808c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci int ret = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci switch (cmd) { 3858c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3868c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3878c2ecf20Sopenharmony_ci enable_intr(substream); 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3928c2ecf20Sopenharmony_ci disable_intr(substream); 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci default: 3958c2ecf20Sopenharmony_ci ret = -EINVAL; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 4048c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 4058c2ecf20Sopenharmony_ci u32 regval; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci p_rbuf = get_ringbuf(substream); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * If free/full mark interrupt occurs, provide timestamp 4138c2ecf20Sopenharmony_ci * to ALSA and update appropriate idx by period_bytes 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 4188c2ecf20Sopenharmony_ci /* Set the ring buffer to full */ 4198c2ecf20Sopenharmony_ci regval = readl(aio->cygaud->audio + p_rbuf->rdaddr); 4208c2ecf20Sopenharmony_ci regval = regval ^ BIT(31); 4218c2ecf20Sopenharmony_ci writel(regval, aio->cygaud->audio + p_rbuf->wraddr); 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci /* Set the ring buffer to empty */ 4248c2ecf20Sopenharmony_ci regval = readl(aio->cygaud->audio + p_rbuf->wraddr); 4258c2ecf20Sopenharmony_ci writel(regval, aio->cygaud->audio + p_rbuf->rdaddr); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* 4308c2ecf20Sopenharmony_ci * ESR0/1/3 status Description 4318c2ecf20Sopenharmony_ci * 0x1 I2S0_out port caused interrupt 4328c2ecf20Sopenharmony_ci * 0x2 I2S1_out port caused interrupt 4338c2ecf20Sopenharmony_ci * 0x4 I2S2_out port caused interrupt 4348c2ecf20Sopenharmony_ci * 0x8 SPDIF_out port caused interrupt 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic void handle_playback_irq(struct cygnus_audio *cygaud) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci void __iomem *audio_io; 4398c2ecf20Sopenharmony_ci u32 port; 4408c2ecf20Sopenharmony_ci u32 esr_status0, esr_status1, esr_status3; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci audio_io = cygaud->audio; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * ESR status gets updates with/without interrupts enabled. 4468c2ecf20Sopenharmony_ci * So, check the ESR mask, which provides interrupt enable/ 4478c2ecf20Sopenharmony_ci * disable status and use it to determine which ESR status 4488c2ecf20Sopenharmony_ci * should be serviced. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET); 4518c2ecf20Sopenharmony_ci esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET); 4528c2ecf20Sopenharmony_ci esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET); 4538c2ecf20Sopenharmony_ci esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET); 4548c2ecf20Sopenharmony_ci esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET); 4558c2ecf20Sopenharmony_ci esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) { 4588c2ecf20Sopenharmony_ci u32 esrmask = BIT(port); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* 4618c2ecf20Sopenharmony_ci * Ringbuffer or FIFO underflow 4628c2ecf20Sopenharmony_ci * If we get this interrupt then, it is also true that we have 4638c2ecf20Sopenharmony_ci * not yet responded to the freemark interrupt. 4648c2ecf20Sopenharmony_ci * Log a debug message. The freemark handler below will 4658c2ecf20Sopenharmony_ci * handle getting everything going again. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if ((esrmask & esr_status1) || (esrmask & esr_status0)) { 4688c2ecf20Sopenharmony_ci dev_dbg(cygaud->dev, 4698c2ecf20Sopenharmony_ci "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n", 4708c2ecf20Sopenharmony_ci esr_status0, esr_status1, esr_status3); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * Freemark is hit. This is the normal interrupt. 4758c2ecf20Sopenharmony_ci * In typical operation the read and write regs will be equal 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci if (esrmask & esr_status3) { 4788c2ecf20Sopenharmony_ci struct snd_pcm_substream *playstr; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci playstr = cygaud->portinfo[port].play_stream; 4818c2ecf20Sopenharmony_ci cygnus_pcm_period_elapsed(playstr); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Clear ESR interrupt */ 4868c2ecf20Sopenharmony_ci writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET); 4878c2ecf20Sopenharmony_ci writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET); 4888c2ecf20Sopenharmony_ci writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET); 4898c2ecf20Sopenharmony_ci /* Rearm freemark logic by writing 1 to the correct bit */ 4908c2ecf20Sopenharmony_ci writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* 4948c2ecf20Sopenharmony_ci * ESR2/4 status Description 4958c2ecf20Sopenharmony_ci * 0x1 I2S0_in port caused interrupt 4968c2ecf20Sopenharmony_ci * 0x2 I2S1_in port caused interrupt 4978c2ecf20Sopenharmony_ci * 0x4 I2S2_in port caused interrupt 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic void handle_capture_irq(struct cygnus_audio *cygaud) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci void __iomem *audio_io; 5028c2ecf20Sopenharmony_ci u32 port; 5038c2ecf20Sopenharmony_ci u32 esr_status2, esr_status4; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci audio_io = cygaud->audio; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * ESR status gets updates with/without interrupts enabled. 5098c2ecf20Sopenharmony_ci * So, check the ESR mask, which provides interrupt enable/ 5108c2ecf20Sopenharmony_ci * disable status and use it to determine which ESR status 5118c2ecf20Sopenharmony_ci * should be serviced. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET); 5148c2ecf20Sopenharmony_ci esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET); 5158c2ecf20Sopenharmony_ci esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET); 5168c2ecf20Sopenharmony_ci esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) { 5198c2ecf20Sopenharmony_ci u32 esrmask = BIT(port); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * Ringbuffer or FIFO overflow 5238c2ecf20Sopenharmony_ci * If we get this interrupt then, it is also true that we have 5248c2ecf20Sopenharmony_ci * not yet responded to the fullmark interrupt. 5258c2ecf20Sopenharmony_ci * Log a debug message. The fullmark handler below will 5268c2ecf20Sopenharmony_ci * handle getting everything going again. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci if (esrmask & esr_status2) 5298c2ecf20Sopenharmony_ci dev_dbg(cygaud->dev, 5308c2ecf20Sopenharmony_ci "Overflow: esr2=0x%x\n", esr_status2); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (esrmask & esr_status4) { 5338c2ecf20Sopenharmony_ci struct snd_pcm_substream *capstr; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci capstr = cygaud->portinfo[port].capture_stream; 5368c2ecf20Sopenharmony_ci cygnus_pcm_period_elapsed(capstr); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET); 5418c2ecf20Sopenharmony_ci writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET); 5428c2ecf20Sopenharmony_ci /* Rearm fullmark logic by writing 1 to the correct bit */ 5438c2ecf20Sopenharmony_ci writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic irqreturn_t cygnus_dma_irq(int irq, void *data) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci u32 r5_status; 5498c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud = data; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * R5 status bits Description 5538c2ecf20Sopenharmony_ci * 0 ESR0 (playback FIFO interrupt) 5548c2ecf20Sopenharmony_ci * 1 ESR1 (playback rbuf interrupt) 5558c2ecf20Sopenharmony_ci * 2 ESR2 (capture rbuf interrupt) 5568c2ecf20Sopenharmony_ci * 3 ESR3 (Freemark play. interrupt) 5578c2ecf20Sopenharmony_ci * 4 ESR4 (Fullmark capt. interrupt) 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ))) 5628c2ecf20Sopenharmony_ci return IRQ_NONE; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* If playback interrupt happened */ 5658c2ecf20Sopenharmony_ci if (ANY_PLAYBACK_IRQ & r5_status) { 5668c2ecf20Sopenharmony_ci handle_playback_irq(cygaud); 5678c2ecf20Sopenharmony_ci writel(ANY_PLAYBACK_IRQ & r5_status, 5688c2ecf20Sopenharmony_ci cygaud->audio + INTH_R5F_CLEAR_OFFSET); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* If capture interrupt happened */ 5728c2ecf20Sopenharmony_ci if (ANY_CAPTURE_IRQ & r5_status) { 5738c2ecf20Sopenharmony_ci handle_capture_irq(cygaud); 5748c2ecf20Sopenharmony_ci writel(ANY_CAPTURE_IRQ & r5_status, 5758c2ecf20Sopenharmony_ci cygaud->audio + INTH_R5F_CLEAR_OFFSET); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int cygnus_pcm_open(struct snd_soc_component *component, 5828c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 5858c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5868c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 5878c2ecf20Sopenharmony_ci int ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 5908c2ecf20Sopenharmony_ci if (!aio) 5918c2ecf20Sopenharmony_ci return -ENODEV; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 5988c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN); 5998c2ecf20Sopenharmony_ci if (ret < 0) 6008c2ecf20Sopenharmony_ci return ret; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_step(runtime, 0, 6038c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN); 6048c2ecf20Sopenharmony_ci if (ret < 0) 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci /* 6078c2ecf20Sopenharmony_ci * Keep track of which substream belongs to which port. 6088c2ecf20Sopenharmony_ci * This info is needed by snd_pcm_period_elapsed() in irq_handler 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 6118c2ecf20Sopenharmony_ci aio->play_stream = substream; 6128c2ecf20Sopenharmony_ci else 6138c2ecf20Sopenharmony_ci aio->capture_stream = substream; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int cygnus_pcm_close(struct snd_soc_component *component, 6198c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6228c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 6298c2ecf20Sopenharmony_ci aio->play_stream = NULL; 6308c2ecf20Sopenharmony_ci else 6318c2ecf20Sopenharmony_ci aio->capture_stream = NULL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!aio->play_stream && !aio->capture_stream) 6348c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int cygnus_pcm_hw_params(struct snd_soc_component *component, 6408c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 6418c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6448c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6458c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 6488c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 6518c2ecf20Sopenharmony_ci runtime->dma_bytes = params_buffer_bytes(params); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int cygnus_pcm_hw_free(struct snd_soc_component *component, 6578c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6608c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 6638c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, NULL); 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic int cygnus_pcm_prepare(struct snd_soc_component *component, 6708c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6738c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6748c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 6758c2ecf20Sopenharmony_ci unsigned long bufsize, periodsize; 6768c2ecf20Sopenharmony_ci bool is_play; 6778c2ecf20Sopenharmony_ci u32 start; 6788c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 6818c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci bufsize = snd_pcm_lib_buffer_bytes(substream); 6848c2ecf20Sopenharmony_ci periodsize = snd_pcm_lib_period_bytes(substream); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n", 6878c2ecf20Sopenharmony_ci __func__, bufsize, periodsize); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci configure_ringbuf_regs(substream); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci p_rbuf = get_ringbuf(substream); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci start = runtime->dma_addr; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start, 6988c2ecf20Sopenharmony_ci periodsize, bufsize); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component, 7048c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct cygnus_aio_port *aio; 7078c2ecf20Sopenharmony_ci unsigned int res = 0, cur = 0, base = 0; 7088c2ecf20Sopenharmony_ci struct ringbuf_regs *p_rbuf = NULL; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci aio = cygnus_dai_get_dma_data(substream); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* 7138c2ecf20Sopenharmony_ci * Get the offset of the current read (for playack) or write 7148c2ecf20Sopenharmony_ci * index (for capture). Report this value back to the asoc framework. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci p_rbuf = get_ringbuf(substream); 7178c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7188c2ecf20Sopenharmony_ci cur = readl(aio->cygaud->audio + p_rbuf->rdaddr); 7198c2ecf20Sopenharmony_ci else 7208c2ecf20Sopenharmony_ci cur = readl(aio->cygaud->audio + p_rbuf->wraddr); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci base = readl(aio->cygaud->audio + p_rbuf->baseaddr); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* 7258c2ecf20Sopenharmony_ci * Mask off the MSB of the rdaddr,wraddr and baseaddr 7268c2ecf20Sopenharmony_ci * since MSB is not part of the address 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_ci res = (cur & 0x7fffffff) - (base & 0x7fffffff); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, res); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = pcm->streams[stream].substream; 7368c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 7378c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf = &substream->dma_buffer; 7388c2ecf20Sopenharmony_ci size_t size; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci size = cygnus_pcm_hw.buffer_bytes_max; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci buf->dev.type = SNDRV_DMA_TYPE_DEV; 7438c2ecf20Sopenharmony_ci buf->dev.dev = pcm->card->dev; 7448c2ecf20Sopenharmony_ci buf->private_data = NULL; 7458c2ecf20Sopenharmony_ci buf->area = dma_alloc_coherent(pcm->card->dev, size, 7468c2ecf20Sopenharmony_ci &buf->addr, GFP_KERNEL); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: size 0x%zx @ %pK\n", 7498c2ecf20Sopenharmony_ci __func__, size, buf->area); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (!buf->area) { 7528c2ecf20Sopenharmony_ci dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: dma_alloc failed\n", __func__); 7538c2ecf20Sopenharmony_ci return -ENOMEM; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci buf->bytes = size; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return 0; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic void cygnus_dma_free_dma_buffers(struct snd_soc_component *component, 7618c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 7648c2ecf20Sopenharmony_ci struct snd_dma_buffer *buf; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 7678c2ecf20Sopenharmony_ci if (substream) { 7688c2ecf20Sopenharmony_ci buf = &substream->dma_buffer; 7698c2ecf20Sopenharmony_ci if (buf->area) { 7708c2ecf20Sopenharmony_ci dma_free_coherent(pcm->card->dev, buf->bytes, 7718c2ecf20Sopenharmony_ci buf->area, buf->addr); 7728c2ecf20Sopenharmony_ci buf->area = NULL; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 7778c2ecf20Sopenharmony_ci if (substream) { 7788c2ecf20Sopenharmony_ci buf = &substream->dma_buffer; 7798c2ecf20Sopenharmony_ci if (buf->area) { 7808c2ecf20Sopenharmony_ci dma_free_coherent(pcm->card->dev, buf->bytes, 7818c2ecf20Sopenharmony_ci buf->area, buf->addr); 7828c2ecf20Sopenharmony_ci buf->area = NULL; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int cygnus_dma_new(struct snd_soc_component *component, 7888c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 7918c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 7928c2ecf20Sopenharmony_ci int ret; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (!card->dev->dma_mask) 7958c2ecf20Sopenharmony_ci card->dev->dma_mask = &cygnus_dma_dmamask; 7968c2ecf20Sopenharmony_ci if (!card->dev->coherent_dma_mask) 7978c2ecf20Sopenharmony_ci card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 8008c2ecf20Sopenharmony_ci ret = cygnus_pcm_preallocate_dma_buffer(pcm, 8018c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 8028c2ecf20Sopenharmony_ci if (ret) 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 8078c2ecf20Sopenharmony_ci ret = cygnus_pcm_preallocate_dma_buffer(pcm, 8088c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE); 8098c2ecf20Sopenharmony_ci if (ret) { 8108c2ecf20Sopenharmony_ci cygnus_dma_free_dma_buffers(component, pcm); 8118c2ecf20Sopenharmony_ci return ret; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic struct snd_soc_component_driver cygnus_soc_platform = { 8198c2ecf20Sopenharmony_ci .open = cygnus_pcm_open, 8208c2ecf20Sopenharmony_ci .close = cygnus_pcm_close, 8218c2ecf20Sopenharmony_ci .hw_params = cygnus_pcm_hw_params, 8228c2ecf20Sopenharmony_ci .hw_free = cygnus_pcm_hw_free, 8238c2ecf20Sopenharmony_ci .prepare = cygnus_pcm_prepare, 8248c2ecf20Sopenharmony_ci .trigger = cygnus_pcm_trigger, 8258c2ecf20Sopenharmony_ci .pointer = cygnus_pcm_pointer, 8268c2ecf20Sopenharmony_ci .pcm_construct = cygnus_dma_new, 8278c2ecf20Sopenharmony_ci .pcm_destruct = cygnus_dma_free_dma_buffers, 8288c2ecf20Sopenharmony_ci}; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ciint cygnus_soc_platform_register(struct device *dev, 8318c2ecf20Sopenharmony_ci struct cygnus_audio *cygaud) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci int rc = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci dev_dbg(dev, "%s Enter\n", __func__); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq, 8388c2ecf20Sopenharmony_ci IRQF_SHARED, "cygnus-audio", cygaud); 8398c2ecf20Sopenharmony_ci if (rc) { 8408c2ecf20Sopenharmony_ci dev_err(dev, "%s request_irq error %d\n", __func__, rc); 8418c2ecf20Sopenharmony_ci return rc; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform, 8458c2ecf20Sopenharmony_ci NULL, 0); 8468c2ecf20Sopenharmony_ci if (rc) { 8478c2ecf20Sopenharmony_ci dev_err(dev, "%s failed\n", __func__); 8488c2ecf20Sopenharmony_ci return rc; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ciint cygnus_soc_platform_unregister(struct device *dev) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 8608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 8618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cygnus ASoC PCM module"); 862