18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/sound/soc/ep93xx-i2s.c 48c2ecf20Sopenharmony_ci * EP93xx I2S driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Ryan Mallon 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the original driver by: 98c2ecf20Sopenharmony_ci * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail> 108c2ecf20Sopenharmony_ci * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 238c2ecf20Sopenharmony_ci#include <sound/initval.h> 248c2ecf20Sopenharmony_ci#include <sound/soc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-ep93xx.h> 278c2ecf20Sopenharmony_ci#include <linux/soc/cirrus/ep93xx.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "ep93xx-pcm.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXCLKCFG 0x00 328c2ecf20Sopenharmony_ci#define EP93XX_I2S_RXCLKCFG 0x04 338c2ecf20Sopenharmony_ci#define EP93XX_I2S_GLSTS 0x08 348c2ecf20Sopenharmony_ci#define EP93XX_I2S_GLCTRL 0x0C 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define EP93XX_I2S_I2STX0LFT 0x10 378c2ecf20Sopenharmony_ci#define EP93XX_I2S_I2STX0RT 0x14 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXLINCTRLDATA 0x28 408c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXCTRL 0x2C 418c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXWRDLEN 0x30 428c2ecf20Sopenharmony_ci#define EP93XX_I2S_TX0EN 0x34 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define EP93XX_I2S_RXLINCTRLDATA 0x58 458c2ecf20Sopenharmony_ci#define EP93XX_I2S_RXCTRL 0x5C 468c2ecf20Sopenharmony_ci#define EP93XX_I2S_RXWRDLEN 0x60 478c2ecf20Sopenharmony_ci#define EP93XX_I2S_RX0EN 0x64 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define EP93XX_I2S_WRDLEN_16 (0 << 0) 508c2ecf20Sopenharmony_ci#define EP93XX_I2S_WRDLEN_24 (1 << 0) 518c2ecf20Sopenharmony_ci#define EP93XX_I2S_WRDLEN_32 (2 << 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define EP93XX_I2S_RXLINCTRLDATA_R_JUST BIT(1) /* Right justify */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXLINCTRLDATA_R_JUST BIT(2) /* Right justify */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Transmit empty interrupt level select: 598c2ecf20Sopenharmony_ci * 0 - Generate interrupt when FIFO is half empty 608c2ecf20Sopenharmony_ci * 1 - Generate interrupt when FIFO is empty 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXCTRL_TXEMPTY_LVL BIT(0) 638c2ecf20Sopenharmony_ci#define EP93XX_I2S_TXCTRL_TXUFIE BIT(1) /* Transmit interrupt enable */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */ 668c2ecf20Sopenharmony_ci#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */ 678c2ecf20Sopenharmony_ci#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */ 688c2ecf20Sopenharmony_ci#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */ 698c2ecf20Sopenharmony_ci#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define EP93XX_I2S_GLSTS_TX0_FIFO_FULL BIT(12) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct ep93xx_i2s_info { 748c2ecf20Sopenharmony_ci struct clk *mclk; 758c2ecf20Sopenharmony_ci struct clk *sclk; 768c2ecf20Sopenharmony_ci struct clk *lrclk; 778c2ecf20Sopenharmony_ci void __iomem *regs; 788c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_rx; 798c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_tx; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct ep93xx_dma_data ep93xx_i2s_dma_data[] = { 838c2ecf20Sopenharmony_ci [SNDRV_PCM_STREAM_PLAYBACK] = { 848c2ecf20Sopenharmony_ci .name = "i2s-pcm-out", 858c2ecf20Sopenharmony_ci .port = EP93XX_DMA_I2S1, 868c2ecf20Sopenharmony_ci .direction = DMA_MEM_TO_DEV, 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci [SNDRV_PCM_STREAM_CAPTURE] = { 898c2ecf20Sopenharmony_ci .name = "i2s-pcm-in", 908c2ecf20Sopenharmony_ci .port = EP93XX_DMA_I2S1, 918c2ecf20Sopenharmony_ci .direction = DMA_DEV_TO_MEM, 928c2ecf20Sopenharmony_ci }, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info, 968c2ecf20Sopenharmony_ci unsigned reg, unsigned val) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci __raw_writel(val, info->regs + reg); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info, 1028c2ecf20Sopenharmony_ci unsigned reg) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return __raw_readl(info->regs + reg); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned base_reg; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && 1128c2ecf20Sopenharmony_ci (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { 1138c2ecf20Sopenharmony_ci /* Enable clocks */ 1148c2ecf20Sopenharmony_ci clk_enable(info->mclk); 1158c2ecf20Sopenharmony_ci clk_enable(info->sclk); 1168c2ecf20Sopenharmony_ci clk_enable(info->lrclk); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Enable i2s */ 1198c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Enable fifo */ 1238c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 1248c2ecf20Sopenharmony_ci base_reg = EP93XX_I2S_TX0EN; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci base_reg = EP93XX_I2S_RX0EN; 1278c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, base_reg, 1); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Enable TX IRQs (FIFO empty or underflow) */ 1308c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && 1318c2ecf20Sopenharmony_ci stream == SNDRV_PCM_STREAM_PLAYBACK) 1328c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 1338c2ecf20Sopenharmony_ci EP93XX_I2S_TXCTRL_TXEMPTY_LVL | 1348c2ecf20Sopenharmony_ci EP93XX_I2S_TXCTRL_TXUFIE); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci unsigned base_reg; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Disable IRQs */ 1428c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && 1438c2ecf20Sopenharmony_ci stream == SNDRV_PCM_STREAM_PLAYBACK) 1448c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 0); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Disable fifo */ 1478c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 1488c2ecf20Sopenharmony_ci base_reg = EP93XX_I2S_TX0EN; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci base_reg = EP93XX_I2S_RX0EN; 1518c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, base_reg, 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && 1548c2ecf20Sopenharmony_ci (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { 1558c2ecf20Sopenharmony_ci /* Disable i2s */ 1568c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Disable clocks */ 1598c2ecf20Sopenharmony_ci clk_disable(info->lrclk); 1608c2ecf20Sopenharmony_ci clk_disable(info->sclk); 1618c2ecf20Sopenharmony_ci clk_disable(info->mclk); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * According to documentation I2S controller can handle underflow conditions 1678c2ecf20Sopenharmony_ci * just fine, but in reality the state machine is sometimes confused so that 1688c2ecf20Sopenharmony_ci * the whole stream is shifted by one byte. The watchdog below disables the TX 1698c2ecf20Sopenharmony_ci * FIFO, fills the buffer with zeroes and re-enables the FIFO. State machine 1708c2ecf20Sopenharmony_ci * is being reset and by filling the buffer we get some time before next 1718c2ecf20Sopenharmony_ci * underflow happens. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic irqreturn_t ep93xx_i2s_interrupt(int irq, void *dev_id) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = dev_id; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Disable FIFO */ 1788c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 0); 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Fill TX FIFO with zeroes, this way we can defer next IRQs as much as 1818c2ecf20Sopenharmony_ci * possible and get more time for DMA to catch up. Actually there are 1828c2ecf20Sopenharmony_ci * only 8 samples in this FIFO, so even on 8kHz maximum deferral here is 1838c2ecf20Sopenharmony_ci * 1ms. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci while (!(ep93xx_i2s_read_reg(info, EP93XX_I2S_GLSTS) & 1868c2ecf20Sopenharmony_ci EP93XX_I2S_GLSTS_TX0_FIFO_FULL)) { 1878c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0LFT, 0); 1888c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0RT, 0); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci /* Re-enable FIFO */ 1918c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 1); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci info->dma_params_tx.filter_data = 2018c2ecf20Sopenharmony_ci &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; 2028c2ecf20Sopenharmony_ci info->dma_params_rx.filter_data = 2038c2ecf20Sopenharmony_ci &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dai->playback_dma_data = &info->dma_params_tx; 2068c2ecf20Sopenharmony_ci dai->capture_dma_data = &info->dma_params_rx; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream, 2128c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ep93xx_i2s_disable(info, substream->stream); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, 2208c2ecf20Sopenharmony_ci unsigned int fmt) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai); 2238c2ecf20Sopenharmony_ci unsigned int clk_cfg; 2248c2ecf20Sopenharmony_ci unsigned int txlin_ctrl = 0; 2258c2ecf20Sopenharmony_ci unsigned int rxlin_ctrl = 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2308c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2318c2ecf20Sopenharmony_ci clk_cfg |= EP93XX_I2S_CLKCFG_REL; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 2358c2ecf20Sopenharmony_ci clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 2398c2ecf20Sopenharmony_ci clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; 2408c2ecf20Sopenharmony_ci rxlin_ctrl |= EP93XX_I2S_RXLINCTRLDATA_R_JUST; 2418c2ecf20Sopenharmony_ci txlin_ctrl |= EP93XX_I2S_TXLINCTRLDATA_R_JUST; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci default: 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 2498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 2508c2ecf20Sopenharmony_ci /* CPU is master */ 2518c2ecf20Sopenharmony_ci clk_cfg |= EP93XX_I2S_CLKCFG_MASTER; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 2558c2ecf20Sopenharmony_ci /* Codec is master */ 2568c2ecf20Sopenharmony_ci clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci return -EINVAL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 2648c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 2658c2ecf20Sopenharmony_ci /* Negative bit clock, lrclk low on left word */ 2668c2ecf20Sopenharmony_ci clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 2708c2ecf20Sopenharmony_ci /* Negative bit clock, lrclk low on right word */ 2718c2ecf20Sopenharmony_ci clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP; 2728c2ecf20Sopenharmony_ci clk_cfg |= EP93XX_I2S_CLKCFG_LRS; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 2768c2ecf20Sopenharmony_ci /* Positive bit clock, lrclk low on left word */ 2778c2ecf20Sopenharmony_ci clk_cfg |= EP93XX_I2S_CLKCFG_CKP; 2788c2ecf20Sopenharmony_ci clk_cfg &= ~EP93XX_I2S_CLKCFG_LRS; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 2828c2ecf20Sopenharmony_ci /* Positive bit clock, lrclk low on right word */ 2838c2ecf20Sopenharmony_ci clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Write new register values */ 2888c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg); 2898c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg); 2908c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, rxlin_ctrl); 2918c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, txlin_ctrl); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, 2968c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 2978c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); 3008c2ecf20Sopenharmony_ci unsigned word_len, div, sdiv, lrdiv; 3018c2ecf20Sopenharmony_ci int err; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci switch (params_format(params)) { 3048c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 3058c2ecf20Sopenharmony_ci word_len = EP93XX_I2S_WRDLEN_16; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 3098c2ecf20Sopenharmony_ci word_len = EP93XX_I2S_WRDLEN_24; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 3138c2ecf20Sopenharmony_ci word_len = EP93XX_I2S_WRDLEN_32; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci default: 3178c2ecf20Sopenharmony_ci return -EINVAL; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3218c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len); 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * EP93xx I2S module can be setup so SCLK / LRCLK value can be 3278c2ecf20Sopenharmony_ci * 32, 64, 128. MCLK / SCLK value can be 2 and 4. 3288c2ecf20Sopenharmony_ci * We set LRCLK equal to `rate' and minimum SCLK / LRCLK 3298c2ecf20Sopenharmony_ci * value is 64, because our sample size is 32 bit * 2 channels. 3308c2ecf20Sopenharmony_ci * I2S standard permits us to transmit more bits than 3318c2ecf20Sopenharmony_ci * the codec uses. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci div = clk_get_rate(info->mclk) / params_rate(params); 3348c2ecf20Sopenharmony_ci sdiv = 4; 3358c2ecf20Sopenharmony_ci if (div > (256 + 512) / 2) { 3368c2ecf20Sopenharmony_ci lrdiv = 128; 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci lrdiv = 64; 3398c2ecf20Sopenharmony_ci if (div < (128 + 256) / 2) 3408c2ecf20Sopenharmony_ci sdiv = 2; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv); 3448c2ecf20Sopenharmony_ci if (err) 3458c2ecf20Sopenharmony_ci return err; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv); 3488c2ecf20Sopenharmony_ci if (err) 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ep93xx_i2s_enable(info, substream->stream); 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, 3568c2ecf20Sopenharmony_ci unsigned int freq, int dir) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (dir == SND_SOC_CLOCK_IN || clk_id != 0) 3618c2ecf20Sopenharmony_ci return -EINVAL; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return clk_set_rate(info->mclk, freq); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3678c2ecf20Sopenharmony_cistatic int ep93xx_i2s_suspend(struct snd_soc_component *component) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); 3758c2ecf20Sopenharmony_ci ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int ep93xx_i2s_resume(struct snd_soc_component *component) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); 3888c2ecf20Sopenharmony_ci ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci#else 3938c2ecf20Sopenharmony_ci#define ep93xx_i2s_suspend NULL 3948c2ecf20Sopenharmony_ci#define ep93xx_i2s_resume NULL 3958c2ecf20Sopenharmony_ci#endif 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { 3988c2ecf20Sopenharmony_ci .shutdown = ep93xx_i2s_shutdown, 3998c2ecf20Sopenharmony_ci .hw_params = ep93xx_i2s_hw_params, 4008c2ecf20Sopenharmony_ci .set_sysclk = ep93xx_i2s_set_sysclk, 4018c2ecf20Sopenharmony_ci .set_fmt = ep93xx_i2s_set_dai_fmt, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver ep93xx_i2s_dai = { 4078c2ecf20Sopenharmony_ci .symmetric_rates= 1, 4088c2ecf20Sopenharmony_ci .probe = ep93xx_i2s_dai_probe, 4098c2ecf20Sopenharmony_ci .playback = { 4108c2ecf20Sopenharmony_ci .channels_min = 2, 4118c2ecf20Sopenharmony_ci .channels_max = 2, 4128c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 4138c2ecf20Sopenharmony_ci .formats = EP93XX_I2S_FORMATS, 4148c2ecf20Sopenharmony_ci }, 4158c2ecf20Sopenharmony_ci .capture = { 4168c2ecf20Sopenharmony_ci .channels_min = 2, 4178c2ecf20Sopenharmony_ci .channels_max = 2, 4188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 4198c2ecf20Sopenharmony_ci .formats = EP93XX_I2S_FORMATS, 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci .ops = &ep93xx_i2s_dai_ops, 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver ep93xx_i2s_component = { 4258c2ecf20Sopenharmony_ci .name = "ep93xx-i2s", 4268c2ecf20Sopenharmony_ci .suspend = ep93xx_i2s_suspend, 4278c2ecf20Sopenharmony_ci .resume = ep93xx_i2s_resume, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int ep93xx_i2s_probe(struct platform_device *pdev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info; 4338c2ecf20Sopenharmony_ci int err; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 4368c2ecf20Sopenharmony_ci if (!info) 4378c2ecf20Sopenharmony_ci return -ENOMEM; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci info->regs = devm_platform_ioremap_resource(pdev, 0); 4408c2ecf20Sopenharmony_ci if (IS_ERR(info->regs)) 4418c2ecf20Sopenharmony_ci return PTR_ERR(info->regs); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) { 4448c2ecf20Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 4458c2ecf20Sopenharmony_ci if (irq <= 0) 4468c2ecf20Sopenharmony_ci return irq < 0 ? irq : -ENODEV; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, irq, ep93xx_i2s_interrupt, 0, 4498c2ecf20Sopenharmony_ci pdev->name, info); 4508c2ecf20Sopenharmony_ci if (err) 4518c2ecf20Sopenharmony_ci return err; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci info->mclk = clk_get(&pdev->dev, "mclk"); 4558c2ecf20Sopenharmony_ci if (IS_ERR(info->mclk)) { 4568c2ecf20Sopenharmony_ci err = PTR_ERR(info->mclk); 4578c2ecf20Sopenharmony_ci goto fail; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci info->sclk = clk_get(&pdev->dev, "sclk"); 4618c2ecf20Sopenharmony_ci if (IS_ERR(info->sclk)) { 4628c2ecf20Sopenharmony_ci err = PTR_ERR(info->sclk); 4638c2ecf20Sopenharmony_ci goto fail_put_mclk; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci info->lrclk = clk_get(&pdev->dev, "lrclk"); 4678c2ecf20Sopenharmony_ci if (IS_ERR(info->lrclk)) { 4688c2ecf20Sopenharmony_ci err = PTR_ERR(info->lrclk); 4698c2ecf20Sopenharmony_ci goto fail_put_sclk; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, info); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci err = devm_snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component, 4758c2ecf20Sopenharmony_ci &ep93xx_i2s_dai, 1); 4768c2ecf20Sopenharmony_ci if (err) 4778c2ecf20Sopenharmony_ci goto fail_put_lrclk; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci err = devm_ep93xx_pcm_platform_register(&pdev->dev); 4808c2ecf20Sopenharmony_ci if (err) 4818c2ecf20Sopenharmony_ci goto fail_put_lrclk; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cifail_put_lrclk: 4868c2ecf20Sopenharmony_ci clk_put(info->lrclk); 4878c2ecf20Sopenharmony_cifail_put_sclk: 4888c2ecf20Sopenharmony_ci clk_put(info->sclk); 4898c2ecf20Sopenharmony_cifail_put_mclk: 4908c2ecf20Sopenharmony_ci clk_put(info->mclk); 4918c2ecf20Sopenharmony_cifail: 4928c2ecf20Sopenharmony_ci return err; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int ep93xx_i2s_remove(struct platform_device *pdev) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci clk_put(info->lrclk); 5008c2ecf20Sopenharmony_ci clk_put(info->sclk); 5018c2ecf20Sopenharmony_ci clk_put(info->mclk); 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct platform_driver ep93xx_i2s_driver = { 5068c2ecf20Sopenharmony_ci .probe = ep93xx_i2s_probe, 5078c2ecf20Sopenharmony_ci .remove = ep93xx_i2s_remove, 5088c2ecf20Sopenharmony_ci .driver = { 5098c2ecf20Sopenharmony_ci .name = "ep93xx-i2s", 5108c2ecf20Sopenharmony_ci }, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cimodule_platform_driver(ep93xx_i2s_driver); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-i2s"); 5168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ryan Mallon"); 5178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EP93XX I2S driver"); 5188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 519