18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ZTE's TDM driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 ZTE Ltd 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Baoyou Xie <baoyou.xie@linaro.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 158c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 168c2ecf20Sopenharmony_ci#include <sound/soc.h> 178c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define REG_TIMING_CTRL 0x04 208c2ecf20Sopenharmony_ci#define REG_TX_FIFO_CTRL 0x0C 218c2ecf20Sopenharmony_ci#define REG_RX_FIFO_CTRL 0x10 228c2ecf20Sopenharmony_ci#define REG_INT_EN 0x1C 238c2ecf20Sopenharmony_ci#define REG_INT_STATUS 0x20 248c2ecf20Sopenharmony_ci#define REG_DATABUF 0x24 258c2ecf20Sopenharmony_ci#define REG_TS_MASK0 0x44 268c2ecf20Sopenharmony_ci#define REG_PROCESS_CTRL 0x54 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define FIFO_CTRL_TX_RST BIT(0) 298c2ecf20Sopenharmony_ci#define FIFO_CTRL_RX_RST BIT(0) 308c2ecf20Sopenharmony_ci#define DEAGULT_FIFO_THRES GENMASK(4, 2) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define FIFO_CTRL_TX_DMA_EN BIT(1) 338c2ecf20Sopenharmony_ci#define FIFO_CTRL_RX_DMA_EN BIT(1) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define TX_FIFO_RST_MASK BIT(0) 368c2ecf20Sopenharmony_ci#define RX_FIFO_RST_MASK BIT(0) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define FIFOCTRL_TX_FIFO_RST BIT(0) 398c2ecf20Sopenharmony_ci#define FIFOCTRL_RX_FIFO_RST BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TXTH_MASK GENMASK(5, 2) 428c2ecf20Sopenharmony_ci#define RXTH_MASK GENMASK(5, 2) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define FIFOCTRL_THRESHOLD(x) ((x) << 2) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define TIMING_MS_MASK BIT(1) 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * 00: 8 clk cycles every timeslot 498c2ecf20Sopenharmony_ci * 01: 16 clk cycles every timeslot 508c2ecf20Sopenharmony_ci * 10: 32 clk cycles every timeslot 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define TIMING_SYNC_WIDTH_MASK GENMASK(6, 5) 538c2ecf20Sopenharmony_ci#define TIMING_WIDTH_SHIFT 5 548c2ecf20Sopenharmony_ci#define TIMING_DEFAULT_WIDTH 0 558c2ecf20Sopenharmony_ci#define TIMING_TS_WIDTH(x) ((x) << TIMING_WIDTH_SHIFT) 568c2ecf20Sopenharmony_ci#define TIMING_WIDTH_FACTOR 8 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define TIMING_MASTER_MODE BIT(21) 598c2ecf20Sopenharmony_ci#define TIMING_LSB_FIRST BIT(20) 608c2ecf20Sopenharmony_ci#define TIMING_TS_NUM(x) (((x) - 1) << 7) 618c2ecf20Sopenharmony_ci#define TIMING_CLK_SEL_MASK GENMASK(2, 0) 628c2ecf20Sopenharmony_ci#define TIMING_CLK_SEL_DEF BIT(2) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define PROCESS_TX_EN BIT(0) 658c2ecf20Sopenharmony_ci#define PROCESS_RX_EN BIT(1) 668c2ecf20Sopenharmony_ci#define PROCESS_TDM_EN BIT(2) 678c2ecf20Sopenharmony_ci#define PROCESS_DISABLE_ALL 0 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define INT_DISABLE_ALL 0 708c2ecf20Sopenharmony_ci#define INT_STATUS_MASK GENMASK(6, 0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct zx_tdm_info { 738c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_playback; 748c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_capture; 758c2ecf20Sopenharmony_ci resource_size_t phy_addr; 768c2ecf20Sopenharmony_ci void __iomem *regbase; 778c2ecf20Sopenharmony_ci struct clk *dai_wclk; 788c2ecf20Sopenharmony_ci struct clk *dai_pclk; 798c2ecf20Sopenharmony_ci int master; 808c2ecf20Sopenharmony_ci struct device *dev; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline u32 zx_tdm_readl(struct zx_tdm_info *tdm, u16 reg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return readl_relaxed(tdm->regbase + reg); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline void zx_tdm_writel(struct zx_tdm_info *tdm, u16 reg, u32 val) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci writel_relaxed(val, tdm->regbase + reg); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void zx_tdm_tx_en(struct zx_tdm_info *tdm, bool on) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned long val; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_PROCESS_CTRL); 988c2ecf20Sopenharmony_ci if (on) 998c2ecf20Sopenharmony_ci val |= PROCESS_TX_EN | PROCESS_TDM_EN; 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci val &= ~(PROCESS_TX_EN | PROCESS_TDM_EN); 1028c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_PROCESS_CTRL, val); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void zx_tdm_rx_en(struct zx_tdm_info *tdm, bool on) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned long val; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_PROCESS_CTRL); 1108c2ecf20Sopenharmony_ci if (on) 1118c2ecf20Sopenharmony_ci val |= PROCESS_RX_EN | PROCESS_TDM_EN; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci val &= ~(PROCESS_RX_EN | PROCESS_TDM_EN); 1148c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_PROCESS_CTRL, val); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void zx_tdm_tx_dma_en(struct zx_tdm_info *tdm, bool on) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned long val; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL); 1228c2ecf20Sopenharmony_ci val |= FIFO_CTRL_TX_RST | DEAGULT_FIFO_THRES; 1238c2ecf20Sopenharmony_ci if (on) 1248c2ecf20Sopenharmony_ci val |= FIFO_CTRL_TX_DMA_EN; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci val &= ~FIFO_CTRL_TX_DMA_EN; 1278c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned long val; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL); 1358c2ecf20Sopenharmony_ci val |= FIFO_CTRL_RX_RST | DEAGULT_FIFO_THRES; 1368c2ecf20Sopenharmony_ci if (on) 1378c2ecf20Sopenharmony_ci val |= FIFO_CTRL_RX_DMA_EN; 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci val &= ~FIFO_CTRL_RX_DMA_EN; 1408c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define ZX_TDM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define ZX_TDM_FMTBIT \ 1468c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_MU_LAW | \ 1478c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_A_LAW) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int zx_tdm_dai_probe(struct snd_soc_dai *dai) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci snd_soc_dai_set_drvdata(dai, zx_tdm); 1548c2ecf20Sopenharmony_ci zx_tdm->dma_playback.addr = zx_tdm->phy_addr + REG_DATABUF; 1558c2ecf20Sopenharmony_ci zx_tdm->dma_playback.maxburst = 16; 1568c2ecf20Sopenharmony_ci zx_tdm->dma_capture.addr = zx_tdm->phy_addr + REG_DATABUF; 1578c2ecf20Sopenharmony_ci zx_tdm->dma_capture.maxburst = 16; 1588c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &zx_tdm->dma_playback, 1598c2ecf20Sopenharmony_ci &zx_tdm->dma_capture); 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int zx_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(cpu_dai); 1668c2ecf20Sopenharmony_ci unsigned long val; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_TIMING_CTRL); 1698c2ecf20Sopenharmony_ci val &= ~(TIMING_SYNC_WIDTH_MASK | TIMING_MS_MASK); 1708c2ecf20Sopenharmony_ci val |= TIMING_DEFAULT_WIDTH << TIMING_WIDTH_SHIFT; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1738c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 1748c2ecf20Sopenharmony_ci tdm->master = 1; 1758c2ecf20Sopenharmony_ci val |= TIMING_MASTER_MODE; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 1788c2ecf20Sopenharmony_ci tdm->master = 0; 1798c2ecf20Sopenharmony_ci val &= ~TIMING_MASTER_MODE; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci default: 1828c2ecf20Sopenharmony_ci dev_err(cpu_dai->dev, "Unknown master/slave format\n"); 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TIMING_CTRL, val); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int zx_tdm_hw_params(struct snd_pcm_substream *substream, 1938c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1948c2ecf20Sopenharmony_ci struct snd_soc_dai *socdai) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(socdai); 1978c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 1988c2ecf20Sopenharmony_ci unsigned int ts_width = TIMING_DEFAULT_WIDTH; 1998c2ecf20Sopenharmony_ci unsigned int ch_num = 32; 2008c2ecf20Sopenharmony_ci unsigned int mask = 0; 2018c2ecf20Sopenharmony_ci unsigned int ret = 0; 2028c2ecf20Sopenharmony_ci unsigned long val; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(socdai, substream); 2058c2ecf20Sopenharmony_ci dma_data->addr_width = ch_num >> 3; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci switch (params_format(params)) { 2088c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_MU_LAW: 2098c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_A_LAW: 2108c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 2118c2ecf20Sopenharmony_ci ts_width = 1; 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci default: 2148c2ecf20Sopenharmony_ci dev_err(socdai->dev, "Unknown data format\n"); 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_TIMING_CTRL); 2198c2ecf20Sopenharmony_ci val |= TIMING_TS_WIDTH(ts_width) | TIMING_TS_NUM(1); 2208c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TIMING_CTRL, val); 2218c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TS_MASK0, mask); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (tdm->master) 2248c2ecf20Sopenharmony_ci ret = clk_set_rate(tdm->dai_wclk, 2258c2ecf20Sopenharmony_ci params_rate(params) * TIMING_WIDTH_FACTOR * ch_num); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int zx_tdm_trigger(struct snd_pcm_substream *substream, int cmd, 2318c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 2348c2ecf20Sopenharmony_ci struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev); 2358c2ecf20Sopenharmony_ci unsigned int val; 2368c2ecf20Sopenharmony_ci int ret = 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci switch (cmd) { 2398c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2408c2ecf20Sopenharmony_ci if (capture) { 2418c2ecf20Sopenharmony_ci val = zx_tdm_readl(zx_tdm, REG_RX_FIFO_CTRL); 2428c2ecf20Sopenharmony_ci val |= FIFOCTRL_RX_FIFO_RST; 2438c2ecf20Sopenharmony_ci zx_tdm_writel(zx_tdm, REG_RX_FIFO_CTRL, val); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci zx_tdm_rx_dma_en(zx_tdm, true); 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci val = zx_tdm_readl(zx_tdm, REG_TX_FIFO_CTRL); 2488c2ecf20Sopenharmony_ci val |= FIFOCTRL_TX_FIFO_RST; 2498c2ecf20Sopenharmony_ci zx_tdm_writel(zx_tdm, REG_TX_FIFO_CTRL, val); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci zx_tdm_tx_dma_en(zx_tdm, true); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2558c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2568c2ecf20Sopenharmony_ci if (capture) 2578c2ecf20Sopenharmony_ci zx_tdm_rx_en(zx_tdm, true); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci zx_tdm_tx_en(zx_tdm, true); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2628c2ecf20Sopenharmony_ci if (capture) 2638c2ecf20Sopenharmony_ci zx_tdm_rx_dma_en(zx_tdm, false); 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci zx_tdm_tx_dma_en(zx_tdm, false); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2688c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2698c2ecf20Sopenharmony_ci if (capture) 2708c2ecf20Sopenharmony_ci zx_tdm_rx_en(zx_tdm, false); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci zx_tdm_tx_en(zx_tdm, false); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci ret = -EINVAL; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return ret; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int zx_tdm_startup(struct snd_pcm_substream *substream, 2838c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev); 2868c2ecf20Sopenharmony_ci int ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = clk_prepare_enable(zx_tdm->dai_wclk); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = clk_prepare_enable(zx_tdm->dai_pclk); 2938c2ecf20Sopenharmony_ci if (ret) { 2948c2ecf20Sopenharmony_ci clk_disable_unprepare(zx_tdm->dai_wclk); 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void zx_tdm_shutdown(struct snd_pcm_substream *substream, 3028c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci clk_disable_unprepare(zx_tdm->dai_pclk); 3078c2ecf20Sopenharmony_ci clk_disable_unprepare(zx_tdm->dai_wclk); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops zx_tdm_dai_ops = { 3118c2ecf20Sopenharmony_ci .trigger = zx_tdm_trigger, 3128c2ecf20Sopenharmony_ci .hw_params = zx_tdm_hw_params, 3138c2ecf20Sopenharmony_ci .set_fmt = zx_tdm_set_fmt, 3148c2ecf20Sopenharmony_ci .startup = zx_tdm_startup, 3158c2ecf20Sopenharmony_ci .shutdown = zx_tdm_shutdown, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver zx_tdm_component = { 3198c2ecf20Sopenharmony_ci .name = "zx-tdm", 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void zx_tdm_init_state(struct zx_tdm_info *tdm) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci unsigned int val; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_PROCESS_CTRL, PROCESS_DISABLE_ALL); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_TIMING_CTRL); 3298c2ecf20Sopenharmony_ci val |= TIMING_LSB_FIRST; 3308c2ecf20Sopenharmony_ci val &= ~TIMING_CLK_SEL_MASK; 3318c2ecf20Sopenharmony_ci val |= TIMING_CLK_SEL_DEF; 3328c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TIMING_CTRL, val); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_INT_EN, INT_DISABLE_ALL); 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * write INT_STATUS register to clear it. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_INT_STATUS, INT_STATUS_MASK); 3398c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, FIFOCTRL_RX_FIFO_RST); 3408c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, FIFOCTRL_TX_FIFO_RST); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL); 3438c2ecf20Sopenharmony_ci val &= ~(RXTH_MASK | RX_FIFO_RST_MASK); 3448c2ecf20Sopenharmony_ci val |= FIFOCTRL_THRESHOLD(8); 3458c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL); 3488c2ecf20Sopenharmony_ci val &= ~(TXTH_MASK | TX_FIFO_RST_MASK); 3498c2ecf20Sopenharmony_ci val |= FIFOCTRL_THRESHOLD(8); 3508c2ecf20Sopenharmony_ci zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver zx_tdm_dai = { 3548c2ecf20Sopenharmony_ci .name = "zx-tdm-dai", 3558c2ecf20Sopenharmony_ci .id = 0, 3568c2ecf20Sopenharmony_ci .probe = zx_tdm_dai_probe, 3578c2ecf20Sopenharmony_ci .playback = { 3588c2ecf20Sopenharmony_ci .channels_min = 1, 3598c2ecf20Sopenharmony_ci .channels_max = 4, 3608c2ecf20Sopenharmony_ci .rates = ZX_TDM_RATES, 3618c2ecf20Sopenharmony_ci .formats = ZX_TDM_FMTBIT, 3628c2ecf20Sopenharmony_ci }, 3638c2ecf20Sopenharmony_ci .capture = { 3648c2ecf20Sopenharmony_ci .channels_min = 1, 3658c2ecf20Sopenharmony_ci .channels_max = 4, 3668c2ecf20Sopenharmony_ci .rates = ZX_TDM_RATES, 3678c2ecf20Sopenharmony_ci .formats = ZX_TDM_FMTBIT, 3688c2ecf20Sopenharmony_ci }, 3698c2ecf20Sopenharmony_ci .ops = &zx_tdm_dai_ops, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int zx_tdm_probe(struct platform_device *pdev) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct of_phandle_args out_args; 3758c2ecf20Sopenharmony_ci unsigned int dma_reg_offset; 3768c2ecf20Sopenharmony_ci struct zx_tdm_info *zx_tdm; 3778c2ecf20Sopenharmony_ci unsigned int dma_mask; 3788c2ecf20Sopenharmony_ci struct resource *res; 3798c2ecf20Sopenharmony_ci struct regmap *regmap_sysctrl; 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci zx_tdm = devm_kzalloc(&pdev->dev, sizeof(*zx_tdm), GFP_KERNEL); 3838c2ecf20Sopenharmony_ci if (!zx_tdm) 3848c2ecf20Sopenharmony_ci return -ENOMEM; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci zx_tdm->dev = &pdev->dev; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk"); 3898c2ecf20Sopenharmony_ci if (IS_ERR(zx_tdm->dai_wclk)) { 3908c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to get wclk\n"); 3918c2ecf20Sopenharmony_ci return PTR_ERR(zx_tdm->dai_wclk); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci zx_tdm->dai_pclk = devm_clk_get(&pdev->dev, "pclk"); 3958c2ecf20Sopenharmony_ci if (IS_ERR(zx_tdm->dai_pclk)) { 3968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to get pclk\n"); 3978c2ecf20Sopenharmony_ci return PTR_ERR(zx_tdm->dai_pclk); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4018c2ecf20Sopenharmony_ci zx_tdm->phy_addr = res->start; 4028c2ecf20Sopenharmony_ci zx_tdm->regbase = devm_ioremap_resource(&pdev->dev, res); 4038c2ecf20Sopenharmony_ci if (IS_ERR(zx_tdm->regbase)) 4048c2ecf20Sopenharmony_ci return PTR_ERR(zx_tdm->regbase); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, 4078c2ecf20Sopenharmony_ci "zte,tdm-dma-sysctrl", 2, 0, &out_args); 4088c2ecf20Sopenharmony_ci if (ret) { 4098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to get zte,tdm-dma-sysctrl\n"); 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci dma_reg_offset = out_args.args[0]; 4148c2ecf20Sopenharmony_ci dma_mask = out_args.args[1]; 4158c2ecf20Sopenharmony_ci regmap_sysctrl = syscon_node_to_regmap(out_args.np); 4168c2ecf20Sopenharmony_ci if (IS_ERR(regmap_sysctrl)) { 4178c2ecf20Sopenharmony_ci of_node_put(out_args.np); 4188c2ecf20Sopenharmony_ci return PTR_ERR(regmap_sysctrl); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci regmap_update_bits(regmap_sysctrl, dma_reg_offset, dma_mask, dma_mask); 4228c2ecf20Sopenharmony_ci of_node_put(out_args.np); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci zx_tdm_init_state(zx_tdm); 4258c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, zx_tdm); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &zx_tdm_component, 4288c2ecf20Sopenharmony_ci &zx_tdm_dai, 1); 4298c2ecf20Sopenharmony_ci if (ret) { 4308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Register DAI failed: %d\n", ret); 4318c2ecf20Sopenharmony_ci return ret; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 4358c2ecf20Sopenharmony_ci if (ret) 4368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic const struct of_device_id zx_tdm_dt_ids[] = { 4428c2ecf20Sopenharmony_ci { .compatible = "zte,zx296718-tdm", }, 4438c2ecf20Sopenharmony_ci {} 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_tdm_dt_ids); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic struct platform_driver tdm_driver = { 4488c2ecf20Sopenharmony_ci .probe = zx_tdm_probe, 4498c2ecf20Sopenharmony_ci .driver = { 4508c2ecf20Sopenharmony_ci .name = "zx-tdm", 4518c2ecf20Sopenharmony_ci .of_match_table = zx_tdm_dt_ids, 4528c2ecf20Sopenharmony_ci }, 4538c2ecf20Sopenharmony_ci}; 4548c2ecf20Sopenharmony_cimodule_platform_driver(tdm_driver); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); 4578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ZTE TDM DAI driver"); 4588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 459