aboutsummaryrefslogtreecommitdiffstats
path: root/sound/drivers/pcsp/pcsp_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/drivers/pcsp/pcsp_lib.c')
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c37
1 files changed, 4 insertions, 33 deletions
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index 7ad4a1534b2b..e341f3f83b6a 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -9,7 +9,6 @@
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/moduleparam.h> 10#include <linux/moduleparam.h>
11#include <sound/pcm.h> 11#include <sound/pcm.h>
12#include <linux/interrupt.h>
13#include <asm/io.h> 12#include <asm/io.h>
14#include "pcsp.h" 13#include "pcsp.h"
15 14
@@ -20,34 +19,8 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
20 19
21#define DMIX_WANTS_S16 1 20#define DMIX_WANTS_S16 1
22 21
23static void pcsp_start_timer(unsigned long dummy)
24{
25 hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
26}
27
28/*
29 * We need the hrtimer_start as a tasklet to avoid
30 * the nasty locking problem. :(
31 * The problem:
32 * - The timer handler is called with the cpu_base->lock
33 * already held by hrtimer code.
34 * - snd_pcm_period_elapsed() takes the
35 * substream->self_group.lock.
36 * So far so good.
37 * But the snd_pcsp_trigger() is called with the
38 * substream->self_group.lock held, and it calls
39 * hrtimer_start(), which takes the cpu_base->lock.
40 * You see the problem. We have the code pathes
41 * which take two locks in a reverse order. This
42 * can deadlock and the lock validator complains.
43 * The only solution I could find was to move the
44 * hrtimer_start() into a tasklet. -stsp
45 */
46static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
47
48enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 22enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
49{ 23{
50 unsigned long flags;
51 unsigned char timer_cnt, val; 24 unsigned char timer_cnt, val;
52 int fmt_size, periods_elapsed; 25 int fmt_size, periods_elapsed;
53 u64 ns; 26 u64 ns;
@@ -66,9 +39,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
66 return HRTIMER_RESTART; 39 return HRTIMER_RESTART;
67 } 40 }
68 41
69 /* hrtimer calls us from both hardirq and softirq contexts, 42 spin_lock_irq(&chip->substream_lock);
70 * so irqsave :( */
71 spin_lock_irqsave(&chip->substream_lock, flags);
72 /* Takashi Iwai says regarding this extra lock: 43 /* Takashi Iwai says regarding this extra lock:
73 44
74 If the irq handler handles some data on the DMA buffer, it should 45 If the irq handler handles some data on the DMA buffer, it should
@@ -139,7 +110,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
139 chip->period_ptr %= buffer_bytes; 110 chip->period_ptr %= buffer_bytes;
140 } 111 }
141 112
142 spin_unlock_irqrestore(&chip->substream_lock, flags); 113 spin_unlock_irq(&chip->substream_lock);
143 114
144 if (!atomic_read(&chip->timer_active)) 115 if (!atomic_read(&chip->timer_active))
145 return HRTIMER_NORESTART; 116 return HRTIMER_NORESTART;
@@ -153,7 +124,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
153exit_nr_unlock2: 124exit_nr_unlock2:
154 snd_pcm_stream_unlock(substream); 125 snd_pcm_stream_unlock(substream);
155exit_nr_unlock1: 126exit_nr_unlock1:
156 spin_unlock_irqrestore(&chip->substream_lock, flags); 127 spin_unlock_irq(&chip->substream_lock);
157 return HRTIMER_NORESTART; 128 return HRTIMER_NORESTART;
158} 129}
159 130
@@ -174,7 +145,7 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
174 atomic_set(&chip->timer_active, 1); 145 atomic_set(&chip->timer_active, 1);
175 chip->thalf = 0; 146 chip->thalf = 0;
176 147
177 tasklet_schedule(&pcsp_start_timer_tasklet); 148 hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
178} 149}
179 150
180static void pcsp_stop_playing(struct snd_pcsp *chip) 151static void pcsp_stop_playing(struct snd_pcsp *chip)