diff options
author | Charles Keepax <ckeepax@opensource.wolfsonmicro.com> | 2016-02-18 10:47:13 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-02-19 10:29:19 -0500 |
commit | 316fa9e09ad76e095b9d7e9350c628b918370a22 (patch) | |
tree | cf71c3a2f24e32ac49e26039eaa5a3fa55cfe17a | |
parent | 92e963f50fc74041b5e9e744c330dca48e04f08d (diff) |
ASoC: samsung: Use IRQ safe spin lock calls
Lockdep warns of a potential lock inversion, i2s->lock is held numerous
times whilst we are under the substream lock (snd_pcm_stream_lock). If
we use the IRQ unsafe spin lock calls, you can also end up locking
snd_pcm_stream_lock whilst under i2s->lock (if an IRQ happens whilst we
are holding i2s->lock). This could result in deadlock.
[ 18.147001] CPU0 CPU1
[ 18.151509] ---- ----
[ 18.156022] lock(&(&pri_dai->spinlock)->rlock);
[ 18.160701] local_irq_disable();
[ 18.166622] lock(&(&substream->self_group.lock)->rlock);
[ 18.174595] lock(&(&pri_dai->spinlock)->rlock);
[ 18.181806] <Interrupt>
[ 18.184408] lock(&(&substream->self_group.lock)->rlock);
[ 18.190045]
[ 18.190045] *** DEADLOCK ***
This patch changes to using the irq safe spinlock calls, to avoid this
issue.
Fixes: ce8bcdbb61d9 ("ASoC: samsung: i2s: Protect more registers with a spinlock")
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Tested-by: Anand Moon <linux.amoon@gmail.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org
-rw-r--r-- | sound/soc/samsung/i2s.c | 21 |
1 files changed, 12 insertions, 9 deletions
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 84d9e77c0fbe..70a2559b63f9 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c | |||
@@ -481,10 +481,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
481 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; | 481 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; |
482 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; | 482 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; |
483 | u32 mod, mask, val = 0; | 483 | u32 mod, mask, val = 0; |
484 | unsigned long flags; | ||
484 | 485 | ||
485 | spin_lock(i2s->lock); | 486 | spin_lock_irqsave(i2s->lock, flags); |
486 | mod = readl(i2s->addr + I2SMOD); | 487 | mod = readl(i2s->addr + I2SMOD); |
487 | spin_unlock(i2s->lock); | 488 | spin_unlock_irqrestore(i2s->lock, flags); |
488 | 489 | ||
489 | switch (clk_id) { | 490 | switch (clk_id) { |
490 | case SAMSUNG_I2S_OPCLK: | 491 | case SAMSUNG_I2S_OPCLK: |
@@ -575,11 +576,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
575 | return -EINVAL; | 576 | return -EINVAL; |
576 | } | 577 | } |
577 | 578 | ||
578 | spin_lock(i2s->lock); | 579 | spin_lock_irqsave(i2s->lock, flags); |
579 | mod = readl(i2s->addr + I2SMOD); | 580 | mod = readl(i2s->addr + I2SMOD); |
580 | mod = (mod & ~mask) | val; | 581 | mod = (mod & ~mask) | val; |
581 | writel(mod, i2s->addr + I2SMOD); | 582 | writel(mod, i2s->addr + I2SMOD); |
582 | spin_unlock(i2s->lock); | 583 | spin_unlock_irqrestore(i2s->lock, flags); |
583 | 584 | ||
584 | return 0; | 585 | return 0; |
585 | } | 586 | } |
@@ -590,6 +591,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
590 | struct i2s_dai *i2s = to_info(dai); | 591 | struct i2s_dai *i2s = to_info(dai); |
591 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; | 592 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; |
592 | u32 mod, tmp = 0; | 593 | u32 mod, tmp = 0; |
594 | unsigned long flags; | ||
593 | 595 | ||
594 | lrp_shift = i2s->variant_regs->lrp_off; | 596 | lrp_shift = i2s->variant_regs->lrp_off; |
595 | sdf_shift = i2s->variant_regs->sdf_off; | 597 | sdf_shift = i2s->variant_regs->sdf_off; |
@@ -649,7 +651,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
649 | return -EINVAL; | 651 | return -EINVAL; |
650 | } | 652 | } |
651 | 653 | ||
652 | spin_lock(i2s->lock); | 654 | spin_lock_irqsave(i2s->lock, flags); |
653 | mod = readl(i2s->addr + I2SMOD); | 655 | mod = readl(i2s->addr + I2SMOD); |
654 | /* | 656 | /* |
655 | * Don't change the I2S mode if any controller is active on this | 657 | * Don't change the I2S mode if any controller is active on this |
@@ -657,7 +659,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
657 | */ | 659 | */ |
658 | if (any_active(i2s) && | 660 | if (any_active(i2s) && |
659 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { | 661 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { |
660 | spin_unlock(i2s->lock); | 662 | spin_unlock_irqrestore(i2s->lock, flags); |
661 | dev_err(&i2s->pdev->dev, | 663 | dev_err(&i2s->pdev->dev, |
662 | "%s:%d Other DAI busy\n", __func__, __LINE__); | 664 | "%s:%d Other DAI busy\n", __func__, __LINE__); |
663 | return -EAGAIN; | 665 | return -EAGAIN; |
@@ -666,7 +668,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
666 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); | 668 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); |
667 | mod |= tmp; | 669 | mod |= tmp; |
668 | writel(mod, i2s->addr + I2SMOD); | 670 | writel(mod, i2s->addr + I2SMOD); |
669 | spin_unlock(i2s->lock); | 671 | spin_unlock_irqrestore(i2s->lock, flags); |
670 | 672 | ||
671 | return 0; | 673 | return 0; |
672 | } | 674 | } |
@@ -676,6 +678,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, | |||
676 | { | 678 | { |
677 | struct i2s_dai *i2s = to_info(dai); | 679 | struct i2s_dai *i2s = to_info(dai); |
678 | u32 mod, mask = 0, val = 0; | 680 | u32 mod, mask = 0, val = 0; |
681 | unsigned long flags; | ||
679 | 682 | ||
680 | if (!is_secondary(i2s)) | 683 | if (!is_secondary(i2s)) |
681 | mask |= (MOD_DC2_EN | MOD_DC1_EN); | 684 | mask |= (MOD_DC2_EN | MOD_DC1_EN); |
@@ -744,11 +747,11 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, | |||
744 | return -EINVAL; | 747 | return -EINVAL; |
745 | } | 748 | } |
746 | 749 | ||
747 | spin_lock(i2s->lock); | 750 | spin_lock_irqsave(i2s->lock, flags); |
748 | mod = readl(i2s->addr + I2SMOD); | 751 | mod = readl(i2s->addr + I2SMOD); |
749 | mod = (mod & ~mask) | val; | 752 | mod = (mod & ~mask) | val; |
750 | writel(mod, i2s->addr + I2SMOD); | 753 | writel(mod, i2s->addr + I2SMOD); |
751 | spin_unlock(i2s->lock); | 754 | spin_unlock_irqrestore(i2s->lock, flags); |
752 | 755 | ||
753 | samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); | 756 | samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); |
754 | 757 | ||