From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom
---
kernel/sound/soc/fsl/fsl_sai.c | 774 ++++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 554 insertions(+), 220 deletions(-)
diff --git a/kernel/sound/soc/fsl/fsl_sai.c b/kernel/sound/soc/fsl/fsl_sai.c
index bfc5b21..3e5c1ea 100644
--- a/kernel/sound/soc/fsl/fsl_sai.c
+++ b/kernel/sound/soc/fsl/fsl_sai.c
@@ -9,6 +9,8 @@
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/time.h>
@@ -35,9 +37,28 @@
.list = fsl_sai_rates,
};
+/**
+ * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream
+ *
+ * SAI supports synchronous mode using bit/frame clocks of either Transmitter's
+ * or Receiver's for both streams. This function is used to check if clocks of
+ * the stream's are synced by the opposite stream.
+ *
+ * @sai: SAI context
+ * @dir: stream direction
+ */
+static inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir)
+{
+ int adir = (dir == TX) ? RX : TX;
+
+ /* current dir in async mode while opposite dir in sync mode */
+ return !sai->synchronous[dir] && sai->synchronous[adir];
+}
+
static irqreturn_t fsl_sai_isr(int irq, void *devid)
{
struct fsl_sai *sai = (struct fsl_sai *)devid;
+ unsigned int ofs = sai->soc_data->reg_offset;
struct device *dev = &sai->pdev->dev;
u32 flags, xcsr, mask;
bool irq_none = true;
@@ -50,7 +71,7 @@
mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
/* Tx IRQ */
- regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
+ regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &xcsr);
flags = xcsr & mask;
if (flags)
@@ -62,10 +83,10 @@
dev_dbg(dev, "isr: Start of Tx word detected\n");
if (flags & FSL_SAI_CSR_SEF)
- dev_warn(dev, "isr: Tx Frame sync error detected\n");
+ dev_dbg(dev, "isr: Tx Frame sync error detected\n");
if (flags & FSL_SAI_CSR_FEF) {
- dev_warn(dev, "isr: Transmit underrun detected\n");
+ dev_dbg(dev, "isr: Transmit underrun detected\n");
/* FIFO reset for safety */
xcsr |= FSL_SAI_CSR_FR;
}
@@ -80,11 +101,11 @@
xcsr &= ~FSL_SAI_CSR_xF_MASK;
if (flags)
- regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), flags | xcsr);
irq_rx:
/* Rx IRQ */
- regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
+ regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &xcsr);
flags = xcsr & mask;
if (flags)
@@ -96,10 +117,10 @@
dev_dbg(dev, "isr: Start of Rx word detected\n");
if (flags & FSL_SAI_CSR_SEF)
- dev_warn(dev, "isr: Rx Frame sync error detected\n");
+ dev_dbg(dev, "isr: Rx Frame sync error detected\n");
if (flags & FSL_SAI_CSR_FEF) {
- dev_warn(dev, "isr: Receive overflow detected\n");
+ dev_dbg(dev, "isr: Receive overflow detected\n");
/* FIFO reset for safety */
xcsr |= FSL_SAI_CSR_FR;
}
@@ -114,7 +135,7 @@
xcsr &= ~FSL_SAI_CSR_xF_MASK;
if (flags)
- regmap_write(sai->regmap, FSL_SAI_RCSR, flags | xcsr);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), flags | xcsr);
out:
if (irq_none)
@@ -134,10 +155,21 @@
return 0;
}
+static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+
+ sai->bclk_ratio = ratio;
+
+ return 0;
+}
+
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0;
@@ -158,7 +190,7 @@
return -EINVAL;
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_MSEL_MASK, val_cr2);
return 0;
@@ -191,6 +223,7 @@
unsigned int fmt, int fsl_dir)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
u32 val_cr2 = 0, val_cr4 = 0;
@@ -285,9 +318,9 @@
return -EINVAL;
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
- regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
@@ -314,8 +347,11 @@
static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
unsigned long clk_rate;
u32 savediv = 0, ratio, savesub = freq;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;
u32 id;
int ret = 0;
@@ -374,19 +410,17 @@
* 4) For Tx and Rx are both Synchronous with another SAI, we just
* ignore it.
*/
- if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
- (!tx && !sai->synchronous[RX])) {
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+ if (fsl_sai_dir_is_synced(sai, adir)) {
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
FSL_SAI_CR2_DIV_MASK, savediv - 1);
- } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
- (tx && !sai->synchronous[TX])) {
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+ } else if (!sai->synchronous[dir]) {
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_DIV_MASK, savediv - 1);
}
@@ -401,12 +435,15 @@
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
u32 word_width = params_width(params);
u32 val_cr4 = 0, val_cr5 = 0;
u32 slots = (channels == 1) ? 2 : channels;
u32 slot_width = word_width;
+ int adir = tx ? RX : TX;
+ u32 pins;
int ret;
if (sai->slots)
@@ -415,9 +452,17 @@
if (sai->slot_width)
slot_width = sai->slot_width;
+ pins = DIV_ROUND_UP(channels, slots);
+
if (!sai->is_slave_mode) {
- ret = fsl_sai_set_bclk(cpu_dai, tx,
- slots * slot_width * params_rate(params));
+ if (sai->bclk_ratio)
+ ret = fsl_sai_set_bclk(cpu_dai, tx,
+ sai->bclk_ratio *
+ params_rate(params));
+ else
+ ret = fsl_sai_set_bclk(cpu_dai, tx,
+ slots * slot_width *
+ params_rate(params));
if (ret)
return ret;
@@ -444,42 +489,38 @@
val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+ /* Set to output mode to avoid tri-stated data pins */
+ if (tx)
+ val_cr4 |= FSL_SAI_CR4_CHMOD;
+
/*
* For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
* generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
- * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
- * error.
+ * RCR5(TCR5) for playback(capture), or there will be sync error.
*/
- if (!sai->is_slave_mode) {
- if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
- regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
- val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
- FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
- FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_TMR,
- ~0UL - ((1 << channels) - 1));
- } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
- regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
- val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
- FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
- FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_RMR,
- ~0UL - ((1 << channels) - 1));
- }
+ if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) {
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
+ FSL_SAI_CR4_CHMOD_MASK,
+ val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs),
+ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+ FSL_SAI_CR5_FBT_MASK, val_cr5);
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
- FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
+ FSL_SAI_CR3_TRCE_MASK,
+ FSL_SAI_CR3_TRCE((1 << pins) - 1));
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
+ FSL_SAI_CR4_CHMOD_MASK,
val_cr4);
- regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
- regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
+ regmap_write(sai->regmap, FSL_SAI_xMR(tx),
+ ~0UL - ((1 << min(channels, slots)) - 1));
return 0;
}
@@ -489,6 +530,10 @@
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
+ FSL_SAI_CR3_TRCE_MASK, 0);
if (!sai->is_slave_mode &&
sai->mclk_streams & BIT(substream->stream)) {
@@ -499,22 +544,58 @@
return 0;
}
+static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
+{
+ unsigned int ofs = sai->soc_data->reg_offset;
+ bool tx = dir == TX;
+ u32 xcsr, count = 100;
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
+ FSL_SAI_CSR_TERE, 0);
+
+ /* TERE will remain set till the end of current frame */
+ do {
+ udelay(10);
+ regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
+ } while (--count && xcsr & FSL_SAI_CSR_TERE);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
+ FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+
+ /*
+ * For sai master mode, after several open/close sai,
+ * there will be no frame clock, and can't recover
+ * anymore. Add software reset to fix this issue.
+ * This is a hardware bug, and will be fix in the
+ * next sai version.
+ */
+ if (!sai->is_slave_mode) {
+ /* Software Reset */
+ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0);
+ }
+}
static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 xcsr, count = 100;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;
+ u32 xcsr;
/*
* Asynchronous mode: Clear SYNC for both Tx and Rx.
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
- sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), FSL_SAI_CR2_SYNC,
+ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
/*
@@ -525,63 +606,56 @@
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ /*
+ * Enable the opposite direction for synchronous mode
+ * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx
+ * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx
+ *
+ * RM recommends to enable RE after TE for case 1 and to enable
+ * TE after RE for case 2, but we here may not always guarantee
+ * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables
+ * TE after RE, which is against what RM recommends but should
+ * be safe to do, judging by years of testing results.
+ */
+ if (fsl_sai_dir_is_synced(sai, adir))
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, 0);
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, 0);
/* Check if the opposite FRDE is also disabled */
- regmap_read(sai->regmap, FSL_SAI_xCSR(!tx), &xcsr);
- if (!(xcsr & FSL_SAI_CSR_FRDE)) {
- /* Disable both directions and reset their FIFOs */
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_TERE, 0);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_TERE, 0);
+ regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr);
- /* TERE will remain set till the end of current frame */
- do {
- udelay(10);
- regmap_read(sai->regmap, FSL_SAI_xCSR(tx), &xcsr);
- } while (--count && xcsr & FSL_SAI_CSR_TERE);
+ /*
+ * If opposite stream provides clocks for synchronous mode and
+ * it is inactive, disable it before disabling the current one
+ */
+ if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE))
+ fsl_sai_config_disable(sai, adir);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+ /*
+ * Disable current stream if either of:
+ * 1. current stream doesn't provide clocks for synchronous mode
+ * 2. current stream provides clocks for synchronous mode but no
+ * more stream is active.
+ */
+ if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE))
+ fsl_sai_config_disable(sai, dir);
- /*
- * For sai master mode, after several open/close sai,
- * there will be no frame clock, and can't recover
- * anymore. Add software reset to fix this issue.
- * This is a hardware bug, and will be fix in the
- * next sai version.
- */
- if (!sai->is_slave_mode) {
- /* Software Reset for both Tx and Rx */
- regmap_write(sai->regmap,
- FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap,
- FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- /* Clear SR bit to finish the reset */
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
- }
- }
break;
default:
return -EINVAL;
@@ -595,17 +669,17 @@
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct device *dev = &sai->pdev->dev;
int ret;
- ret = clk_prepare_enable(sai->bus_clk);
- if (ret) {
- dev_err(dev, "failed to enable bus clock: %d\n", ret);
- return ret;
- }
-
- regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
- FSL_SAI_CR3_TRCE);
+ /*
+ * EDMA controller needs period size to be a multiple of
+ * tx/rx maxburst
+ */
+ if (sai->soc_data->use_edma)
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ tx ? sai->dma_params_tx.maxburst :
+ sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
@@ -613,18 +687,8 @@
return ret;
}
-static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
- bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-
- regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
-
- clk_disable_unprepare(sai->bus_clk);
-}
-
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
@@ -632,23 +696,25 @@
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
.startup = fsl_sai_startup,
- .shutdown = fsl_sai_shutdown,
};
static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
/* Software Reset for both Tx and Rx */
- regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
- regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
- FSL_SAI_MAXBURST_TX * 2);
- regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR1(ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
+ sai->soc_data->fifo_depth - FSL_SAI_MAXBURST_TX);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR1(ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
FSL_SAI_MAXBURST_RX - 1);
snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
@@ -659,7 +725,7 @@
return 0;
}
-static struct snd_soc_dai_driver fsl_sai_dai = {
+static struct snd_soc_dai_driver fsl_sai_dai_template = {
.probe = fsl_sai_dai_probe,
.playback = {
.stream_name = "CPU-Playback",
@@ -686,42 +752,104 @@
.name = "fsl-sai",
};
-static struct reg_default fsl_sai_reg_defaults[] = {
- {FSL_SAI_TCR1, 0},
- {FSL_SAI_TCR2, 0},
- {FSL_SAI_TCR3, 0},
- {FSL_SAI_TCR4, 0},
- {FSL_SAI_TCR5, 0},
- {FSL_SAI_TDR, 0},
- {FSL_SAI_TMR, 0},
- {FSL_SAI_RCR1, 0},
- {FSL_SAI_RCR2, 0},
- {FSL_SAI_RCR3, 0},
- {FSL_SAI_RCR4, 0},
- {FSL_SAI_RCR5, 0},
- {FSL_SAI_RMR, 0},
+static struct reg_default fsl_sai_reg_defaults_ofs0[] = {
+ {FSL_SAI_TCR1(0), 0},
+ {FSL_SAI_TCR2(0), 0},
+ {FSL_SAI_TCR3(0), 0},
+ {FSL_SAI_TCR4(0), 0},
+ {FSL_SAI_TCR5(0), 0},
+ {FSL_SAI_TDR0, 0},
+ {FSL_SAI_TDR1, 0},
+ {FSL_SAI_TDR2, 0},
+ {FSL_SAI_TDR3, 0},
+ {FSL_SAI_TDR4, 0},
+ {FSL_SAI_TDR5, 0},
+ {FSL_SAI_TDR6, 0},
+ {FSL_SAI_TDR7, 0},
+ {FSL_SAI_TMR, 0},
+ {FSL_SAI_RCR1(0), 0},
+ {FSL_SAI_RCR2(0), 0},
+ {FSL_SAI_RCR3(0), 0},
+ {FSL_SAI_RCR4(0), 0},
+ {FSL_SAI_RCR5(0), 0},
+ {FSL_SAI_RMR, 0},
+};
+
+static struct reg_default fsl_sai_reg_defaults_ofs8[] = {
+ {FSL_SAI_TCR1(8), 0},
+ {FSL_SAI_TCR2(8), 0},
+ {FSL_SAI_TCR3(8), 0},
+ {FSL_SAI_TCR4(8), 0},
+ {FSL_SAI_TCR5(8), 0},
+ {FSL_SAI_TDR0, 0},
+ {FSL_SAI_TDR1, 0},
+ {FSL_SAI_TDR2, 0},
+ {FSL_SAI_TDR3, 0},
+ {FSL_SAI_TDR4, 0},
+ {FSL_SAI_TDR5, 0},
+ {FSL_SAI_TDR6, 0},
+ {FSL_SAI_TDR7, 0},
+ {FSL_SAI_TMR, 0},
+ {FSL_SAI_RCR1(8), 0},
+ {FSL_SAI_RCR2(8), 0},
+ {FSL_SAI_RCR3(8), 0},
+ {FSL_SAI_RCR4(8), 0},
+ {FSL_SAI_RCR5(8), 0},
+ {FSL_SAI_RMR, 0},
+ {FSL_SAI_MCTL, 0},
+ {FSL_SAI_MDIV, 0},
};
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
+ return true;
+
+ if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_TCR1:
- case FSL_SAI_TCR2:
- case FSL_SAI_TCR3:
- case FSL_SAI_TCR4:
- case FSL_SAI_TCR5:
- case FSL_SAI_TFR:
+ case FSL_SAI_TFR0:
+ case FSL_SAI_TFR1:
+ case FSL_SAI_TFR2:
+ case FSL_SAI_TFR3:
+ case FSL_SAI_TFR4:
+ case FSL_SAI_TFR5:
+ case FSL_SAI_TFR6:
+ case FSL_SAI_TFR7:
case FSL_SAI_TMR:
- case FSL_SAI_RCSR:
- case FSL_SAI_RCR1:
- case FSL_SAI_RCR2:
- case FSL_SAI_RCR3:
- case FSL_SAI_RCR4:
- case FSL_SAI_RCR5:
- case FSL_SAI_RDR:
- case FSL_SAI_RFR:
+ case FSL_SAI_RDR0:
+ case FSL_SAI_RDR1:
+ case FSL_SAI_RDR2:
+ case FSL_SAI_RDR3:
+ case FSL_SAI_RDR4:
+ case FSL_SAI_RDR5:
+ case FSL_SAI_RDR6:
+ case FSL_SAI_RDR7:
+ case FSL_SAI_RFR0:
+ case FSL_SAI_RFR1:
+ case FSL_SAI_RFR2:
+ case FSL_SAI_RFR3:
+ case FSL_SAI_RFR4:
+ case FSL_SAI_RFR5:
+ case FSL_SAI_RFR6:
+ case FSL_SAI_RFR7:
case FSL_SAI_RMR:
+ case FSL_SAI_MCTL:
+ case FSL_SAI_MDIV:
+ case FSL_SAI_VERID:
+ case FSL_SAI_PARAM:
+ case FSL_SAI_TTCTN:
+ case FSL_SAI_RTCTN:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_TBCTN:
+ case FSL_SAI_TTCAP:
+ case FSL_SAI_RTCTL:
+ case FSL_SAI_RBCTN:
+ case FSL_SAI_RTCAP:
return true;
default:
return false;
@@ -730,12 +858,41 @@
static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs))
+ return true;
+
+ /* Set VERID and PARAM be volatile for reading value in probe */
+ if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_RCSR:
- case FSL_SAI_TFR:
- case FSL_SAI_RFR:
- case FSL_SAI_RDR:
+ case FSL_SAI_TFR0:
+ case FSL_SAI_TFR1:
+ case FSL_SAI_TFR2:
+ case FSL_SAI_TFR3:
+ case FSL_SAI_TFR4:
+ case FSL_SAI_TFR5:
+ case FSL_SAI_TFR6:
+ case FSL_SAI_TFR7:
+ case FSL_SAI_RFR0:
+ case FSL_SAI_RFR1:
+ case FSL_SAI_RFR2:
+ case FSL_SAI_RFR3:
+ case FSL_SAI_RFR4:
+ case FSL_SAI_RFR5:
+ case FSL_SAI_RFR6:
+ case FSL_SAI_RFR7:
+ case FSL_SAI_RDR0:
+ case FSL_SAI_RDR1:
+ case FSL_SAI_RDR2:
+ case FSL_SAI_RDR3:
+ case FSL_SAI_RDR4:
+ case FSL_SAI_RDR5:
+ case FSL_SAI_RDR6:
+ case FSL_SAI_RDR7:
return true;
default:
return false;
@@ -744,41 +901,92 @@
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+
+ if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs))
+ return true;
+
+ if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs))
+ return true;
+
switch (reg) {
- case FSL_SAI_TCSR:
- case FSL_SAI_TCR1:
- case FSL_SAI_TCR2:
- case FSL_SAI_TCR3:
- case FSL_SAI_TCR4:
- case FSL_SAI_TCR5:
- case FSL_SAI_TDR:
+ case FSL_SAI_TDR0:
+ case FSL_SAI_TDR1:
+ case FSL_SAI_TDR2:
+ case FSL_SAI_TDR3:
+ case FSL_SAI_TDR4:
+ case FSL_SAI_TDR5:
+ case FSL_SAI_TDR6:
+ case FSL_SAI_TDR7:
case FSL_SAI_TMR:
- case FSL_SAI_RCSR:
- case FSL_SAI_RCR1:
- case FSL_SAI_RCR2:
- case FSL_SAI_RCR3:
- case FSL_SAI_RCR4:
- case FSL_SAI_RCR5:
case FSL_SAI_RMR:
+ case FSL_SAI_MCTL:
+ case FSL_SAI_MDIV:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_RTCTL:
return true;
default:
return false;
}
}
-static const struct regmap_config fsl_sai_regmap_config = {
+static struct regmap_config fsl_sai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
+ .fast_io = true,
.max_register = FSL_SAI_RMR,
- .reg_defaults = fsl_sai_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
+ .reg_defaults = fsl_sai_reg_defaults_ofs0,
+ .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults_ofs0),
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
.cache_type = REGCACHE_FLAT,
};
+
+static int fsl_sai_check_version(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned char ofs = sai->soc_data->reg_offset;
+ unsigned int val;
+ int ret;
+
+ if (FSL_SAI_TCSR(ofs) == FSL_SAI_VERID)
+ return 0;
+
+ ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "VERID: 0x%016X\n", val);
+
+ sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >>
+ FSL_SAI_VERID_MAJOR_SHIFT;
+ sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >>
+ FSL_SAI_VERID_MINOR_SHIFT;
+ sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
+
+ ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "PARAM: 0x%016X\n", val);
+
+ /* Max slots per frame, power of 2 */
+ sai->param.slot_num = 1 <<
+ ((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT);
+
+ /* Words per fifo, power of 2 */
+ sai->param.fifo_depth = 1 <<
+ ((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT);
+
+ /* Number of datalines implemented */
+ sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK;
+
+ return 0;
+}
static int fsl_sai_probe(struct platform_device *pdev)
{
@@ -796,10 +1004,7 @@
return -ENOMEM;
sai->pdev = pdev;
-
- if (of_device_is_compatible(np, "fsl,imx6sx-sai") ||
- of_device_is_compatible(np, "fsl,imx6ul-sai"))
- sai->sai_on_imx = true;
+ sai->soc_data = of_device_get_match_data(&pdev->dev);
sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
@@ -808,11 +1013,18 @@
if (IS_ERR(base))
return PTR_ERR(base);
+ if (sai->soc_data->reg_offset == 8) {
+ fsl_sai_regmap_config.reg_defaults = fsl_sai_reg_defaults_ofs8;
+ fsl_sai_regmap_config.max_register = FSL_SAI_MDIV;
+ fsl_sai_regmap_config.num_reg_defaults =
+ ARRAY_SIZE(fsl_sai_reg_defaults_ofs8);
+ }
+
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
"bus", base, &fsl_sai_regmap_config);
/* Compatible with old DTB cases */
- if (IS_ERR(sai->regmap))
+ if (IS_ERR(sai->regmap) && PTR_ERR(sai->regmap) != -EPROBE_DEFER)
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
"sai", base, &fsl_sai_regmap_config);
if (IS_ERR(sai->regmap)) {
@@ -840,23 +1052,25 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai);
+ ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, IRQF_SHARED,
+ np->name, sai);
if (ret) {
dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
return ret;
}
+ memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template,
+ sizeof(fsl_sai_dai_template));
+
/* Sync Tx with Rx as default by following old DT binding */
sai->synchronous[RX] = true;
sai->synchronous[TX] = false;
- fsl_sai_dai.symmetric_rates = 1;
- fsl_sai_dai.symmetric_channels = 1;
- fsl_sai_dai.symmetric_samplebits = 1;
+ sai->cpu_dai_drv.symmetric_rates = 1;
+ sai->cpu_dai_drv.symmetric_channels = 1;
+ sai->cpu_dai_drv.symmetric_samplebits = 1;
if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
of_find_property(np, "fsl,sai-asynchronous", NULL)) {
@@ -873,9 +1087,9 @@
/* Discard all settings for asynchronous mode */
sai->synchronous[RX] = false;
sai->synchronous[TX] = false;
- fsl_sai_dai.symmetric_rates = 0;
- fsl_sai_dai.symmetric_channels = 0;
- fsl_sai_dai.symmetric_samplebits = 0;
+ sai->cpu_dai_drv.symmetric_rates = 0;
+ sai->cpu_dai_drv.symmetric_channels = 0;
+ sai->cpu_dai_drv.symmetric_samplebits = 0;
}
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
@@ -894,63 +1108,183 @@
MCLK_DIR(index));
}
- sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
- sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
+ sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0;
+ sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0;
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
platform_set_drvdata(pdev, sai);
- ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
- &fsl_sai_dai, 1);
- if (ret)
- return ret;
+ /* Get sai version */
+ ret = fsl_sai_check_version(&pdev->dev);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret);
- if (sai->sai_on_imx)
- return imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
- else
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ /* Select MCLK direction */
+ if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+ sai->verid.major >= 3 && sai->verid.minor >= 1) {
+ regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
+ FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ regcache_cache_only(sai->regmap, true);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
+ &sai->cpu_dai_drv, 1);
+ if (ret)
+ goto err_pm_disable;
+
+ if (sai->soc_data->use_imx_pcm) {
+ ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
+ if (ret)
+ goto err_pm_disable;
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ return ret;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
}
-static const struct of_device_id fsl_sai_ids[] = {
- { .compatible = "fsl,vf610-sai", },
- { .compatible = "fsl,imx6sx-sai", },
- { .compatible = "fsl,imx6ul-sai", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, fsl_sai_ids);
-
-#ifdef CONFIG_PM_SLEEP
-static int fsl_sai_suspend(struct device *dev)
+static int fsl_sai_remove(struct platform_device *pdev)
{
- struct fsl_sai *sai = dev_get_drvdata(dev);
-
- regcache_cache_only(sai->regmap, true);
- regcache_mark_dirty(sai->regmap);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
-static int fsl_sai_resume(struct device *dev)
+static const struct fsl_sai_soc_data fsl_sai_vf610_data = {
+ .use_imx_pcm = false,
+ .use_edma = false,
+ .fifo_depth = 32,
+ .reg_offset = 0,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 32,
+ .reg_offset = 0,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 16,
+ .reg_offset = 8,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
+ .use_imx_pcm = true,
+ .use_edma = false,
+ .fifo_depth = 128,
+ .reg_offset = 8,
+};
+
+static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = {
+ .use_imx_pcm = true,
+ .use_edma = true,
+ .fifo_depth = 64,
+ .reg_offset = 0,
+};
+
+static const struct of_device_id fsl_sai_ids[] = {
+ { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
+ { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
+ { .compatible = "fsl,imx6ul-sai", .data = &fsl_sai_imx6sx_data },
+ { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
+ { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
+ { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
+
+#ifdef CONFIG_PM
+static int fsl_sai_runtime_suspend(struct device *dev)
{
struct fsl_sai *sai = dev_get_drvdata(dev);
- regcache_cache_only(sai->regmap, false);
- regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
- regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- usleep_range(1000, 2000);
- regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
- regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
- return regcache_sync(sai->regmap);
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
+
+ clk_disable_unprepare(sai->bus_clk);
+
+ regcache_cache_only(sai->regmap, true);
+
+ return 0;
}
-#endif /* CONFIG_PM_SLEEP */
+
+static int fsl_sai_runtime_resume(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int ofs = sai->soc_data->reg_offset;
+ int ret;
+
+ ret = clk_prepare_enable(sai->bus_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable bus clock: %d\n", ret);
+ return ret;
+ }
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK)) {
+ ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[1]]);
+ if (ret)
+ goto disable_bus_clk;
+ }
+
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) {
+ ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[0]]);
+ if (ret)
+ goto disable_tx_clk;
+ }
+
+ regcache_cache_only(sai->regmap, false);
+ regcache_mark_dirty(sai->regmap);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR);
+ usleep_range(1000, 2000);
+ regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
+
+ ret = regcache_sync(sai->regmap);
+ if (ret)
+ goto disable_rx_clk;
+
+ return 0;
+
+disable_rx_clk:
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]);
+disable_tx_clk:
+ if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_PLAYBACK))
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[1]]);
+disable_bus_clk:
+ clk_disable_unprepare(sai->bus_clk);
+
+ return ret;
+}
+#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_sai_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+ SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend,
+ fsl_sai_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
+ .remove = fsl_sai_remove,
.driver = {
.name = "fsl-sai",
.pm = &fsl_sai_pm_ops,
--
Gitblit v1.6.2