Commit 3a6a489f authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl',...

Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl', 'asoc/topic/fsl-esai', 'asoc/topic/fsl-sai', 'asoc/topic/fsl-spdif' and 'asoc/topic/fsl-ssi' into asoc-next
...@@ -7,10 +7,11 @@ codec/DSP interfaces. ...@@ -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".
...@@ -30,8 +31,10 @@ sai2: sai@40031000 { ...@@ -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>;
......
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
...@@ -33,6 +80,8 @@ config SND_MPC52xx_DMA ...@@ -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
...@@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA ...@@ -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
...@@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ ...@@ -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"
...@@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783 ...@@ -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
...@@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o ...@@ -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
......
...@@ -39,6 +39,8 @@ ...@@ -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
...@@ -55,6 +57,8 @@ struct fsl_esai { ...@@ -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;
...@@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, ...@@ -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;
...@@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, ...@@ -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,
...@@ -294,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) ...@@ -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)
...@@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) ...@@ -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,
...@@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -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
...@@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, ...@@ -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;
...@@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev) ...@@ -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);
......
...@@ -22,6 +22,7 @@ ...@@ -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)
...@@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) ...@@ -30,78 +31,96 @@ 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);
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, 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:
...@@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, ...@@ -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;
} }
...@@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, ...@@ -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); if (!sai->big_endian_data)
regmap_read(sai->regmap, reg_cr4, &val_cr4);
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 */
...@@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, ...@@ -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:
/* /*
...@@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, ...@@ -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;
...@@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, ...@@ -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:
...@@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, ...@@ -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;
} }
...@@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, ...@@ -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);
...@@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, ...@@ -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;
} }
...@@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -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;
/* /*
...@@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -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.
...@@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -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);
/* 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; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, ...@@ -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;
...@@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, ...@@ -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 = {
...@@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) ...@@ -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,
...@@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -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)
...@@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -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;
...@@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev)
return PTR_ERR(base); return PTR_ERR(base);
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, 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)) { if (IS_ERR(sai->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n"); dev_err(&pdev->dev, "regmap init failed\n");
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);
...@@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, if (sai->sai_on_imx)
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); 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[] = { static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,vf610-sai", },
{ .compatible = "fsl,imx6sx-sai", },
{ /* sentinel */ } { /* sentinel */ }
}; };
......
...@@ -35,6 +35,16 @@ ...@@ -35,6 +35,16 @@
#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */ #define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ #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 */ /* SAI Transmit/Recieve Control Register */
#define FSL_SAI_CSR_TERE BIT(31) #define FSL_SAI_CSR_TERE BIT(31)
#define FSL_SAI_CSR_FR BIT(25) #define FSL_SAI_CSR_FR BIT(25)
...@@ -48,6 +58,7 @@ ...@@ -48,6 +58,7 @@
#define FSL_SAI_CSR_FWF BIT(17) #define FSL_SAI_CSR_FWF BIT(17)
#define FSL_SAI_CSR_FRF BIT(16) #define FSL_SAI_CSR_FRF BIT(16)
#define FSL_SAI_CSR_xIE_SHIFT 8 #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_WSIE BIT(12)
#define FSL_SAI_CSR_SEIE BIT(11) #define FSL_SAI_CSR_SEIE BIT(11)
#define FSL_SAI_CSR_FEIE BIT(10) #define FSL_SAI_CSR_FEIE BIT(10)
...@@ -108,6 +119,8 @@ ...@@ -108,6 +119,8 @@
#define FSL_SAI_CLK_MAST2 2 #define FSL_SAI_CLK_MAST2 2
#define FSL_SAI_CLK_MAST3 3 #define FSL_SAI_CLK_MAST3 3
#define FSL_SAI_MCLK_MAX 3
/* SAI data transfer numbers per DMA request */ /* SAI data transfer numbers per DMA request */
#define FSL_SAI_MAXBURST_TX 6 #define FSL_SAI_MAXBURST_TX 6
#define FSL_SAI_MAXBURST_RX 6 #define FSL_SAI_MAXBURST_RX 6
...@@ -115,10 +128,13 @@ ...@@ -115,10 +128,13 @@
struct fsl_sai { struct fsl_sai {
struct platform_device *pdev; struct platform_device *pdev;
struct regmap *regmap; struct regmap *regmap;
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
bool big_endian_regs; bool big_endian_regs;
bool big_endian_data; bool big_endian_data;
bool is_dsp_mode; 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_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_tx;
......
...@@ -13,18 +13,18 @@ ...@@ -13,18 +13,18 @@
* kind, whether express or implied. * kind, whether express or implied.
*/ */
#include <linux/module.h> #include <linux/bitrev.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-private.h> #include <linux/clk-private.h>
#include <linux/bitrev.h> #include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/regmap.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
#include "fsl_spdif.h" #include "fsl_spdif.h"
#include "imx-pcm.h" #include "imx-pcm.h"
...@@ -69,17 +69,42 @@ struct spdif_mixer_control { ...@@ -69,17 +69,42 @@ struct spdif_mixer_control {
u32 ready_buf; 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 fsl_spdif_priv {
struct spdif_mixer_control fsl_spdif_control; struct spdif_mixer_control fsl_spdif_control;
struct snd_soc_dai_driver cpu_dai_drv; struct snd_soc_dai_driver cpu_dai_drv;
struct platform_device *pdev; struct platform_device *pdev;
struct regmap *regmap; struct regmap *regmap;
bool dpll_locked; 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 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src; u8 rxclk_src;
struct clk *txclk[SPDIF_TXRATE_MAX]; struct clk *txclk[SPDIF_TXRATE_MAX];
struct clk *rxclk; 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_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_rx;
...@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, ...@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
struct platform_device *pdev = spdif_priv->pdev; struct platform_device *pdev = spdif_priv->pdev;
unsigned long csfs = 0; unsigned long csfs = 0;
u32 stc, mask, rate; u32 stc, mask, rate;
u8 clk, div; u8 clk, txclk_df, sysclk_df;
int ret; int ret;
switch (sample_rate) { switch (sample_rate) {
...@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, ...@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
div = spdif_priv->txclk_div[rate]; txclk_df = spdif_priv->txclk_df[rate];
if (div == 0) { if (txclk_df == 0) {
dev_err(&pdev->dev, "the divisor can't be zero\n"); dev_err(&pdev->dev, "the txclk_df can't be zero\n");
return -EINVAL; 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 * The S/PDIF block needs a clock of 64 * fs * txclk_df.
* will divide by (div). So request 64 * fs * (div+1) which will * So request 64 * fs * (txclk_df + 1) to get rounded.
* 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) { if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n"); dev_err(&pdev->dev, "failed to set tx clock rate\n");
return ret; return ret;
} }
clk_set_bypass:
dev_dbg(&pdev->dev, "expected clock rate = %d\n", 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", dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
clk_get_rate(spdif_priv->txclk[rate])); clk_get_rate(spdif_priv->txclk[rate]));
...@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, ...@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
/* select clock source and divisor */ /* select clock source and divisor */
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div); 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_DIV_MASK; mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc); 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; return 0;
} }
...@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, ...@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
/* Reset module and interrupts only for first initialization */ /* Reset module and interrupts only for first initialization */
if (!cpu_dai->active) { 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); ret = spdif_softreset(spdif_priv);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n"); dev_err(&pdev->dev, "failed to soft reset\n");
return ret; goto err;
} }
/* Disable all the interrupts */ /* Disable all the interrupts */
...@@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, ...@@ -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); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
return 0; return 0;
err:
clk_disable_unprepare(spdif_priv->coreclk);
return ret;
} }
static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
...@@ -484,6 +530,7 @@ 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); spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR, regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER); 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, ...@@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
/* Get bus clock from system */ /* 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 */ /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
...@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = { ...@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
struct clk *clk, u64 savesub, struct clk *clk, u64 savesub,
enum spdif_txrate index) enum spdif_txrate index, bool round)
{ {
const u32 rate[] = { 32000, 44100, 48000 }; const u32 rate[] = { 32000, 44100, 48000 };
bool is_sysclk = clk == spdif_priv->sysclk;
u64 rate_ideal, rate_actual, sub; u64 rate_ideal, rate_actual, sub;
u32 div, arate; u32 sysclk_dfmin, sysclk_dfmax;
u32 txclk_df, sysclk_df, arate;
for (div = 1; div <= 128; div++) {
rate_ideal = rate[index] * (div + 1) * 64; /* The sysclk has an extra divisor [2, 512] */
rate_actual = clk_round_rate(clk, rate_ideal); sysclk_dfmin = is_sysclk ? 2 : 1;
sysclk_dfmax = is_sysclk ? 512 : 1;
arate = rate_actual / 64;
arate /= div; for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
if (arate == rate[index]) { rate_ideal = rate[index] * (txclk_df + 1) * 64;
/* We are lucky */ if (round)
savesub = 0; rate_actual = clk_round_rate(clk, rate_ideal);
spdif_priv->txclk_div[index] = div; else
break; rate_actual = clk_get_rate(clk);
} else if (arate / rate[index] == 1) {
/* A little bigger than expect */ arate = rate_actual / 64;
sub = (arate - rate[index]) * 100000; arate /= txclk_df * sysclk_df;
do_div(sub, rate[index]);
if (sub < savesub) { 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; savesub = sub;
spdif_priv->txclk_div[index] = div; spdif_priv->txclk_df[index] = txclk_df;
} spdif_priv->sysclk_df[index] = sysclk_df;
} else if (rate[index] / arate == 1) { spdif_priv->txrate[index] = arate;
/* A little smaller than expect */ } else if (rate[index] / arate == 1) {
sub = (rate[index] - arate) * 100000; /* A little smaller than expect */
do_div(sub, rate[index]); sub = (rate[index] - arate) * 100000;
if (sub < savesub) { do_div(sub, rate[index]);
if (sub >= savesub)
continue;
savesub = sub; 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; return savesub;
} }
...@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, ...@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
if (!clk_get_rate(clk)) if (!clk_get_rate(clk))
continue; 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) if (savesub == ret)
continue; continue;
...@@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, ...@@ -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", dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
spdif_priv->txclk_src[index], rate[index]); spdif_priv->txclk_src[index], rate[index]);
dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n", dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
spdif_priv->txclk_div[index], rate[index]); 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; return 0;
} }
...@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev) ...@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret; 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 */ /* Select clock source for rx/tx clock */
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
if (IS_ERR(spdif_priv->rxclk)) { if (IS_ERR(spdif_priv->rxclk)) {
...@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) ...@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
static const struct of_device_id fsl_spdif_dt_ids[] = { static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", }, { .compatible = "fsl,imx35-spdif", },
{ .compatible = "fsl,vf610-spdif", },
{} {}
}; };
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
......
...@@ -143,20 +143,22 @@ enum spdif_gainsel { ...@@ -143,20 +143,22 @@ enum spdif_gainsel {
#define INT_RXFIFO_FUL (1 << 0) #define INT_RXFIFO_FUL (1 << 0)
/* SPDIF Clock register */ /* SPDIF Clock register */
#define STC_SYSCLK_DIV_OFFSET 11 #define STC_SYSCLK_DF_OFFSET 11
#define STC_SYSCLK_DIV_MASK (0x1ff << STC_SYSCLK_DIV_OFFSET) #define STC_SYSCLK_DF_MASK (0x1ff << STC_SYSCLK_DF_OFFSET)
#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK) #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_OFFSET 8
#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET) #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_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
#define STC_TXCLK_ALL_EN_OFFSET 7 #define STC_TXCLK_ALL_EN_OFFSET 7
#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) #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_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
#define STC_TXCLK_DIV_OFFSET 0 #define STC_TXCLK_DF_OFFSET 0
#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET) #define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET)
#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) #define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
#define STC_TXCLK_SRC_MAX 8 #define STC_TXCLK_SRC_MAX 8
#define STC_TXCLK_SPDIF_ROOT 1
/* SPDIF tx rate */ /* SPDIF tx rate */
enum spdif_txrate { enum spdif_txrate {
SPDIF_TXRATE_32000 = 0, SPDIF_TXRATE_32000 = 0,
......
...@@ -35,11 +35,11 @@ ...@@ -35,11 +35,11 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
...@@ -113,8 +113,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) ...@@ -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 | \ #define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_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 { enum fsl_ssi_type {
FSL_SSI_MCP8610, FSL_SSI_MCP8610,
...@@ -146,7 +144,6 @@ struct fsl_ssi_rxtx_reg_val { ...@@ -146,7 +144,6 @@ struct fsl_ssi_rxtx_reg_val {
* @cpu_dai: the CPU DAI for this device * @cpu_dai: the CPU DAI for this device
* @dev_attr: the sysfs device attribute structure * @dev_attr: the sysfs device attribute structure
* @stats: SSI statistics * @stats: SSI statistics
* @name: name for this device
*/ */
struct fsl_ssi_private { struct fsl_ssi_private {
struct ccsr_ssi __iomem *ssi; struct ccsr_ssi __iomem *ssi;
...@@ -155,15 +152,11 @@ struct fsl_ssi_private { ...@@ -155,15 +152,11 @@ struct fsl_ssi_private {
unsigned int fifo_depth; unsigned int fifo_depth;
struct snd_soc_dai_driver cpu_dai_drv; struct snd_soc_dai_driver cpu_dai_drv;
struct platform_device *pdev; struct platform_device *pdev;
unsigned int dai_fmt;
enum fsl_ssi_type hw_type; enum fsl_ssi_type hw_type;
bool new_binding;
bool ssi_on_imx;
bool imx_ac97;
bool use_dma; bool use_dma;
bool baudclk_locked; bool baudclk_locked;
bool irq_stats;
bool offline_config;
bool use_dual_fifo; bool use_dual_fifo;
u8 i2s_mode; u8 i2s_mode;
spinlock_t baudclk_lock; spinlock_t baudclk_lock;
...@@ -171,39 +164,11 @@ struct fsl_ssi_private { ...@@ -171,39 +164,11 @@ struct fsl_ssi_private {
struct clk *clk; struct clk *clk;
struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx; 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; struct imx_pcm_fiq_params fiq_params;
/* Register values for rx/tx configuration */ /* Register values for rx/tx configuration */
struct fsl_ssi_rxtx_reg_val rxtx_reg_val; struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
struct { struct fsl_ssi_dbg dbg_stats;
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];
}; };
static const struct of_device_id fsl_ssi_ids[] = { static const struct of_device_id fsl_ssi_ids[] = {
...@@ -215,6 +180,54 @@ 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); 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 * fsl_ssi_isr: SSI interrupt handler
* *
...@@ -231,7 +244,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) ...@@ -231,7 +244,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
{ {
struct fsl_ssi_private *ssi_private = dev_id; struct fsl_ssi_private *ssi_private = dev_id;
struct ccsr_ssi __iomem *ssi = ssi_private->ssi; struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
irqreturn_t ret = IRQ_NONE;
__be32 sisr; __be32 sisr;
__be32 sisr2; __be32 sisr2;
__be32 sisr_write_mask = 0; __be32 sisr_write_mask = 0;
...@@ -258,217 +270,18 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) ...@@ -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 were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in. so that we only check for events that we're interested in.
*/ */
sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK; sisr = read_ssi(&ssi->sisr);
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;
}
sisr2 = sisr & sisr_write_mask; sisr2 = sisr & sisr_write_mask;
/* Clear the bits that we set */ /* Clear the bits that we set */
if (sisr2) if (sisr2)
write_ssi(sisr2, &ssi->sisr); write_ssi(sisr2, &ssi->sisr);
return ret; fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
}
#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);
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. * Enable/Disable all rx/tx config flags at once.
*/ */
...@@ -489,6 +302,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, ...@@ -489,6 +302,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 * Enable/Disable a ssi configuration. You have to pass either
* ssi_private->rxtx_reg_val.rx or tx as vals parameter. * 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, ...@@ -501,6 +334,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
u32 scr_val = read_ssi(&ssi->scr); u32 scr_val = read_ssi(&ssi->scr);
int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) + int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
!!(scr_val & CCSR_SSI_SCR_RE); !!(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 /* Find the other direction values rx or tx which we do not want to
* modify */ * modify */
...@@ -511,7 +350,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -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 vals should be disabled, start with disabling the unit */
if (!enable) { 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); write_ssi_mask(&ssi->scr, scr, 0);
} }
...@@ -520,9 +360,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -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 * reconfiguration, so we have to enable all necessary flags at once
* even if we do not use them later (capture and playback configuration) * 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) || if ((enable && !nr_active_streams) ||
(!enable && nr_active_streams == 1)) (!enable && !keep_active))
fsl_ssi_rxtx_config(ssi_private, enable); fsl_ssi_rxtx_config(ssi_private, enable);
goto config_done; goto config_done;
...@@ -551,9 +391,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -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*/ /* These assignments are simply vals without bits set in avals*/
sier = vals->sier & (vals->sier ^ avals->sier); sier = fsl_ssi_disable_val(vals->sier, avals->sier,
srcr = vals->srcr & (vals->srcr ^ avals->srcr); keep_active);
stcr = vals->stcr & (vals->stcr ^ avals->stcr); 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->srcr, srcr, 0);
write_ssi_mask(&ssi->stcr, stcr, 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) ...@@ -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.stcr = CCSR_SSI_STCR_TFEN0;
reg->tx.scr = 0; 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.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN; reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE; 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) ...@@ -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); 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 * fsl_ssi_startup: create a new substream
* *
...@@ -748,12 +501,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ...@@ -748,12 +501,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
snd_soc_dai_get_drvdata(rtd->cpu_dai); snd_soc_dai_get_drvdata(rtd->cpu_dai);
unsigned long flags; unsigned long flags;
/* First, we only do fsl_ssi_setup() when SSI is going to be active. if (!dai->active && !fsl_ssi_is_ac97(ssi_private)) {
* 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);
spin_lock_irqsave(&ssi_private->baudclk_lock, flags); spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false; ssi_private->baudclk_locked = false;
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
...@@ -771,6 +519,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ...@@ -771,6 +519,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
return 0; 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;
}
/* 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 * fsl_ssi_hw_params - program the sample size
* *
...@@ -819,7 +663,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, ...@@ -819,7 +663,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
else else
write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); 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, write_ssi_mask(&ssi->scr,
CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
channels == 1 ? 0 : ssi_private->i2s_mode); 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) ...@@ -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 fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi; struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
u32 strcr = 0, stcr, srcr, scr, mask; 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 = 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 | 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 | 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) ...@@ -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; stcr = read_ssi(&ssi->stcr) & ~mask;
srcr = read_ssi(&ssi->srcr) & ~mask; srcr = read_ssi(&ssi->srcr) & ~mask;
ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS: 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; break;
case SND_SOC_DAIFMT_CBM_CFM: 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; break;
default: default:
return -EINVAL; return -EINVAL;
} }
scr |= ssi_private->i2s_mode;
/* Data on rising edge of bclk, frame low, 1clk before data */ /* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP | 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) ...@@ -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 | strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
CCSR_SSI_STCR_TXBIT0; CCSR_SSI_STCR_TXBIT0;
break; break;
case SND_SOC_DAIFMT_AC97:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
scr |= ssi_private->i2s_mode;
/* DAI clock inversion */ /* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 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) ...@@ -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(srcr, &ssi->srcr);
write_ssi(scr, &ssi->scr); write_ssi(scr, &ssi->scr);
return 0; /*
} * 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
* fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock * elements equals one frame (left channel, right channel). This value,
* * however, depends on the depth of the transmit buffer.
* Note: This function can be only called when using SSI as DAI master *
* * We set the watermark on the same level as the DMA burstsize. For
* Quick instruction for parameters: * fiq it is probably better to use the biggest possible watermark
* freq: Output BCLK frequency = samplerate * 32 (fixed) * channels * size.
* dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. */
*/ if (ssi_private->use_dma)
static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, wm = ssi_private->fifo_depth - 2;
int clk_id, unsigned int freq, int dir) else
{ wm = ssi_private->fifo_depth;
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 */ write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
if (savesub == 0) CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
break; &ssi->sfcsr);
}
/* No proper pm found if it is still remaining the initial value */ if (ssi_private->use_dual_fifo) {
if (pm == 999) { write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1,
dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); CCSR_SSI_SRCR_RFEN1);
return -EINVAL; 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) | if (fmt & SND_SOC_DAIFMT_AC97)
(psr ? CCSR_SSI_SxCCR_PSR : 0); fsl_ssi_setup_ac97(ssi_private);
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; return 0;
} }
...@@ -1097,7 +888,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -1097,7 +888,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
else else
fsl_ssi_rx_config(ssi_private, false); 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) { (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
spin_lock_irqsave(&ssi_private->baudclk_lock, flags); spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false; ssi_private->baudclk_locked = false;
...@@ -1109,7 +900,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -1109,7 +900,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL; return -EINVAL;
} }
if (ssi_private->imx_ac97) { if (fsl_ssi_is_ac97(ssi_private)) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
else else
...@@ -1123,7 +914,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) ...@@ -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); 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->playback_dma_data = &ssi_private->dma_params_tx;
dai->capture_dma_data = &ssi_private->dma_params_rx; dai->capture_dma_data = &ssi_private->dma_params_rx;
} }
...@@ -1184,11 +975,6 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { ...@@ -1184,11 +975,6 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
static struct fsl_ssi_private *fsl_ac97_data; 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, static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val) unsigned short val)
{ {
...@@ -1251,11 +1037,107 @@ static void make_lowercase(char *s) ...@@ -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) static int fsl_ssi_probe(struct platform_device *pdev)
{ {
struct fsl_ssi_private *ssi_private; struct fsl_ssi_private *ssi_private;
int ret = 0; int ret = 0;
struct device_attribute *dev_attr = NULL;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id; const struct of_device_id *of_id;
enum fsl_ssi_type hw_type; enum fsl_ssi_type hw_type;
...@@ -1263,7 +1145,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1263,7 +1145,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
const uint32_t *iprop; const uint32_t *iprop;
struct resource res; struct resource res;
char name[64]; char name[64];
bool shared;
bool ac97 = false; bool ac97 = false;
/* SSIs that are not connected on the board should have a /* SSIs that are not connected on the board should have a
...@@ -1286,17 +1167,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1286,17 +1167,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (!strcmp(sprop, "ac97-slave")) if (!strcmp(sprop, "ac97-slave"))
ac97 = true; ac97 = true;
/* The DAI name is the last part of the full name of the node. */ ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private),
p = strrchr(np->full_name, '/') + 1; GFP_KERNEL);
ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
GFP_KERNEL);
if (!ssi_private) { if (!ssi_private) {
dev_err(&pdev->dev, "could not allocate DAI object\n"); dev_err(&pdev->dev, "could not allocate DAI object\n");
return -ENOMEM; return -ENOMEM;
} }
strcpy(ssi_private->name, p);
ssi_private->use_dma = !of_property_read_bool(np, ssi_private->use_dma = !of_property_read_bool(np,
"fsl,fiq-stream-filter"); "fsl,fiq-stream-filter");
ssi_private->hw_type = hw_type; ssi_private->hw_type = hw_type;
...@@ -1306,7 +1183,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1306,7 +1183,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
sizeof(fsl_ssi_ac97_dai)); sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi_private; fsl_ac97_data = ssi_private;
ssi_private->imx_ac97 = true;
snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
} else { } else {
...@@ -1314,7 +1190,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1314,7 +1190,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
sizeof(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 */ /* Get the addresses and IRQ */
ret = of_address_to_resource(np, 0, &res); ret = of_address_to_resource(np, 0, &res);
...@@ -1353,177 +1229,43 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1353,177 +1229,43 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ssi_private->baudclk_locked = false; ssi_private->baudclk_locked = false;
spin_lock_init(&ssi_private->baudclk_lock); spin_lock_init(&ssi_private->baudclk_lock);
/* dev_set_drvdata(&pdev->dev, ssi_private);
* 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;
ssi_private->clk = devm_clk_get(&pdev->dev, NULL); if (fsl_ssi_on_imx(ssi_private)) {
if (IS_ERR(ssi_private->clk)) { ret = fsl_ssi_imx_probe(pdev, ssi_private, ssi_private->ssi);
ret = PTR_ERR(ssi_private->clk); if (ret)
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);
goto error_irqmap; 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, ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); &ssi_private->cpu_dai_drv, 1);
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, if (ret) {
dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); 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) { if (ssi_private->use_dma) {
/* The 'name' should not have any slashes in it. */
ret = devm_request_irq(&pdev->dev, ssi_private->irq, 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);
ssi_private->irq_stats = true;
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "could not claim irq %u\n", dev_err(&pdev->dev, "could not claim irq %u\n",
ssi_private->irq); ssi_private->irq);
goto error_clk; goto error_irq;
} }
} }
/* Register with ASoC */ ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
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);
if (ret) if (ret)
goto error_dbgfs; goto error_asoc_register;
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;
}
}
/* /*
* If codec-handle property is missing from SSI node, we assume * If codec-handle property is missing from SSI node, we assume
* that the machine driver uses new binding which does not require * that the machine driver uses new binding which does not require
* SSI driver to trigger machine driver's probe. * SSI driver to trigger machine driver's probe.
*/ */
if (!of_get_property(np, "codec-handle", NULL)) { if (!of_get_property(np, "codec-handle", NULL))
ssi_private->new_binding = true;
goto done; goto done;
}
/* Trigger the machine driver's probe function. The platform driver /* Trigger the machine driver's probe function. The platform driver
* name of the machine driver is taken from /compatible property of the * 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) ...@@ -1543,37 +1285,24 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (IS_ERR(ssi_private->pdev)) { if (IS_ERR(ssi_private->pdev)) {
ret = PTR_ERR(ssi_private->pdev); ret = PTR_ERR(ssi_private->pdev);
dev_err(&pdev->dev, "failed to register platform: %d\n", ret); dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
goto error_dai; goto error_sound_card;
} }
done: done:
if (ssi_private->imx_ac97)
fsl_ssi_ac97_init();
return 0; return 0;
error_dai: error_sound_card:
if (ssi_private->ssi_on_imx && !ssi_private->use_dma) fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
imx_pcm_fiq_exit(pdev);
error_pcm:
fsl_ssi_debugfs_remove(ssi_private);
error_dbgfs: error_irq:
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
error_dev: error_asoc_register:
device_remove_file(&pdev->dev, dev_attr); if (fsl_ssi_on_imx(ssi_private))
fsl_ssi_imx_clean(pdev, ssi_private);
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_irqmap: error_irqmap:
if (ssi_private->irq_stats) if (ssi_private->use_dma)
irq_dispose_mapping(ssi_private->irq); irq_dispose_mapping(ssi_private->irq);
return ret; return ret;
...@@ -1583,17 +1312,16 @@ static int fsl_ssi_remove(struct platform_device *pdev) ...@@ -1583,17 +1312,16 @@ static int fsl_ssi_remove(struct platform_device *pdev)
{ {
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); 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); platform_device_unregister(ssi_private->pdev);
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
if (ssi_private->ssi_on_imx) {
if (!IS_ERR(ssi_private->baudclk)) if (fsl_ssi_on_imx(ssi_private))
clk_disable_unprepare(ssi_private->baudclk); fsl_ssi_imx_clean(pdev, ssi_private);
clk_disable_unprepare(ssi_private->clk);
} if (ssi_private->use_dma)
if (ssi_private->irq_stats)
irq_dispose_mapping(ssi_private->irq); irq_dispose_mapping(ssi_private->irq);
return 0; return 0;
......
...@@ -39,6 +39,7 @@ struct ccsr_ssi { ...@@ -39,6 +39,7 @@ struct ccsr_ssi {
__be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */ __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_RFR_CLK_DIS 0x00000800
#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400 #define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400
#define CCSR_SSI_SCR_TCH_EN 0x00000100 #define CCSR_SSI_SCR_TCH_EN 0x00000100
...@@ -206,5 +207,64 @@ struct ccsr_ssi { ...@@ -206,5 +207,64 @@ struct ccsr_ssi {
#define CCSR_SSI_SACNT_FV 0x00000002 #define CCSR_SSI_SACNT_FV 0x00000002
#define CCSR_SSI_SACNT_AC97EN 0x00000001 #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
/*
* 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);
}
...@@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { ...@@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME, SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE, .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
.period_bytes_min = 128, .period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */ .period_bytes_max = 65535, /* Limited by SDMA engine */
......
...@@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev, ...@@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform);
static void devm_card_release(struct device *dev, void *res) static void devm_card_release(struct device *dev, void *res)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment