diff options
Diffstat (limited to 'sound/soc/fsl/fsl_sai.c')
| -rw-r--r-- | sound/soc/fsl/fsl_sai.c | 538 |
1 files changed, 365 insertions, 173 deletions
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index cdd3fa83070..c5a0e8af822 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -15,54 +15,112 @@ #include <linux/dmaengine.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> #include "fsl_sai.h" +#include "imx-pcm.h" -static inline u32 sai_readl(struct fsl_sai *sai, - const void __iomem *addr) +#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ + FSL_SAI_CSR_FEIE) + +static irqreturn_t fsl_sai_isr(int irq, void *devid) { - u32 val; + struct fsl_sai *sai = (struct fsl_sai *)devid; + struct device *dev = &sai->pdev->dev; + u32 flags, xcsr, mask; + bool irq_none = true; + + /* + * Both IRQ status bits and IRQ mask bits are in the xCSR but + * different shifts. And we here create a mask only for those + * IRQs that we activated. + */ + mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT; + + /* Tx IRQ */ + regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr); + flags = xcsr & mask; + + if (flags) + irq_none = false; + else + goto irq_rx; - val = __raw_readl(addr); + if (flags & FSL_SAI_CSR_WSF) + dev_dbg(dev, "isr: Start of Tx word detected\n"); - if (likely(sai->big_endian_regs)) - val = be32_to_cpu(val); - else - val = le32_to_cpu(val); - rmb(); + if (flags & FSL_SAI_CSR_SEF) + dev_warn(dev, "isr: Tx Frame sync error detected\n"); - return val; -} + if (flags & FSL_SAI_CSR_FEF) { + dev_warn(dev, "isr: Transmit underrun detected\n"); + /* FIFO reset for safety */ + xcsr |= FSL_SAI_CSR_FR; + } -static inline void sai_writel(struct fsl_sai *sai, - u32 val, void __iomem *addr) -{ - wmb(); - if (likely(sai->big_endian_regs)) - val = cpu_to_be32(val); + if (flags & FSL_SAI_CSR_FWF) + dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n"); + + if (flags & FSL_SAI_CSR_FRF) + dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); + + flags &= FSL_SAI_CSR_xF_W_MASK; + xcsr &= ~FSL_SAI_CSR_xF_MASK; + + if (flags) + regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr); + +irq_rx: + /* Rx IRQ */ + regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr); + flags = xcsr & mask; + + if (flags) + irq_none = false; else - val = cpu_to_le32(val); + goto out; + + if (flags & FSL_SAI_CSR_WSF) + 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"); + + if (flags & FSL_SAI_CSR_FEF) { + dev_warn(dev, "isr: Receive overflow detected\n"); + /* FIFO reset for safety */ + xcsr |= FSL_SAI_CSR_FR; + } + + if (flags & FSL_SAI_CSR_FWF) + dev_dbg(dev, "isr: Enabled receive FIFO is full\n"); - __raw_writel(val, addr); + if (flags & FSL_SAI_CSR_FRF) + dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); + + flags &= FSL_SAI_CSR_xF_W_MASK; + xcsr &= ~FSL_SAI_CSR_xF_MASK; + + if (flags) + regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr); + +out: + if (irq_none) + return IRQ_NONE; + else + return IRQ_HANDLED; } 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); - u32 val_cr2, reg_cr2; - - if (fsl_dir == FSL_FMT_TRANSMITTER) - reg_cr2 = FSL_SAI_TCR2; - else - reg_cr2 = FSL_SAI_RCR2; - - val_cr2 = sai_readl(sai, sai->base + reg_cr2); - val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + bool tx = fsl_dir == FSL_FMT_TRANSMITTER; + u32 val_cr2 = 0; switch (clk_id) { case FSL_SAI_CLK_BUS: @@ -81,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - sai_writel(sai, val_cr2, sai->base + reg_cr2); + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx), + FSL_SAI_CR2_MSEL_MASK, val_cr2); return 0; } @@ -89,32 +148,22 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; if (dir == SND_SOC_CLOCK_IN) return 0; - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); - goto err_clk; + return ret; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_RECEIVER); - if (ret) { + if (ret) dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); - goto err_clk; - } - -err_clk: - clk_disable_unprepare(sai->clk); return ret; } @@ -123,95 +172,118 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 val_cr2, val_cr4, reg_cr2, reg_cr4; - - if (fsl_dir == FSL_FMT_TRANSMITTER) { - reg_cr2 = FSL_SAI_TCR2; - reg_cr4 = FSL_SAI_TCR4; - } else { - reg_cr2 = FSL_SAI_RCR2; - reg_cr4 = FSL_SAI_RCR4; - } + bool tx = fsl_dir == FSL_FMT_TRANSMITTER; + u32 val_cr2 = 0, val_cr4 = 0; - val_cr2 = sai_readl(sai, sai->base + reg_cr2); - val_cr4 = sai_readl(sai, sai->base + reg_cr4); - - if (sai->big_endian_data) - val_cr4 &= ~FSL_SAI_CR4_MF; - else + if (!sai->big_endian_data) val_cr4 |= FSL_SAI_CR4_MF; + /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: + /* + * Frame low, 1clk before data, one word length for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 |= FSL_SAI_CR2_BCP; + val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* + * Frame high, one word length for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 |= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_DSP_A: + /* + * Frame high, 1clk before data, one bit for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 |= FSL_SAI_CR4_FSE; + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * Frame high, one bit for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 |= FSL_SAI_CR2_BCP; + sai->is_dsp_mode = true; break; + case SND_SOC_DAIFMT_RIGHT_J: + /* To be done */ default: return -EINVAL; } + /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: - val_cr4 |= FSL_SAI_CR4_FSP; - val_cr2 &= ~FSL_SAI_CR2_BCP; + /* Invert both clocks */ + val_cr2 ^= FSL_SAI_CR2_BCP; + val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_IB_NF: - val_cr4 &= ~FSL_SAI_CR4_FSP; - val_cr2 &= ~FSL_SAI_CR2_BCP; + /* Invert bit clock */ + val_cr2 ^= FSL_SAI_CR2_BCP; break; case SND_SOC_DAIFMT_NB_IF: - val_cr4 |= FSL_SAI_CR4_FSP; - val_cr2 |= FSL_SAI_CR2_BCP; + /* Invert frame clock */ + val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_NB_NF: - val_cr4 &= ~FSL_SAI_CR4_FSP; - val_cr2 |= FSL_SAI_CR2_BCP; + /* Nothing to do for both normal cases */ break; default: return -EINVAL; } + /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; case SND_SOC_DAIFMT_CBM_CFM: - val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; - val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBS_CFM: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; default: return -EINVAL; } - sai_writel(sai, val_cr2, sai->base + reg_cr2); - sai_writel(sai, val_cr4, sai->base + reg_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx), + FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2); + regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), + FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE | + FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4); return 0; } static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); - goto err_clk; + return ret; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); - if (ret) { + if (ret) dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); - goto err_clk; - } - -err_clk: - clk_disable_unprepare(sai->clk); return ret; } @@ -221,45 +293,31 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(params)); + u32 val_cr4 = 0, val_cr5 = 0; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - reg_cr4 = FSL_SAI_TCR4; - reg_cr5 = FSL_SAI_TCR5; - reg_mr = FSL_SAI_TMR; - } else { - reg_cr4 = FSL_SAI_RCR4; - reg_cr5 = FSL_SAI_RCR5; - reg_mr = FSL_SAI_RMR; - } - - val_cr4 = sai_readl(sai, sai->base + reg_cr4); - val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; - val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; - - val_cr5 = sai_readl(sai, sai->base + reg_cr5); - val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; - val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; - val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + if (!sai->is_dsp_mode) + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); - val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) val_cr5 |= FSL_SAI_CR5_FBT(0); else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); - val_mr = ~0UL - ((1 << channels) - 1); - sai_writel(sai, val_cr4, sai->base + reg_cr4); - sai_writel(sai, val_cr5, sai->base + reg_cr5); - sai_writel(sai, val_mr, sai->base + reg_mr); + regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx), + 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)); return 0; } @@ -268,57 +326,56 @@ 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); - u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3; - - val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2); - val_cr2 &= ~FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2); - - val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2); - val_cr2 |= FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2); - - tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); - rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - tcsr |= FSL_SAI_CSR_FRDE; - rcsr &= ~FSL_SAI_CSR_FRDE; - reg_cr3 = FSL_SAI_TCR3; - } else { - rcsr |= FSL_SAI_CSR_FRDE; - tcsr &= ~FSL_SAI_CSR_FRDE; - reg_cr3 = FSL_SAI_RCR3; - } - - val_cr3 = sai_readl(sai, sai->base + reg_cr3); - + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 tcsr, rcsr; + + /* + * The transmitter bit clock and frame sync are to be + * used by both the transmitter and receiver. + */ + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + ~FSL_SAI_CR2_SYNC); + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, + FSL_SAI_CR2_SYNC); + + regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr); + regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); + + /* + * It is recommended that the transmitter is the last enabled + * and the first disabled. + */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - tcsr |= FSL_SAI_CSR_TERE; - rcsr |= FSL_SAI_CSR_TERE; - val_cr3 |= FSL_SAI_CR3_TRCE; + if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) { + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, + FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, + FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); + } - sai_writel(sai, val_cr3, sai->base + reg_cr3); - sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); - sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), + FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS); + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), + FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE); break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { - tcsr &= ~FSL_SAI_CSR_TERE; - rcsr &= ~FSL_SAI_CSR_TERE; + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), + FSL_SAI_CSR_FRDE, 0); + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), + FSL_SAI_CSR_xIE_MASK, 0); + + /* Check if the opposite FRDE is also disabled */ + if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) { + 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); } - - val_cr3 &= ~FSL_SAI_CR3_TRCE; - - sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); - sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); - sai_writel(sai, val_cr3, sai->base + reg_cr3); break; default: return -EINVAL; @@ -331,16 +388,31 @@ static int fsl_sai_startup(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; + struct device *dev = &sai->pdev->dev; + int ret; - return clk_prepare_enable(sai->clk); + 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); + + return 0; } 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; - clk_disable_unprepare(sai->clk); + 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 = { @@ -355,18 +427,13 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); - int ret; - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - - sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); - sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); - sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); - sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); - - clk_disable_unprepare(sai->clk); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + 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, + FSL_SAI_MAXBURST_RX - 1); snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx, &sai->dma_params_rx); @@ -397,26 +464,150 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", }; +static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) +{ + 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_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_RMR: + return true; + default: + return false; + } +} + +static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TFR: + case FSL_SAI_RFR: + case FSL_SAI_TDR: + case FSL_SAI_RDR: + return true; + default: + return false; + } + +} + +static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) +{ + 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_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: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_sai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FSL_SAI_RMR, + .readable_reg = fsl_sai_readable_reg, + .volatile_reg = fsl_sai_volatile_reg, + .writeable_reg = fsl_sai_writeable_reg, +}; + static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; struct resource *res; - int ret; + void __iomem *base; + char tmp[8]; + int irq, ret, i; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; + sai->pdev = pdev; + + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) + sai->sai_on_imx = true; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + if (sai->big_endian_regs) + fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sai->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(sai->base)) - return PTR_ERR(sai->base); - - sai->clk = devm_clk_get(&pdev->dev, "sai"); - if (IS_ERR(sai->clk)) { - dev_err(&pdev->dev, "Cannot get SAI's clock\n"); - return PTR_ERR(sai->clk); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + 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)) + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "sai", base, &fsl_sai_regmap_config); + if (IS_ERR(sai->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(sai->regmap); + } + + /* No error out for old DTB cases but only mark the clock NULL */ + sai->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(sai->bus_clk)) { + dev_err(&pdev->dev, "failed to get bus clock: %ld\n", + PTR_ERR(sai->bus_clk)); + sai->bus_clk = NULL; + } + + for (i = 0; i < FSL_SAI_MCLK_MAX; i++) { + sprintf(tmp, "mclk%d", i + 1); + sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(sai->mclk_clk[i])) { + dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", + i + 1, PTR_ERR(sai->mclk_clk[i])); + sai->mclk_clk[i] = NULL; + } + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u\n", irq); + return ret; } sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; @@ -424,9 +615,6 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; - sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); - sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); - platform_set_drvdata(pdev, sai); ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, @@ -434,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + if (sai->sai_on_imx) + return imx_pcm_dma_init(pdev); + else + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); } static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", }, + { .compatible = "fsl,imx6sx-sai", }, { /* sentinel */ } }; |
