mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
ASoC: fsl_xcvr: reset RX dpath after wrong preamble
Below preamble error means wrong preamble of IEC958 received, the channel order may be wrong at the moment. FSL_XCVR_IRQ_PREAMBLE_MISMATCH FSL_XCVR_IRQ_UNEXP_PRE_REC FSL_XCVR_IRQ_M_W_PRE_MISMATCH FSL_XCVR_IRQ_B_PRE_MISMATCH All above errors may cause channel swap, to avoid such issues, need to reset the DMAC path. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Link: https://patch.msgid.link/1728368873-31379-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
06461e288a
commit
1e5d0f1061
2 changed files with 81 additions and 13 deletions
|
@ -53,6 +53,8 @@ struct fsl_xcvr {
|
|||
struct snd_aes_iec958 rx_iec958;
|
||||
struct snd_aes_iec958 tx_iec958;
|
||||
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
|
||||
struct work_struct work_rst;
|
||||
spinlock_t lock; /* Protect hw_reset and trigger */
|
||||
};
|
||||
|
||||
static const struct fsl_xcvr_pll_conf {
|
||||
|
@ -663,7 +665,10 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
{
|
||||
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
int ret;
|
||||
unsigned long lock_flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&xcvr->lock, lock_flags);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
|
@ -675,7 +680,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_EXT_CTRL_DPTH_RESET(tx));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
if (tx) {
|
||||
|
@ -687,7 +692,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_ISR_CMDC_TX_EN);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "err updating isr %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
fallthrough;
|
||||
case FSL_XCVR_MODE_SPDIF:
|
||||
|
@ -696,7 +701,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -707,14 +712,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to enable DMA: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
|
||||
FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
/* clear DPATH RESET */
|
||||
|
@ -723,7 +728,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
0);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -736,14 +741,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_EXT_CTRL_DMA_DIS(tx));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to disable DMA: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
|
||||
FSL_XCVR_IRQ_EARC_ALL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to clear IER0: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
|
||||
if (tx) {
|
||||
|
@ -754,7 +759,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
break;
|
||||
|
@ -768,17 +773,20 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
if (ret < 0) {
|
||||
dev_err(dai->dev,
|
||||
"Err updating ISR %d\n", ret);
|
||||
return ret;
|
||||
goto release_lock;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
release_lock:
|
||||
spin_unlock_irqrestore(&xcvr->lock, lock_flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr)
|
||||
|
@ -1198,6 +1206,34 @@ static const struct regmap_config fsl_xcvr_regmap_cfg = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static void reset_rx_work(struct work_struct *work)
|
||||
{
|
||||
struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst);
|
||||
struct device *dev = &xcvr->pdev->dev;
|
||||
unsigned long lock_flags;
|
||||
u32 ext_ctrl;
|
||||
|
||||
dev_dbg(dev, "reset rx path\n");
|
||||
spin_lock_irqsave(&xcvr->lock, lock_flags);
|
||||
regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl);
|
||||
|
||||
if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) {
|
||||
regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
|
||||
FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
|
||||
FSL_XCVR_EXT_CTRL_DMA_RD_DIS);
|
||||
regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
|
||||
FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
|
||||
FSL_XCVR_EXT_CTRL_RX_DPTH_RESET);
|
||||
regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
|
||||
FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
|
||||
0);
|
||||
regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
|
||||
FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
|
||||
0);
|
||||
}
|
||||
spin_unlock_irqrestore(&xcvr->lock, lock_flags);
|
||||
}
|
||||
|
||||
static irqreturn_t irq0_isr(int irq, void *devid)
|
||||
{
|
||||
struct fsl_xcvr *xcvr = (struct fsl_xcvr *)devid;
|
||||
|
@ -1269,6 +1305,29 @@ static irqreturn_t irq0_isr(int irq, void *devid)
|
|||
dev_dbg(dev, "CMDC status update\n");
|
||||
isr_clr |= FSL_XCVR_IRQ_CMDC_STATUS_UPD;
|
||||
}
|
||||
if (isr & FSL_XCVR_IRQ_PREAMBLE_MISMATCH) {
|
||||
dev_dbg(dev, "Preamble mismatch\n");
|
||||
isr_clr |= FSL_XCVR_IRQ_PREAMBLE_MISMATCH;
|
||||
}
|
||||
if (isr & FSL_XCVR_IRQ_UNEXP_PRE_REC) {
|
||||
dev_dbg(dev, "Unexpected preamble received\n");
|
||||
isr_clr |= FSL_XCVR_IRQ_UNEXP_PRE_REC;
|
||||
}
|
||||
if (isr & FSL_XCVR_IRQ_M_W_PRE_MISMATCH) {
|
||||
dev_dbg(dev, "M/W preamble mismatch\n");
|
||||
isr_clr |= FSL_XCVR_IRQ_M_W_PRE_MISMATCH;
|
||||
}
|
||||
if (isr & FSL_XCVR_IRQ_B_PRE_MISMATCH) {
|
||||
dev_dbg(dev, "B preamble mismatch\n");
|
||||
isr_clr |= FSL_XCVR_IRQ_B_PRE_MISMATCH;
|
||||
}
|
||||
|
||||
if (isr & (FSL_XCVR_IRQ_PREAMBLE_MISMATCH |
|
||||
FSL_XCVR_IRQ_UNEXP_PRE_REC |
|
||||
FSL_XCVR_IRQ_M_W_PRE_MISMATCH |
|
||||
FSL_XCVR_IRQ_B_PRE_MISMATCH)) {
|
||||
schedule_work(&xcvr->work_rst);
|
||||
}
|
||||
|
||||
if (isr_clr) {
|
||||
regmap_write(regmap, FSL_XCVR_EXT_ISR_CLR, isr_clr);
|
||||
|
@ -1415,11 +1474,16 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
|
|||
fsl_xcvr_comp.name);
|
||||
}
|
||||
|
||||
INIT_WORK(&xcvr->work_rst, reset_rx_work);
|
||||
spin_lock_init(&xcvr->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_xcvr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
cancel_work_sync(&xcvr->work_rst);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -166,6 +166,10 @@
|
|||
FSL_XCVR_IRQ_FIFO_UOFL_ERR | \
|
||||
FSL_XCVR_IRQ_HOST_WAKEUP | \
|
||||
FSL_XCVR_IRQ_CMDC_STATUS_UPD |\
|
||||
FSL_XCVR_IRQ_B_PRE_MISMATCH |\
|
||||
FSL_XCVR_IRQ_M_W_PRE_MISMATCH |\
|
||||
FSL_XCVR_IRQ_PREAMBLE_MISMATCH |\
|
||||
FSL_XCVR_IRQ_UNEXP_PRE_REC |\
|
||||
FSL_XCVR_IRQ_ARC_MODE)
|
||||
|
||||
#define FSL_XCVR_ISR_CMDC_TX_EN BIT(3)
|
||||
|
|
Loading…
Add table
Reference in a new issue