#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
-#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
-#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS)
-
enum fsl_ssi_type {
FSL_SSI_MCP8610,
/* Register values for rx/tx configuration */
struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
- struct {
- unsigned int rfrc;
- unsigned int tfrc;
- unsigned int cmdau;
- unsigned int cmddu;
- unsigned int rxt;
- unsigned int rdr1;
- unsigned int rdr0;
- unsigned int tde1;
- unsigned int tde0;
- unsigned int roe1;
- unsigned int roe0;
- unsigned int tue1;
- unsigned int tue0;
- unsigned int tfs;
- unsigned int rfs;
- unsigned int tls;
- unsigned int rls;
- unsigned int rff1;
- unsigned int rff0;
- unsigned int tfe1;
- unsigned int tfe0;
- } stats;
- struct dentry *dbg_dir;
- struct dentry *dbg_stats;
+ struct fsl_ssi_dbg dbg_stats;
char name[1];
};
{
struct fsl_ssi_private *ssi_private = dev_id;
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- irqreturn_t ret = IRQ_NONE;
__be32 sisr;
__be32 sisr2;
__be32 sisr_write_mask = 0;
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
- sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK;
-
- if (sisr & CCSR_SSI_SISR_RFRC) {
- ssi_private->stats.rfrc++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFRC) {
- ssi_private->stats.tfrc++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_CMDAU) {
- ssi_private->stats.cmdau++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_CMDDU) {
- ssi_private->stats.cmddu++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RXT) {
- ssi_private->stats.rxt++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RDR1) {
- ssi_private->stats.rdr1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RDR0) {
- ssi_private->stats.rdr0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TDE1) {
- ssi_private->stats.tde1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TDE0) {
- ssi_private->stats.tde0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_ROE1) {
- ssi_private->stats.roe1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_ROE0) {
- ssi_private->stats.roe0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TUE1) {
- ssi_private->stats.tue1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TUE0) {
- ssi_private->stats.tue0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFS) {
- ssi_private->stats.tfs++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFS) {
- ssi_private->stats.rfs++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TLS) {
- ssi_private->stats.tls++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RLS) {
- ssi_private->stats.rls++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFF1) {
- ssi_private->stats.rff1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_RFF0) {
- ssi_private->stats.rff0++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFE1) {
- ssi_private->stats.tfe1++;
- ret = IRQ_HANDLED;
- }
-
- if (sisr & CCSR_SSI_SISR_TFE0) {
- ssi_private->stats.tfe0++;
- ret = IRQ_HANDLED;
- }
+ sisr = read_ssi(&ssi->sisr);
sisr2 = sisr & sisr_write_mask;
/* Clear the bits that we set */
if (sisr2)
write_ssi(sisr2, &ssi->sisr);
- return ret;
-}
-
-#if IS_ENABLED(CONFIG_DEBUG_FS)
-/* Show the statistics of a flag only if its interrupt is enabled. The
- * compiler will optimze this code to a no-op if the interrupt is not
- * enabled.
- */
-#define SIER_SHOW(flag, name) \
- do { \
- if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
- seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
- } while (0)
-
-
-/**
- * fsl_sysfs_ssi_show: display SSI statistics
- *
- * Display the statistics for the current SSI device. To avoid confusion,
- * we only show those counts that are enabled.
- */
-static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
-{
- struct fsl_ssi_private *ssi_private = s->private;
-
- SIER_SHOW(RFRC_EN, rfrc);
- SIER_SHOW(TFRC_EN, tfrc);
- SIER_SHOW(CMDAU_EN, cmdau);
- SIER_SHOW(CMDDU_EN, cmddu);
- SIER_SHOW(RXT_EN, rxt);
- SIER_SHOW(RDR1_EN, rdr1);
- SIER_SHOW(RDR0_EN, rdr0);
- SIER_SHOW(TDE1_EN, tde1);
- SIER_SHOW(TDE0_EN, tde0);
- SIER_SHOW(ROE1_EN, roe1);
- SIER_SHOW(ROE0_EN, roe0);
- SIER_SHOW(TUE1_EN, tue1);
- SIER_SHOW(TUE0_EN, tue0);
- SIER_SHOW(TFS_EN, tfs);
- SIER_SHOW(RFS_EN, rfs);
- SIER_SHOW(TLS_EN, tls);
- SIER_SHOW(RLS_EN, rls);
- SIER_SHOW(RFF1_EN, rff1);
- SIER_SHOW(RFF0_EN, rff0);
- SIER_SHOW(TFE1_EN, tfe1);
- SIER_SHOW(TFE0_EN, tfe0);
-
- 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,
-};
-
-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;
-}
+ fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
-static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
-{
+ return IRQ_HANDLED;
}
-#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
-
/*
* Enable/Disable all rx/tx config flags at once.
*/
}
}
+/*
+ * Calculate the bits that have to be disabled for the current stream that is
+ * getting disabled. This keeps the bits enabled that are necessary for the
+ * second stream to work if 'stream_active' is true.
+ *
+ * Detailed calculation:
+ * These are the values that need to be active after disabling. For non-active
+ * second stream, this is 0:
+ * vals_stream * !!stream_active
+ *
+ * The following computes the overall differences between the setup for the
+ * to-disable stream and the active stream, a simple XOR:
+ * vals_disable ^ (vals_stream * !!(stream_active))
+ *
+ * The full expression adds a mask on all values we care about
+ */
+#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \
+ ((vals_disable) & \
+ ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
+
/*
* Enable/Disable a ssi configuration. You have to pass either
* ssi_private->rxtx_reg_val.rx or tx as vals parameter.
u32 scr_val = read_ssi(&ssi->scr);
int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
!!(scr_val & CCSR_SSI_SCR_RE);
+ int keep_active;
+
+ if (nr_active_streams - 1 > 0)
+ keep_active = 1;
+ else
+ keep_active = 0;
/* Find the other direction values rx or tx which we do not want to
* modify */
/* If vals should be disabled, start with disabling the unit */
if (!enable) {
- u32 scr = vals->scr & (vals->scr ^ avals->scr);
+ u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
+ keep_active);
write_ssi_mask(&ssi->scr, scr, 0);
}
*/
if (ssi_private->offline_config) {
if ((enable && !nr_active_streams) ||
- (!enable && nr_active_streams == 1))
+ (!enable && !keep_active))
fsl_ssi_rxtx_config(ssi_private, enable);
goto config_done;
*/
/* These assignments are simply vals without bits set in avals*/
- sier = vals->sier & (vals->sier ^ avals->sier);
- srcr = vals->srcr & (vals->srcr ^ avals->srcr);
- stcr = vals->stcr & (vals->stcr ^ avals->stcr);
+ sier = fsl_ssi_disable_val(vals->sier, avals->sier,
+ keep_active);
+ srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
+ keep_active);
+ stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
+ keep_active);
write_ssi_mask(&ssi->srcr, srcr, 0);
write_ssi_mask(&ssi->stcr, stcr, 0);
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
*
snd_soc_dai_get_drvdata(rtd->cpu_dai);
unsigned long flags;
- /* First, we only do fsl_ssi_setup() when SSI is going to be active.
- * Second, fsl_ssi_setup was already called by ac97_init earlier if
- * the driver is in ac97 mode.
- */
if (!dai->active && !ssi_private->imx_ac97) {
- fsl_ssi_setup(ssi_private);
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false;
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
u32 strcr = 0, stcr, srcr, scr, mask;
+ u8 wm;
+
+ fsl_ssi_setup_reg_vals(ssi_private);
scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
- scr |= CCSR_SSI_SCR_NET;
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 |
stcr = read_ssi(&ssi->stcr) & ~mask;
srcr = read_ssi(&ssi->srcr) & ~mask;
+ ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
break;
default:
return -EINVAL;
}
- scr |= ssi_private->i2s_mode;
/* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
CCSR_SSI_STCR_TXBIT0;
break;
+ case SND_SOC_DAIFMT_AC97:
+ ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+ break;
default:
return -EINVAL;
}
+ scr |= ssi_private->i2s_mode;
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
write_ssi(srcr, &ssi->srcr);
write_ssi(scr, &ssi->scr);
+ /*
+ * 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);
+
+ if (ssi_private->use_dual_fifo) {
+ write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1,
+ CCSR_SSI_SRCR_RFEN1);
+ write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1,
+ CCSR_SSI_STCR_TFEN1);
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN,
+ CCSR_SSI_SCR_TCH_EN);
+ }
+
+ if (fmt & SND_SOC_DAIFMT_AC97)
+ fsl_ssi_setup_ac97(ssi_private);
+
return 0;
}
static struct fsl_ssi_private *fsl_ac97_data;
-static void fsl_ssi_ac97_init(void)
-{
- fsl_ssi_setup(fsl_ac97_data);
-}
-
static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
goto error_dev;
}
- ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
+ ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
if (ret)
goto error_dbgfs;
}
done:
- if (ssi_private->imx_ac97)
- fsl_ssi_ac97_init();
-
return 0;
error_dai:
imx_pcm_fiq_exit(pdev);
error_pcm:
- fsl_ssi_debugfs_remove(ssi_private);
+ fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
error_dbgfs:
snd_soc_unregister_component(&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)
platform_device_unregister(ssi_private->pdev);