diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/fsl-sai.txt | 11 | ||||
-rw-r--r-- | sound/soc/fsl/Kconfig | 75 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai.c | 32 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_sai.c | 255 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_sai.h | 16 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 186 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_spdif.h | 14 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 996 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.h | 62 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi_dbg.c | 163 | ||||
-rw-r--r-- | sound/soc/fsl/imx-pcm-dma.c | 1 | ||||
-rw-r--r-- | sound/soc/soc-devres.c | 1 |
13 files changed, 977 insertions, 838 deletions
diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 98611a6761c0..0f4e23828190 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -7,10 +7,11 @@ codec/DSP interfaces. Required properties: -- compatible: Compatible list, contains "fsl,vf610-sai". +- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai". - reg: Offset and length of the register set for the device. - clocks: Must contain an entry for each entry in clock-names. -- clock-names : Must include the "sai" entry. +- clock-names : Must include the "bus" for register access and "mclk1" "mclk2" + "mclk3" for bit clock and frame clock providing. - dmas : Generic dma devicetree binding as described in Documentation/devicetree/bindings/dma/dma.txt. - dma-names : Two dmas have to be defined, "tx" and "rx". @@ -30,8 +31,10 @@ sai2: sai@40031000 { reg = <0x40031000 0x1000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai2_1>; - clocks = <&clks VF610_CLK_SAI2>; - clock-names = "sai"; + clocks = <&clks VF610_CLK_PLATFORM_BUS>, + <&clks VF610_CLK_SAI2>, + <&clks 0>, <&clks 0>; + clock-names = "bus", "mclk1", "mclk2", "mclk3"; dma-names = "tx", "rx"; dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index f4069d0ef811..d262ec0653d3 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,30 +1,77 @@ +menu "SoC Audio for Freescale CPUs" + +comment "Common SoC Audio options for Freescale CPUs:" + config SND_SOC_FSL_SAI - tristate + tristate "Synchronous Audio Interface (SAI) module support" select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y if you want to add Synchronous Audio Interface (SAI) + support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. config SND_SOC_FSL_SSI - tristate + tristate "Synchronous Serial Interface module support" + select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n + select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC + help + Say Y if you want to add Synchronous Serial Interface (SSI) + support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. config SND_SOC_FSL_SPDIF - tristate + tristate "Sony/Philips Digital Interface module support" select REGMAP_MMIO + select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n + select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC + help + Say Y if you want to add Sony/Philips Digital Interface (SPDIF) + support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. config SND_SOC_FSL_ESAI - tristate + tristate "Enhanced Serial Audio Interface (ESAI) module support" select REGMAP_MMIO select SND_SOC_FSL_UTILS + help + Say Y if you want to add Enhanced Synchronous Audio Interface + (ESAI) support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. config SND_SOC_FSL_UTILS tristate -menuconfig SND_POWERPC_SOC +config SND_SOC_IMX_PCM_DMA + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + +config SND_SOC_IMX_AUDMUX + tristate "Digital Audio Mux module support" + help + Say Y if you want to add Digital Audio Mux (AUDMUX) support + for the ARM i.MX CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + +config SND_POWERPC_SOC tristate "SoC Audio for Freescale PowerPC CPUs" depends on FSL_SOC || PPC_MPC52xx help Say Y or M if you want to add support for codecs attached to the PowerPC CPUs. +config SND_IMX_SOC + tristate "SoC Audio for Freescale i.MX CPUs" + depends on ARCH_MXC || COMPILE_TEST + help + Say Y or M if you want to add support for codecs attached to + the i.MX CPUs. + if SND_POWERPC_SOC config SND_MPC52xx_DMA @@ -33,6 +80,8 @@ config SND_MPC52xx_DMA config SND_SOC_POWERPC_DMA tristate +comment "SoC Audio support for Freescale PPC boards:" + config SND_SOC_MPC8610_HPCD tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" # I2C is necessary for the CS4270 driver @@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA endif # SND_POWERPC_SOC -menuconfig SND_IMX_SOC - tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC || COMPILE_TEST - help - Say Y or M if you want to add support for codecs attached to - the i.MX CPUs. - if SND_IMX_SOC config SND_SOC_IMX_SSI @@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ tristate select FIQ -config SND_SOC_IMX_PCM_DMA - tristate - select SND_SOC_GENERIC_DMAENGINE_PCM - -config SND_SOC_IMX_AUDMUX - tristate +comment "SoC Audio support for Freescale i.MX boards:" config SND_MXC_SOC_WM1133_EV1 tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" @@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783 select SND_SOC_IMX_PCM_DMA endif # SND_IMX_SOC + +endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index b12ad4b9b4da..db254e358c18 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-sai-objs := fsl_sai.o -snd-soc-fsl-ssi-objs := fsl_ssi.o +snd-soc-fsl-ssi-y := fsl_ssi.o +snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 496ce2eb2f1f..d719caf26dc2 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -39,6 +39,8 @@ * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @hck_rate: clock rate of desired HCKx clock + * @sck_rate: clock rate of desired SCKx clock + * @hck_dir: the direction of HCKx pads * @sck_div: if using PSR/PM dividers for SCKx clock * @slave_mode: if fully using DAI slave mode * @synchronous: if using tx/rx synchronous mode @@ -55,6 +57,8 @@ struct fsl_esai { u32 fifo_depth; u32 slot_width; u32 hck_rate[2]; + u32 sck_rate[2]; + bool hck_dir[2]; bool sck_div[2]; bool slave_mode; bool synchronous; @@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, struct clk *clksrc = esai_priv->extalclk; bool tx = clk_id <= ESAI_HCKT_EXTAL; bool in = dir == SND_SOC_CLOCK_IN; - u32 ret, ratio, ecr = 0; + u32 ratio, ecr = 0; unsigned long clk_rate; + int ret; + + /* Bypass divider settings if the requirement doesn't change */ + if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx]) + return 0; /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ esai_priv->sck_div[tx] = true; @@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, esai_priv->sck_div[tx] = false; out: + esai_priv->hck_dir[tx] = dir; esai_priv->hck_rate[tx] = freq; regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, @@ -294,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); u32 hck_rate = esai_priv->hck_rate[tx]; u32 sub, ratio = hck_rate / freq; + int ret; - /* Don't apply for fully slave mode*/ - if (esai_priv->slave_mode) + /* Don't apply for fully slave mode or unchanged bclk */ + if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq) return 0; if (ratio * freq > hck_rate) @@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; } - return fsl_esai_divisor_cal(dai, tx, ratio, true, + ret = fsl_esai_divisor_cal(dai, tx, ratio, true, esai_priv->sck_div[tx] ? 0 : ratio); + if (ret) + return ret; + + /* Save current bclk rate */ + esai_priv->sck_rate[tx] = freq; + + return 0; } static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, @@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int fsl_esai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + int ret; /* * Some platforms might use the same bit to gate all three or two of @@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(params); - u32 bclk, mask, val, ret; + u32 bclk, mask, val; + int ret; bclk = params_rate(params) * esai_priv->slot_width * 2; @@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev) static const struct of_device_id fsl_esai_dt_ids[] = { { .compatible = "fsl,imx35-esai", }, + { .compatible = "fsl,vf610-esai", }, {} }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 56da8c8c5960..c5a0e8af8226 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -22,6 +22,7 @@ #include <sound/pcm_params.h> #include "fsl_sai.h" +#include "imx-pcm.h" #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) @@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) { struct fsl_sai *sai = (struct fsl_sai *)devid; struct device *dev = &sai->pdev->dev; - u32 xcsr, mask; + u32 flags, xcsr, mask; + bool irq_none = true; - /* Only handle those what we enabled */ + /* + * 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); - xcsr &= mask; + flags = xcsr & mask; + + if (flags) + irq_none = false; + else + goto irq_rx; - if (xcsr & FSL_SAI_CSR_WSF) + if (flags & FSL_SAI_CSR_WSF) dev_dbg(dev, "isr: Start of Tx word detected\n"); - if (xcsr & FSL_SAI_CSR_SEF) + if (flags & FSL_SAI_CSR_SEF) dev_warn(dev, "isr: Tx Frame sync error detected\n"); - if (xcsr & FSL_SAI_CSR_FEF) { + if (flags & FSL_SAI_CSR_FEF) { dev_warn(dev, "isr: Transmit underrun detected\n"); /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; } - if (xcsr & FSL_SAI_CSR_FWF) + if (flags & FSL_SAI_CSR_FWF) dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n"); - if (xcsr & FSL_SAI_CSR_FRF) + if (flags & FSL_SAI_CSR_FRF) dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); - regmap_update_bits(sai->regmap, FSL_SAI_TCSR, - FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); + 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); - xcsr &= mask; + flags = xcsr & mask; - if (xcsr & FSL_SAI_CSR_WSF) + if (flags) + irq_none = false; + else + goto out; + + if (flags & FSL_SAI_CSR_WSF) dev_dbg(dev, "isr: Start of Rx word detected\n"); - if (xcsr & FSL_SAI_CSR_SEF) + if (flags & FSL_SAI_CSR_SEF) dev_warn(dev, "isr: Rx Frame sync error detected\n"); - if (xcsr & FSL_SAI_CSR_FEF) { + if (flags & FSL_SAI_CSR_FEF) { dev_warn(dev, "isr: Receive overflow detected\n"); /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; } - if (xcsr & FSL_SAI_CSR_FWF) + if (flags & FSL_SAI_CSR_FWF) dev_dbg(dev, "isr: Enabled receive FIFO is full\n"); - if (xcsr & FSL_SAI_CSR_FRF) + if (flags & FSL_SAI_CSR_FRF) dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR, - FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); + flags &= FSL_SAI_CSR_xF_W_MASK; + xcsr &= ~FSL_SAI_CSR_xF_MASK; + + if (flags) + regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr); - return IRQ_HANDLED; +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; - - regmap_read(sai->regmap, reg_cr2, &val_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: @@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - regmap_write(sai->regmap, reg_cr2, val_cr2); + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx), + FSL_SAI_CR2_MSEL_MASK, val_cr2); return 0; } @@ -152,22 +172,10 @@ 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; - regmap_read(sai->regmap, reg_cr2, &val_cr2); - regmap_read(sai->regmap, reg_cr4, &val_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 */ @@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, * frame sync asserts with the first bit of the frame. */ val_cr2 |= FSL_SAI_CR2_BCP; - val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); break; case SND_SOC_DAIFMT_DSP_A: /* @@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, * data word. */ val_cr2 |= FSL_SAI_CR2_BCP; - val_cr4 &= ~FSL_SAI_CR4_FSP; val_cr4 |= FSL_SAI_CR4_FSE; sai->is_dsp_mode = true; break; @@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, * frame sync asserts with the first bit of the frame. */ val_cr2 |= FSL_SAI_CR2_BCP; - val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); sai->is_dsp_mode = true; break; case SND_SOC_DAIFMT_RIGHT_J: @@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, 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; - val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; case SND_SOC_DAIFMT_CBM_CFS: - val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; default: return -EINVAL; } - regmap_write(sai->regmap, reg_cr2, val_cr2); - regmap_write(sai->regmap, reg_cr4, val_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; } @@ -289,29 +293,10 @@ 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)); - - 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; - } - - regmap_read(sai->regmap, reg_cr4, &val_cr4); - regmap_read(sai->regmap, reg_cr4, &val_cr5); - - val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; - val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; - - val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; - val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; - val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + u32 val_cr4 = 0, val_cr5 = 0; if (!sai->is_dsp_mode) val_cr4 |= FSL_SAI_CR4_SYWD(word_width); @@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, 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); - regmap_write(sai->regmap, reg_cr4, val_cr4); - regmap_write(sai->regmap, reg_cr5, val_cr5); - regmap_write(sai->regmap, reg_mr, val_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; } @@ -339,6 +326,7 @@ 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); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 tcsr, rcsr; /* @@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr); regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - tcsr |= FSL_SAI_CSR_FRDE; - rcsr &= ~FSL_SAI_CSR_FRDE; - } else { - rcsr |= FSL_SAI_CSR_FRDE; - tcsr &= ~FSL_SAI_CSR_FRDE; - } - /* * It is recommended that the transmitter is the last enabled * and the first disabled. @@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int 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; + 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); + } - regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); - regmap_write(sai->regmap, FSL_SAI_TCSR, 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); } - - regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); - regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); break; default: return -EINVAL; @@ -397,14 +388,17 @@ 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); - u32 reg; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct device *dev = &sai->pdev->dev; + int ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - reg = FSL_SAI_TCR3; - else - reg = FSL_SAI_RCR3; + 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, reg, FSL_SAI_CR3_TRCE, + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, FSL_SAI_CR3_TRCE); return 0; @@ -414,15 +408,11 @@ 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); - u32 reg; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - reg = FSL_SAI_TCR3; - else - reg = FSL_SAI_RCR3; + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0); - regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, - ~FSL_SAI_CR3_TRCE); + clk_disable_unprepare(sai->bus_clk); } static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { @@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); - regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS); + 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, @@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev) struct fsl_sai *sai; struct resource *res; void __iomem *base; - int irq, ret; + char tmp[8]; + int irq, ret, i; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev) 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; @@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev) return PTR_ERR(base); sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, - "sai", base, &fsl_sai_regmap_config); + "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); @@ -605,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 */ } }; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index a264185c7138..0e6c9f595d75 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -35,6 +35,16 @@ #define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */ #define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ +#define FSL_SAI_xCSR(tx) (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR) +#define FSL_SAI_xCR1(tx) (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1) +#define FSL_SAI_xCR2(tx) (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2) +#define FSL_SAI_xCR3(tx) (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3) +#define FSL_SAI_xCR4(tx) (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4) +#define FSL_SAI_xCR5(tx) (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5) +#define FSL_SAI_xDR(tx) (tx ? FSL_SAI_TDR : FSL_SAI_RDR) +#define FSL_SAI_xFR(tx) (tx ? FSL_SAI_TFR : FSL_SAI_RFR) +#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR) + /* SAI Transmit/Recieve Control Register */ #define FSL_SAI_CSR_TERE BIT(31) #define FSL_SAI_CSR_FR BIT(25) @@ -48,6 +58,7 @@ #define FSL_SAI_CSR_FWF BIT(17) #define FSL_SAI_CSR_FRF BIT(16) #define FSL_SAI_CSR_xIE_SHIFT 8 +#define FSL_SAI_CSR_xIE_MASK (0x1f << FSL_SAI_CSR_xIE_SHIFT) #define FSL_SAI_CSR_WSIE BIT(12) #define FSL_SAI_CSR_SEIE BIT(11) #define FSL_SAI_CSR_FEIE BIT(10) @@ -108,6 +119,8 @@ #define FSL_SAI_CLK_MAST2 2 #define FSL_SAI_CLK_MAST3 3 +#define FSL_SAI_MCLK_MAX 3 + /* SAI data transfer numbers per DMA request */ #define FSL_SAI_MAXBURST_TX 6 #define FSL_SAI_MAXBURST_RX 6 @@ -115,10 +128,13 @@ struct fsl_sai { struct platform_device *pdev; struct regmap *regmap; + struct clk *bus_clk; + struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; bool big_endian_regs; bool big_endian_data; bool is_dsp_mode; + bool sai_on_imx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 6452ca83d889..b912d45a2a4c 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -13,18 +13,18 @@ * kind, whether express or implied. */ -#include <linux/module.h> +#include <linux/bitrev.h> #include <linux/clk.h> #include <linux/clk-private.h> -#include <linux/bitrev.h> -#include <linux/regmap.h> +#include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/regmap.h> #include <sound/asoundef.h> -#include <sound/soc.h> #include <sound/dmaengine_pcm.h> +#include <sound/soc.h> #include "fsl_spdif.h" #include "imx-pcm.h" @@ -69,17 +69,42 @@ struct spdif_mixer_control { u32 ready_buf; }; +/** + * fsl_spdif_priv: Freescale SPDIF private data + * + * @fsl_spdif_control: SPDIF control data + * @cpu_dai_drv: cpu dai driver + * @pdev: platform device pointer + * @regmap: regmap handler + * @dpll_locked: dpll lock flag + * @txrate: the best rates for playback + * @txclk_df: STC_TXCLK_DF dividers value for playback + * @sysclk_df: STC_SYSCLK_DF dividers value for playback + * @txclk_src: STC_TXCLK_SRC values for playback + * @rxclk_src: SRPC_CLKSRC_SEL values for capture + * @txclk: tx clock sources for playback + * @rxclk: rx clock sources for capture + * @coreclk: core clock for register access via DMA + * @sysclk: system clock for rx clock rate measurement + * @dma_params_tx: DMA parameters for transmit channel + * @dma_params_rx: DMA parameters for receive channel + * @name: driver name + */ struct fsl_spdif_priv { struct spdif_mixer_control fsl_spdif_control; struct snd_soc_dai_driver cpu_dai_drv; struct platform_device *pdev; struct regmap *regmap; bool dpll_locked; - u8 txclk_div[SPDIF_TXRATE_MAX]; + u16 txrate[SPDIF_TXRATE_MAX]; + u8 txclk_df[SPDIF_TXRATE_MAX]; + u8 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; struct clk *txclk[SPDIF_TXRATE_MAX]; struct clk *rxclk; + struct clk *coreclk; + struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; unsigned long csfs = 0; u32 stc, mask, rate; - u8 clk, div; + u8 clk, txclk_df, sysclk_df; int ret; switch (sample_rate) { @@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return -EINVAL; } - div = spdif_priv->txclk_div[rate]; - if (div == 0) { - dev_err(&pdev->dev, "the divisor can't be zero\n"); + txclk_df = spdif_priv->txclk_df[rate]; + if (txclk_df == 0) { + dev_err(&pdev->dev, "the txclk_df can't be zero\n"); return -EINVAL; } + sysclk_df = spdif_priv->sysclk_df[rate]; + + /* Don't mess up the clocks from other modules */ + if (clk != STC_TXCLK_SPDIF_ROOT) + goto clk_set_bypass; + /* - * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block - * will divide by (div). So request 64 * fs * (div+1) which will - * get rounded. + * The S/PDIF block needs a clock of 64 * fs * txclk_df. + * So request 64 * fs * (txclk_df + 1) to get rounded. */ - ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1)); + ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1)); if (ret) { dev_err(&pdev->dev, "failed to set tx clock rate\n"); return ret; } +clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", - (64 * sample_rate * div)); + (64 * sample_rate * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", clk_get_rate(spdif_priv->txclk[rate])); @@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); /* select clock source and divisor */ - stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div); - mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK; + stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df); + mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK; regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc); - dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate); + regmap_update_bits(regmap, REG_SPDIF_STC, + STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df)); + + dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n", + spdif_priv->txrate[rate], sample_rate); return 0; } @@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, /* Reset module and interrupts only for first initialization */ if (!cpu_dai->active) { + ret = clk_prepare_enable(spdif_priv->coreclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable core clock\n"); + return ret; + } + ret = spdif_softreset(spdif_priv); if (ret) { dev_err(&pdev->dev, "failed to soft reset\n"); - return ret; + goto err; } /* Disable all the interrupts */ @@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); return 0; + +err: + clk_disable_unprepare(spdif_priv->coreclk); + + return ret; } static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, @@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, spdif_intr_status_clear(spdif_priv); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER); + clk_disable_unprepare(spdif_priv->coreclk); } } @@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv, clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { /* Get bus clock from system */ - busclk_freq = clk_get_rate(spdif_priv->rxclk); + busclk_freq = clk_get_rate(spdif_priv->sysclk); } /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */ @@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = { static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, - enum spdif_txrate index) + enum spdif_txrate index, bool round) { const u32 rate[] = { 32000, 44100, 48000 }; + bool is_sysclk = clk == spdif_priv->sysclk; u64 rate_ideal, rate_actual, sub; - u32 div, arate; - - for (div = 1; div <= 128; div++) { - rate_ideal = rate[index] * (div + 1) * 64; - rate_actual = clk_round_rate(clk, rate_ideal); - - arate = rate_actual / 64; - arate /= div; - - if (arate == rate[index]) { - /* We are lucky */ - savesub = 0; - spdif_priv->txclk_div[index] = div; - break; - } else if (arate / rate[index] == 1) { - /* A little bigger than expect */ - sub = (arate - rate[index]) * 100000; - do_div(sub, rate[index]); - if (sub < savesub) { + u32 sysclk_dfmin, sysclk_dfmax; + u32 txclk_df, sysclk_df, arate; + + /* The sysclk has an extra divisor [2, 512] */ + sysclk_dfmin = is_sysclk ? 2 : 1; + sysclk_dfmax = is_sysclk ? 512 : 1; + + for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) { + for (txclk_df = 1; txclk_df <= 128; txclk_df++) { + rate_ideal = rate[index] * (txclk_df + 1) * 64; + if (round) + rate_actual = clk_round_rate(clk, rate_ideal); + else + rate_actual = clk_get_rate(clk); + + arate = rate_actual / 64; + arate /= txclk_df * sysclk_df; + + if (arate == rate[index]) { + /* We are lucky */ + savesub = 0; + spdif_priv->txclk_df[index] = txclk_df; + spdif_priv->sysclk_df[index] = sysclk_df; + spdif_priv->txrate[index] = arate; + goto out; + } else if (arate / rate[index] == 1) { + /* A little bigger than expect */ + sub = (arate - rate[index]) * 100000; + do_div(sub, rate[index]); + if (sub >= savesub) + continue; savesub = sub; - spdif_priv->txclk_div[index] = div; - } - } else if (rate[index] / arate == 1) { - /* A little smaller than expect */ - sub = (rate[index] - arate) * 100000; - do_div(sub, rate[index]); - if (sub < savesub) { + spdif_priv->txclk_df[index] = txclk_df; + spdif_priv->sysclk_df[index] = sysclk_df; + spdif_priv->txrate[index] = arate; + } else if (rate[index] / arate == 1) { + /* A little smaller than expect */ + sub = (rate[index] - arate) * 100000; + do_div(sub, rate[index]); + if (sub >= savesub) + continue; savesub = sub; - spdif_priv->txclk_div[index] = div; + spdif_priv->txclk_df[index] = txclk_df; + spdif_priv->sysclk_df[index] = sysclk_df; + spdif_priv->txrate[index] = arate; } } } +out: return savesub; } @@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, if (!clk_get_rate(clk)) continue; - ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index); + ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index, + i == STC_TXCLK_SPDIF_ROOT); if (savesub == ret) continue; @@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n", spdif_priv->txclk_src[index], rate[index]); - dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n", - spdif_priv->txclk_div[index], rate[index]); + dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n", + spdif_priv->txclk_df[index], rate[index]); + if (spdif_priv->txclk[index] == spdif_priv->sysclk) + dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n", + spdif_priv->sysclk_df[index], rate[index]); + dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n", + rate[index], spdif_priv->txrate[index]); return 0; } @@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } + /* Get system clock for rx clock rate calculation */ + spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5"); + if (IS_ERR(spdif_priv->sysclk)) { + dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n"); + return PTR_ERR(spdif_priv->sysclk); + } + + /* Get core clock for data register access via DMA */ + spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(spdif_priv->coreclk)) { + dev_err(&pdev->dev, "no core clock in devicetree\n"); + return PTR_ERR(spdif_priv->coreclk); + } + /* Select clock source for rx/tx clock */ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); if (IS_ERR(spdif_priv->rxclk)) { @@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, + { .compatible = "fsl,vf610-spdif", }, {} }; MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 605a10b2112b..16fde4b927d3 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -143,20 +143,22 @@ enum spdif_gainsel { #define INT_RXFIFO_FUL (1 << 0) /* SPDIF Clock register */ -#define STC_SYSCLK_DIV_OFFSET 11 -#define STC_SYSCLK_DIV_MASK (0x1ff << STC_SYSCLK_DIV_OFFSET) -#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK) +#define STC_SYSCLK_DF_OFFSET 11 +#define STC_SYSCLK_DF_MASK (0x1ff << STC_SYSCLK_DF_OFFSET) +#define STC_SYSCLK_DF(x) ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK) #define STC_TXCLK_SRC_OFFSET 8 #define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET) #define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK) #define STC_TXCLK_ALL_EN_OFFSET 7 #define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) #define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET) -#define STC_TXCLK_DIV_OFFSET 0 -#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET) -#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) +#define STC_TXCLK_DF_OFFSET 0 +#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET) +#define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK) #define STC_TXCLK_SRC_MAX 8 +#define STC_TXCLK_SPDIF_ROOT 1 + /* SPDIF tx rate */ enum spdif_txrate { SPDIF_TXRATE_32000 = 0, diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 5428a1fda260..f233d915b7e4 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -35,11 +35,11 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/clk.h> -#include <linux/debugfs.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -113,8 +113,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) #define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \ CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN) -#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS) - enum fsl_ssi_type { FSL_SSI_MCP8610, @@ -146,7 +144,6 @@ struct fsl_ssi_rxtx_reg_val { * @cpu_dai: the CPU DAI for this device * @dev_attr: the sysfs device attribute structure * @stats: SSI statistics - * @name: name for this device */ struct fsl_ssi_private { struct ccsr_ssi __iomem *ssi; @@ -155,15 +152,11 @@ struct fsl_ssi_private { unsigned int fifo_depth; struct snd_soc_dai_driver cpu_dai_drv; struct platform_device *pdev; + unsigned int dai_fmt; enum fsl_ssi_type hw_type; - bool new_binding; - bool ssi_on_imx; - bool imx_ac97; bool use_dma; bool baudclk_locked; - bool irq_stats; - bool offline_config; bool use_dual_fifo; u8 i2s_mode; spinlock_t baudclk_lock; @@ -171,39 +164,11 @@ struct fsl_ssi_private { struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; - struct imx_dma_data filter_data_tx; - struct imx_dma_data filter_data_rx; struct imx_pcm_fiq_params fiq_params; /* Register values for rx/tx configuration */ struct fsl_ssi_rxtx_reg_val rxtx_reg_val; - struct { - unsigned int rfrc; - unsigned int tfrc; - unsigned int cmdau; - unsigned int cmddu; - unsigned int rxt; - unsigned int rdr1; - unsigned int rdr0; - unsigned int tde1; - unsigned int tde0; - unsigned int roe1; - unsigned int roe0; - unsigned int tue1; - unsigned int tue0; - unsigned int tfs; - unsigned int rfs; - unsigned int tls; - unsigned int rls; - unsigned int rff1; - unsigned int rff0; - unsigned int tfe1; - unsigned int tfe0; - } stats; - struct dentry *dbg_dir; - struct dentry *dbg_stats; - - char name[1]; + struct fsl_ssi_dbg dbg_stats; }; static const struct of_device_id fsl_ssi_ids[] = { @@ -215,6 +180,54 @@ static const struct of_device_id fsl_ssi_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_ssi_ids); +static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private) +{ + return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97); +} + +static bool fsl_ssi_on_imx(struct fsl_ssi_private *ssi_private) +{ + switch (ssi_private->hw_type) { + case FSL_SSI_MX21: + case FSL_SSI_MX35: + case FSL_SSI_MX51: + return true; + case FSL_SSI_MCP8610: + return false; + } + + return false; +} + +/* + * imx51 and later SoCs have a slightly different IP that allows the + * SSI configuration while the SSI unit is running. + * + * More important, it is necessary on those SoCs to configure the + * sperate TX/RX DMA bits just before starting the stream + * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi + * sends any DMA requests to the SDMA unit, otherwise it is not defined + * how the SDMA unit handles the DMA request. + * + * SDMA units are present on devices starting at imx35 but the imx35 + * reference manual states that the DMA bits should not be changed + * while the SSI unit is running (SSIEN). So we support the necessary + * online configuration of fsl-ssi starting at imx51. + */ +static bool fsl_ssi_offline_config(struct fsl_ssi_private *ssi_private) +{ + switch (ssi_private->hw_type) { + case FSL_SSI_MCP8610: + case FSL_SSI_MX21: + case FSL_SSI_MX35: + return true; + case FSL_SSI_MX51: + return false; + } + + return true; +} + /** * fsl_ssi_isr: SSI interrupt handler * @@ -231,7 +244,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) { struct fsl_ssi_private *ssi_private = dev_id; struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - irqreturn_t ret = IRQ_NONE; __be32 sisr; __be32 sisr2; __be32 sisr_write_mask = 0; @@ -258,217 +270,18 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) were interrupted for. We mask it with the Interrupt Enable register so that we only check for events that we're interested in. */ - sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK; - - if (sisr & CCSR_SSI_SISR_RFRC) { - ssi_private->stats.rfrc++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TFRC) { - ssi_private->stats.tfrc++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_CMDAU) { - ssi_private->stats.cmdau++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_CMDDU) { - ssi_private->stats.cmddu++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RXT) { - ssi_private->stats.rxt++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RDR1) { - ssi_private->stats.rdr1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RDR0) { - ssi_private->stats.rdr0++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TDE1) { - ssi_private->stats.tde1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TDE0) { - ssi_private->stats.tde0++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_ROE1) { - ssi_private->stats.roe1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_ROE0) { - ssi_private->stats.roe0++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TUE1) { - ssi_private->stats.tue1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TUE0) { - ssi_private->stats.tue0++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TFS) { - ssi_private->stats.tfs++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RFS) { - ssi_private->stats.rfs++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TLS) { - ssi_private->stats.tls++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RLS) { - ssi_private->stats.rls++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RFF1) { - ssi_private->stats.rff1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_RFF0) { - ssi_private->stats.rff0++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TFE1) { - ssi_private->stats.tfe1++; - ret = IRQ_HANDLED; - } - - if (sisr & CCSR_SSI_SISR_TFE0) { - ssi_private->stats.tfe0++; - ret = IRQ_HANDLED; - } + sisr = read_ssi(&ssi->sisr); sisr2 = sisr & sisr_write_mask; /* Clear the bits that we set */ if (sisr2) write_ssi(sisr2, &ssi->sisr); - return ret; -} - -#if IS_ENABLED(CONFIG_DEBUG_FS) -/* Show the statistics of a flag only if its interrupt is enabled. The - * compiler will optimze this code to a no-op if the interrupt is not - * enabled. - */ -#define SIER_SHOW(flag, name) \ - do { \ - if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \ - seq_printf(s, #name "=%u\n", ssi_private->stats.name); \ - } while (0) - - -/** - * fsl_sysfs_ssi_show: display SSI statistics - * - * Display the statistics for the current SSI device. To avoid confusion, - * we only show those counts that are enabled. - */ -static int fsl_ssi_stats_show(struct seq_file *s, void *unused) -{ - struct fsl_ssi_private *ssi_private = s->private; - - SIER_SHOW(RFRC_EN, rfrc); - SIER_SHOW(TFRC_EN, tfrc); - SIER_SHOW(CMDAU_EN, cmdau); - SIER_SHOW(CMDDU_EN, cmddu); - SIER_SHOW(RXT_EN, rxt); - SIER_SHOW(RDR1_EN, rdr1); - SIER_SHOW(RDR0_EN, rdr0); - SIER_SHOW(TDE1_EN, tde1); - SIER_SHOW(TDE0_EN, tde0); - SIER_SHOW(ROE1_EN, roe1); - SIER_SHOW(ROE0_EN, roe0); - SIER_SHOW(TUE1_EN, tue1); - SIER_SHOW(TUE0_EN, tue0); - SIER_SHOW(TFS_EN, tfs); - SIER_SHOW(RFS_EN, rfs); - SIER_SHOW(TLS_EN, tls); - SIER_SHOW(RLS_EN, rls); - SIER_SHOW(RFF1_EN, rff1); - SIER_SHOW(RFF0_EN, rff0); - SIER_SHOW(TFE1_EN, tfe1); - SIER_SHOW(TFE0_EN, tfe0); + fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr); - return 0; + return IRQ_HANDLED; } -static int fsl_ssi_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, fsl_ssi_stats_show, inode->i_private); -} - -static const struct file_operations fsl_ssi_stats_ops = { - .open = fsl_ssi_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, - struct device *dev) -{ - ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); - if (!ssi_private->dbg_dir) - return -ENOMEM; - - ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO, - ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops); - if (!ssi_private->dbg_stats) { - debugfs_remove(ssi_private->dbg_dir); - return -ENOMEM; - } - - return 0; -} - -static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) -{ - debugfs_remove(ssi_private->dbg_stats); - debugfs_remove(ssi_private->dbg_dir); -} - -#else - -static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, - struct device *dev) -{ - return 0; -} - -static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) -{ -} - -#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ - /* * Enable/Disable all rx/tx config flags at once. */ @@ -490,6 +303,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, } /* + * Calculate the bits that have to be disabled for the current stream that is + * getting disabled. This keeps the bits enabled that are necessary for the + * second stream to work if 'stream_active' is true. + * + * Detailed calculation: + * These are the values that need to be active after disabling. For non-active + * second stream, this is 0: + * vals_stream * !!stream_active + * + * The following computes the overall differences between the setup for the + * to-disable stream and the active stream, a simple XOR: + * vals_disable ^ (vals_stream * !!(stream_active)) + * + * The full expression adds a mask on all values we care about + */ +#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \ + ((vals_disable) & \ + ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active)))) + +/* * Enable/Disable a ssi configuration. You have to pass either * ssi_private->rxtx_reg_val.rx or tx as vals parameter. */ @@ -501,6 +334,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, u32 scr_val = read_ssi(&ssi->scr); int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) + !!(scr_val & CCSR_SSI_SCR_RE); + int keep_active; + + if (nr_active_streams - 1 > 0) + keep_active = 1; + else + keep_active = 0; /* Find the other direction values rx or tx which we do not want to * modify */ @@ -511,7 +350,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, /* If vals should be disabled, start with disabling the unit */ if (!enable) { - u32 scr = vals->scr & (vals->scr ^ avals->scr); + u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, + keep_active); write_ssi_mask(&ssi->scr, scr, 0); } @@ -520,9 +360,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, * reconfiguration, so we have to enable all necessary flags at once * even if we do not use them later (capture and playback configuration) */ - if (ssi_private->offline_config) { + if (fsl_ssi_offline_config(ssi_private)) { if ((enable && !nr_active_streams) || - (!enable && nr_active_streams == 1)) + (!enable && !keep_active)) fsl_ssi_rxtx_config(ssi_private, enable); goto config_done; @@ -551,9 +391,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, */ /* These assignments are simply vals without bits set in avals*/ - sier = vals->sier & (vals->sier ^ avals->sier); - srcr = vals->srcr & (vals->srcr ^ avals->srcr); - stcr = vals->stcr & (vals->stcr ^ avals->stcr); + sier = fsl_ssi_disable_val(vals->sier, avals->sier, + keep_active); + srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, + keep_active); + stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr, + keep_active); write_ssi_mask(&ssi->srcr, srcr, 0); write_ssi_mask(&ssi->stcr, stcr, 0); @@ -593,7 +436,7 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private) reg->tx.stcr = CCSR_SSI_STCR_TFEN0; reg->tx.scr = 0; - if (!ssi_private->imx_ac97) { + if (!fsl_ssi_is_ac97(ssi_private)) { reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE; reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN; reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE; @@ -642,96 +485,6 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); } -static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) -{ - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - u8 wm; - int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; - - fsl_ssi_setup_reg_vals(ssi_private); - - if (ssi_private->imx_ac97) - ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; - else - ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; - - /* - * Section 16.5 of the MPC8610 reference manual says that the SSI needs - * to be disabled before updating the registers we set here. - */ - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - - /* - * Program the SSI into I2S Slave Non-Network Synchronous mode. Also - * enable the transmit and receive FIFO. - * - * FIXME: Little-endian samples require a different shift dir - */ - write_ssi_mask(&ssi->scr, - CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, - CCSR_SSI_SCR_TFR_CLK_DIS | - ssi_private->i2s_mode | - (synchronous ? CCSR_SSI_SCR_SYN : 0)); - - write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI | - CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr); - - write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI | - CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr); - - /* - * The DC and PM bits are only used if the SSI is the clock master. - */ - - /* - * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't - * use FIFO 1. We program the transmit water to signal a DMA transfer - * if there are only two (or fewer) elements left in the FIFO. Two - * elements equals one frame (left channel, right channel). This value, - * however, depends on the depth of the transmit buffer. - * - * We set the watermark on the same level as the DMA burstsize. For - * fiq it is probably better to use the biggest possible watermark - * size. - */ - if (ssi_private->use_dma) - wm = ssi_private->fifo_depth - 2; - else - wm = ssi_private->fifo_depth; - - write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | - CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm), - &ssi->sfcsr); - - /* - * For ac97 interrupts are enabled with the startup of the substream - * because it is also running without an active substream. Normally SSI - * is only enabled when there is a substream. - */ - if (ssi_private->imx_ac97) - fsl_ssi_setup_ac97(ssi_private); - - /* - * Set a default slot number so that there is no need for those common - * cases like I2S mode to call the extra set_tdm_slot() any more. - */ - if (!ssi_private->imx_ac97) { - write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(2)); - write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK, - CCSR_SSI_SxCCR_DC(2)); - } - - if (ssi_private->use_dual_fifo) { - write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1); - write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1); - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN); - } - - return 0; -} - - /** * fsl_ssi_startup: create a new substream * @@ -748,12 +501,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, snd_soc_dai_get_drvdata(rtd->cpu_dai); unsigned long flags; - /* First, we only do fsl_ssi_setup() when SSI is going to be active. - * Second, fsl_ssi_setup was already called by ac97_init earlier if - * the driver is in ac97 mode. - */ - if (!dai->active && !ssi_private->imx_ac97) { - fsl_ssi_setup(ssi_private); + if (!dai->active && !fsl_ssi_is_ac97(ssi_private)) { spin_lock_irqsave(&ssi_private->baudclk_lock, flags); ssi_private->baudclk_locked = false; spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); @@ -772,6 +520,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, } /** + * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock + * + * Note: This function can be only called when using SSI as DAI master + * + * Quick instruction for parameters: + * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels + * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. + */ +static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; + u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; + unsigned long flags, clkrate, baudrate, tmprate; + u64 sub, savesub = 100000; + + /* Don't apply it to any non-baudclk circumstance */ + if (IS_ERR(ssi_private->baudclk)) + return -EINVAL; + + /* It should be already enough to divide clock by setting pm alone */ + psr = 0; + div2 = 0; + + factor = (div2 + 1) * (7 * psr + 1) * 2; + + for (i = 0; i < 255; i++) { + /* The bclk rate must be smaller than 1/5 sysclk rate */ + if (factor * (i + 1) < 5) + continue; + + tmprate = freq * factor * (i + 2); + clkrate = clk_round_rate(ssi_private->baudclk, tmprate); + + do_div(clkrate, factor); + afreq = (u32)clkrate / (i + 1); + + if (freq == afreq) + sub = 0; + else if (freq / afreq == 1) + sub = freq - afreq; + else if (afreq / freq == 1) + sub = afreq - freq; + else + continue; + + /* Calculate the fraction */ + sub *= 100000; + do_div(sub, freq); + + if (sub < savesub) { + baudrate = tmprate; + savesub = sub; + pm = i; + } + + /* We are lucky */ + if (savesub == 0) + break; + } + + /* No proper pm found if it is still remaining the initial value */ + if (pm == 999) { + dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); + return -EINVAL; + } + + stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) | + (psr ? CCSR_SSI_SxCCR_PSR : 0); + mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | + CCSR_SSI_SxCCR_PSR; + + if (dir == SND_SOC_CLOCK_OUT || synchronous) + write_ssi_mask(&ssi->stccr, mask, stccr); + else + write_ssi_mask(&ssi->srccr, mask, stccr); + + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + if (!ssi_private->baudclk_locked) { + ret = clk_set_rate(ssi_private->baudclk, baudrate); + if (ret) { + spin_unlock_irqrestore(&ssi_private->baudclk_lock, + flags); + dev_err(cpu_dai->dev, "failed to set baudclk rate\n"); + return -EINVAL; + } + ssi_private->baudclk_locked = true; + } + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + + return 0; +} + +/** * fsl_ssi_hw_params - program the sample size * * Most of the SSI registers have been programmed in the startup function, @@ -819,7 +663,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, else write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); - if (!ssi_private->imx_ac97) + if (!fsl_ssi_is_ac97(ssi_private)) write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, channels == 1 ? 0 : ssi_private->i2s_mode); @@ -835,9 +679,14 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; u32 strcr = 0, stcr, srcr, scr, mask; + u8 wm; + + ssi_private->dai_fmt = fmt; + + fsl_ssi_setup_reg_vals(ssi_private); scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK); - scr |= CCSR_SSI_SCR_NET; + scr |= CCSR_SSI_SCR_SYNC_TX_FS; mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR | CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL | @@ -845,19 +694,19 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) stcr = read_ssi(&ssi->stcr) & ~mask; srcr = read_ssi(&ssi->srcr) & ~mask; + ssi_private->i2s_mode = CCSR_SSI_SCR_NET; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER; + ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER; break; case SND_SOC_DAIFMT_CBM_CFM: - ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE; break; default: return -EINVAL; } - scr |= ssi_private->i2s_mode; /* Data on rising edge of bclk, frame low, 1clk before data */ strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP | @@ -877,9 +726,13 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TXBIT0; break; + case SND_SOC_DAIFMT_AC97: + ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL; + break; default: return -EINVAL; } + scr |= ssi_private->i2s_mode; /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -929,99 +782,37 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) write_ssi(srcr, &ssi->srcr); write_ssi(scr, &ssi->scr); - return 0; -} - -/** - * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock - * - * Note: This function can be only called when using SSI as DAI master - * - * Quick instruction for parameters: - * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels - * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. - */ -static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; - u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; - unsigned long flags, clkrate, baudrate, tmprate; - u64 sub, savesub = 100000; - - /* Don't apply it to any non-baudclk circumstance */ - if (IS_ERR(ssi_private->baudclk)) - return -EINVAL; - - /* It should be already enough to divide clock by setting pm alone */ - psr = 0; - div2 = 0; - - factor = (div2 + 1) * (7 * psr + 1) * 2; - - for (i = 0; i < 255; i++) { - /* The bclk rate must be smaller than 1/5 sysclk rate */ - if (factor * (i + 1) < 5) - continue; - - tmprate = freq * factor * (i + 2); - clkrate = clk_round_rate(ssi_private->baudclk, tmprate); - - do_div(clkrate, factor); - afreq = (u32)clkrate / (i + 1); - - if (freq == afreq) - sub = 0; - else if (freq / afreq == 1) - sub = freq - afreq; - else if (afreq / freq == 1) - sub = afreq - freq; - else - continue; - - /* Calculate the fraction */ - sub *= 100000; - do_div(sub, freq); - - if (sub < savesub) { - baudrate = tmprate; - savesub = sub; - pm = i; - } + /* + * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't + * use FIFO 1. We program the transmit water to signal a DMA transfer + * if there are only two (or fewer) elements left in the FIFO. Two + * elements equals one frame (left channel, right channel). This value, + * however, depends on the depth of the transmit buffer. + * + * We set the watermark on the same level as the DMA burstsize. For + * fiq it is probably better to use the biggest possible watermark + * size. + */ + if (ssi_private->use_dma) + wm = ssi_private->fifo_depth - 2; + else + wm = ssi_private->fifo_depth; - /* We are lucky */ - if (savesub == 0) - break; - } + write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | + CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm), + &ssi->sfcsr); - /* No proper pm found if it is still remaining the initial value */ - if (pm == 999) { - dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); - return -EINVAL; + if (ssi_private->use_dual_fifo) { + write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1, + CCSR_SSI_SRCR_RFEN1); + write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1, + CCSR_SSI_STCR_TFEN1); + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN, + CCSR_SSI_SCR_TCH_EN); } - stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) | - (psr ? CCSR_SSI_SxCCR_PSR : 0); - mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR; - - if (dir == SND_SOC_CLOCK_OUT || synchronous) - write_ssi_mask(&ssi->stccr, mask, stccr); - else - write_ssi_mask(&ssi->srccr, mask, stccr); - - spin_lock_irqsave(&ssi_private->baudclk_lock, flags); - if (!ssi_private->baudclk_locked) { - ret = clk_set_rate(ssi_private->baudclk, baudrate); - if (ret) { - spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); - dev_err(cpu_dai->dev, "failed to set baudclk rate\n"); - return -EINVAL; - } - ssi_private->baudclk_locked = true; - } - spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + if (fmt & SND_SOC_DAIFMT_AC97) + fsl_ssi_setup_ac97(ssi_private); return 0; } @@ -1097,7 +888,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, else fsl_ssi_rx_config(ssi_private, false); - if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & + if (!fsl_ssi_is_ac97(ssi_private) && (read_ssi(&ssi->scr) & (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) { spin_lock_irqsave(&ssi_private->baudclk_lock, flags); ssi_private->baudclk_locked = false; @@ -1109,7 +900,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, return -EINVAL; } - if (ssi_private->imx_ac97) { + if (fsl_ssi_is_ac97(ssi_private)) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); else @@ -1123,7 +914,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); - if (ssi_private->ssi_on_imx && ssi_private->use_dma) { + if (fsl_ssi_on_imx(ssi_private) && ssi_private->use_dma) { dai->playback_dma_data = &ssi_private->dma_params_tx; dai->capture_dma_data = &ssi_private->dma_params_rx; } @@ -1184,11 +975,6 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { static struct fsl_ssi_private *fsl_ac97_data; -static void fsl_ssi_ac97_init(void) -{ - fsl_ssi_setup(fsl_ac97_data); -} - static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { @@ -1251,11 +1037,107 @@ static void make_lowercase(char *s) } } +static int fsl_ssi_imx_probe(struct platform_device *pdev, + struct fsl_ssi_private *ssi_private, void __iomem *iomem) +{ + struct device_node *np = pdev->dev.of_node; + u32 dmas[4]; + int ret; + + ssi_private->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ssi_private->clk)) { + ret = PTR_ERR(ssi_private->clk); + dev_err(&pdev->dev, "could not get clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(ssi_private->clk); + if (ret) { + dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); + return ret; + } + + /* For those SLAVE implementations, we ingore non-baudclk cases + * and, instead, abandon MASTER mode that needs baud clock. + */ + ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); + if (IS_ERR(ssi_private->baudclk)) + dev_dbg(&pdev->dev, "could not get baud clock: %ld\n", + PTR_ERR(ssi_private->baudclk)); + else + clk_prepare_enable(ssi_private->baudclk); + + /* + * We have burstsize be "fifo_depth - 2" to match the SSI + * watermark setting in fsl_ssi_startup(). + */ + ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2; + ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2; + ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + + offsetof(struct ccsr_ssi, stx0); + ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + + offsetof(struct ccsr_ssi, srx0); + + ret = !of_property_read_u32_array(np, "dmas", dmas, 4); + if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { + ssi_private->use_dual_fifo = true; + /* When using dual fifo mode, we need to keep watermark + * as even numbers due to dma script limitation. + */ + ssi_private->dma_params_tx.maxburst &= ~0x1; + ssi_private->dma_params_rx.maxburst &= ~0x1; + } + + if (!ssi_private->use_dma) { + + /* + * Some boards use an incompatible codec. To get it + * working, we are using imx-fiq-pcm-audio, that + * can handle those codecs. DMA is not possible in this + * situation. + */ + + ssi_private->fiq_params.irq = ssi_private->irq; + ssi_private->fiq_params.base = iomem; + ssi_private->fiq_params.dma_params_rx = + &ssi_private->dma_params_rx; + ssi_private->fiq_params.dma_params_tx = + &ssi_private->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); + if (ret) + goto error_pcm; + } else { + ret = imx_pcm_dma_init(pdev); + if (ret) + goto error_pcm; + } + + return 0; + +error_pcm: + if (!IS_ERR(ssi_private->baudclk)) + clk_disable_unprepare(ssi_private->baudclk); + + clk_disable_unprepare(ssi_private->clk); + + return ret; +} + +static void fsl_ssi_imx_clean(struct platform_device *pdev, + struct fsl_ssi_private *ssi_private) +{ + if (!ssi_private->use_dma) + imx_pcm_fiq_exit(pdev); + if (!IS_ERR(ssi_private->baudclk)) + clk_disable_unprepare(ssi_private->baudclk); + clk_disable_unprepare(ssi_private->clk); +} + static int fsl_ssi_probe(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private; int ret = 0; - struct device_attribute *dev_attr = NULL; struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id; enum fsl_ssi_type hw_type; @@ -1263,7 +1145,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) const uint32_t *iprop; struct resource res; char name[64]; - bool shared; bool ac97 = false; /* SSIs that are not connected on the board should have a @@ -1286,17 +1167,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (!strcmp(sprop, "ac97-slave")) ac97 = true; - /* The DAI name is the last part of the full name of the node. */ - p = strrchr(np->full_name, '/') + 1; - ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p), - GFP_KERNEL); + ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private), + GFP_KERNEL); if (!ssi_private) { dev_err(&pdev->dev, "could not allocate DAI object\n"); return -ENOMEM; } - strcpy(ssi_private->name, p); - ssi_private->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); ssi_private->hw_type = hw_type; @@ -1306,7 +1183,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) sizeof(fsl_ssi_ac97_dai)); fsl_ac97_data = ssi_private; - ssi_private->imx_ac97 = true; snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); } else { @@ -1314,7 +1190,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, sizeof(fsl_ssi_dai_template)); } - ssi_private->cpu_dai_drv.name = ssi_private->name; + ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev); /* Get the addresses and IRQ */ ret = of_address_to_resource(np, 0, &res); @@ -1353,177 +1229,43 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->baudclk_locked = false; spin_lock_init(&ssi_private->baudclk_lock); - /* - * imx51 and later SoCs have a slightly different IP that allows the - * SSI configuration while the SSI unit is running. - * - * More important, it is necessary on those SoCs to configure the - * sperate TX/RX DMA bits just before starting the stream - * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi - * sends any DMA requests to the SDMA unit, otherwise it is not defined - * how the SDMA unit handles the DMA request. - * - * SDMA units are present on devices starting at imx35 but the imx35 - * reference manual states that the DMA bits should not be changed - * while the SSI unit is running (SSIEN). So we support the necessary - * online configuration of fsl-ssi starting at imx51. - */ - switch (hw_type) { - case FSL_SSI_MCP8610: - case FSL_SSI_MX21: - case FSL_SSI_MX35: - ssi_private->offline_config = true; - break; - case FSL_SSI_MX51: - ssi_private->offline_config = false; - break; - } - - if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 || - hw_type == FSL_SSI_MX35) { - u32 dma_events[2], dmas[4]; - ssi_private->ssi_on_imx = true; + dev_set_drvdata(&pdev->dev, ssi_private); - ssi_private->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ssi_private->clk)) { - ret = PTR_ERR(ssi_private->clk); - dev_err(&pdev->dev, "could not get clock: %d\n", ret); - goto error_irqmap; - } - ret = clk_prepare_enable(ssi_private->clk); - if (ret) { - dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", - ret); + if (fsl_ssi_on_imx(ssi_private)) { + ret = fsl_ssi_imx_probe(pdev, ssi_private, ssi_private->ssi); + if (ret) goto error_irqmap; - } - - /* For those SLAVE implementations, we ingore non-baudclk cases - * and, instead, abandon MASTER mode that needs baud clock. - */ - ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); - if (IS_ERR(ssi_private->baudclk)) - dev_dbg(&pdev->dev, "could not get baud clock: %ld\n", - PTR_ERR(ssi_private->baudclk)); - else - clk_prepare_enable(ssi_private->baudclk); - - /* - * We have burstsize be "fifo_depth - 2" to match the SSI - * watermark setting in fsl_ssi_startup(). - */ - ssi_private->dma_params_tx.maxburst = - ssi_private->fifo_depth - 2; - ssi_private->dma_params_rx.maxburst = - ssi_private->fifo_depth - 2; - ssi_private->dma_params_tx.addr = - ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0); - ssi_private->dma_params_rx.addr = - ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0); - ssi_private->dma_params_tx.filter_data = - &ssi_private->filter_data_tx; - ssi_private->dma_params_rx.filter_data = - &ssi_private->filter_data_rx; - if (!of_property_read_bool(pdev->dev.of_node, "dmas") && - ssi_private->use_dma) { - /* - * FIXME: This is a temporary solution until all - * necessary dma drivers support the generic dma - * bindings. - */ - ret = of_property_read_u32_array(pdev->dev.of_node, - "fsl,ssi-dma-events", dma_events, 2); - if (ret && ssi_private->use_dma) { - dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n"); - goto error_clk; - } - } - /* Should this be merge with the above? */ - if (!of_property_read_u32_array(pdev->dev.of_node, "dmas", dmas, 4) - && dmas[2] == IMX_DMATYPE_SSI_DUAL) { - ssi_private->use_dual_fifo = true; - /* When using dual fifo mode, we need to keep watermark - * as even numbers due to dma script limitation. - */ - ssi_private->dma_params_tx.maxburst &= ~0x1; - ssi_private->dma_params_rx.maxburst &= ~0x1; - } - - shared = of_device_is_compatible(of_get_parent(np), - "fsl,spba-bus"); + } - imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx, - dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); - imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, - dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); + ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, + &ssi_private->cpu_dai_drv, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + goto error_asoc_register; } - /* - * Enable interrupts only for MCP8610 and MX51. The other MXs have - * different writeable interrupt status registers. - */ if (ssi_private->use_dma) { - /* The 'name' should not have any slashes in it. */ ret = devm_request_irq(&pdev->dev, ssi_private->irq, - fsl_ssi_isr, 0, ssi_private->name, + fsl_ssi_isr, 0, dev_name(&pdev->dev), ssi_private); - ssi_private->irq_stats = true; if (ret < 0) { dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_clk; + goto error_irq; } } - /* Register with ASoC */ - dev_set_drvdata(&pdev->dev, ssi_private); - - ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, - &ssi_private->cpu_dai_drv, 1); - if (ret) { - dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); - goto error_dev; - } - - ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev); + ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); if (ret) - goto error_dbgfs; - - if (ssi_private->ssi_on_imx) { - if (!ssi_private->use_dma) { - - /* - * Some boards use an incompatible codec. To get it - * working, we are using imx-fiq-pcm-audio, that - * can handle those codecs. DMA is not possible in this - * situation. - */ - - ssi_private->fiq_params.irq = ssi_private->irq; - ssi_private->fiq_params.base = ssi_private->ssi; - ssi_private->fiq_params.dma_params_rx = - &ssi_private->dma_params_rx; - ssi_private->fiq_params.dma_params_tx = - &ssi_private->dma_params_tx; - - ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); - if (ret) - goto error_pcm; - } else { - ret = imx_pcm_dma_init(pdev); - if (ret) - goto error_pcm; - } - } + goto error_asoc_register; /* * If codec-handle property is missing from SSI node, we assume * that the machine driver uses new binding which does not require * SSI driver to trigger machine driver's probe. */ - if (!of_get_property(np, "codec-handle", NULL)) { - ssi_private->new_binding = true; + if (!of_get_property(np, "codec-handle", NULL)) goto done; - } /* Trigger the machine driver's probe function. The platform driver * name of the machine driver is taken from /compatible property of the @@ -1543,37 +1285,24 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (IS_ERR(ssi_private->pdev)) { ret = PTR_ERR(ssi_private->pdev); dev_err(&pdev->dev, "failed to register platform: %d\n", ret); - goto error_dai; + goto error_sound_card; } done: - if (ssi_private->imx_ac97) - fsl_ssi_ac97_init(); - return 0; -error_dai: - if (ssi_private->ssi_on_imx && !ssi_private->use_dma) - imx_pcm_fiq_exit(pdev); - -error_pcm: - fsl_ssi_debugfs_remove(ssi_private); +error_sound_card: + fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); -error_dbgfs: +error_irq: snd_soc_unregister_component(&pdev->dev); -error_dev: - device_remove_file(&pdev->dev, dev_attr); - -error_clk: - if (ssi_private->ssi_on_imx) { - if (!IS_ERR(ssi_private->baudclk)) - clk_disable_unprepare(ssi_private->baudclk); - clk_disable_unprepare(ssi_private->clk); - } +error_asoc_register: + if (fsl_ssi_on_imx(ssi_private)) + fsl_ssi_imx_clean(pdev, ssi_private); error_irqmap: - if (ssi_private->irq_stats) + if (ssi_private->use_dma) irq_dispose_mapping(ssi_private->irq); return ret; @@ -1583,17 +1312,16 @@ static int fsl_ssi_remove(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); - fsl_ssi_debugfs_remove(ssi_private); + fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); - if (!ssi_private->new_binding) + if (ssi_private->pdev) platform_device_unregister(ssi_private->pdev); snd_soc_unregister_component(&pdev->dev); - if (ssi_private->ssi_on_imx) { - if (!IS_ERR(ssi_private->baudclk)) - clk_disable_unprepare(ssi_private->baudclk); - clk_disable_unprepare(ssi_private->clk); - } - if (ssi_private->irq_stats) + + if (fsl_ssi_on_imx(ssi_private)) + fsl_ssi_imx_clean(pdev, ssi_private); + + if (ssi_private->use_dma) irq_dispose_mapping(ssi_private->irq); return 0; diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index e6b63240a3d7..71c3e7e4340d 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -39,6 +39,7 @@ struct ccsr_ssi { __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */ }; +#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000 #define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800 #define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400 #define CCSR_SSI_SCR_TCH_EN 0x00000100 @@ -206,5 +207,64 @@ struct ccsr_ssi { #define CCSR_SSI_SACNT_FV 0x00000002 #define CCSR_SSI_SACNT_AC97EN 0x00000001 -#endif +struct device; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + +struct fsl_ssi_dbg { + struct dentry *dbg_dir; + struct dentry *dbg_stats; + + struct { + unsigned int rfrc; + unsigned int tfrc; + unsigned int cmdau; + unsigned int cmddu; + unsigned int rxt; + unsigned int rdr1; + unsigned int rdr0; + unsigned int tde1; + unsigned int tde0; + unsigned int roe1; + unsigned int roe0; + unsigned int tue1; + unsigned int tue0; + unsigned int tfs; + unsigned int rfs; + unsigned int tls; + unsigned int rls; + unsigned int rff1; + unsigned int rff0; + unsigned int tfe1; + unsigned int tfe0; + } stats; +}; + +void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr); + +int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev); + +void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg); + +#else + +struct fsl_ssi_dbg { +}; + +static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr) +{ +} + +static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, + struct device *dev) +{ + return 0; +} + +static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg) +{ +} +#endif /* ! IS_ENABLED(CONFIG_DEBUG_FS) */ + +#endif diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c new file mode 100644 index 000000000000..5469ffbc0253 --- /dev/null +++ b/sound/soc/fsl/fsl_ssi_dbg.c @@ -0,0 +1,163 @@ +/* + * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions + * + * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix + * + * Splitted from fsl_ssi.c + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/kernel.h> + +#include "fsl_ssi.h" + +void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr) +{ + if (sisr & CCSR_SSI_SISR_RFRC) + dbg->stats.rfrc++; + + if (sisr & CCSR_SSI_SISR_TFRC) + dbg->stats.tfrc++; + + if (sisr & CCSR_SSI_SISR_CMDAU) + dbg->stats.cmdau++; + + if (sisr & CCSR_SSI_SISR_CMDDU) + dbg->stats.cmddu++; + + if (sisr & CCSR_SSI_SISR_RXT) + dbg->stats.rxt++; + + if (sisr & CCSR_SSI_SISR_RDR1) + dbg->stats.rdr1++; + + if (sisr & CCSR_SSI_SISR_RDR0) + dbg->stats.rdr0++; + + if (sisr & CCSR_SSI_SISR_TDE1) + dbg->stats.tde1++; + + if (sisr & CCSR_SSI_SISR_TDE0) + dbg->stats.tde0++; + + if (sisr & CCSR_SSI_SISR_ROE1) + dbg->stats.roe1++; + + if (sisr & CCSR_SSI_SISR_ROE0) + dbg->stats.roe0++; + + if (sisr & CCSR_SSI_SISR_TUE1) + dbg->stats.tue1++; + + if (sisr & CCSR_SSI_SISR_TUE0) + dbg->stats.tue0++; + + if (sisr & CCSR_SSI_SISR_TFS) + dbg->stats.tfs++; + + if (sisr & CCSR_SSI_SISR_RFS) + dbg->stats.rfs++; + + if (sisr & CCSR_SSI_SISR_TLS) + dbg->stats.tls++; + + if (sisr & CCSR_SSI_SISR_RLS) + dbg->stats.rls++; + + if (sisr & CCSR_SSI_SISR_RFF1) + dbg->stats.rff1++; + + if (sisr & CCSR_SSI_SISR_RFF0) + dbg->stats.rff0++; + + if (sisr & CCSR_SSI_SISR_TFE1) + dbg->stats.tfe1++; + + if (sisr & CCSR_SSI_SISR_TFE0) + dbg->stats.tfe0++; +} + +/* Show the statistics of a flag only if its interrupt is enabled. The + * compiler will optimze this code to a no-op if the interrupt is not + * enabled. + */ +#define SIER_SHOW(flag, name) \ + do { \ + if (CCSR_SSI_SIER_##flag) \ + seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \ + } while (0) + + +/** + * fsl_sysfs_ssi_show: display SSI statistics + * + * Display the statistics for the current SSI device. To avoid confusion, + * we only show those counts that are enabled. + */ +static int fsl_ssi_stats_show(struct seq_file *s, void *unused) +{ + struct fsl_ssi_dbg *ssi_dbg = s->private; + + SIER_SHOW(RFRC_EN, rfrc); + SIER_SHOW(TFRC_EN, tfrc); + SIER_SHOW(CMDAU_EN, cmdau); + SIER_SHOW(CMDDU_EN, cmddu); + SIER_SHOW(RXT_EN, rxt); + SIER_SHOW(RDR1_EN, rdr1); + SIER_SHOW(RDR0_EN, rdr0); + SIER_SHOW(TDE1_EN, tde1); + SIER_SHOW(TDE0_EN, tde0); + SIER_SHOW(ROE1_EN, roe1); + SIER_SHOW(ROE0_EN, roe0); + SIER_SHOW(TUE1_EN, tue1); + SIER_SHOW(TUE0_EN, tue0); + SIER_SHOW(TFS_EN, tfs); + SIER_SHOW(RFS_EN, rfs); + SIER_SHOW(TLS_EN, tls); + SIER_SHOW(RLS_EN, rls); + SIER_SHOW(RFF1_EN, rff1); + SIER_SHOW(RFF0_EN, rff0); + SIER_SHOW(TFE1_EN, tfe1); + SIER_SHOW(TFE0_EN, tfe0); + + return 0; +} + +static int fsl_ssi_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, fsl_ssi_stats_show, inode->i_private); +} + +static const struct file_operations fsl_ssi_stats_ops = { + .open = fsl_ssi_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) +{ + ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); + if (!ssi_dbg->dbg_dir) + return -ENOMEM; + + ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO, + ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops); + if (!ssi_dbg->dbg_stats) { + debugfs_remove(ssi_dbg->dbg_dir); + return -ENOMEM; + } + + return 0; +} + +void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg) +{ + debugfs_remove(ssi_dbg->dbg_stats); + debugfs_remove(ssi_dbg->dbg_dir); +} diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 2585ae44e634..0849b7b83f0a 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE, .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 65535, /* Limited by SDMA engine */ diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index e94aa0277250..057e5ef7dcce 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev, return ret; } +EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform); static void devm_card_release(struct device *dev, void *res) { |