18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/of_device.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci 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/initval.h> 258c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "jz4740-i2s.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define JZ4740_DMA_TYPE_AIC_TRANSMIT 24 308c2ecf20Sopenharmony_ci#define JZ4740_DMA_TYPE_AIC_RECEIVE 25 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define JZ_REG_AIC_CONF 0x00 338c2ecf20Sopenharmony_ci#define JZ_REG_AIC_CTRL 0x04 348c2ecf20Sopenharmony_ci#define JZ_REG_AIC_I2S_FMT 0x10 358c2ecf20Sopenharmony_ci#define JZ_REG_AIC_FIFO_STATUS 0x14 368c2ecf20Sopenharmony_ci#define JZ_REG_AIC_I2S_STATUS 0x1c 378c2ecf20Sopenharmony_ci#define JZ_REG_AIC_CLK_DIV 0x30 388c2ecf20Sopenharmony_ci#define JZ_REG_AIC_FIFO 0x34 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12) 418c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8) 428c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6) 438c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5) 448c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_I2S BIT(4) 458c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_RESET BIT(3) 468c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2) 478c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1) 488c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_ENABLE BIT(0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 518c2ecf20Sopenharmony_ci#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 528c2ecf20Sopenharmony_ci#define JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 538c2ecf20Sopenharmony_ci#define JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) 568c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) 578c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15) 588c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14) 598c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) 608c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) 618c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) 628c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_TFLUSH BIT(8) 638c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_RFLUSH BIT(7) 648c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) 658c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) 668c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) 678c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3) 688c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2) 698c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1) 708c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19 738c2ecf20Sopenharmony_ci#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) 768c2ecf20Sopenharmony_ci#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) 778c2ecf20Sopenharmony_ci#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) 788c2ecf20Sopenharmony_ci#define JZ_AIC_I2S_FMT_MSB BIT(0) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define JZ_AIC_I2S_STATUS_BUSY BIT(2) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define JZ_AIC_CLK_DIV_MASK 0xf 838c2ecf20Sopenharmony_ci#define I2SDIV_DV_SHIFT 0 848c2ecf20Sopenharmony_ci#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT) 858c2ecf20Sopenharmony_ci#define I2SDIV_IDV_SHIFT 8 868c2ecf20Sopenharmony_ci#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cienum jz47xx_i2s_version { 898c2ecf20Sopenharmony_ci JZ_I2S_JZ4740, 908c2ecf20Sopenharmony_ci JZ_I2S_JZ4760, 918c2ecf20Sopenharmony_ci JZ_I2S_JZ4770, 928c2ecf20Sopenharmony_ci JZ_I2S_JZ4780, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct i2s_soc_info { 968c2ecf20Sopenharmony_ci enum jz47xx_i2s_version version; 978c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci bool shared_fifo_flush; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct jz4740_i2s { 1038c2ecf20Sopenharmony_ci struct resource *mem; 1048c2ecf20Sopenharmony_ci void __iomem *base; 1058c2ecf20Sopenharmony_ci dma_addr_t phys_base; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci struct clk *clk_aic; 1088c2ecf20Sopenharmony_ci struct clk *clk_i2s; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data playback_dma_data; 1118c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data capture_dma_data; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci const struct i2s_soc_info *soc_info; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, 1178c2ecf20Sopenharmony_ci unsigned int reg) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci return readl(i2s->base + reg); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline void jz4740_i2s_write(const struct jz4740_i2s *i2s, 1238c2ecf20Sopenharmony_ci unsigned int reg, uint32_t value) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci writel(value, i2s->base + reg); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic inline void jz4740_i2s_set_bits(const struct jz4740_i2s *i2s, 1298c2ecf20Sopenharmony_ci unsigned int reg, uint32_t bits) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci uint32_t value = jz4740_i2s_read(i2s, reg); 1328c2ecf20Sopenharmony_ci value |= bits; 1338c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, reg, value); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int jz4740_i2s_startup(struct snd_pcm_substream *substream, 1378c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 1408c2ecf20Sopenharmony_ci uint32_t conf; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * When we can flush FIFOs independently, only flush the FIFO 1458c2ecf20Sopenharmony_ci * that is starting up. We can do this when the DAI is active 1468c2ecf20Sopenharmony_ci * because it does not disturb other active substreams. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci if (!i2s->soc_info->shared_fifo_flush) { 1498c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1508c2ecf20Sopenharmony_ci jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); 1518c2ecf20Sopenharmony_ci else 1528c2ecf20Sopenharmony_ci jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (snd_soc_dai_active(dai)) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * When there is a shared flush bit for both FIFOs, the TFLUSH 1608c2ecf20Sopenharmony_ci * bit flushes both FIFOs. Flushing while the DAI is active would 1618c2ecf20Sopenharmony_ci * cause FIFO underruns in other active substreams so we have to 1628c2ecf20Sopenharmony_ci * guard this behind the snd_soc_dai_active() check. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci if (i2s->soc_info->shared_fifo_flush) 1658c2ecf20Sopenharmony_ci jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_i2s); 1688c2ecf20Sopenharmony_ci if (ret) 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); 1728c2ecf20Sopenharmony_ci conf |= JZ_AIC_CONF_ENABLE; 1738c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, 1798c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 1828c2ecf20Sopenharmony_ci uint32_t conf; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (snd_soc_dai_active(dai)) 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); 1888c2ecf20Sopenharmony_ci conf &= ~JZ_AIC_CONF_ENABLE; 1898c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_i2s); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 1958c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci uint32_t ctrl; 2008c2ecf20Sopenharmony_ci uint32_t mask; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2038c2ecf20Sopenharmony_ci mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA; 2048c2ecf20Sopenharmony_ci else 2058c2ecf20Sopenharmony_ci mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (cmd) { 2108c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2118c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2128c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2138c2ecf20Sopenharmony_ci ctrl |= mask; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2168c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2178c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2188c2ecf20Sopenharmony_ci ctrl &= ~mask; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci default: 2218c2ecf20Sopenharmony_ci return -EINVAL; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci uint32_t format = 0; 2348c2ecf20Sopenharmony_ci uint32_t conf; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 2418c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 2428c2ecf20Sopenharmony_ci conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER; 2438c2ecf20Sopenharmony_ci format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 2468c2ecf20Sopenharmony_ci conf |= JZ_AIC_CONF_SYNC_CLK_MASTER; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 2498c2ecf20Sopenharmony_ci conf |= JZ_AIC_CONF_BIT_CLK_MASTER; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci default: 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2588c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_MSB: 2598c2ecf20Sopenharmony_ci format |= JZ_AIC_I2S_FMT_MSB; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 2688c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci default: 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 2758c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, 2818c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 2848c2ecf20Sopenharmony_ci unsigned int sample_size; 2858c2ecf20Sopenharmony_ci uint32_t ctrl, div_reg; 2868c2ecf20Sopenharmony_ci int div; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci div_reg = jz4740_i2s_read(i2s, JZ_REG_AIC_CLK_DIV); 2918c2ecf20Sopenharmony_ci div = clk_get_rate(i2s->clk_i2s) / (64 * params_rate(params)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (params_format(params)) { 2948c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 2958c2ecf20Sopenharmony_ci sample_size = 0; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16: 2988c2ecf20Sopenharmony_ci sample_size = 1; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3058c2ecf20Sopenharmony_ci ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK; 3068c2ecf20Sopenharmony_ci ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET; 3078c2ecf20Sopenharmony_ci if (params_channels(params) == 1) 3088c2ecf20Sopenharmony_ci ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci div_reg &= ~I2SDIV_DV_MASK; 3138c2ecf20Sopenharmony_ci div_reg |= (div - 1) << I2SDIV_DV_SHIFT; 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; 3168c2ecf20Sopenharmony_ci ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (i2s->soc_info->version >= JZ_I2S_JZ4770) { 3198c2ecf20Sopenharmony_ci div_reg &= ~I2SDIV_IDV_MASK; 3208c2ecf20Sopenharmony_ci div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci div_reg &= ~I2SDIV_DV_MASK; 3238c2ecf20Sopenharmony_ci div_reg |= (div - 1) << I2SDIV_DV_SHIFT; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); 3288c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, 3348c2ecf20Sopenharmony_ci unsigned int freq, int dir) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 3378c2ecf20Sopenharmony_ci struct clk *parent; 3388c2ecf20Sopenharmony_ci int ret = 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci switch (clk_id) { 3418c2ecf20Sopenharmony_ci case JZ4740_I2S_CLKSRC_EXT: 3428c2ecf20Sopenharmony_ci parent = clk_get(NULL, "ext"); 3438c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 3448c2ecf20Sopenharmony_ci return PTR_ERR(parent); 3458c2ecf20Sopenharmony_ci clk_set_parent(i2s->clk_i2s, parent); 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case JZ4740_I2S_CLKSRC_PLL: 3488c2ecf20Sopenharmony_ci parent = clk_get(NULL, "pll half"); 3498c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 3508c2ecf20Sopenharmony_ci return PTR_ERR(parent); 3518c2ecf20Sopenharmony_ci clk_set_parent(i2s->clk_i2s, parent); 3528c2ecf20Sopenharmony_ci ret = clk_set_rate(i2s->clk_i2s, freq); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci default: 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci clk_put(parent); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int jz4740_i2s_suspend(struct snd_soc_component *component) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); 3658c2ecf20Sopenharmony_ci uint32_t conf; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (snd_soc_component_active(component)) { 3688c2ecf20Sopenharmony_ci conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); 3698c2ecf20Sopenharmony_ci conf &= ~JZ_AIC_CONF_ENABLE; 3708c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_i2s); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_aic); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int jz4740_i2s_resume(struct snd_soc_component *component) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); 3838c2ecf20Sopenharmony_ci uint32_t conf; 3848c2ecf20Sopenharmony_ci int ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_aic); 3878c2ecf20Sopenharmony_ci if (ret) 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (snd_soc_component_active(component)) { 3918c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_i2s); 3928c2ecf20Sopenharmony_ci if (ret) { 3938c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_aic); 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); 3988c2ecf20Sopenharmony_ci conf |= JZ_AIC_CONF_ENABLE; 3998c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Playback */ 4108c2ecf20Sopenharmony_ci dma_data = &i2s->playback_dma_data; 4118c2ecf20Sopenharmony_ci dma_data->maxburst = 16; 4128c2ecf20Sopenharmony_ci dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT; 4138c2ecf20Sopenharmony_ci dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Capture */ 4168c2ecf20Sopenharmony_ci dma_data = &i2s->capture_dma_data; 4178c2ecf20Sopenharmony_ci dma_data->maxburst = 16; 4188c2ecf20Sopenharmony_ci dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE; 4198c2ecf20Sopenharmony_ci dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 4258c2ecf20Sopenharmony_ci uint32_t conf; 4268c2ecf20Sopenharmony_ci int ret; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = clk_prepare_enable(i2s->clk_aic); 4298c2ecf20Sopenharmony_ci if (ret) 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci jz4740_i2c_init_pcm_config(i2s); 4338c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, 4348c2ecf20Sopenharmony_ci &i2s->capture_dma_data); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (i2s->soc_info->version >= JZ_I2S_JZ4760) { 4378c2ecf20Sopenharmony_ci conf = (7 << JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | 4388c2ecf20Sopenharmony_ci (8 << JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | 4398c2ecf20Sopenharmony_ci JZ_AIC_CONF_OVERFLOW_PLAY_LAST | 4408c2ecf20Sopenharmony_ci JZ_AIC_CONF_I2S | 4418c2ecf20Sopenharmony_ci JZ_AIC_CONF_INTERNAL_CODEC; 4428c2ecf20Sopenharmony_ci } else { 4438c2ecf20Sopenharmony_ci conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | 4448c2ecf20Sopenharmony_ci (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | 4458c2ecf20Sopenharmony_ci JZ_AIC_CONF_OVERFLOW_PLAY_LAST | 4468c2ecf20Sopenharmony_ci JZ_AIC_CONF_I2S | 4478c2ecf20Sopenharmony_ci JZ_AIC_CONF_INTERNAL_CODEC; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); 4518c2ecf20Sopenharmony_ci jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int jz4740_i2s_dai_remove(struct snd_soc_dai *dai) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci clk_disable_unprepare(i2s->clk_aic); 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { 4658c2ecf20Sopenharmony_ci .startup = jz4740_i2s_startup, 4668c2ecf20Sopenharmony_ci .shutdown = jz4740_i2s_shutdown, 4678c2ecf20Sopenharmony_ci .trigger = jz4740_i2s_trigger, 4688c2ecf20Sopenharmony_ci .hw_params = jz4740_i2s_hw_params, 4698c2ecf20Sopenharmony_ci .set_fmt = jz4740_i2s_set_fmt, 4708c2ecf20Sopenharmony_ci .set_sysclk = jz4740_i2s_set_sysclk, 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ 4748c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE) 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver jz4740_i2s_dai = { 4778c2ecf20Sopenharmony_ci .probe = jz4740_i2s_dai_probe, 4788c2ecf20Sopenharmony_ci .remove = jz4740_i2s_dai_remove, 4798c2ecf20Sopenharmony_ci .playback = { 4808c2ecf20Sopenharmony_ci .channels_min = 1, 4818c2ecf20Sopenharmony_ci .channels_max = 2, 4828c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 4838c2ecf20Sopenharmony_ci .formats = JZ4740_I2S_FMTS, 4848c2ecf20Sopenharmony_ci }, 4858c2ecf20Sopenharmony_ci .capture = { 4868c2ecf20Sopenharmony_ci .channels_min = 2, 4878c2ecf20Sopenharmony_ci .channels_max = 2, 4888c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 4898c2ecf20Sopenharmony_ci .formats = JZ4740_I2S_FMTS, 4908c2ecf20Sopenharmony_ci }, 4918c2ecf20Sopenharmony_ci .symmetric_rates = 1, 4928c2ecf20Sopenharmony_ci .ops = &jz4740_i2s_dai_ops, 4938c2ecf20Sopenharmony_ci}; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const struct i2s_soc_info jz4740_i2s_soc_info = { 4968c2ecf20Sopenharmony_ci .version = JZ_I2S_JZ4740, 4978c2ecf20Sopenharmony_ci .dai = &jz4740_i2s_dai, 4988c2ecf20Sopenharmony_ci .shared_fifo_flush = true, 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic const struct i2s_soc_info jz4760_i2s_soc_info = { 5028c2ecf20Sopenharmony_ci .version = JZ_I2S_JZ4760, 5038c2ecf20Sopenharmony_ci .dai = &jz4740_i2s_dai, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver jz4770_i2s_dai = { 5078c2ecf20Sopenharmony_ci .probe = jz4740_i2s_dai_probe, 5088c2ecf20Sopenharmony_ci .remove = jz4740_i2s_dai_remove, 5098c2ecf20Sopenharmony_ci .playback = { 5108c2ecf20Sopenharmony_ci .channels_min = 1, 5118c2ecf20Sopenharmony_ci .channels_max = 2, 5128c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 5138c2ecf20Sopenharmony_ci .formats = JZ4740_I2S_FMTS, 5148c2ecf20Sopenharmony_ci }, 5158c2ecf20Sopenharmony_ci .capture = { 5168c2ecf20Sopenharmony_ci .channels_min = 2, 5178c2ecf20Sopenharmony_ci .channels_max = 2, 5188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 5198c2ecf20Sopenharmony_ci .formats = JZ4740_I2S_FMTS, 5208c2ecf20Sopenharmony_ci }, 5218c2ecf20Sopenharmony_ci .ops = &jz4740_i2s_dai_ops, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct i2s_soc_info jz4770_i2s_soc_info = { 5258c2ecf20Sopenharmony_ci .version = JZ_I2S_JZ4770, 5268c2ecf20Sopenharmony_ci .dai = &jz4770_i2s_dai, 5278c2ecf20Sopenharmony_ci}; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic const struct i2s_soc_info jz4780_i2s_soc_info = { 5308c2ecf20Sopenharmony_ci .version = JZ_I2S_JZ4780, 5318c2ecf20Sopenharmony_ci .dai = &jz4770_i2s_dai, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver jz4740_i2s_component = { 5358c2ecf20Sopenharmony_ci .name = "jz4740-i2s", 5368c2ecf20Sopenharmony_ci .suspend = jz4740_i2s_suspend, 5378c2ecf20Sopenharmony_ci .resume = jz4740_i2s_resume, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct of_device_id jz4740_of_matches[] = { 5418c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-i2s", .data = &jz4740_i2s_soc_info }, 5428c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info }, 5438c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info }, 5448c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info }, 5458c2ecf20Sopenharmony_ci { /* sentinel */ } 5468c2ecf20Sopenharmony_ci}; 5478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_of_matches); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int jz4740_i2s_dev_probe(struct platform_device *pdev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5528c2ecf20Sopenharmony_ci struct jz4740_i2s *i2s; 5538c2ecf20Sopenharmony_ci struct resource *mem; 5548c2ecf20Sopenharmony_ci int ret; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); 5578c2ecf20Sopenharmony_ci if (!i2s) 5588c2ecf20Sopenharmony_ci return -ENOMEM; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci i2s->soc_info = device_get_match_data(dev); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5638c2ecf20Sopenharmony_ci i2s->base = devm_ioremap_resource(dev, mem); 5648c2ecf20Sopenharmony_ci if (IS_ERR(i2s->base)) 5658c2ecf20Sopenharmony_ci return PTR_ERR(i2s->base); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci i2s->phys_base = mem->start; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci i2s->clk_aic = devm_clk_get(dev, "aic"); 5708c2ecf20Sopenharmony_ci if (IS_ERR(i2s->clk_aic)) 5718c2ecf20Sopenharmony_ci return PTR_ERR(i2s->clk_aic); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci i2s->clk_i2s = devm_clk_get(dev, "i2s"); 5748c2ecf20Sopenharmony_ci if (IS_ERR(i2s->clk_i2s)) 5758c2ecf20Sopenharmony_ci return PTR_ERR(i2s->clk_i2s); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, i2s); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component, 5808c2ecf20Sopenharmony_ci i2s->soc_info->dai, 1); 5818c2ecf20Sopenharmony_ci if (ret) 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return devm_snd_dmaengine_pcm_register(dev, NULL, 5858c2ecf20Sopenharmony_ci SND_DMAENGINE_PCM_FLAG_COMPAT); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic struct platform_driver jz4740_i2s_driver = { 5898c2ecf20Sopenharmony_ci .probe = jz4740_i2s_dev_probe, 5908c2ecf20Sopenharmony_ci .driver = { 5918c2ecf20Sopenharmony_ci .name = "jz4740-i2s", 5928c2ecf20Sopenharmony_ci .of_match_table = jz4740_of_matches, 5938c2ecf20Sopenharmony_ci }, 5948c2ecf20Sopenharmony_ci}; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cimodule_platform_driver(jz4740_i2s_driver); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>"); 5998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver"); 6008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:jz4740-i2s"); 602