18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 118c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 138c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "fsl_esai.h" 178c2ecf20Sopenharmony_ci#include "imx-pcm.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 208c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 218c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | \ 228c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * struct fsl_esai_soc_data - soc specific data 268c2ecf20Sopenharmony_ci * @imx: for imx platform 278c2ecf20Sopenharmony_ci * @reset_at_xrun: flags for enable reset operaton 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistruct fsl_esai_soc_data { 308c2ecf20Sopenharmony_ci bool imx; 318c2ecf20Sopenharmony_ci bool reset_at_xrun; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/** 358c2ecf20Sopenharmony_ci * struct fsl_esai - ESAI private data 368c2ecf20Sopenharmony_ci * @dma_params_rx: DMA parameters for receive channel 378c2ecf20Sopenharmony_ci * @dma_params_tx: DMA parameters for transmit channel 388c2ecf20Sopenharmony_ci * @pdev: platform device pointer 398c2ecf20Sopenharmony_ci * @regmap: regmap handler 408c2ecf20Sopenharmony_ci * @coreclk: clock source to access register 418c2ecf20Sopenharmony_ci * @extalclk: esai clock source to derive HCK, SCK and FS 428c2ecf20Sopenharmony_ci * @fsysclk: system clock source to derive HCK, SCK and FS 438c2ecf20Sopenharmony_ci * @spbaclk: SPBA clock (optional, depending on SoC design) 448c2ecf20Sopenharmony_ci * @work: work to handle the reset operation 458c2ecf20Sopenharmony_ci * @soc: soc specific data 468c2ecf20Sopenharmony_ci * @lock: spin lock between hw_reset() and trigger() 478c2ecf20Sopenharmony_ci * @fifo_depth: depth of tx/rx FIFO 488c2ecf20Sopenharmony_ci * @slot_width: width of each DAI slot 498c2ecf20Sopenharmony_ci * @slots: number of slots 508c2ecf20Sopenharmony_ci * @tx_mask: slot mask for TX 518c2ecf20Sopenharmony_ci * @rx_mask: slot mask for RX 528c2ecf20Sopenharmony_ci * @channels: channel num for tx or rx 538c2ecf20Sopenharmony_ci * @hck_rate: clock rate of desired HCKx clock 548c2ecf20Sopenharmony_ci * @sck_rate: clock rate of desired SCKx clock 558c2ecf20Sopenharmony_ci * @hck_dir: the direction of HCKx pads 568c2ecf20Sopenharmony_ci * @sck_div: if using PSR/PM dividers for SCKx clock 578c2ecf20Sopenharmony_ci * @slave_mode: if fully using DAI slave mode 588c2ecf20Sopenharmony_ci * @synchronous: if using tx/rx synchronous mode 598c2ecf20Sopenharmony_ci * @name: driver name 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistruct fsl_esai { 628c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_rx; 638c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_tx; 648c2ecf20Sopenharmony_ci struct platform_device *pdev; 658c2ecf20Sopenharmony_ci struct regmap *regmap; 668c2ecf20Sopenharmony_ci struct clk *coreclk; 678c2ecf20Sopenharmony_ci struct clk *extalclk; 688c2ecf20Sopenharmony_ci struct clk *fsysclk; 698c2ecf20Sopenharmony_ci struct clk *spbaclk; 708c2ecf20Sopenharmony_ci struct work_struct work; 718c2ecf20Sopenharmony_ci const struct fsl_esai_soc_data *soc; 728c2ecf20Sopenharmony_ci spinlock_t lock; /* Protect hw_reset and trigger */ 738c2ecf20Sopenharmony_ci u32 fifo_depth; 748c2ecf20Sopenharmony_ci u32 slot_width; 758c2ecf20Sopenharmony_ci u32 slots; 768c2ecf20Sopenharmony_ci u32 tx_mask; 778c2ecf20Sopenharmony_ci u32 rx_mask; 788c2ecf20Sopenharmony_ci u32 channels[2]; 798c2ecf20Sopenharmony_ci u32 hck_rate[2]; 808c2ecf20Sopenharmony_ci u32 sck_rate[2]; 818c2ecf20Sopenharmony_ci bool hck_dir[2]; 828c2ecf20Sopenharmony_ci bool sck_div[2]; 838c2ecf20Sopenharmony_ci bool slave_mode; 848c2ecf20Sopenharmony_ci bool synchronous; 858c2ecf20Sopenharmony_ci char name[32]; 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_vf610 = { 898c2ecf20Sopenharmony_ci .imx = false, 908c2ecf20Sopenharmony_ci .reset_at_xrun = true, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_imx35 = { 948c2ecf20Sopenharmony_ci .imx = true, 958c2ecf20Sopenharmony_ci .reset_at_xrun = true, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_imx6ull = { 998c2ecf20Sopenharmony_ci .imx = true, 1008c2ecf20Sopenharmony_ci .reset_at_xrun = false, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic irqreturn_t esai_isr(int irq, void *devid) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = (struct fsl_esai *)devid; 1068c2ecf20Sopenharmony_ci struct platform_device *pdev = esai_priv->pdev; 1078c2ecf20Sopenharmony_ci u32 esr; 1088c2ecf20Sopenharmony_ci u32 saisr; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); 1118c2ecf20Sopenharmony_ci regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) && 1148c2ecf20Sopenharmony_ci esai_priv->soc->reset_at_xrun) { 1158c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "reset module for xrun\n"); 1168c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, 1178c2ecf20Sopenharmony_ci ESAI_xCR_xEIE_MASK, 0); 1188c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, 1198c2ecf20Sopenharmony_ci ESAI_xCR_xEIE_MASK, 0); 1208c2ecf20Sopenharmony_ci schedule_work(&esai_priv->work); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TINIT_MASK) 1248c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_RFF_MASK) 1278c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "isr: Receiving overrun\n"); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TFE_MASK) 1308c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "isr: Transmission underrun\n"); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TLS_MASK) 1338c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TDE_MASK) 1368c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Transmission data exception\n"); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TED_MASK) 1398c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Transmitting even slots\n"); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_TD_MASK) 1428c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Transmitting data\n"); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_RLS_MASK) 1458c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Just received the last slot\n"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_RDE_MASK) 1488c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Receiving data exception\n"); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_RED_MASK) 1518c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Receiving even slots\n"); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (esr & ESAI_ESR_RD_MASK) 1548c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "isr: Receiving data\n"); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * fsl_esai_divisor_cal - This function is used to calculate the 1618c2ecf20Sopenharmony_ci * divisors of psr, pm, fp and it is supposed to be called in 1628c2ecf20Sopenharmony_ci * set_dai_sysclk() and set_bclk(). 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * @dai: pointer to DAI 1658c2ecf20Sopenharmony_ci * @tx: current setting is for playback or capture 1668c2ecf20Sopenharmony_ci * @ratio: desired overall ratio for the paticipating dividers 1678c2ecf20Sopenharmony_ci * @usefp: for HCK setting, there is no need to set fp divider 1688c2ecf20Sopenharmony_ci * @fp: bypass other dividers by setting fp directly if fp != 0 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio, 1718c2ecf20Sopenharmony_ci bool usefp, u32 fp) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 1748c2ecf20Sopenharmony_ci u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci maxfp = usefp ? 16 : 1; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (usefp && fp) 1798c2ecf20Sopenharmony_ci goto out_fp; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) { 1828c2ecf20Sopenharmony_ci dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n", 1838c2ecf20Sopenharmony_ci 2 * 8 * 256 * maxfp); 1848c2ecf20Sopenharmony_ci return -EINVAL; 1858c2ecf20Sopenharmony_ci } else if (ratio % 2) { 1868c2ecf20Sopenharmony_ci dev_err(dai->dev, "the raio must be even if using upper divider\n"); 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ratio /= 2; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Do not loop-search if PM (1 ~ 256) alone can serve the ratio */ 1958c2ecf20Sopenharmony_ci if (ratio <= 256) { 1968c2ecf20Sopenharmony_ci pm = ratio; 1978c2ecf20Sopenharmony_ci fp = 1; 1988c2ecf20Sopenharmony_ci goto out; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Set the max fluctuation -- 0.1% of the max devisor */ 2028c2ecf20Sopenharmony_ci savesub = (psr ? 1 : 8) * 256 * maxfp / 1000; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Find the best value for PM */ 2058c2ecf20Sopenharmony_ci for (i = 1; i <= 256; i++) { 2068c2ecf20Sopenharmony_ci for (j = 1; j <= maxfp; j++) { 2078c2ecf20Sopenharmony_ci /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */ 2088c2ecf20Sopenharmony_ci prod = (psr ? 1 : 8) * i * j; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (prod == ratio) 2118c2ecf20Sopenharmony_ci sub = 0; 2128c2ecf20Sopenharmony_ci else if (prod / ratio == 1) 2138c2ecf20Sopenharmony_ci sub = prod - ratio; 2148c2ecf20Sopenharmony_ci else if (ratio / prod == 1) 2158c2ecf20Sopenharmony_ci sub = ratio - prod; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci continue; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Calculate the fraction */ 2208c2ecf20Sopenharmony_ci sub = sub * 1000 / ratio; 2218c2ecf20Sopenharmony_ci if (sub < savesub) { 2228c2ecf20Sopenharmony_ci savesub = sub; 2238c2ecf20Sopenharmony_ci pm = i; 2248c2ecf20Sopenharmony_ci fp = j; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* We are lucky */ 2288c2ecf20Sopenharmony_ci if (savesub == 0) 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (pm == 999) { 2348c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to calculate proper divisors\n"); 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciout: 2398c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 2408c2ecf20Sopenharmony_ci ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK, 2418c2ecf20Sopenharmony_ci psr | ESAI_xCCR_xPM(pm)); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciout_fp: 2448c2ecf20Sopenharmony_ci /* Bypass fp if not being required */ 2458c2ecf20Sopenharmony_ci if (maxfp <= 1) 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 2498c2ecf20Sopenharmony_ci ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp)); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/** 2558c2ecf20Sopenharmony_ci * fsl_esai_set_dai_sysclk - configure the clock frequency of MCLK (HCKT/HCKR) 2568c2ecf20Sopenharmony_ci * @dai: pointer to DAI 2578c2ecf20Sopenharmony_ci * @clk_id: The clock source of HCKT/HCKR 2588c2ecf20Sopenharmony_ci * (Input from outside; output from inside, FSYS or EXTAL) 2598c2ecf20Sopenharmony_ci * @freq: The required clock rate of HCKT/HCKR 2608c2ecf20Sopenharmony_ci * @dir: The clock direction of HCKT/HCKR 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * Note: If the direction is input, we do not care about clk_id. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 2658c2ecf20Sopenharmony_ci unsigned int freq, int dir) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 2688c2ecf20Sopenharmony_ci struct clk *clksrc = esai_priv->extalclk; 2698c2ecf20Sopenharmony_ci bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous); 2708c2ecf20Sopenharmony_ci bool in = dir == SND_SOC_CLOCK_IN; 2718c2ecf20Sopenharmony_ci u32 ratio, ecr = 0; 2728c2ecf20Sopenharmony_ci unsigned long clk_rate; 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (freq == 0) { 2768c2ecf20Sopenharmony_ci dev_err(dai->dev, "%sput freq of HCK%c should not be 0Hz\n", 2778c2ecf20Sopenharmony_ci in ? "in" : "out", tx ? 'T' : 'R'); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Bypass divider settings if the requirement doesn't change */ 2828c2ecf20Sopenharmony_ci if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx]) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ 2868c2ecf20Sopenharmony_ci esai_priv->sck_div[tx] = true; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Set the direction of HCKT/HCKR pins */ 2898c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 2908c2ecf20Sopenharmony_ci ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (in) 2938c2ecf20Sopenharmony_ci goto out; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci switch (clk_id) { 2968c2ecf20Sopenharmony_ci case ESAI_HCKT_FSYS: 2978c2ecf20Sopenharmony_ci case ESAI_HCKR_FSYS: 2988c2ecf20Sopenharmony_ci clksrc = esai_priv->fsysclk; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case ESAI_HCKT_EXTAL: 3018c2ecf20Sopenharmony_ci ecr |= ESAI_ECR_ETI; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case ESAI_HCKR_EXTAL: 3048c2ecf20Sopenharmony_ci ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci default: 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (IS_ERR(clksrc)) { 3118c2ecf20Sopenharmony_ci dev_err(dai->dev, "no assigned %s clock\n", 3128c2ecf20Sopenharmony_ci clk_id % 2 ? "extal" : "fsys"); 3138c2ecf20Sopenharmony_ci return PTR_ERR(clksrc); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(clksrc); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ratio = clk_rate / freq; 3188c2ecf20Sopenharmony_ci if (ratio * freq > clk_rate) 3198c2ecf20Sopenharmony_ci ret = ratio * freq - clk_rate; 3208c2ecf20Sopenharmony_ci else if (ratio * freq < clk_rate) 3218c2ecf20Sopenharmony_ci ret = clk_rate - ratio * freq; 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci ret = 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Block if clock source can not be divided into the required rate */ 3268c2ecf20Sopenharmony_ci if (ret != 0 && clk_rate / ret < 1000) { 3278c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to derive required HCK%c rate\n", 3288c2ecf20Sopenharmony_ci tx ? 'T' : 'R'); 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Only EXTAL source can be output directly without using PSR and PM */ 3338c2ecf20Sopenharmony_ci if (ratio == 1 && clksrc == esai_priv->extalclk) { 3348c2ecf20Sopenharmony_ci /* Bypass all the dividers if not being needed */ 3358c2ecf20Sopenharmony_ci ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } else if (ratio < 2) { 3388c2ecf20Sopenharmony_ci /* The ratio should be no less than 2 if using other sources */ 3398c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to derive required HCK%c rate\n", 3408c2ecf20Sopenharmony_ci tx ? 'T' : 'R'); 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); 3458c2ecf20Sopenharmony_ci if (ret) 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci esai_priv->sck_div[tx] = false; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciout: 3518c2ecf20Sopenharmony_ci esai_priv->hck_dir[tx] = dir; 3528c2ecf20Sopenharmony_ci esai_priv->hck_rate[tx] = freq; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, 3558c2ecf20Sopenharmony_ci tx ? ESAI_ECR_ETI | ESAI_ECR_ETO : 3568c2ecf20Sopenharmony_ci ESAI_ECR_ERI | ESAI_ECR_ERO, ecr); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/** 3628c2ecf20Sopenharmony_ci * fsl_esai_set_bclk - configure the related dividers according to the bclk rate 3638c2ecf20Sopenharmony_ci * @dai: pointer to DAI 3648c2ecf20Sopenharmony_ci * @tx: direction boolean 3658c2ecf20Sopenharmony_ci * @freq: bclk freq 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 3708c2ecf20Sopenharmony_ci u32 hck_rate = esai_priv->hck_rate[tx]; 3718c2ecf20Sopenharmony_ci u32 sub, ratio = hck_rate / freq; 3728c2ecf20Sopenharmony_ci int ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Don't apply for fully slave mode or unchanged bclk */ 3758c2ecf20Sopenharmony_ci if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq) 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (ratio * freq > hck_rate) 3798c2ecf20Sopenharmony_ci sub = ratio * freq - hck_rate; 3808c2ecf20Sopenharmony_ci else if (ratio * freq < hck_rate) 3818c2ecf20Sopenharmony_ci sub = hck_rate - ratio * freq; 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci sub = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* Block if clock source can not be divided into the required rate */ 3868c2ecf20Sopenharmony_ci if (sub != 0 && hck_rate / sub < 1000) { 3878c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to derive required SCK%c rate\n", 3888c2ecf20Sopenharmony_ci tx ? 'T' : 'R'); 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* The ratio should be contented by FP alone if bypassing PM and PSR */ 3938c2ecf20Sopenharmony_ci if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { 3948c2ecf20Sopenharmony_ci dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); 3958c2ecf20Sopenharmony_ci return -EINVAL; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ret = fsl_esai_divisor_cal(dai, tx, ratio, true, 3998c2ecf20Sopenharmony_ci esai_priv->sck_div[tx] ? 0 : ratio); 4008c2ecf20Sopenharmony_ci if (ret) 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Save current bclk rate */ 4048c2ecf20Sopenharmony_ci esai_priv->sck_rate[tx] = freq; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, 4108c2ecf20Sopenharmony_ci u32 rx_mask, int slots, int slot_width) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, 4158c2ecf20Sopenharmony_ci ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, 4188c2ecf20Sopenharmony_ci ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci esai_priv->slot_width = slot_width; 4218c2ecf20Sopenharmony_ci esai_priv->slots = slots; 4228c2ecf20Sopenharmony_ci esai_priv->tx_mask = tx_mask; 4238c2ecf20Sopenharmony_ci esai_priv->rx_mask = rx_mask; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 4318c2ecf20Sopenharmony_ci u32 xcr = 0, xccr = 0, mask; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* DAI mode */ 4348c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4358c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 4368c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame low, 1clk before data */ 4378c2ecf20Sopenharmony_ci xcr |= ESAI_xCR_xFSR; 4388c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 4418c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high */ 4428c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 4458c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high, right aligned */ 4468c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4478c2ecf20Sopenharmony_ci xcr |= ESAI_xCR_xWA; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 4508c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high, 1clk before data */ 4518c2ecf20Sopenharmony_ci xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR; 4528c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 4558c2ecf20Sopenharmony_ci /* Data on rising edge of bclk, frame high */ 4568c2ecf20Sopenharmony_ci xcr |= ESAI_xCR_xFSL; 4578c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci default: 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* DAI clock inversion */ 4648c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 4658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 4668c2ecf20Sopenharmony_ci /* Nothing to do for both normal cases */ 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 4698c2ecf20Sopenharmony_ci /* Invert bit clock */ 4708c2ecf20Sopenharmony_ci xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 4738c2ecf20Sopenharmony_ci /* Invert frame clock */ 4748c2ecf20Sopenharmony_ci xccr ^= ESAI_xCCR_xFSP; 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 4778c2ecf20Sopenharmony_ci /* Invert both clocks */ 4788c2ecf20Sopenharmony_ci xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP; 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci default: 4818c2ecf20Sopenharmony_ci return -EINVAL; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci esai_priv->slave_mode = false; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* DAI clock master masks */ 4878c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 4888c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 4898c2ecf20Sopenharmony_ci esai_priv->slave_mode = true; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 4928c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xCKD; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 4958c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xFSD; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 4988c2ecf20Sopenharmony_ci xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci default: 5018c2ecf20Sopenharmony_ci return -EINVAL; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR | ESAI_xCR_xWA; 5058c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr); 5068c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP | 5098c2ecf20Sopenharmony_ci ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; 5108c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr); 5118c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int fsl_esai_startup(struct snd_pcm_substream *substream, 5178c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(dai)) { 5228c2ecf20Sopenharmony_ci /* Set synchronous mode */ 5238c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, 5248c2ecf20Sopenharmony_ci ESAI_SAICR_SYNC, esai_priv->synchronous ? 5258c2ecf20Sopenharmony_ci ESAI_SAICR_SYNC : 0); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Set slots count */ 5288c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, 5298c2ecf20Sopenharmony_ci ESAI_xCCR_xDC_MASK, 5308c2ecf20Sopenharmony_ci ESAI_xCCR_xDC(esai_priv->slots)); 5318c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, 5328c2ecf20Sopenharmony_ci ESAI_xCCR_xDC_MASK, 5338c2ecf20Sopenharmony_ci ESAI_xCCR_xDC(esai_priv->slots)); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int fsl_esai_hw_params(struct snd_pcm_substream *substream, 5418c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 5428c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 5458c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 5468c2ecf20Sopenharmony_ci u32 width = params_width(params); 5478c2ecf20Sopenharmony_ci u32 channels = params_channels(params); 5488c2ecf20Sopenharmony_ci u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); 5498c2ecf20Sopenharmony_ci u32 slot_width = width; 5508c2ecf20Sopenharmony_ci u32 bclk, mask, val; 5518c2ecf20Sopenharmony_ci int ret; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Override slot_width if being specifically set */ 5548c2ecf20Sopenharmony_ci if (esai_priv->slot_width) 5558c2ecf20Sopenharmony_ci slot_width = esai_priv->slot_width; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci bclk = params_rate(params) * slot_width * esai_priv->slots; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk); 5608c2ecf20Sopenharmony_ci if (ret) 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci mask = ESAI_xCR_xSWS_MASK; 5648c2ecf20Sopenharmony_ci val = ESAI_xCR_xSWS(slot_width, width); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); 5678c2ecf20Sopenharmony_ci /* Recording in synchronous mode needs to set TCR also */ 5688c2ecf20Sopenharmony_ci if (!tx && esai_priv->synchronous) 5698c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Use Normal mode to support monaural audio */ 5728c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 5738c2ecf20Sopenharmony_ci ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ? 5748c2ecf20Sopenharmony_ci ESAI_xCR_xMOD_NETWORK : 0); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 5778c2ecf20Sopenharmony_ci ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | 5808c2ecf20Sopenharmony_ci (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); 5818c2ecf20Sopenharmony_ci val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | 5828c2ecf20Sopenharmony_ci (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins)); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (tx) 5878c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, 5888c2ecf20Sopenharmony_ci ESAI_xCR_PADC, ESAI_xCR_PADC); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */ 5918c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, 5928c2ecf20Sopenharmony_ci ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); 5938c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, 5948c2ecf20Sopenharmony_ci ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int fsl_esai_hw_init(struct fsl_esai *esai_priv) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct platform_device *pdev = esai_priv->pdev; 6018c2ecf20Sopenharmony_ci int ret; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Reset ESAI unit */ 6048c2ecf20Sopenharmony_ci ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, 6058c2ecf20Sopenharmony_ci ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, 6068c2ecf20Sopenharmony_ci ESAI_ECR_ESAIEN | ESAI_ECR_ERST); 6078c2ecf20Sopenharmony_ci if (ret) { 6088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * We need to enable ESAI so as to access some of its registers. 6148c2ecf20Sopenharmony_ci * Otherwise, we would fail to dump regmap from user space. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, 6178c2ecf20Sopenharmony_ci ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, 6188c2ecf20Sopenharmony_ci ESAI_ECR_ESAIEN); 6198c2ecf20Sopenharmony_ci if (ret) { 6208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, 6258c2ecf20Sopenharmony_ci ESAI_PRRC_PDC_MASK, 0); 6268c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, 6278c2ecf20Sopenharmony_ci ESAI_PCRC_PC_MASK, 0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int fsl_esai_register_restore(struct fsl_esai *esai_priv) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* FIFO reset for safety */ 6378c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, 6388c2ecf20Sopenharmony_ci ESAI_xFCR_xFR, ESAI_xFCR_xFR); 6398c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, 6408c2ecf20Sopenharmony_ci ESAI_xFCR_xFR, ESAI_xFCR_xFR); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci regcache_mark_dirty(esai_priv->regmap); 6438c2ecf20Sopenharmony_ci ret = regcache_sync(esai_priv->regmap); 6448c2ecf20Sopenharmony_ci if (ret) 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* FIFO reset done */ 6488c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); 6498c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci u8 i, channels = esai_priv->channels[tx]; 6578c2ecf20Sopenharmony_ci u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); 6588c2ecf20Sopenharmony_ci u32 mask; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 6618c2ecf20Sopenharmony_ci ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Write initial words reqiured by ESAI as normal procedure */ 6648c2ecf20Sopenharmony_ci for (i = 0; tx && i < channels; i++) 6658c2ecf20Sopenharmony_ci regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * When set the TE/RE in the end of enablement flow, there 6698c2ecf20Sopenharmony_ci * will be channel swap issue for multi data line case. 6708c2ecf20Sopenharmony_ci * In order to workaround this issue, we switch the bit 6718c2ecf20Sopenharmony_ci * enablement sequence to below sequence 6728c2ecf20Sopenharmony_ci * 1) clear the xSMB & xSMA: which is done in probe and 6738c2ecf20Sopenharmony_ci * stop state. 6748c2ecf20Sopenharmony_ci * 2) set TE/RE 6758c2ecf20Sopenharmony_ci * 3) set xSMB 6768c2ecf20Sopenharmony_ci * 4) set xSMA: xSMA is the last one in this flow, which 6778c2ecf20Sopenharmony_ci * will trigger esai to start. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 6808c2ecf20Sopenharmony_ci tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 6818c2ecf20Sopenharmony_ci tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); 6828c2ecf20Sopenharmony_ci mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), 6858c2ecf20Sopenharmony_ci ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask)); 6868c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), 6878c2ecf20Sopenharmony_ci ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask)); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* Enable Exception interrupt */ 6908c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 6918c2ecf20Sopenharmony_ci ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 6978c2ecf20Sopenharmony_ci ESAI_xCR_xEIE_MASK, 0); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 7008c2ecf20Sopenharmony_ci tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); 7018c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), 7028c2ecf20Sopenharmony_ci ESAI_xSMA_xS_MASK, 0); 7038c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), 7048c2ecf20Sopenharmony_ci ESAI_xSMB_xS_MASK, 0); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* Disable and reset FIFO */ 7078c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 7088c2ecf20Sopenharmony_ci ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); 7098c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 7108c2ecf20Sopenharmony_ci ESAI_xFCR_xFR, 0); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic void fsl_esai_hw_reset(struct work_struct *work) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); 7168c2ecf20Sopenharmony_ci bool tx = true, rx = false, enabled[2]; 7178c2ecf20Sopenharmony_ci unsigned long lock_flags; 7188c2ecf20Sopenharmony_ci u32 tfcr, rfcr; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci spin_lock_irqsave(&esai_priv->lock, lock_flags); 7218c2ecf20Sopenharmony_ci /* Save the registers */ 7228c2ecf20Sopenharmony_ci regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); 7238c2ecf20Sopenharmony_ci regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); 7248c2ecf20Sopenharmony_ci enabled[tx] = tfcr & ESAI_xFCR_xFEN; 7258c2ecf20Sopenharmony_ci enabled[rx] = rfcr & ESAI_xFCR_xFEN; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Stop the tx & rx */ 7288c2ecf20Sopenharmony_ci fsl_esai_trigger_stop(esai_priv, tx); 7298c2ecf20Sopenharmony_ci fsl_esai_trigger_stop(esai_priv, rx); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Reset the esai, and ignore return value */ 7328c2ecf20Sopenharmony_ci fsl_esai_hw_init(esai_priv); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* Enforce ESAI personal resets for both TX and RX */ 7358c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, 7368c2ecf20Sopenharmony_ci ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); 7378c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, 7388c2ecf20Sopenharmony_ci ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Restore registers by regcache_sync, and ignore return value */ 7418c2ecf20Sopenharmony_ci fsl_esai_register_restore(esai_priv); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* Remove ESAI personal resets by configuring PCRC and PRRC also */ 7448c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, 7458c2ecf20Sopenharmony_ci ESAI_xCR_xPR_MASK, 0); 7468c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, 7478c2ecf20Sopenharmony_ci ESAI_xCR_xPR_MASK, 0); 7488c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, 7498c2ecf20Sopenharmony_ci ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); 7508c2ecf20Sopenharmony_ci regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, 7518c2ecf20Sopenharmony_ci ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Restart tx / rx, if they already enabled */ 7548c2ecf20Sopenharmony_ci if (enabled[tx]) 7558c2ecf20Sopenharmony_ci fsl_esai_trigger_start(esai_priv, tx); 7568c2ecf20Sopenharmony_ci if (enabled[rx]) 7578c2ecf20Sopenharmony_ci fsl_esai_trigger_start(esai_priv, rx); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&esai_priv->lock, lock_flags); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, 7638c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 7668c2ecf20Sopenharmony_ci bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 7678c2ecf20Sopenharmony_ci unsigned long lock_flags; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci esai_priv->channels[tx] = substream->runtime->channels; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci switch (cmd) { 7728c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7738c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7748c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7758c2ecf20Sopenharmony_ci spin_lock_irqsave(&esai_priv->lock, lock_flags); 7768c2ecf20Sopenharmony_ci fsl_esai_trigger_start(esai_priv, tx); 7778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&esai_priv->lock, lock_flags); 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 7808c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 7818c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7828c2ecf20Sopenharmony_ci spin_lock_irqsave(&esai_priv->lock, lock_flags); 7838c2ecf20Sopenharmony_ci fsl_esai_trigger_stop(esai_priv, tx); 7848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&esai_priv->lock, lock_flags); 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci default: 7878c2ecf20Sopenharmony_ci return -EINVAL; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_esai_dai_ops = { 7948c2ecf20Sopenharmony_ci .startup = fsl_esai_startup, 7958c2ecf20Sopenharmony_ci .trigger = fsl_esai_trigger, 7968c2ecf20Sopenharmony_ci .hw_params = fsl_esai_hw_params, 7978c2ecf20Sopenharmony_ci .set_sysclk = fsl_esai_set_dai_sysclk, 7988c2ecf20Sopenharmony_ci .set_fmt = fsl_esai_set_dai_fmt, 7998c2ecf20Sopenharmony_ci .set_tdm_slot = fsl_esai_set_dai_tdm_slot, 8008c2ecf20Sopenharmony_ci}; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int fsl_esai_dai_probe(struct snd_soc_dai *dai) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx, 8078c2ecf20Sopenharmony_ci &esai_priv->dma_params_rx); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver fsl_esai_dai = { 8138c2ecf20Sopenharmony_ci .probe = fsl_esai_dai_probe, 8148c2ecf20Sopenharmony_ci .playback = { 8158c2ecf20Sopenharmony_ci .stream_name = "CPU-Playback", 8168c2ecf20Sopenharmony_ci .channels_min = 1, 8178c2ecf20Sopenharmony_ci .channels_max = 12, 8188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 8198c2ecf20Sopenharmony_ci .formats = FSL_ESAI_FORMATS, 8208c2ecf20Sopenharmony_ci }, 8218c2ecf20Sopenharmony_ci .capture = { 8228c2ecf20Sopenharmony_ci .stream_name = "CPU-Capture", 8238c2ecf20Sopenharmony_ci .channels_min = 1, 8248c2ecf20Sopenharmony_ci .channels_max = 8, 8258c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 8268c2ecf20Sopenharmony_ci .formats = FSL_ESAI_FORMATS, 8278c2ecf20Sopenharmony_ci }, 8288c2ecf20Sopenharmony_ci .ops = &fsl_esai_dai_ops, 8298c2ecf20Sopenharmony_ci}; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver fsl_esai_component = { 8328c2ecf20Sopenharmony_ci .name = "fsl-esai", 8338c2ecf20Sopenharmony_ci}; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic const struct reg_default fsl_esai_reg_defaults[] = { 8368c2ecf20Sopenharmony_ci {REG_ESAI_ETDR, 0x00000000}, 8378c2ecf20Sopenharmony_ci {REG_ESAI_ECR, 0x00000000}, 8388c2ecf20Sopenharmony_ci {REG_ESAI_TFCR, 0x00000000}, 8398c2ecf20Sopenharmony_ci {REG_ESAI_RFCR, 0x00000000}, 8408c2ecf20Sopenharmony_ci {REG_ESAI_TX0, 0x00000000}, 8418c2ecf20Sopenharmony_ci {REG_ESAI_TX1, 0x00000000}, 8428c2ecf20Sopenharmony_ci {REG_ESAI_TX2, 0x00000000}, 8438c2ecf20Sopenharmony_ci {REG_ESAI_TX3, 0x00000000}, 8448c2ecf20Sopenharmony_ci {REG_ESAI_TX4, 0x00000000}, 8458c2ecf20Sopenharmony_ci {REG_ESAI_TX5, 0x00000000}, 8468c2ecf20Sopenharmony_ci {REG_ESAI_TSR, 0x00000000}, 8478c2ecf20Sopenharmony_ci {REG_ESAI_SAICR, 0x00000000}, 8488c2ecf20Sopenharmony_ci {REG_ESAI_TCR, 0x00000000}, 8498c2ecf20Sopenharmony_ci {REG_ESAI_TCCR, 0x00000000}, 8508c2ecf20Sopenharmony_ci {REG_ESAI_RCR, 0x00000000}, 8518c2ecf20Sopenharmony_ci {REG_ESAI_RCCR, 0x00000000}, 8528c2ecf20Sopenharmony_ci {REG_ESAI_TSMA, 0x0000ffff}, 8538c2ecf20Sopenharmony_ci {REG_ESAI_TSMB, 0x0000ffff}, 8548c2ecf20Sopenharmony_ci {REG_ESAI_RSMA, 0x0000ffff}, 8558c2ecf20Sopenharmony_ci {REG_ESAI_RSMB, 0x0000ffff}, 8568c2ecf20Sopenharmony_ci {REG_ESAI_PRRC, 0x00000000}, 8578c2ecf20Sopenharmony_ci {REG_ESAI_PCRC, 0x00000000}, 8588c2ecf20Sopenharmony_ci}; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci switch (reg) { 8638c2ecf20Sopenharmony_ci case REG_ESAI_ERDR: 8648c2ecf20Sopenharmony_ci case REG_ESAI_ECR: 8658c2ecf20Sopenharmony_ci case REG_ESAI_ESR: 8668c2ecf20Sopenharmony_ci case REG_ESAI_TFCR: 8678c2ecf20Sopenharmony_ci case REG_ESAI_TFSR: 8688c2ecf20Sopenharmony_ci case REG_ESAI_RFCR: 8698c2ecf20Sopenharmony_ci case REG_ESAI_RFSR: 8708c2ecf20Sopenharmony_ci case REG_ESAI_RX0: 8718c2ecf20Sopenharmony_ci case REG_ESAI_RX1: 8728c2ecf20Sopenharmony_ci case REG_ESAI_RX2: 8738c2ecf20Sopenharmony_ci case REG_ESAI_RX3: 8748c2ecf20Sopenharmony_ci case REG_ESAI_SAISR: 8758c2ecf20Sopenharmony_ci case REG_ESAI_SAICR: 8768c2ecf20Sopenharmony_ci case REG_ESAI_TCR: 8778c2ecf20Sopenharmony_ci case REG_ESAI_TCCR: 8788c2ecf20Sopenharmony_ci case REG_ESAI_RCR: 8798c2ecf20Sopenharmony_ci case REG_ESAI_RCCR: 8808c2ecf20Sopenharmony_ci case REG_ESAI_TSMA: 8818c2ecf20Sopenharmony_ci case REG_ESAI_TSMB: 8828c2ecf20Sopenharmony_ci case REG_ESAI_RSMA: 8838c2ecf20Sopenharmony_ci case REG_ESAI_RSMB: 8848c2ecf20Sopenharmony_ci case REG_ESAI_PRRC: 8858c2ecf20Sopenharmony_ci case REG_ESAI_PCRC: 8868c2ecf20Sopenharmony_ci return true; 8878c2ecf20Sopenharmony_ci default: 8888c2ecf20Sopenharmony_ci return false; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci switch (reg) { 8958c2ecf20Sopenharmony_ci case REG_ESAI_ERDR: 8968c2ecf20Sopenharmony_ci case REG_ESAI_ESR: 8978c2ecf20Sopenharmony_ci case REG_ESAI_TFSR: 8988c2ecf20Sopenharmony_ci case REG_ESAI_RFSR: 8998c2ecf20Sopenharmony_ci case REG_ESAI_RX0: 9008c2ecf20Sopenharmony_ci case REG_ESAI_RX1: 9018c2ecf20Sopenharmony_ci case REG_ESAI_RX2: 9028c2ecf20Sopenharmony_ci case REG_ESAI_RX3: 9038c2ecf20Sopenharmony_ci case REG_ESAI_SAISR: 9048c2ecf20Sopenharmony_ci return true; 9058c2ecf20Sopenharmony_ci default: 9068c2ecf20Sopenharmony_ci return false; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci switch (reg) { 9138c2ecf20Sopenharmony_ci case REG_ESAI_ETDR: 9148c2ecf20Sopenharmony_ci case REG_ESAI_ECR: 9158c2ecf20Sopenharmony_ci case REG_ESAI_TFCR: 9168c2ecf20Sopenharmony_ci case REG_ESAI_RFCR: 9178c2ecf20Sopenharmony_ci case REG_ESAI_TX0: 9188c2ecf20Sopenharmony_ci case REG_ESAI_TX1: 9198c2ecf20Sopenharmony_ci case REG_ESAI_TX2: 9208c2ecf20Sopenharmony_ci case REG_ESAI_TX3: 9218c2ecf20Sopenharmony_ci case REG_ESAI_TX4: 9228c2ecf20Sopenharmony_ci case REG_ESAI_TX5: 9238c2ecf20Sopenharmony_ci case REG_ESAI_TSR: 9248c2ecf20Sopenharmony_ci case REG_ESAI_SAICR: 9258c2ecf20Sopenharmony_ci case REG_ESAI_TCR: 9268c2ecf20Sopenharmony_ci case REG_ESAI_TCCR: 9278c2ecf20Sopenharmony_ci case REG_ESAI_RCR: 9288c2ecf20Sopenharmony_ci case REG_ESAI_RCCR: 9298c2ecf20Sopenharmony_ci case REG_ESAI_TSMA: 9308c2ecf20Sopenharmony_ci case REG_ESAI_TSMB: 9318c2ecf20Sopenharmony_ci case REG_ESAI_RSMA: 9328c2ecf20Sopenharmony_ci case REG_ESAI_RSMB: 9338c2ecf20Sopenharmony_ci case REG_ESAI_PRRC: 9348c2ecf20Sopenharmony_ci case REG_ESAI_PCRC: 9358c2ecf20Sopenharmony_ci return true; 9368c2ecf20Sopenharmony_ci default: 9378c2ecf20Sopenharmony_ci return false; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic const struct regmap_config fsl_esai_regmap_config = { 9428c2ecf20Sopenharmony_ci .reg_bits = 32, 9438c2ecf20Sopenharmony_ci .reg_stride = 4, 9448c2ecf20Sopenharmony_ci .val_bits = 32, 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci .max_register = REG_ESAI_PCRC, 9478c2ecf20Sopenharmony_ci .reg_defaults = fsl_esai_reg_defaults, 9488c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults), 9498c2ecf20Sopenharmony_ci .readable_reg = fsl_esai_readable_reg, 9508c2ecf20Sopenharmony_ci .volatile_reg = fsl_esai_volatile_reg, 9518c2ecf20Sopenharmony_ci .writeable_reg = fsl_esai_writeable_reg, 9528c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 9538c2ecf20Sopenharmony_ci}; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int fsl_esai_probe(struct platform_device *pdev) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 9588c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv; 9598c2ecf20Sopenharmony_ci struct resource *res; 9608c2ecf20Sopenharmony_ci const __be32 *iprop; 9618c2ecf20Sopenharmony_ci void __iomem *regs; 9628c2ecf20Sopenharmony_ci int irq, ret; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL); 9658c2ecf20Sopenharmony_ci if (!esai_priv) 9668c2ecf20Sopenharmony_ci return -ENOMEM; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci esai_priv->pdev = pdev; 9698c2ecf20Sopenharmony_ci snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci esai_priv->soc = of_device_get_match_data(&pdev->dev); 9728c2ecf20Sopenharmony_ci if (!esai_priv->soc) { 9738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get soc data\n"); 9748c2ecf20Sopenharmony_ci return -ENODEV; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* Get the addresses and IRQ */ 9788c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9798c2ecf20Sopenharmony_ci regs = devm_ioremap_resource(&pdev->dev, res); 9808c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 9818c2ecf20Sopenharmony_ci return PTR_ERR(regs); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, 9848c2ecf20Sopenharmony_ci "core", regs, &fsl_esai_regmap_config); 9858c2ecf20Sopenharmony_ci if (IS_ERR(esai_priv->regmap)) { 9868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to init regmap: %ld\n", 9878c2ecf20Sopenharmony_ci PTR_ERR(esai_priv->regmap)); 9888c2ecf20Sopenharmony_ci return PTR_ERR(esai_priv->regmap); 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci esai_priv->coreclk = devm_clk_get(&pdev->dev, "core"); 9928c2ecf20Sopenharmony_ci if (IS_ERR(esai_priv->coreclk)) { 9938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get core clock: %ld\n", 9948c2ecf20Sopenharmony_ci PTR_ERR(esai_priv->coreclk)); 9958c2ecf20Sopenharmony_ci return PTR_ERR(esai_priv->coreclk); 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal"); 9998c2ecf20Sopenharmony_ci if (IS_ERR(esai_priv->extalclk)) 10008c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to get extal clock: %ld\n", 10018c2ecf20Sopenharmony_ci PTR_ERR(esai_priv->extalclk)); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys"); 10048c2ecf20Sopenharmony_ci if (IS_ERR(esai_priv->fsysclk)) 10058c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", 10068c2ecf20Sopenharmony_ci PTR_ERR(esai_priv->fsysclk)); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba"); 10098c2ecf20Sopenharmony_ci if (IS_ERR(esai_priv->spbaclk)) 10108c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to get spba clock: %ld\n", 10118c2ecf20Sopenharmony_ci PTR_ERR(esai_priv->spbaclk)); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 10148c2ecf20Sopenharmony_ci if (irq < 0) 10158c2ecf20Sopenharmony_ci return irq; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, esai_isr, IRQF_SHARED, 10188c2ecf20Sopenharmony_ci esai_priv->name, esai_priv); 10198c2ecf20Sopenharmony_ci if (ret) { 10208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to claim irq %u\n", irq); 10218c2ecf20Sopenharmony_ci return ret; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Set a default slot number */ 10258c2ecf20Sopenharmony_ci esai_priv->slots = 2; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Set a default master/slave state */ 10288c2ecf20Sopenharmony_ci esai_priv->slave_mode = true; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Determine the FIFO depth */ 10318c2ecf20Sopenharmony_ci iprop = of_get_property(np, "fsl,fifo-depth", NULL); 10328c2ecf20Sopenharmony_ci if (iprop) 10338c2ecf20Sopenharmony_ci esai_priv->fifo_depth = be32_to_cpup(iprop); 10348c2ecf20Sopenharmony_ci else 10358c2ecf20Sopenharmony_ci esai_priv->fifo_depth = 64; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci esai_priv->dma_params_tx.maxburst = 16; 10388c2ecf20Sopenharmony_ci esai_priv->dma_params_rx.maxburst = 16; 10398c2ecf20Sopenharmony_ci esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR; 10408c2ecf20Sopenharmony_ci esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci esai_priv->synchronous = 10438c2ecf20Sopenharmony_ci of_property_read_bool(np, "fsl,esai-synchronous"); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Implement full symmetry for synchronous mode */ 10468c2ecf20Sopenharmony_ci if (esai_priv->synchronous) { 10478c2ecf20Sopenharmony_ci fsl_esai_dai.symmetric_rates = 1; 10488c2ecf20Sopenharmony_ci fsl_esai_dai.symmetric_channels = 1; 10498c2ecf20Sopenharmony_ci fsl_esai_dai.symmetric_samplebits = 1; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, esai_priv); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci spin_lock_init(&esai_priv->lock); 10558c2ecf20Sopenharmony_ci ret = fsl_esai_hw_init(esai_priv); 10568c2ecf20Sopenharmony_ci if (ret) 10578c2ecf20Sopenharmony_ci return ret; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci esai_priv->tx_mask = 0xFFFFFFFF; 10608c2ecf20Sopenharmony_ci esai_priv->rx_mask = 0xFFFFFFFF; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* Clear the TSMA, TSMB, RSMA, RSMB */ 10638c2ecf20Sopenharmony_ci regmap_write(esai_priv->regmap, REG_ESAI_TSMA, 0); 10648c2ecf20Sopenharmony_ci regmap_write(esai_priv->regmap, REG_ESAI_TSMB, 0); 10658c2ecf20Sopenharmony_ci regmap_write(esai_priv->regmap, REG_ESAI_RSMA, 0); 10668c2ecf20Sopenharmony_ci regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component, 10698c2ecf20Sopenharmony_ci &fsl_esai_dai, 1); 10708c2ecf20Sopenharmony_ci if (ret) { 10718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); 10728c2ecf20Sopenharmony_ci return ret; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci INIT_WORK(&esai_priv->work, fsl_esai_hw_reset); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci regcache_cache_only(esai_priv->regmap, true); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE); 10828c2ecf20Sopenharmony_ci if (ret) 10838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci return ret; 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic int fsl_esai_remove(struct platform_device *pdev) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct fsl_esai *esai_priv = platform_get_drvdata(pdev); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 10938c2ecf20Sopenharmony_ci cancel_work_sync(&esai_priv->work); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci return 0; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_esai_dt_ids[] = { 10998c2ecf20Sopenharmony_ci { .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 }, 11008c2ecf20Sopenharmony_ci { .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 }, 11018c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull }, 11028c2ecf20Sopenharmony_ci {} 11038c2ecf20Sopenharmony_ci}; 11048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11078c2ecf20Sopenharmony_cistatic int fsl_esai_runtime_resume(struct device *dev) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct fsl_esai *esai = dev_get_drvdata(dev); 11108c2ecf20Sopenharmony_ci int ret; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* 11138c2ecf20Sopenharmony_ci * Some platforms might use the same bit to gate all three or two of 11148c2ecf20Sopenharmony_ci * clocks, so keep all clocks open/close at the same time for safety 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci ret = clk_prepare_enable(esai->coreclk); 11178c2ecf20Sopenharmony_ci if (ret) 11188c2ecf20Sopenharmony_ci return ret; 11198c2ecf20Sopenharmony_ci if (!IS_ERR(esai->spbaclk)) { 11208c2ecf20Sopenharmony_ci ret = clk_prepare_enable(esai->spbaclk); 11218c2ecf20Sopenharmony_ci if (ret) 11228c2ecf20Sopenharmony_ci goto err_spbaclk; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci if (!IS_ERR(esai->extalclk)) { 11258c2ecf20Sopenharmony_ci ret = clk_prepare_enable(esai->extalclk); 11268c2ecf20Sopenharmony_ci if (ret) 11278c2ecf20Sopenharmony_ci goto err_extalclk; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci if (!IS_ERR(esai->fsysclk)) { 11308c2ecf20Sopenharmony_ci ret = clk_prepare_enable(esai->fsysclk); 11318c2ecf20Sopenharmony_ci if (ret) 11328c2ecf20Sopenharmony_ci goto err_fsysclk; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci regcache_cache_only(esai->regmap, false); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci ret = fsl_esai_register_restore(esai); 11388c2ecf20Sopenharmony_ci if (ret) 11398c2ecf20Sopenharmony_ci goto err_regcache_sync; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cierr_regcache_sync: 11448c2ecf20Sopenharmony_ci if (!IS_ERR(esai->fsysclk)) 11458c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->fsysclk); 11468c2ecf20Sopenharmony_cierr_fsysclk: 11478c2ecf20Sopenharmony_ci if (!IS_ERR(esai->extalclk)) 11488c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->extalclk); 11498c2ecf20Sopenharmony_cierr_extalclk: 11508c2ecf20Sopenharmony_ci if (!IS_ERR(esai->spbaclk)) 11518c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->spbaclk); 11528c2ecf20Sopenharmony_cierr_spbaclk: 11538c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->coreclk); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic int fsl_esai_runtime_suspend(struct device *dev) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct fsl_esai *esai = dev_get_drvdata(dev); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci regcache_cache_only(esai->regmap, true); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (!IS_ERR(esai->fsysclk)) 11658c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->fsysclk); 11668c2ecf20Sopenharmony_ci if (!IS_ERR(esai->extalclk)) 11678c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->extalclk); 11688c2ecf20Sopenharmony_ci if (!IS_ERR(esai->spbaclk)) 11698c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->spbaclk); 11708c2ecf20Sopenharmony_ci clk_disable_unprepare(esai->coreclk); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fsl_esai_pm_ops = { 11778c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend, 11788c2ecf20Sopenharmony_ci fsl_esai_runtime_resume, 11798c2ecf20Sopenharmony_ci NULL) 11808c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 11818c2ecf20Sopenharmony_ci pm_runtime_force_resume) 11828c2ecf20Sopenharmony_ci}; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic struct platform_driver fsl_esai_driver = { 11858c2ecf20Sopenharmony_ci .probe = fsl_esai_probe, 11868c2ecf20Sopenharmony_ci .remove = fsl_esai_remove, 11878c2ecf20Sopenharmony_ci .driver = { 11888c2ecf20Sopenharmony_ci .name = "fsl-esai-dai", 11898c2ecf20Sopenharmony_ci .pm = &fsl_esai_pm_ops, 11908c2ecf20Sopenharmony_ci .of_match_table = fsl_esai_dt_ids, 11918c2ecf20Sopenharmony_ci }, 11928c2ecf20Sopenharmony_ci}; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cimodule_platform_driver(fsl_esai_driver); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 11978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale ESAI CPU DAI driver"); 11988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 11998c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:fsl-esai-dai"); 1200