[alsa-devel] [PATCH v2] ASoC: fsl_sai: Add isr to deal with error flag
It's quite cricial to clear error flags because SAI might hang if getting FIFO underrun during playback (I haven't confirmed the same issue on Rx overflow though).
So this patch enables those irq and adds isr() to clear the flags so as to keep playback entirely safe.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com ---
Changelog v2: * Mask the active flags only for the following handler. * Reset FIFO for FIFO underrun/overflow cases. * Enable two error flags only as default. * Use dev_warn for two error flags. * Only clear those W1C bits.
sound/soc/fsl/fsl_sai.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/soc/fsl/fsl_sai.h | 15 +++++++++ 2 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index c4a4231..0bc98bb 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -23,6 +23,71 @@
#include "fsl_sai.h"
+#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ + FSL_SAI_CSR_FEIE) + +static irqreturn_t fsl_sai_isr(int irq, void *devid) +{ + struct fsl_sai *sai = (struct fsl_sai *)devid; + struct device *dev = &sai->pdev->dev; + u32 xcsr, mask; + + /* Only handle those what we enabled */ + mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT; + + /* Tx IRQ */ + regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr); + xcsr &= mask; + + if (xcsr & FSL_SAI_CSR_WSF) + dev_dbg(dev, "isr: Start of Tx word detected\n"); + + if (xcsr & FSL_SAI_CSR_SEF) + dev_warn(dev, "isr: Tx Frame sync error detected\n"); + + if (xcsr & FSL_SAI_CSR_FEF) { + dev_warn(dev, "isr: Transmit underrun detected\n"); + /* FIFO reset for safety */ + xcsr |= FSL_SAI_CSR_FR; + } + + if (xcsr & FSL_SAI_CSR_FWF) + dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n"); + + if (xcsr & FSL_SAI_CSR_FRF) + dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n"); + + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, + FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); + + /* Rx IRQ */ + regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr); + xcsr &= mask; + + if (xcsr & FSL_SAI_CSR_WSF) + dev_dbg(dev, "isr: Start of Rx word detected\n"); + + if (xcsr & FSL_SAI_CSR_SEF) + dev_warn(dev, "isr: Rx Frame sync error detected\n"); + + if (xcsr & FSL_SAI_CSR_FEF) { + dev_warn(dev, "isr: Receive overflow detected\n"); + /* FIFO reset for safety */ + xcsr |= FSL_SAI_CSR_FR; + } + + if (xcsr & FSL_SAI_CSR_FWF) + dev_dbg(dev, "isr: Enabled receive FIFO is full\n"); + + if (xcsr & FSL_SAI_CSR_FRF) + dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n"); + + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, + FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr); + + return IRQ_HANDLED; +} + static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { @@ -373,8 +438,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS); regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, FSL_SAI_MAXBURST_TX * 2); regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, @@ -490,12 +555,14 @@ static int fsl_sai_probe(struct platform_device *pdev) struct fsl_sai *sai; struct resource *res; void __iomem *base; - int ret; + int irq, ret;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM;
+ sai->pdev = pdev; + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); if (sai->big_endian_regs) fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; @@ -514,6 +581,18 @@ static int fsl_sai_probe(struct platform_device *pdev) return PTR_ERR(sai->regmap); }
+ irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u\n", irq); + return ret; + } + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index e432260..a264185 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -37,7 +37,21 @@
/* SAI Transmit/Recieve Control Register */ #define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_FR BIT(25) +#define FSL_SAI_CSR_xF_SHIFT 16 +#define FSL_SAI_CSR_xF_W_SHIFT 18 +#define FSL_SAI_CSR_xF_MASK (0x1f << FSL_SAI_CSR_xF_SHIFT) +#define FSL_SAI_CSR_xF_W_MASK (0x7 << FSL_SAI_CSR_xF_W_SHIFT) +#define FSL_SAI_CSR_WSF BIT(20) +#define FSL_SAI_CSR_SEF BIT(19) +#define FSL_SAI_CSR_FEF BIT(18) #define FSL_SAI_CSR_FWF BIT(17) +#define FSL_SAI_CSR_FRF BIT(16) +#define FSL_SAI_CSR_xIE_SHIFT 8 +#define FSL_SAI_CSR_WSIE BIT(12) +#define FSL_SAI_CSR_SEIE BIT(11) +#define FSL_SAI_CSR_FEIE BIT(10) +#define FSL_SAI_CSR_FWIE BIT(9) #define FSL_SAI_CSR_FRIE BIT(8) #define FSL_SAI_CSR_FRDE BIT(0)
@@ -99,6 +113,7 @@ #define FSL_SAI_MAXBURST_RX 6
struct fsl_sai { + struct platform_device *pdev; struct regmap *regmap;
bool big_endian_regs;
On Thu, Mar 27, 2014 at 07:06:59PM +0800, Nicolin Chen wrote:
It's quite cricial to clear error flags because SAI might hang if getting FIFO underrun during playback (I haven't confirmed the same issue on Rx overflow though).
So this patch enables those irq and adds isr() to clear the flags so as to keep playback entirely safe.
So, I've applied this since we're (hopefully!) very near the merge window opening and it seems like it should be an improvement overall. However a few things below:
- /* Only handle those what we enabled */
- mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
The shifting here could use a comment.
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
Using update_bits() is going to do an extra read, better to do this as:
if (xcsr) regmap_write(sai->regmap, FSL_SAI_RCSR, xcsr);
otherwise we might be ignoring any of the bits that are actually clear on read (it seems like there are some?).
- return IRQ_HANDLED;
I'd expect to see IRQ_NONE if we didn't actually see an interrupt source.
On Thu, Mar 27, 2014 at 01:26:27PM +0000, Mark Brown wrote:
On Thu, Mar 27, 2014 at 07:06:59PM +0800, Nicolin Chen wrote:
It's quite cricial to clear error flags because SAI might hang if getting FIFO underrun during playback (I haven't confirmed the same issue on Rx overflow though).
So this patch enables those irq and adds isr() to clear the flags so as to keep playback entirely safe.
So, I've applied this since we're (hopefully!) very near the merge window opening and it seems like it should be an improvement overall. However a few things below:
- /* Only handle those what we enabled */
- mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
The shifting here could use a comment.
Will send an extra patch to cover it.
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
Using update_bits() is going to do an extra read, better to do this as:
if (xcsr) regmap_write(sai->regmap, FSL_SAI_RCSR, xcsr);
T/RCSR register stands for T/Rx Control (Status -- I guess) Register. It actually contains control bits, IRQ mask bits and IRQ status bits. Thus we can't regard it as a entire status register. That's why I try to update it partially for status bits only at this point.
But if it's just for saving this extra read instance, I may save those non-status bits from upper regmap_read() and then merge them into this regmap_write() over here.
otherwise we might be ignoring any of the bits that are actually clear on read (it seems like there are some?).
For all status flags bits, actually five for each, three of them are write-1-clear and the other two are self-clearance by SAI self -- so none of them are clear-on-read type.
- return IRQ_HANDLED;
I'd expect to see IRQ_NONE if we didn't actually see an interrupt source.
Will add this.
Thank you, Nicolin
participants (2)
-
Mark Brown
-
Nicolin Chen