|
@@ -187,42 +187,48 @@ struct fsl_ssi_soc_data {
|
|
|
/**
|
|
|
* fsl_ssi: per-SSI private data
|
|
|
*
|
|
|
- * @reg: Pointer to the regmap registers
|
|
|
+ * @regs: Pointer to the regmap registers
|
|
|
* @irq: IRQ of this SSI
|
|
|
* @cpu_dai_drv: CPU DAI driver for this device
|
|
|
*
|
|
|
* @dai_fmt: DAI configuration this device is currently used with
|
|
|
- * @i2s_mode: i2s and network mode configuration of the device. Is used to
|
|
|
- * switch between normal and i2s/network mode
|
|
|
- * mode depending on the number of channels
|
|
|
+ * @i2s_mode: I2S and Network mode configuration of SCR register
|
|
|
* @use_dma: DMA is used or FIQ with stream filter
|
|
|
- * @use_dual_fifo: DMA with support for both FIFOs used
|
|
|
- * @fifo_deph: Depth of the SSI FIFOs
|
|
|
- * @slot_width: width of each DAI slot
|
|
|
- * @slots: number of slots
|
|
|
- * @rxtx_reg_val: Specific register settings for receive/transmit configuration
|
|
|
+ * @use_dual_fifo: DMA with support for dual FIFO mode
|
|
|
+ * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
|
|
|
+ * @fifo_depth: Depth of the SSI FIFOs
|
|
|
+ * @slot_width: Width of each DAI slot
|
|
|
+ * @slots: Number of slots
|
|
|
+ * @rxtx_reg_val: Specific RX/TX register settings
|
|
|
*
|
|
|
- * @clk: SSI clock
|
|
|
- * @baudclk: SSI baud clock for master mode
|
|
|
+ * @clk: Clock source to access register
|
|
|
+ * @baudclk: Clock source to generate bit and frame-sync clocks
|
|
|
* @baudclk_streams: Active streams that are using baudclk
|
|
|
*
|
|
|
+ * @regcache_sfcsr: Cache sfcsr register value during suspend and resume
|
|
|
+ * @regcache_sacnt: Cache sacnt register value during suspend and resume
|
|
|
+ *
|
|
|
* @dma_params_tx: DMA transmit parameters
|
|
|
* @dma_params_rx: DMA receive parameters
|
|
|
* @ssi_phys: physical address of the SSI registers
|
|
|
*
|
|
|
* @fiq_params: FIQ stream filtering parameters
|
|
|
*
|
|
|
- * @pdev: Pointer to pdev used for deprecated fsl-ssi sound card
|
|
|
+ * @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only)
|
|
|
+ * TODO: Should be replaced with simple-sound-card
|
|
|
*
|
|
|
* @dbg_stats: Debugging statistics
|
|
|
*
|
|
|
* @soc: SoC specific data
|
|
|
+ * @dev: Pointer to &pdev->dev
|
|
|
+ *
|
|
|
+ * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are
|
|
|
+ * @fifo_watermark or fewer words in TX fifo or
|
|
|
+ * @fifo_watermark or more empty words in RX fifo.
|
|
|
+ * @dma_maxburst: Max number of words to transfer in one go. So far,
|
|
|
+ * this is always the same as fifo_watermark.
|
|
|
*
|
|
|
- * @fifo_watermark: the FIFO watermark setting. Notifies DMA when
|
|
|
- * there are @fifo_watermark or fewer words in TX fifo or
|
|
|
- * @fifo_watermark or more empty words in RX fifo.
|
|
|
- * @dma_maxburst: max number of words to transfer in one go. So far,
|
|
|
- * this is always the same as fifo_watermark.
|
|
|
+ * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
|
|
|
*/
|
|
|
struct fsl_ssi {
|
|
|
struct regmap *regs;
|
|
@@ -243,20 +249,15 @@ struct fsl_ssi {
|
|
|
struct clk *baudclk;
|
|
|
unsigned int baudclk_streams;
|
|
|
|
|
|
- /* regcache for volatile regs */
|
|
|
u32 regcache_sfcsr;
|
|
|
u32 regcache_sacnt;
|
|
|
|
|
|
- /* DMA params */
|
|
|
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
|
|
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
|
|
dma_addr_t ssi_phys;
|
|
|
|
|
|
- /* params for non-dma FIQ stream filtered mode */
|
|
|
struct imx_pcm_fiq_params fiq_params;
|
|
|
|
|
|
- /* Used when using fsl-ssi as sound-card. This is only used by ppc and
|
|
|
- * should be replaced with simple-sound-card. */
|
|
|
struct platform_device *pdev;
|
|
|
|
|
|
struct fsl_ssi_dbg dbg_stats;
|
|
@@ -271,19 +272,19 @@ struct fsl_ssi {
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
- * 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.
|
|
|
+ * SoC specific data
|
|
|
*
|
|
|
- * 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.
|
|
|
+ * Notes:
|
|
|
+ * 1) SSI in earlier SoCS has critical bits in control registers that
|
|
|
+ * cannot be changed after SSI starts running -- a software reset
|
|
|
+ * (set SSIEN to 0) is required to change their values. So adding
|
|
|
+ * an offline_config flag for these SoCs.
|
|
|
+ * 2) SDMA is available since imx35. However, imx35 does not support
|
|
|
+ * DMA bits changing when SSI is running, so set offline_config.
|
|
|
+ * 3) imx51 and later versions support register configurations when
|
|
|
+ * SSI is running (SSIEN); For these versions, DMA needs to be
|
|
|
+ * configured before SSI sends DMA request to avoid an undefined
|
|
|
+ * DMA request on the SDMA side.
|
|
|
*/
|
|
|
|
|
|
static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
|
|
@@ -342,17 +343,9 @@ static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi)
|
|
|
return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
|
|
|
SND_SOC_DAIFMT_CBM_CFS;
|
|
|
}
|
|
|
+
|
|
|
/**
|
|
|
- * fsl_ssi_isr: SSI interrupt handler
|
|
|
- *
|
|
|
- * Although it's possible to use the interrupt handler to send and receive
|
|
|
- * data to/from the SSI, we use the DMA instead. Programming is more
|
|
|
- * complicated, but the performance is much better.
|
|
|
- *
|
|
|
- * This interrupt handler is used only to gather statistics.
|
|
|
- *
|
|
|
- * @irq: IRQ of the SSI device
|
|
|
- * @dev_id: pointer to the fsl_ssi structure for this SSI device
|
|
|
+ * Interrupt handler to gather states
|
|
|
*/
|
|
|
static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|
|
{
|
|
@@ -361,10 +354,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|
|
__be32 sisr;
|
|
|
__be32 sisr2;
|
|
|
|
|
|
- /* We got an interrupt, so read the status register to see what we
|
|
|
- were interrupted for. We mask it with the Interrupt Enable register
|
|
|
- so that we only check for events that we're interested in.
|
|
|
- */
|
|
|
regmap_read(regs, CCSR_SSI_SISR, &sisr);
|
|
|
|
|
|
sisr2 = sisr & ssi->soc->sisr_write_mask;
|
|
@@ -377,8 +366,8 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Enable/Disable all rx/tx config flags at once.
|
|
|
+/**
|
|
|
+ * Enable or disable all rx/tx config flags at once
|
|
|
*/
|
|
|
static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable)
|
|
|
{
|
|
@@ -405,13 +394,8 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Clear RX or TX FIFO to remove samples from the previous
|
|
|
- * stream session which may be still present in the FIFO and
|
|
|
- * may introduce bad samples and/or channel slipping.
|
|
|
- *
|
|
|
- * Note: The SOR is not documented in recent IMX datasheet, but
|
|
|
- * is described in IMX51 reference manual at section 56.3.3.15.
|
|
|
+/**
|
|
|
+ * Clear remaining data in the FIFO to avoid dirty data or channel slipping
|
|
|
*/
|
|
|
static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx)
|
|
|
{
|
|
@@ -424,7 +408,7 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
+/**
|
|
|
* 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.
|
|
@@ -444,9 +428,8 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx)
|
|
|
((vals_disable) & \
|
|
|
((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
|
|
|
|
|
|
-/*
|
|
|
- * Enable/Disable a ssi configuration. You have to pass either
|
|
|
- * ssi->rxtx_reg_val.rx or tx as vals parameter.
|
|
|
+/**
|
|
|
+ * Enable or disable SSI configuration.
|
|
|
*/
|
|
|
static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
struct fsl_ssi_reg_val *vals)
|
|
@@ -467,24 +450,28 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
else
|
|
|
keep_active = 0;
|
|
|
|
|
|
- /* Find the other direction values rx or tx which we do not want to
|
|
|
- * modify */
|
|
|
+ /* Get the opposite direction to keep its values untouched */
|
|
|
if (&ssi->rxtx_reg_val.rx == vals)
|
|
|
avals = &ssi->rxtx_reg_val.tx;
|
|
|
else
|
|
|
avals = &ssi->rxtx_reg_val.rx;
|
|
|
|
|
|
- /* If vals should be disabled, start with disabling the unit */
|
|
|
if (!enable) {
|
|
|
+ /*
|
|
|
+ * To keep the other stream safe, exclude shared bits between
|
|
|
+ * both streams, and get safe bits to disable current stream
|
|
|
+ */
|
|
|
u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
|
|
|
keep_active);
|
|
|
+ /* Safely disable SCR register for the stream */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR, scr, 0);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * We are running on a SoC which does not support online SSI
|
|
|
- * reconfiguration, so we have to enable all necessary flags at once
|
|
|
- * even if we do not use them later (capture and playback configuration)
|
|
|
+ * For cases where online configuration is not supported,
|
|
|
+ * 1) Enable all necessary bits of both streams when 1st stream starts
|
|
|
+ * even if the opposite stream will not start
|
|
|
+ * 2) Disable all remaining bits of both streams when last stream ends
|
|
|
*/
|
|
|
if (ssi->soc->offline_config) {
|
|
|
if ((enable && !nr_active_streams) ||
|
|
@@ -494,10 +481,7 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
goto config_done;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Configure single direction units while the SSI unit is running
|
|
|
- * (online configuration)
|
|
|
- */
|
|
|
+ /* Online configure single direction while SSI is running */
|
|
|
if (enable) {
|
|
|
fsl_ssi_fifo_clear(ssi, vals->scr & CCSR_SSI_SCR_RE);
|
|
|
|
|
@@ -510,15 +494,9 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
u32 stcr;
|
|
|
|
|
|
/*
|
|
|
- * Disabling the necessary flags for one of rx/tx while the
|
|
|
- * other stream is active is a little bit more difficult. We
|
|
|
- * have to disable only those flags that differ between both
|
|
|
- * streams (rx XOR tx) and that are set in the stream that is
|
|
|
- * disabled now. Otherwise we could alter flags of the other
|
|
|
- * stream
|
|
|
+ * To keep the other stream safe, exclude shared bits between
|
|
|
+ * both streams, and get safe bits to disable current stream
|
|
|
*/
|
|
|
-
|
|
|
- /* These assignments are simply vals without bits set in avals*/
|
|
|
sier = fsl_ssi_disable_val(vals->sier, avals->sier,
|
|
|
keep_active);
|
|
|
srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
|
|
@@ -526,6 +504,7 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
|
|
|
keep_active);
|
|
|
|
|
|
+ /* Safely disable other control registers for the stream */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SRCR, srcr, 0);
|
|
|
regmap_update_bits(regs, CCSR_SSI_STCR, stcr, 0);
|
|
|
regmap_update_bits(regs, CCSR_SSI_SIER, sier, 0);
|
|
@@ -534,26 +513,21 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
|
|
|
config_done:
|
|
|
/* Enabling of subunits is done after configuration */
|
|
|
if (enable) {
|
|
|
+ /*
|
|
|
+ * Start DMA before setting TE to avoid FIFO underrun
|
|
|
+ * which may cause a channel slip or a channel swap
|
|
|
+ *
|
|
|
+ * TODO: FIQ cases might also need this upon testing
|
|
|
+ */
|
|
|
if (ssi->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
|
|
|
- /*
|
|
|
- * Be sure the Tx FIFO is filled when TE is set.
|
|
|
- * Otherwise, there are some chances to start the
|
|
|
- * playback with some void samples inserted first,
|
|
|
- * generating a channel slip.
|
|
|
- *
|
|
|
- * First, SSIEN must be set, to let the FIFO be filled.
|
|
|
- *
|
|
|
- * Notes:
|
|
|
- * - Limit this fix to the DMA case until FIQ cases can
|
|
|
- * be tested.
|
|
|
- * - Limit the length of the busy loop to not lock the
|
|
|
- * system too long, even if 1-2 loops are sufficient
|
|
|
- * in general.
|
|
|
- */
|
|
|
int i;
|
|
|
int max_loop = 100;
|
|
|
+
|
|
|
+ /* Enable SSI first to send TX DMA request */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR,
|
|
|
CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
|
|
|
+
|
|
|
+ /* Busy wait until TX FIFO not empty -- DMA working */
|
|
|
for (i = 0; i < max_loop; i++) {
|
|
|
u32 sfcsr;
|
|
|
regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
|
|
@@ -565,6 +539,7 @@ config_done:
|
|
|
"Timeout waiting TX FIFO filling\n");
|
|
|
}
|
|
|
}
|
|
|
+ /* Enable all remaining bits */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
|
|
|
}
|
|
|
}
|
|
@@ -581,20 +556,9 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
|
|
|
|
|
|
/* no SACC{ST,EN,DIS} regs on imx21-class SSI */
|
|
|
if (!ssi->soc->imx21regs) {
|
|
|
- /*
|
|
|
- * Note that these below aren't just normal registers.
|
|
|
- * They are a way to disable or enable bits in SACCST
|
|
|
- * register:
|
|
|
- * - writing a '1' bit at some position in SACCEN sets the
|
|
|
- * relevant bit in SACCST,
|
|
|
- * - writing a '1' bit at some position in SACCDIS unsets
|
|
|
- * the relevant bit in SACCST register.
|
|
|
- *
|
|
|
- * The two writes below first disable all channels slots,
|
|
|
- * then enable just slots 3 & 4 ("PCM Playback Left Channel"
|
|
|
- * and "PCM Playback Right Channel").
|
|
|
- */
|
|
|
+ /* Disable all channel slots */
|
|
|
regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
|
|
|
+ /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */
|
|
|
regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
|
|
|
}
|
|
|
}
|
|
@@ -602,23 +566,11 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
|
|
|
static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable)
|
|
|
{
|
|
|
/*
|
|
|
- * Why are we setting up SACCST everytime we are starting a
|
|
|
- * playback?
|
|
|
- * Some CODECs (like VT1613 CODEC on UDOO board) like to
|
|
|
- * (sometimes) set extra bits in their SLOTREQ requests.
|
|
|
- * When a bit is set in a SLOTREQ request then SSI sets the
|
|
|
- * relevant bit in SACCST automatically (it is enough if a bit was
|
|
|
- * set in a SLOTREQ just once, bits in SACCST are 'sticky').
|
|
|
- * If an extra slot gets enabled that's a disaster for playback
|
|
|
- * because some of normal left or right channel samples are
|
|
|
- * redirected instead to this extra slot.
|
|
|
+ * SACCST might be modified via AC Link by a CODEC if it sends
|
|
|
+ * extra bits in their SLOTREQ requests, which'll accidentally
|
|
|
+ * send valid data to slots other than normal playback slots.
|
|
|
*
|
|
|
- * A workaround implemented in fsl-asoc-card of setting an
|
|
|
- * appropriate CODEC register so that slots 3 & 4 (the normal
|
|
|
- * stereo playback slots) are used for S/PDIF seems to mostly fix
|
|
|
- * this issue on the UDOO board but since this CODEC is so
|
|
|
- * untrustworthy let's play safe here and make sure that no extra
|
|
|
- * slots are enabled every time a playback is started.
|
|
|
+ * To be safe, configure SACCST right before TX starts.
|
|
|
*/
|
|
|
if (enable && fsl_ssi_is_ac97(ssi))
|
|
|
fsl_ssi_tx_ac97_saccst_setup(ssi);
|
|
@@ -626,10 +578,8 @@ static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable)
|
|
|
fsl_ssi_config(ssi, enable, &ssi->rxtx_reg_val.tx);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Setup rx/tx register values used to enable/disable the streams. These will
|
|
|
- * be used later in fsl_ssi_config to setup the streams without the need to
|
|
|
- * check for all different SSI modes.
|
|
|
+/**
|
|
|
+ * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely
|
|
|
*/
|
|
|
static void fsl_ssi_setup_reg_vals(struct fsl_ssi *ssi)
|
|
|
{
|
|
@@ -642,6 +592,7 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi *ssi)
|
|
|
reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
|
|
|
reg->tx.scr = 0;
|
|
|
|
|
|
+ /* AC97 has already enabled SSIEN, RE and TE, so ignore them */
|
|
|
if (!fsl_ssi_is_ac97(ssi)) {
|
|
|
reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
|
|
|
reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
|
|
@@ -663,24 +614,17 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
|
|
|
{
|
|
|
struct regmap *regs = ssi->regs;
|
|
|
|
|
|
- /*
|
|
|
- * Setup the clock control register
|
|
|
- */
|
|
|
+ /* Setup the clock control register */
|
|
|
regmap_write(regs, CCSR_SSI_STCCR,
|
|
|
CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
|
|
|
regmap_write(regs, CCSR_SSI_SRCCR,
|
|
|
CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
|
|
|
|
|
|
- /*
|
|
|
- * Enable AC97 mode and startup the SSI
|
|
|
- */
|
|
|
+ /* Enable AC97 mode and startup the SSI */
|
|
|
regmap_write(regs, CCSR_SSI_SACNT,
|
|
|
CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
|
|
|
|
|
|
- /*
|
|
|
- * Enable SSI, Transmit and Receive. AC97 has to communicate with the
|
|
|
- * codec before a stream is started.
|
|
|
- */
|
|
|
+ /* AC97 has to communicate with codec before starting a stream */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR,
|
|
|
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE,
|
|
|
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
|
|
@@ -688,14 +632,6 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
|
|
|
regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_WAIT(3));
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * fsl_ssi_startup: create a new substream
|
|
|
- *
|
|
|
- * This is the first function called when a stream is opened.
|
|
|
- *
|
|
|
- * If this is the first stream open, then grab the IRQ and program most of
|
|
|
- * the SSI registers.
|
|
|
- */
|
|
|
static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
@@ -707,7 +643,8 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- /* When using dual fifo mode, it is safer to ensure an even period
|
|
|
+ /*
|
|
|
+ * When using dual fifo mode, it is safer to ensure an even period
|
|
|
* size. If appearing to an odd number while DMA always starts its
|
|
|
* task from fifo0, fifo1 would be neglected at the end of each
|
|
|
* period. But SSI would still access fifo1 with an invalid data.
|
|
@@ -719,10 +656,6 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * fsl_ssi_shutdown: shutdown the SSI
|
|
|
- *
|
|
|
- */
|
|
|
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
@@ -734,7 +667,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock
|
|
|
+ * Configure Digital Audio Interface bit clock
|
|
|
*
|
|
|
* Note: This function can be only called when using SSI as DAI master
|
|
|
*
|
|
@@ -851,17 +784,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * fsl_ssi_hw_params - program the sample size
|
|
|
- *
|
|
|
- * Most of the SSI registers have been programmed in the startup function,
|
|
|
- * but the word length must be programmed here. Unfortunately, programming
|
|
|
- * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
|
|
|
- * cause a problem with supporting simultaneous playback and capture. If
|
|
|
- * the SSI is already playing a stream, then that stream may be temporarily
|
|
|
- * stopped when you start capture.
|
|
|
+ * Configure SSI based on PCM hardware parameters
|
|
|
*
|
|
|
- * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
|
|
|
- * clock master.
|
|
|
+ * Notes:
|
|
|
+ * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily
|
|
|
+ * disabled on offline_config SoCs. Even for online configurable SoCs
|
|
|
+ * running in synchronous mode (both TX and RX use STCCR), it is not
|
|
|
+ * safe to re-configure them when both two streams start running.
|
|
|
+ * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the
|
|
|
+ * fsl_ssi_set_bclk() if SSI is the DAI clock master.
|
|
|
*/
|
|
|
static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|
|
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
|
|
@@ -879,8 +810,10 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|
|
enabled = scr_val & CCSR_SSI_SCR_SSIEN;
|
|
|
|
|
|
/*
|
|
|
- * If we're in synchronous mode, and the SSI is already enabled,
|
|
|
- * then STCCR is already set properly.
|
|
|
+ * SSI is properly configured if it is enabled and running in
|
|
|
+ * the synchronous mode; Note that AC97 mode is an exception
|
|
|
+ * that should set separate configurations for STCCR and SRCCR
|
|
|
+ * despite running in the synchronous mode.
|
|
|
*/
|
|
|
if (enabled && ssi->cpu_dai_drv.symmetric_rates)
|
|
|
return 0;
|
|
@@ -902,10 +835,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
|
|
if (!fsl_ssi_is_ac97(ssi)) {
|
|
|
u8 i2smode;
|
|
|
- /*
|
|
|
- * Switch to normal net mode in order to have a frame sync
|
|
|
- * signal every 32 bits instead of 16 bits
|
|
|
- */
|
|
|
+ /* Normal + Network mode to send 16-bit data in 32-bit frames */
|
|
|
if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16)
|
|
|
i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL |
|
|
|
CCSR_SSI_SCR_NET;
|
|
@@ -917,16 +847,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|
|
channels == 1 ? 0 : i2smode);
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * FIXME: The documentation says that SxCCR[WL] should not be
|
|
|
- * modified while the SSI is enabled. The only time this can
|
|
|
- * happen is if we're trying to do simultaneous playback and
|
|
|
- * capture in asynchronous mode. Unfortunately, I have been enable
|
|
|
- * to get that to work at all on the P1022DS. Therefore, we don't
|
|
|
- * bother to disable/enable the SSI when setting SxCCR[WL], because
|
|
|
- * the SSI will stop anyway. Maybe one day, this will get fixed.
|
|
|
- */
|
|
|
-
|
|
|
/* In synchronous mode, the SSI uses STCCR for capture */
|
|
|
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
|
|
|
ssi->cpu_dai_drv.symmetric_rates)
|
|
@@ -972,6 +892,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
|
|
|
regmap_read(regs, CCSR_SSI_SCR, &scr);
|
|
|
scr &= ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
|
|
|
+ /* Synchronize frame sync clock for TE to avoid data slipping */
|
|
|
scr |= CCSR_SSI_SCR_SYNC_TX_FS;
|
|
|
|
|
|
mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
|
|
@@ -982,6 +903,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
stcr &= ~mask;
|
|
|
srcr &= ~mask;
|
|
|
|
|
|
+ /* Use Network mode as default */
|
|
|
ssi->i2s_mode = CCSR_SSI_SCR_NET;
|
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
|
case SND_SOC_DAIFMT_I2S:
|
|
@@ -1022,6 +944,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
CCSR_SSI_STCR_TXBIT0;
|
|
|
break;
|
|
|
case SND_SOC_DAIFMT_AC97:
|
|
|
+ /* Data on falling edge of bclk, frame high, 1clk before data */
|
|
|
ssi->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
|
|
|
break;
|
|
|
default:
|
|
@@ -1054,13 +977,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
/* DAI clock master masks */
|
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
|
+ /* Output bit and frame sync clocks */
|
|
|
strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR;
|
|
|
scr |= CCSR_SSI_SCR_SYS_CLK_EN;
|
|
|
break;
|
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
|
+ /* Input bit or frame sync clocks */
|
|
|
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
|
|
|
break;
|
|
|
case SND_SOC_DAIFMT_CBM_CFS:
|
|
|
+ /* Input bit clock but output frame sync clock */
|
|
|
strcr &= ~CCSR_SSI_STCR_TXDIR;
|
|
|
strcr |= CCSR_SSI_STCR_TFDIR;
|
|
|
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
|
|
@@ -1073,8 +999,8 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
stcr |= strcr;
|
|
|
srcr |= strcr;
|
|
|
|
|
|
+ /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
|
|
|
if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) {
|
|
|
- /* Need to clear RXDIR when using SYNC or AC97 mode */
|
|
|
srcr &= ~CCSR_SSI_SRCR_RXDIR;
|
|
|
scr |= CCSR_SSI_SCR_SYN;
|
|
|
}
|
|
@@ -1106,12 +1032,13 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format.
|
|
|
+ * Configure Digital Audio Interface (DAI) Format
|
|
|
*/
|
|
|
static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
|
|
{
|
|
|
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
|
|
|
|
|
|
+ /* AC97 configured DAIFMT earlier in the probe() */
|
|
|
if (fsl_ssi_is_ac97(ssi))
|
|
|
return 0;
|
|
|
|
|
@@ -1119,9 +1046,7 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * fsl_ssi_set_dai_tdm_slot - set TDM slot number
|
|
|
- *
|
|
|
- * Note: This function can be only called when using SSI as DAI master
|
|
|
+ * Set TDM slot number and slot width
|
|
|
*/
|
|
|
static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
|
|
|
u32 rx_mask, int slots, int slot_width)
|
|
@@ -1149,17 +1074,17 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
|
|
|
regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_DC_MASK,
|
|
|
CCSR_SSI_SxCCR_DC(slots));
|
|
|
|
|
|
- /* The register SxMSKs needs SSI to provide essential clock due to
|
|
|
- * hardware design. So we here temporarily enable SSI to set them.
|
|
|
- */
|
|
|
+ /* Save SSIEN bit of the SCR register */
|
|
|
regmap_read(regs, CCSR_SSI_SCR, &val);
|
|
|
val &= CCSR_SSI_SCR_SSIEN;
|
|
|
+ /* Temporarily enable SSI to allow SxMSKs to be configurable */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN,
|
|
|
CCSR_SSI_SCR_SSIEN);
|
|
|
|
|
|
regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask);
|
|
|
regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask);
|
|
|
|
|
|
+ /* Restore the value of SSIEN bit */
|
|
|
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val);
|
|
|
|
|
|
ssi->slot_width = slot_width;
|
|
@@ -1169,10 +1094,7 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * fsl_ssi_trigger: start and stop the DMA transfer.
|
|
|
- *
|
|
|
- * This function is called by ALSA to start, stop, pause, and resume the DMA
|
|
|
- * transfer of data.
|
|
|
+ * Start or stop SSI and corresponding DMA transaction.
|
|
|
*
|
|
|
* The DMA channel is in external master start and pause mode, which
|
|
|
* means the SSI completely controls the flow of data.
|
|
@@ -1207,6 +1129,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ /* Clear corresponding FIFO */
|
|
|
if (fsl_ssi_is_ac97(ssi)) {
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_TX_CLR);
|
|
@@ -1239,7 +1162,6 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
|
|
|
.trigger = fsl_ssi_trigger,
|
|
|
};
|
|
|
|
|
|
-/* Template for the CPU dai driver structure */
|
|
|
static struct snd_soc_dai_driver fsl_ssi_dai_template = {
|
|
|
.probe = fsl_ssi_dai_probe,
|
|
|
.playback = {
|
|
@@ -1383,6 +1305,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|
|
u32 dmas[4];
|
|
|
int ret;
|
|
|
|
|
|
+ /* Backward compatible for a DT without ipg clock name assigned */
|
|
|
if (ssi->has_ipg_clk_name)
|
|
|
ssi->clk = devm_clk_get(dev, "ipg");
|
|
|
else
|
|
@@ -1393,6 +1316,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ /* Enable the clock since regmap will not handle it in this case */
|
|
|
if (!ssi->has_ipg_clk_name) {
|
|
|
ret = clk_prepare_enable(ssi->clk);
|
|
|
if (ret) {
|
|
@@ -1401,9 +1325,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* For those SLAVE implementations, we ignore non-baudclk cases
|
|
|
- * and, instead, abandon MASTER mode that needs baud clock.
|
|
|
- */
|
|
|
+ /* Do not error out for slave cases that live without a baud clock */
|
|
|
ssi->baudclk = devm_clk_get(dev, "baud");
|
|
|
if (IS_ERR(ssi->baudclk))
|
|
|
dev_dbg(dev, "could not get baud clock: %ld\n",
|
|
@@ -1414,25 +1336,23 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|
|
ssi->dma_params_tx.addr = ssi->ssi_phys + CCSR_SSI_STX0;
|
|
|
ssi->dma_params_rx.addr = ssi->ssi_phys + CCSR_SSI_SRX0;
|
|
|
|
|
|
+ /* Set to dual FIFO mode according to the SDMA sciprt */
|
|
|
ret = of_property_read_u32_array(np, "dmas", dmas, 4);
|
|
|
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
|
|
|
ssi->use_dual_fifo = true;
|
|
|
- /* When using dual fifo mode, we need to keep watermark
|
|
|
- * as even numbers due to dma script limitation.
|
|
|
+ /*
|
|
|
+ * Use even numbers to avoid channel swap due to SDMA
|
|
|
+ * script design
|
|
|
*/
|
|
|
ssi->dma_params_tx.maxburst &= ~0x1;
|
|
|
ssi->dma_params_rx.maxburst &= ~0x1;
|
|
|
}
|
|
|
|
|
|
if (!ssi->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.
|
|
|
+ * Some boards use an incompatible codec. Use imx-fiq-pcm-audio
|
|
|
+ * to get it working, as DMA is not possible in this situation.
|
|
|
*/
|
|
|
-
|
|
|
ssi->fiq_params.irq = ssi->irq;
|
|
|
ssi->fiq_params.base = iomem;
|
|
|
ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
|
|
@@ -1490,12 +1410,14 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
ssi->soc = of_id->data;
|
|
|
ssi->dev = dev;
|
|
|
|
|
|
+ /* Check if being used in AC97 mode */
|
|
|
sprop = of_get_property(np, "fsl,mode", NULL);
|
|
|
if (sprop) {
|
|
|
if (!strcmp(sprop, "ac97-slave"))
|
|
|
ssi->dai_fmt = SND_SOC_DAIFMT_AC97;
|
|
|
}
|
|
|
|
|
|
+ /* Select DMA or FIQ */
|
|
|
ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
|
|
|
|
|
|
if (fsl_ssi_is_ac97(ssi)) {
|
|
@@ -1504,7 +1426,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
|
|
|
fsl_ac97_data = ssi;
|
|
|
} else {
|
|
|
- /* Initialize this copy of the CPU DAI driver structure */
|
|
|
memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
|
|
|
sizeof(fsl_ssi_dai_template));
|
|
|
}
|
|
@@ -1517,10 +1438,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
ssi->ssi_phys = res->start;
|
|
|
|
|
|
if (ssi->soc->imx21regs) {
|
|
|
- /*
|
|
|
- * According to datasheet imx21-class SSI
|
|
|
- * don't have SACC{ST,EN,DIS} regs.
|
|
|
- */
|
|
|
+ /* No SACC{ST,EN,DIS} regs in imx21-class SSI */
|
|
|
regconfig.max_register = CCSR_SSI_SRMSK;
|
|
|
regconfig.num_reg_defaults_raw =
|
|
|
CCSR_SSI_SRMSK / sizeof(uint32_t) + 1;
|
|
@@ -1546,7 +1464,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
return ssi->irq;
|
|
|
}
|
|
|
|
|
|
- /* Are the RX and the TX clocks locked? */
|
|
|
+ /* Set software limitations for synchronous mode */
|
|
|
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
|
|
|
if (!fsl_ssi_is_ac97(ssi)) {
|
|
|
ssi->cpu_dai_drv.symmetric_rates = 1;
|
|
@@ -1556,50 +1474,35 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
ssi->cpu_dai_drv.symmetric_channels = 1;
|
|
|
}
|
|
|
|
|
|
- /* Determine the FIFO depth. */
|
|
|
+ /* Fetch FIFO depth; Set to 8 for older DT without this property */
|
|
|
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
|
|
|
if (iprop)
|
|
|
ssi->fifo_depth = be32_to_cpup(iprop);
|
|
|
else
|
|
|
- /* Older 8610 DTs didn't have the fifo-depth property */
|
|
|
ssi->fifo_depth = 8;
|
|
|
|
|
|
/*
|
|
|
- * Set the watermark for transmit FIFO 0 and receive FIFO 0. We don't
|
|
|
- * use FIFO 1 but set the watermark appropriately nontheless.
|
|
|
- * We program the transmit water to signal a DMA transfer
|
|
|
- * if there are N elements left in the FIFO. For chips with 15-deep
|
|
|
- * FIFOs, set watermark to 8. This allows the SSI to operate at a
|
|
|
- * high data rate without channel slipping. Behavior is unchanged
|
|
|
- * for the older chips with a fifo depth of only 8. A value of 4
|
|
|
- * might be appropriate for the older chips, but is left at
|
|
|
- * fifo_depth-2 until sombody has a chance to test.
|
|
|
+ * Configure TX and RX DMA watermarks -- when to send a DMA request
|
|
|
*
|
|
|
- * 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.
|
|
|
+ * Values should be tested to avoid FIFO under/over run. Set maxburst
|
|
|
+ * to fifo_watermark to maxiumize DMA transaction to reduce overhead.
|
|
|
*/
|
|
|
switch (ssi->fifo_depth) {
|
|
|
case 15:
|
|
|
/*
|
|
|
- * 2 samples is not enough when running at high data
|
|
|
- * rates (like 48kHz @ 16 bits/channel, 16 channels)
|
|
|
- * 8 seems to split things evenly and leave enough time
|
|
|
- * for the DMA to fill the FIFO before it's over/under
|
|
|
- * run.
|
|
|
+ * Set to 8 as a balanced configuration -- When TX FIFO has 8
|
|
|
+ * empty slots, send a DMA request to fill these 8 slots. The
|
|
|
+ * remaining 7 slots should be able to allow DMA to finish the
|
|
|
+ * transaction before TX FIFO underruns; Same applies to RX.
|
|
|
+ *
|
|
|
+ * Tested with cases running at 48kHz @ 16 bits x 16 channels
|
|
|
*/
|
|
|
ssi->fifo_watermark = 8;
|
|
|
ssi->dma_maxburst = 8;
|
|
|
break;
|
|
|
case 8:
|
|
|
default:
|
|
|
- /*
|
|
|
- * maintain old behavior for older chips.
|
|
|
- * Keeping it the same because I don't have an older
|
|
|
- * board to test with.
|
|
|
- * I suspect this could be changed to be something to
|
|
|
- * leave some more space in the fifo.
|
|
|
- */
|
|
|
+ /* Safely use old watermark configurations for older chips */
|
|
|
ssi->fifo_watermark = ssi->fifo_depth - 2;
|
|
|
ssi->dma_maxburst = ssi->fifo_depth - 2;
|
|
|
break;
|
|
@@ -1642,18 +1545,14 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto error_asoc_register;
|
|
|
|
|
|
- /*
|
|
|
- * If codec-handle property is missing from SSI node, we assume
|
|
|
- * that the machine driver uses new binding which does not require
|
|
|
- * SSI driver to trigger machine driver's probe.
|
|
|
- */
|
|
|
+ /* Bypass it if using newer DT bindings of ASoC machine drivers */
|
|
|
if (!of_get_property(np, "codec-handle", NULL))
|
|
|
goto done;
|
|
|
|
|
|
- /* Trigger the machine driver's probe function. The platform driver
|
|
|
- * name of the machine driver is taken from /compatible property of the
|
|
|
- * device tree. We also pass the address of the CPU DAI driver
|
|
|
- * structure.
|
|
|
+ /*
|
|
|
+ * Backward compatible for older bindings by manually triggering the
|
|
|
+ * machine driver's probe(). Use /compatible property, including the
|
|
|
+ * address of CPU DAI driver structure, as the name of machine driver.
|
|
|
*/
|
|
|
sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
|
|
|
/* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
|