1*43d24e76SNicolin Chen /* 2*43d24e76SNicolin Chen * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver 3*43d24e76SNicolin Chen * 4*43d24e76SNicolin Chen * Copyright (C) 2014 Freescale Semiconductor, Inc. 5*43d24e76SNicolin Chen * 6*43d24e76SNicolin Chen * This file is licensed under the terms of the GNU General Public License 7*43d24e76SNicolin Chen * version 2. This program is licensed "as is" without any warranty of any 8*43d24e76SNicolin Chen * kind, whether express or implied. 9*43d24e76SNicolin Chen */ 10*43d24e76SNicolin Chen 11*43d24e76SNicolin Chen #include <linux/clk.h> 12*43d24e76SNicolin Chen #include <linux/dmaengine.h> 13*43d24e76SNicolin Chen #include <linux/module.h> 14*43d24e76SNicolin Chen #include <linux/of_irq.h> 15*43d24e76SNicolin Chen #include <linux/of_platform.h> 16*43d24e76SNicolin Chen #include <sound/dmaengine_pcm.h> 17*43d24e76SNicolin Chen #include <sound/pcm_params.h> 18*43d24e76SNicolin Chen 19*43d24e76SNicolin Chen #include "fsl_esai.h" 20*43d24e76SNicolin Chen #include "imx-pcm.h" 21*43d24e76SNicolin Chen 22*43d24e76SNicolin Chen #define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000 23*43d24e76SNicolin Chen #define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 24*43d24e76SNicolin Chen SNDRV_PCM_FMTBIT_S16_LE | \ 25*43d24e76SNicolin Chen SNDRV_PCM_FMTBIT_S20_3LE | \ 26*43d24e76SNicolin Chen SNDRV_PCM_FMTBIT_S24_LE) 27*43d24e76SNicolin Chen 28*43d24e76SNicolin Chen /** 29*43d24e76SNicolin Chen * fsl_esai: ESAI private data 30*43d24e76SNicolin Chen * 31*43d24e76SNicolin Chen * @dma_params_rx: DMA parameters for receive channel 32*43d24e76SNicolin Chen * @dma_params_tx: DMA parameters for transmit channel 33*43d24e76SNicolin Chen * @pdev: platform device pointer 34*43d24e76SNicolin Chen * @regmap: regmap handler 35*43d24e76SNicolin Chen * @coreclk: clock source to access register 36*43d24e76SNicolin Chen * @extalclk: esai clock source to derive HCK, SCK and FS 37*43d24e76SNicolin Chen * @fsysclk: system clock source to derive HCK, SCK and FS 38*43d24e76SNicolin Chen * @fifo_depth: depth of tx/rx FIFO 39*43d24e76SNicolin Chen * @slot_width: width of each DAI slot 40*43d24e76SNicolin Chen * @hck_rate: clock rate of desired HCKx clock 41*43d24e76SNicolin Chen * @sck_div: if using PSR/PM dividers for SCKx clock 42*43d24e76SNicolin Chen * @slave_mode: if fully using DAI slave mode 43*43d24e76SNicolin Chen * @synchronous: if using tx/rx synchronous mode 44*43d24e76SNicolin Chen * @name: driver name 45*43d24e76SNicolin Chen */ 46*43d24e76SNicolin Chen struct fsl_esai { 47*43d24e76SNicolin Chen struct snd_dmaengine_dai_dma_data dma_params_rx; 48*43d24e76SNicolin Chen struct snd_dmaengine_dai_dma_data dma_params_tx; 49*43d24e76SNicolin Chen struct platform_device *pdev; 50*43d24e76SNicolin Chen struct regmap *regmap; 51*43d24e76SNicolin Chen struct clk *coreclk; 52*43d24e76SNicolin Chen struct clk *extalclk; 53*43d24e76SNicolin Chen struct clk *fsysclk; 54*43d24e76SNicolin Chen u32 fifo_depth; 55*43d24e76SNicolin Chen u32 slot_width; 56*43d24e76SNicolin Chen u32 hck_rate[2]; 57*43d24e76SNicolin Chen bool sck_div[2]; 58*43d24e76SNicolin Chen bool slave_mode; 59*43d24e76SNicolin Chen bool synchronous; 60*43d24e76SNicolin Chen char name[32]; 61*43d24e76SNicolin Chen }; 62*43d24e76SNicolin Chen 63*43d24e76SNicolin Chen static irqreturn_t esai_isr(int irq, void *devid) 64*43d24e76SNicolin Chen { 65*43d24e76SNicolin Chen struct fsl_esai *esai_priv = (struct fsl_esai *)devid; 66*43d24e76SNicolin Chen struct platform_device *pdev = esai_priv->pdev; 67*43d24e76SNicolin Chen u32 esr; 68*43d24e76SNicolin Chen 69*43d24e76SNicolin Chen regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); 70*43d24e76SNicolin Chen 71*43d24e76SNicolin Chen if (esr & ESAI_ESR_TINIT_MASK) 72*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Transmition Initialized\n"); 73*43d24e76SNicolin Chen 74*43d24e76SNicolin Chen if (esr & ESAI_ESR_RFF_MASK) 75*43d24e76SNicolin Chen dev_warn(&pdev->dev, "isr: Receiving overrun\n"); 76*43d24e76SNicolin Chen 77*43d24e76SNicolin Chen if (esr & ESAI_ESR_TFE_MASK) 78*43d24e76SNicolin Chen dev_warn(&pdev->dev, "isr: Transmition underrun\n"); 79*43d24e76SNicolin Chen 80*43d24e76SNicolin Chen if (esr & ESAI_ESR_TLS_MASK) 81*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n"); 82*43d24e76SNicolin Chen 83*43d24e76SNicolin Chen if (esr & ESAI_ESR_TDE_MASK) 84*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Transmition data exception\n"); 85*43d24e76SNicolin Chen 86*43d24e76SNicolin Chen if (esr & ESAI_ESR_TED_MASK) 87*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Transmitting even slots\n"); 88*43d24e76SNicolin Chen 89*43d24e76SNicolin Chen if (esr & ESAI_ESR_TD_MASK) 90*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Transmitting data\n"); 91*43d24e76SNicolin Chen 92*43d24e76SNicolin Chen if (esr & ESAI_ESR_RLS_MASK) 93*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Just received the last slot\n"); 94*43d24e76SNicolin Chen 95*43d24e76SNicolin Chen if (esr & ESAI_ESR_RDE_MASK) 96*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Receiving data exception\n"); 97*43d24e76SNicolin Chen 98*43d24e76SNicolin Chen if (esr & ESAI_ESR_RED_MASK) 99*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Receiving even slots\n"); 100*43d24e76SNicolin Chen 101*43d24e76SNicolin Chen if (esr & ESAI_ESR_RD_MASK) 102*43d24e76SNicolin Chen dev_dbg(&pdev->dev, "isr: Receiving data\n"); 103*43d24e76SNicolin Chen 104*43d24e76SNicolin Chen return IRQ_HANDLED; 105*43d24e76SNicolin Chen } 106*43d24e76SNicolin Chen 107*43d24e76SNicolin Chen /** 108*43d24e76SNicolin Chen * This function is used to calculate the divisors of psr, pm, fp and it is 109*43d24e76SNicolin Chen * supposed to be called in set_dai_sysclk() and set_bclk(). 110*43d24e76SNicolin Chen * 111*43d24e76SNicolin Chen * @ratio: desired overall ratio for the paticipating dividers 112*43d24e76SNicolin Chen * @usefp: for HCK setting, there is no need to set fp divider 113*43d24e76SNicolin Chen * @fp: bypass other dividers by setting fp directly if fp != 0 114*43d24e76SNicolin Chen * @tx: current setting is for playback or capture 115*43d24e76SNicolin Chen */ 116*43d24e76SNicolin Chen static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio, 117*43d24e76SNicolin Chen bool usefp, u32 fp) 118*43d24e76SNicolin Chen { 119*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 120*43d24e76SNicolin Chen u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j; 121*43d24e76SNicolin Chen 122*43d24e76SNicolin Chen maxfp = usefp ? 16 : 1; 123*43d24e76SNicolin Chen 124*43d24e76SNicolin Chen if (usefp && fp) 125*43d24e76SNicolin Chen goto out_fp; 126*43d24e76SNicolin Chen 127*43d24e76SNicolin Chen if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) { 128*43d24e76SNicolin Chen dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n", 129*43d24e76SNicolin Chen 2 * 8 * 256 * maxfp); 130*43d24e76SNicolin Chen return -EINVAL; 131*43d24e76SNicolin Chen } else if (ratio % 2) { 132*43d24e76SNicolin Chen dev_err(dai->dev, "the raio must be even if using upper divider\n"); 133*43d24e76SNicolin Chen return -EINVAL; 134*43d24e76SNicolin Chen } 135*43d24e76SNicolin Chen 136*43d24e76SNicolin Chen ratio /= 2; 137*43d24e76SNicolin Chen 138*43d24e76SNicolin Chen psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8; 139*43d24e76SNicolin Chen 140*43d24e76SNicolin Chen /* Set the max fluctuation -- 0.1% of the max devisor */ 141*43d24e76SNicolin Chen savesub = (psr ? 1 : 8) * 256 * maxfp / 1000; 142*43d24e76SNicolin Chen 143*43d24e76SNicolin Chen /* Find the best value for PM */ 144*43d24e76SNicolin Chen for (i = 1; i <= 256; i++) { 145*43d24e76SNicolin Chen for (j = 1; j <= maxfp; j++) { 146*43d24e76SNicolin Chen /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */ 147*43d24e76SNicolin Chen prod = (psr ? 1 : 8) * i * j; 148*43d24e76SNicolin Chen 149*43d24e76SNicolin Chen if (prod == ratio) 150*43d24e76SNicolin Chen sub = 0; 151*43d24e76SNicolin Chen else if (prod / ratio == 1) 152*43d24e76SNicolin Chen sub = prod - ratio; 153*43d24e76SNicolin Chen else if (ratio / prod == 1) 154*43d24e76SNicolin Chen sub = ratio - prod; 155*43d24e76SNicolin Chen else 156*43d24e76SNicolin Chen continue; 157*43d24e76SNicolin Chen 158*43d24e76SNicolin Chen /* Calculate the fraction */ 159*43d24e76SNicolin Chen sub = sub * 1000 / ratio; 160*43d24e76SNicolin Chen if (sub < savesub) { 161*43d24e76SNicolin Chen savesub = sub; 162*43d24e76SNicolin Chen pm = i; 163*43d24e76SNicolin Chen fp = j; 164*43d24e76SNicolin Chen } 165*43d24e76SNicolin Chen 166*43d24e76SNicolin Chen /* We are lucky */ 167*43d24e76SNicolin Chen if (savesub == 0) 168*43d24e76SNicolin Chen goto out; 169*43d24e76SNicolin Chen } 170*43d24e76SNicolin Chen } 171*43d24e76SNicolin Chen 172*43d24e76SNicolin Chen if (pm == 999) { 173*43d24e76SNicolin Chen dev_err(dai->dev, "failed to calculate proper divisors\n"); 174*43d24e76SNicolin Chen return -EINVAL; 175*43d24e76SNicolin Chen } 176*43d24e76SNicolin Chen 177*43d24e76SNicolin Chen out: 178*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 179*43d24e76SNicolin Chen ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK, 180*43d24e76SNicolin Chen psr | ESAI_xCCR_xPM(pm)); 181*43d24e76SNicolin Chen 182*43d24e76SNicolin Chen out_fp: 183*43d24e76SNicolin Chen /* Bypass fp if not being required */ 184*43d24e76SNicolin Chen if (maxfp <= 1) 185*43d24e76SNicolin Chen return 0; 186*43d24e76SNicolin Chen 187*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 188*43d24e76SNicolin Chen ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp)); 189*43d24e76SNicolin Chen 190*43d24e76SNicolin Chen return 0; 191*43d24e76SNicolin Chen } 192*43d24e76SNicolin Chen 193*43d24e76SNicolin Chen /** 194*43d24e76SNicolin Chen * This function mainly configures the clock frequency of MCLK (HCKT/HCKR) 195*43d24e76SNicolin Chen * 196*43d24e76SNicolin Chen * @Parameters: 197*43d24e76SNicolin Chen * clk_id: The clock source of HCKT/HCKR 198*43d24e76SNicolin Chen * (Input from outside; output from inside, FSYS or EXTAL) 199*43d24e76SNicolin Chen * freq: The required clock rate of HCKT/HCKR 200*43d24e76SNicolin Chen * dir: The clock direction of HCKT/HCKR 201*43d24e76SNicolin Chen * 202*43d24e76SNicolin Chen * Note: If the direction is input, we do not care about clk_id. 203*43d24e76SNicolin Chen */ 204*43d24e76SNicolin Chen static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 205*43d24e76SNicolin Chen unsigned int freq, int dir) 206*43d24e76SNicolin Chen { 207*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 208*43d24e76SNicolin Chen struct clk *clksrc = esai_priv->extalclk; 209*43d24e76SNicolin Chen bool tx = clk_id <= ESAI_HCKT_EXTAL; 210*43d24e76SNicolin Chen bool in = dir == SND_SOC_CLOCK_IN; 211*43d24e76SNicolin Chen u32 ret, ratio, ecr = 0; 212*43d24e76SNicolin Chen unsigned long clk_rate; 213*43d24e76SNicolin Chen 214*43d24e76SNicolin Chen /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ 215*43d24e76SNicolin Chen esai_priv->sck_div[tx] = true; 216*43d24e76SNicolin Chen 217*43d24e76SNicolin Chen /* Set the direction of HCKT/HCKR pins */ 218*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), 219*43d24e76SNicolin Chen ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD); 220*43d24e76SNicolin Chen 221*43d24e76SNicolin Chen if (in) 222*43d24e76SNicolin Chen goto out; 223*43d24e76SNicolin Chen 224*43d24e76SNicolin Chen switch (clk_id) { 225*43d24e76SNicolin Chen case ESAI_HCKT_FSYS: 226*43d24e76SNicolin Chen case ESAI_HCKR_FSYS: 227*43d24e76SNicolin Chen clksrc = esai_priv->fsysclk; 228*43d24e76SNicolin Chen break; 229*43d24e76SNicolin Chen case ESAI_HCKT_EXTAL: 230*43d24e76SNicolin Chen ecr |= ESAI_ECR_ETI; 231*43d24e76SNicolin Chen case ESAI_HCKR_EXTAL: 232*43d24e76SNicolin Chen ecr |= ESAI_ECR_ERI; 233*43d24e76SNicolin Chen break; 234*43d24e76SNicolin Chen default: 235*43d24e76SNicolin Chen return -EINVAL; 236*43d24e76SNicolin Chen } 237*43d24e76SNicolin Chen 238*43d24e76SNicolin Chen if (IS_ERR(clksrc)) { 239*43d24e76SNicolin Chen dev_err(dai->dev, "no assigned %s clock\n", 240*43d24e76SNicolin Chen clk_id % 2 ? "extal" : "fsys"); 241*43d24e76SNicolin Chen return PTR_ERR(clksrc); 242*43d24e76SNicolin Chen } 243*43d24e76SNicolin Chen clk_rate = clk_get_rate(clksrc); 244*43d24e76SNicolin Chen 245*43d24e76SNicolin Chen ratio = clk_rate / freq; 246*43d24e76SNicolin Chen if (ratio * freq > clk_rate) 247*43d24e76SNicolin Chen ret = ratio * freq - clk_rate; 248*43d24e76SNicolin Chen else if (ratio * freq < clk_rate) 249*43d24e76SNicolin Chen ret = clk_rate - ratio * freq; 250*43d24e76SNicolin Chen else 251*43d24e76SNicolin Chen ret = 0; 252*43d24e76SNicolin Chen 253*43d24e76SNicolin Chen /* Block if clock source can not be divided into the required rate */ 254*43d24e76SNicolin Chen if (ret != 0 && clk_rate / ret < 1000) { 255*43d24e76SNicolin Chen dev_err(dai->dev, "failed to derive required HCK%c rate\n", 256*43d24e76SNicolin Chen tx ? 'T' : 'R'); 257*43d24e76SNicolin Chen return -EINVAL; 258*43d24e76SNicolin Chen } 259*43d24e76SNicolin Chen 260*43d24e76SNicolin Chen if (ratio == 1) { 261*43d24e76SNicolin Chen /* Bypass all the dividers if not being needed */ 262*43d24e76SNicolin Chen ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; 263*43d24e76SNicolin Chen goto out; 264*43d24e76SNicolin Chen } 265*43d24e76SNicolin Chen 266*43d24e76SNicolin Chen ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); 267*43d24e76SNicolin Chen if (ret) 268*43d24e76SNicolin Chen return ret; 269*43d24e76SNicolin Chen 270*43d24e76SNicolin Chen esai_priv->sck_div[tx] = false; 271*43d24e76SNicolin Chen 272*43d24e76SNicolin Chen out: 273*43d24e76SNicolin Chen esai_priv->hck_rate[tx] = freq; 274*43d24e76SNicolin Chen 275*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, 276*43d24e76SNicolin Chen tx ? ESAI_ECR_ETI | ESAI_ECR_ETO : 277*43d24e76SNicolin Chen ESAI_ECR_ERI | ESAI_ECR_ERO, ecr); 278*43d24e76SNicolin Chen 279*43d24e76SNicolin Chen return 0; 280*43d24e76SNicolin Chen } 281*43d24e76SNicolin Chen 282*43d24e76SNicolin Chen /** 283*43d24e76SNicolin Chen * This function configures the related dividers according to the bclk rate 284*43d24e76SNicolin Chen */ 285*43d24e76SNicolin Chen static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) 286*43d24e76SNicolin Chen { 287*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 288*43d24e76SNicolin Chen u32 hck_rate = esai_priv->hck_rate[tx]; 289*43d24e76SNicolin Chen u32 sub, ratio = hck_rate / freq; 290*43d24e76SNicolin Chen 291*43d24e76SNicolin Chen /* Don't apply for fully slave mode*/ 292*43d24e76SNicolin Chen if (esai_priv->slave_mode) 293*43d24e76SNicolin Chen return 0; 294*43d24e76SNicolin Chen 295*43d24e76SNicolin Chen if (ratio * freq > hck_rate) 296*43d24e76SNicolin Chen sub = ratio * freq - hck_rate; 297*43d24e76SNicolin Chen else if (ratio * freq < hck_rate) 298*43d24e76SNicolin Chen sub = hck_rate - ratio * freq; 299*43d24e76SNicolin Chen else 300*43d24e76SNicolin Chen sub = 0; 301*43d24e76SNicolin Chen 302*43d24e76SNicolin Chen /* Block if clock source can not be divided into the required rate */ 303*43d24e76SNicolin Chen if (sub != 0 && hck_rate / sub < 1000) { 304*43d24e76SNicolin Chen dev_err(dai->dev, "failed to derive required SCK%c rate\n", 305*43d24e76SNicolin Chen tx ? 'T' : 'R'); 306*43d24e76SNicolin Chen return -EINVAL; 307*43d24e76SNicolin Chen } 308*43d24e76SNicolin Chen 309*43d24e76SNicolin Chen if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { 310*43d24e76SNicolin Chen dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); 311*43d24e76SNicolin Chen return -EINVAL; 312*43d24e76SNicolin Chen } 313*43d24e76SNicolin Chen 314*43d24e76SNicolin Chen return fsl_esai_divisor_cal(dai, tx, ratio, true, 315*43d24e76SNicolin Chen esai_priv->sck_div[tx] ? 0 : ratio); 316*43d24e76SNicolin Chen } 317*43d24e76SNicolin Chen 318*43d24e76SNicolin Chen static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, 319*43d24e76SNicolin Chen u32 rx_mask, int slots, int slot_width) 320*43d24e76SNicolin Chen { 321*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 322*43d24e76SNicolin Chen 323*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, 324*43d24e76SNicolin Chen ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); 325*43d24e76SNicolin Chen 326*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA, 327*43d24e76SNicolin Chen ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask)); 328*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB, 329*43d24e76SNicolin Chen ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(tx_mask)); 330*43d24e76SNicolin Chen 331*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, 332*43d24e76SNicolin Chen ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); 333*43d24e76SNicolin Chen 334*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA, 335*43d24e76SNicolin Chen ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask)); 336*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB, 337*43d24e76SNicolin Chen ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(rx_mask)); 338*43d24e76SNicolin Chen 339*43d24e76SNicolin Chen esai_priv->slot_width = slot_width; 340*43d24e76SNicolin Chen 341*43d24e76SNicolin Chen return 0; 342*43d24e76SNicolin Chen } 343*43d24e76SNicolin Chen 344*43d24e76SNicolin Chen static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 345*43d24e76SNicolin Chen { 346*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 347*43d24e76SNicolin Chen u32 xcr = 0, xccr = 0, mask; 348*43d24e76SNicolin Chen 349*43d24e76SNicolin Chen /* DAI mode */ 350*43d24e76SNicolin Chen switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 351*43d24e76SNicolin Chen case SND_SOC_DAIFMT_I2S: 352*43d24e76SNicolin Chen /* Data on rising edge of bclk, frame low, 1clk before data */ 353*43d24e76SNicolin Chen xcr |= ESAI_xCR_xFSR; 354*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 355*43d24e76SNicolin Chen break; 356*43d24e76SNicolin Chen case SND_SOC_DAIFMT_LEFT_J: 357*43d24e76SNicolin Chen /* Data on rising edge of bclk, frame high */ 358*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 359*43d24e76SNicolin Chen break; 360*43d24e76SNicolin Chen case SND_SOC_DAIFMT_RIGHT_J: 361*43d24e76SNicolin Chen /* Data on rising edge of bclk, frame high, right aligned */ 362*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA; 363*43d24e76SNicolin Chen break; 364*43d24e76SNicolin Chen case SND_SOC_DAIFMT_DSP_A: 365*43d24e76SNicolin Chen /* Data on rising edge of bclk, frame high, 1clk before data */ 366*43d24e76SNicolin Chen xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR; 367*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 368*43d24e76SNicolin Chen break; 369*43d24e76SNicolin Chen case SND_SOC_DAIFMT_DSP_B: 370*43d24e76SNicolin Chen /* Data on rising edge of bclk, frame high */ 371*43d24e76SNicolin Chen xcr |= ESAI_xCR_xFSL; 372*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 373*43d24e76SNicolin Chen break; 374*43d24e76SNicolin Chen default: 375*43d24e76SNicolin Chen return -EINVAL; 376*43d24e76SNicolin Chen } 377*43d24e76SNicolin Chen 378*43d24e76SNicolin Chen /* DAI clock inversion */ 379*43d24e76SNicolin Chen switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 380*43d24e76SNicolin Chen case SND_SOC_DAIFMT_NB_NF: 381*43d24e76SNicolin Chen /* Nothing to do for both normal cases */ 382*43d24e76SNicolin Chen break; 383*43d24e76SNicolin Chen case SND_SOC_DAIFMT_IB_NF: 384*43d24e76SNicolin Chen /* Invert bit clock */ 385*43d24e76SNicolin Chen xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; 386*43d24e76SNicolin Chen break; 387*43d24e76SNicolin Chen case SND_SOC_DAIFMT_NB_IF: 388*43d24e76SNicolin Chen /* Invert frame clock */ 389*43d24e76SNicolin Chen xccr ^= ESAI_xCCR_xFSP; 390*43d24e76SNicolin Chen break; 391*43d24e76SNicolin Chen case SND_SOC_DAIFMT_IB_IF: 392*43d24e76SNicolin Chen /* Invert both clocks */ 393*43d24e76SNicolin Chen xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP; 394*43d24e76SNicolin Chen break; 395*43d24e76SNicolin Chen default: 396*43d24e76SNicolin Chen return -EINVAL; 397*43d24e76SNicolin Chen } 398*43d24e76SNicolin Chen 399*43d24e76SNicolin Chen esai_priv->slave_mode = false; 400*43d24e76SNicolin Chen 401*43d24e76SNicolin Chen /* DAI clock master masks */ 402*43d24e76SNicolin Chen switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 403*43d24e76SNicolin Chen case SND_SOC_DAIFMT_CBM_CFM: 404*43d24e76SNicolin Chen esai_priv->slave_mode = true; 405*43d24e76SNicolin Chen break; 406*43d24e76SNicolin Chen case SND_SOC_DAIFMT_CBS_CFM: 407*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xCKD; 408*43d24e76SNicolin Chen break; 409*43d24e76SNicolin Chen case SND_SOC_DAIFMT_CBM_CFS: 410*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xFSD; 411*43d24e76SNicolin Chen break; 412*43d24e76SNicolin Chen case SND_SOC_DAIFMT_CBS_CFS: 413*43d24e76SNicolin Chen xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; 414*43d24e76SNicolin Chen break; 415*43d24e76SNicolin Chen default: 416*43d24e76SNicolin Chen return -EINVAL; 417*43d24e76SNicolin Chen } 418*43d24e76SNicolin Chen 419*43d24e76SNicolin Chen mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR; 420*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr); 421*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr); 422*43d24e76SNicolin Chen 423*43d24e76SNicolin Chen mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP | 424*43d24e76SNicolin Chen ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA; 425*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr); 426*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr); 427*43d24e76SNicolin Chen 428*43d24e76SNicolin Chen return 0; 429*43d24e76SNicolin Chen } 430*43d24e76SNicolin Chen 431*43d24e76SNicolin Chen static int fsl_esai_startup(struct snd_pcm_substream *substream, 432*43d24e76SNicolin Chen struct snd_soc_dai *dai) 433*43d24e76SNicolin Chen { 434*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 435*43d24e76SNicolin Chen 436*43d24e76SNicolin Chen /* 437*43d24e76SNicolin Chen * Some platforms might use the same bit to gate all three or two of 438*43d24e76SNicolin Chen * clocks, so keep all clocks open/close at the same time for safety 439*43d24e76SNicolin Chen */ 440*43d24e76SNicolin Chen clk_prepare_enable(esai_priv->coreclk); 441*43d24e76SNicolin Chen if (!IS_ERR(esai_priv->extalclk)) 442*43d24e76SNicolin Chen clk_prepare_enable(esai_priv->extalclk); 443*43d24e76SNicolin Chen if (!IS_ERR(esai_priv->fsysclk)) 444*43d24e76SNicolin Chen clk_prepare_enable(esai_priv->fsysclk); 445*43d24e76SNicolin Chen 446*43d24e76SNicolin Chen if (!dai->active) { 447*43d24e76SNicolin Chen /* Reset Port C */ 448*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, 449*43d24e76SNicolin Chen ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); 450*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, 451*43d24e76SNicolin Chen ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); 452*43d24e76SNicolin Chen 453*43d24e76SNicolin Chen /* Set synchronous mode */ 454*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, 455*43d24e76SNicolin Chen ESAI_SAICR_SYNC, esai_priv->synchronous ? 456*43d24e76SNicolin Chen ESAI_SAICR_SYNC : 0); 457*43d24e76SNicolin Chen 458*43d24e76SNicolin Chen /* Set a default slot number -- 2 */ 459*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, 460*43d24e76SNicolin Chen ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); 461*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, 462*43d24e76SNicolin Chen ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); 463*43d24e76SNicolin Chen } 464*43d24e76SNicolin Chen 465*43d24e76SNicolin Chen return 0; 466*43d24e76SNicolin Chen } 467*43d24e76SNicolin Chen 468*43d24e76SNicolin Chen static int fsl_esai_hw_params(struct snd_pcm_substream *substream, 469*43d24e76SNicolin Chen struct snd_pcm_hw_params *params, 470*43d24e76SNicolin Chen struct snd_soc_dai *dai) 471*43d24e76SNicolin Chen { 472*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 473*43d24e76SNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 474*43d24e76SNicolin Chen u32 width = snd_pcm_format_width(params_format(params)); 475*43d24e76SNicolin Chen u32 channels = params_channels(params); 476*43d24e76SNicolin Chen u32 bclk, mask, val, ret; 477*43d24e76SNicolin Chen 478*43d24e76SNicolin Chen bclk = params_rate(params) * esai_priv->slot_width * 2; 479*43d24e76SNicolin Chen 480*43d24e76SNicolin Chen ret = fsl_esai_set_bclk(dai, tx, bclk); 481*43d24e76SNicolin Chen if (ret) 482*43d24e76SNicolin Chen return ret; 483*43d24e76SNicolin Chen 484*43d24e76SNicolin Chen /* Use Normal mode to support monaural audio */ 485*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 486*43d24e76SNicolin Chen ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ? 487*43d24e76SNicolin Chen ESAI_xCR_xMOD_NETWORK : 0); 488*43d24e76SNicolin Chen 489*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 490*43d24e76SNicolin Chen ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR); 491*43d24e76SNicolin Chen 492*43d24e76SNicolin Chen mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | 493*43d24e76SNicolin Chen (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); 494*43d24e76SNicolin Chen val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | 495*43d24e76SNicolin Chen (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels)); 496*43d24e76SNicolin Chen 497*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); 498*43d24e76SNicolin Chen 499*43d24e76SNicolin Chen mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); 500*43d24e76SNicolin Chen val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0); 501*43d24e76SNicolin Chen 502*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); 503*43d24e76SNicolin Chen 504*43d24e76SNicolin Chen return 0; 505*43d24e76SNicolin Chen } 506*43d24e76SNicolin Chen 507*43d24e76SNicolin Chen static void fsl_esai_shutdown(struct snd_pcm_substream *substream, 508*43d24e76SNicolin Chen struct snd_soc_dai *dai) 509*43d24e76SNicolin Chen { 510*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 511*43d24e76SNicolin Chen 512*43d24e76SNicolin Chen if (!IS_ERR(esai_priv->fsysclk)) 513*43d24e76SNicolin Chen clk_disable_unprepare(esai_priv->fsysclk); 514*43d24e76SNicolin Chen if (!IS_ERR(esai_priv->extalclk)) 515*43d24e76SNicolin Chen clk_disable_unprepare(esai_priv->extalclk); 516*43d24e76SNicolin Chen clk_disable_unprepare(esai_priv->coreclk); 517*43d24e76SNicolin Chen } 518*43d24e76SNicolin Chen 519*43d24e76SNicolin Chen static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, 520*43d24e76SNicolin Chen struct snd_soc_dai *dai) 521*43d24e76SNicolin Chen { 522*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 523*43d24e76SNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 524*43d24e76SNicolin Chen u8 i, channels = substream->runtime->channels; 525*43d24e76SNicolin Chen 526*43d24e76SNicolin Chen switch (cmd) { 527*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_START: 528*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_RESUME: 529*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 530*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 531*43d24e76SNicolin Chen ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); 532*43d24e76SNicolin Chen 533*43d24e76SNicolin Chen /* Write initial words reqiured by ESAI as normal procedure */ 534*43d24e76SNicolin Chen for (i = 0; tx && i < channels; i++) 535*43d24e76SNicolin Chen regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); 536*43d24e76SNicolin Chen 537*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 538*43d24e76SNicolin Chen tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 539*43d24e76SNicolin Chen tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels)); 540*43d24e76SNicolin Chen break; 541*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_SUSPEND: 542*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_STOP: 543*43d24e76SNicolin Chen case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 544*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), 545*43d24e76SNicolin Chen tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); 546*43d24e76SNicolin Chen 547*43d24e76SNicolin Chen /* Disable and reset FIFO */ 548*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 549*43d24e76SNicolin Chen ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); 550*43d24e76SNicolin Chen regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), 551*43d24e76SNicolin Chen ESAI_xFCR_xFR, 0); 552*43d24e76SNicolin Chen break; 553*43d24e76SNicolin Chen default: 554*43d24e76SNicolin Chen return -EINVAL; 555*43d24e76SNicolin Chen } 556*43d24e76SNicolin Chen 557*43d24e76SNicolin Chen return 0; 558*43d24e76SNicolin Chen } 559*43d24e76SNicolin Chen 560*43d24e76SNicolin Chen static struct snd_soc_dai_ops fsl_esai_dai_ops = { 561*43d24e76SNicolin Chen .startup = fsl_esai_startup, 562*43d24e76SNicolin Chen .shutdown = fsl_esai_shutdown, 563*43d24e76SNicolin Chen .trigger = fsl_esai_trigger, 564*43d24e76SNicolin Chen .hw_params = fsl_esai_hw_params, 565*43d24e76SNicolin Chen .set_sysclk = fsl_esai_set_dai_sysclk, 566*43d24e76SNicolin Chen .set_fmt = fsl_esai_set_dai_fmt, 567*43d24e76SNicolin Chen .set_tdm_slot = fsl_esai_set_dai_tdm_slot, 568*43d24e76SNicolin Chen }; 569*43d24e76SNicolin Chen 570*43d24e76SNicolin Chen static int fsl_esai_dai_probe(struct snd_soc_dai *dai) 571*43d24e76SNicolin Chen { 572*43d24e76SNicolin Chen struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 573*43d24e76SNicolin Chen 574*43d24e76SNicolin Chen snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx, 575*43d24e76SNicolin Chen &esai_priv->dma_params_rx); 576*43d24e76SNicolin Chen 577*43d24e76SNicolin Chen return 0; 578*43d24e76SNicolin Chen } 579*43d24e76SNicolin Chen 580*43d24e76SNicolin Chen static struct snd_soc_dai_driver fsl_esai_dai = { 581*43d24e76SNicolin Chen .probe = fsl_esai_dai_probe, 582*43d24e76SNicolin Chen .playback = { 583*43d24e76SNicolin Chen .channels_min = 1, 584*43d24e76SNicolin Chen .channels_max = 12, 585*43d24e76SNicolin Chen .rates = FSL_ESAI_RATES, 586*43d24e76SNicolin Chen .formats = FSL_ESAI_FORMATS, 587*43d24e76SNicolin Chen }, 588*43d24e76SNicolin Chen .capture = { 589*43d24e76SNicolin Chen .channels_min = 1, 590*43d24e76SNicolin Chen .channels_max = 8, 591*43d24e76SNicolin Chen .rates = FSL_ESAI_RATES, 592*43d24e76SNicolin Chen .formats = FSL_ESAI_FORMATS, 593*43d24e76SNicolin Chen }, 594*43d24e76SNicolin Chen .ops = &fsl_esai_dai_ops, 595*43d24e76SNicolin Chen }; 596*43d24e76SNicolin Chen 597*43d24e76SNicolin Chen static const struct snd_soc_component_driver fsl_esai_component = { 598*43d24e76SNicolin Chen .name = "fsl-esai", 599*43d24e76SNicolin Chen }; 600*43d24e76SNicolin Chen 601*43d24e76SNicolin Chen static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) 602*43d24e76SNicolin Chen { 603*43d24e76SNicolin Chen switch (reg) { 604*43d24e76SNicolin Chen case REG_ESAI_ERDR: 605*43d24e76SNicolin Chen case REG_ESAI_ECR: 606*43d24e76SNicolin Chen case REG_ESAI_ESR: 607*43d24e76SNicolin Chen case REG_ESAI_TFCR: 608*43d24e76SNicolin Chen case REG_ESAI_TFSR: 609*43d24e76SNicolin Chen case REG_ESAI_RFCR: 610*43d24e76SNicolin Chen case REG_ESAI_RFSR: 611*43d24e76SNicolin Chen case REG_ESAI_RX0: 612*43d24e76SNicolin Chen case REG_ESAI_RX1: 613*43d24e76SNicolin Chen case REG_ESAI_RX2: 614*43d24e76SNicolin Chen case REG_ESAI_RX3: 615*43d24e76SNicolin Chen case REG_ESAI_SAISR: 616*43d24e76SNicolin Chen case REG_ESAI_SAICR: 617*43d24e76SNicolin Chen case REG_ESAI_TCR: 618*43d24e76SNicolin Chen case REG_ESAI_TCCR: 619*43d24e76SNicolin Chen case REG_ESAI_RCR: 620*43d24e76SNicolin Chen case REG_ESAI_RCCR: 621*43d24e76SNicolin Chen case REG_ESAI_TSMA: 622*43d24e76SNicolin Chen case REG_ESAI_TSMB: 623*43d24e76SNicolin Chen case REG_ESAI_RSMA: 624*43d24e76SNicolin Chen case REG_ESAI_RSMB: 625*43d24e76SNicolin Chen case REG_ESAI_PRRC: 626*43d24e76SNicolin Chen case REG_ESAI_PCRC: 627*43d24e76SNicolin Chen return true; 628*43d24e76SNicolin Chen default: 629*43d24e76SNicolin Chen return false; 630*43d24e76SNicolin Chen } 631*43d24e76SNicolin Chen } 632*43d24e76SNicolin Chen 633*43d24e76SNicolin Chen static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) 634*43d24e76SNicolin Chen { 635*43d24e76SNicolin Chen switch (reg) { 636*43d24e76SNicolin Chen case REG_ESAI_ETDR: 637*43d24e76SNicolin Chen case REG_ESAI_ECR: 638*43d24e76SNicolin Chen case REG_ESAI_TFCR: 639*43d24e76SNicolin Chen case REG_ESAI_RFCR: 640*43d24e76SNicolin Chen case REG_ESAI_TX0: 641*43d24e76SNicolin Chen case REG_ESAI_TX1: 642*43d24e76SNicolin Chen case REG_ESAI_TX2: 643*43d24e76SNicolin Chen case REG_ESAI_TX3: 644*43d24e76SNicolin Chen case REG_ESAI_TX4: 645*43d24e76SNicolin Chen case REG_ESAI_TX5: 646*43d24e76SNicolin Chen case REG_ESAI_TSR: 647*43d24e76SNicolin Chen case REG_ESAI_SAICR: 648*43d24e76SNicolin Chen case REG_ESAI_TCR: 649*43d24e76SNicolin Chen case REG_ESAI_TCCR: 650*43d24e76SNicolin Chen case REG_ESAI_RCR: 651*43d24e76SNicolin Chen case REG_ESAI_RCCR: 652*43d24e76SNicolin Chen case REG_ESAI_TSMA: 653*43d24e76SNicolin Chen case REG_ESAI_TSMB: 654*43d24e76SNicolin Chen case REG_ESAI_RSMA: 655*43d24e76SNicolin Chen case REG_ESAI_RSMB: 656*43d24e76SNicolin Chen case REG_ESAI_PRRC: 657*43d24e76SNicolin Chen case REG_ESAI_PCRC: 658*43d24e76SNicolin Chen return true; 659*43d24e76SNicolin Chen default: 660*43d24e76SNicolin Chen return false; 661*43d24e76SNicolin Chen } 662*43d24e76SNicolin Chen } 663*43d24e76SNicolin Chen 664*43d24e76SNicolin Chen static const struct regmap_config fsl_esai_regmap_config = { 665*43d24e76SNicolin Chen .reg_bits = 32, 666*43d24e76SNicolin Chen .reg_stride = 4, 667*43d24e76SNicolin Chen .val_bits = 32, 668*43d24e76SNicolin Chen 669*43d24e76SNicolin Chen .max_register = REG_ESAI_PCRC, 670*43d24e76SNicolin Chen .readable_reg = fsl_esai_readable_reg, 671*43d24e76SNicolin Chen .writeable_reg = fsl_esai_writeable_reg, 672*43d24e76SNicolin Chen }; 673*43d24e76SNicolin Chen 674*43d24e76SNicolin Chen static int fsl_esai_probe(struct platform_device *pdev) 675*43d24e76SNicolin Chen { 676*43d24e76SNicolin Chen struct device_node *np = pdev->dev.of_node; 677*43d24e76SNicolin Chen struct fsl_esai *esai_priv; 678*43d24e76SNicolin Chen struct resource *res; 679*43d24e76SNicolin Chen const uint32_t *iprop; 680*43d24e76SNicolin Chen void __iomem *regs; 681*43d24e76SNicolin Chen int irq, ret; 682*43d24e76SNicolin Chen 683*43d24e76SNicolin Chen esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL); 684*43d24e76SNicolin Chen if (!esai_priv) 685*43d24e76SNicolin Chen return -ENOMEM; 686*43d24e76SNicolin Chen 687*43d24e76SNicolin Chen esai_priv->pdev = pdev; 688*43d24e76SNicolin Chen strcpy(esai_priv->name, np->name); 689*43d24e76SNicolin Chen 690*43d24e76SNicolin Chen /* Get the addresses and IRQ */ 691*43d24e76SNicolin Chen res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 692*43d24e76SNicolin Chen regs = devm_ioremap_resource(&pdev->dev, res); 693*43d24e76SNicolin Chen if (IS_ERR(regs)) 694*43d24e76SNicolin Chen return PTR_ERR(regs); 695*43d24e76SNicolin Chen 696*43d24e76SNicolin Chen esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, 697*43d24e76SNicolin Chen "core", regs, &fsl_esai_regmap_config); 698*43d24e76SNicolin Chen if (IS_ERR(esai_priv->regmap)) { 699*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to init regmap: %ld\n", 700*43d24e76SNicolin Chen PTR_ERR(esai_priv->regmap)); 701*43d24e76SNicolin Chen return PTR_ERR(esai_priv->regmap); 702*43d24e76SNicolin Chen } 703*43d24e76SNicolin Chen 704*43d24e76SNicolin Chen esai_priv->coreclk = devm_clk_get(&pdev->dev, "core"); 705*43d24e76SNicolin Chen if (IS_ERR(esai_priv->coreclk)) { 706*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to get core clock: %ld\n", 707*43d24e76SNicolin Chen PTR_ERR(esai_priv->coreclk)); 708*43d24e76SNicolin Chen return PTR_ERR(esai_priv->coreclk); 709*43d24e76SNicolin Chen } 710*43d24e76SNicolin Chen 711*43d24e76SNicolin Chen esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal"); 712*43d24e76SNicolin Chen if (IS_ERR(esai_priv->extalclk)) 713*43d24e76SNicolin Chen dev_warn(&pdev->dev, "failed to get extal clock: %ld\n", 714*43d24e76SNicolin Chen PTR_ERR(esai_priv->extalclk)); 715*43d24e76SNicolin Chen 716*43d24e76SNicolin Chen esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys"); 717*43d24e76SNicolin Chen if (IS_ERR(esai_priv->fsysclk)) 718*43d24e76SNicolin Chen dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", 719*43d24e76SNicolin Chen PTR_ERR(esai_priv->fsysclk)); 720*43d24e76SNicolin Chen 721*43d24e76SNicolin Chen irq = platform_get_irq(pdev, 0); 722*43d24e76SNicolin Chen if (irq < 0) { 723*43d24e76SNicolin Chen dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); 724*43d24e76SNicolin Chen return irq; 725*43d24e76SNicolin Chen } 726*43d24e76SNicolin Chen 727*43d24e76SNicolin Chen ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0, 728*43d24e76SNicolin Chen esai_priv->name, esai_priv); 729*43d24e76SNicolin Chen if (ret) { 730*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to claim irq %u\n", irq); 731*43d24e76SNicolin Chen return ret; 732*43d24e76SNicolin Chen } 733*43d24e76SNicolin Chen 734*43d24e76SNicolin Chen /* Set a default slot size */ 735*43d24e76SNicolin Chen esai_priv->slot_width = 32; 736*43d24e76SNicolin Chen 737*43d24e76SNicolin Chen /* Set a default master/slave state */ 738*43d24e76SNicolin Chen esai_priv->slave_mode = true; 739*43d24e76SNicolin Chen 740*43d24e76SNicolin Chen /* Determine the FIFO depth */ 741*43d24e76SNicolin Chen iprop = of_get_property(np, "fsl,fifo-depth", NULL); 742*43d24e76SNicolin Chen if (iprop) 743*43d24e76SNicolin Chen esai_priv->fifo_depth = be32_to_cpup(iprop); 744*43d24e76SNicolin Chen else 745*43d24e76SNicolin Chen esai_priv->fifo_depth = 64; 746*43d24e76SNicolin Chen 747*43d24e76SNicolin Chen esai_priv->dma_params_tx.maxburst = 16; 748*43d24e76SNicolin Chen esai_priv->dma_params_rx.maxburst = 16; 749*43d24e76SNicolin Chen esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR; 750*43d24e76SNicolin Chen esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR; 751*43d24e76SNicolin Chen 752*43d24e76SNicolin Chen esai_priv->synchronous = 753*43d24e76SNicolin Chen of_property_read_bool(np, "fsl,esai-synchronous"); 754*43d24e76SNicolin Chen 755*43d24e76SNicolin Chen /* Implement full symmetry for synchronous mode */ 756*43d24e76SNicolin Chen if (esai_priv->synchronous) { 757*43d24e76SNicolin Chen fsl_esai_dai.symmetric_rates = 1; 758*43d24e76SNicolin Chen fsl_esai_dai.symmetric_channels = 1; 759*43d24e76SNicolin Chen fsl_esai_dai.symmetric_samplebits = 1; 760*43d24e76SNicolin Chen } 761*43d24e76SNicolin Chen 762*43d24e76SNicolin Chen dev_set_drvdata(&pdev->dev, esai_priv); 763*43d24e76SNicolin Chen 764*43d24e76SNicolin Chen /* Reset ESAI unit */ 765*43d24e76SNicolin Chen ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); 766*43d24e76SNicolin Chen if (ret) { 767*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); 768*43d24e76SNicolin Chen return ret; 769*43d24e76SNicolin Chen } 770*43d24e76SNicolin Chen 771*43d24e76SNicolin Chen /* 772*43d24e76SNicolin Chen * We need to enable ESAI so as to access some of its registers. 773*43d24e76SNicolin Chen * Otherwise, we would fail to dump regmap from user space. 774*43d24e76SNicolin Chen */ 775*43d24e76SNicolin Chen ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); 776*43d24e76SNicolin Chen if (ret) { 777*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); 778*43d24e76SNicolin Chen return ret; 779*43d24e76SNicolin Chen } 780*43d24e76SNicolin Chen 781*43d24e76SNicolin Chen ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component, 782*43d24e76SNicolin Chen &fsl_esai_dai, 1); 783*43d24e76SNicolin Chen if (ret) { 784*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); 785*43d24e76SNicolin Chen return ret; 786*43d24e76SNicolin Chen } 787*43d24e76SNicolin Chen 788*43d24e76SNicolin Chen ret = imx_pcm_dma_init(pdev); 789*43d24e76SNicolin Chen if (ret) 790*43d24e76SNicolin Chen dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); 791*43d24e76SNicolin Chen 792*43d24e76SNicolin Chen return ret; 793*43d24e76SNicolin Chen } 794*43d24e76SNicolin Chen 795*43d24e76SNicolin Chen static const struct of_device_id fsl_esai_dt_ids[] = { 796*43d24e76SNicolin Chen { .compatible = "fsl,imx35-esai", }, 797*43d24e76SNicolin Chen {} 798*43d24e76SNicolin Chen }; 799*43d24e76SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); 800*43d24e76SNicolin Chen 801*43d24e76SNicolin Chen static struct platform_driver fsl_esai_driver = { 802*43d24e76SNicolin Chen .probe = fsl_esai_probe, 803*43d24e76SNicolin Chen .driver = { 804*43d24e76SNicolin Chen .name = "fsl-esai-dai", 805*43d24e76SNicolin Chen .owner = THIS_MODULE, 806*43d24e76SNicolin Chen .of_match_table = fsl_esai_dt_ids, 807*43d24e76SNicolin Chen }, 808*43d24e76SNicolin Chen }; 809*43d24e76SNicolin Chen 810*43d24e76SNicolin Chen module_platform_driver(fsl_esai_driver); 811*43d24e76SNicolin Chen 812*43d24e76SNicolin Chen MODULE_AUTHOR("Freescale Semiconductor, Inc."); 813*43d24e76SNicolin Chen MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver"); 814*43d24e76SNicolin Chen MODULE_LICENSE("GPL v2"); 815*43d24e76SNicolin Chen MODULE_ALIAS("platform:fsl-esai-dai"); 816