162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2014 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/dmaengine.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of_irq.h>
1162306a36Sopenharmony_ci#include <linux/of_platform.h>
1262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1362306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
1462306a36Sopenharmony_ci#include <sound/pcm_params.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "fsl_esai.h"
1762306a36Sopenharmony_ci#include "imx-pcm.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define FSL_ESAI_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
2062306a36Sopenharmony_ci				SNDRV_PCM_FMTBIT_S16_LE | \
2162306a36Sopenharmony_ci				SNDRV_PCM_FMTBIT_S20_3LE | \
2262306a36Sopenharmony_ci				SNDRV_PCM_FMTBIT_S24_LE)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * struct fsl_esai_soc_data - soc specific data
2662306a36Sopenharmony_ci * @reset_at_xrun: flags for enable reset operaton
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistruct fsl_esai_soc_data {
2962306a36Sopenharmony_ci	bool reset_at_xrun;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/**
3362306a36Sopenharmony_ci * struct fsl_esai - ESAI private data
3462306a36Sopenharmony_ci * @dma_params_rx: DMA parameters for receive channel
3562306a36Sopenharmony_ci * @dma_params_tx: DMA parameters for transmit channel
3662306a36Sopenharmony_ci * @pdev: platform device pointer
3762306a36Sopenharmony_ci * @regmap: regmap handler
3862306a36Sopenharmony_ci * @coreclk: clock source to access register
3962306a36Sopenharmony_ci * @extalclk: esai clock source to derive HCK, SCK and FS
4062306a36Sopenharmony_ci * @fsysclk: system clock source to derive HCK, SCK and FS
4162306a36Sopenharmony_ci * @spbaclk: SPBA clock (optional, depending on SoC design)
4262306a36Sopenharmony_ci * @work: work to handle the reset operation
4362306a36Sopenharmony_ci * @soc: soc specific data
4462306a36Sopenharmony_ci * @lock: spin lock between hw_reset() and trigger()
4562306a36Sopenharmony_ci * @fifo_depth: depth of tx/rx FIFO
4662306a36Sopenharmony_ci * @slot_width: width of each DAI slot
4762306a36Sopenharmony_ci * @slots: number of slots
4862306a36Sopenharmony_ci * @tx_mask: slot mask for TX
4962306a36Sopenharmony_ci * @rx_mask: slot mask for RX
5062306a36Sopenharmony_ci * @channels: channel num for tx or rx
5162306a36Sopenharmony_ci * @hck_rate: clock rate of desired HCKx clock
5262306a36Sopenharmony_ci * @sck_rate: clock rate of desired SCKx clock
5362306a36Sopenharmony_ci * @hck_dir: the direction of HCKx pads
5462306a36Sopenharmony_ci * @sck_div: if using PSR/PM dividers for SCKx clock
5562306a36Sopenharmony_ci * @consumer_mode: if fully using DAI clock consumer mode
5662306a36Sopenharmony_ci * @synchronous: if using tx/rx synchronous mode
5762306a36Sopenharmony_ci * @name: driver name
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistruct fsl_esai {
6062306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_rx;
6162306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data dma_params_tx;
6262306a36Sopenharmony_ci	struct platform_device *pdev;
6362306a36Sopenharmony_ci	struct regmap *regmap;
6462306a36Sopenharmony_ci	struct clk *coreclk;
6562306a36Sopenharmony_ci	struct clk *extalclk;
6662306a36Sopenharmony_ci	struct clk *fsysclk;
6762306a36Sopenharmony_ci	struct clk *spbaclk;
6862306a36Sopenharmony_ci	struct work_struct work;
6962306a36Sopenharmony_ci	const struct fsl_esai_soc_data *soc;
7062306a36Sopenharmony_ci	spinlock_t lock; /* Protect hw_reset and trigger */
7162306a36Sopenharmony_ci	u32 fifo_depth;
7262306a36Sopenharmony_ci	u32 slot_width;
7362306a36Sopenharmony_ci	u32 slots;
7462306a36Sopenharmony_ci	u32 tx_mask;
7562306a36Sopenharmony_ci	u32 rx_mask;
7662306a36Sopenharmony_ci	u32 channels[2];
7762306a36Sopenharmony_ci	u32 hck_rate[2];
7862306a36Sopenharmony_ci	u32 sck_rate[2];
7962306a36Sopenharmony_ci	bool hck_dir[2];
8062306a36Sopenharmony_ci	bool sck_div[2];
8162306a36Sopenharmony_ci	bool consumer_mode;
8262306a36Sopenharmony_ci	bool synchronous;
8362306a36Sopenharmony_ci	char name[32];
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_vf610 = {
8762306a36Sopenharmony_ci	.reset_at_xrun = true,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_imx35 = {
9162306a36Sopenharmony_ci	.reset_at_xrun = true,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct fsl_esai_soc_data fsl_esai_imx6ull = {
9562306a36Sopenharmony_ci	.reset_at_xrun = false,
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic irqreturn_t esai_isr(int irq, void *devid)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
10162306a36Sopenharmony_ci	struct platform_device *pdev = esai_priv->pdev;
10262306a36Sopenharmony_ci	u32 esr;
10362306a36Sopenharmony_ci	u32 saisr;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
10662306a36Sopenharmony_ci	regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&
10962306a36Sopenharmony_ci	    esai_priv->soc->reset_at_xrun) {
11062306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "reset module for xrun\n");
11162306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
11262306a36Sopenharmony_ci				   ESAI_xCR_xEIE_MASK, 0);
11362306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
11462306a36Sopenharmony_ci				   ESAI_xCR_xEIE_MASK, 0);
11562306a36Sopenharmony_ci		schedule_work(&esai_priv->work);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (esr & ESAI_ESR_TINIT_MASK)
11962306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (esr & ESAI_ESR_RFF_MASK)
12262306a36Sopenharmony_ci		dev_warn(&pdev->dev, "isr: Receiving overrun\n");
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (esr & ESAI_ESR_TFE_MASK)
12562306a36Sopenharmony_ci		dev_warn(&pdev->dev, "isr: Transmission underrun\n");
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (esr & ESAI_ESR_TLS_MASK)
12862306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (esr & ESAI_ESR_TDE_MASK)
13162306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Transmission data exception\n");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (esr & ESAI_ESR_TED_MASK)
13462306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (esr & ESAI_ESR_TD_MASK)
13762306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Transmitting data\n");
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (esr & ESAI_ESR_RLS_MASK)
14062306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Just received the last slot\n");
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (esr & ESAI_ESR_RDE_MASK)
14362306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Receiving data exception\n");
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (esr & ESAI_ESR_RED_MASK)
14662306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Receiving even slots\n");
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (esr & ESAI_ESR_RD_MASK)
14962306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "isr: Receiving data\n");
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return IRQ_HANDLED;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/**
15562306a36Sopenharmony_ci * fsl_esai_divisor_cal - This function is used to calculate the
15662306a36Sopenharmony_ci * divisors of psr, pm, fp and it is supposed to be called in
15762306a36Sopenharmony_ci * set_dai_sysclk() and set_bclk().
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * @dai: pointer to DAI
16062306a36Sopenharmony_ci * @tx: current setting is for playback or capture
16162306a36Sopenharmony_ci * @ratio: desired overall ratio for the paticipating dividers
16262306a36Sopenharmony_ci * @usefp: for HCK setting, there is no need to set fp divider
16362306a36Sopenharmony_ci * @fp: bypass other dividers by setting fp directly if fp != 0
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
16662306a36Sopenharmony_ci				bool usefp, u32 fp)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
16962306a36Sopenharmony_ci	u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	maxfp = usefp ? 16 : 1;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (usefp && fp)
17462306a36Sopenharmony_ci		goto out_fp;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {
17762306a36Sopenharmony_ci		dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",
17862306a36Sopenharmony_ci				2 * 8 * 256 * maxfp);
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci	} else if (ratio % 2) {
18162306a36Sopenharmony_ci		dev_err(dai->dev, "the raio must be even if using upper divider\n");
18262306a36Sopenharmony_ci		return -EINVAL;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ratio /= 2;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Do not loop-search if PM (1 ~ 256) alone can serve the ratio */
19062306a36Sopenharmony_ci	if (ratio <= 256) {
19162306a36Sopenharmony_ci		pm = ratio;
19262306a36Sopenharmony_ci		fp = 1;
19362306a36Sopenharmony_ci		goto out;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Set the max fluctuation -- 0.1% of the max devisor */
19762306a36Sopenharmony_ci	savesub = (psr ? 1 : 8)  * 256 * maxfp / 1000;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Find the best value for PM */
20062306a36Sopenharmony_ci	for (i = 1; i <= 256; i++) {
20162306a36Sopenharmony_ci		for (j = 1; j <= maxfp; j++) {
20262306a36Sopenharmony_ci			/* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */
20362306a36Sopenharmony_ci			prod = (psr ? 1 : 8) * i * j;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci			if (prod == ratio)
20662306a36Sopenharmony_ci				sub = 0;
20762306a36Sopenharmony_ci			else if (prod / ratio == 1)
20862306a36Sopenharmony_ci				sub = prod - ratio;
20962306a36Sopenharmony_ci			else if (ratio / prod == 1)
21062306a36Sopenharmony_ci				sub = ratio - prod;
21162306a36Sopenharmony_ci			else
21262306a36Sopenharmony_ci				continue;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci			/* Calculate the fraction */
21562306a36Sopenharmony_ci			sub = sub * 1000 / ratio;
21662306a36Sopenharmony_ci			if (sub < savesub) {
21762306a36Sopenharmony_ci				savesub = sub;
21862306a36Sopenharmony_ci				pm = i;
21962306a36Sopenharmony_ci				fp = j;
22062306a36Sopenharmony_ci			}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci			/* We are lucky */
22362306a36Sopenharmony_ci			if (savesub == 0)
22462306a36Sopenharmony_ci				goto out;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (pm == 999) {
22962306a36Sopenharmony_ci		dev_err(dai->dev, "failed to calculate proper divisors\n");
23062306a36Sopenharmony_ci		return -EINVAL;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciout:
23462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
23562306a36Sopenharmony_ci			   ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,
23662306a36Sopenharmony_ci			   psr | ESAI_xCCR_xPM(pm));
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciout_fp:
23962306a36Sopenharmony_ci	/* Bypass fp if not being required */
24062306a36Sopenharmony_ci	if (maxfp <= 1)
24162306a36Sopenharmony_ci		return 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
24462306a36Sopenharmony_ci			   ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/**
25062306a36Sopenharmony_ci * fsl_esai_set_dai_sysclk - configure the clock frequency of MCLK (HCKT/HCKR)
25162306a36Sopenharmony_ci * @dai: pointer to DAI
25262306a36Sopenharmony_ci * @clk_id: The clock source of HCKT/HCKR
25362306a36Sopenharmony_ci *	  (Input from outside; output from inside, FSYS or EXTAL)
25462306a36Sopenharmony_ci * @freq: The required clock rate of HCKT/HCKR
25562306a36Sopenharmony_ci * @dir: The clock direction of HCKT/HCKR
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * Note: If the direction is input, we do not care about clk_id.
25862306a36Sopenharmony_ci */
25962306a36Sopenharmony_cistatic int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
26062306a36Sopenharmony_ci				   unsigned int freq, int dir)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
26362306a36Sopenharmony_ci	struct clk *clksrc = esai_priv->extalclk;
26462306a36Sopenharmony_ci	bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous);
26562306a36Sopenharmony_ci	bool in = dir == SND_SOC_CLOCK_IN;
26662306a36Sopenharmony_ci	u32 ratio, ecr = 0;
26762306a36Sopenharmony_ci	unsigned long clk_rate;
26862306a36Sopenharmony_ci	int ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (freq == 0) {
27162306a36Sopenharmony_ci		dev_err(dai->dev, "%sput freq of HCK%c should not be 0Hz\n",
27262306a36Sopenharmony_ci			in ? "in" : "out", tx ? 'T' : 'R');
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Bypass divider settings if the requirement doesn't change */
27762306a36Sopenharmony_ci	if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
27862306a36Sopenharmony_ci		return 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
28162306a36Sopenharmony_ci	esai_priv->sck_div[tx] = true;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* Set the direction of HCKT/HCKR pins */
28462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
28562306a36Sopenharmony_ci			   ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (in)
28862306a36Sopenharmony_ci		goto out;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	switch (clk_id) {
29162306a36Sopenharmony_ci	case ESAI_HCKT_FSYS:
29262306a36Sopenharmony_ci	case ESAI_HCKR_FSYS:
29362306a36Sopenharmony_ci		clksrc = esai_priv->fsysclk;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case ESAI_HCKT_EXTAL:
29662306a36Sopenharmony_ci		ecr |= ESAI_ECR_ETI;
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	case ESAI_HCKR_EXTAL:
29962306a36Sopenharmony_ci		ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI;
30062306a36Sopenharmony_ci		break;
30162306a36Sopenharmony_ci	default:
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (IS_ERR(clksrc)) {
30662306a36Sopenharmony_ci		dev_err(dai->dev, "no assigned %s clock\n",
30762306a36Sopenharmony_ci			(clk_id % 2) ? "extal" : "fsys");
30862306a36Sopenharmony_ci		return PTR_ERR(clksrc);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	clk_rate = clk_get_rate(clksrc);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ratio = clk_rate / freq;
31362306a36Sopenharmony_ci	if (ratio * freq > clk_rate)
31462306a36Sopenharmony_ci		ret = ratio * freq - clk_rate;
31562306a36Sopenharmony_ci	else if (ratio * freq < clk_rate)
31662306a36Sopenharmony_ci		ret = clk_rate - ratio * freq;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		ret = 0;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Block if clock source can not be divided into the required rate */
32162306a36Sopenharmony_ci	if (ret != 0 && clk_rate / ret < 1000) {
32262306a36Sopenharmony_ci		dev_err(dai->dev, "failed to derive required HCK%c rate\n",
32362306a36Sopenharmony_ci				tx ? 'T' : 'R');
32462306a36Sopenharmony_ci		return -EINVAL;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Only EXTAL source can be output directly without using PSR and PM */
32862306a36Sopenharmony_ci	if (ratio == 1 && clksrc == esai_priv->extalclk) {
32962306a36Sopenharmony_ci		/* Bypass all the dividers if not being needed */
33062306a36Sopenharmony_ci		ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
33162306a36Sopenharmony_ci		goto out;
33262306a36Sopenharmony_ci	} else if (ratio < 2) {
33362306a36Sopenharmony_ci		/* The ratio should be no less than 2 if using other sources */
33462306a36Sopenharmony_ci		dev_err(dai->dev, "failed to derive required HCK%c rate\n",
33562306a36Sopenharmony_ci				tx ? 'T' : 'R');
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
34062306a36Sopenharmony_ci	if (ret)
34162306a36Sopenharmony_ci		return ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	esai_priv->sck_div[tx] = false;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciout:
34662306a36Sopenharmony_ci	esai_priv->hck_dir[tx] = dir;
34762306a36Sopenharmony_ci	esai_priv->hck_rate[tx] = freq;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
35062306a36Sopenharmony_ci			   tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
35162306a36Sopenharmony_ci			   ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return 0;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/**
35762306a36Sopenharmony_ci * fsl_esai_set_bclk - configure the related dividers according to the bclk rate
35862306a36Sopenharmony_ci * @dai: pointer to DAI
35962306a36Sopenharmony_ci * @tx: direction boolean
36062306a36Sopenharmony_ci * @freq: bclk freq
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
36562306a36Sopenharmony_ci	u32 hck_rate = esai_priv->hck_rate[tx];
36662306a36Sopenharmony_ci	u32 sub, ratio = hck_rate / freq;
36762306a36Sopenharmony_ci	int ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Don't apply for fully consumer mode or unchanged bclk */
37062306a36Sopenharmony_ci	if (esai_priv->consumer_mode || esai_priv->sck_rate[tx] == freq)
37162306a36Sopenharmony_ci		return 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (ratio * freq > hck_rate)
37462306a36Sopenharmony_ci		sub = ratio * freq - hck_rate;
37562306a36Sopenharmony_ci	else if (ratio * freq < hck_rate)
37662306a36Sopenharmony_ci		sub = hck_rate - ratio * freq;
37762306a36Sopenharmony_ci	else
37862306a36Sopenharmony_ci		sub = 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Block if clock source can not be divided into the required rate */
38162306a36Sopenharmony_ci	if (sub != 0 && hck_rate / sub < 1000) {
38262306a36Sopenharmony_ci		dev_err(dai->dev, "failed to derive required SCK%c rate\n",
38362306a36Sopenharmony_ci				tx ? 'T' : 'R');
38462306a36Sopenharmony_ci		return -EINVAL;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* The ratio should be contented by FP alone if bypassing PM and PSR */
38862306a36Sopenharmony_ci	if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
38962306a36Sopenharmony_ci		dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
39462306a36Sopenharmony_ci			esai_priv->sck_div[tx] ? 0 : ratio);
39562306a36Sopenharmony_ci	if (ret)
39662306a36Sopenharmony_ci		return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Save current bclk rate */
39962306a36Sopenharmony_ci	esai_priv->sck_rate[tx] = freq;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
40562306a36Sopenharmony_ci				     u32 rx_mask, int slots, int slot_width)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
41062306a36Sopenharmony_ci			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
41362306a36Sopenharmony_ci			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	esai_priv->slot_width = slot_width;
41662306a36Sopenharmony_ci	esai_priv->slots = slots;
41762306a36Sopenharmony_ci	esai_priv->tx_mask = tx_mask;
41862306a36Sopenharmony_ci	esai_priv->rx_mask = rx_mask;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
42662306a36Sopenharmony_ci	u32 xcr = 0, xccr = 0, mask;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* DAI mode */
42962306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
43062306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
43162306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame low, 1clk before data */
43262306a36Sopenharmony_ci		xcr |= ESAI_xCR_xFSR;
43362306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
43662306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high */
43762306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
44062306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high, right aligned */
44162306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
44262306a36Sopenharmony_ci		xcr  |= ESAI_xCR_xWA;
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
44562306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high, 1clk before data */
44662306a36Sopenharmony_ci		xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR;
44762306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
44862306a36Sopenharmony_ci		break;
44962306a36Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_B:
45062306a36Sopenharmony_ci		/* Data on rising edge of bclk, frame high */
45162306a36Sopenharmony_ci		xcr |= ESAI_xCR_xFSL;
45262306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci	default:
45562306a36Sopenharmony_ci		return -EINVAL;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* DAI clock inversion */
45962306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
46062306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_NF:
46162306a36Sopenharmony_ci		/* Nothing to do for both normal cases */
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_NF:
46462306a36Sopenharmony_ci		/* Invert bit clock */
46562306a36Sopenharmony_ci		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	case SND_SOC_DAIFMT_NB_IF:
46862306a36Sopenharmony_ci		/* Invert frame clock */
46962306a36Sopenharmony_ci		xccr ^= ESAI_xCCR_xFSP;
47062306a36Sopenharmony_ci		break;
47162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_IB_IF:
47262306a36Sopenharmony_ci		/* Invert both clocks */
47362306a36Sopenharmony_ci		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	default:
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	esai_priv->consumer_mode = false;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* DAI clock provider masks */
48262306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
48362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BC_FC:
48462306a36Sopenharmony_ci		esai_priv->consumer_mode = true;
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BP_FC:
48762306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xCKD;
48862306a36Sopenharmony_ci		break;
48962306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BC_FP:
49062306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xFSD;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BP_FP:
49362306a36Sopenharmony_ci		xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci	default:
49662306a36Sopenharmony_ci		return -EINVAL;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR | ESAI_xCR_xWA;
50062306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
50162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
50462306a36Sopenharmony_ci		ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
50562306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
50662306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return 0;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int fsl_esai_startup(struct snd_pcm_substream *substream,
51262306a36Sopenharmony_ci			    struct snd_soc_dai *dai)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!snd_soc_dai_active(dai)) {
51762306a36Sopenharmony_ci		/* Set synchronous mode */
51862306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
51962306a36Sopenharmony_ci				   ESAI_SAICR_SYNC, esai_priv->synchronous ?
52062306a36Sopenharmony_ci				   ESAI_SAICR_SYNC : 0);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* Set slots count */
52362306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
52462306a36Sopenharmony_ci				   ESAI_xCCR_xDC_MASK,
52562306a36Sopenharmony_ci				   ESAI_xCCR_xDC(esai_priv->slots));
52662306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
52762306a36Sopenharmony_ci				   ESAI_xCCR_xDC_MASK,
52862306a36Sopenharmony_ci				   ESAI_xCCR_xDC(esai_priv->slots));
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int fsl_esai_hw_params(struct snd_pcm_substream *substream,
53662306a36Sopenharmony_ci			      struct snd_pcm_hw_params *params,
53762306a36Sopenharmony_ci			      struct snd_soc_dai *dai)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
54062306a36Sopenharmony_ci	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
54162306a36Sopenharmony_ci	u32 width = params_width(params);
54262306a36Sopenharmony_ci	u32 channels = params_channels(params);
54362306a36Sopenharmony_ci	u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
54462306a36Sopenharmony_ci	u32 slot_width = width;
54562306a36Sopenharmony_ci	u32 bclk, mask, val;
54662306a36Sopenharmony_ci	int ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Override slot_width if being specifically set */
54962306a36Sopenharmony_ci	if (esai_priv->slot_width)
55062306a36Sopenharmony_ci		slot_width = esai_priv->slot_width;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	bclk = params_rate(params) * slot_width * esai_priv->slots;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk);
55562306a36Sopenharmony_ci	if (ret)
55662306a36Sopenharmony_ci		return ret;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	mask = ESAI_xCR_xSWS_MASK;
55962306a36Sopenharmony_ci	val = ESAI_xCR_xSWS(slot_width, width);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
56262306a36Sopenharmony_ci	/* Recording in synchronous mode needs to set TCR also */
56362306a36Sopenharmony_ci	if (!tx && esai_priv->synchronous)
56462306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* Use Normal mode to support monaural audio */
56762306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
56862306a36Sopenharmony_ci			   ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
56962306a36Sopenharmony_ci			   ESAI_xCR_xMOD_NETWORK : 0);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
57262306a36Sopenharmony_ci			   ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
57562306a36Sopenharmony_ci	      (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
57662306a36Sopenharmony_ci	val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
57762306a36Sopenharmony_ci	     (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (tx)
58262306a36Sopenharmony_ci		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
58362306a36Sopenharmony_ci				ESAI_xCR_PADC, ESAI_xCR_PADC);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
58662306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
58762306a36Sopenharmony_ci			   ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
58862306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
58962306a36Sopenharmony_ci			   ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int fsl_esai_hw_init(struct fsl_esai *esai_priv)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct platform_device *pdev = esai_priv->pdev;
59662306a36Sopenharmony_ci	int ret;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* Reset ESAI unit */
59962306a36Sopenharmony_ci	ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
60062306a36Sopenharmony_ci				 ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
60162306a36Sopenharmony_ci				 ESAI_ECR_ESAIEN | ESAI_ECR_ERST);
60262306a36Sopenharmony_ci	if (ret) {
60362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	/*
60862306a36Sopenharmony_ci	 * We need to enable ESAI so as to access some of its registers.
60962306a36Sopenharmony_ci	 * Otherwise, we would fail to dump regmap from user space.
61062306a36Sopenharmony_ci	 */
61162306a36Sopenharmony_ci	ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
61262306a36Sopenharmony_ci				 ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
61362306a36Sopenharmony_ci				 ESAI_ECR_ESAIEN);
61462306a36Sopenharmony_ci	if (ret) {
61562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
61662306a36Sopenharmony_ci		return ret;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
62062306a36Sopenharmony_ci			   ESAI_PRRC_PDC_MASK, 0);
62162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
62262306a36Sopenharmony_ci			   ESAI_PCRC_PC_MASK, 0);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int fsl_esai_register_restore(struct fsl_esai *esai_priv)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	int ret;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* FIFO reset for safety */
63262306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR,
63362306a36Sopenharmony_ci			   ESAI_xFCR_xFR, ESAI_xFCR_xFR);
63462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR,
63562306a36Sopenharmony_ci			   ESAI_xFCR_xFR, ESAI_xFCR_xFR);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	regcache_mark_dirty(esai_priv->regmap);
63862306a36Sopenharmony_ci	ret = regcache_sync(esai_priv->regmap);
63962306a36Sopenharmony_ci	if (ret)
64062306a36Sopenharmony_ci		return ret;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	/* FIFO reset done */
64362306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
64462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return 0;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	u8 i, channels = esai_priv->channels[tx];
65262306a36Sopenharmony_ci	u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
65362306a36Sopenharmony_ci	u32 mask;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
65662306a36Sopenharmony_ci			   ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* Write initial words reqiured by ESAI as normal procedure */
65962306a36Sopenharmony_ci	for (i = 0; tx && i < channels; i++)
66062306a36Sopenharmony_ci		regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/*
66362306a36Sopenharmony_ci	 * When set the TE/RE in the end of enablement flow, there
66462306a36Sopenharmony_ci	 * will be channel swap issue for multi data line case.
66562306a36Sopenharmony_ci	 * In order to workaround this issue, we switch the bit
66662306a36Sopenharmony_ci	 * enablement sequence to below sequence
66762306a36Sopenharmony_ci	 * 1) clear the xSMB & xSMA: which is done in probe and
66862306a36Sopenharmony_ci	 *                           stop state.
66962306a36Sopenharmony_ci	 * 2) set TE/RE
67062306a36Sopenharmony_ci	 * 3) set xSMB
67162306a36Sopenharmony_ci	 * 4) set xSMA:  xSMA is the last one in this flow, which
67262306a36Sopenharmony_ci	 *               will trigger esai to start.
67362306a36Sopenharmony_ci	 */
67462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
67562306a36Sopenharmony_ci			   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
67662306a36Sopenharmony_ci			   tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
67762306a36Sopenharmony_ci	mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
68062306a36Sopenharmony_ci			   ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
68162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
68262306a36Sopenharmony_ci			   ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* Enable Exception interrupt */
68562306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
68662306a36Sopenharmony_ci			   ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE);
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
69262306a36Sopenharmony_ci			   ESAI_xCR_xEIE_MASK, 0);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
69562306a36Sopenharmony_ci			   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
69662306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
69762306a36Sopenharmony_ci			   ESAI_xSMA_xS_MASK, 0);
69862306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
69962306a36Sopenharmony_ci			   ESAI_xSMB_xS_MASK, 0);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* Disable and reset FIFO */
70262306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
70362306a36Sopenharmony_ci			   ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
70462306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
70562306a36Sopenharmony_ci			   ESAI_xFCR_xFR, 0);
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic void fsl_esai_hw_reset(struct work_struct *work)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work);
71162306a36Sopenharmony_ci	bool tx = true, rx = false, enabled[2];
71262306a36Sopenharmony_ci	unsigned long lock_flags;
71362306a36Sopenharmony_ci	u32 tfcr, rfcr;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	spin_lock_irqsave(&esai_priv->lock, lock_flags);
71662306a36Sopenharmony_ci	/* Save the registers */
71762306a36Sopenharmony_ci	regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
71862306a36Sopenharmony_ci	regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
71962306a36Sopenharmony_ci	enabled[tx] = tfcr & ESAI_xFCR_xFEN;
72062306a36Sopenharmony_ci	enabled[rx] = rfcr & ESAI_xFCR_xFEN;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Stop the tx & rx */
72362306a36Sopenharmony_ci	fsl_esai_trigger_stop(esai_priv, tx);
72462306a36Sopenharmony_ci	fsl_esai_trigger_stop(esai_priv, rx);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Reset the esai, and ignore return value */
72762306a36Sopenharmony_ci	fsl_esai_hw_init(esai_priv);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Enforce ESAI personal resets for both TX and RX */
73062306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
73162306a36Sopenharmony_ci			   ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
73262306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
73362306a36Sopenharmony_ci			   ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* Restore registers by regcache_sync, and ignore return value */
73662306a36Sopenharmony_ci	fsl_esai_register_restore(esai_priv);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	/* Remove ESAI personal resets by configuring PCRC and PRRC also */
73962306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
74062306a36Sopenharmony_ci			   ESAI_xCR_xPR_MASK, 0);
74162306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
74262306a36Sopenharmony_ci			   ESAI_xCR_xPR_MASK, 0);
74362306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
74462306a36Sopenharmony_ci			   ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
74562306a36Sopenharmony_ci	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
74662306a36Sopenharmony_ci			   ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/* Restart tx / rx, if they already enabled */
74962306a36Sopenharmony_ci	if (enabled[tx])
75062306a36Sopenharmony_ci		fsl_esai_trigger_start(esai_priv, tx);
75162306a36Sopenharmony_ci	if (enabled[rx])
75262306a36Sopenharmony_ci		fsl_esai_trigger_start(esai_priv, rx);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
75862306a36Sopenharmony_ci			    struct snd_soc_dai *dai)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
76162306a36Sopenharmony_ci	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
76262306a36Sopenharmony_ci	unsigned long lock_flags;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	esai_priv->channels[tx] = substream->runtime->channels;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	switch (cmd) {
76762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
76862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
76962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
77062306a36Sopenharmony_ci		spin_lock_irqsave(&esai_priv->lock, lock_flags);
77162306a36Sopenharmony_ci		fsl_esai_trigger_start(esai_priv, tx);
77262306a36Sopenharmony_ci		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
77562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
77662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
77762306a36Sopenharmony_ci		spin_lock_irqsave(&esai_priv->lock, lock_flags);
77862306a36Sopenharmony_ci		fsl_esai_trigger_stop(esai_priv, tx);
77962306a36Sopenharmony_ci		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
78062306a36Sopenharmony_ci		break;
78162306a36Sopenharmony_ci	default:
78262306a36Sopenharmony_ci		return -EINVAL;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int fsl_esai_dai_probe(struct snd_soc_dai *dai)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx,
79362306a36Sopenharmony_ci				  &esai_priv->dma_params_rx);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return 0;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops fsl_esai_dai_ops = {
79962306a36Sopenharmony_ci	.probe		= fsl_esai_dai_probe,
80062306a36Sopenharmony_ci	.startup	= fsl_esai_startup,
80162306a36Sopenharmony_ci	.trigger	= fsl_esai_trigger,
80262306a36Sopenharmony_ci	.hw_params	= fsl_esai_hw_params,
80362306a36Sopenharmony_ci	.set_sysclk	= fsl_esai_set_dai_sysclk,
80462306a36Sopenharmony_ci	.set_fmt	= fsl_esai_set_dai_fmt,
80562306a36Sopenharmony_ci	.set_tdm_slot	= fsl_esai_set_dai_tdm_slot,
80662306a36Sopenharmony_ci};
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic struct snd_soc_dai_driver fsl_esai_dai = {
80962306a36Sopenharmony_ci	.playback = {
81062306a36Sopenharmony_ci		.stream_name = "CPU-Playback",
81162306a36Sopenharmony_ci		.channels_min = 1,
81262306a36Sopenharmony_ci		.channels_max = 12,
81362306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_192000,
81462306a36Sopenharmony_ci		.formats = FSL_ESAI_FORMATS,
81562306a36Sopenharmony_ci	},
81662306a36Sopenharmony_ci	.capture = {
81762306a36Sopenharmony_ci		.stream_name = "CPU-Capture",
81862306a36Sopenharmony_ci		.channels_min = 1,
81962306a36Sopenharmony_ci		.channels_max = 8,
82062306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_192000,
82162306a36Sopenharmony_ci		.formats = FSL_ESAI_FORMATS,
82262306a36Sopenharmony_ci	},
82362306a36Sopenharmony_ci	.ops = &fsl_esai_dai_ops,
82462306a36Sopenharmony_ci};
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic const struct snd_soc_component_driver fsl_esai_component = {
82762306a36Sopenharmony_ci	.name			= "fsl-esai",
82862306a36Sopenharmony_ci	.legacy_dai_naming	= 1,
82962306a36Sopenharmony_ci};
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic const struct reg_default fsl_esai_reg_defaults[] = {
83262306a36Sopenharmony_ci	{REG_ESAI_ETDR,	 0x00000000},
83362306a36Sopenharmony_ci	{REG_ESAI_ECR,	 0x00000000},
83462306a36Sopenharmony_ci	{REG_ESAI_TFCR,	 0x00000000},
83562306a36Sopenharmony_ci	{REG_ESAI_RFCR,	 0x00000000},
83662306a36Sopenharmony_ci	{REG_ESAI_TX0,	 0x00000000},
83762306a36Sopenharmony_ci	{REG_ESAI_TX1,	 0x00000000},
83862306a36Sopenharmony_ci	{REG_ESAI_TX2,	 0x00000000},
83962306a36Sopenharmony_ci	{REG_ESAI_TX3,	 0x00000000},
84062306a36Sopenharmony_ci	{REG_ESAI_TX4,	 0x00000000},
84162306a36Sopenharmony_ci	{REG_ESAI_TX5,	 0x00000000},
84262306a36Sopenharmony_ci	{REG_ESAI_TSR,	 0x00000000},
84362306a36Sopenharmony_ci	{REG_ESAI_SAICR, 0x00000000},
84462306a36Sopenharmony_ci	{REG_ESAI_TCR,	 0x00000000},
84562306a36Sopenharmony_ci	{REG_ESAI_TCCR,	 0x00000000},
84662306a36Sopenharmony_ci	{REG_ESAI_RCR,	 0x00000000},
84762306a36Sopenharmony_ci	{REG_ESAI_RCCR,	 0x00000000},
84862306a36Sopenharmony_ci	{REG_ESAI_TSMA,  0x0000ffff},
84962306a36Sopenharmony_ci	{REG_ESAI_TSMB,  0x0000ffff},
85062306a36Sopenharmony_ci	{REG_ESAI_RSMA,  0x0000ffff},
85162306a36Sopenharmony_ci	{REG_ESAI_RSMB,  0x0000ffff},
85262306a36Sopenharmony_ci	{REG_ESAI_PRRC,  0x00000000},
85362306a36Sopenharmony_ci	{REG_ESAI_PCRC,  0x00000000},
85462306a36Sopenharmony_ci};
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	switch (reg) {
85962306a36Sopenharmony_ci	case REG_ESAI_ERDR:
86062306a36Sopenharmony_ci	case REG_ESAI_ECR:
86162306a36Sopenharmony_ci	case REG_ESAI_ESR:
86262306a36Sopenharmony_ci	case REG_ESAI_TFCR:
86362306a36Sopenharmony_ci	case REG_ESAI_TFSR:
86462306a36Sopenharmony_ci	case REG_ESAI_RFCR:
86562306a36Sopenharmony_ci	case REG_ESAI_RFSR:
86662306a36Sopenharmony_ci	case REG_ESAI_RX0:
86762306a36Sopenharmony_ci	case REG_ESAI_RX1:
86862306a36Sopenharmony_ci	case REG_ESAI_RX2:
86962306a36Sopenharmony_ci	case REG_ESAI_RX3:
87062306a36Sopenharmony_ci	case REG_ESAI_SAISR:
87162306a36Sopenharmony_ci	case REG_ESAI_SAICR:
87262306a36Sopenharmony_ci	case REG_ESAI_TCR:
87362306a36Sopenharmony_ci	case REG_ESAI_TCCR:
87462306a36Sopenharmony_ci	case REG_ESAI_RCR:
87562306a36Sopenharmony_ci	case REG_ESAI_RCCR:
87662306a36Sopenharmony_ci	case REG_ESAI_TSMA:
87762306a36Sopenharmony_ci	case REG_ESAI_TSMB:
87862306a36Sopenharmony_ci	case REG_ESAI_RSMA:
87962306a36Sopenharmony_ci	case REG_ESAI_RSMB:
88062306a36Sopenharmony_ci	case REG_ESAI_PRRC:
88162306a36Sopenharmony_ci	case REG_ESAI_PCRC:
88262306a36Sopenharmony_ci		return true;
88362306a36Sopenharmony_ci	default:
88462306a36Sopenharmony_ci		return false;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	switch (reg) {
89162306a36Sopenharmony_ci	case REG_ESAI_ERDR:
89262306a36Sopenharmony_ci	case REG_ESAI_ESR:
89362306a36Sopenharmony_ci	case REG_ESAI_TFSR:
89462306a36Sopenharmony_ci	case REG_ESAI_RFSR:
89562306a36Sopenharmony_ci	case REG_ESAI_RX0:
89662306a36Sopenharmony_ci	case REG_ESAI_RX1:
89762306a36Sopenharmony_ci	case REG_ESAI_RX2:
89862306a36Sopenharmony_ci	case REG_ESAI_RX3:
89962306a36Sopenharmony_ci	case REG_ESAI_SAISR:
90062306a36Sopenharmony_ci		return true;
90162306a36Sopenharmony_ci	default:
90262306a36Sopenharmony_ci		return false;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	switch (reg) {
90962306a36Sopenharmony_ci	case REG_ESAI_ETDR:
91062306a36Sopenharmony_ci	case REG_ESAI_ECR:
91162306a36Sopenharmony_ci	case REG_ESAI_TFCR:
91262306a36Sopenharmony_ci	case REG_ESAI_RFCR:
91362306a36Sopenharmony_ci	case REG_ESAI_TX0:
91462306a36Sopenharmony_ci	case REG_ESAI_TX1:
91562306a36Sopenharmony_ci	case REG_ESAI_TX2:
91662306a36Sopenharmony_ci	case REG_ESAI_TX3:
91762306a36Sopenharmony_ci	case REG_ESAI_TX4:
91862306a36Sopenharmony_ci	case REG_ESAI_TX5:
91962306a36Sopenharmony_ci	case REG_ESAI_TSR:
92062306a36Sopenharmony_ci	case REG_ESAI_SAICR:
92162306a36Sopenharmony_ci	case REG_ESAI_TCR:
92262306a36Sopenharmony_ci	case REG_ESAI_TCCR:
92362306a36Sopenharmony_ci	case REG_ESAI_RCR:
92462306a36Sopenharmony_ci	case REG_ESAI_RCCR:
92562306a36Sopenharmony_ci	case REG_ESAI_TSMA:
92662306a36Sopenharmony_ci	case REG_ESAI_TSMB:
92762306a36Sopenharmony_ci	case REG_ESAI_RSMA:
92862306a36Sopenharmony_ci	case REG_ESAI_RSMB:
92962306a36Sopenharmony_ci	case REG_ESAI_PRRC:
93062306a36Sopenharmony_ci	case REG_ESAI_PCRC:
93162306a36Sopenharmony_ci		return true;
93262306a36Sopenharmony_ci	default:
93362306a36Sopenharmony_ci		return false;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic const struct regmap_config fsl_esai_regmap_config = {
93862306a36Sopenharmony_ci	.reg_bits = 32,
93962306a36Sopenharmony_ci	.reg_stride = 4,
94062306a36Sopenharmony_ci	.val_bits = 32,
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	.max_register = REG_ESAI_PCRC,
94362306a36Sopenharmony_ci	.reg_defaults = fsl_esai_reg_defaults,
94462306a36Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
94562306a36Sopenharmony_ci	.readable_reg = fsl_esai_readable_reg,
94662306a36Sopenharmony_ci	.volatile_reg = fsl_esai_volatile_reg,
94762306a36Sopenharmony_ci	.writeable_reg = fsl_esai_writeable_reg,
94862306a36Sopenharmony_ci	.cache_type = REGCACHE_FLAT,
94962306a36Sopenharmony_ci};
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic int fsl_esai_runtime_resume(struct device *dev);
95262306a36Sopenharmony_cistatic int fsl_esai_runtime_suspend(struct device *dev);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic int fsl_esai_probe(struct platform_device *pdev)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
95762306a36Sopenharmony_ci	struct fsl_esai *esai_priv;
95862306a36Sopenharmony_ci	struct resource *res;
95962306a36Sopenharmony_ci	const __be32 *iprop;
96062306a36Sopenharmony_ci	void __iomem *regs;
96162306a36Sopenharmony_ci	int irq, ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);
96462306a36Sopenharmony_ci	if (!esai_priv)
96562306a36Sopenharmony_ci		return -ENOMEM;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	esai_priv->pdev = pdev;
96862306a36Sopenharmony_ci	snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	esai_priv->soc = of_device_get_match_data(&pdev->dev);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* Get the addresses and IRQ */
97362306a36Sopenharmony_ci	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
97462306a36Sopenharmony_ci	if (IS_ERR(regs))
97562306a36Sopenharmony_ci		return PTR_ERR(regs);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	esai_priv->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_esai_regmap_config);
97862306a36Sopenharmony_ci	if (IS_ERR(esai_priv->regmap)) {
97962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to init regmap: %ld\n",
98062306a36Sopenharmony_ci				PTR_ERR(esai_priv->regmap));
98162306a36Sopenharmony_ci		return PTR_ERR(esai_priv->regmap);
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	esai_priv->coreclk = devm_clk_get(&pdev->dev, "core");
98562306a36Sopenharmony_ci	if (IS_ERR(esai_priv->coreclk)) {
98662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get core clock: %ld\n",
98762306a36Sopenharmony_ci				PTR_ERR(esai_priv->coreclk));
98862306a36Sopenharmony_ci		return PTR_ERR(esai_priv->coreclk);
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal");
99262306a36Sopenharmony_ci	if (IS_ERR(esai_priv->extalclk))
99362306a36Sopenharmony_ci		dev_warn(&pdev->dev, "failed to get extal clock: %ld\n",
99462306a36Sopenharmony_ci				PTR_ERR(esai_priv->extalclk));
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys");
99762306a36Sopenharmony_ci	if (IS_ERR(esai_priv->fsysclk))
99862306a36Sopenharmony_ci		dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
99962306a36Sopenharmony_ci				PTR_ERR(esai_priv->fsysclk));
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
100262306a36Sopenharmony_ci	if (IS_ERR(esai_priv->spbaclk))
100362306a36Sopenharmony_ci		dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
100462306a36Sopenharmony_ci				PTR_ERR(esai_priv->spbaclk));
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
100762306a36Sopenharmony_ci	if (irq < 0)
100862306a36Sopenharmony_ci		return irq;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, esai_isr, IRQF_SHARED,
101162306a36Sopenharmony_ci			       esai_priv->name, esai_priv);
101262306a36Sopenharmony_ci	if (ret) {
101362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
101462306a36Sopenharmony_ci		return ret;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* Set a default slot number */
101862306a36Sopenharmony_ci	esai_priv->slots = 2;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* Set a default clock provider state */
102162306a36Sopenharmony_ci	esai_priv->consumer_mode = true;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Determine the FIFO depth */
102462306a36Sopenharmony_ci	iprop = of_get_property(np, "fsl,fifo-depth", NULL);
102562306a36Sopenharmony_ci	if (iprop)
102662306a36Sopenharmony_ci		esai_priv->fifo_depth = be32_to_cpup(iprop);
102762306a36Sopenharmony_ci	else
102862306a36Sopenharmony_ci		esai_priv->fifo_depth = 64;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	esai_priv->dma_params_tx.maxburst = 16;
103162306a36Sopenharmony_ci	esai_priv->dma_params_rx.maxburst = 16;
103262306a36Sopenharmony_ci	esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
103362306a36Sopenharmony_ci	esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	esai_priv->synchronous =
103662306a36Sopenharmony_ci		of_property_read_bool(np, "fsl,esai-synchronous");
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Implement full symmetry for synchronous mode */
103962306a36Sopenharmony_ci	if (esai_priv->synchronous) {
104062306a36Sopenharmony_ci		fsl_esai_dai.symmetric_rate = 1;
104162306a36Sopenharmony_ci		fsl_esai_dai.symmetric_channels = 1;
104262306a36Sopenharmony_ci		fsl_esai_dai.symmetric_sample_bits = 1;
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, esai_priv);
104662306a36Sopenharmony_ci	spin_lock_init(&esai_priv->lock);
104762306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
104862306a36Sopenharmony_ci	if (!pm_runtime_enabled(&pdev->dev)) {
104962306a36Sopenharmony_ci		ret = fsl_esai_runtime_resume(&pdev->dev);
105062306a36Sopenharmony_ci		if (ret)
105162306a36Sopenharmony_ci			goto err_pm_disable;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
105562306a36Sopenharmony_ci	if (ret < 0)
105662306a36Sopenharmony_ci		goto err_pm_get_sync;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	ret = fsl_esai_hw_init(esai_priv);
105962306a36Sopenharmony_ci	if (ret)
106062306a36Sopenharmony_ci		goto err_pm_get_sync;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	esai_priv->tx_mask = 0xFFFFFFFF;
106362306a36Sopenharmony_ci	esai_priv->rx_mask = 0xFFFFFFFF;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* Clear the TSMA, TSMB, RSMA, RSMB */
106662306a36Sopenharmony_ci	regmap_write(esai_priv->regmap, REG_ESAI_TSMA, 0);
106762306a36Sopenharmony_ci	regmap_write(esai_priv->regmap, REG_ESAI_TSMB, 0);
106862306a36Sopenharmony_ci	regmap_write(esai_priv->regmap, REG_ESAI_RSMA, 0);
106962306a36Sopenharmony_ci	regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ret = pm_runtime_put_sync(&pdev->dev);
107262306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOSYS)
107362306a36Sopenharmony_ci		goto err_pm_get_sync;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/*
107662306a36Sopenharmony_ci	 * Register platform component before registering cpu dai for there
107762306a36Sopenharmony_ci	 * is not defer probe for platform component in snd_soc_add_pcm_runtime().
107862306a36Sopenharmony_ci	 */
107962306a36Sopenharmony_ci	ret = imx_pcm_dma_init(pdev);
108062306a36Sopenharmony_ci	if (ret) {
108162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
108262306a36Sopenharmony_ci		goto err_pm_get_sync;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
108662306a36Sopenharmony_ci					      &fsl_esai_dai, 1);
108762306a36Sopenharmony_ci	if (ret) {
108862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
108962306a36Sopenharmony_ci		goto err_pm_get_sync;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	INIT_WORK(&esai_priv->work, fsl_esai_hw_reset);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return ret;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cierr_pm_get_sync:
109762306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(&pdev->dev))
109862306a36Sopenharmony_ci		fsl_esai_runtime_suspend(&pdev->dev);
109962306a36Sopenharmony_cierr_pm_disable:
110062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
110162306a36Sopenharmony_ci	return ret;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic void fsl_esai_remove(struct platform_device *pdev)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
110962306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(&pdev->dev))
111062306a36Sopenharmony_ci		fsl_esai_runtime_suspend(&pdev->dev);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	cancel_work_sync(&esai_priv->work);
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic const struct of_device_id fsl_esai_dt_ids[] = {
111662306a36Sopenharmony_ci	{ .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 },
111762306a36Sopenharmony_ci	{ .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 },
111862306a36Sopenharmony_ci	{ .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull },
111962306a36Sopenharmony_ci	{}
112062306a36Sopenharmony_ci};
112162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic int fsl_esai_runtime_resume(struct device *dev)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct fsl_esai *esai = dev_get_drvdata(dev);
112662306a36Sopenharmony_ci	int ret;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	/*
112962306a36Sopenharmony_ci	 * Some platforms might use the same bit to gate all three or two of
113062306a36Sopenharmony_ci	 * clocks, so keep all clocks open/close at the same time for safety
113162306a36Sopenharmony_ci	 */
113262306a36Sopenharmony_ci	ret = clk_prepare_enable(esai->coreclk);
113362306a36Sopenharmony_ci	if (ret)
113462306a36Sopenharmony_ci		return ret;
113562306a36Sopenharmony_ci	if (!IS_ERR(esai->spbaclk)) {
113662306a36Sopenharmony_ci		ret = clk_prepare_enable(esai->spbaclk);
113762306a36Sopenharmony_ci		if (ret)
113862306a36Sopenharmony_ci			goto err_spbaclk;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci	if (!IS_ERR(esai->extalclk)) {
114162306a36Sopenharmony_ci		ret = clk_prepare_enable(esai->extalclk);
114262306a36Sopenharmony_ci		if (ret)
114362306a36Sopenharmony_ci			goto err_extalclk;
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci	if (!IS_ERR(esai->fsysclk)) {
114662306a36Sopenharmony_ci		ret = clk_prepare_enable(esai->fsysclk);
114762306a36Sopenharmony_ci		if (ret)
114862306a36Sopenharmony_ci			goto err_fsysclk;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	regcache_cache_only(esai->regmap, false);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	ret = fsl_esai_register_restore(esai);
115462306a36Sopenharmony_ci	if (ret)
115562306a36Sopenharmony_ci		goto err_regcache_sync;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return 0;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cierr_regcache_sync:
116062306a36Sopenharmony_ci	if (!IS_ERR(esai->fsysclk))
116162306a36Sopenharmony_ci		clk_disable_unprepare(esai->fsysclk);
116262306a36Sopenharmony_cierr_fsysclk:
116362306a36Sopenharmony_ci	if (!IS_ERR(esai->extalclk))
116462306a36Sopenharmony_ci		clk_disable_unprepare(esai->extalclk);
116562306a36Sopenharmony_cierr_extalclk:
116662306a36Sopenharmony_ci	if (!IS_ERR(esai->spbaclk))
116762306a36Sopenharmony_ci		clk_disable_unprepare(esai->spbaclk);
116862306a36Sopenharmony_cierr_spbaclk:
116962306a36Sopenharmony_ci	clk_disable_unprepare(esai->coreclk);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	return ret;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_cistatic int fsl_esai_runtime_suspend(struct device *dev)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct fsl_esai *esai = dev_get_drvdata(dev);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	regcache_cache_only(esai->regmap, true);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (!IS_ERR(esai->fsysclk))
118162306a36Sopenharmony_ci		clk_disable_unprepare(esai->fsysclk);
118262306a36Sopenharmony_ci	if (!IS_ERR(esai->extalclk))
118362306a36Sopenharmony_ci		clk_disable_unprepare(esai->extalclk);
118462306a36Sopenharmony_ci	if (!IS_ERR(esai->spbaclk))
118562306a36Sopenharmony_ci		clk_disable_unprepare(esai->spbaclk);
118662306a36Sopenharmony_ci	clk_disable_unprepare(esai->coreclk);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	return 0;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic const struct dev_pm_ops fsl_esai_pm_ops = {
119262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend,
119362306a36Sopenharmony_ci			   fsl_esai_runtime_resume,
119462306a36Sopenharmony_ci			   NULL)
119562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
119662306a36Sopenharmony_ci				pm_runtime_force_resume)
119762306a36Sopenharmony_ci};
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic struct platform_driver fsl_esai_driver = {
120062306a36Sopenharmony_ci	.probe = fsl_esai_probe,
120162306a36Sopenharmony_ci	.remove_new = fsl_esai_remove,
120262306a36Sopenharmony_ci	.driver = {
120362306a36Sopenharmony_ci		.name = "fsl-esai-dai",
120462306a36Sopenharmony_ci		.pm = &fsl_esai_pm_ops,
120562306a36Sopenharmony_ci		.of_match_table = fsl_esai_dt_ids,
120662306a36Sopenharmony_ci	},
120762306a36Sopenharmony_ci};
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cimodule_platform_driver(fsl_esai_driver);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
121262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale ESAI CPU DAI driver");
121362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
121462306a36Sopenharmony_ciMODULE_ALIAS("platform:fsl-esai-dai");
1215