Loading Documentation/devicetree/bindings/sound/fsl-sai.txt +7 −4 Original line number Original line Diff line number Diff line Loading @@ -7,10 +7,11 @@ codec/DSP interfaces. Required properties: 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. - reg: Offset and length of the register set for the device. - clocks: Must contain an entry for each entry in clock-names. - 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 - dmas : Generic dma devicetree binding as described in Documentation/devicetree/bindings/dma/dma.txt. Documentation/devicetree/bindings/dma/dma.txt. - dma-names : Two dmas have to be defined, "tx" and "rx". - dma-names : Two dmas have to be defined, "tx" and "rx". Loading @@ -30,8 +31,10 @@ sai2: sai@40031000 { reg = <0x40031000 0x1000>; reg = <0x40031000 0x1000>; pinctrl-names = "default"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai2_1>; pinctrl-0 = <&pinctrl_sai2_1>; clocks = <&clks VF610_CLK_SAI2>; clocks = <&clks VF610_CLK_PLATFORM_BUS>, clock-names = "sai"; <&clks VF610_CLK_SAI2>, <&clks 0>, <&clks 0>; clock-names = "bus", "mclk1", "mclk2", "mclk3"; dma-names = "tx", "rx"; dma-names = "tx", "rx"; dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; Loading sound/soc/fsl/Kconfig +57 −18 Original line number Original line Diff line number Diff line menu "SoC Audio for Freescale CPUs" comment "Common SoC Audio options for Freescale CPUs:" config SND_SOC_FSL_SAI config SND_SOC_FSL_SAI tristate tristate "Synchronous Audio Interface (SAI) module support" select REGMAP_MMIO select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM 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 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 config SND_SOC_FSL_SPDIF tristate tristate "Sony/Philips Digital Interface module support" select REGMAP_MMIO 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 config SND_SOC_FSL_ESAI tristate tristate "Enhanced Serial Audio Interface (ESAI) module support" select REGMAP_MMIO select REGMAP_MMIO select SND_SOC_FSL_UTILS 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 config SND_SOC_FSL_UTILS tristate 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" tristate "SoC Audio for Freescale PowerPC CPUs" depends on FSL_SOC || PPC_MPC52xx depends on FSL_SOC || PPC_MPC52xx help help Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to the PowerPC CPUs. 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 if SND_POWERPC_SOC config SND_MPC52xx_DMA config SND_MPC52xx_DMA Loading @@ -33,6 +80,8 @@ config SND_MPC52xx_DMA config SND_SOC_POWERPC_DMA config SND_SOC_POWERPC_DMA tristate tristate comment "SoC Audio support for Freescale PPC boards:" config SND_SOC_MPC8610_HPCD config SND_SOC_MPC8610_HPCD tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" # I2C is necessary for the CS4270 driver # I2C is necessary for the CS4270 driver Loading Loading @@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA endif # SND_POWERPC_SOC 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 if SND_IMX_SOC config SND_SOC_IMX_SSI config SND_SOC_IMX_SSI Loading @@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ tristate tristate select FIQ select FIQ config SND_SOC_IMX_PCM_DMA comment "SoC Audio support for Freescale i.MX boards:" tristate select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX tristate config SND_MXC_SOC_WM1133_EV1 config SND_MXC_SOC_WM1133_EV1 tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" Loading Loading @@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_PCM_DMA endif # SND_IMX_SOC endif # SND_IMX_SOC endmenu sound/soc/fsl/Makefile +2 −1 Original line number Original line Diff line number Diff line Loading @@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-sai-objs := fsl_sai.o 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-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-utils-objs := fsl_utils.o Loading sound/soc/fsl/fsl_esai.c +26 −6 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,8 @@ * @fifo_depth: depth of tx/rx FIFO * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slot_width: width of each DAI slot * @hck_rate: clock rate of desired HCKx clock * @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 * @sck_div: if using PSR/PM dividers for SCKx clock * @slave_mode: if fully using DAI slave mode * @slave_mode: if fully using DAI slave mode * @synchronous: if using tx/rx synchronous mode * @synchronous: if using tx/rx synchronous mode Loading @@ -55,6 +57,8 @@ struct fsl_esai { u32 fifo_depth; u32 fifo_depth; u32 slot_width; u32 slot_width; u32 hck_rate[2]; u32 hck_rate[2]; u32 sck_rate[2]; bool hck_dir[2]; bool sck_div[2]; bool sck_div[2]; bool slave_mode; bool slave_mode; bool synchronous; bool synchronous; Loading Loading @@ -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; struct clk *clksrc = esai_priv->extalclk; bool tx = clk_id <= ESAI_HCKT_EXTAL; bool tx = clk_id <= ESAI_HCKT_EXTAL; bool in = dir == SND_SOC_CLOCK_IN; bool in = dir == SND_SOC_CLOCK_IN; u32 ret, ratio, ecr = 0; u32 ratio, ecr = 0; unsigned long clk_rate; 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 */ /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ esai_priv->sck_div[tx] = true; esai_priv->sck_div[tx] = true; Loading Loading @@ -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; esai_priv->sck_div[tx] = false; out: out: esai_priv->hck_dir[tx] = dir; esai_priv->hck_rate[tx] = freq; esai_priv->hck_rate[tx] = freq; regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, Loading @@ -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); struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); u32 hck_rate = esai_priv->hck_rate[tx]; u32 hck_rate = esai_priv->hck_rate[tx]; u32 sub, ratio = hck_rate / freq; u32 sub, ratio = hck_rate / freq; int ret; /* Don't apply for fully slave mode*/ /* Don't apply for fully slave mode or unchanged bclk */ if (esai_priv->slave_mode) if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq) return 0; return 0; if (ratio * freq > hck_rate) if (ratio * freq > hck_rate) Loading @@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; 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); 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, static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, Loading Loading @@ -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, static int fsl_esai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct snd_soc_dai *dai) { { int ret; struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 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 * Some platforms might use the same bit to gate all three or two of Loading Loading @@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 width = snd_pcm_format_width(params_format(params)); u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(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; bclk = params_rate(params) * esai_priv->slot_width * 2; Loading Loading @@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev) static const struct of_device_id fsl_esai_dt_ids[] = { static const struct of_device_id fsl_esai_dt_ids[] = { { .compatible = "fsl,imx35-esai", }, { .compatible = "fsl,imx35-esai", }, { .compatible = "fsl,vf610-esai", }, {} {} }; }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); Loading sound/soc/fsl/fsl_sai.c +138 −117 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <sound/pcm_params.h> #include <sound/pcm_params.h> #include "fsl_sai.h" #include "fsl_sai.h" #include "imx-pcm.h" #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) FSL_SAI_CSR_FEIE) Loading @@ -30,61 +31,87 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) { { struct fsl_sai *sai = (struct fsl_sai *)devid; struct fsl_sai *sai = (struct fsl_sai *)devid; struct device *dev = &sai->pdev->dev; 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; mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT; /* Tx IRQ */ /* Tx IRQ */ regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr); 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"); 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"); 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"); dev_warn(dev, "isr: Transmit underrun detected\n"); /* FIFO reset for safety */ /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; 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"); 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"); dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); regmap_update_bits(sai->regmap, FSL_SAI_TCSR, flags &= FSL_SAI_CSR_xF_W_MASK; FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); xcsr &= ~FSL_SAI_CSR_xF_MASK; if (flags) regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr); irq_rx: /* Rx IRQ */ /* Rx IRQ */ regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr); 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"); 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"); 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"); dev_warn(dev, "isr: Receive overflow detected\n"); /* FIFO reset for safety */ /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; 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"); 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"); dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, flags &= FSL_SAI_CSR_xF_W_MASK; FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); 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; return IRQ_HANDLED; } } Loading @@ -92,16 +119,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) int clk_id, unsigned int freq, int fsl_dir) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr2, reg_cr2; bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0; 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; switch (clk_id) { switch (clk_id) { case FSL_SAI_CLK_BUS: case FSL_SAI_CLK_BUS: Loading @@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; 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; return 0; } } Loading Loading @@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) unsigned int fmt, int fsl_dir) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr2, val_cr4, reg_cr2, reg_cr4; bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0, val_cr4 = 0; 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; } regmap_read(sai->regmap, reg_cr2, &val_cr2); regmap_read(sai->regmap, reg_cr4, &val_cr4); if (sai->big_endian_data) if (!sai->big_endian_data) val_cr4 &= ~FSL_SAI_CR4_MF; else val_cr4 |= FSL_SAI_CR4_MF; val_cr4 |= FSL_SAI_CR4_MF; /* DAI mode */ /* DAI mode */ Loading @@ -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. * frame sync asserts with the first bit of the frame. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); break; break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A: /* /* Loading @@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, * data word. * data word. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~FSL_SAI_CR4_FSP; val_cr4 |= FSL_SAI_CR4_FSE; val_cr4 |= FSL_SAI_CR4_FSE; sai->is_dsp_mode = true; sai->is_dsp_mode = true; break; break; Loading @@ -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. * frame sync asserts with the first bit of the frame. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); sai->is_dsp_mode = true; sai->is_dsp_mode = true; break; break; case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_RIGHT_J: Loading Loading @@ -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; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM: val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBS_CFM: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFS: val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; break; default: default: return -EINVAL; return -EINVAL; } } regmap_write(sai->regmap, reg_cr2, val_cr2); regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx), regmap_write(sai->regmap, reg_cr4, val_cr4); 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; return 0; } } Loading @@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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); unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(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; } 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; if (!sai->is_dsp_mode) if (!sai->is_dsp_mode) val_cr4 |= FSL_SAI_CR4_SYWD(word_width); val_cr4 |= FSL_SAI_CR4_SYWD(word_width); Loading @@ -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_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) if (sai->big_endian_data) val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr5 |= FSL_SAI_CR5_FBT(0); else else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_mr = ~0UL - ((1 << channels) - 1); regmap_write(sai->regmap, reg_cr4, val_cr4); regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), regmap_write(sai->regmap, reg_cr5, val_cr5); FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, regmap_write(sai->regmap, reg_mr, val_mr); 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; return 0; } } Loading @@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 tcsr, rcsr; u32 tcsr, rcsr; /* /* Loading @@ -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_TCSR, &tcsr); regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); 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 * It is recommended that the transmitter is the last enabled * and the first disabled. * and the first disabled. Loading @@ -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_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: tcsr |= FSL_SAI_CSR_TERE; if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) { rcsr |= FSL_SAI_CSR_TERE; 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_update_bits(sai->regmap, FSL_SAI_xCSR(tx), regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); 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; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), tcsr &= ~FSL_SAI_CSR_TERE; FSL_SAI_CSR_FRDE, 0); rcsr &= ~FSL_SAI_CSR_TERE; regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), } FSL_SAI_CSR_xIE_MASK, 0); regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); /* Check if the opposite FRDE is also disabled */ regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); 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); } break; break; default: default: return -EINVAL; return -EINVAL; Loading @@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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) ret = clk_prepare_enable(sai->bus_clk); reg = FSL_SAI_TCR3; if (ret) { else dev_err(dev, "failed to enable bus clock: %d\n", ret); reg = FSL_SAI_RCR3; 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); FSL_SAI_CR3_TRCE); return 0; return 0; Loading @@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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) regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0); reg = FSL_SAI_TCR3; else reg = FSL_SAI_RCR3; regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, clk_disable_unprepare(sai->bus_clk); ~FSL_SAI_CR3_TRCE); } } static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { Loading @@ -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); 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_TCSR, 0xffffffff, 0x0); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, FSL_SAI_MAXBURST_TX * 2); FSL_SAI_MAXBURST_TX * 2); regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, Loading Loading @@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev) struct fsl_sai *sai; struct fsl_sai *sai; struct resource *res; struct resource *res; void __iomem *base; void __iomem *base; int irq, ret; char tmp[8]; int irq, ret, i; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) if (!sai) Loading @@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->pdev = 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"); sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); if (sai->big_endian_regs) if (sai->big_endian_regs) fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; Loading @@ -574,6 +568,11 @@ static int fsl_sai_probe(struct platform_device *pdev) if (IS_ERR(base)) if (IS_ERR(base)) return PTR_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->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai", base, &fsl_sai_regmap_config); "sai", base, &fsl_sai_regmap_config); if (IS_ERR(sai->regmap)) { if (IS_ERR(sai->regmap)) { Loading @@ -581,6 +580,24 @@ static int fsl_sai_probe(struct platform_device *pdev) return PTR_ERR(sai->regmap); 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); irq = platform_get_irq(pdev, 0); if (irq < 0) { if (irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); Loading @@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret) if (ret) return ret; return ret; if (sai->sai_on_imx) return imx_pcm_dma_init(pdev); else return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); } } static const struct of_device_id fsl_sai_ids[] = { static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,imx6sx-sai", }, { /* sentinel */ } { /* sentinel */ } }; }; Loading Loading
Documentation/devicetree/bindings/sound/fsl-sai.txt +7 −4 Original line number Original line Diff line number Diff line Loading @@ -7,10 +7,11 @@ codec/DSP interfaces. Required properties: 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. - reg: Offset and length of the register set for the device. - clocks: Must contain an entry for each entry in clock-names. - 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 - dmas : Generic dma devicetree binding as described in Documentation/devicetree/bindings/dma/dma.txt. Documentation/devicetree/bindings/dma/dma.txt. - dma-names : Two dmas have to be defined, "tx" and "rx". - dma-names : Two dmas have to be defined, "tx" and "rx". Loading @@ -30,8 +31,10 @@ sai2: sai@40031000 { reg = <0x40031000 0x1000>; reg = <0x40031000 0x1000>; pinctrl-names = "default"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai2_1>; pinctrl-0 = <&pinctrl_sai2_1>; clocks = <&clks VF610_CLK_SAI2>; clocks = <&clks VF610_CLK_PLATFORM_BUS>, clock-names = "sai"; <&clks VF610_CLK_SAI2>, <&clks 0>, <&clks 0>; clock-names = "bus", "mclk1", "mclk2", "mclk3"; dma-names = "tx", "rx"; dma-names = "tx", "rx"; dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; Loading
sound/soc/fsl/Kconfig +57 −18 Original line number Original line Diff line number Diff line menu "SoC Audio for Freescale CPUs" comment "Common SoC Audio options for Freescale CPUs:" config SND_SOC_FSL_SAI config SND_SOC_FSL_SAI tristate tristate "Synchronous Audio Interface (SAI) module support" select REGMAP_MMIO select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM 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 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 config SND_SOC_FSL_SPDIF tristate tristate "Sony/Philips Digital Interface module support" select REGMAP_MMIO 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 config SND_SOC_FSL_ESAI tristate tristate "Enhanced Serial Audio Interface (ESAI) module support" select REGMAP_MMIO select REGMAP_MMIO select SND_SOC_FSL_UTILS 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 config SND_SOC_FSL_UTILS tristate 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" tristate "SoC Audio for Freescale PowerPC CPUs" depends on FSL_SOC || PPC_MPC52xx depends on FSL_SOC || PPC_MPC52xx help help Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to the PowerPC CPUs. 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 if SND_POWERPC_SOC config SND_MPC52xx_DMA config SND_MPC52xx_DMA Loading @@ -33,6 +80,8 @@ config SND_MPC52xx_DMA config SND_SOC_POWERPC_DMA config SND_SOC_POWERPC_DMA tristate tristate comment "SoC Audio support for Freescale PPC boards:" config SND_SOC_MPC8610_HPCD config SND_SOC_MPC8610_HPCD tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" # I2C is necessary for the CS4270 driver # I2C is necessary for the CS4270 driver Loading Loading @@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA endif # SND_POWERPC_SOC 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 if SND_IMX_SOC config SND_SOC_IMX_SSI config SND_SOC_IMX_SSI Loading @@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ tristate tristate select FIQ select FIQ config SND_SOC_IMX_PCM_DMA comment "SoC Audio support for Freescale i.MX boards:" tristate select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX tristate config SND_MXC_SOC_WM1133_EV1 config SND_MXC_SOC_WM1133_EV1 tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" Loading Loading @@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_PCM_DMA endif # SND_IMX_SOC endif # SND_IMX_SOC endmenu
sound/soc/fsl/Makefile +2 −1 Original line number Original line Diff line number Diff line Loading @@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-sai-objs := fsl_sai.o 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-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-utils-objs := fsl_utils.o Loading
sound/soc/fsl/fsl_esai.c +26 −6 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,8 @@ * @fifo_depth: depth of tx/rx FIFO * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slot_width: width of each DAI slot * @hck_rate: clock rate of desired HCKx clock * @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 * @sck_div: if using PSR/PM dividers for SCKx clock * @slave_mode: if fully using DAI slave mode * @slave_mode: if fully using DAI slave mode * @synchronous: if using tx/rx synchronous mode * @synchronous: if using tx/rx synchronous mode Loading @@ -55,6 +57,8 @@ struct fsl_esai { u32 fifo_depth; u32 fifo_depth; u32 slot_width; u32 slot_width; u32 hck_rate[2]; u32 hck_rate[2]; u32 sck_rate[2]; bool hck_dir[2]; bool sck_div[2]; bool sck_div[2]; bool slave_mode; bool slave_mode; bool synchronous; bool synchronous; Loading Loading @@ -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; struct clk *clksrc = esai_priv->extalclk; bool tx = clk_id <= ESAI_HCKT_EXTAL; bool tx = clk_id <= ESAI_HCKT_EXTAL; bool in = dir == SND_SOC_CLOCK_IN; bool in = dir == SND_SOC_CLOCK_IN; u32 ret, ratio, ecr = 0; u32 ratio, ecr = 0; unsigned long clk_rate; 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 */ /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ esai_priv->sck_div[tx] = true; esai_priv->sck_div[tx] = true; Loading Loading @@ -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; esai_priv->sck_div[tx] = false; out: out: esai_priv->hck_dir[tx] = dir; esai_priv->hck_rate[tx] = freq; esai_priv->hck_rate[tx] = freq; regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, Loading @@ -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); struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); u32 hck_rate = esai_priv->hck_rate[tx]; u32 hck_rate = esai_priv->hck_rate[tx]; u32 sub, ratio = hck_rate / freq; u32 sub, ratio = hck_rate / freq; int ret; /* Don't apply for fully slave mode*/ /* Don't apply for fully slave mode or unchanged bclk */ if (esai_priv->slave_mode) if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq) return 0; return 0; if (ratio * freq > hck_rate) if (ratio * freq > hck_rate) Loading @@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; 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); 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, static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, Loading Loading @@ -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, static int fsl_esai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct snd_soc_dai *dai) { { int ret; struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); 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 * Some platforms might use the same bit to gate all three or two of Loading Loading @@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 width = snd_pcm_format_width(params_format(params)); u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(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; bclk = params_rate(params) * esai_priv->slot_width * 2; Loading Loading @@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev) static const struct of_device_id fsl_esai_dt_ids[] = { static const struct of_device_id fsl_esai_dt_ids[] = { { .compatible = "fsl,imx35-esai", }, { .compatible = "fsl,imx35-esai", }, { .compatible = "fsl,vf610-esai", }, {} {} }; }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); Loading
sound/soc/fsl/fsl_sai.c +138 −117 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <sound/pcm_params.h> #include <sound/pcm_params.h> #include "fsl_sai.h" #include "fsl_sai.h" #include "imx-pcm.h" #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) FSL_SAI_CSR_FEIE) Loading @@ -30,61 +31,87 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) { { struct fsl_sai *sai = (struct fsl_sai *)devid; struct fsl_sai *sai = (struct fsl_sai *)devid; struct device *dev = &sai->pdev->dev; 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; mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT; /* Tx IRQ */ /* Tx IRQ */ regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr); 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"); 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"); 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"); dev_warn(dev, "isr: Transmit underrun detected\n"); /* FIFO reset for safety */ /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; 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"); 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"); dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); regmap_update_bits(sai->regmap, FSL_SAI_TCSR, flags &= FSL_SAI_CSR_xF_W_MASK; FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); xcsr &= ~FSL_SAI_CSR_xF_MASK; if (flags) regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr); irq_rx: /* Rx IRQ */ /* Rx IRQ */ regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr); 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"); 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"); 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"); dev_warn(dev, "isr: Receive overflow detected\n"); /* FIFO reset for safety */ /* FIFO reset for safety */ xcsr |= FSL_SAI_CSR_FR; 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"); 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"); dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, flags &= FSL_SAI_CSR_xF_W_MASK; FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); 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; return IRQ_HANDLED; } } Loading @@ -92,16 +119,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) int clk_id, unsigned int freq, int fsl_dir) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr2, reg_cr2; bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0; 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; switch (clk_id) { switch (clk_id) { case FSL_SAI_CLK_BUS: case FSL_SAI_CLK_BUS: Loading @@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; 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; return 0; } } Loading Loading @@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) unsigned int fmt, int fsl_dir) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr2, val_cr4, reg_cr2, reg_cr4; bool tx = fsl_dir == FSL_FMT_TRANSMITTER; u32 val_cr2 = 0, val_cr4 = 0; 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; } regmap_read(sai->regmap, reg_cr2, &val_cr2); regmap_read(sai->regmap, reg_cr4, &val_cr4); if (sai->big_endian_data) if (!sai->big_endian_data) val_cr4 &= ~FSL_SAI_CR4_MF; else val_cr4 |= FSL_SAI_CR4_MF; val_cr4 |= FSL_SAI_CR4_MF; /* DAI mode */ /* DAI mode */ Loading @@ -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. * frame sync asserts with the first bit of the frame. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); break; break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A: /* /* Loading @@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, * data word. * data word. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~FSL_SAI_CR4_FSP; val_cr4 |= FSL_SAI_CR4_FSE; val_cr4 |= FSL_SAI_CR4_FSE; sai->is_dsp_mode = true; sai->is_dsp_mode = true; break; break; Loading @@ -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. * frame sync asserts with the first bit of the frame. */ */ val_cr2 |= FSL_SAI_CR2_BCP; val_cr2 |= FSL_SAI_CR2_BCP; val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); sai->is_dsp_mode = true; sai->is_dsp_mode = true; break; break; case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_RIGHT_J: Loading Loading @@ -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; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM: val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBS_CFM: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFS: val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; break; default: default: return -EINVAL; return -EINVAL; } } regmap_write(sai->regmap, reg_cr2, val_cr2); regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx), regmap_write(sai->regmap, reg_cr4, val_cr4); 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; return 0; } } Loading @@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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); unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(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; } 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; if (!sai->is_dsp_mode) if (!sai->is_dsp_mode) val_cr4 |= FSL_SAI_CR4_SYWD(word_width); val_cr4 |= FSL_SAI_CR4_SYWD(word_width); Loading @@ -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_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) if (sai->big_endian_data) val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr5 |= FSL_SAI_CR5_FBT(0); else else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_mr = ~0UL - ((1 << channels) - 1); regmap_write(sai->regmap, reg_cr4, val_cr4); regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), regmap_write(sai->regmap, reg_cr5, val_cr5); FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, regmap_write(sai->regmap, reg_mr, val_mr); 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; return 0; } } Loading @@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 tcsr, rcsr; u32 tcsr, rcsr; /* /* Loading @@ -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_TCSR, &tcsr); regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); 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 * It is recommended that the transmitter is the last enabled * and the first disabled. * and the first disabled. Loading @@ -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_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: tcsr |= FSL_SAI_CSR_TERE; if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) { rcsr |= FSL_SAI_CSR_TERE; 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_update_bits(sai->regmap, FSL_SAI_xCSR(tx), regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); 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; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), tcsr &= ~FSL_SAI_CSR_TERE; FSL_SAI_CSR_FRDE, 0); rcsr &= ~FSL_SAI_CSR_TERE; regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx), } FSL_SAI_CSR_xIE_MASK, 0); regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); /* Check if the opposite FRDE is also disabled */ regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); 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); } break; break; default: default: return -EINVAL; return -EINVAL; Loading @@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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) ret = clk_prepare_enable(sai->bus_clk); reg = FSL_SAI_TCR3; if (ret) { else dev_err(dev, "failed to enable bus clock: %d\n", ret); reg = FSL_SAI_RCR3; 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); FSL_SAI_CR3_TRCE); return 0; return 0; Loading @@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai) { { struct fsl_sai *sai = snd_soc_dai_get_drvdata(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) regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0); reg = FSL_SAI_TCR3; else reg = FSL_SAI_RCR3; regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, clk_disable_unprepare(sai->bus_clk); ~FSL_SAI_CR3_TRCE); } } static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { Loading @@ -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); 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_TCSR, 0xffffffff, 0x0); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, FSL_SAI_MAXBURST_TX * 2); FSL_SAI_MAXBURST_TX * 2); regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, Loading Loading @@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev) struct fsl_sai *sai; struct fsl_sai *sai; struct resource *res; struct resource *res; void __iomem *base; void __iomem *base; int irq, ret; char tmp[8]; int irq, ret, i; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) if (!sai) Loading @@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->pdev = 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"); sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); if (sai->big_endian_regs) if (sai->big_endian_regs) fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; Loading @@ -574,6 +568,11 @@ static int fsl_sai_probe(struct platform_device *pdev) if (IS_ERR(base)) if (IS_ERR(base)) return PTR_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->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai", base, &fsl_sai_regmap_config); "sai", base, &fsl_sai_regmap_config); if (IS_ERR(sai->regmap)) { if (IS_ERR(sai->regmap)) { Loading @@ -581,6 +580,24 @@ static int fsl_sai_probe(struct platform_device *pdev) return PTR_ERR(sai->regmap); 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); irq = platform_get_irq(pdev, 0); if (irq < 0) { if (irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); Loading @@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret) if (ret) return ret; return ret; if (sai->sai_on_imx) return imx_pcm_dma_init(pdev); else return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); } } static const struct of_device_id fsl_sai_ids[] = { static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,imx6sx-sai", }, { /* sentinel */ } { /* sentinel */ } }; }; Loading