diff options
Diffstat (limited to 'sound/drivers/pcsp/pcsp_lib.c')
-rw-r--r-- | sound/drivers/pcsp/pcsp_lib.c | 58 |
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); | |||
18 | MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " | 17 | MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " |
19 | "(expect bad sound)"); | 18 | "(expect bad sound)"); |
20 | 19 | ||
21 | static 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 | */ | ||
44 | static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0); | ||
45 | 21 | ||
46 | enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | 22 | enum 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) | |||
146 | exit_nr_unlock2: | 124 | exit_nr_unlock2: |
147 | snd_pcm_stream_unlock(substream); | 125 | snd_pcm_stream_unlock(substream); |
148 | exit_nr_unlock1: | 126 | exit_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 | ||
173 | static void pcsp_stop_playing(struct snd_pcsp *chip) | 151 | static 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, |