1dbbeaad4SFabio Estevam // SPDX-License-Identifier: GPL-2.0+
2dbbeaad4SFabio Estevam //
3dbbeaad4SFabio Estevam // Freescale ALSA SoC Digital Audio Interface (SAI) driver.
4dbbeaad4SFabio Estevam //
5dbbeaad4SFabio Estevam // Copyright 2012-2015 Freescale Semiconductor, Inc.
643550821SXiubo Li
743550821SXiubo Li #include <linux/clk.h>
843550821SXiubo Li #include <linux/delay.h>
943550821SXiubo Li #include <linux/dmaengine.h>
1043550821SXiubo Li #include <linux/module.h>
1143550821SXiubo Li #include <linux/of_address.h>
1289c9679fSLucas Stach #include <linux/of_device.h>
13b4ee8a91SShengjiu Wang #include <linux/pinctrl/consumer.h>
14907e0cdeSShengjiu Wang #include <linux/pm_qos.h>
15812ad463SDaniel Baluta #include <linux/pm_runtime.h>
1678957fc3SXiubo Li #include <linux/regmap.h>
1743550821SXiubo Li #include <linux/slab.h>
18512feb4eSXiubo Li #include <linux/time.h>
1943550821SXiubo Li #include <sound/core.h>
2043550821SXiubo Li #include <sound/dmaengine_pcm.h>
2143550821SXiubo Li #include <sound/pcm_params.h>
224d245850SFabio Estevam #include <linux/mfd/syscon.h>
234d245850SFabio Estevam #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
2443550821SXiubo Li
2543550821SXiubo Li #include "fsl_sai.h"
267cb7f07dSShengjiu Wang #include "fsl_utils.h"
27c7540644SNicolin Chen #include "imx-pcm.h"
2843550821SXiubo Li
29e2681a1bSNicolin Chen #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
30e2681a1bSNicolin Chen FSL_SAI_CSR_FEIE)
31e2681a1bSNicolin Chen
32444c37aeSLars-Peter Clausen static const unsigned int fsl_sai_rates[] = {
33c5f4823bSZidan Wang 8000, 11025, 12000, 16000, 22050,
34c5f4823bSZidan Wang 24000, 32000, 44100, 48000, 64000,
350d11bab8SShengjiu Wang 88200, 96000, 176400, 192000, 352800,
360d11bab8SShengjiu Wang 384000, 705600, 768000, 1411200, 2822400,
37c5f4823bSZidan Wang };
38c5f4823bSZidan Wang
39444c37aeSLars-Peter Clausen static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
40c5f4823bSZidan Wang .count = ARRAY_SIZE(fsl_sai_rates),
41c5f4823bSZidan Wang .list = fsl_sai_rates,
42c5f4823bSZidan Wang };
43c5f4823bSZidan Wang
4494741ebaSShengjiu Wang /**
4594741ebaSShengjiu Wang * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream
4694741ebaSShengjiu Wang *
4794741ebaSShengjiu Wang * SAI supports synchronous mode using bit/frame clocks of either Transmitter's
4894741ebaSShengjiu Wang * or Receiver's for both streams. This function is used to check if clocks of
4994741ebaSShengjiu Wang * the stream's are synced by the opposite stream.
5094741ebaSShengjiu Wang *
5194741ebaSShengjiu Wang * @sai: SAI context
5294741ebaSShengjiu Wang * @dir: stream direction
5394741ebaSShengjiu Wang */
fsl_sai_dir_is_synced(struct fsl_sai * sai,int dir)5494741ebaSShengjiu Wang static inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir)
5594741ebaSShengjiu Wang {
5694741ebaSShengjiu Wang int adir = (dir == TX) ? RX : TX;
5794741ebaSShengjiu Wang
5894741ebaSShengjiu Wang /* current dir in async mode while opposite dir in sync mode */
5994741ebaSShengjiu Wang return !sai->synchronous[dir] && sai->synchronous[adir];
6094741ebaSShengjiu Wang }
6194741ebaSShengjiu Wang
fsl_sai_get_pins_state(struct fsl_sai * sai,u32 bclk)62b4ee8a91SShengjiu Wang static struct pinctrl_state *fsl_sai_get_pins_state(struct fsl_sai *sai, u32 bclk)
63b4ee8a91SShengjiu Wang {
64b17079d3SShengjiu Wang struct pinctrl_state *state = NULL;
65b4ee8a91SShengjiu Wang
66b4ee8a91SShengjiu Wang if (sai->is_pdm_mode) {
67b4ee8a91SShengjiu Wang /* DSD512@44.1kHz, DSD512@48kHz */
68b4ee8a91SShengjiu Wang if (bclk >= 22579200)
69b4ee8a91SShengjiu Wang state = pinctrl_lookup_state(sai->pinctrl, "dsd512");
70b4ee8a91SShengjiu Wang
71b4ee8a91SShengjiu Wang /* Get default DSD state */
72b4ee8a91SShengjiu Wang if (IS_ERR_OR_NULL(state))
73b4ee8a91SShengjiu Wang state = pinctrl_lookup_state(sai->pinctrl, "dsd");
74b4ee8a91SShengjiu Wang } else {
75b4ee8a91SShengjiu Wang /* 706k32b2c, 768k32b2c, etc */
76b4ee8a91SShengjiu Wang if (bclk >= 45158400)
77b4ee8a91SShengjiu Wang state = pinctrl_lookup_state(sai->pinctrl, "pcm_b2m");
78b4ee8a91SShengjiu Wang }
79b4ee8a91SShengjiu Wang
80b4ee8a91SShengjiu Wang /* Get default state */
81b4ee8a91SShengjiu Wang if (IS_ERR_OR_NULL(state))
82b4ee8a91SShengjiu Wang state = pinctrl_lookup_state(sai->pinctrl, "default");
83b4ee8a91SShengjiu Wang
84b4ee8a91SShengjiu Wang return state;
85b4ee8a91SShengjiu Wang }
86b4ee8a91SShengjiu Wang
fsl_sai_isr(int irq,void * devid)87e2681a1bSNicolin Chen static irqreturn_t fsl_sai_isr(int irq, void *devid)
88e2681a1bSNicolin Chen {
89e2681a1bSNicolin Chen struct fsl_sai *sai = (struct fsl_sai *)devid;
904f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
91e2681a1bSNicolin Chen struct device *dev = &sai->pdev->dev;
92413312aaSNicolin Chen u32 flags, xcsr, mask;
93cb00b4c1SSascha Hauer irqreturn_t iret = IRQ_NONE;
94e2681a1bSNicolin Chen
95413312aaSNicolin Chen /*
96413312aaSNicolin Chen * Both IRQ status bits and IRQ mask bits are in the xCSR but
97413312aaSNicolin Chen * different shifts. And we here create a mask only for those
98413312aaSNicolin Chen * IRQs that we activated.
99413312aaSNicolin Chen */
100e2681a1bSNicolin Chen mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
101e2681a1bSNicolin Chen
102e2681a1bSNicolin Chen /* Tx IRQ */
1034f7a0728SDaniel Baluta regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &xcsr);
104413312aaSNicolin Chen flags = xcsr & mask;
105e2681a1bSNicolin Chen
106413312aaSNicolin Chen if (flags)
107cb00b4c1SSascha Hauer iret = IRQ_HANDLED;
108413312aaSNicolin Chen else
109413312aaSNicolin Chen goto irq_rx;
110413312aaSNicolin Chen
111413312aaSNicolin Chen if (flags & FSL_SAI_CSR_WSF)
112e2681a1bSNicolin Chen dev_dbg(dev, "isr: Start of Tx word detected\n");
113e2681a1bSNicolin Chen
114413312aaSNicolin Chen if (flags & FSL_SAI_CSR_SEF)
115e412fcb0SShengjiu Wang dev_dbg(dev, "isr: Tx Frame sync error detected\n");
116e2681a1bSNicolin Chen
117cb225ac1SShengjiu Wang if (flags & FSL_SAI_CSR_FEF)
118e412fcb0SShengjiu Wang dev_dbg(dev, "isr: Transmit underrun detected\n");
119e2681a1bSNicolin Chen
120413312aaSNicolin Chen if (flags & FSL_SAI_CSR_FWF)
121e2681a1bSNicolin Chen dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
122e2681a1bSNicolin Chen
123413312aaSNicolin Chen if (flags & FSL_SAI_CSR_FRF)
124e2681a1bSNicolin Chen dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
125e2681a1bSNicolin Chen
126413312aaSNicolin Chen flags &= FSL_SAI_CSR_xF_W_MASK;
127413312aaSNicolin Chen xcsr &= ~FSL_SAI_CSR_xF_MASK;
128e2681a1bSNicolin Chen
129413312aaSNicolin Chen if (flags)
1304f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), flags | xcsr);
131413312aaSNicolin Chen
132413312aaSNicolin Chen irq_rx:
133e2681a1bSNicolin Chen /* Rx IRQ */
1344f7a0728SDaniel Baluta regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &xcsr);
135413312aaSNicolin Chen flags = xcsr & mask;
136e2681a1bSNicolin Chen
137413312aaSNicolin Chen if (flags)
138cb00b4c1SSascha Hauer iret = IRQ_HANDLED;
139413312aaSNicolin Chen else
140413312aaSNicolin Chen goto out;
141413312aaSNicolin Chen
142413312aaSNicolin Chen if (flags & FSL_SAI_CSR_WSF)
143e2681a1bSNicolin Chen dev_dbg(dev, "isr: Start of Rx word detected\n");
144e2681a1bSNicolin Chen
145413312aaSNicolin Chen if (flags & FSL_SAI_CSR_SEF)
146e412fcb0SShengjiu Wang dev_dbg(dev, "isr: Rx Frame sync error detected\n");
147e2681a1bSNicolin Chen
148cb225ac1SShengjiu Wang if (flags & FSL_SAI_CSR_FEF)
149e412fcb0SShengjiu Wang dev_dbg(dev, "isr: Receive overflow detected\n");
150e2681a1bSNicolin Chen
151413312aaSNicolin Chen if (flags & FSL_SAI_CSR_FWF)
152e2681a1bSNicolin Chen dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
153e2681a1bSNicolin Chen
154413312aaSNicolin Chen if (flags & FSL_SAI_CSR_FRF)
155e2681a1bSNicolin Chen dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
156e2681a1bSNicolin Chen
157413312aaSNicolin Chen flags &= FSL_SAI_CSR_xF_W_MASK;
158413312aaSNicolin Chen xcsr &= ~FSL_SAI_CSR_xF_MASK;
159e2681a1bSNicolin Chen
160413312aaSNicolin Chen if (flags)
1614f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr);
162413312aaSNicolin Chen
163413312aaSNicolin Chen out:
164cb00b4c1SSascha Hauer return iret;
165e2681a1bSNicolin Chen }
166e2681a1bSNicolin Chen
fsl_sai_set_dai_tdm_slot(struct snd_soc_dai * cpu_dai,u32 tx_mask,u32 rx_mask,int slots,int slot_width)167c1df2964SZidan Wang static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
168c1df2964SZidan Wang u32 rx_mask, int slots, int slot_width)
169c1df2964SZidan Wang {
170c1df2964SZidan Wang struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
171c1df2964SZidan Wang
172c1df2964SZidan Wang sai->slots = slots;
173c1df2964SZidan Wang sai->slot_width = slot_width;
174c1df2964SZidan Wang
175c1df2964SZidan Wang return 0;
176c1df2964SZidan Wang }
177c1df2964SZidan Wang
fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai * dai,unsigned int ratio)17863d1a348SViorel Suman static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai,
17963d1a348SViorel Suman unsigned int ratio)
18063d1a348SViorel Suman {
18163d1a348SViorel Suman struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
18263d1a348SViorel Suman
18363d1a348SViorel Suman sai->bclk_ratio = ratio;
18463d1a348SViorel Suman
18563d1a348SViorel Suman return 0;
18663d1a348SViorel Suman }
18763d1a348SViorel Suman
fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,bool tx)18843550821SXiubo Li static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
189bd393e2eSSascha Hauer int clk_id, unsigned int freq, bool tx)
19043550821SXiubo Li {
19143550821SXiubo Li struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
1924f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
1932a266f8bSNicolin Chen u32 val_cr2 = 0;
194633ff8f8SXiubo Li
19543550821SXiubo Li switch (clk_id) {
19643550821SXiubo Li case FSL_SAI_CLK_BUS:
19743550821SXiubo Li val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
19843550821SXiubo Li break;
19943550821SXiubo Li case FSL_SAI_CLK_MAST1:
20043550821SXiubo Li val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
20143550821SXiubo Li break;
20243550821SXiubo Li case FSL_SAI_CLK_MAST2:
20343550821SXiubo Li val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
20443550821SXiubo Li break;
20543550821SXiubo Li case FSL_SAI_CLK_MAST3:
20643550821SXiubo Li val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
20743550821SXiubo Li break;
20843550821SXiubo Li default:
20943550821SXiubo Li return -EINVAL;
21043550821SXiubo Li }
211633ff8f8SXiubo Li
2124f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
2132a266f8bSNicolin Chen FSL_SAI_CR2_MSEL_MASK, val_cr2);
21443550821SXiubo Li
21543550821SXiubo Li return 0;
21643550821SXiubo Li }
21743550821SXiubo Li
fsl_sai_set_mclk_rate(struct snd_soc_dai * dai,int clk_id,unsigned int freq)2187cb7f07dSShengjiu Wang static int fsl_sai_set_mclk_rate(struct snd_soc_dai *dai, int clk_id, unsigned int freq)
2197cb7f07dSShengjiu Wang {
2207cb7f07dSShengjiu Wang struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
2217cb7f07dSShengjiu Wang int ret;
2227cb7f07dSShengjiu Wang
2237cb7f07dSShengjiu Wang fsl_asoc_reparent_pll_clocks(dai->dev, sai->mclk_clk[clk_id],
2247cb7f07dSShengjiu Wang sai->pll8k_clk, sai->pll11k_clk, freq);
2257cb7f07dSShengjiu Wang
2267cb7f07dSShengjiu Wang ret = clk_set_rate(sai->mclk_clk[clk_id], freq);
2277cb7f07dSShengjiu Wang if (ret < 0)
2287cb7f07dSShengjiu Wang dev_err(dai->dev, "failed to set clock rate (%u): %d\n", freq, ret);
2297cb7f07dSShengjiu Wang
2307cb7f07dSShengjiu Wang return ret;
2317cb7f07dSShengjiu Wang }
2327cb7f07dSShengjiu Wang
fsl_sai_set_dai_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)23343550821SXiubo Li static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
23443550821SXiubo Li int clk_id, unsigned int freq, int dir)
23543550821SXiubo Li {
2367cb7f07dSShengjiu Wang struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
2374e3a99f5SNicolin Chen int ret;
23843550821SXiubo Li
23943550821SXiubo Li if (dir == SND_SOC_CLOCK_IN)
24043550821SXiubo Li return 0;
24143550821SXiubo Li
2427cb7f07dSShengjiu Wang if (freq > 0 && clk_id != FSL_SAI_CLK_BUS) {
2437cb7f07dSShengjiu Wang if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
2447cb7f07dSShengjiu Wang dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
2457cb7f07dSShengjiu Wang return -EINVAL;
2467cb7f07dSShengjiu Wang }
2477cb7f07dSShengjiu Wang
2487cb7f07dSShengjiu Wang if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
2497cb7f07dSShengjiu Wang dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
2507cb7f07dSShengjiu Wang return -EINVAL;
2517cb7f07dSShengjiu Wang }
2527cb7f07dSShengjiu Wang
2537cb7f07dSShengjiu Wang if (sai->mclk_streams == 0) {
2547cb7f07dSShengjiu Wang ret = fsl_sai_set_mclk_rate(cpu_dai, clk_id, freq);
2557cb7f07dSShengjiu Wang if (ret < 0)
2567cb7f07dSShengjiu Wang return ret;
2577cb7f07dSShengjiu Wang }
2587cb7f07dSShengjiu Wang }
2597cb7f07dSShengjiu Wang
260bd393e2eSSascha Hauer ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
26143550821SXiubo Li if (ret) {
262190af12dSNicolin Chen dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
26378957fc3SXiubo Li return ret;
26443550821SXiubo Li }
26543550821SXiubo Li
266bd393e2eSSascha Hauer ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
26778957fc3SXiubo Li if (ret)
268190af12dSNicolin Chen dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
26943550821SXiubo Li
2701fb2d9d7SNicolin Chen return ret;
27143550821SXiubo Li }
27243550821SXiubo Li
fsl_sai_set_dai_fmt_tr(struct snd_soc_dai * cpu_dai,unsigned int fmt,bool tx)27343550821SXiubo Li static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
274bd393e2eSSascha Hauer unsigned int fmt, bool tx)
27543550821SXiubo Li {
27643550821SXiubo Li struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
2774f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
2782a266f8bSNicolin Chen u32 val_cr2 = 0, val_cr4 = 0;
27943550821SXiubo Li
280eadb0019SXiubo Li if (!sai->is_lsb_first)
28172aa62beSXiubo Li val_cr4 |= FSL_SAI_CR4_MF;
28243550821SXiubo Li
283c111c2ddSShengjiu Wang sai->is_pdm_mode = false;
284a23924b7SShengjiu Wang sai->is_dsp_mode = false;
28513cde090SXiubo Li /* DAI mode */
28643550821SXiubo Li switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
28743550821SXiubo Li case SND_SOC_DAIFMT_I2S:
288a3f7dcc9SXiubo Li /*
289a3f7dcc9SXiubo Li * Frame low, 1clk before data, one word length for frame sync,
290a3f7dcc9SXiubo Li * frame sync starts one serial clock cycle earlier,
291a3f7dcc9SXiubo Li * that is, together with the last bit of the previous
292a3f7dcc9SXiubo Li * data word.
293a3f7dcc9SXiubo Li */
294ef33bc32SNicolin Chen val_cr2 |= FSL_SAI_CR2_BCP;
29513cde090SXiubo Li val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP;
29643550821SXiubo Li break;
29713cde090SXiubo Li case SND_SOC_DAIFMT_LEFT_J:
298a3f7dcc9SXiubo Li /*
299a3f7dcc9SXiubo Li * Frame high, one word length for frame sync,
300a3f7dcc9SXiubo Li * frame sync asserts with the first bit of the frame.
301a3f7dcc9SXiubo Li */
302ef33bc32SNicolin Chen val_cr2 |= FSL_SAI_CR2_BCP;
30313cde090SXiubo Li break;
304a3f7dcc9SXiubo Li case SND_SOC_DAIFMT_DSP_A:
305a3f7dcc9SXiubo Li /*
306a3f7dcc9SXiubo Li * Frame high, 1clk before data, one bit for frame sync,
307a3f7dcc9SXiubo Li * frame sync starts one serial clock cycle earlier,
308a3f7dcc9SXiubo Li * that is, together with the last bit of the previous
309a3f7dcc9SXiubo Li * data word.
310a3f7dcc9SXiubo Li */
311ef33bc32SNicolin Chen val_cr2 |= FSL_SAI_CR2_BCP;
312a3f7dcc9SXiubo Li val_cr4 |= FSL_SAI_CR4_FSE;
313a3f7dcc9SXiubo Li sai->is_dsp_mode = true;
314a3f7dcc9SXiubo Li break;
315a3f7dcc9SXiubo Li case SND_SOC_DAIFMT_DSP_B:
316a3f7dcc9SXiubo Li /*
317a3f7dcc9SXiubo Li * Frame high, one bit for frame sync,
318a3f7dcc9SXiubo Li * frame sync asserts with the first bit of the frame.
319a3f7dcc9SXiubo Li */
320ef33bc32SNicolin Chen val_cr2 |= FSL_SAI_CR2_BCP;
321a3f7dcc9SXiubo Li sai->is_dsp_mode = true;
322a3f7dcc9SXiubo Li break;
323c111c2ddSShengjiu Wang case SND_SOC_DAIFMT_PDM:
324c111c2ddSShengjiu Wang val_cr2 |= FSL_SAI_CR2_BCP;
325c111c2ddSShengjiu Wang val_cr4 &= ~FSL_SAI_CR4_MF;
326c111c2ddSShengjiu Wang sai->is_pdm_mode = true;
327c111c2ddSShengjiu Wang break;
32813cde090SXiubo Li case SND_SOC_DAIFMT_RIGHT_J:
32913cde090SXiubo Li /* To be done */
33043550821SXiubo Li default:
33143550821SXiubo Li return -EINVAL;
33243550821SXiubo Li }
33343550821SXiubo Li
33413cde090SXiubo Li /* DAI clock inversion */
33543550821SXiubo Li switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
33643550821SXiubo Li case SND_SOC_DAIFMT_IB_IF:
33713cde090SXiubo Li /* Invert both clocks */
33813cde090SXiubo Li val_cr2 ^= FSL_SAI_CR2_BCP;
33913cde090SXiubo Li val_cr4 ^= FSL_SAI_CR4_FSP;
34043550821SXiubo Li break;
34143550821SXiubo Li case SND_SOC_DAIFMT_IB_NF:
34213cde090SXiubo Li /* Invert bit clock */
34313cde090SXiubo Li val_cr2 ^= FSL_SAI_CR2_BCP;
34443550821SXiubo Li break;
34543550821SXiubo Li case SND_SOC_DAIFMT_NB_IF:
34613cde090SXiubo Li /* Invert frame clock */
34713cde090SXiubo Li val_cr4 ^= FSL_SAI_CR4_FSP;
34843550821SXiubo Li break;
34943550821SXiubo Li case SND_SOC_DAIFMT_NB_NF:
35013cde090SXiubo Li /* Nothing to do for both normal cases */
35143550821SXiubo Li break;
35243550821SXiubo Li default:
35343550821SXiubo Li return -EINVAL;
35443550821SXiubo Li }
35543550821SXiubo Li
356361284a4SMark Brown /* DAI clock provider masks */
357361284a4SMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
3583b14c15aSCharles Keepax case SND_SOC_DAIFMT_BP_FP:
35943550821SXiubo Li val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
36043550821SXiubo Li val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
361361284a4SMark Brown sai->is_consumer_mode = false;
36243550821SXiubo Li break;
3633b14c15aSCharles Keepax case SND_SOC_DAIFMT_BC_FC:
364361284a4SMark Brown sai->is_consumer_mode = true;
36543550821SXiubo Li break;
3663b14c15aSCharles Keepax case SND_SOC_DAIFMT_BP_FC:
36713cde090SXiubo Li val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
368361284a4SMark Brown sai->is_consumer_mode = false;
36913cde090SXiubo Li break;
3703b14c15aSCharles Keepax case SND_SOC_DAIFMT_BC_FP:
37113cde090SXiubo Li val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
372361284a4SMark Brown sai->is_consumer_mode = true;
37313cde090SXiubo Li break;
37443550821SXiubo Li default:
37543550821SXiubo Li return -EINVAL;
37643550821SXiubo Li }
37743550821SXiubo Li
3784f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
3792a266f8bSNicolin Chen FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
3804f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
3812a266f8bSNicolin Chen FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
3822a266f8bSNicolin Chen FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
38343550821SXiubo Li
38443550821SXiubo Li return 0;
38543550821SXiubo Li }
38643550821SXiubo Li
fsl_sai_set_dai_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)38743550821SXiubo Li static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
38843550821SXiubo Li {
3894e3a99f5SNicolin Chen int ret;
39043550821SXiubo Li
391bd393e2eSSascha Hauer ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
39243550821SXiubo Li if (ret) {
393190af12dSNicolin Chen dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
39478957fc3SXiubo Li return ret;
39543550821SXiubo Li }
39643550821SXiubo Li
397bd393e2eSSascha Hauer ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
39878957fc3SXiubo Li if (ret)
399190af12dSNicolin Chen dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
40043550821SXiubo Li
4011fb2d9d7SNicolin Chen return ret;
40243550821SXiubo Li }
40343550821SXiubo Li
fsl_sai_set_bclk(struct snd_soc_dai * dai,bool tx,u32 freq)404c3ecef21SZidan Wang static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
405c3ecef21SZidan Wang {
406c3ecef21SZidan Wang struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
407814c9fc4SAhmad Fatoum unsigned int reg, ofs = sai->soc_data->reg_offset;
408c3ecef21SZidan Wang unsigned long clk_rate;
409c56359f4SSascha Hauer u32 savediv = 0, ratio, bestdiff = freq;
4109355a7b1SShengjiu Wang int adir = tx ? RX : TX;
4119355a7b1SShengjiu Wang int dir = tx ? TX : RX;
412c3ecef21SZidan Wang u32 id;
413a50b7926SAhmad Fatoum bool support_1_1_ratio = sai->verid.version >= 0x0301;
414c3ecef21SZidan Wang
415361284a4SMark Brown /* Don't apply to consumer mode */
416361284a4SMark Brown if (sai->is_consumer_mode)
417c3ecef21SZidan Wang return 0;
418c3ecef21SZidan Wang
41953233e40SShengjiu Wang /*
42053233e40SShengjiu Wang * There is no point in polling MCLK0 if it is identical to MCLK1.
42153233e40SShengjiu Wang * And given that MQS use case has to use MCLK1 though two clocks
42253233e40SShengjiu Wang * are the same, we simply skip MCLK0 and start to find from MCLK1.
42353233e40SShengjiu Wang */
42453233e40SShengjiu Wang id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0;
42553233e40SShengjiu Wang
42653233e40SShengjiu Wang for (; id < FSL_SAI_MCLK_MAX; id++) {
427c56359f4SSascha Hauer int diff;
428c56359f4SSascha Hauer
429c3ecef21SZidan Wang clk_rate = clk_get_rate(sai->mclk_clk[id]);
430c3ecef21SZidan Wang if (!clk_rate)
431c3ecef21SZidan Wang continue;
432c3ecef21SZidan Wang
4331d4cbdf7SSascha Hauer ratio = DIV_ROUND_CLOSEST(clk_rate, freq);
434a50b7926SAhmad Fatoum if (!ratio || ratio > 512)
435a50b7926SAhmad Fatoum continue;
436a50b7926SAhmad Fatoum if (ratio == 1 && !support_1_1_ratio)
437a50b7926SAhmad Fatoum continue;
438d00887c1SAhmad Fatoum if ((ratio & 1) && ratio > 1)
4391d4cbdf7SSascha Hauer continue;
440c3ecef21SZidan Wang
4411d4cbdf7SSascha Hauer diff = abs((long)clk_rate - ratio * freq);
442c3ecef21SZidan Wang
443c3ecef21SZidan Wang /*
444c3ecef21SZidan Wang * Drop the source that can not be
445c3ecef21SZidan Wang * divided into the required rate.
446c3ecef21SZidan Wang */
447c56359f4SSascha Hauer if (diff != 0 && clk_rate / diff < 1000)
448c3ecef21SZidan Wang continue;
449c3ecef21SZidan Wang
450c3ecef21SZidan Wang dev_dbg(dai->dev,
451c3ecef21SZidan Wang "ratio %d for freq %dHz based on clock %ldHz\n",
452c3ecef21SZidan Wang ratio, freq, clk_rate);
453c3ecef21SZidan Wang
454c3ecef21SZidan Wang
455c56359f4SSascha Hauer if (diff < bestdiff) {
456c3ecef21SZidan Wang savediv = ratio;
457c3ecef21SZidan Wang sai->mclk_id[tx] = id;
458c56359f4SSascha Hauer bestdiff = diff;
459c3ecef21SZidan Wang }
460c3ecef21SZidan Wang
461c56359f4SSascha Hauer if (diff == 0)
462c3ecef21SZidan Wang break;
463c3ecef21SZidan Wang }
464c3ecef21SZidan Wang
465c3ecef21SZidan Wang if (savediv == 0) {
466c3ecef21SZidan Wang dev_err(dai->dev, "failed to derive required %cx rate: %d\n",
467c3ecef21SZidan Wang tx ? 'T' : 'R', freq);
468c3ecef21SZidan Wang return -EINVAL;
469c3ecef21SZidan Wang }
470c3ecef21SZidan Wang
471814c9fc4SAhmad Fatoum dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
472c56359f4SSascha Hauer sai->mclk_id[tx], savediv, bestdiff);
473814c9fc4SAhmad Fatoum
4749cc58712SZidan Wang /*
4759cc58712SZidan Wang * 1) For Asynchronous mode, we must set RCR2 register for capture, and
4769cc58712SZidan Wang * set TCR2 register for playback.
4779cc58712SZidan Wang * 2) For Tx sync with Rx clock, we must set RCR2 register for playback
4789cc58712SZidan Wang * and capture.
4799cc58712SZidan Wang * 3) For Rx sync with Tx clock, we must set TCR2 register for playback
4809cc58712SZidan Wang * and capture.
4819cc58712SZidan Wang * 4) For Tx and Rx are both Synchronous with another SAI, we just
4829cc58712SZidan Wang * ignore it.
4839cc58712SZidan Wang */
484814c9fc4SAhmad Fatoum if (fsl_sai_dir_is_synced(sai, adir))
485814c9fc4SAhmad Fatoum reg = FSL_SAI_xCR2(!tx, ofs);
486814c9fc4SAhmad Fatoum else if (!sai->synchronous[dir])
487814c9fc4SAhmad Fatoum reg = FSL_SAI_xCR2(tx, ofs);
488814c9fc4SAhmad Fatoum else
489814c9fc4SAhmad Fatoum return 0;
490c3ecef21SZidan Wang
491814c9fc4SAhmad Fatoum regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
492814c9fc4SAhmad Fatoum FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
493a50b7926SAhmad Fatoum
49432cf0046SChancel Liu if (savediv == 1) {
495a50b7926SAhmad Fatoum regmap_update_bits(sai->regmap, reg,
496a50b7926SAhmad Fatoum FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
497a50b7926SAhmad Fatoum FSL_SAI_CR2_BYP);
49832cf0046SChancel Liu if (fsl_sai_dir_is_synced(sai, adir))
49932cf0046SChancel Liu regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
50032cf0046SChancel Liu FSL_SAI_CR2_BCI, FSL_SAI_CR2_BCI);
501a50b7926SAhmad Fatoum else
50232cf0046SChancel Liu regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
50332cf0046SChancel Liu FSL_SAI_CR2_BCI, 0);
50432cf0046SChancel Liu } else {
505a50b7926SAhmad Fatoum regmap_update_bits(sai->regmap, reg,
506a50b7926SAhmad Fatoum FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP,
507a50b7926SAhmad Fatoum savediv / 2 - 1);
50832cf0046SChancel Liu }
509c3ecef21SZidan Wang
510c3ecef21SZidan Wang return 0;
511c3ecef21SZidan Wang }
512c3ecef21SZidan Wang
fsl_sai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * cpu_dai)51343550821SXiubo Li static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
51443550821SXiubo Li struct snd_pcm_hw_params *params,
51543550821SXiubo Li struct snd_soc_dai *cpu_dai)
51643550821SXiubo Li {
5174e3a99f5SNicolin Chen struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
5184f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
5192a266f8bSNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
52043550821SXiubo Li unsigned int channels = params_channels(params);
521e3f4e5b1SShengjiu Wang struct snd_dmaengine_dai_dma_data *dma_params;
522e3f4e5b1SShengjiu Wang struct fsl_sai_dl_cfg *dl_cfg = sai->dl_cfg;
5234ca73043SZidan Wang u32 word_width = params_width(params);
524e3f4e5b1SShengjiu Wang int trce_mask = 0, dl_cfg_idx = 0;
525e3f4e5b1SShengjiu Wang int dl_cfg_cnt = sai->dl_cfg_cnt;
526e3f4e5b1SShengjiu Wang u32 dl_type = FSL_SAI_DL_I2S;
5272a266f8bSNicolin Chen u32 val_cr4 = 0, val_cr5 = 0;
528c1df2964SZidan Wang u32 slots = (channels == 1) ? 2 : channels;
529c1df2964SZidan Wang u32 slot_width = word_width;
5309355a7b1SShengjiu Wang int adir = tx ? RX : TX;
531b4ee8a91SShengjiu Wang u32 pins, bclk;
53288630575SShengjiu Wang u32 watermark;
533e3f4e5b1SShengjiu Wang int ret, i;
534c3ecef21SZidan Wang
535c1df2964SZidan Wang if (sai->slot_width)
536c1df2964SZidan Wang slot_width = sai->slot_width;
537c1df2964SZidan Wang
538837b4029SShengjiu Wang if (sai->slots)
539837b4029SShengjiu Wang slots = sai->slots;
540837b4029SShengjiu Wang else if (sai->bclk_ratio)
541837b4029SShengjiu Wang slots = sai->bclk_ratio / slot_width;
542837b4029SShengjiu Wang
543770f58d7SShengjiu Wang pins = DIV_ROUND_UP(channels, slots);
544770f58d7SShengjiu Wang
545c111c2ddSShengjiu Wang /*
546c111c2ddSShengjiu Wang * PDM mode, channels are independent
547c111c2ddSShengjiu Wang * each channels are on one dataline/FIFO.
548c111c2ddSShengjiu Wang */
549e3f4e5b1SShengjiu Wang if (sai->is_pdm_mode) {
550c111c2ddSShengjiu Wang pins = channels;
551e3f4e5b1SShengjiu Wang dl_type = FSL_SAI_DL_PDM;
552e3f4e5b1SShengjiu Wang }
553e3f4e5b1SShengjiu Wang
554e3f4e5b1SShengjiu Wang for (i = 0; i < dl_cfg_cnt; i++) {
555e3f4e5b1SShengjiu Wang if (dl_cfg[i].type == dl_type && dl_cfg[i].pins[tx] == pins) {
556e3f4e5b1SShengjiu Wang dl_cfg_idx = i;
557e3f4e5b1SShengjiu Wang break;
558e3f4e5b1SShengjiu Wang }
559e3f4e5b1SShengjiu Wang }
560e3f4e5b1SShengjiu Wang
561e3f4e5b1SShengjiu Wang if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) < pins) {
562e3f4e5b1SShengjiu Wang dev_err(cpu_dai->dev, "channel not supported\n");
563e3f4e5b1SShengjiu Wang return -EINVAL;
564e3f4e5b1SShengjiu Wang }
565c111c2ddSShengjiu Wang
566b4ee8a91SShengjiu Wang bclk = params_rate(params) * (sai->bclk_ratio ? sai->bclk_ratio : slots * slot_width);
567b4ee8a91SShengjiu Wang
568b4ee8a91SShengjiu Wang if (!IS_ERR_OR_NULL(sai->pinctrl)) {
569b4ee8a91SShengjiu Wang sai->pins_state = fsl_sai_get_pins_state(sai, bclk);
570b4ee8a91SShengjiu Wang if (!IS_ERR_OR_NULL(sai->pins_state)) {
571b4ee8a91SShengjiu Wang ret = pinctrl_select_state(sai->pinctrl, sai->pins_state);
572b4ee8a91SShengjiu Wang if (ret) {
573b4ee8a91SShengjiu Wang dev_err(cpu_dai->dev, "failed to set proper pins state: %d\n", ret);
574b4ee8a91SShengjiu Wang return ret;
575b4ee8a91SShengjiu Wang }
576b4ee8a91SShengjiu Wang }
577b4ee8a91SShengjiu Wang }
578b4ee8a91SShengjiu Wang
579361284a4SMark Brown if (!sai->is_consumer_mode) {
580b4ee8a91SShengjiu Wang ret = fsl_sai_set_bclk(cpu_dai, tx, bclk);
581c3ecef21SZidan Wang if (ret)
582c3ecef21SZidan Wang return ret;
583c3ecef21SZidan Wang
584c3ecef21SZidan Wang /* Do not enable the clock if it is already enabled */
585c3ecef21SZidan Wang if (!(sai->mclk_streams & BIT(substream->stream))) {
586c3ecef21SZidan Wang ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]);
587c3ecef21SZidan Wang if (ret)
588c3ecef21SZidan Wang return ret;
589c3ecef21SZidan Wang
590c3ecef21SZidan Wang sai->mclk_streams |= BIT(substream->stream);
591c3ecef21SZidan Wang }
592c3ecef21SZidan Wang }
59343550821SXiubo Li
594c111c2ddSShengjiu Wang if (!sai->is_dsp_mode && !sai->is_pdm_mode)
595c1df2964SZidan Wang val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
596a3f7dcc9SXiubo Li
597c1df2964SZidan Wang val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
598c1df2964SZidan Wang val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
59943550821SXiubo Li
600c111c2ddSShengjiu Wang if (sai->is_lsb_first || sai->is_pdm_mode)
60143550821SXiubo Li val_cr5 |= FSL_SAI_CR5_FBT(0);
60272aa62beSXiubo Li else
60372aa62beSXiubo Li val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
60443550821SXiubo Li
605c1df2964SZidan Wang val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
60643550821SXiubo Li
607*85e70dcdSShengjiu Wang /* Set to avoid channel swap */
608*85e70dcdSShengjiu Wang val_cr4 |= FSL_SAI_CR4_FCONT;
609*85e70dcdSShengjiu Wang
610f4c4b1bbSShengjiu Wang /* Set to output mode to avoid tri-stated data pins */
611f4c4b1bbSShengjiu Wang if (tx)
612f4c4b1bbSShengjiu Wang val_cr4 |= FSL_SAI_CR4_CHMOD;
613f4c4b1bbSShengjiu Wang
61451659ca0SZidan Wang /*
615361284a4SMark Brown * For SAI provider mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
61651659ca0SZidan Wang * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
6177b3bee09SShengjiu Wang * RCR5(TCR5) for playback(capture), or there will be sync error.
61851659ca0SZidan Wang */
61951659ca0SZidan Wang
620361284a4SMark Brown if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) {
6219355a7b1SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
622f4c4b1bbSShengjiu Wang FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
623f4c4b1bbSShengjiu Wang FSL_SAI_CR4_CHMOD_MASK,
62451659ca0SZidan Wang val_cr4);
6259355a7b1SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs),
62651659ca0SZidan Wang FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
62751659ca0SZidan Wang FSL_SAI_CR5_FBT_MASK, val_cr5);
62851659ca0SZidan Wang }
62943550821SXiubo Li
63088630575SShengjiu Wang /*
63188630575SShengjiu Wang * Combine mode has limation:
63288630575SShengjiu Wang * - Can't used for singel dataline/FIFO case except the FIFO0
63388630575SShengjiu Wang * - Can't used for multi dataline/FIFO case except the enabled FIFOs
63488630575SShengjiu Wang * are successive and start from FIFO0
63588630575SShengjiu Wang *
63688630575SShengjiu Wang * So for common usage, all multi fifo case disable the combine mode.
63788630575SShengjiu Wang */
63888630575SShengjiu Wang if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) <= 1 || sai->is_multi_fifo_dma)
639e3f4e5b1SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
640e3f4e5b1SShengjiu Wang FSL_SAI_CR4_FCOMB_MASK, 0);
641e3f4e5b1SShengjiu Wang else
642eba0f007SSascha Hauer regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
643eba0f007SSascha Hauer FSL_SAI_CR4_FCOMB_MASK, FSL_SAI_CR4_FCOMB_SOFT);
644eba0f007SSascha Hauer
645e3f4e5b1SShengjiu Wang dma_params = tx ? &sai->dma_params_tx : &sai->dma_params_rx;
646e3f4e5b1SShengjiu Wang dma_params->addr = sai->res->start + FSL_SAI_xDR0(tx) +
647e3f4e5b1SShengjiu Wang dl_cfg[dl_cfg_idx].start_off[tx] * 0x4;
648e3f4e5b1SShengjiu Wang
64988630575SShengjiu Wang if (sai->is_multi_fifo_dma) {
65088630575SShengjiu Wang sai->audio_config[tx].words_per_fifo = min(slots, channels);
65188630575SShengjiu Wang if (tx) {
65288630575SShengjiu Wang sai->audio_config[tx].n_fifos_dst = pins;
65388630575SShengjiu Wang sai->audio_config[tx].stride_fifos_dst = dl_cfg[dl_cfg_idx].next_off[tx];
65488630575SShengjiu Wang } else {
65588630575SShengjiu Wang sai->audio_config[tx].n_fifos_src = pins;
65688630575SShengjiu Wang sai->audio_config[tx].stride_fifos_src = dl_cfg[dl_cfg_idx].next_off[tx];
65788630575SShengjiu Wang }
65888630575SShengjiu Wang dma_params->maxburst = sai->audio_config[tx].words_per_fifo * pins;
65988630575SShengjiu Wang dma_params->peripheral_config = &sai->audio_config[tx];
66088630575SShengjiu Wang dma_params->peripheral_size = sizeof(sai->audio_config[tx]);
66188630575SShengjiu Wang
66288630575SShengjiu Wang watermark = tx ? (sai->soc_data->fifo_depth - dma_params->maxburst) :
66388630575SShengjiu Wang (dma_params->maxburst - 1);
66488630575SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR1(tx, ofs),
66588630575SShengjiu Wang FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
66688630575SShengjiu Wang watermark);
66788630575SShengjiu Wang }
66888630575SShengjiu Wang
669e3f4e5b1SShengjiu Wang /* Find a proper tcre setting */
670e3f4e5b1SShengjiu Wang for (i = 0; i < sai->soc_data->pins; i++) {
671e3f4e5b1SShengjiu Wang trce_mask = (1 << (i + 1)) - 1;
672e3f4e5b1SShengjiu Wang if (hweight8(dl_cfg[dl_cfg_idx].mask[tx] & trce_mask) == pins)
673e3f4e5b1SShengjiu Wang break;
674e3f4e5b1SShengjiu Wang }
675e3f4e5b1SShengjiu Wang
676770f58d7SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
677770f58d7SShengjiu Wang FSL_SAI_CR3_TRCE_MASK,
678e3f4e5b1SShengjiu Wang FSL_SAI_CR3_TRCE((dl_cfg[dl_cfg_idx].mask[tx] & trce_mask)));
679e3f4e5b1SShengjiu Wang
680e1e4a5cbSShengjiu Wang /*
681e1e4a5cbSShengjiu Wang * When the TERE and FSD_MSTR enabled before configuring the word width
682e1e4a5cbSShengjiu Wang * There will be no frame sync clock issue, because word width impact
683e1e4a5cbSShengjiu Wang * the generation of frame sync clock.
684e1e4a5cbSShengjiu Wang *
685e1e4a5cbSShengjiu Wang * TERE enabled earlier only for i.MX8MP case for the hardware limitation,
686e1e4a5cbSShengjiu Wang * We need to disable FSD_MSTR before configuring word width, then enable
687e1e4a5cbSShengjiu Wang * FSD_MSTR bit for this specific case.
688e1e4a5cbSShengjiu Wang */
689e1e4a5cbSShengjiu Wang if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
690e1e4a5cbSShengjiu Wang !sai->is_consumer_mode)
691e1e4a5cbSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
692e1e4a5cbSShengjiu Wang FSL_SAI_CR4_FSD_MSTR, 0);
693e1e4a5cbSShengjiu Wang
6944f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
695f4c4b1bbSShengjiu Wang FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
696*85e70dcdSShengjiu Wang FSL_SAI_CR4_CHMOD_MASK | FSL_SAI_CR4_FCONT_MASK,
6972a266f8bSNicolin Chen val_cr4);
6984f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs),
6992a266f8bSNicolin Chen FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
7002a266f8bSNicolin Chen FSL_SAI_CR5_FBT_MASK, val_cr5);
701e1e4a5cbSShengjiu Wang
702e1e4a5cbSShengjiu Wang /* Enable FSD_MSTR after configuring word width */
703e1e4a5cbSShengjiu Wang if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
704e1e4a5cbSShengjiu Wang !sai->is_consumer_mode)
705e1e4a5cbSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
706e1e4a5cbSShengjiu Wang FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR);
707e1e4a5cbSShengjiu Wang
708770f58d7SShengjiu Wang regmap_write(sai->regmap, FSL_SAI_xMR(tx),
709770f58d7SShengjiu Wang ~0UL - ((1 << min(channels, slots)) - 1));
71043550821SXiubo Li
71143550821SXiubo Li return 0;
71243550821SXiubo Li }
71343550821SXiubo Li
fsl_sai_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)714c3ecef21SZidan Wang static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
715c3ecef21SZidan Wang struct snd_soc_dai *cpu_dai)
716c3ecef21SZidan Wang {
717c3ecef21SZidan Wang struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
718c3ecef21SZidan Wang bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
719770f58d7SShengjiu Wang unsigned int ofs = sai->soc_data->reg_offset;
720770f58d7SShengjiu Wang
721fb0f25c8SShengjiu Wang /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
722fb0f25c8SShengjiu Wang regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
723fb0f25c8SShengjiu Wang
724770f58d7SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
725770f58d7SShengjiu Wang FSL_SAI_CR3_TRCE_MASK, 0);
726c3ecef21SZidan Wang
727361284a4SMark Brown if (!sai->is_consumer_mode &&
728c3ecef21SZidan Wang sai->mclk_streams & BIT(substream->stream)) {
729c3ecef21SZidan Wang clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
730c3ecef21SZidan Wang sai->mclk_streams &= ~BIT(substream->stream);
731c3ecef21SZidan Wang }
732c3ecef21SZidan Wang
733c3ecef21SZidan Wang return 0;
734c3ecef21SZidan Wang }
735c3ecef21SZidan Wang
fsl_sai_config_disable(struct fsl_sai * sai,int dir)73694741ebaSShengjiu Wang static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
73794741ebaSShengjiu Wang {
73894741ebaSShengjiu Wang unsigned int ofs = sai->soc_data->reg_offset;
73994741ebaSShengjiu Wang bool tx = dir == TX;
740197c53c8SShengjiu Wang u32 xcsr, count = 100, mask;
741197c53c8SShengjiu Wang
742197c53c8SShengjiu Wang if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output)
743197c53c8SShengjiu Wang mask = FSL_SAI_CSR_TERE;
744197c53c8SShengjiu Wang else
745197c53c8SShengjiu Wang mask = FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE;
74694741ebaSShengjiu Wang
74794741ebaSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
748197c53c8SShengjiu Wang mask, 0);
74994741ebaSShengjiu Wang
75094741ebaSShengjiu Wang /* TERE will remain set till the end of current frame */
75194741ebaSShengjiu Wang do {
75294741ebaSShengjiu Wang udelay(10);
75394741ebaSShengjiu Wang regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
75494741ebaSShengjiu Wang } while (--count && xcsr & FSL_SAI_CSR_TERE);
75594741ebaSShengjiu Wang
75694741ebaSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
75794741ebaSShengjiu Wang FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
75894741ebaSShengjiu Wang
75994741ebaSShengjiu Wang /*
76094741ebaSShengjiu Wang * For sai master mode, after several open/close sai,
76194741ebaSShengjiu Wang * there will be no frame clock, and can't recover
76294741ebaSShengjiu Wang * anymore. Add software reset to fix this issue.
76394741ebaSShengjiu Wang * This is a hardware bug, and will be fix in the
76494741ebaSShengjiu Wang * next sai version.
76594741ebaSShengjiu Wang */
766361284a4SMark Brown if (!sai->is_consumer_mode) {
76794741ebaSShengjiu Wang /* Software Reset */
76894741ebaSShengjiu Wang regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
76994741ebaSShengjiu Wang /* Clear SR bit to finish the reset */
77094741ebaSShengjiu Wang regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0);
77194741ebaSShengjiu Wang }
77294741ebaSShengjiu Wang }
773c3ecef21SZidan Wang
fsl_sai_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * cpu_dai)77443550821SXiubo Li static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
77543550821SXiubo Li struct snd_soc_dai *cpu_dai)
77643550821SXiubo Li {
77743550821SXiubo Li struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
7784f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
7794f7a0728SDaniel Baluta
780e6b39846SNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
78194741ebaSShengjiu Wang int adir = tx ? RX : TX;
78294741ebaSShengjiu Wang int dir = tx ? TX : RX;
78394741ebaSShengjiu Wang u32 xcsr;
784496a39d9SXiubo Li
785a3f7dcc9SXiubo Li /*
78608fdf65eSNicolin Chen * Asynchronous mode: Clear SYNC for both Tx and Rx.
78708fdf65eSNicolin Chen * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
78808fdf65eSNicolin Chen * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
789a3f7dcc9SXiubo Li */
7904f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), FSL_SAI_CR2_SYNC,
7913cc7780bSStefan Agner sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
7924f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), FSL_SAI_CR2_SYNC,
79308fdf65eSNicolin Chen sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
794496a39d9SXiubo Li
795a3f7dcc9SXiubo Li /*
796a3f7dcc9SXiubo Li * It is recommended that the transmitter is the last enabled
797a3f7dcc9SXiubo Li * and the first disabled.
798a3f7dcc9SXiubo Li */
79943550821SXiubo Li switch (cmd) {
80043550821SXiubo Li case SNDRV_PCM_TRIGGER_START:
80143550821SXiubo Li case SNDRV_PCM_TRIGGER_RESUME:
80243550821SXiubo Li case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
8034f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
804a3fdc674SNicolin Chen FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
805a3fdc674SNicolin Chen
80694741ebaSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
807e6b39846SNicolin Chen FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
80894741ebaSShengjiu Wang /*
80994741ebaSShengjiu Wang * Enable the opposite direction for synchronous mode
81094741ebaSShengjiu Wang * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx
81194741ebaSShengjiu Wang * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx
81294741ebaSShengjiu Wang *
81394741ebaSShengjiu Wang * RM recommends to enable RE after TE for case 1 and to enable
81494741ebaSShengjiu Wang * TE after RE for case 2, but we here may not always guarantee
81594741ebaSShengjiu Wang * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables
81694741ebaSShengjiu Wang * TE after RE, which is against what RM recommends but should
81794741ebaSShengjiu Wang * be safe to do, judging by years of testing results.
81894741ebaSShengjiu Wang */
81994741ebaSShengjiu Wang if (fsl_sai_dir_is_synced(sai, adir))
82094741ebaSShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
821e6b39846SNicolin Chen FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
822e5d0fa9cSXiubo Li
8234f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
8248abba5d6SNicolin Chen FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
82543550821SXiubo Li break;
82643550821SXiubo Li case SNDRV_PCM_TRIGGER_STOP:
82743550821SXiubo Li case SNDRV_PCM_TRIGGER_SUSPEND:
82843550821SXiubo Li case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
8294f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
830e6b39846SNicolin Chen FSL_SAI_CSR_FRDE, 0);
8314f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
8328abba5d6SNicolin Chen FSL_SAI_CSR_xIE_MASK, 0);
833e5d0fa9cSXiubo Li
834f84526cfSNicolin Chen /* Check if the opposite FRDE is also disabled */
8354f7a0728SDaniel Baluta regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr);
8363e3f8bd5SZidan Wang
8373e3f8bd5SZidan Wang /*
83894741ebaSShengjiu Wang * If opposite stream provides clocks for synchronous mode and
83994741ebaSShengjiu Wang * it is inactive, disable it before disabling the current one
8403e3f8bd5SZidan Wang */
84194741ebaSShengjiu Wang if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE))
84294741ebaSShengjiu Wang fsl_sai_config_disable(sai, adir);
84394741ebaSShengjiu Wang
84494741ebaSShengjiu Wang /*
84594741ebaSShengjiu Wang * Disable current stream if either of:
84694741ebaSShengjiu Wang * 1. current stream doesn't provide clocks for synchronous mode
84794741ebaSShengjiu Wang * 2. current stream provides clocks for synchronous mode but no
84894741ebaSShengjiu Wang * more stream is active.
84994741ebaSShengjiu Wang */
85094741ebaSShengjiu Wang if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE))
85194741ebaSShengjiu Wang fsl_sai_config_disable(sai, dir);
85294741ebaSShengjiu Wang
85343550821SXiubo Li break;
85443550821SXiubo Li default:
85543550821SXiubo Li return -EINVAL;
85643550821SXiubo Li }
85743550821SXiubo Li
85843550821SXiubo Li return 0;
85943550821SXiubo Li }
86043550821SXiubo Li
fsl_sai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)86143550821SXiubo Li static int fsl_sai_startup(struct snd_pcm_substream *substream,
86243550821SXiubo Li struct snd_soc_dai *cpu_dai)
86343550821SXiubo Li {
86443550821SXiubo Li struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
8652a266f8bSNicolin Chen bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
866ca3e35c7SNicolin Chen int ret;
867ca3e35c7SNicolin Chen
868e75f4940SMihai Serban /*
869e75f4940SMihai Serban * EDMA controller needs period size to be a multiple of
870e75f4940SMihai Serban * tx/rx maxburst
871e75f4940SMihai Serban */
872e75f4940SMihai Serban if (sai->soc_data->use_edma)
873e75f4940SMihai Serban snd_pcm_hw_constraint_step(substream->runtime, 0,
874e75f4940SMihai Serban SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
875e75f4940SMihai Serban tx ? sai->dma_params_tx.maxburst :
876e75f4940SMihai Serban sai->dma_params_rx.maxburst);
877e75f4940SMihai Serban
878c5f4823bSZidan Wang ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
879c5f4823bSZidan Wang SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
880c5f4823bSZidan Wang
881c5f4823bSZidan Wang return ret;
88243550821SXiubo Li }
88343550821SXiubo Li
fsl_sai_dai_probe(struct snd_soc_dai * cpu_dai)88443550821SXiubo Li static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
88543550821SXiubo Li {
88643550821SXiubo Li struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
8874f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
888e6dc12d7SXiubo Li
889376d1a92SNicolin Chen /* Software Reset for both Tx and Rx */
8904f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR);
8914f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR);
892376d1a92SNicolin Chen /* Clear SR bit to finish the reset */
8934f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
8944f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
895376d1a92SNicolin Chen
8964f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs),
8975aef1ff2SShengjiu Wang FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
898870b89d1SChancel Liu sai->soc_data->fifo_depth - sai->dma_params_tx.maxburst);
8994f7a0728SDaniel Baluta regmap_update_bits(sai->regmap, FSL_SAI_RCR1(ofs),
9005aef1ff2SShengjiu Wang FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
901870b89d1SChancel Liu sai->dma_params_rx.maxburst - 1);
90243550821SXiubo Li
903dd9f4060SXiubo Li snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
904dd9f4060SXiubo Li &sai->dma_params_rx);
90543550821SXiubo Li
90643550821SXiubo Li return 0;
90743550821SXiubo Li }
90843550821SXiubo Li
909ac27ca16SKuninori Morimoto static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
910ac27ca16SKuninori Morimoto .probe = fsl_sai_dai_probe,
911ac27ca16SKuninori Morimoto .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
912ac27ca16SKuninori Morimoto .set_sysclk = fsl_sai_set_dai_sysclk,
913ac27ca16SKuninori Morimoto .set_fmt = fsl_sai_set_dai_fmt,
914ac27ca16SKuninori Morimoto .set_tdm_slot = fsl_sai_set_dai_tdm_slot,
915ac27ca16SKuninori Morimoto .hw_params = fsl_sai_hw_params,
916ac27ca16SKuninori Morimoto .hw_free = fsl_sai_hw_free,
917ac27ca16SKuninori Morimoto .trigger = fsl_sai_trigger,
918ac27ca16SKuninori Morimoto .startup = fsl_sai_startup,
919ac27ca16SKuninori Morimoto };
920ac27ca16SKuninori Morimoto
fsl_sai_dai_resume(struct snd_soc_component * component)921b4ee8a91SShengjiu Wang static int fsl_sai_dai_resume(struct snd_soc_component *component)
922b4ee8a91SShengjiu Wang {
923b4ee8a91SShengjiu Wang struct fsl_sai *sai = snd_soc_component_get_drvdata(component);
924b4ee8a91SShengjiu Wang struct device *dev = &sai->pdev->dev;
925b4ee8a91SShengjiu Wang int ret;
926b4ee8a91SShengjiu Wang
927b4ee8a91SShengjiu Wang if (!IS_ERR_OR_NULL(sai->pinctrl) && !IS_ERR_OR_NULL(sai->pins_state)) {
928b4ee8a91SShengjiu Wang ret = pinctrl_select_state(sai->pinctrl, sai->pins_state);
929b4ee8a91SShengjiu Wang if (ret) {
930b4ee8a91SShengjiu Wang dev_err(dev, "failed to set proper pins state: %d\n", ret);
931b4ee8a91SShengjiu Wang return ret;
932b4ee8a91SShengjiu Wang }
933b4ee8a91SShengjiu Wang }
934b4ee8a91SShengjiu Wang
935b4ee8a91SShengjiu Wang return 0;
936b4ee8a91SShengjiu Wang }
937b4ee8a91SShengjiu Wang
93822a16145SShengjiu Wang static struct snd_soc_dai_driver fsl_sai_dai_template = {
93943550821SXiubo Li .playback = {
94020d5b76fSNicolin Chen .stream_name = "CPU-Playback",
94143550821SXiubo Li .channels_min = 1,
9424957b556SAlexandre Belloni .channels_max = 32,
943c5f4823bSZidan Wang .rate_min = 8000,
9440d11bab8SShengjiu Wang .rate_max = 2822400,
945c5f4823bSZidan Wang .rates = SNDRV_PCM_RATE_KNOT,
94643550821SXiubo Li .formats = FSL_SAI_FORMATS,
94743550821SXiubo Li },
94843550821SXiubo Li .capture = {
94920d5b76fSNicolin Chen .stream_name = "CPU-Capture",
95043550821SXiubo Li .channels_min = 1,
9514957b556SAlexandre Belloni .channels_max = 32,
952c5f4823bSZidan Wang .rate_min = 8000,
9530d11bab8SShengjiu Wang .rate_max = 2822400,
954c5f4823bSZidan Wang .rates = SNDRV_PCM_RATE_KNOT,
95543550821SXiubo Li .formats = FSL_SAI_FORMATS,
95643550821SXiubo Li },
95743550821SXiubo Li .ops = &fsl_sai_pcm_dai_ops,
95843550821SXiubo Li };
95943550821SXiubo Li
96043550821SXiubo Li static const struct snd_soc_component_driver fsl_component = {
96143550821SXiubo Li .name = "fsl-sai",
962b4ee8a91SShengjiu Wang .resume = fsl_sai_dai_resume,
9631e63fcc7SCharles Keepax .legacy_dai_naming = 1,
96443550821SXiubo Li };
96543550821SXiubo Li
9664f7a0728SDaniel Baluta static struct reg_default fsl_sai_reg_defaults_ofs0[] = {
9674f7a0728SDaniel Baluta {FSL_SAI_TCR1(0), 0},
9684f7a0728SDaniel Baluta {FSL_SAI_TCR2(0), 0},
9694f7a0728SDaniel Baluta {FSL_SAI_TCR3(0), 0},
9704f7a0728SDaniel Baluta {FSL_SAI_TCR4(0), 0},
9714f7a0728SDaniel Baluta {FSL_SAI_TCR5(0), 0},
9725f0ac20eSDaniel Baluta {FSL_SAI_TDR0, 0},
9735f0ac20eSDaniel Baluta {FSL_SAI_TDR1, 0},
9745f0ac20eSDaniel Baluta {FSL_SAI_TDR2, 0},
9755f0ac20eSDaniel Baluta {FSL_SAI_TDR3, 0},
9765f0ac20eSDaniel Baluta {FSL_SAI_TDR4, 0},
9775f0ac20eSDaniel Baluta {FSL_SAI_TDR5, 0},
9785f0ac20eSDaniel Baluta {FSL_SAI_TDR6, 0},
9795f0ac20eSDaniel Baluta {FSL_SAI_TDR7, 0},
9803f6f5b0cSZidan Wang {FSL_SAI_TMR, 0},
9814f7a0728SDaniel Baluta {FSL_SAI_RCR1(0), 0},
9824f7a0728SDaniel Baluta {FSL_SAI_RCR2(0), 0},
9834f7a0728SDaniel Baluta {FSL_SAI_RCR3(0), 0},
9844f7a0728SDaniel Baluta {FSL_SAI_RCR4(0), 0},
9854f7a0728SDaniel Baluta {FSL_SAI_RCR5(0), 0},
9864f7a0728SDaniel Baluta {FSL_SAI_RMR, 0},
9874f7a0728SDaniel Baluta };
9884f7a0728SDaniel Baluta
9894f7a0728SDaniel Baluta static struct reg_default fsl_sai_reg_defaults_ofs8[] = {
9904f7a0728SDaniel Baluta {FSL_SAI_TCR1(8), 0},
9914f7a0728SDaniel Baluta {FSL_SAI_TCR2(8), 0},
9924f7a0728SDaniel Baluta {FSL_SAI_TCR3(8), 0},
9934f7a0728SDaniel Baluta {FSL_SAI_TCR4(8), 0},
9944f7a0728SDaniel Baluta {FSL_SAI_TCR5(8), 0},
9954f7a0728SDaniel Baluta {FSL_SAI_TDR0, 0},
9964f7a0728SDaniel Baluta {FSL_SAI_TDR1, 0},
9974f7a0728SDaniel Baluta {FSL_SAI_TDR2, 0},
9984f7a0728SDaniel Baluta {FSL_SAI_TDR3, 0},
9994f7a0728SDaniel Baluta {FSL_SAI_TDR4, 0},
10004f7a0728SDaniel Baluta {FSL_SAI_TDR5, 0},
10014f7a0728SDaniel Baluta {FSL_SAI_TDR6, 0},
10024f7a0728SDaniel Baluta {FSL_SAI_TDR7, 0},
10034f7a0728SDaniel Baluta {FSL_SAI_TMR, 0},
10044f7a0728SDaniel Baluta {FSL_SAI_RCR1(8), 0},
10054f7a0728SDaniel Baluta {FSL_SAI_RCR2(8), 0},
10064f7a0728SDaniel Baluta {FSL_SAI_RCR3(8), 0},
10074f7a0728SDaniel Baluta {FSL_SAI_RCR4(8), 0},
10084f7a0728SDaniel Baluta {FSL_SAI_RCR5(8), 0},
10093f6f5b0cSZidan Wang {FSL_SAI_RMR, 0},
10100b2cbce6SShengjiu Wang {FSL_SAI_MCTL, 0},
10110b2cbce6SShengjiu Wang {FSL_SAI_MDIV, 0},
10123f6f5b0cSZidan Wang };
10133f6f5b0cSZidan Wang
fsl_sai_readable_reg(struct device * dev,unsigned int reg)101478957fc3SXiubo Li static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
101578957fc3SXiubo Li {
10164f7a0728SDaniel Baluta struct fsl_sai *sai = dev_get_drvdata(dev);
10174f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
10184f7a0728SDaniel Baluta
10194f7a0728SDaniel Baluta if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
10204f7a0728SDaniel Baluta return true;
10214f7a0728SDaniel Baluta
10224f7a0728SDaniel Baluta if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
10234f7a0728SDaniel Baluta return true;
10244f7a0728SDaniel Baluta
102578957fc3SXiubo Li switch (reg) {
10265f0ac20eSDaniel Baluta case FSL_SAI_TFR0:
10275f0ac20eSDaniel Baluta case FSL_SAI_TFR1:
10285f0ac20eSDaniel Baluta case FSL_SAI_TFR2:
10295f0ac20eSDaniel Baluta case FSL_SAI_TFR3:
10305f0ac20eSDaniel Baluta case FSL_SAI_TFR4:
10315f0ac20eSDaniel Baluta case FSL_SAI_TFR5:
10325f0ac20eSDaniel Baluta case FSL_SAI_TFR6:
10335f0ac20eSDaniel Baluta case FSL_SAI_TFR7:
103478957fc3SXiubo Li case FSL_SAI_TMR:
10355f0ac20eSDaniel Baluta case FSL_SAI_RDR0:
10365f0ac20eSDaniel Baluta case FSL_SAI_RDR1:
10375f0ac20eSDaniel Baluta case FSL_SAI_RDR2:
10385f0ac20eSDaniel Baluta case FSL_SAI_RDR3:
10395f0ac20eSDaniel Baluta case FSL_SAI_RDR4:
10405f0ac20eSDaniel Baluta case FSL_SAI_RDR5:
10415f0ac20eSDaniel Baluta case FSL_SAI_RDR6:
10425f0ac20eSDaniel Baluta case FSL_SAI_RDR7:
10435f0ac20eSDaniel Baluta case FSL_SAI_RFR0:
10445f0ac20eSDaniel Baluta case FSL_SAI_RFR1:
10455f0ac20eSDaniel Baluta case FSL_SAI_RFR2:
10465f0ac20eSDaniel Baluta case FSL_SAI_RFR3:
10475f0ac20eSDaniel Baluta case FSL_SAI_RFR4:
10485f0ac20eSDaniel Baluta case FSL_SAI_RFR5:
10495f0ac20eSDaniel Baluta case FSL_SAI_RFR6:
10505f0ac20eSDaniel Baluta case FSL_SAI_RFR7:
105178957fc3SXiubo Li case FSL_SAI_RMR:
10520b2cbce6SShengjiu Wang case FSL_SAI_MCTL:
10530b2cbce6SShengjiu Wang case FSL_SAI_MDIV:
10540b2cbce6SShengjiu Wang case FSL_SAI_VERID:
10550b2cbce6SShengjiu Wang case FSL_SAI_PARAM:
10560b2cbce6SShengjiu Wang case FSL_SAI_TTCTN:
10570b2cbce6SShengjiu Wang case FSL_SAI_RTCTN:
10580b2cbce6SShengjiu Wang case FSL_SAI_TTCTL:
10590b2cbce6SShengjiu Wang case FSL_SAI_TBCTN:
10600b2cbce6SShengjiu Wang case FSL_SAI_TTCAP:
10610b2cbce6SShengjiu Wang case FSL_SAI_RTCTL:
10620b2cbce6SShengjiu Wang case FSL_SAI_RBCTN:
10630b2cbce6SShengjiu Wang case FSL_SAI_RTCAP:
106478957fc3SXiubo Li return true;
106578957fc3SXiubo Li default:
106678957fc3SXiubo Li return false;
106778957fc3SXiubo Li }
106878957fc3SXiubo Li }
106978957fc3SXiubo Li
fsl_sai_volatile_reg(struct device * dev,unsigned int reg)107078957fc3SXiubo Li static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
107178957fc3SXiubo Li {
10724f7a0728SDaniel Baluta struct fsl_sai *sai = dev_get_drvdata(dev);
10734f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
10744f7a0728SDaniel Baluta
10754f7a0728SDaniel Baluta if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs))
10764f7a0728SDaniel Baluta return true;
10774f7a0728SDaniel Baluta
10780b2cbce6SShengjiu Wang /* Set VERID and PARAM be volatile for reading value in probe */
10790b2cbce6SShengjiu Wang if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM))
10800b2cbce6SShengjiu Wang return true;
10810b2cbce6SShengjiu Wang
108278957fc3SXiubo Li switch (reg) {
10835f0ac20eSDaniel Baluta case FSL_SAI_TFR0:
10845f0ac20eSDaniel Baluta case FSL_SAI_TFR1:
10855f0ac20eSDaniel Baluta case FSL_SAI_TFR2:
10865f0ac20eSDaniel Baluta case FSL_SAI_TFR3:
10875f0ac20eSDaniel Baluta case FSL_SAI_TFR4:
10885f0ac20eSDaniel Baluta case FSL_SAI_TFR5:
10895f0ac20eSDaniel Baluta case FSL_SAI_TFR6:
10905f0ac20eSDaniel Baluta case FSL_SAI_TFR7:
10915f0ac20eSDaniel Baluta case FSL_SAI_RFR0:
10925f0ac20eSDaniel Baluta case FSL_SAI_RFR1:
10935f0ac20eSDaniel Baluta case FSL_SAI_RFR2:
10945f0ac20eSDaniel Baluta case FSL_SAI_RFR3:
10955f0ac20eSDaniel Baluta case FSL_SAI_RFR4:
10965f0ac20eSDaniel Baluta case FSL_SAI_RFR5:
10975f0ac20eSDaniel Baluta case FSL_SAI_RFR6:
10985f0ac20eSDaniel Baluta case FSL_SAI_RFR7:
10995f0ac20eSDaniel Baluta case FSL_SAI_RDR0:
11005f0ac20eSDaniel Baluta case FSL_SAI_RDR1:
11015f0ac20eSDaniel Baluta case FSL_SAI_RDR2:
11025f0ac20eSDaniel Baluta case FSL_SAI_RDR3:
11035f0ac20eSDaniel Baluta case FSL_SAI_RDR4:
11045f0ac20eSDaniel Baluta case FSL_SAI_RDR5:
11055f0ac20eSDaniel Baluta case FSL_SAI_RDR6:
11065f0ac20eSDaniel Baluta case FSL_SAI_RDR7:
110778957fc3SXiubo Li return true;
110878957fc3SXiubo Li default:
110978957fc3SXiubo Li return false;
111078957fc3SXiubo Li }
111178957fc3SXiubo Li }
111278957fc3SXiubo Li
fsl_sai_writeable_reg(struct device * dev,unsigned int reg)111378957fc3SXiubo Li static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
111478957fc3SXiubo Li {
11154f7a0728SDaniel Baluta struct fsl_sai *sai = dev_get_drvdata(dev);
11164f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
11174f7a0728SDaniel Baluta
11184f7a0728SDaniel Baluta if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
11194f7a0728SDaniel Baluta return true;
11204f7a0728SDaniel Baluta
11214f7a0728SDaniel Baluta if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
11224f7a0728SDaniel Baluta return true;
11234f7a0728SDaniel Baluta
112478957fc3SXiubo Li switch (reg) {
11255f0ac20eSDaniel Baluta case FSL_SAI_TDR0:
11265f0ac20eSDaniel Baluta case FSL_SAI_TDR1:
11275f0ac20eSDaniel Baluta case FSL_SAI_TDR2:
11285f0ac20eSDaniel Baluta case FSL_SAI_TDR3:
11295f0ac20eSDaniel Baluta case FSL_SAI_TDR4:
11305f0ac20eSDaniel Baluta case FSL_SAI_TDR5:
11315f0ac20eSDaniel Baluta case FSL_SAI_TDR6:
11325f0ac20eSDaniel Baluta case FSL_SAI_TDR7:
113378957fc3SXiubo Li case FSL_SAI_TMR:
113478957fc3SXiubo Li case FSL_SAI_RMR:
11350b2cbce6SShengjiu Wang case FSL_SAI_MCTL:
11360b2cbce6SShengjiu Wang case FSL_SAI_MDIV:
11370b2cbce6SShengjiu Wang case FSL_SAI_TTCTL:
11380b2cbce6SShengjiu Wang case FSL_SAI_RTCTL:
113978957fc3SXiubo Li return true;
114078957fc3SXiubo Li default:
114178957fc3SXiubo Li return false;
114278957fc3SXiubo Li }
114378957fc3SXiubo Li }
114478957fc3SXiubo Li
11454f7a0728SDaniel Baluta static struct regmap_config fsl_sai_regmap_config = {
114678957fc3SXiubo Li .reg_bits = 32,
114778957fc3SXiubo Li .reg_stride = 4,
114878957fc3SXiubo Li .val_bits = 32,
11496d19d8a3SLucas Stach .fast_io = true,
115078957fc3SXiubo Li
115178957fc3SXiubo Li .max_register = FSL_SAI_RMR,
11524f7a0728SDaniel Baluta .reg_defaults = fsl_sai_reg_defaults_ofs0,
11534f7a0728SDaniel Baluta .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults_ofs0),
115478957fc3SXiubo Li .readable_reg = fsl_sai_readable_reg,
115578957fc3SXiubo Li .volatile_reg = fsl_sai_volatile_reg,
115678957fc3SXiubo Li .writeable_reg = fsl_sai_writeable_reg,
11571fde5e83SZidan Wang .cache_type = REGCACHE_FLAT,
115878957fc3SXiubo Li };
115978957fc3SXiubo Li
fsl_sai_check_version(struct device * dev)11601dc658b1SShengjiu Wang static int fsl_sai_check_version(struct device *dev)
11611dc658b1SShengjiu Wang {
11621dc658b1SShengjiu Wang struct fsl_sai *sai = dev_get_drvdata(dev);
11631dc658b1SShengjiu Wang unsigned char ofs = sai->soc_data->reg_offset;
11641dc658b1SShengjiu Wang unsigned int val;
11651dc658b1SShengjiu Wang int ret;
11661dc658b1SShengjiu Wang
11671dc658b1SShengjiu Wang if (FSL_SAI_TCSR(ofs) == FSL_SAI_VERID)
11681dc658b1SShengjiu Wang return 0;
11691dc658b1SShengjiu Wang
11701dc658b1SShengjiu Wang ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val);
11711dc658b1SShengjiu Wang if (ret < 0)
11721dc658b1SShengjiu Wang return ret;
11731dc658b1SShengjiu Wang
11741dc658b1SShengjiu Wang dev_dbg(dev, "VERID: 0x%016X\n", val);
11751dc658b1SShengjiu Wang
117699c1e74fSSascha Hauer sai->verid.version = val &
117799c1e74fSSascha Hauer (FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK);
117829aab388SShengjiu Wang sai->verid.version >>= FSL_SAI_VERID_MINOR_SHIFT;
11791dc658b1SShengjiu Wang sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
11801dc658b1SShengjiu Wang
11811dc658b1SShengjiu Wang ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val);
11821dc658b1SShengjiu Wang if (ret < 0)
11831dc658b1SShengjiu Wang return ret;
11841dc658b1SShengjiu Wang
11851dc658b1SShengjiu Wang dev_dbg(dev, "PARAM: 0x%016X\n", val);
11861dc658b1SShengjiu Wang
11871dc658b1SShengjiu Wang /* Max slots per frame, power of 2 */
11881dc658b1SShengjiu Wang sai->param.slot_num = 1 <<
11891dc658b1SShengjiu Wang ((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT);
11901dc658b1SShengjiu Wang
11911dc658b1SShengjiu Wang /* Words per fifo, power of 2 */
11921dc658b1SShengjiu Wang sai->param.fifo_depth = 1 <<
11931dc658b1SShengjiu Wang ((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT);
11941dc658b1SShengjiu Wang
11951dc658b1SShengjiu Wang /* Number of datalines implemented */
11961dc658b1SShengjiu Wang sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK;
11971dc658b1SShengjiu Wang
11981dc658b1SShengjiu Wang return 0;
11991dc658b1SShengjiu Wang }
12001dc658b1SShengjiu Wang
1201e3f4e5b1SShengjiu Wang /*
1202e3f4e5b1SShengjiu Wang * Calculate the offset between first two datalines, don't
1203e3f4e5b1SShengjiu Wang * different offset in one case.
1204e3f4e5b1SShengjiu Wang */
fsl_sai_calc_dl_off(unsigned long dl_mask)1205e3f4e5b1SShengjiu Wang static unsigned int fsl_sai_calc_dl_off(unsigned long dl_mask)
1206e3f4e5b1SShengjiu Wang {
1207e3f4e5b1SShengjiu Wang int fbidx, nbidx, offset;
1208e3f4e5b1SShengjiu Wang
1209e3f4e5b1SShengjiu Wang fbidx = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
1210e3f4e5b1SShengjiu Wang nbidx = find_next_bit(&dl_mask, FSL_SAI_DL_NUM, fbidx + 1);
1211e3f4e5b1SShengjiu Wang offset = nbidx - fbidx - 1;
1212e3f4e5b1SShengjiu Wang
1213e3f4e5b1SShengjiu Wang return (offset < 0 || offset >= (FSL_SAI_DL_NUM - 1) ? 0 : offset);
1214e3f4e5b1SShengjiu Wang }
1215e3f4e5b1SShengjiu Wang
1216e3f4e5b1SShengjiu Wang /*
1217e3f4e5b1SShengjiu Wang * read the fsl,dataline property from dts file.
1218e3f4e5b1SShengjiu Wang * It has 3 value for each configuration, first one means the type:
1219e3f4e5b1SShengjiu Wang * I2S(1) or PDM(2), second one is dataline mask for 'rx', third one is
1220e3f4e5b1SShengjiu Wang * dataline mask for 'tx'. for example
1221e3f4e5b1SShengjiu Wang *
1222e3f4e5b1SShengjiu Wang * fsl,dataline = <1 0xff 0xff 2 0xff 0x11>,
1223e3f4e5b1SShengjiu Wang *
1224e3f4e5b1SShengjiu Wang * It means I2S type rx mask is 0xff, tx mask is 0xff, PDM type
1225e3f4e5b1SShengjiu Wang * rx mask is 0xff, tx mask is 0x11 (dataline 1 and 4 enabled).
1226e3f4e5b1SShengjiu Wang *
1227e3f4e5b1SShengjiu Wang */
fsl_sai_read_dlcfg(struct fsl_sai * sai)1228e3f4e5b1SShengjiu Wang static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
1229e3f4e5b1SShengjiu Wang {
1230e3f4e5b1SShengjiu Wang struct platform_device *pdev = sai->pdev;
1231e3f4e5b1SShengjiu Wang struct device_node *np = pdev->dev.of_node;
1232e3f4e5b1SShengjiu Wang struct device *dev = &pdev->dev;
1233e3f4e5b1SShengjiu Wang int ret, elems, i, index, num_cfg;
1234e3f4e5b1SShengjiu Wang char *propname = "fsl,dataline";
1235e3f4e5b1SShengjiu Wang struct fsl_sai_dl_cfg *cfg;
1236e3f4e5b1SShengjiu Wang unsigned long dl_mask;
1237e3f4e5b1SShengjiu Wang unsigned int soc_dl;
1238e3f4e5b1SShengjiu Wang u32 rx, tx, type;
1239e3f4e5b1SShengjiu Wang
1240e3f4e5b1SShengjiu Wang elems = of_property_count_u32_elems(np, propname);
1241e3f4e5b1SShengjiu Wang
1242e3f4e5b1SShengjiu Wang if (elems <= 0) {
1243e3f4e5b1SShengjiu Wang elems = 0;
1244e3f4e5b1SShengjiu Wang } else if (elems % 3) {
1245e3f4e5b1SShengjiu Wang dev_err(dev, "Number of elements must be divisible to 3.\n");
1246e3f4e5b1SShengjiu Wang return -EINVAL;
1247e3f4e5b1SShengjiu Wang }
1248e3f4e5b1SShengjiu Wang
1249e3f4e5b1SShengjiu Wang num_cfg = elems / 3;
1250e3f4e5b1SShengjiu Wang /* Add one more for default value */
1251e3f4e5b1SShengjiu Wang cfg = devm_kzalloc(&pdev->dev, (num_cfg + 1) * sizeof(*cfg), GFP_KERNEL);
1252e3f4e5b1SShengjiu Wang if (!cfg)
1253e3f4e5b1SShengjiu Wang return -ENOMEM;
1254e3f4e5b1SShengjiu Wang
1255e3f4e5b1SShengjiu Wang /* Consider default value "0 0xFF 0xFF" if property is missing */
1256e3f4e5b1SShengjiu Wang soc_dl = BIT(sai->soc_data->pins) - 1;
1257e3f4e5b1SShengjiu Wang cfg[0].type = FSL_SAI_DL_DEFAULT;
1258e3f4e5b1SShengjiu Wang cfg[0].pins[0] = sai->soc_data->pins;
1259e3f4e5b1SShengjiu Wang cfg[0].mask[0] = soc_dl;
1260e3f4e5b1SShengjiu Wang cfg[0].start_off[0] = 0;
1261e3f4e5b1SShengjiu Wang cfg[0].next_off[0] = 0;
1262e3f4e5b1SShengjiu Wang
1263e3f4e5b1SShengjiu Wang cfg[0].pins[1] = sai->soc_data->pins;
1264e3f4e5b1SShengjiu Wang cfg[0].mask[1] = soc_dl;
1265e3f4e5b1SShengjiu Wang cfg[0].start_off[1] = 0;
1266e3f4e5b1SShengjiu Wang cfg[0].next_off[1] = 0;
1267e3f4e5b1SShengjiu Wang for (i = 1, index = 0; i < num_cfg + 1; i++) {
1268e3f4e5b1SShengjiu Wang /*
1269e3f4e5b1SShengjiu Wang * type of dataline
1270e3f4e5b1SShengjiu Wang * 0 means default mode
1271e3f4e5b1SShengjiu Wang * 1 means I2S mode
1272e3f4e5b1SShengjiu Wang * 2 means PDM mode
1273e3f4e5b1SShengjiu Wang */
1274e3f4e5b1SShengjiu Wang ret = of_property_read_u32_index(np, propname, index++, &type);
1275e3f4e5b1SShengjiu Wang if (ret)
1276e3f4e5b1SShengjiu Wang return -EINVAL;
1277e3f4e5b1SShengjiu Wang
1278e3f4e5b1SShengjiu Wang ret = of_property_read_u32_index(np, propname, index++, &rx);
1279e3f4e5b1SShengjiu Wang if (ret)
1280e3f4e5b1SShengjiu Wang return -EINVAL;
1281e3f4e5b1SShengjiu Wang
1282e3f4e5b1SShengjiu Wang ret = of_property_read_u32_index(np, propname, index++, &tx);
1283e3f4e5b1SShengjiu Wang if (ret)
1284e3f4e5b1SShengjiu Wang return -EINVAL;
1285e3f4e5b1SShengjiu Wang
1286e3f4e5b1SShengjiu Wang if ((rx & ~soc_dl) || (tx & ~soc_dl)) {
1287e3f4e5b1SShengjiu Wang dev_err(dev, "dataline cfg[%d] setting error, mask is 0x%x\n", i, soc_dl);
1288e3f4e5b1SShengjiu Wang return -EINVAL;
1289e3f4e5b1SShengjiu Wang }
1290e3f4e5b1SShengjiu Wang
1291e3f4e5b1SShengjiu Wang rx = rx & soc_dl;
1292e3f4e5b1SShengjiu Wang tx = tx & soc_dl;
1293e3f4e5b1SShengjiu Wang
1294e3f4e5b1SShengjiu Wang cfg[i].type = type;
1295e3f4e5b1SShengjiu Wang cfg[i].pins[0] = hweight8(rx);
1296e3f4e5b1SShengjiu Wang cfg[i].mask[0] = rx;
1297e3f4e5b1SShengjiu Wang dl_mask = rx;
1298e3f4e5b1SShengjiu Wang cfg[i].start_off[0] = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
1299e3f4e5b1SShengjiu Wang cfg[i].next_off[0] = fsl_sai_calc_dl_off(rx);
1300e3f4e5b1SShengjiu Wang
1301e3f4e5b1SShengjiu Wang cfg[i].pins[1] = hweight8(tx);
1302e3f4e5b1SShengjiu Wang cfg[i].mask[1] = tx;
1303e3f4e5b1SShengjiu Wang dl_mask = tx;
1304e3f4e5b1SShengjiu Wang cfg[i].start_off[1] = find_first_bit(&dl_mask, FSL_SAI_DL_NUM);
1305e3f4e5b1SShengjiu Wang cfg[i].next_off[1] = fsl_sai_calc_dl_off(tx);
1306e3f4e5b1SShengjiu Wang }
1307e3f4e5b1SShengjiu Wang
1308e3f4e5b1SShengjiu Wang sai->dl_cfg = cfg;
1309e3f4e5b1SShengjiu Wang sai->dl_cfg_cnt = num_cfg + 1;
1310e3f4e5b1SShengjiu Wang return 0;
1311e3f4e5b1SShengjiu Wang }
1312e3f4e5b1SShengjiu Wang
13132277e7e3SShengjiu Wang static int fsl_sai_runtime_suspend(struct device *dev);
13142277e7e3SShengjiu Wang static int fsl_sai_runtime_resume(struct device *dev);
13152277e7e3SShengjiu Wang
fsl_sai_probe(struct platform_device * pdev)131643550821SXiubo Li static int fsl_sai_probe(struct platform_device *pdev)
131743550821SXiubo Li {
13184e3a99f5SNicolin Chen struct device_node *np = pdev->dev.of_node;
1319f53f50eeSMarco Felsch struct device *dev = &pdev->dev;
132043550821SXiubo Li struct fsl_sai *sai;
13214d245850SFabio Estevam struct regmap *gpr;
132278957fc3SXiubo Li void __iomem *base;
1323ca3e35c7SNicolin Chen char tmp[8];
1324ca3e35c7SNicolin Chen int irq, ret, i;
13254d245850SFabio Estevam int index;
132688630575SShengjiu Wang u32 dmas[4];
132743550821SXiubo Li
1328f53f50eeSMarco Felsch sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
132943550821SXiubo Li if (!sai)
133043550821SXiubo Li return -ENOMEM;
133143550821SXiubo Li
1332e2681a1bSNicolin Chen sai->pdev = pdev;
1333f53f50eeSMarco Felsch sai->soc_data = of_device_get_match_data(dev);
1334c7540644SNicolin Chen
1335eadb0019SXiubo Li sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
133678957fc3SXiubo Li
1337cd640ca2SShengjiu Wang base = devm_platform_get_and_ioremap_resource(pdev, 0, &sai->res);
133878957fc3SXiubo Li if (IS_ERR(base))
133978957fc3SXiubo Li return PTR_ERR(base);
134078957fc3SXiubo Li
13414f7a0728SDaniel Baluta if (sai->soc_data->reg_offset == 8) {
13424f7a0728SDaniel Baluta fsl_sai_regmap_config.reg_defaults = fsl_sai_reg_defaults_ofs8;
13430b2cbce6SShengjiu Wang fsl_sai_regmap_config.max_register = FSL_SAI_MDIV;
13444f7a0728SDaniel Baluta fsl_sai_regmap_config.num_reg_defaults =
13454f7a0728SDaniel Baluta ARRAY_SIZE(fsl_sai_reg_defaults_ofs8);
13464f7a0728SDaniel Baluta }
13474f7a0728SDaniel Baluta
1348f53f50eeSMarco Felsch sai->regmap = devm_regmap_init_mmio(dev, base, &fsl_sai_regmap_config);
134978957fc3SXiubo Li if (IS_ERR(sai->regmap)) {
1350f53f50eeSMarco Felsch dev_err(dev, "regmap init failed\n");
135178957fc3SXiubo Li return PTR_ERR(sai->regmap);
135243550821SXiubo Li }
135343550821SXiubo Li
1354f53f50eeSMarco Felsch sai->bus_clk = devm_clk_get(dev, "bus");
13552277e7e3SShengjiu Wang /* Compatible with old DTB cases */
13562277e7e3SShengjiu Wang if (IS_ERR(sai->bus_clk) && PTR_ERR(sai->bus_clk) != -EPROBE_DEFER)
1357f53f50eeSMarco Felsch sai->bus_clk = devm_clk_get(dev, "sai");
1358ca3e35c7SNicolin Chen if (IS_ERR(sai->bus_clk)) {
1359f53f50eeSMarco Felsch dev_err(dev, "failed to get bus clock: %ld\n",
1360ca3e35c7SNicolin Chen PTR_ERR(sai->bus_clk));
13612277e7e3SShengjiu Wang /* -EPROBE_DEFER */
13622277e7e3SShengjiu Wang return PTR_ERR(sai->bus_clk);
1363ca3e35c7SNicolin Chen }
1364ca3e35c7SNicolin Chen
1365c3ecef21SZidan Wang for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
1366c3ecef21SZidan Wang sprintf(tmp, "mclk%d", i);
1367f53f50eeSMarco Felsch sai->mclk_clk[i] = devm_clk_get(dev, tmp);
1368ca3e35c7SNicolin Chen if (IS_ERR(sai->mclk_clk[i])) {
1369f53f50eeSMarco Felsch dev_err(dev, "failed to get mclk%d clock: %ld\n",
13703b43a713SPieterjan Camerlynck i, PTR_ERR(sai->mclk_clk[i]));
1371ca3e35c7SNicolin Chen sai->mclk_clk[i] = NULL;
1372ca3e35c7SNicolin Chen }
1373ca3e35c7SNicolin Chen }
1374ca3e35c7SNicolin Chen
137553233e40SShengjiu Wang if (sai->soc_data->mclk0_is_mclk1)
137653233e40SShengjiu Wang sai->mclk_clk[0] = sai->mclk_clk[1];
137753233e40SShengjiu Wang else
137853233e40SShengjiu Wang sai->mclk_clk[0] = sai->bus_clk;
137953233e40SShengjiu Wang
13807cb7f07dSShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
13817cb7f07dSShengjiu Wang &sai->pll11k_clk);
13827cb7f07dSShengjiu Wang
138388630575SShengjiu Wang /* Use Multi FIFO mode depending on the support from SDMA script */
138488630575SShengjiu Wang ret = of_property_read_u32_array(np, "dmas", dmas, 4);
138588630575SShengjiu Wang if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
138688630575SShengjiu Wang sai->is_multi_fifo_dma = true;
138788630575SShengjiu Wang
1388e3f4e5b1SShengjiu Wang /* read dataline mask for rx and tx*/
1389e3f4e5b1SShengjiu Wang ret = fsl_sai_read_dlcfg(sai);
1390e3f4e5b1SShengjiu Wang if (ret < 0) {
1391e3f4e5b1SShengjiu Wang dev_err(dev, "failed to read dlcfg %d\n", ret);
1392e3f4e5b1SShengjiu Wang return ret;
1393e3f4e5b1SShengjiu Wang }
1394e3f4e5b1SShengjiu Wang
1395e2681a1bSNicolin Chen irq = platform_get_irq(pdev, 0);
1396cf9441adSStephen Boyd if (irq < 0)
1397e2681a1bSNicolin Chen return irq;
1398e2681a1bSNicolin Chen
1399f53f50eeSMarco Felsch ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
14002eb2d314SMichael Walle np->name, sai);
1401e2681a1bSNicolin Chen if (ret) {
1402f53f50eeSMarco Felsch dev_err(dev, "failed to claim irq %u\n", irq);
1403e2681a1bSNicolin Chen return ret;
1404e2681a1bSNicolin Chen }
1405e2681a1bSNicolin Chen
140622a16145SShengjiu Wang memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template,
140722a16145SShengjiu Wang sizeof(fsl_sai_dai_template));
140822a16145SShengjiu Wang
140908fdf65eSNicolin Chen /* Sync Tx with Rx as default by following old DT binding */
141008fdf65eSNicolin Chen sai->synchronous[RX] = true;
141108fdf65eSNicolin Chen sai->synchronous[TX] = false;
1412cb2f6927SKuninori Morimoto sai->cpu_dai_drv.symmetric_rate = 1;
141322a16145SShengjiu Wang sai->cpu_dai_drv.symmetric_channels = 1;
1414cb2f6927SKuninori Morimoto sai->cpu_dai_drv.symmetric_sample_bits = 1;
141508fdf65eSNicolin Chen
14162d2998b8SRob Herring if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
14172d2998b8SRob Herring of_property_read_bool(np, "fsl,sai-asynchronous")) {
1418ce7344a4SNicolin Chen /* error out if both synchronous and asynchronous are present */
1419f53f50eeSMarco Felsch dev_err(dev, "invalid binding for synchronous mode\n");
1420ce7344a4SNicolin Chen return -EINVAL;
1421ce7344a4SNicolin Chen }
1422ce7344a4SNicolin Chen
14232d2998b8SRob Herring if (of_property_read_bool(np, "fsl,sai-synchronous-rx")) {
142408fdf65eSNicolin Chen /* Sync Rx with Tx */
142508fdf65eSNicolin Chen sai->synchronous[RX] = false;
142608fdf65eSNicolin Chen sai->synchronous[TX] = true;
14272d2998b8SRob Herring } else if (of_property_read_bool(np, "fsl,sai-asynchronous")) {
142808fdf65eSNicolin Chen /* Discard all settings for asynchronous mode */
142908fdf65eSNicolin Chen sai->synchronous[RX] = false;
143008fdf65eSNicolin Chen sai->synchronous[TX] = false;
1431cb2f6927SKuninori Morimoto sai->cpu_dai_drv.symmetric_rate = 0;
143222a16145SShengjiu Wang sai->cpu_dai_drv.symmetric_channels = 0;
1433cb2f6927SKuninori Morimoto sai->cpu_dai_drv.symmetric_sample_bits = 0;
143408fdf65eSNicolin Chen }
143508fdf65eSNicolin Chen
14363e4a8261SShengjiu Wang sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output");
14373e4a8261SShengjiu Wang
14383e4a8261SShengjiu Wang if (sai->mclk_direction_output &&
14396ea9c7ddSFabio Estevam of_device_is_compatible(np, "fsl,imx6ul-sai")) {
14404d245850SFabio Estevam gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
14414d245850SFabio Estevam if (IS_ERR(gpr)) {
1442f53f50eeSMarco Felsch dev_err(dev, "cannot find iomuxc registers\n");
14434d245850SFabio Estevam return PTR_ERR(gpr);
14444d245850SFabio Estevam }
14454d245850SFabio Estevam
14464d245850SFabio Estevam index = of_alias_get_id(np, "sai");
14474d245850SFabio Estevam if (index < 0)
14484d245850SFabio Estevam return index;
14494d245850SFabio Estevam
14504d245850SFabio Estevam regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
14514d245850SFabio Estevam MCLK_DIR(index));
14524d245850SFabio Estevam }
14534d245850SFabio Estevam
1454cd640ca2SShengjiu Wang sai->dma_params_rx.addr = sai->res->start + FSL_SAI_RDR0;
1455cd640ca2SShengjiu Wang sai->dma_params_tx.addr = sai->res->start + FSL_SAI_TDR0;
1456870b89d1SChancel Liu sai->dma_params_rx.maxburst =
1457870b89d1SChancel Liu sai->soc_data->max_burst[RX] ? sai->soc_data->max_burst[RX] : FSL_SAI_MAXBURST_RX;
1458870b89d1SChancel Liu sai->dma_params_tx.maxburst =
1459870b89d1SChancel Liu sai->soc_data->max_burst[TX] ? sai->soc_data->max_burst[TX] : FSL_SAI_MAXBURST_TX;
146043550821SXiubo Li
1461b4ee8a91SShengjiu Wang sai->pinctrl = devm_pinctrl_get(&pdev->dev);
1462b4ee8a91SShengjiu Wang
146343550821SXiubo Li platform_set_drvdata(pdev, sai);
1464f53f50eeSMarco Felsch pm_runtime_enable(dev);
1465f53f50eeSMarco Felsch if (!pm_runtime_enabled(dev)) {
1466f53f50eeSMarco Felsch ret = fsl_sai_runtime_resume(dev);
14672277e7e3SShengjiu Wang if (ret)
14682277e7e3SShengjiu Wang goto err_pm_disable;
14692277e7e3SShengjiu Wang }
14702277e7e3SShengjiu Wang
147137cb8a58SPierre-Louis Bossart ret = pm_runtime_resume_and_get(dev);
147237cb8a58SPierre-Louis Bossart if (ret < 0)
14732277e7e3SShengjiu Wang goto err_pm_get_sync;
147443550821SXiubo Li
14751dc658b1SShengjiu Wang /* Get sai version */
1476f53f50eeSMarco Felsch ret = fsl_sai_check_version(dev);
14771dc658b1SShengjiu Wang if (ret < 0)
1478f53f50eeSMarco Felsch dev_warn(dev, "Error reading SAI version: %d\n", ret);
14791dc658b1SShengjiu Wang
1480a57d4e87SShengjiu Wang /* Select MCLK direction */
14813e4a8261SShengjiu Wang if (sai->mclk_direction_output &&
1482af0bd3c0SShengjiu Wang sai->soc_data->max_register >= FSL_SAI_MCTL) {
1483a57d4e87SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
1484a57d4e87SShengjiu Wang FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
1485a57d4e87SShengjiu Wang }
1486a57d4e87SShengjiu Wang
1487f53f50eeSMarco Felsch ret = pm_runtime_put_sync(dev);
14886a564338SMaarten Zanders if (ret < 0 && ret != -ENOSYS)
14892277e7e3SShengjiu Wang goto err_pm_get_sync;
1490812ad463SDaniel Baluta
14919c3ad33bSShengjiu Wang /*
14929c3ad33bSShengjiu Wang * Register platform component before registering cpu dai for there
14939c3ad33bSShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime().
14949c3ad33bSShengjiu Wang */
1495d1520889SOleksandr Suvorov if (sai->soc_data->use_imx_pcm) {
14969b3ff637SSascha Hauer ret = imx_pcm_dma_init(pdev);
149722205521SMarco Felsch if (ret) {
1498aea11bcdSAlexander Stein dev_err_probe(dev, ret, "PCM DMA init failed\n");
149922205521SMarco Felsch if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
150022205521SMarco Felsch dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n");
15012277e7e3SShengjiu Wang goto err_pm_get_sync;
150222205521SMarco Felsch }
1503d1520889SOleksandr Suvorov } else {
1504f53f50eeSMarco Felsch ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
1505aea11bcdSAlexander Stein if (ret) {
1506aea11bcdSAlexander Stein dev_err_probe(dev, ret, "Registering PCM dmaengine failed\n");
15072277e7e3SShengjiu Wang goto err_pm_get_sync;
1508d1520889SOleksandr Suvorov }
1509aea11bcdSAlexander Stein }
1510d1520889SOleksandr Suvorov
1511f53f50eeSMarco Felsch ret = devm_snd_soc_register_component(dev, &fsl_component,
15129c3ad33bSShengjiu Wang &sai->cpu_dai_drv, 1);
15139c3ad33bSShengjiu Wang if (ret)
15149c3ad33bSShengjiu Wang goto err_pm_get_sync;
15159c3ad33bSShengjiu Wang
151643550821SXiubo Li return ret;
151743550821SXiubo Li
15182277e7e3SShengjiu Wang err_pm_get_sync:
1519f53f50eeSMarco Felsch if (!pm_runtime_status_suspended(dev))
1520f53f50eeSMarco Felsch fsl_sai_runtime_suspend(dev);
1521d1520889SOleksandr Suvorov err_pm_disable:
1522f53f50eeSMarco Felsch pm_runtime_disable(dev);
1523d1520889SOleksandr Suvorov
1524d1520889SOleksandr Suvorov return ret;
152543550821SXiubo Li }
152643550821SXiubo Li
fsl_sai_remove(struct platform_device * pdev)152778c2698dSUwe Kleine-König static void fsl_sai_remove(struct platform_device *pdev)
1528812ad463SDaniel Baluta {
1529812ad463SDaniel Baluta pm_runtime_disable(&pdev->dev);
15302277e7e3SShengjiu Wang if (!pm_runtime_status_suspended(&pdev->dev))
15312277e7e3SShengjiu Wang fsl_sai_runtime_suspend(&pdev->dev);
1532812ad463SDaniel Baluta }
1533812ad463SDaniel Baluta
153489c9679fSLucas Stach static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
153589c9679fSLucas Stach .use_imx_pcm = false,
1536e75f4940SMihai Serban .use_edma = false,
1537bd517707SLucas Stach .fifo_depth = 32,
1538eba0f007SSascha Hauer .pins = 1,
15394f7a0728SDaniel Baluta .reg_offset = 0,
154053233e40SShengjiu Wang .mclk0_is_mclk1 = false,
1541907e0cdeSShengjiu Wang .flags = 0,
15429e71bc33SShengjiu Wang .max_register = FSL_SAI_RMR,
154389c9679fSLucas Stach };
154489c9679fSLucas Stach
154589c9679fSLucas Stach static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
154689c9679fSLucas Stach .use_imx_pcm = true,
1547e75f4940SMihai Serban .use_edma = false,
1548bd517707SLucas Stach .fifo_depth = 32,
1549eba0f007SSascha Hauer .pins = 1,
15504f7a0728SDaniel Baluta .reg_offset = 0,
155153233e40SShengjiu Wang .mclk0_is_mclk1 = true,
1552907e0cdeSShengjiu Wang .flags = 0,
15539e71bc33SShengjiu Wang .max_register = FSL_SAI_RMR,
155489c9679fSLucas Stach };
155589c9679fSLucas Stach
1556a860fac4SDaniel Baluta static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
1557a860fac4SDaniel Baluta .use_imx_pcm = true,
1558e75f4940SMihai Serban .use_edma = false,
1559a860fac4SDaniel Baluta .fifo_depth = 16,
1560eba0f007SSascha Hauer .pins = 2,
1561a860fac4SDaniel Baluta .reg_offset = 8,
156253233e40SShengjiu Wang .mclk0_is_mclk1 = false,
1563907e0cdeSShengjiu Wang .flags = PMQOS_CPU_LATENCY,
15649e71bc33SShengjiu Wang .max_register = FSL_SAI_RMR,
1565a860fac4SDaniel Baluta };
1566a860fac4SDaniel Baluta
1567a860fac4SDaniel Baluta static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
1568a860fac4SDaniel Baluta .use_imx_pcm = true,
1569e75f4940SMihai Serban .use_edma = false,
1570a860fac4SDaniel Baluta .fifo_depth = 128,
1571eba0f007SSascha Hauer .pins = 8,
1572a860fac4SDaniel Baluta .reg_offset = 8,
157353233e40SShengjiu Wang .mclk0_is_mclk1 = false,
1574907e0cdeSShengjiu Wang .flags = 0,
15759e71bc33SShengjiu Wang .max_register = FSL_SAI_RMR,
1576a860fac4SDaniel Baluta };
1577a860fac4SDaniel Baluta
15786eeb60beSDaniel Baluta static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
15796eeb60beSDaniel Baluta .use_imx_pcm = true,
1580e75f4940SMihai Serban .use_edma = true,
15816eeb60beSDaniel Baluta .fifo_depth = 64,
158223878715SChancel Liu .pins = 4,
15836eeb60beSDaniel Baluta .reg_offset = 0,
158453233e40SShengjiu Wang .mclk0_is_mclk1 = false,
1585907e0cdeSShengjiu Wang .flags = 0,
15869e71bc33SShengjiu Wang .max_register = FSL_SAI_RMR,
15879e71bc33SShengjiu Wang };
15889e71bc33SShengjiu Wang
15899e71bc33SShengjiu Wang static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = {
15909e71bc33SShengjiu Wang .use_imx_pcm = true,
15919e71bc33SShengjiu Wang .use_edma = false,
15929e71bc33SShengjiu Wang .fifo_depth = 128,
15939e71bc33SShengjiu Wang .reg_offset = 8,
15949e71bc33SShengjiu Wang .mclk0_is_mclk1 = false,
15959e71bc33SShengjiu Wang .pins = 8,
15969e71bc33SShengjiu Wang .flags = 0,
15979e71bc33SShengjiu Wang .max_register = FSL_SAI_MCTL,
15986eeb60beSDaniel Baluta };
15996eeb60beSDaniel Baluta
16003e4a8261SShengjiu Wang static const struct fsl_sai_soc_data fsl_sai_imx8mn_data = {
16013e4a8261SShengjiu Wang .use_imx_pcm = true,
16023e4a8261SShengjiu Wang .use_edma = false,
16033e4a8261SShengjiu Wang .fifo_depth = 128,
16043e4a8261SShengjiu Wang .reg_offset = 8,
16053e4a8261SShengjiu Wang .mclk0_is_mclk1 = false,
16063e4a8261SShengjiu Wang .pins = 8,
16073e4a8261SShengjiu Wang .flags = 0,
16083e4a8261SShengjiu Wang .max_register = FSL_SAI_MDIV,
16093e4a8261SShengjiu Wang };
16103e4a8261SShengjiu Wang
16112530c5e8SShengjiu Wang static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = {
16122530c5e8SShengjiu Wang .use_imx_pcm = true,
16132530c5e8SShengjiu Wang .use_edma = false,
16142530c5e8SShengjiu Wang .fifo_depth = 128,
16152530c5e8SShengjiu Wang .reg_offset = 8,
16162530c5e8SShengjiu Wang .mclk0_is_mclk1 = false,
16172530c5e8SShengjiu Wang .pins = 8,
16182530c5e8SShengjiu Wang .flags = 0,
16192530c5e8SShengjiu Wang .max_register = FSL_SAI_MDIV,
16203e4a8261SShengjiu Wang .mclk_with_tere = true,
16212530c5e8SShengjiu Wang };
16222530c5e8SShengjiu Wang
1623af0bd3c0SShengjiu Wang static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = {
1624af0bd3c0SShengjiu Wang .use_imx_pcm = true,
1625af0bd3c0SShengjiu Wang .use_edma = true,
1626af0bd3c0SShengjiu Wang .fifo_depth = 16,
1627af0bd3c0SShengjiu Wang .reg_offset = 8,
1628af0bd3c0SShengjiu Wang .mclk0_is_mclk1 = false,
1629af0bd3c0SShengjiu Wang .pins = 4,
1630af0bd3c0SShengjiu Wang .flags = PMQOS_CPU_LATENCY,
1631af0bd3c0SShengjiu Wang .max_register = FSL_SAI_RTCAP,
1632af0bd3c0SShengjiu Wang };
1633af0bd3c0SShengjiu Wang
163467d5c6c1SChancel Liu static const struct fsl_sai_soc_data fsl_sai_imx93_data = {
163567d5c6c1SChancel Liu .use_imx_pcm = true,
163667d5c6c1SChancel Liu .use_edma = true,
163767d5c6c1SChancel Liu .fifo_depth = 128,
163867d5c6c1SChancel Liu .reg_offset = 8,
163967d5c6c1SChancel Liu .mclk0_is_mclk1 = false,
164067d5c6c1SChancel Liu .pins = 4,
164167d5c6c1SChancel Liu .flags = 0,
164267d5c6c1SChancel Liu .max_register = FSL_SAI_MCTL,
1643870b89d1SChancel Liu .max_burst = {8, 8},
164467d5c6c1SChancel Liu };
164567d5c6c1SChancel Liu
164643550821SXiubo Li static const struct of_device_id fsl_sai_ids[] = {
164789c9679fSLucas Stach { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
164889c9679fSLucas Stach { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
164989c9679fSLucas Stach { .compatible = "fsl,imx6ul-sai", .data = &fsl_sai_imx6sx_data },
1650a860fac4SDaniel Baluta { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
1651a860fac4SDaniel Baluta { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
16526eeb60beSDaniel Baluta { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data },
16539e71bc33SShengjiu Wang { .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data },
16542530c5e8SShengjiu Wang { .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data },
1655af0bd3c0SShengjiu Wang { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data },
16563e4a8261SShengjiu Wang { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data },
165767d5c6c1SChancel Liu { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data },
165843550821SXiubo Li { /* sentinel */ }
165943550821SXiubo Li };
1660c759241fSLuis de Bethencourt MODULE_DEVICE_TABLE(of, fsl_sai_ids);
166143550821SXiubo Li
fsl_sai_runtime_suspend(struct device * dev)1662812ad463SDaniel Baluta static int fsl_sai_runtime_suspend(struct device *dev)
16631fde5e83SZidan Wang {
16641fde5e83SZidan Wang struct fsl_sai *sai = dev_get_drvdata(dev);
16651fde5e83SZidan Wang
1666c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
1667c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
1668c0ffbd64SShengjiu Wang
1669c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
1670c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
1671c0ffbd64SShengjiu Wang
1672c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->bus_clk);
1673c0ffbd64SShengjiu Wang
1674907e0cdeSShengjiu Wang if (sai->soc_data->flags & PMQOS_CPU_LATENCY)
1675907e0cdeSShengjiu Wang cpu_latency_qos_remove_request(&sai->pm_qos_req);
1676907e0cdeSShengjiu Wang
16771fde5e83SZidan Wang regcache_cache_only(sai->regmap, true);
16781fde5e83SZidan Wang
16791fde5e83SZidan Wang return 0;
16801fde5e83SZidan Wang }
16811fde5e83SZidan Wang
fsl_sai_runtime_resume(struct device * dev)1682812ad463SDaniel Baluta static int fsl_sai_runtime_resume(struct device *dev)
16831fde5e83SZidan Wang {
16841fde5e83SZidan Wang struct fsl_sai *sai = dev_get_drvdata(dev);
16854f7a0728SDaniel Baluta unsigned int ofs = sai->soc_data->reg_offset;
1686c0ffbd64SShengjiu Wang int ret;
1687c0ffbd64SShengjiu Wang
1688c0ffbd64SShengjiu Wang ret = clk_prepare_enable(sai->bus_clk);
1689c0ffbd64SShengjiu Wang if (ret) {
1690c0ffbd64SShengjiu Wang dev_err(dev, "failed to enable bus clock: %d\n", ret);
1691c0ffbd64SShengjiu Wang return ret;
1692c0ffbd64SShengjiu Wang }
1693c0ffbd64SShengjiu Wang
1694c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) {
1695c0ffbd64SShengjiu Wang ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[1]]);
1696c0ffbd64SShengjiu Wang if (ret)
1697c0ffbd64SShengjiu Wang goto disable_bus_clk;
1698c0ffbd64SShengjiu Wang }
1699c0ffbd64SShengjiu Wang
1700c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) {
1701c0ffbd64SShengjiu Wang ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[0]]);
1702c0ffbd64SShengjiu Wang if (ret)
1703c0ffbd64SShengjiu Wang goto disable_tx_clk;
1704c0ffbd64SShengjiu Wang }
17051fde5e83SZidan Wang
1706907e0cdeSShengjiu Wang if (sai->soc_data->flags & PMQOS_CPU_LATENCY)
1707907e0cdeSShengjiu Wang cpu_latency_qos_add_request(&sai->pm_qos_req, 0);
1708907e0cdeSShengjiu Wang
17091fde5e83SZidan Wang regcache_cache_only(sai->regmap, false);
1710d8d702e1SShengjiu Wang regcache_mark_dirty(sai->regmap);
17114f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR);
17124f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR);
1713512feb4eSXiubo Li usleep_range(1000, 2000);
17144f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
17154f7a0728SDaniel Baluta regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
1716c0ffbd64SShengjiu Wang
1717c0ffbd64SShengjiu Wang ret = regcache_sync(sai->regmap);
1718c0ffbd64SShengjiu Wang if (ret)
1719c0ffbd64SShengjiu Wang goto disable_rx_clk;
1720c0ffbd64SShengjiu Wang
17213e4a8261SShengjiu Wang if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output)
17223e4a8261SShengjiu Wang regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
17233e4a8261SShengjiu Wang FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
17243e4a8261SShengjiu Wang
1725c0ffbd64SShengjiu Wang return 0;
1726c0ffbd64SShengjiu Wang
1727c0ffbd64SShengjiu Wang disable_rx_clk:
1728c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
1729c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
1730c0ffbd64SShengjiu Wang disable_tx_clk:
1731c0ffbd64SShengjiu Wang if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
1732c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
1733c0ffbd64SShengjiu Wang disable_bus_clk:
1734c0ffbd64SShengjiu Wang clk_disable_unprepare(sai->bus_clk);
1735c0ffbd64SShengjiu Wang
1736c0ffbd64SShengjiu Wang return ret;
17371fde5e83SZidan Wang }
17381fde5e83SZidan Wang
17391fde5e83SZidan Wang static const struct dev_pm_ops fsl_sai_pm_ops = {
1740812ad463SDaniel Baluta SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend,
1741812ad463SDaniel Baluta fsl_sai_runtime_resume, NULL)
1742812ad463SDaniel Baluta SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1743812ad463SDaniel Baluta pm_runtime_force_resume)
17441fde5e83SZidan Wang };
17451fde5e83SZidan Wang
174643550821SXiubo Li static struct platform_driver fsl_sai_driver = {
174743550821SXiubo Li .probe = fsl_sai_probe,
174878c2698dSUwe Kleine-König .remove_new = fsl_sai_remove,
174943550821SXiubo Li .driver = {
175043550821SXiubo Li .name = "fsl-sai",
17511fde5e83SZidan Wang .pm = &fsl_sai_pm_ops,
175243550821SXiubo Li .of_match_table = fsl_sai_ids,
175343550821SXiubo Li },
175443550821SXiubo Li };
175543550821SXiubo Li module_platform_driver(fsl_sai_driver);
175643550821SXiubo Li
175743550821SXiubo Li MODULE_DESCRIPTION("Freescale Soc SAI Interface");
175843550821SXiubo Li MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>");
175943550821SXiubo Li MODULE_ALIAS("platform:fsl-sai");
176043550821SXiubo Li MODULE_LICENSE("GPL");
1761