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.c58
1 files changed, 20 insertions, 38 deletions
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index ac6238e93513..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
@@ -18,36 +17,12 @@ module_param(nforce_wa, bool, 0444);
18MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " 17MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
19 "(expect bad sound)"); 18 "(expect bad sound)");
20 19
21static void pcsp_start_timer(unsigned long dummy) 20#define DMIX_WANTS_S16 1
22{
23 hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
24}
25
26/*
27 * We need the hrtimer_start as a tasklet to avoid
28 * the nasty locking problem. :(
29 * The problem:
30 * - The timer handler is called with the cpu_base->lock
31 * already held by hrtimer code.
32 * - snd_pcm_period_elapsed() takes the
33 * substream->self_group.lock.
34 * So far so good.
35 * But the snd_pcsp_trigger() is called with the
36 * substream->self_group.lock held, and it calls
37 * hrtimer_start(), which takes the cpu_base->lock.
38 * You see the problem. We have the code pathes
39 * which take two locks in a reverse order. This
40 * can deadlock and the lock validator complains.
41 * The only solution I could find was to move the
42 * hrtimer_start() into a tasklet. -stsp
43 */
44static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
45 21
46enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 22enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
47{ 23{
48 unsigned long flags;
49 unsigned char timer_cnt, val; 24 unsigned char timer_cnt, val;
50 int periods_elapsed; 25 int fmt_size, periods_elapsed;
51 u64 ns; 26 u64 ns;
52 size_t period_bytes, buffer_bytes; 27 size_t period_bytes, buffer_bytes;
53 struct snd_pcm_substream *substream; 28 struct snd_pcm_substream *substream;
@@ -64,9 +39,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
64 return HRTIMER_RESTART; 39 return HRTIMER_RESTART;
65 } 40 }
66 41
67 /* hrtimer calls us from both hardirq and softirq contexts, 42 spin_lock_irq(&chip->substream_lock);
68 * so irqsave :( */
69 spin_lock_irqsave(&chip->substream_lock, flags);
70 /* Takashi Iwai says regarding this extra lock: 43 /* Takashi Iwai says regarding this extra lock:
71 44
72 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
@@ -92,8 +65,11 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
92 goto exit_nr_unlock2; 65 goto exit_nr_unlock2;
93 66
94 runtime = substream->runtime; 67 runtime = substream->runtime;
95 /* assume it is u8 mono */ 68 fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
96 val = runtime->dma_area[chip->playback_ptr]; 69 /* assume it is mono! */
70 val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
71 if (snd_pcm_format_signed(runtime->format))
72 val ^= 0x80;
97 timer_cnt = val * CUR_DIV() / 256; 73 timer_cnt = val * CUR_DIV() / 256;
98 74
99 if (timer_cnt && chip->enable) { 75 if (timer_cnt && chip->enable) {
@@ -111,12 +87,14 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
111 87
112 period_bytes = snd_pcm_lib_period_bytes(substream); 88 period_bytes = snd_pcm_lib_period_bytes(substream);
113 buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 89 buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
114 chip->playback_ptr += PCSP_INDEX_INC(); 90 chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
115 periods_elapsed = chip->playback_ptr - chip->period_ptr; 91 periods_elapsed = chip->playback_ptr - chip->period_ptr;
116 if (periods_elapsed < 0) { 92 if (periods_elapsed < 0) {
117 printk(KERN_WARNING "PCSP: playback_ptr inconsistent " 93#if PCSP_DEBUG
94 printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? "
118 "(%zi %zi %zi)\n", 95 "(%zi %zi %zi)\n",
119 chip->playback_ptr, period_bytes, buffer_bytes); 96 chip->playback_ptr, period_bytes, buffer_bytes);
97#endif
120 periods_elapsed += buffer_bytes; 98 periods_elapsed += buffer_bytes;
121 } 99 }
122 periods_elapsed /= period_bytes; 100 periods_elapsed /= period_bytes;
@@ -132,7 +110,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
132 chip->period_ptr %= buffer_bytes; 110 chip->period_ptr %= buffer_bytes;
133 } 111 }
134 112
135 spin_unlock_irqrestore(&chip->substream_lock, flags); 113 spin_unlock_irq(&chip->substream_lock);
136 114
137 if (!atomic_read(&chip->timer_active)) 115 if (!atomic_read(&chip->timer_active))
138 return HRTIMER_NORESTART; 116 return HRTIMER_NORESTART;
@@ -146,7 +124,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
146exit_nr_unlock2: 124exit_nr_unlock2:
147 snd_pcm_stream_unlock(substream); 125 snd_pcm_stream_unlock(substream);
148exit_nr_unlock1: 126exit_nr_unlock1:
149 spin_unlock_irqrestore(&chip->substream_lock, flags); 127 spin_unlock_irq(&chip->substream_lock);
150 return HRTIMER_NORESTART; 128 return HRTIMER_NORESTART;
151} 129}
152 130
@@ -167,7 +145,7 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
167 atomic_set(&chip->timer_active, 1); 145 atomic_set(&chip->timer_active, 1);
168 chip->thalf = 0; 146 chip->thalf = 0;
169 147
170 tasklet_schedule(&pcsp_start_timer_tasklet); 148 hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
171} 149}
172 150
173static void pcsp_stop_playing(struct snd_pcsp *chip) 151static void pcsp_stop_playing(struct snd_pcsp *chip)
@@ -270,7 +248,11 @@ static struct snd_pcm_hardware snd_pcsp_playback = {
270 .info = (SNDRV_PCM_INFO_INTERLEAVED | 248 .info = (SNDRV_PCM_INFO_INTERLEAVED |
271 SNDRV_PCM_INFO_HALF_DUPLEX | 249 SNDRV_PCM_INFO_HALF_DUPLEX |
272 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), 250 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
273 .formats = SNDRV_PCM_FMTBIT_U8, 251 .formats = (SNDRV_PCM_FMTBIT_U8
252#if DMIX_WANTS_S16
253 | SNDRV_PCM_FMTBIT_S16_LE
254#endif
255 ),
274 .rates = SNDRV_PCM_RATE_KNOT, 256 .rates = SNDRV_PCM_RATE_KNOT,
275 .rate_min = PCSP_DEFAULT_SRATE, 257 .rate_min = PCSP_DEFAULT_SRATE,
276 .rate_max = PCSP_DEFAULT_SRATE, 258 .rate_max = PCSP_DEFAULT_SRATE,