brintos

brintos / linux-shallow public Read only

0
0
Text · 32.7 KiB · 0b247f1 Raw
1215 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver4//5// Copyright (C) 2014 Freescale Semiconductor, Inc.6 7#include <linux/clk.h>8#include <linux/dmaengine.h>9#include <linux/module.h>10#include <linux/of_irq.h>11#include <linux/of_platform.h>12#include <linux/pm_runtime.h>13#include <sound/dmaengine_pcm.h>14#include <sound/pcm_params.h>15 16#include "fsl_esai.h"17#include "imx-pcm.h"18 19#define FSL_ESAI_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \20				SNDRV_PCM_FMTBIT_S16_LE | \21				SNDRV_PCM_FMTBIT_S20_3LE | \22				SNDRV_PCM_FMTBIT_S24_LE)23 24/**25 * struct fsl_esai_soc_data - soc specific data26 * @reset_at_xrun: flags for enable reset operaton27 */28struct fsl_esai_soc_data {29	bool reset_at_xrun;30};31 32/**33 * struct fsl_esai - ESAI private data34 * @dma_params_rx: DMA parameters for receive channel35 * @dma_params_tx: DMA parameters for transmit channel36 * @pdev: platform device pointer37 * @regmap: regmap handler38 * @coreclk: clock source to access register39 * @extalclk: esai clock source to derive HCK, SCK and FS40 * @fsysclk: system clock source to derive HCK, SCK and FS41 * @spbaclk: SPBA clock (optional, depending on SoC design)42 * @work: work to handle the reset operation43 * @soc: soc specific data44 * @lock: spin lock between hw_reset() and trigger()45 * @fifo_depth: depth of tx/rx FIFO46 * @slot_width: width of each DAI slot47 * @slots: number of slots48 * @tx_mask: slot mask for TX49 * @rx_mask: slot mask for RX50 * @channels: channel num for tx or rx51 * @hck_rate: clock rate of desired HCKx clock52 * @sck_rate: clock rate of desired SCKx clock53 * @hck_dir: the direction of HCKx pads54 * @sck_div: if using PSR/PM dividers for SCKx clock55 * @consumer_mode: if fully using DAI clock consumer mode56 * @synchronous: if using tx/rx synchronous mode57 * @name: driver name58 */59struct fsl_esai {60	struct snd_dmaengine_dai_dma_data dma_params_rx;61	struct snd_dmaengine_dai_dma_data dma_params_tx;62	struct platform_device *pdev;63	struct regmap *regmap;64	struct clk *coreclk;65	struct clk *extalclk;66	struct clk *fsysclk;67	struct clk *spbaclk;68	struct work_struct work;69	const struct fsl_esai_soc_data *soc;70	spinlock_t lock; /* Protect hw_reset and trigger */71	u32 fifo_depth;72	u32 slot_width;73	u32 slots;74	u32 tx_mask;75	u32 rx_mask;76	u32 channels[2];77	u32 hck_rate[2];78	u32 sck_rate[2];79	bool hck_dir[2];80	bool sck_div[2];81	bool consumer_mode;82	bool synchronous;83	char name[32];84};85 86static struct fsl_esai_soc_data fsl_esai_vf610 = {87	.reset_at_xrun = true,88};89 90static struct fsl_esai_soc_data fsl_esai_imx35 = {91	.reset_at_xrun = true,92};93 94static struct fsl_esai_soc_data fsl_esai_imx6ull = {95	.reset_at_xrun = false,96};97 98static irqreturn_t esai_isr(int irq, void *devid)99{100	struct fsl_esai *esai_priv = (struct fsl_esai *)devid;101	struct platform_device *pdev = esai_priv->pdev;102	u32 esr;103	u32 saisr;104 105	regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);106	regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);107 108	if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&109	    esai_priv->soc->reset_at_xrun) {110		dev_dbg(&pdev->dev, "reset module for xrun\n");111		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,112				   ESAI_xCR_xEIE_MASK, 0);113		regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,114				   ESAI_xCR_xEIE_MASK, 0);115		schedule_work(&esai_priv->work);116	}117 118	if (esr & ESAI_ESR_TINIT_MASK)119		dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");120 121	if (esr & ESAI_ESR_RFF_MASK)122		dev_dbg(&pdev->dev, "isr: Receiving overrun\n");123 124	if (esr & ESAI_ESR_TFE_MASK)125		dev_dbg(&pdev->dev, "isr: Transmission underrun\n");126 127	if (esr & ESAI_ESR_TLS_MASK)128		dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");129 130	if (esr & ESAI_ESR_TDE_MASK)131		dev_dbg(&pdev->dev, "isr: Transmission data exception\n");132 133	if (esr & ESAI_ESR_TED_MASK)134		dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");135 136	if (esr & ESAI_ESR_TD_MASK)137		dev_dbg(&pdev->dev, "isr: Transmitting data\n");138 139	if (esr & ESAI_ESR_RLS_MASK)140		dev_dbg(&pdev->dev, "isr: Just received the last slot\n");141 142	if (esr & ESAI_ESR_RDE_MASK)143		dev_dbg(&pdev->dev, "isr: Receiving data exception\n");144 145	if (esr & ESAI_ESR_RED_MASK)146		dev_dbg(&pdev->dev, "isr: Receiving even slots\n");147 148	if (esr & ESAI_ESR_RD_MASK)149		dev_dbg(&pdev->dev, "isr: Receiving data\n");150 151	return IRQ_HANDLED;152}153 154/**155 * fsl_esai_divisor_cal - This function is used to calculate the156 * divisors of psr, pm, fp and it is supposed to be called in157 * set_dai_sysclk() and set_bclk().158 *159 * @dai: pointer to DAI160 * @tx: current setting is for playback or capture161 * @ratio: desired overall ratio for the paticipating dividers162 * @usefp: for HCK setting, there is no need to set fp divider163 * @fp: bypass other dividers by setting fp directly if fp != 0164 */165static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,166				bool usefp, u32 fp)167{168	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);169	u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;170 171	maxfp = usefp ? 16 : 1;172 173	if (usefp && fp)174		goto out_fp;175 176	if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {177		dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",178				2 * 8 * 256 * maxfp);179		return -EINVAL;180	} else if (ratio % 2) {181		dev_err(dai->dev, "the raio must be even if using upper divider\n");182		return -EINVAL;183	}184 185	ratio /= 2;186 187	psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;188 189	/* Do not loop-search if PM (1 ~ 256) alone can serve the ratio */190	if (ratio <= 256) {191		pm = ratio;192		fp = 1;193		goto out;194	}195 196	/* Set the max fluctuation -- 0.1% of the max devisor */197	savesub = (psr ? 1 : 8)  * 256 * maxfp / 1000;198 199	/* Find the best value for PM */200	for (i = 1; i <= 256; i++) {201		for (j = 1; j <= maxfp; j++) {202			/* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */203			prod = (psr ? 1 : 8) * i * j;204 205			if (prod == ratio)206				sub = 0;207			else if (prod / ratio == 1)208				sub = prod - ratio;209			else if (ratio / prod == 1)210				sub = ratio - prod;211			else212				continue;213 214			/* Calculate the fraction */215			sub = sub * 1000 / ratio;216			if (sub < savesub) {217				savesub = sub;218				pm = i;219				fp = j;220			}221 222			/* We are lucky */223			if (savesub == 0)224				goto out;225		}226	}227 228	if (pm == 999) {229		dev_err(dai->dev, "failed to calculate proper divisors\n");230		return -EINVAL;231	}232 233out:234	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),235			   ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,236			   psr | ESAI_xCCR_xPM(pm));237 238out_fp:239	/* Bypass fp if not being required */240	if (maxfp <= 1)241		return 0;242 243	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),244			   ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));245 246	return 0;247}248 249/**250 * fsl_esai_set_dai_sysclk - configure the clock frequency of MCLK (HCKT/HCKR)251 * @dai: pointer to DAI252 * @clk_id: The clock source of HCKT/HCKR253 *	  (Input from outside; output from inside, FSYS or EXTAL)254 * @freq: The required clock rate of HCKT/HCKR255 * @dir: The clock direction of HCKT/HCKR256 *257 * Note: If the direction is input, we do not care about clk_id.258 */259static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,260				   unsigned int freq, int dir)261{262	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);263	struct clk *clksrc = esai_priv->extalclk;264	bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous);265	bool in = dir == SND_SOC_CLOCK_IN;266	u32 ratio, ecr = 0;267	unsigned long clk_rate;268	int ret;269 270	if (freq == 0) {271		dev_err(dai->dev, "%sput freq of HCK%c should not be 0Hz\n",272			in ? "in" : "out", tx ? 'T' : 'R');273		return -EINVAL;274	}275 276	/* Bypass divider settings if the requirement doesn't change */277	if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])278		return 0;279 280	/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */281	esai_priv->sck_div[tx] = true;282 283	/* Set the direction of HCKT/HCKR pins */284	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),285			   ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);286 287	if (in)288		goto out;289 290	switch (clk_id) {291	case ESAI_HCKT_FSYS:292	case ESAI_HCKR_FSYS:293		clksrc = esai_priv->fsysclk;294		break;295	case ESAI_HCKT_EXTAL:296		ecr |= ESAI_ECR_ETI;297		break;298	case ESAI_HCKR_EXTAL:299		ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI;300		break;301	default:302		return -EINVAL;303	}304 305	if (IS_ERR(clksrc)) {306		dev_err(dai->dev, "no assigned %s clock\n",307			(clk_id % 2) ? "extal" : "fsys");308		return PTR_ERR(clksrc);309	}310	clk_rate = clk_get_rate(clksrc);311 312	ratio = clk_rate / freq;313	if (ratio * freq > clk_rate)314		ret = ratio * freq - clk_rate;315	else if (ratio * freq < clk_rate)316		ret = clk_rate - ratio * freq;317	else318		ret = 0;319 320	/* Block if clock source can not be divided into the required rate */321	if (ret != 0 && clk_rate / ret < 1000) {322		dev_err(dai->dev, "failed to derive required HCK%c rate\n",323				tx ? 'T' : 'R');324		return -EINVAL;325	}326 327	/* Only EXTAL source can be output directly without using PSR and PM */328	if (ratio == 1 && clksrc == esai_priv->extalclk) {329		/* Bypass all the dividers if not being needed */330		ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;331		goto out;332	} else if (ratio < 2) {333		/* The ratio should be no less than 2 if using other sources */334		dev_err(dai->dev, "failed to derive required HCK%c rate\n",335				tx ? 'T' : 'R');336		return -EINVAL;337	}338 339	ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);340	if (ret)341		return ret;342 343	esai_priv->sck_div[tx] = false;344 345out:346	esai_priv->hck_dir[tx] = dir;347	esai_priv->hck_rate[tx] = freq;348 349	regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,350			   tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :351			   ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);352 353	return 0;354}355 356/**357 * fsl_esai_set_bclk - configure the related dividers according to the bclk rate358 * @dai: pointer to DAI359 * @tx: direction boolean360 * @freq: bclk freq361 */362static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)363{364	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);365	u32 hck_rate = esai_priv->hck_rate[tx];366	u32 sub, ratio = hck_rate / freq;367	int ret;368 369	/* Don't apply for fully consumer mode or unchanged bclk */370	if (esai_priv->consumer_mode || esai_priv->sck_rate[tx] == freq)371		return 0;372 373	if (ratio * freq > hck_rate)374		sub = ratio * freq - hck_rate;375	else if (ratio * freq < hck_rate)376		sub = hck_rate - ratio * freq;377	else378		sub = 0;379 380	/* Block if clock source can not be divided into the required rate */381	if (sub != 0 && hck_rate / sub < 1000) {382		dev_err(dai->dev, "failed to derive required SCK%c rate\n",383				tx ? 'T' : 'R');384		return -EINVAL;385	}386 387	/* The ratio should be contented by FP alone if bypassing PM and PSR */388	if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {389		dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");390		return -EINVAL;391	}392 393	ret = fsl_esai_divisor_cal(dai, tx, ratio, true,394			esai_priv->sck_div[tx] ? 0 : ratio);395	if (ret)396		return ret;397 398	/* Save current bclk rate */399	esai_priv->sck_rate[tx] = freq;400 401	return 0;402}403 404static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,405				     u32 rx_mask, int slots, int slot_width)406{407	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);408 409	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,410			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));411 412	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,413			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));414 415	esai_priv->slot_width = slot_width;416	esai_priv->slots = slots;417	esai_priv->tx_mask = tx_mask;418	esai_priv->rx_mask = rx_mask;419 420	return 0;421}422 423static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)424{425	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);426	u32 xcr = 0, xccr = 0, mask;427 428	/* DAI mode */429	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {430	case SND_SOC_DAIFMT_I2S:431		/* Data on rising edge of bclk, frame low, 1clk before data */432		xcr |= ESAI_xCR_xFSR;433		xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;434		break;435	case SND_SOC_DAIFMT_LEFT_J:436		/* Data on rising edge of bclk, frame high */437		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;438		break;439	case SND_SOC_DAIFMT_RIGHT_J:440		/* Data on rising edge of bclk, frame high, right aligned */441		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;442		xcr  |= ESAI_xCR_xWA;443		break;444	case SND_SOC_DAIFMT_DSP_A:445		/* Data on rising edge of bclk, frame high, 1clk before data */446		xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR;447		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;448		break;449	case SND_SOC_DAIFMT_DSP_B:450		/* Data on rising edge of bclk, frame high */451		xcr |= ESAI_xCR_xFSL;452		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;453		break;454	default:455		return -EINVAL;456	}457 458	/* DAI clock inversion */459	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {460	case SND_SOC_DAIFMT_NB_NF:461		/* Nothing to do for both normal cases */462		break;463	case SND_SOC_DAIFMT_IB_NF:464		/* Invert bit clock */465		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;466		break;467	case SND_SOC_DAIFMT_NB_IF:468		/* Invert frame clock */469		xccr ^= ESAI_xCCR_xFSP;470		break;471	case SND_SOC_DAIFMT_IB_IF:472		/* Invert both clocks */473		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP;474		break;475	default:476		return -EINVAL;477	}478 479	esai_priv->consumer_mode = false;480 481	/* DAI clock provider masks */482	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {483	case SND_SOC_DAIFMT_BC_FC:484		esai_priv->consumer_mode = true;485		break;486	case SND_SOC_DAIFMT_BP_FC:487		xccr |= ESAI_xCCR_xCKD;488		break;489	case SND_SOC_DAIFMT_BC_FP:490		xccr |= ESAI_xCCR_xFSD;491		break;492	case SND_SOC_DAIFMT_BP_FP:493		xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;494		break;495	default:496		return -EINVAL;497	}498 499	mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR | ESAI_xCR_xWA;500	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);501	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);502 503	mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |504		ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;505	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);506	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);507 508	return 0;509}510 511static int fsl_esai_startup(struct snd_pcm_substream *substream,512			    struct snd_soc_dai *dai)513{514	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);515 516	if (!snd_soc_dai_active(dai)) {517		/* Set synchronous mode */518		regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,519				   ESAI_SAICR_SYNC, esai_priv->synchronous ?520				   ESAI_SAICR_SYNC : 0);521 522		/* Set slots count */523		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,524				   ESAI_xCCR_xDC_MASK,525				   ESAI_xCCR_xDC(esai_priv->slots));526		regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,527				   ESAI_xCCR_xDC_MASK,528				   ESAI_xCCR_xDC(esai_priv->slots));529	}530 531	return 0;532 533}534 535static int fsl_esai_hw_params(struct snd_pcm_substream *substream,536			      struct snd_pcm_hw_params *params,537			      struct snd_soc_dai *dai)538{539	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);540	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;541	u32 width = params_width(params);542	u32 channels = params_channels(params);543	u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);544	u32 slot_width = width;545	u32 bclk, mask, val;546	int ret;547 548	/* Override slot_width if being specifically set */549	if (esai_priv->slot_width)550		slot_width = esai_priv->slot_width;551 552	bclk = params_rate(params) * slot_width * esai_priv->slots;553 554	ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk);555	if (ret)556		return ret;557 558	mask = ESAI_xCR_xSWS_MASK;559	val = ESAI_xCR_xSWS(slot_width, width);560 561	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);562	/* Recording in synchronous mode needs to set TCR also */563	if (!tx && esai_priv->synchronous)564		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val);565 566	/* Use Normal mode to support monaural audio */567	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),568			   ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?569			   ESAI_xCR_xMOD_NETWORK : 0);570 571	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),572			   ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);573 574	mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |575	      (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);576	val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |577	     (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins));578 579	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);580 581	if (tx)582		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,583				ESAI_xCR_PADC, ESAI_xCR_PADC);584 585	/* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */586	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,587			   ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));588	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,589			   ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));590	return 0;591}592 593static int fsl_esai_hw_init(struct fsl_esai *esai_priv)594{595	struct platform_device *pdev = esai_priv->pdev;596	int ret;597 598	/* Reset ESAI unit */599	ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,600				 ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,601				 ESAI_ECR_ESAIEN | ESAI_ECR_ERST);602	if (ret) {603		dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);604		return ret;605	}606 607	/*608	 * We need to enable ESAI so as to access some of its registers.609	 * Otherwise, we would fail to dump regmap from user space.610	 */611	ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,612				 ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,613				 ESAI_ECR_ESAIEN);614	if (ret) {615		dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);616		return ret;617	}618 619	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,620			   ESAI_PRRC_PDC_MASK, 0);621	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,622			   ESAI_PCRC_PC_MASK, 0);623 624	return 0;625}626 627static int fsl_esai_register_restore(struct fsl_esai *esai_priv)628{629	int ret;630 631	/* FIFO reset for safety */632	regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR,633			   ESAI_xFCR_xFR, ESAI_xFCR_xFR);634	regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR,635			   ESAI_xFCR_xFR, ESAI_xFCR_xFR);636 637	regcache_mark_dirty(esai_priv->regmap);638	ret = regcache_sync(esai_priv->regmap);639	if (ret)640		return ret;641 642	/* FIFO reset done */643	regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);644	regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);645 646	return 0;647}648 649static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx)650{651	u8 i, channels = esai_priv->channels[tx];652	u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);653	u32 mask;654 655	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),656			   ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);657 658	/* Write initial words reqiured by ESAI as normal procedure */659	for (i = 0; tx && i < channels; i++)660		regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);661 662	/*663	 * When set the TE/RE in the end of enablement flow, there664	 * will be channel swap issue for multi data line case.665	 * In order to workaround this issue, we switch the bit666	 * enablement sequence to below sequence667	 * 1) clear the xSMB & xSMA: which is done in probe and668	 *                           stop state.669	 * 2) set TE/RE670	 * 3) set xSMB671	 * 4) set xSMA:  xSMA is the last one in this flow, which672	 *               will trigger esai to start.673	 */674	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),675			   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,676			   tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));677	mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;678 679	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),680			   ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));681	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),682			   ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));683 684	/* Enable Exception interrupt */685	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),686			   ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE);687}688 689static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)690{691	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),692			   ESAI_xCR_xEIE_MASK, 0);693 694	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),695			   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);696	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),697			   ESAI_xSMA_xS_MASK, 0);698	regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),699			   ESAI_xSMB_xS_MASK, 0);700 701	/* Disable and reset FIFO */702	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),703			   ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);704	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),705			   ESAI_xFCR_xFR, 0);706}707 708static void fsl_esai_hw_reset(struct work_struct *work)709{710	struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work);711	bool tx = true, rx = false, enabled[2];712	unsigned long lock_flags;713	u32 tfcr, rfcr;714 715	spin_lock_irqsave(&esai_priv->lock, lock_flags);716	/* Save the registers */717	regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);718	regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);719	enabled[tx] = tfcr & ESAI_xFCR_xFEN;720	enabled[rx] = rfcr & ESAI_xFCR_xFEN;721 722	/* Stop the tx & rx */723	fsl_esai_trigger_stop(esai_priv, tx);724	fsl_esai_trigger_stop(esai_priv, rx);725 726	/* Reset the esai, and ignore return value */727	fsl_esai_hw_init(esai_priv);728 729	/* Enforce ESAI personal resets for both TX and RX */730	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,731			   ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);732	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,733			   ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);734 735	/* Restore registers by regcache_sync, and ignore return value */736	fsl_esai_register_restore(esai_priv);737 738	/* Remove ESAI personal resets by configuring PCRC and PRRC also */739	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,740			   ESAI_xCR_xPR_MASK, 0);741	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,742			   ESAI_xCR_xPR_MASK, 0);743	regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,744			   ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));745	regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,746			   ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));747 748	/* Restart tx / rx, if they already enabled */749	if (enabled[tx])750		fsl_esai_trigger_start(esai_priv, tx);751	if (enabled[rx])752		fsl_esai_trigger_start(esai_priv, rx);753 754	spin_unlock_irqrestore(&esai_priv->lock, lock_flags);755}756 757static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,758			    struct snd_soc_dai *dai)759{760	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);761	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;762	unsigned long lock_flags;763 764	esai_priv->channels[tx] = substream->runtime->channels;765 766	switch (cmd) {767	case SNDRV_PCM_TRIGGER_START:768	case SNDRV_PCM_TRIGGER_RESUME:769	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:770		spin_lock_irqsave(&esai_priv->lock, lock_flags);771		fsl_esai_trigger_start(esai_priv, tx);772		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);773		break;774	case SNDRV_PCM_TRIGGER_SUSPEND:775	case SNDRV_PCM_TRIGGER_STOP:776	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:777		spin_lock_irqsave(&esai_priv->lock, lock_flags);778		fsl_esai_trigger_stop(esai_priv, tx);779		spin_unlock_irqrestore(&esai_priv->lock, lock_flags);780		break;781	default:782		return -EINVAL;783	}784 785	return 0;786}787 788static int fsl_esai_dai_probe(struct snd_soc_dai *dai)789{790	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);791 792	snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx,793				  &esai_priv->dma_params_rx);794 795	return 0;796}797 798static const struct snd_soc_dai_ops fsl_esai_dai_ops = {799	.probe		= fsl_esai_dai_probe,800	.startup	= fsl_esai_startup,801	.trigger	= fsl_esai_trigger,802	.hw_params	= fsl_esai_hw_params,803	.set_sysclk	= fsl_esai_set_dai_sysclk,804	.set_fmt	= fsl_esai_set_dai_fmt,805	.set_tdm_slot	= fsl_esai_set_dai_tdm_slot,806};807 808static struct snd_soc_dai_driver fsl_esai_dai = {809	.playback = {810		.stream_name = "CPU-Playback",811		.channels_min = 1,812		.channels_max = 12,813		.rates = SNDRV_PCM_RATE_8000_192000,814		.formats = FSL_ESAI_FORMATS,815	},816	.capture = {817		.stream_name = "CPU-Capture",818		.channels_min = 1,819		.channels_max = 8,820		.rates = SNDRV_PCM_RATE_8000_192000,821		.formats = FSL_ESAI_FORMATS,822	},823	.ops = &fsl_esai_dai_ops,824};825 826static const struct snd_soc_component_driver fsl_esai_component = {827	.name			= "fsl-esai",828	.legacy_dai_naming	= 1,829};830 831static const struct reg_default fsl_esai_reg_defaults[] = {832	{REG_ESAI_ETDR,	 0x00000000},833	{REG_ESAI_ECR,	 0x00000000},834	{REG_ESAI_TFCR,	 0x00000000},835	{REG_ESAI_RFCR,	 0x00000000},836	{REG_ESAI_TX0,	 0x00000000},837	{REG_ESAI_TX1,	 0x00000000},838	{REG_ESAI_TX2,	 0x00000000},839	{REG_ESAI_TX3,	 0x00000000},840	{REG_ESAI_TX4,	 0x00000000},841	{REG_ESAI_TX5,	 0x00000000},842	{REG_ESAI_TSR,	 0x00000000},843	{REG_ESAI_SAICR, 0x00000000},844	{REG_ESAI_TCR,	 0x00000000},845	{REG_ESAI_TCCR,	 0x00000000},846	{REG_ESAI_RCR,	 0x00000000},847	{REG_ESAI_RCCR,	 0x00000000},848	{REG_ESAI_TSMA,  0x0000ffff},849	{REG_ESAI_TSMB,  0x0000ffff},850	{REG_ESAI_RSMA,  0x0000ffff},851	{REG_ESAI_RSMB,  0x0000ffff},852	{REG_ESAI_PRRC,  0x00000000},853	{REG_ESAI_PCRC,  0x00000000},854};855 856static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)857{858	switch (reg) {859	case REG_ESAI_ERDR:860	case REG_ESAI_ECR:861	case REG_ESAI_ESR:862	case REG_ESAI_TFCR:863	case REG_ESAI_TFSR:864	case REG_ESAI_RFCR:865	case REG_ESAI_RFSR:866	case REG_ESAI_RX0:867	case REG_ESAI_RX1:868	case REG_ESAI_RX2:869	case REG_ESAI_RX3:870	case REG_ESAI_SAISR:871	case REG_ESAI_SAICR:872	case REG_ESAI_TCR:873	case REG_ESAI_TCCR:874	case REG_ESAI_RCR:875	case REG_ESAI_RCCR:876	case REG_ESAI_TSMA:877	case REG_ESAI_TSMB:878	case REG_ESAI_RSMA:879	case REG_ESAI_RSMB:880	case REG_ESAI_PRRC:881	case REG_ESAI_PCRC:882		return true;883	default:884		return false;885	}886}887 888static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)889{890	switch (reg) {891	case REG_ESAI_ERDR:892	case REG_ESAI_ESR:893	case REG_ESAI_TFSR:894	case REG_ESAI_RFSR:895	case REG_ESAI_RX0:896	case REG_ESAI_RX1:897	case REG_ESAI_RX2:898	case REG_ESAI_RX3:899	case REG_ESAI_SAISR:900		return true;901	default:902		return false;903	}904}905 906static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)907{908	switch (reg) {909	case REG_ESAI_ETDR:910	case REG_ESAI_ECR:911	case REG_ESAI_TFCR:912	case REG_ESAI_RFCR:913	case REG_ESAI_TX0:914	case REG_ESAI_TX1:915	case REG_ESAI_TX2:916	case REG_ESAI_TX3:917	case REG_ESAI_TX4:918	case REG_ESAI_TX5:919	case REG_ESAI_TSR:920	case REG_ESAI_SAICR:921	case REG_ESAI_TCR:922	case REG_ESAI_TCCR:923	case REG_ESAI_RCR:924	case REG_ESAI_RCCR:925	case REG_ESAI_TSMA:926	case REG_ESAI_TSMB:927	case REG_ESAI_RSMA:928	case REG_ESAI_RSMB:929	case REG_ESAI_PRRC:930	case REG_ESAI_PCRC:931		return true;932	default:933		return false;934	}935}936 937static const struct regmap_config fsl_esai_regmap_config = {938	.reg_bits = 32,939	.reg_stride = 4,940	.val_bits = 32,941 942	.max_register = REG_ESAI_PCRC,943	.reg_defaults = fsl_esai_reg_defaults,944	.num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),945	.readable_reg = fsl_esai_readable_reg,946	.volatile_reg = fsl_esai_volatile_reg,947	.writeable_reg = fsl_esai_writeable_reg,948	.cache_type = REGCACHE_FLAT,949};950 951static int fsl_esai_runtime_resume(struct device *dev);952static int fsl_esai_runtime_suspend(struct device *dev);953 954static int fsl_esai_probe(struct platform_device *pdev)955{956	struct device_node *np = pdev->dev.of_node;957	struct fsl_esai *esai_priv;958	struct resource *res;959	const __be32 *iprop;960	void __iomem *regs;961	int irq, ret;962 963	esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);964	if (!esai_priv)965		return -ENOMEM;966 967	esai_priv->pdev = pdev;968	snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np);969 970	esai_priv->soc = of_device_get_match_data(&pdev->dev);971 972	/* Get the addresses and IRQ */973	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);974	if (IS_ERR(regs))975		return PTR_ERR(regs);976 977	esai_priv->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_esai_regmap_config);978	if (IS_ERR(esai_priv->regmap)) {979		dev_err(&pdev->dev, "failed to init regmap: %ld\n",980				PTR_ERR(esai_priv->regmap));981		return PTR_ERR(esai_priv->regmap);982	}983 984	esai_priv->coreclk = devm_clk_get(&pdev->dev, "core");985	if (IS_ERR(esai_priv->coreclk)) {986		dev_err(&pdev->dev, "failed to get core clock: %ld\n",987				PTR_ERR(esai_priv->coreclk));988		return PTR_ERR(esai_priv->coreclk);989	}990 991	esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal");992	if (IS_ERR(esai_priv->extalclk))993		dev_warn(&pdev->dev, "failed to get extal clock: %ld\n",994				PTR_ERR(esai_priv->extalclk));995 996	esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys");997	if (IS_ERR(esai_priv->fsysclk))998		dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",999				PTR_ERR(esai_priv->fsysclk));1000 1001	esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");1002	if (IS_ERR(esai_priv->spbaclk))1003		dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",1004				PTR_ERR(esai_priv->spbaclk));1005 1006	irq = platform_get_irq(pdev, 0);1007	if (irq < 0)1008		return irq;1009 1010	ret = devm_request_irq(&pdev->dev, irq, esai_isr, IRQF_SHARED,1011			       esai_priv->name, esai_priv);1012	if (ret) {1013		dev_err(&pdev->dev, "failed to claim irq %u\n", irq);1014		return ret;1015	}1016 1017	/* Set a default slot number */1018	esai_priv->slots = 2;1019 1020	/* Set a default clock provider state */1021	esai_priv->consumer_mode = true;1022 1023	/* Determine the FIFO depth */1024	iprop = of_get_property(np, "fsl,fifo-depth", NULL);1025	if (iprop)1026		esai_priv->fifo_depth = be32_to_cpup(iprop);1027	else1028		esai_priv->fifo_depth = 64;1029 1030	esai_priv->dma_params_tx.maxburst = 16;1031	esai_priv->dma_params_rx.maxburst = 16;1032	esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;1033	esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;1034 1035	esai_priv->synchronous =1036		of_property_read_bool(np, "fsl,esai-synchronous");1037 1038	/* Implement full symmetry for synchronous mode */1039	if (esai_priv->synchronous) {1040		fsl_esai_dai.symmetric_rate = 1;1041		fsl_esai_dai.symmetric_channels = 1;1042		fsl_esai_dai.symmetric_sample_bits = 1;1043	}1044 1045	dev_set_drvdata(&pdev->dev, esai_priv);1046	spin_lock_init(&esai_priv->lock);1047	pm_runtime_enable(&pdev->dev);1048	if (!pm_runtime_enabled(&pdev->dev)) {1049		ret = fsl_esai_runtime_resume(&pdev->dev);1050		if (ret)1051			goto err_pm_disable;1052	}1053 1054	ret = pm_runtime_resume_and_get(&pdev->dev);1055	if (ret < 0)1056		goto err_pm_get_sync;1057 1058	ret = fsl_esai_hw_init(esai_priv);1059	if (ret)1060		goto err_pm_get_sync;1061 1062	esai_priv->tx_mask = 0xFFFFFFFF;1063	esai_priv->rx_mask = 0xFFFFFFFF;1064 1065	/* Clear the TSMA, TSMB, RSMA, RSMB */1066	regmap_write(esai_priv->regmap, REG_ESAI_TSMA, 0);1067	regmap_write(esai_priv->regmap, REG_ESAI_TSMB, 0);1068	regmap_write(esai_priv->regmap, REG_ESAI_RSMA, 0);1069	regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0);1070 1071	ret = pm_runtime_put_sync(&pdev->dev);1072	if (ret < 0 && ret != -ENOSYS)1073		goto err_pm_get_sync;1074 1075	/*1076	 * Register platform component before registering cpu dai for there1077	 * is not defer probe for platform component in snd_soc_add_pcm_runtime().1078	 */1079	ret = imx_pcm_dma_init(pdev);1080	if (ret) {1081		dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);1082		goto err_pm_get_sync;1083	}1084 1085	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,1086					      &fsl_esai_dai, 1);1087	if (ret) {1088		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);1089		goto err_pm_get_sync;1090	}1091 1092	INIT_WORK(&esai_priv->work, fsl_esai_hw_reset);1093 1094	return ret;1095 1096err_pm_get_sync:1097	if (!pm_runtime_status_suspended(&pdev->dev))1098		fsl_esai_runtime_suspend(&pdev->dev);1099err_pm_disable:1100	pm_runtime_disable(&pdev->dev);1101	return ret;1102}1103 1104static void fsl_esai_remove(struct platform_device *pdev)1105{1106	struct fsl_esai *esai_priv = platform_get_drvdata(pdev);1107 1108	pm_runtime_disable(&pdev->dev);1109	if (!pm_runtime_status_suspended(&pdev->dev))1110		fsl_esai_runtime_suspend(&pdev->dev);1111 1112	cancel_work_sync(&esai_priv->work);1113}1114 1115static const struct of_device_id fsl_esai_dt_ids[] = {1116	{ .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 },1117	{ .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 },1118	{ .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull },1119	{}1120};1121MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);1122 1123static int fsl_esai_runtime_resume(struct device *dev)1124{1125	struct fsl_esai *esai = dev_get_drvdata(dev);1126	int ret;1127 1128	/*1129	 * Some platforms might use the same bit to gate all three or two of1130	 * clocks, so keep all clocks open/close at the same time for safety1131	 */1132	ret = clk_prepare_enable(esai->coreclk);1133	if (ret)1134		return ret;1135	if (!IS_ERR(esai->spbaclk)) {1136		ret = clk_prepare_enable(esai->spbaclk);1137		if (ret)1138			goto err_spbaclk;1139	}1140	if (!IS_ERR(esai->extalclk)) {1141		ret = clk_prepare_enable(esai->extalclk);1142		if (ret)1143			goto err_extalclk;1144	}1145	if (!IS_ERR(esai->fsysclk)) {1146		ret = clk_prepare_enable(esai->fsysclk);1147		if (ret)1148			goto err_fsysclk;1149	}1150 1151	regcache_cache_only(esai->regmap, false);1152 1153	ret = fsl_esai_register_restore(esai);1154	if (ret)1155		goto err_regcache_sync;1156 1157	return 0;1158 1159err_regcache_sync:1160	if (!IS_ERR(esai->fsysclk))1161		clk_disable_unprepare(esai->fsysclk);1162err_fsysclk:1163	if (!IS_ERR(esai->extalclk))1164		clk_disable_unprepare(esai->extalclk);1165err_extalclk:1166	if (!IS_ERR(esai->spbaclk))1167		clk_disable_unprepare(esai->spbaclk);1168err_spbaclk:1169	clk_disable_unprepare(esai->coreclk);1170 1171	return ret;1172}1173 1174static int fsl_esai_runtime_suspend(struct device *dev)1175{1176	struct fsl_esai *esai = dev_get_drvdata(dev);1177 1178	regcache_cache_only(esai->regmap, true);1179 1180	if (!IS_ERR(esai->fsysclk))1181		clk_disable_unprepare(esai->fsysclk);1182	if (!IS_ERR(esai->extalclk))1183		clk_disable_unprepare(esai->extalclk);1184	if (!IS_ERR(esai->spbaclk))1185		clk_disable_unprepare(esai->spbaclk);1186	clk_disable_unprepare(esai->coreclk);1187 1188	return 0;1189}1190 1191static const struct dev_pm_ops fsl_esai_pm_ops = {1192	SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend,1193			   fsl_esai_runtime_resume,1194			   NULL)1195	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,1196				pm_runtime_force_resume)1197};1198 1199static struct platform_driver fsl_esai_driver = {1200	.probe = fsl_esai_probe,1201	.remove = fsl_esai_remove,1202	.driver = {1203		.name = "fsl-esai-dai",1204		.pm = &fsl_esai_pm_ops,1205		.of_match_table = fsl_esai_dt_ids,1206	},1207};1208 1209module_platform_driver(fsl_esai_driver);1210 1211MODULE_AUTHOR("Freescale Semiconductor, Inc.");1212MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver");1213MODULE_LICENSE("GPL v2");1214MODULE_ALIAS("platform:fsl-esai-dai");1215